天天看點

最小堆了解簡介實作

簡介

當一棵二叉樹的每個結點都大于它的兩個子結點時,被稱為堆有序;

如果我們用指針來表示堆有序的二叉樹,那麼每個元素都需要三個指針來找到它的上下結點;但是如果我們使用完全二叉樹,隻用數組而不需要指針就可以表示;

什麼是最小堆呢?

最小堆就是在二叉堆的基礎上,符合了每個結點都比他的子結點要小的規則!

我們會遇到這兩種情況,這也是最小堆中特别要注意的兩種實作:

  • 上浮:從下至上恢複堆的順序,在當某個節點的優先級上升時;
  • 下沉:從上至下恢複的順序,當某個節點的優先級下降的時候;

我們可以從下面的圖中觀察最小堆是如何運作的:

最小堆了解簡介實作

隻要不滿足小的規則就交換

實作

1.上浮

先看上浮,其思想就是,因為是從下至上,是以根據目前結點,算出父親結點,然後跟父親結點進行比較,如果子結點小的話,則需要進行互相交換,反之不用,直到循環到下标到達結尾;

/**
 *  将end到0的資料都上浮
 *
 *  @param end
 */
void MinHeap::shiftUp(int end) {
    int i = end;
    while (i > 0) {
        int parent = i % 2 == 0 ? i/2 - 1 : i/2;
        if (heap[i] < heap[parent]) {
            swap(&heap[i], &heap[parent]);
            i = parent;
        }
        else {
            --i;
        }
    }
}                

2.下沉

接着看下沉,其思想就是,因為是從上至下,是以根據目前結點,算出左右子結點,然後跟目前結點進行比較,如果左右結點小,這裡需要注意的是,取左右子結點中小的進行交換,因為其中必然會有三者之中的最小值,反之不用,直到循環到下标到達0;

/**
 *  将start到end的資料都往下沉
 *  将子結點和父結點進行比較
 *
 *  @param start <#start description#>
 *  @param end   <#end description#>
 */
void MinHeap::shiftDown(int start, int end) {
    int i = start;
    while (i < end) {
        int lchild = i*2 + 1, rchild = i*2 + 2;
        
        if ((heap[i] > heap[lchild] && lchild < currentSize) || (heap[i] > heap[rchild] && rchild < currentSize)) {
            bool leftSmall = heap[lchild] < heap[rchild];
            
            if (leftSmall) {
                swap(&heap[i], &heap[lchild]);
                i = lchild;
            }
            else {
                swap(&heap[i], &heap[rchild]);
                i = rchild;;
            }
        }
        else {
            ++i;
        }
    }
}                

3.插入删除

還有一個需要注意的點是插入删除;

  • 插入:插到數組的最後一個位置上,增加堆大小,并且上浮;
  • 删除:将數組頭元素用數組尾元素進行替換,減小堆大小,并且下沉;

4.整體實作

//
//  main.cpp
//  Heap
//
//  Created by George on 16/11/1.
//  Copyright © 2016年 George. All rights reserved.
//

#include <iostream>

#define DefaultSize 10
#define Log(content) { std::cout << "Log:" << content << std::endl; }

class MinHeap {
public:
    MinHeap(int size = DefaultSize);
    ~MinHeap();
    
    inline bool isEmpty() {
        return currentSize == 0;
    };
    
    inline bool isFull() {
        return currentSize == maxSize;
    };
    
    bool insert(const int value);
    bool removeMin();
    int poll();
    
    void shiftDown(int start, int end);
    void shiftUp(int end);
    
    void toString();
    
private:
    int * heap;
    int currentSize;
    int maxSize;
    
    void swap(int *num1, int *num2);
};

MinHeap::MinHeap(int size) : currentSize(0) {
    heap = new int[size];
    maxSize = size > DefaultSize ? size : DefaultSize;
}

MinHeap::~MinHeap() {
    
}

void MinHeap::toString() {
    for (int i = 0; i < currentSize; ++i) {
        std::cout << heap[i] << std::endl;
    }
}

void MinHeap::swap(int *num1, int *num2) {
    int temp = *num1;
    *num1 = *num2;
    *num2 = temp;
}

/**
 *  将start到end的資料都往下沉
 *  将子結點和父結點進行比較
 *
 *  @param start <#start description#>
 *  @param end   <#end description#>
 */
void MinHeap::shiftDown(int start, int end) {
    int i = start;
    while (i < end) {
        int lchild = i*2 + 1, rchild = i*2 + 2;
        
        if ((heap[i] > heap[lchild] && lchild < currentSize) || (heap[i] > heap[rchild] && rchild < currentSize)) {
            bool leftSmall = heap[lchild] < heap[rchild];
            
            if (leftSmall) {
                swap(&heap[i], &heap[lchild]);
                i = lchild;
            }
            else {
                swap(&heap[i], &heap[rchild]);
                i = rchild;;
            }
        }
        else {
            ++i;
        }
    }
}

/**
 *  将end到0的資料都上浮
 *
 *  @param end
 */
void MinHeap::shiftUp(int end) {
    int i = end;
    while (i > 0) {
        int parent = i % 2 == 0 ? i/2 - 1 : i/2;
        if (heap[i] < heap[parent]) {
            swap(&heap[i], &heap[parent]);
            i = parent;
        }
        else {
            --i;
        }
    }
}

/**
 *  删除最小值,也就是堆頂,用最大值替代,并且下沉
 *
 *  @return <#return value description#>
 */
bool MinHeap::removeMin() {
    if (currentSize == 0) {
        Log("current size is 0!");
        return false;
    }
    heap[0] = heap[currentSize-1];
    --currentSize;
    shiftDown(0, currentSize-1);
    return true;
}

/**
 *  插入值後要進行上浮
 *
 *  @param value <#value description#>
 *
 *  @return <#return value description#>
 */
bool MinHeap::insert(const int value) {
    if (currentSize == maxSize) {
        int *nHeap = heap;
        heap = new int(maxSize + 10);
        for (int i = 0; i < maxSize; ++i) {
            heap[i] = nHeap[i];
        }
        delete [] nHeap;
        Log("heap is resize!");
    }
    heap[currentSize++] = value;
    shiftUp(currentSize-1);
    return true;
}

/**
 *  取得堆頂值
 *
 *  @return <#return value description#>
 */
int MinHeap::poll() {
    if (isEmpty()) {
        Log("heap is null!");
        return -1;
    }
    return heap[0];
}

int main(int argc, const char * argv[]) {
    // insert code here...
    
    MinHeap Heap;
    
    Log("------------Insert------------");
    int arr[8] = {8, 7, 6, 5, 4, 3, 2, 1};
    for (int i = 0; i < 8; i++) {
        Heap.insert(arr[i]);
    }
    
    Heap.toString();
    
    Log("------------Remove------------");
    Heap.removeMin();
    
    Heap.toString();
    
    Log("------------Get------------");
    std::cout << "poll:" << Heap.poll() << std::endl;
    
    return 0;
}                

看結果:

最小堆了解簡介實作

轉載于:https://www.cnblogs.com/George1994/p/6399887.html