日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

求数组中第k个最小数

發布時間:2025/6/15 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 求数组中第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?
  • //返回數組中的第k個最小元素的啟動函數,注意會破壞原數組??
  • template<typename?T>??
  • T?FindTheKMin(T?*x,?int?x_size,?int?k);??
  • //實現查找數組中第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)??
  • {??
  • ????//判斷k的值是否過大,即超過數組的大小??
  • ????//若是則返回第0個元素,主要是為了防止無效的遞歸??
  • ????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)??
  • ????{??
  • ????????//從前向后掃描,找到第一個小于樞紐的值,??
  • ????????//在到達數組末尾前,必定結束循環,因為最后一個值為centre??
  • ????????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]);??
  • ????//如果此時centre的位置剛好為k,則centre為第k個最小的數??
  • ????if(i+1?==?k)??
  • ????????return?x[i];??
  • ????else?if(i+1?<?k)??
  • ????{??
  • ????????//如果此時centre的位置比k前,遞歸地在其右邊尋找??
  • ????????TheKMin(x,?i+1,?right,?k);??
  • ????}??
  • ????else??
  • ????{??
  • ????????//如果此時centre的位置比k后,遞歸地在其左邊尋找??
  • ????????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);??
  • //實現查找數組中第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)??
  • {??
  • ????//判斷k的值是否過大,即超過數組的大小??
  • ????//若是則返回下標-1,主要是為了防止無效的遞歸??
  • ????if(x_size?<?k)??
  • ????????return?-1;??
  • ????//創建一個跟蹤數組,其內容為原數組中元素的下標,??
  • ????//用于記錄元素的交換(即代替元素的交換)??
  • ????//按順序以track數組中的數據為下標訪問元素,訪問順序與上一方法相同??
  • ????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)??
  • ????{??
  • ????????//從前向后掃描,找到第一個小于樞紐的值,??
  • ????????//在到達數組末尾前,必定結束循環,因為最后一個值為centre??
  • ????????//注意此時的數據的下標不是i,而是track[i]??
  • ????????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]);??
  • ????//如果此時centre的位置剛好為k,則centre為第k個最小的數,??
  • ????//返回其在真實數組中的下標,即track[i]??
  • ????if(i+1?==?k)??
  • ????????return?track[i];??
  • ????else?if(i+1?<?k)??
  • ????{??
  • ????????//如果此時centre的位置比k前,遞歸地在其右邊尋找??
  • ????????TheKMin(x,?track,?i+1,?right,?k);??
  • ????}??
  • ????else??
  • ????{??
  • ????????//如果此時centre的位置比k后,遞歸地在其左邊尋找??
  • ????????TheKMin(x,?track,?left,?i-1,?k);??
  • ????}??
  • }??
  • 代碼說明:

    從上面的代碼,我們可以看出,這個函數是返回數組中的第k個最小元的下標,所以當k不合理時,就可以返回-1來表示這個錯誤,同時,它使用了一個跟蹤數組,track數組中的內容,實質是原數組中數據的一個索引,利用跟蹤數組的元素的交換來代替了原數組元素的交換,因為該跟蹤數組的數據類型是int,所以其交換速度相當快,從而解決了上面提到的兩個問題。


    從上面的代碼,我們也可以看到,其時間復雜度與前面的實現是一樣的,也為O(N),但是,這個實現方法卻帶來了一定的空間開銷,它開辟了一個與原數組元素個數相等的一維數組,用于跟蹤原數組中的元素的交換情況。

    至于在實際中,要使用哪一種算法,取決于使用者的需要!

    《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

    總結

    以上是生活随笔為你收集整理的求数组中第k个最小数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。