最小的K個數

2021-10-03 17:36:28 字數 3381 閱讀 4168

輸入整數陣列 arr ,找出其中最小的 k 個數。例如,輸入4、5、1、6、2、7、3、8這8個數字,則最小的4個數字是1、2、3、4。

示例1:

輸入:arr = [3,2,1], k = 2

輸出:[1,2] 或者 [2,1]

示例2:

輸入:arr = [0,1,2,1], k = 1

輸出:[0]

限制:0 <= k <= arr.length <= 10000

0 <= arr[i] <= 10000

這道題應該是比較常見的乙個問題了,還有乙個映象問題是最大的k個數。總結之後,大概有如下幾種解題思路:

最容易想到的方法,直接按公升序對原陣列排序,然後輸出前k個數即可。時間複雜度依賴於所使用的排序演算法,快排為o(nlogn)。

class

solution

};

只需要前k個最小的數,而不需要對全部陣列排序,因此自然而然就可以想到建立乙個最小堆,並且取k次堆頂的元素,最終就得到了最小的前k個數。建堆的時間複雜度為o(n),插入即取出堆元素後堆化的時間複雜度為o(logn),原則上這個演算法的時間複雜度也是o(nlogn)(o(klogn)),但是當k比較小時,時間複雜度會想o(logn)靠近,所以相比於o(nlogn)的排序演算法,效率還是會高一些。

class

solution

return ans;

}void

buildheap

(vector<

int>

&arr,

int heapsize)}}

void

heapify

(vector<

int>

&arr,

int heapsize)if(

(index +1)

*2< heapsize && arr[minpos]

> arr[

(index +1)

*2])

if(minpos == index)

int temp = arr[index]

; arr[index]

= arr[minpos]

; arr[minpos]

= temp;

index = minpos;}}

};

思路與第二種方法類似,只不過是變成了維護乙個k個元素的最大堆。將剩餘的每個元素都和堆頂的元素進行比較,如果小於堆頂元素,就和堆頂元素替換,這樣最終這個堆裡的元素就是最小的k個數。這種方法相比於第二種方法,減少了一次堆化操作的消耗,但增大了堆化操作的次數,總的來說時間複雜度還是o(nlogn)(或者說是o(nlogk)),只不過當k變化時實際的消耗會有所不同。

我們還可以採用紅黑樹來實現這個容器,紅黑樹通過把節點分為紅、黑兩種顏色並根據一些規則確保樹在一定程度上是平衡的,從而保證在紅黑樹中的查詢、刪除和插入操作都只需要o(logk)時間。在stl中,set和multiset都是基於紅黑樹實現的。

這個演算法適合海量資料的輸入。因為只需在記憶體中維護最小的k個數字即可,其餘的資料可以放在輔助記憶體(如硬碟中)。

class

solution

}return ans;

}void

buildheap

(vector<

int>

&arr,

int heapsize)}}

void

heapify

(vector<

int>

&arr,

int heapsize)if(

(index +1)

*2< heapsize && arr[maxpos]

< arr[

(index +1)

*2])

if(maxpos == index)

int temp = arr[index]

; arr[index]

= arr[maxpos]

; arr[maxpos]

= temp;

index = maxpos;}}

};

今天看面經看到這個問題,面試官問除了堆之外有沒有可以達到o(n)的演算法,問的就是基於快速排序的這個操作。

(佛了本來寫了挺多的結果一斷網沒了)

先簡單複習一下快排:

兩種partition寫法:

int

partition1

(vector<

int>

&arr,

int low,

int high)

arr[low]

= arr[high]

;while

(low < high && arr[low]

<= pivot)

arr[high]

= arr[low];}

}int

partition2

(vector<

int>

&arr,

int low,

int high)

}swap

(arr[high]

, arr[

++location]);

return location;

}

快速排序主程式:

void

quicksort

(vector<

int>

&arr,

int low,

int high)

}

這道題使用快排中的partition函式,思路是,每次選擇乙個pivot元素,如果這個元素恰好是第k個,則左邊的元素就是最小的k個數,如果這個pivot元素不是第k個,則縮小範圍,在它的左邊或右邊繼續查詢。採用這種思路是有限制的。我們需要修改輸入的陣列,因為函式partition會調整陣列中數字的順序。如果要求不能修改輸入陣列,則只能使用堆的方法。

class

solution

else

}for

(int i =

0; i < k; i++

)return ans;

}int

mypartition

(vector<

int>

&arr,

int low,

int high)

}swap

(arr[high]

, arr[

++location]);

return location;}}

;

最小的K個數

問題描述 給定的n個整數,計算其中最小的k個數。最直觀的解法莫過於將n個數按公升序排列後輸出前k個。但是就效率來看,這種方法並不是最理想的。一種改進方法是借助快速排序中對陣列的劃分,以第k個元素對陣列進行劃分,使得比第k個數字小的數字都在其左邊,比其大的數字都在它的右邊。void swap int ...

最小的K個數

從 陣列中出現次數超過一半的數字 得到啟發,同樣可以基於partition函式來解決。一 o n 演算法 void getleastnumbers int input,int n,int output,int k else for int i 0 i k i output i input i 二 o...

最小的K個數

輸入n個整數,找出其中最小的k個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,如果不讓使用sort的話,自己實現乙個,或者依次選取最小的 class solution public vectorgetleastnumbers solution vectori...