生活随笔
收集整理的這篇文章主要介紹了
求数组中第k个最小数
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、問題描述
給定一個數組,數組中的數據無序,在一個數組中找出其第k個最小的數,例如對于數組x,x = {3,2,1,4,5,6},則其第2個最小的數為2。
二、解題思路
本算法跟快排的思想相似,首先在數組中選取一個數centre作為樞紐,將比centre小的數,放到centre的前面將比centre大的數,放到centre的后面。如果此時centre的位置剛好為k,則centre為第k個最小的數;如果此時centre的位置比k前,則第k個最小數一定在centre后面,遞歸地在其右邊尋找;如果此時centre的位置比k后,則第k個最小數一定在centre后面,遞歸地在其左邊尋找。
注意:centre的位置=其下標值+1,因為數組中的第一個元素的下標為0。
從上面的描述中,我們可以看到這個算法運用了減治的方法求解。減治的思想與分治非常相似,同樣是在一次操作中,削減問題的規模,只是分治把每個子問題求解后,要合并每個子問題的解才能得到問題,而減治的方法,卻不用合并子問題的解,子問題的解,直接就是原問題的解。舉個例子來說,就像快排和二分查找算法,前者是分治,后者是減治。因為快排要等到所有的子數組都排完序,原數組才有序,而二分查找卻不用,它每執行一次查找,直接丟棄一半的數組,而不用合并子問題的解。不過也有不少書,把他們都歸為分治法。
三、代碼實現
考慮到代碼的通用性,使用了模板函數,如果看不懂模板函數,則只需要忽略template<typename T>,并把T看作是一個類型即可。代碼如下:
[cpp]?view plaincopyprint?
?? template<typename?T>?? T?FindTheKMin(T?*x,?int?x_size,?int?k);?? ?? template<typename?T>?? T?TheKMin(T?*x,?int?left,?int?right,?int?k);?? template<typename?T>?? T?FindTheKMin(T?*x,?int?x_size,?int?k)?? {?? ?????? ?????? ????if(x_size?<?k)?? ????????return?x[0];?? ????return?TheKMin(x,?0,?x_size-1,?k);?? }?? template<typename?T>?? T?TheKMin(T?*x,?int?left,?int?right,?int?k)?? {?? ?????? ????T?centre?=?x[right];?? ????int?i?=?left;?? ????int?j?=?right?-?1;?? ????while(true)?? ????{?? ?????????? ?????????? ????????while(x[i]?<?centre)?? ????????????++i;?? ?????????? ????????while(j?>=?left?&&?x[j]?>?centre)?? ????????????--j;?? ?????????? ????????if(i?<?j)?? ????????????Swap(x[i],?x[j]);?? ????????else?? ????????????break;?? ????}?? ?????? ????Swap(x[i],?x[right]);?? ?????? ????if(i+1?==?k)?? ????????return?x[i];?? ????else?if(i+1?<?k)?? ????{?? ?????????? ????????TheKMin(x,?i+1,?right,?k);?? ????}?? ????else?? ????{?? ?????????? ????????TheKMin(x,?left,?i-1,?k);?? ????}?? }??
代碼說明:
在上面的代碼中,我們要注意,TheKMin函數的最后的if-else,這個算法不同于快排,當樞紐不是要找到元素時,它只會選擇其中一個方向的子數組繼續尋找,而不像快排那樣,會在兩個方向的子數組中繼續。從上面的代碼來看,其運行速度應該在使用相同選取樞紐的策略的快排之上,時間復雜度為O(N)。
同時,當K值不合理時,我們只能返回第0個元素,這點有一點的不合理,但是,我不知道該返回一個什么樣的合適的值,因為它是泛型的。
其實,這段代碼有兩個缺陷,第一個,就是在查找時,破壞了數組原來的數據(交換了位置);第二個是,當類型T的復制和構造開銷較大時,直接多次交換兩個元素,可能會帶來相當大。
另一種實現
下面,再來看看另一種實現,算法的思想和策略相同,但是使用了一個跟蹤數組track,用來跟蹤使用第一種方法下的數據的交換情況,利用跟蹤數組的元素交換代替原數組中元素的交換,解決了上面提到的兩個問題。它的實現如下:
[cpp]?view plaincopyprint?
?? template<typename?T>?? int?IndexOfKMin(const?T?*x,?int?x_size,?int?k);?? ?? template<typename?T>?? int?TheKMin(const?T?*x,?int?*track,?int?left,?int?right,?int?k);?? template<typename?T>?? int?IndexOfKMin(const?T?*x,?int?x_size,?int?k)?? {?? ?????? ?????? ????if(x_size?<?k)?? ????????return?-1;?? ?????? ?????? ?????? ????int?*track?=?new?int[x_size];?? ????for(int?i?=?0;?i?<?x_size;?++i)??? ????????track[i]?=?i;?? ????int?i?=?TheKMin(x,?track,?0,?x_size-1,?k);?? ????delete?[]track;?? ????return?i;?? }?? template<typename?T>?? int?TheKMin(const?T?*x,?int?*track,?int?left,?int?right,?int?k)?? {?? ?????? ????T?centre?=?x[track[right]];?? ????int?i?=?left;?? ????int?j?=?right?-?1;?? ????while(true)?? ????{?? ?????????? ?????????? ?????????? ????????while(x[track[i]]?<?centre)?? ????????????++i;?? ?????????? ????????while(j?>=?left?&&?x[track[j]]?>?centre)?? ????????????--j;?? ?????????? ????????if(i?<?j)?? ????????????Swap(track[i],?track[j]);?? ????????else?? ????????????break;?? ????}?? ?????? ????Swap(track[i],?track[right]);?? ?????? ?????? ????if(i+1?==?k)?? ????????return?track[i];?? ????else?if(i+1?<?k)?? ????{?? ?????????? ????????TheKMin(x,?track,?i+1,?right,?k);?? ????}?? ????else?? ????{?? ?????????? ????????TheKMin(x,?track,?left,?i-1,?k);?? ????}?? }??
代碼說明:
從上面的代碼,我們可以看出,這個函數是返回數組中的第k個最小元的下標,所以當k不合理時,就可以返回-1來表示這個錯誤,同時,它使用了一個跟蹤數組,track數組中的內容,實質是原數組中數據的一個索引,利用跟蹤數組的元素的交換來代替了原數組元素的交換,因為該跟蹤數組的數據類型是int,所以其交換速度相當快,從而解決了上面提到的兩個問題。
從上面的代碼,我們也可以看到,其時間復雜度與前面的實現是一樣的,也為O(N),但是,這個實現方法卻帶來了一定的空間開銷,它開辟了一個與原數組元素個數相等的一維數組,用于跟蹤原數組中的元素的交換情況。
至于在實際中,要使用哪一種算法,取決于使用者的需要!
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的求数组中第k个最小数的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。