找出乱序数组中最小(最大)的k个数字

输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。


这道题最简单的思路莫过于把输入的n个整数排序,排序之后位于最前面的k个数就是最小的k个数。这种思路的时间复杂度是O( nlogn),显然不太能让面试官满意.

我们可以按照找一个最小值为基础展开思路, 找到一个最小值, 然后修改原数组去掉此元素, 在找最小值, 找k次就完成了.O(kn)的时间复杂度.  是不是有点像选择排序法 , 如果k接近于n, 就会退化成选择排序, O(n^2)的时间复杂度.

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self findMinKNumSelect];
}

// 查找数组中最小的k个数字, 使用选择排序法解决
- (void)findMinKNumSelect {
    NSArray * array = @[@(1),@(7),@(22),@(9),
                        @(4),@(5),@(122),@(8),
                        @(35),@(18),@(3),@(13),
                        @(5),@(218),@(63),@(103),

    ];
    int a[100] = {};
    
    for (int i = 0; i<array.count; i++) {
        a[i] = [array[i] intValue];
    }
    // 设置查找最小的数字
    int k = 6;
    // --------------算法开始
    
    NSMutableArray * result = [NSMutableArray arrayWithCapacity:k];
    while (result.count<k) {
        
        int min = a[0];
        int minIndex = 0;
        for (int i = 0; i<array.count-result.count; i++) {
            if (a[i]<min) {
                min = a[i];
                minIndex = i;
            }
        }
        NSLog(@"本次最小值 %d 下标%d",min,minIndex);
        // 每次找到最小值, 把对应最小值的换成原始数组的最后一个元素
        a[minIndex] = a[array.count-result.count-1];
        [result addObject:@(min)];
        
    }
    
    NSLog(@"%@",[result componentsJoinedByString:@" "]);
    
}


有了选择排序法的思路, 我们在往快排上迁移一下, 看看有没有更好的思路,  "快速排序法"中重要的一步就是查找基准数, 使基准数左边都比基准数小, 基准数右边都比基准数大, 如果能找到这个数字, 使得左边的数字为k个(或者k-1个,加上基准数本身就是k个),那么左边的数字就是最小的k个数字了.

找到以前写的快排代码, 去掉递归调用, 增加一个返回值,返回本次调用使用的基准值下标, 如果 (基准值下标==k || 基准值下标==k-1),说明0~k-1已经是最小的k个数字了.

- (void)viewDidLoad {
    [super viewDidLoad];

    [self findMinKNum];
}

// 查找数组中最小的k个数字
- (void)findMinKNum {
    
    NSArray * array = @[@(1),@(7),@(22),@(9),
                        @(4),@(5),@(122),@(8),
                        @(35),@(18),@(3),@(13),
                        @(5),@(218),@(63),@(103),

    ];
    int a[100] = {};
    
    for (int i = 0; i<array.count; i++) {
        a[i] = [array[i] intValue];
    }
    // 设置查找最小的数字
    int k = 6;
    // -------------- 算法开始
    
    
    int mid = quickSort(a, 0, (int)array.count-1);
    // mid==k, 那么0~k-1就是前k个最小的数
    // mid==k-1, 还是0~k-1
    while (mid!=k-1 && mid!=k) {
        if (mid>k) {
            mid = quickSort(a, 0, mid-1);
        }
        if (mid<k){
            mid = quickSort(a, mid+1, (int)array.count-1);
        }

    }
    
    
    NSString * str = @"";
    for (int i = 0; i<k; i++) {
        str = [str stringByAppendingFormat:@"%d  ",a[i]];
    }
    NSLog(@"前k=%d个最小值是 : %@",k,str);
    
    
    str = @"";
     for (int i = 0; i<array.count; i++) {
         str = [str stringByAppendingFormat:@"%d  ",a[i]];
     }
     NSLog(@"完整数组为 : %@",str);
    
}



// 对快排算法简单改造了一下, 去掉了递归调用, 返回本次的基准值下标
int quickSort(int a[], int left, int right) {
    if (a==NULL) {
        return -1;
    }
    if (left>right) {
        return -1;
    }
    
    int low = left;
    int high = right;

    // 选择基准数
    int temp = a[left];
    while (low<high) {
        
        while (low<high && a[high]>=temp) {
            high--;
        }
        
        while (low<high && a[low] <= temp) {
            low++;
        }
        
        if (low<high) {
            int t = a[low];
            a[low] = a[high];
            a[high] = t;
        }
        
    }
    
    // 到这里说明low==high, 交换基准数和相遇点的位置
    a[left] = a[low];
    a[low] = temp;
    NSLog(@"本次基准值下标为  %d  %d",low,high);
    return low;
}

如果是让找最大的k个数字, 有2种改法,
一种是修改排序函数,变成降序排列,这样前k个就是最大的k个元素,判断条件就保持原状
一种是修改判断条件. 数组还是按照升序排列, 判断条件改成这样


在想一下,如果想要找到数组中最小(或者最小)的数字, 我们一般会用一个临时变量, 然后遍历一遍数组, 每个元素依次与这个临时变量比较, 如果小于这个临时变量, 就把值赋给临时变量.

现在用一个临时变量保存肯定是不够了, 需要使用一个集合来保存这K个元素, 然后遍历数组, 集合中没有填满的时候, 把数组元素填入集合, 如果集合满了, 就用数组中元素与集合中的最大值比较, 如果比集合最大值小, 就把集合中的最大值剔除,并把此元素放入集合. 当遍历完所有元素的时候, 集合中的元素就是最小的k个数字了.

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章