找出亂序數組中最小(最大)的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個數字了.

 

 

 

 

 

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