k個最小的數

2021-07-10 19:09:08 字數 4183 閱讀 5221

問題:查詢大量無序元素中最大的k個數。

解法一:該解法是大部分能想到的,也是第一想到的方法。假設資料量不大,可以先用快速排序或堆排序,他們的平均時間複雜度為o(n*logn),然後取出前k個,時間複雜度為o(k),總的時間複雜度為o(n*logn)+o(k).

當k=1時,上面的演算法的時間複雜度也是o(n*logn),上面的演算法是把整個陣列都進行了排序,而原題目只要求最大的k個數,並不需要前k個數有限,也不需要後n-k個數有序。可以通過部分排序演算法如選擇排序和交換排序,把n個數中的前k個數排序出來,複雜度為o(n*k),選擇哪乙個,取決於k的大小,在k(k

解法二

:(掌握)避免對前k個數進行排序來獲取更好的效能(利用快速排序的原理)。

假設n個數儲存在陣列s中,從陣列中隨機找乙個元素x,將陣列分成兩部分sa和sb.sa中的元素大於等於x,sb中的元素小於x。

出現如下兩種情況:

(1)若sa組的個數大於或等於k,則繼續在sa分組中找取最大的k個數字 。

(2)若sa組中的數字小於k ,其個數為t,則繼續在sb中找取 k-t個數字 。

一直這樣遞迴下去,不斷把問題分解成小問題,平均時間複雜度為o(n*logk)。

**如下:

[cpp]view plain

/*將陣列a[s]...a[t]中的元素用乙個元素劃開,儲存中a[k]中*/

void

partition(

inta, 

ints,

intt,

int&k)    

while

(i//直到指標i與j相等    

a[i]=x;      //劃分元素就位   

k=i;    

}    

/*查詢陣列前k個最大的元素,index:返回陣列中最大元素中第k個元素的下標(從0開始編號),high為陣列最大下標*/

intfindkmax(

inta,

intlow,

inthigh,

intk)  

return

index;  

}  int

main()  

;    

intlen=

sizeof

(a)/

sizeof

(int

);   

intk=4;  

findkmax(a , 0 , len- 1 , k) ;      

for(

inti = 0 ; i < k ; i++)    

cout<" "

;    

return

0;  

}  

解法三:(

掌握)用容量為k的最小堆來儲存最大的k個數。最小堆的堆頂元素就是最大k個數中的最小的乙個。每次掃瞄乙個資料x,如果x比堆頂元素y小,則不需要改變原來的堆。如果x比堆頂元素大,那麼用x替換堆頂元素y,在替換之後,x可能破壞了最小堆的結構,需要調整堆來維持堆的性質。調整過程時間複雜度為o(logk)。 全部的時間複雜度為o(n*logk)。

這種方法當資料量比較大的時候,比較方便。因為對所有的資料只會遍歷一次,第一種方法則會多次遍歷陣列。 如果所查詢的k的數量比較大。可以考慮先求出k` ,然後再求出看k`+1 到 2 * k`之間的資料,然後一次求取。

**如下:

[cpp]view plain

void

heapifymin(

intarray,

inti,

intsize)  

if(right

//開始交換父結點和最大的子結點

if(smallest!=i)  

}  }  //建堆過程,建立最小堆,從最後乙個結點開始調整為最小堆

void

min_heapify(

intarray,

intsize)  

//k為需要查詢的最大元素個數,size為陣列大小,kmax儲存k個元素的最小堆

void

findmax(

intarray,

intk,

intsize,

intkmax)  

}  }  int

main()  

;  int

length=

sizeof

(a)/

sizeof

(int

);  

//最大四個元素為23,52,35,12

/***************查詢陣列中前k個最大的元素****************/

intk=4;  

int* kmax=(

int*)malloc(k*

sizeof

(int

));  

findmax(a,k,length,kmax);  

printf("最大的%d個元素如下所示 : \n"

,k);  

for(

inti=0;i

printf("%4d"

,kmax[i]);  

printf("\n"

);  

return

0;  

}  

解法四:這也是尋找n個數中的第k大的數演算法。利用二分的方法求取top k問題。 首先查詢 max 和 min,然後計算出mid = (max + min) / 2該演算法的實質是尋找最大的k個數中最小的乙個。

[cpp]view plain

const

intn = 8 ;    

const

intk = 4 ;    

/* 利用二分的方法求取top k問題。 

首先查詢 max 和 min,然後計算出 mid = (max + min) / 2 

該演算法的實質是尋找最大的k個數中最小的乙個。  

*/int

find(

int* a , 

intx) 

//查詢出大於或者等於x的元素個數    

return

sum ;    

}    

intgetk(

int* a , 

intmax , 

intmin) 

//最終max min之間只會存在乙個或者多個相同的數字    

cout<<"end"

<

return

min ;    

}    

intmain()    

;      

intx = getk(a , 554 , 2) ;     

coutreturn

0 ;        

}  

該演算法在實際應用中效果不佳。

解法五:如果n個數都是正數,取值範圍不太大,可以考慮用空間換時間。申請乙個包括n中最大值的maxn大小的陣列count[maxn],count[i]表示整數i在所有整數中的個數。這樣只要掃瞄一遍陣列,就可以得到第k大的元素。

**如下:

[cpp]view plain

for(sumcount = 0, v = maxn -1; v >=0; v--)    

return

v;   

擴充套件問題:

1.如果需要找出n個數中最大的k個不同的浮點數呢?比如,含有10個浮點數的陣列(1.5,1.5,2.5,3.5,3.5,5,0,- 1.5,3.5)中最大的3個不同的浮點數是(5,3.5,2.5)。

解答:除了解法五不行,其他的都可以。因為最後一種需要是正數。

2. 如果是找第k到第m(0解法三)用堆排序當每乙個網頁權重更新的時候,更新堆。

舉一反三:查詢最小的k個元素

解答:最直觀的方法是用快速排序或堆排序先排好,在取前k小的資料。最好的辦法是利用解法二解法三的原理進行查詢。

k個最小的數

輸入n個整數,輸出k個最小的數。例如輸入4,5,1,6,2,7,3,8這8個數,則最小的4個數字是1,2,3,4。利用堆排序 1 遍歷陣列,將前k個數插入到堆中,使用multiset來實現堆 2 繼續從輸入陣列中讀入元素,作為待插入的整數,並將它與堆中的最大值進行比較,若待插入的值比這個最大值小,則...

查詢最小的k個元素

題目 輸入n個整數,輸出其中最小的k個。演算法思想,要是將n個數排序然後輸出前k個,方法很簡單,但計算量比較大,為o nlogn 要是新建乙個有k個元素陣列,在陣列不滿時,將每乙個輸入的資料存入陣列。若陣列滿了,則比較輸入的資料與陣列中最大元素的大小,來決定接下來幹什麼,接下來幹什麼,我語言表達能力...

查詢最小的k個元素

查詢最小的k個元素 題目 輸入n個整數,輸出其中最小的k個。例如輸入1,2,3,4,5,6,7和8這8個數字,則最小的4個數字為1,2,3和4。1 輸入元素,並排序 2 輸出前k個元素 排序 採用高效的排序方法,如 快速排序,歸併排序等 方法一 採用最簡單的方法 include include in...