国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

堆(兩種建堆方法)、堆排序和Top-K問題

這篇具有很好參考價值的文章主要介紹了堆(兩種建堆方法)、堆排序和Top-K問題。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。


是一種完全二叉樹,分為大堆,小堆

如果有一個關(guān)鍵碼的集合 int K[ ] = {27,15,19,18,28,34,65,49,25,37} ; 把它的所有元素按完全二叉樹的順序存儲方式存儲
在一個一維數(shù)組中,并滿足:Ki <=K2*i+1 且Ki <=K2*i+2 ( Ki >=K2*i+1 且Ki >=K2*i+2) i = 0,1,
2…,則稱為小堆(或大堆)。將根節(jié)點最大的堆叫做最大堆或大根堆,根節(jié)點最小的堆叫做最小堆或小根堆。

堆的性質(zhì):

  • 堆中某個節(jié)點的值總是不大于或不小于其父節(jié)點的值;
  • 堆總是一棵完全二叉樹。

堆(兩種建堆方法)、堆排序和Top-K問題

建堆的方式

有兩種,1.向上建堆,2.向下建堆

向上建堆

時間復(fù)雜度為:O(N*logN)

typedef int HPDataType;
//交換函數(shù)
void Swap(HPDataType* a, HPDataType* b) {
	HPDataType temp = *a;
	*a = *b;
	*b = temp;
}
//向上調(diào)整的條件是:調(diào)整的child結(jié)點之前的結(jié)點應(yīng)該也為大堆/小堆,所以,向上調(diào)整建堆的時候,是從數(shù)組的第一個元素開始
void ADjustUp(HPDataType* a, int child) {
	//向上調(diào)整
	int parent = (child - 1)/2;
	while (child >0) //child!=0
	{
		if (a[child] > a[parent]) 
		{
			Swap(&a[child], &a[parent]);//取地址進行交換
			//進行交換
			child = parent;
			parent = (child - 1) /2;
		}
		else {
			break;
		}
		
	}
}
//向上建堆
//for (int i = 0; i < n; i++) {
//	ADjustUp(arr, i);
//}
int main(){
    int arr[]={23,23,1,32,12,3,12,31,324};
    int n=sizeof(arr)/sizeof(arr[0]);//得到數(shù)組的個數(shù)大小
    //n-1表示為數(shù)組最后一個元素的下標,(n-1-1)/2得到其父節(jié)點,從父結(jié)點開始,向下調(diào)整
	for (int i = 0; i < n; i++) {
		ADjustUp(arr, i);
    }
}

向下建堆

時間復(fù)雜堆為:O(N)

typedef int HPDataType;
//交換函數(shù)
void Swap(HPDataType* a, HPDataType* b) {
	HPDataType temp = *a;
	*a = *b;
	*b = temp;
}
//向下調(diào)整建堆的條件為:左右子樹都為大堆/小堆,所以從最后一個結(jié)點開始,又因為如果是葉子結(jié)點的話,本身調(diào)整是沒有意義的,所以就從倒數(shù)第二層開始
void AdjustDown(HPDataType* a, int n, int parent) {
	int child = parent * 2 + 1;//先找到左孩子的位置
	while (child < n) {
		//從左右孩子里面選一個
		if (child + 1 < n && a[child + 1] > a[child]) {
			//如果右孩子存在,且,右孩子大于左孩子
			child++;//
		}
		if (a[child] > a[parent]) {
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			break;
		}
	}
}
//向下建堆
int main(){
    int arr[]={23,23,1,32,12,3,12,31,324};
    int n=sizeof(arr)/sizeof(arr[0]);//得到數(shù)組的個數(shù)大小
    //n-1表示為數(shù)組最后一個元素的下標,(n-1-1)/2得到其父節(jié)點,從父結(jié)點開始,向下調(diào)整
 	for (int i = (n - 1 - 1) / 2; i >= 0; i--) {
		AdjustDown(arr, n, i);
	}   
}

計算兩種方式的時間復(fù)雜度

我們可以看到的是向下調(diào)整建堆時間復(fù)雜度更小,我們通過每一層的每一個結(jié)點的最多需要調(diào)整的次數(shù)為依據(jù),假設(shè)高度為h,如果是向上調(diào)整的話,最后一個結(jié)點(最后一層,也就是2^(h-1)個結(jié)點),最多調(diào)整h-1次,向上的時候需要調(diào)整的每一層結(jié)點變少,層數(shù)變少,所以最后肯定是總調(diào)整次數(shù)更多

如圖所示:這個圖可以從數(shù)學的角度上來解決這個問題

堆(兩種建堆方法)、堆排序和Top-K問題

堆排序

采用向上或者是向下調(diào)整的方法,最后實現(xiàn)排序,最后的時間復(fù)雜度為:O(N*logN)

//所以不管是向上還是向下建堆,時間復(fù)雜度都是為O(N*logN)
void HeapSort(int arr[],int n){
    //比較偏向于向下調(diào)整建堆 時間復(fù)雜度:O(N)
    for(int i=(n-1-1)/2;i>=0;i--){
        AdjustDown(arr,n,i);
    }
    //向上建堆,時間復(fù)雜度為 O(N*logN)
    //for (int i = 0; i < n; i++) {
	//	ADjustUp(arr, i);
	//}
    //進行排序,如果想要升序,那就建大堆,然后進行向下調(diào)整
    int end = n - 1;//從最后一個結(jié)點開始
	while (end>0) {
		Swap(&arr[end], &arr[0]);
		AdjustDown(arr, end, 0);//向下調(diào)整
		end--;
	}
	for (int i = 0; i < n; i++) {
		printf("%d ", arr[i]);
	}
}
int main()
{
    int arr[]={2,231,2,3,12,312,3,123,423,4,2};
    int n=sizeof(arr)/sizeof(arr[0]);
    HeapSort(arr,n);
    return0;
}

Top-K問題

Top-K問題:從N個數(shù)據(jù)中找出前k個最大/最小的數(shù)

當然,在N的數(shù)值不是很大的時候,我們可以使用堆排序的方法,找到對應(yīng)的前k個數(shù)值,但是當N的個數(shù)為10億,或者是100億的時候,這個數(shù)據(jù)的處理量就不能用原本的簡單的堆排序方法啦

思路如下

假如說是100億個數(shù)據(jù)量,我們由以下的內(nèi)存換算可知,100億個整數(shù)的數(shù)據(jù)量就是將近40G,所以不能單純用簡單的堆排序來運算

新的思路如下:

  • 建立一個大小為k的數(shù)組,將前k個數(shù)值建成一個小堆
  • 最后遍歷剩余的數(shù)值,對于每一個數(shù)值,都進行向下調(diào)整(如果是這個數(shù)值大于頂部結(jié)點,就代替他,并進行向下調(diào)整)
  • 最后這個小堆的數(shù)據(jù)就是最大的前k個
void AdjustDown(int* a, int n,int parent) {
    int child = parent * 2 + 1;//先找到左孩子
    while (child < n) {//對于小堆的向下調(diào)整
        if (child + 1 < n && a[child + 1] < a[child]) {
            //如果右孩子大于左孩子,那么就進行交換
            child++;
        }
        if (a[child] < a[parent]) {
            int temp = a[child];
            a[child] = a[parent];
            a[parent] = temp;
            parent = child;
            child = parent * 2 + 1;
        }
        else {
            break;
        }
    }
}
void PrintTopK(const char*file,int k){
    //輸出前k個數(shù)值
    //1.我們先創(chuàng)建為k個數(shù)據(jù)的小堆
    int*topK=(int*)malloc(sizeof(int)*k);
    assert(topK);
    FILE*fout=fopen(file,"r");//讀取文件 file
    if(fout==NULL){
        perror("open fail");
        return;
    }
    
    //讀取前k個數(shù)值做小堆
    for(int i=0;i<k;i++){
        fscanf(fout,"%d",&topK[i]);//讀取前k個數(shù)值在數(shù)組topK中
    }
    for(int i=(k-2)/2;i>=0;i--){
        AdjustDown(topK,k,i);//將topK數(shù)組建立小堆
    }
    
    //2.將剩余的n-k個元素依次于對頂元素進行交換,不滿則替換
    int val=0;
    int ret=fscanf(fout,"%d",&val);
    while(ret!=EOF){//如果文件中的數(shù)值全部被讀取,那么就會返回EOF
        if(val>topK[0]){
            topK[0]=val;//直接賦值即可,不必要交換
            AdjustDown(topK,k,0);//向下調(diào)整
        }
        ret=fscanf(fout,"%d",&val);
    }
    for(int i=0;i<k;i++){
        printf("%d",topK[i]);//打印topK數(shù)組中k個數(shù)值,這就是文件中n個數(shù)值中最大的前k個數(shù)
    }
    print("\n");
    free(topK);
    fclose(fout);
}
void CreateNData(){
    //創(chuàng)建N個數(shù)據(jù)
    int n=1000000;
    srand(time(0));//使用隨機數(shù)前要i使用這個代碼
    const char*file="data.txt";
    FILE*fin=fopen(file,"w");
    if(fin==NULL){
        perror("fopen fail");
        return;
    }
    
    for(int i=0;i<n;i++){
        int x=rand()%10000;
        fprintf(fin,"%d\n",x);//讀寫n個數(shù)據(jù)在fin所對應(yīng)的“data.txt”文件中
    }
    fclose(fin);//關(guān)閉文件,這樣才能正確的在硬盤文件中“data.txt”存儲數(shù)值
}
int main()
{
    CreateNData();
    PrintTopK("data.txt",10);//表示從文件data.txt中讀取前10個最大的數(shù)值
    return 0;
}

實驗結(jié)果如下:成功讀取出來1000000隨機數(shù)中前k個最大的數(shù)字

堆(兩種建堆方法)、堆排序和Top-K問題

本文主要是對于堆的定義,堆的兩種創(chuàng)建方式,向上建堆和向下建堆,進行代碼演示,并對其分析時間復(fù)雜度,且實現(xiàn)了堆排序,實際上堆排序就是先建立堆(向上向下都可,建議向下),然后使用while循環(huán),循環(huán)n-1次對于頭尾進行交換,然后進行向下調(diào)整,上文有詳解,最后是對于Top-K堆問題的解釋,并附上代碼進行演示文章來源地址http://www.zghlxwxcb.cn/news/detail-407961.html

到了這里,關(guān)于堆(兩種建堆方法)、堆排序和Top-K問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包