題目:
n個無序的數(可能數目非常大),選出其中最大的k個數。
分析與解法:
【解法一】
對n個數進行排序,然後選出最大的k個數。可以使用快速排序或堆排序,時間複雜度為o(nlogn)。
因為這裡n的數目可能非常大,即n>>k,而前n-k個數可以不進行排序,使用可以部分排序的演算法,如選擇排序或交換排序,時間複雜度為o(nk)。
【解法二】
當n的數值很大的時候,即n>>k。可以使用快速排序中的partition函式, 將數分為兩組。
(1)分為兩個組,sa和sb。
(2)若sa組的個數大於k,則繼續在sa分組中找取最大的k個數字 。
(3)若sa組中的數字小於k ,其個數為t,則繼續在sb中找取 k-t個數字 。
時間複雜度為o(nlogk)。
**:
【解法三】#include
using
namespace
std ;
const
int n = 8 ;
const
int k = 4 ;
int partition(int a ,int low , int high)
j++ ;
} //最後處理a[high]
swap(a[i+1] , a[high]) ;
return i + 1;
} int findk(int a , int low , int high , int k)
} int main()
; findk(a , 0 , n - 1 , k) ;
for(int i = 0 ; i < k ; i++)
cout
<"pause") ;
return
0 ;
}
該問題的實質是尋找最大的k個數中最小的那個,也就是第k大的數p,可以先找出n個數中最大的和最小的元素的值,然後使用二分法進行搜尋找到p,再根據p找到其他大於它的k-1個數。其時間複雜度是o(n)+o(nlogn)+o(n),近似後為o(nlogn)。
**:
【解法四】#include using namespace std ;
const int n = 8 ;
const int k = 4 ;
/* 利用二分的方法求取top k問題。
首先查詢 max 和 min,然後計算出 mid = (max + min) / 2
該演算法的實質是尋找最大的k個數中最小的乙個。
*/int find(int * a , int x) //查詢出大於或者等於x的元素個數
return sum ;
} int getk(int * a , int
max , int
min) //最終max min之間只會存在乙個或者多個相同的數字
return
min ;
} int main()
;
int x = getk(a , 554 , 2);
cout << x << " ";
for(int i = 0; i < n; i++)
return
0 ;
}
當n的數值很大的時候,即n>>k。從n個數中任取k個數建立乙個有k個節點的小頂堆,每次從剩下n-k個數中取出乙個數,與堆頂元素進行比較,若小於等於堆頂元素則捨棄,若大於等於堆頂,則將堆頂元素更新為該元素並重新調整堆。維護堆的時間複雜度是o(logk)。所以總的時間複雜度是o(nlogk)。
**:
【解法五】#include
using
namespace
std;
void buildminheap(int *parray, int k);
void adjustheap(int *parray, int rootindex, int heapsize);
int main()
; int k = 5 ;
//建乙個k個元素大小的最小堆
buildminheap(a, k);
//從第k個元素開始掃瞄,看有沒有比根節點更大的節點,若有則替換,並更新堆;若沒有比根節點大則掃瞄下乙個元素,直到陣列結束
for (int i = k; i < sizeof(a) / sizeof(int); i++)
}//列印出前k大的數,沒有排序。
for (int i = 0; i < k; i++)
system("pause");
} //建乙個k個元素大小的最小堆
void buildminheap(int *parray, int k)
}void adjustheap (int *parray, int rootindex, int heapsize)
if ((leftindex < heapsize) && (rightindex >= heapsize) &&
(parray[leftindex] < parray[rootindex]))
if ((rightindex < heapsize) && (parray[rightindex] <
parray[leftindex]) && (parray[rightindex] <
parray[rootindex]))
if (minindex != rootindex)
}
n個數都是正整數,且取值範圍不大的時候。可以使用空間換時間的方法,使用乙個陣列記錄每個元素出現的次數,陣列長度為maxn,為n個數中的最大元素的值,然後找出最大的k個數。時間複雜度為o(n)+o(maxn),近似為o(n)。
**:
擴充套件問題:#include
using namespace std;
int findmaxn(int *parray, int len);
int main()
; int k = 5;
int maxn = findmaxn(a, sizeof(a) / sizeof(int));
//申請乙個count陣列,記錄每乙個數出現的次數
int *count = new
int[maxn + 1]();
for (int i = 0; i < sizeof(a) / sizeof(int); i++)
intindex = maxn;
int sumcount = 0;
for (;index >= 0; index--)
}//列印出最大的k個數
for (int i = maxn; i >= index; i--)
}system("pause");
} //找出乙個陣列中最大的值
int findmaxn(int *parray, int len)
}return maxn;
}
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大的數呢?
5.如第4點所說,對於每個文件d,相對於不同的關鍵字q1, q2, …, qm,分別有相關性權重f(d, q1),f(d, q2), …, f(d, qm)。如果使用者輸入關鍵字qi之後,我們已經獲得了最相關的k個文件,而已知關鍵字qj跟關鍵字qi相似,文件跟這兩個關鍵字的權重大小比較靠近,那麼關鍵字qi的最相關的k個文件,對尋找qj最相關的k個文件有沒有幫助呢?
分析與解法:
1.除了解法五,其他四種方法都可以。還可以利用解法五的思想使用stl中的map儲存每個數出現的次數,之後從大到小掃瞄出k個最大的數,時間複雜度為o(nlogn),空間複雜度為o(n),因為stl中map是由紅黑樹實現的,每次插入的時間複雜度為o(logn)。
2.維護乙個m個節點的小頂堆,然後遍歷找出比第m大的元素小的m-k個元素。
3.4.5.解法見參考博文。
文章及**參考以下博文:
程式設計之美 尋找最大的K個數
有很多個無序數,我們姑且假定他們各不相等,怎麼挑選出其中最大的若干個數呢?如果這個資料量很大,比如1億個,如果所存資料是浮點型呢?我們該怎麼處理呢?分兩部分,第一部分是我個人的解答,第二部分是書上的解答 第一部分 1,如果這個問題裡的資料都是整數,這個問題利用hash對映應該很簡單,就是在開闢乙個陣...
程式設計之美 尋找最大的K個數
有很多個無序數,我們姑且假定他們各不相等,怎麼挑選出其中最大的若干個數呢?如果這個資料量很大,比如1億個,如果所存資料是浮點型呢?我們該怎麼處理呢?分兩部分,第一部分是我個人的解答,第二部分是書上的解答 第一部分 1,如果這個問題裡的資料都是整數,這個問題利用hash對映應該很簡單,就是在開闢乙個陣...
程式設計之美 尋找最大的k個數
有很多無序的數,我們姑且假定它們各不相等,怎麼選出其中最大的若干個數呢?idea 1 先用快速排序或者堆排序進行排序,然後取出最大的k個數,時間複雜度為o nlogn o k o nlogn idea 2 進行k趟最大冒泡或者k次大頂堆的輸出,時間複雜度為o n k 根據k與logn的大小比較,選取...