用bootstrap基礎(chǔ)教程做的網(wǎng)站百度熱詞指數(shù)
在數(shù)據(jù)結(jié)構(gòu)之《二叉樹》(上)中學(xué)習(xí)了樹的相關(guān)概念,還了解的樹中的二叉樹的順序結(jié)構(gòu)和鏈式結(jié)構(gòu),在本篇中我們將重點學(xué)習(xí)二叉樹中的堆的相關(guān)概念與性質(zhì),同時試著實現(xiàn)堆中的相關(guān)方法,一起加油吧!
1.實現(xiàn)順序結(jié)構(gòu)二叉樹
在實現(xiàn)順序結(jié)構(gòu)的二叉樹中通常把堆使用順序結(jié)構(gòu)的數(shù)組來存儲,因此我們先要了解堆的概念與結(jié)構(gòu)
1.1 堆的概念與結(jié)構(gòu)
如果有一個關(guān)鍵碼的集合 K??= ?{k0 , k1 , k2 , ...,kn?1 } ,把它的所有元素按完全二叉樹的順序存儲方式式存儲,在?個?維數(shù)組中,并滿足: Ki??<= ?K2?i+1 ( Ki >= K2?i+1 且 Ki??<= ?K2?i+2 ),i = 0、1、2... ,則稱為小堆(或大堆)。將根結(jié)點最大的堆叫做最大堆或大根堆,根結(jié)點最小的堆叫做最小堆或小根堆。
以上的堆的概念簡單來說就是堆是完全二叉樹,在大堆中根結(jié)點為最大的元素,在此之后的每個孩子結(jié)點都要小于或者等于它的父結(jié)點;在小堆中根結(jié)點為最小的元素,在此之后的每個孩子結(jié)點都要大于或者等于的父結(jié)點
因此通過堆的概念的了解需要知道堆有以下的性質(zhì):
? 堆中某個結(jié)點的值總是不大于或不小于其父結(jié)點的值
? 堆總是一棵完全二叉樹
例如以下圖示就是大堆,并且將其結(jié)點的數(shù)據(jù)存儲到數(shù)組當中
以下圖示就是小堆,并且將其結(jié)點的數(shù)據(jù)存儲到數(shù)組當中
1.2二叉樹的性質(zhì)?
在了解的堆的相關(guān)概念和結(jié)構(gòu)后,之后我們要來實現(xiàn)堆,因此在此之前還要再了解二叉樹的相關(guān)性質(zhì)
💡 二叉樹性質(zhì)
? 對于具有 n 個結(jié)點的完全二叉樹,如果按照從上至下從左至右的數(shù)組順序?qū)λ薪Y(jié)點從0 開始編號,則對于序號為 i 的結(jié)點有:
1. 若 i>0 ,i 位置結(jié)點的雙親序號:; i=0 , i 為根結(jié)點編號,無雙親結(jié)點
2. 若 2i+1<n ,左孩?序號:, 2i+1>=n 否則無左孩子
3. 若 2i+2<n ,右孩?序號:, 2i+2>=n 否則無右孩子
對以上的性質(zhì)來通過一個示例來加深理解
以下就是根據(jù)二叉樹的性質(zhì)來求出各節(jié)點的孩子結(jié)點以及父結(jié)點的圖示
1.3堆的實現(xiàn)
在了解了堆相關(guān)的性質(zhì)與結(jié)構(gòu)后接下來就可以來試著實現(xiàn)堆
在實現(xiàn)堆的程序中還是將代碼分為三個文件
1.堆結(jié)構(gòu)的定義?
//堆結(jié)構(gòu)的定義
typedef int HDataType;
typedef struct Heap
{HDataType* arr;int size;//有效數(shù)據(jù)個數(shù)int capacity;//空間大小
}Heap;
在定義堆的結(jié)構(gòu)中,使用一個結(jié)構(gòu)體來定義堆的結(jié)構(gòu),里面的成員變量和順序表中相同,arr為數(shù)組首元素的指針,size為數(shù)組中有效元素的個數(shù),capacity為數(shù)組空間的大小
2.堆的初始化
在堆的初始化函數(shù)的實現(xiàn)中先要在Heap.h內(nèi)完成初始化函數(shù)的聲明
//初始化堆
void HeapInit(Heap* php);
將該函數(shù)命名為HeapInit,函數(shù)的參數(shù)為結(jié)構(gòu)體指針
在完成了函數(shù)的聲明后就是在Heap.c內(nèi)完成函數(shù)的定義
//初始化堆
void HeapInit(Heap* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}
3.堆的插入
?在堆的插入函數(shù)的實現(xiàn)中先要在Heap.h內(nèi)完成插入函數(shù)的聲明
//堆的插入
void HeapPush(Heap* php,HDataType x);
將該函數(shù)命名為HeapPush,函數(shù)參數(shù)有兩個,第一個為結(jié)構(gòu)體指針,第二個為要插入的數(shù)據(jù)
在完成了函數(shù)的聲明后就是在Heap.c內(nèi)完成函數(shù)的定義
在堆的插入當中我們要實現(xiàn)的是在堆的末尾插入數(shù)據(jù),也就是在數(shù)組的size-1位置插入新的數(shù)據(jù),并且在插入之后由于堆的特性還要將堆中各元素的位置進行調(diào)整,使得其變?yōu)橐粋€小堆或者大堆
因此在插入函數(shù)中我們先要實現(xiàn)向上調(diào)整的函數(shù)
3.1 向上調(diào)整法?
要實現(xiàn)堆中的向上調(diào)整結(jié)點的函數(shù)首先要來分析在調(diào)整過程中需要實現(xiàn)哪些步驟,我們來看下面這個堆的示例
在這個小堆中我們先在堆的末尾插入數(shù)據(jù)為10的結(jié)點,在這之后要將該二叉樹重新調(diào)整為小堆需要哪些操作呢?
首先就是要將新插入的結(jié)點10與它的父結(jié)點做對比,如果比父結(jié)點大就和父結(jié)點交換之后再和父結(jié)點的父結(jié)點比較重復(fù)以上操作直到最后父結(jié)點為0時就停止;在此過程中如果比父結(jié)點小就不進行交換
所以以上的二叉樹要調(diào)整為小堆就要經(jīng)過以下的步驟?
在完全向上調(diào)整實例的分析后接下來就可以來實現(xiàn)向上調(diào)整的代碼?
先再Heap.h內(nèi)對向上調(diào)整函數(shù)進行聲明,通過以上的分析就可以推測出函數(shù)的參數(shù)有兩個,一個為數(shù)組的首元素指針,另一個為數(shù)組的有效元素個數(shù)?
//向上調(diào)整法 void AdjustUp(HDataType* arr, int child);
在以下的代碼當中parent就來表示父結(jié)點的數(shù)組下標,child就來表示孩子節(jié)點的下標,因此在知道孩子結(jié)點的下標時要求父結(jié)點的下標就可以用到前面提到的二叉樹的性質(zhì),父結(jié)點的下標等于其孩子結(jié)點下標減一再除以二
//向上調(diào)整法
void AdjustUp(HDataType* arr, int child)
{int parent = (child - 1) / 2;//求父結(jié)點下標while (child>0){//小堆時下面的if判斷部分就用<//大堆時下面的if判斷部分就用>if (arr[child] < arr[parent])//若父結(jié)點大于孩子結(jié)點就交換{Swap(&arr[child], & arr[parent]);child = parent;parent= (child - 1) / 2;}else{break;//若不符合以上if的條件就退出循環(huán)}}
}
例如以上的示例中在以下代碼中parent和child的變化就如以下圖所示
在以上函數(shù)中的Swap是來實現(xiàn)兩個數(shù)的交換,以下是函數(shù)的定義
void Swap(HDataType* p1, HDataType* p2) {HDataType* tmp = *p1;*p1 = *p2;*p2 = tmp; }
3.2 插入函數(shù)代碼實現(xiàn)?
在完成了向上調(diào)整的代碼后接下來就可以來完成堆插入函數(shù)的實現(xiàn)
在插入函數(shù)中由于php為結(jié)構(gòu)體指針,因此php不能為空,所以要將php進行assert斷言
并且在插入之前也要判斷數(shù)組空間是否滿了,滿了就要對空間進行調(diào)整,在此的調(diào)整代碼和順序表中相同
之后再將數(shù)據(jù)尾插到數(shù)組當中,再進行向上調(diào)整,最后切記要將size+1
//堆的插入
void HeapPush(Heap* php, HDataType x)
{assert(php);if (php->size == php->capacity)//對數(shù)組空間進行調(diào)整{int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;Heap* tmp = (Heap*)realloc(php->arr,sizeof(HDataType) * newcapacity);if (tmp == NULL){perror("malloc file");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;//進行尾插AdjustUp(php->arr, php->size);//進行向上調(diào)整php->size++;
}
4.堆的刪除?
在堆的刪除函數(shù)的實現(xiàn)中先要在Heap.h內(nèi)完成刪除函數(shù)的聲明
//堆的刪除
void HeapPop(Heap* php);
將該函數(shù)命名為HeapPop,函數(shù)的參數(shù)是結(jié)構(gòu)體指針
在完成了函數(shù)的聲明后就是在Heap.c內(nèi)完成函數(shù)的定義
在堆的刪除中是將跟結(jié)點給刪除,在刪除過程中是將根結(jié)點和最后一個結(jié)點交換,之后將數(shù)組的有效個數(shù)減一這樣就將原來的根結(jié)點給刪除了,之后再進行各結(jié)點的調(diào)整使其重新變?yōu)橐粋€堆
因此在插入函數(shù)中我們先要實現(xiàn)向下調(diào)整的函數(shù)
4.1向下調(diào)整法
要實現(xiàn)堆中的向下調(diào)整結(jié)點的函數(shù)首先要來分析在調(diào)整過程中需要實現(xiàn)哪些步驟,我們來看下面這個堆的示例
在這個小堆中我們?nèi)绻呀?jīng)跟結(jié)點刪除,在這之后要將該二叉樹重新調(diào)整為小堆需要哪些操作呢?
?
首先就是要將新的的跟結(jié)點70與它的兩個孩子結(jié)點中小的做對比,如果比該孩子結(jié)點大就和該孩子結(jié)點交換之后再和該孩子結(jié)點的兩的孩子結(jié)點中小的比較重復(fù)以上操作直到最后開始的跟結(jié)點為葉子節(jié)點時就停止;如果在這過程中比孩子結(jié)點中小的那個還小就不進行交換
所以以上的二叉樹要調(diào)整為小堆就要經(jīng)過以下的步驟?
在完全向下調(diào)整實例的分析后接下來就可以來實現(xiàn)向下調(diào)整的代碼?
?先再Heap.h內(nèi)對向上調(diào)整函數(shù)進行聲明,通過以上的分析就可以推測出函數(shù)的參數(shù)有三個,一個為數(shù)組的首元素指針,另一個為數(shù)組的有效元素個數(shù)?,最后一個是要向下調(diào)整的元素的數(shù)組下標
//向下調(diào)整法 void AdjustDown(HDataType* arr, int n, int parent);
之后就是在Heap.c內(nèi)完成函數(shù)的定義
在以下的代碼當中parent就來表示父結(jié)點的數(shù)組下標,child就來表示孩子節(jié)點的下標,因此在知道父結(jié)點的下標時要求孩子結(jié)點的下標就可以用到前面提到的二叉樹的性質(zhì),孩子結(jié)點的下標等于其父結(jié)點下標乘二再加一或二
while循環(huán)中當孩子節(jié)點下標大于數(shù)組有效個數(shù)也就是父節(jié)點為葉子節(jié)點時就退出循環(huán),因此進入while條件為child<n
//向下調(diào)整法
void AdjustDown(HDataType* arr, int n, int parent)
{int child = 2 * parent + 1;//孩子節(jié)點的下標while (child<n){//如果為小堆 以下的if判斷部分就用>//如果為大堆 以下的if判斷部分就用<if (child+1<n && arr[child] > arr[child + 1]){child++;//若為小堆就找孩子節(jié)點中小的,大堆就找孩子節(jié)點中大的}if (arr[parent] > arr[child])//若孩子節(jié)點小于父節(jié)點就交換{Swap(&arr[parent], &arr[child]);parent = child;child= 2 * parent + 1;}else{break;//若不符合以上if的條件就退出循環(huán)}}
}
例如以上的示例中在以下代碼中parent和child的變化就如以下圖所示
?4.2刪除函數(shù)代碼實現(xiàn)
在插入函數(shù)中由于php為結(jié)構(gòu)體指針,因此php不能為空,所以要將php進行assert斷言
在刪除函數(shù)中因為要刪除的是堆的根結(jié)點,所以先將堆的根結(jié)點和堆的最后一個節(jié)點進行交換,之后再將size-1,這樣就可以讓數(shù)組當中的有效個數(shù)減一,使得原來的根結(jié)點被刪除,之后再將該二叉樹使用向下調(diào)整重新調(diào)整為堆
//堆的刪除
void HeapPop(Heap* php)
{assert(php && php->size);php->arr[0] = php->arr[php->size - 1];--php->size;AdjustDown(php->arr, php->size, 0);
}
5.取堆頂?shù)脑?
在堆的取堆頂?shù)脑睾瘮?shù)的實現(xiàn)中先要在Heap.h內(nèi)完成取堆頂?shù)脑?/strong>函數(shù)的聲明
//取堆頂?shù)脑?HDataType HeapTop(Heap* php);
將該函數(shù)命名為HeapTop,函數(shù)的參數(shù)是結(jié)構(gòu)體指針,函數(shù)的返回值是堆跟結(jié)點內(nèi)的數(shù)據(jù)
在完成了函數(shù)的聲明后就是在Heap.c內(nèi)完成函數(shù)的定義
//取堆頂?shù)脑?HDataType HeapTop(Heap* php)
{assert(php);return php->arr[0];
}
6.堆的數(shù)據(jù)個數(shù)?
在求堆的數(shù)據(jù)個數(shù)函數(shù)的實現(xiàn)中先要在Heap.h內(nèi)完成堆的數(shù)據(jù)個數(shù)函數(shù)的聲明
//堆的數(shù)據(jù)個數(shù)
int HeapSize(Heap* php);
將該函數(shù)命名為HeapSize,函數(shù)的參數(shù)是結(jié)構(gòu)體指針,函數(shù)的返回值是堆的結(jié)點結(jié)點個數(shù)
在完成了函數(shù)的聲明后就是在Heap.c內(nèi)完成函數(shù)的定義
因為堆是用數(shù)組來實現(xiàn)的,所以堆中的結(jié)點個數(shù)就為數(shù)組的有效元素個數(shù)
//堆的數(shù)據(jù)個數(shù)
int HeapSize(Heap* php)
{assert(php);return php->size;
}
7.堆的判空
在判斷堆是否為空函數(shù)的實現(xiàn)中先要在Heap.h內(nèi)完成判斷堆是否為空函數(shù)的聲明
//堆的判空
bool HeapEmpty(Heap* php);
將該函數(shù)命名為HeapEmpty,函數(shù)的參數(shù)是結(jié)構(gòu)體指針,函數(shù)的返回類型是布爾類型
在完成了函數(shù)的聲明后就是在Heap.c內(nèi)完成函數(shù)的定義
在該函數(shù)中當當堆為空時就返回true,不為空時返回false
//堆的判空
bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}
8.堆的銷毀
在堆的銷毀函數(shù)的實現(xiàn)中先要在Heap.h內(nèi)完成堆的銷毀函數(shù)的聲明
//銷毀堆
void HeapDestory(Heap* php);
將該函數(shù)命名為HeapDestory,函數(shù)的參數(shù)是結(jié)構(gòu)體指針
在完成了函數(shù)的聲明后就是在Heap.c內(nèi)完成函數(shù)的定義
//銷毀堆
void HeapDestory(Heap* php)
{assert(php);if (php->arr){free(php->arr);}php->arr = NULL;php->size = php->capacity = 0;
}
?9.堆實現(xiàn)完整代碼
Heap.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//堆結(jié)構(gòu)的定義
typedef int HDataType;
typedef struct Heap
{HDataType* arr;int size;//有效數(shù)據(jù)個數(shù)int capacity;//空間大小
}Heap;//初始化堆
void HeapInit(Heap* php);
//銷毀堆
void HeapDestory(Heap* php);
//堆的插入
void HeapPush(Heap* php,HDataType x);
//堆的刪除
void HeapPop(Heap* php);
//取堆頂?shù)脑?HDataType HeapTop(Heap* php);
//堆的數(shù)據(jù)個數(shù)
int HeapSize(Heap* php);
//堆的判空
bool HeapEmpty(Heap* php);
//向上調(diào)整法
void AdjustUp(HDataType* arr, int child);
//向下調(diào)整法
void AdjustDown(HDataType* arr, int n, int parent);
Heap.c
#include"Heap.h"void Swap(HDataType* p1, HDataType* p2)
{HDataType* tmp = *p1;*p1 = *p2;*p2 = tmp;
}//向上調(diào)整法
void AdjustUp(HDataType* arr, int child)
{int parent = (child - 1) / 2;while (child>0){//小堆 <//大堆 >if (arr[child] < arr[parent]){Swap(&arr[child], & arr[parent]);child = parent;parent= (child - 1) / 2;}else{break;}}}//向下調(diào)整法
void AdjustDown(HDataType* arr, int n, int parent)
{int child = 2 * parent + 1;while (child<n){//小堆 >//大堆 <if (child+1<n && arr[child] > arr[child + 1]){child++;}if (arr[parent] > arr[child]){Swap(&arr[parent], &arr[child]);parent = child;child= 2 * parent + 1;}else{break;}}
}//初始化堆
void HeapInit(Heap* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}//銷毀堆
void HeapDestory(Heap* php)
{assert(php);if (php->arr){free(php->arr);}php->arr = NULL;php->size = php->capacity = 0;
}//堆的插入
void HeapPush(Heap* php, HDataType x)
{assert(php);if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;Heap* tmp = (Heap*)realloc(php->arr,sizeof(HDataType) * newcapacity);if (tmp == NULL){perror("malloc file");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;AdjustUp(php->arr, php->size);php->size++;
}//堆的刪除
void HeapPop(Heap* php)
{assert(php && php->size);php->arr[0] = php->arr[php->size - 1];--php->size;AdjustDown(php->arr, php->size, 0);
}//取堆頂?shù)脑?HDataType HeapTop(Heap* php)
{assert(php);return php->arr[0];
}//堆的數(shù)據(jù)個數(shù)
int HeapSize(Heap* php)
{assert(php);return php->size;
}//堆的判空
bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}
?2.堆的應(yīng)用
1.堆排序
在之前的C語言的學(xué)習(xí)當中我們實現(xiàn)了冒泡排序,但在算法的復(fù)雜度中得出了冒泡排序的時間復(fù)雜度為O(n^2),因此其實冒泡排序的效率是不高的。接下來我們就要來學(xué)習(xí)一種使用堆來實現(xiàn)排序的算法。
在此之前通過學(xué)習(xí)堆的相關(guān)概念知道了在小堆中的根結(jié)點是堆中最小的,那么在小堆中只要一直取堆的根結(jié)點就可以得到升序的數(shù)據(jù),以下就是使用這種方法來實現(xiàn)的堆排序
void HeapSort(int* a, int n)
{Heap hp;for(int i = 0; i < n; i++){HeapPush(&hp,a[i]);}int i = 0;while (!HeapEmpty(&hp)){a[i++] = HeapTop(&hp);HeapPop(&hp);}HeapDestroy(&hp);
}
但是在以上的這種算法中需要將數(shù)組中的數(shù)據(jù)先要先存儲在堆當中才能在之后得到堆頂?shù)臄?shù)據(jù),因此以上這種方法的空間復(fù)雜度就為O(n),那么有什么方法能在不申請新的空間下來實現(xiàn)堆排序呢?接下來就來看以下的這種基于原來數(shù)組建堆的方法
//堆排序算法
void HeapSort(int* arr, int n)
{for (int i = 0; i < n; i++){AdjustUp(arr, i);}int end = n - 1;while (end > 0){Swap(&arr[end], &arr[0]);AdjustDown(arr, end, 0);end--;}for (int i = 0; i < n; i++){printf("%d ", arr[i]);}
}
在以上這種堆排序中先用向上調(diào)整法來將數(shù)組通過循環(huán)的將數(shù)組數(shù)組調(diào)整為堆,根據(jù)之前向上調(diào)整的代碼在此的建的是小堆。之后的while循環(huán)中實現(xiàn)的是將小堆中的跟結(jié)點和尾結(jié)點進行交換這時數(shù)組中最小的元素就排在了數(shù)組的末尾之后再進行向下排序就可重新變?yōu)樾《?#xff0c;一直重復(fù)以上的操作就可以將數(shù)組的元素從小到大依次移動到數(shù)組末尾,最終原數(shù)組就變?yōu)樯虻牧恕?/span>
那么以上除了使用向上調(diào)整建堆外,其實使用向下調(diào)整法也可以建堆,以下是使用向下調(diào)整法建堆的代碼
//堆排序算法
void HeapSort(int* arr, int n)
{for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, n, i);}int end = n - 1;while (end > 0){Swap(&arr[end], &arr[0]);AdjustDown(arr, end, 0);end--;}for (int i = 0; i < n; i++){printf("%d ", arr[i]);}
}
這兩種那一這種效率更好呢,這就需要來分析向上建堆和向下建堆的時間復(fù)雜度
先來看向上調(diào)整法
因為堆是完全二叉樹,而滿二叉樹也是完全二叉樹,此處為了簡化使用滿二叉樹來證明(時間復(fù)雜度本來看的就是近似值,多幾個結(jié)點不影響最終結(jié)果)
以下是各層的結(jié)點在向上調(diào)整過程中各層結(jié)點在調(diào)整過程中最壞的情況下移動的層數(shù)
需要移動結(jié)點總的移動步數(shù)為:每層結(jié)點個數(shù) * 向上調(diào)整次數(shù)(第?層調(diào)整次數(shù)為0)
由此可得:
💡 向上調(diào)整算法建堆時間復(fù)雜度為: O(n ? log2 n)
接下來看向下調(diào)整法
以下是各層的結(jié)點在向下調(diào)整過程中各層結(jié)點在調(diào)整過程中最壞的情況下移動的層數(shù)
則需要移動結(jié)點總的移動步數(shù)為:每層結(jié)點個數(shù) * 向下調(diào)整次數(shù)?
💡 向下調(diào)整算法建堆時間復(fù)雜度為: O(n)?
通過以上的分析后可以得出在堆排序中使用向下調(diào)整法建堆更好
堆排序第二個循環(huán)中的向下調(diào)整與建堆中的向上調(diào)整算法時間復(fù)雜度計算一致。因此堆排序的時間復(fù)雜度為 O(n + n ? log n) ,即 O(n log n)
?
?
2.TOP-K問題
TOP-K問題:即求數(shù)據(jù)結(jié)合中前K個最大的元素或者最小的元素,一般情況下數(shù)據(jù)量都比較大。
比如:專業(yè)前10名、世界500強、富豪榜、游戲中前100的活躍玩家等。
對于Top-K問題,能想到的最簡單直接的方式就是排序,但是:如果數(shù)據(jù)量非常大,排序就不太可取了(可能數(shù)據(jù)都不能一下?全部加載到內(nèi)存中)。最佳的方式就是用堆來解決,基本思路如下:
(1)用數(shù)據(jù)集合中前K個元素來建堆
? ? ? 前k個最大的元素,則建小堆
? ? ? 前k個最小的元素,則建大堆
(2)用剩余的N-K個元素依次與堆頂元素來比較,不滿足則替換堆頂元素
將剩余N-K個元素依次與堆頂元素比完之后,堆中剩余的K個元素就是所求的前K個最小或者大的元素
以下這段代碼可以實現(xiàn)將大量的整型數(shù)據(jù)輸入到data.txt的文件當中
void CreateNDate()
{// 造數(shù)據(jù)int n = 100000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (int i = 0; i < n; ++i){int x = (rand() + i) % 1000000;fprintf(fin, "%d\n", x);}fclose(fin);
}
?接下來就來實現(xiàn)解決TOP-K問題的代碼,以下是實現(xiàn)的是得出數(shù)據(jù)結(jié)合中前K個最大的元素
void topk()
{int k=0;printf("k:");scanf("%d", &k);//讀取文件中的前k個數(shù)據(jù)FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen fail");exit(1);}int* pa = (int*)malloc(k * sizeof(int));if (pa == NULL){perror("malloc fail");exit(2);}for (int i = 0; i < k; i++){fscanf(pf, "%d", &pa[i]);}//使用前k個數(shù)據(jù)建堆for (int i = (k - 1 - 1) / 2; i >= 0; i--){AdjustDown(pa, k, i);}int tmp=0;//循環(huán)將k個數(shù)據(jù)之后的數(shù)據(jù)堆中最小的元素比較,若比這個元素大就交換while (fscanf(pf, "%d", &tmp) != EOF){if (tmp > pa[0]){pa[0] = tmp;AdjustDown(pa, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", pa[i]);}fclose(pf);pf = NULL;}
?
?
以上就是二叉樹(中)的全部內(nèi)容了,接下來在二叉樹(下)將繼續(xù)學(xué)習(xí)二叉樹的知識,在下一篇中我們將重點學(xué)習(xí)鏈式結(jié)構(gòu)的二叉樹的相關(guān)知識,未完待續(xù)……?