再写堆(堆的性质,向下调整,建堆,堆的插入删除初始化,堆排序,TopK问题)
生活随笔
收集整理的這篇文章主要介紹了
再写堆(堆的性质,向下调整,建堆,堆的插入删除初始化,堆排序,TopK问题)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
堆的概念
如果有一個關鍵碼的集合K={k0,k1,k2,…,kn-1},把它的所有元素按完全二叉樹的順序存儲方式存儲再一個一維數組中,并滿足:Ki<=K2i+1且Ki<=K2i+1(Ki >= K2i+1 且 Ki >= K2i+2),i=0,1,2,3…。則稱為小堆(或大堆)。將根結點最大的堆叫做最大堆或大根堆,根結點最小的堆叫做最小堆或小根堆
堆的性質
堆的向下調整算法
順序存儲的完全二叉樹
已知[parent] [left] = 2*[parent]+1; [right] = 2*[parent]+2;已知[child] 無論左右 [parent] = ([child]-1)/2基本步驟
建立小堆
代碼實現
void AdjustDown(int array[], int size, int root) {//判斷 root 是否是葉子結點//因為 堆是完全二叉樹,所以沒有左孩子一定沒有右孩子//又因為堆是順序存儲的//所以,找到左孩子的下標,如果左孩子的下標越界了,則沒有左孩子while (1){int left = 2 * root + 1;if (left >= size){//越界了,就是葉子結點return;}//走完上面一定有左孩子,判讀是否有右孩子//找到左右孩子最小的一個int right = 2 * root +2;int min = left;//最開始就認為最小的值為左孩子if (right < size && array[right] < array[left]){//沒有越界,就有右孩子,如果右孩子的值小于左孩子的值min = right;}//比較array[min] array[root]if (array[root] <= array[min]){return;}//調整,交換值int t = array[root];array[root] = array[min];array[min] = t;//需要繼續向下調整,以min作為結點root = min;} }測試
void PrintArray(int array[], int size) {for (int i =0; i < size; ++i){printf("% d", array[i]);}printf("\n"); }void TestAdjustDown() {int array[] = { 27, 15, 19, 18, 28, 34, 65, 49, 25, 37 };int size = sizeof array / sizeof(int);PrintArray(array, size);AdjustDown(array, size, 0);PrintArray(array, size); }時間復雜度(logn)
建堆
循環變量i從最后一個非葉子節點開始,到0結束,在這之間不斷的做向下調整
最后一個非葉子節點 = 最后一個結點的雙親結點( size-1) = ((size-1)-1)/2
代碼實現
//建堆 void CreateHeap(int array[], int size) {for (int i = (size - 2) / 2; i >= 0; i--){//不斷的向下調整AdjustDown(array, size, i);} }測試
時間復雜度O(n)
void TestCreateHeap(){int array[] = { 15, 37, 2, 45, 63, 9, 18, 7, 16, 13 };int size = sizeof(array) / sizeof(int);CreateHeap(array, size);PrintArray(array, size); }堆的初始化
//初始化 void HeapInit(Heap *heap, int array[], int size){memcpy(heap->array, array, size*sizeof(int));heap->size = size;CreateHeap(heap->array, size); }堆的刪除
代碼實現
//刪除 void HeapPop(Heap *heap) {heap->array[0] = heap->array[heap->size - 1];AdjustDown(heap->array, heap->size - 1,0);heap->size--; }堆的插入
堆排序
堆排序不能找最小的放到最前,要不然堆的結構會被破壞
堆排序的注意事項
代碼實現
//大堆的情況void AdjustDown222(int array[], int size, int root) {//判斷 root 是否是葉子結點//因為 堆是完全二叉樹,所以沒有左孩子一定沒有右孩子//又因為堆是順序存儲的//所以,找到左孩子的下標,如果左孩子的下標越界了,則沒有左孩子while (1){int left = 2 * root + 1;if (left >= size){//越界了,就是葉子結點return;}//走完上面一定有左孩子,判讀是否有右孩子//找到左右孩子最小的一個int right = 2 * root + 2;int min = left;//最開始就認為最小的值為左孩子if (right < size && array[right] > array[left]){//沒有越界,就有右孩子,如果右孩子的值小于左孩子的值min = right;}//比較array[min] array[root]if (array[root] >= array[min]){return;}//調整,交換值int t = array[root];array[root] = array[min];array[min] = t;//需要繼續向下調整,以min作為結點root = min;} } void AdjustUp222(int array[], int size, int child) {while (1){//已經到堆頂位置if (child == 0){return;}int parent = (child - 1) / 2;//父結點的值比孩子的值小的就不用調整if (array[parent] >= array[child]){return;}//交換int t = array[parent];array[parent] = array[child];array[child] = t;child = parent;} }//堆排序 //升序,建大堆void HeapSort(int array[], int size) {CreateHeap(array, size);//i 表示被找出的最大的數的個數for (int i = 0; i < size - 1; i++){//每次循環,會找出最大的一個數放到最后int t = array[0];array[0] = array[size - i - 1];array[size - i - 1] = t;//進行向下調整,數據規模是size-1-i;AdjustDown222(array, size - 1 - i, 0);} }測試
void TestHeapSort() {int array[] = { 39, 129, 12, 38, 27, 9, 33, 2, 14 };int size = sizeof(array) / sizeof(int);HeapSort(array, size);PrintArray(array, size); }測試堆排序與冒泡排序的速度
#define SIZE 50000void TestSortSpeed(){srand(20190104);int array[SIZE] ;for (int i = 0; i < SIZE; i++){array[i] = rand() % 10 * 10000;}int s = time();HeapSort(array, SIZE);int e = time();printf("%d\n", e - s);}堆排序速度:
冒泡排序速度:
TopK問題
在海量數據中(n>>100*10000),找最大的k=10個數
/* 類似偽代碼,實際中,size是海量的,內存中放不下,需要借助文件操作 */ void TopK(int array[], int size, int k){int *heap = (int *)malloc(sizeof(int)*k);for (int i = 0; i < k; i++){heap[i] = array[i];}//針對heap建小堆CreateHeap(heap, k);for (int j = k; j < size; j++){if (array[j]>heap[0]){heap[0] = array[j];AdjustDown(heap, k, 0);}} }總結
以上是生活随笔為你收集整理的再写堆(堆的性质,向下调整,建堆,堆的插入删除初始化,堆排序,TopK问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二叉树题目----5 平衡二叉树
- 下一篇: 二叉树题目----6 二叉树的最近公共祖