輸入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個數字了.