小根堆(Heap)的詳細實現


堆的介紹

Heap是一種數據結構具有以下的特點:

1)完全二叉樹
2)heap中存儲的值是偏序

Min-heap: 父節點的值小於或等於子節點的值

Max-heap: 父節點的值大於或等於子節點的值

圖1

堆的存儲

一般都用數組來表示堆,i結點的父結點下標就為(i–1)/2。它的左右子結點下標分別為2 * i + 1和2 * i + 2。如第0個結點左右子結點下標分別為1和2。

圖2

由於堆存儲在下標從0開始計數的數組中,因此,在堆中給定下標為i的結點時:

(1)如果i=0,結點i是根結點,無父結點;否則結點i的父結點為結點(i-1)/2;

(2)如果2i+1>n-1,則結點i無左子女;否則結點i的左子女為結點2i+1;

(3)如果2i+2>n-1,則結點i無右子女;否則結點i的右子女為結點2i+2。

堆的操作:小根堆插入元素

插入一個元素:新元素被加入到heap的末尾,然后更新樹以恢復堆的次序。

每次插入都是將新數據放在數組最后。可以發現從這個新數據的父結點到根結點必然為一個有序的數列,現在的任務是將這個新數據插入到這個有序數據中——這就類似於直接插入排序中將一個數據並入到有序區間中。需要從下網上,與父節點的關鍵碼進行比較,對調。

圖3

堆的操作:刪除小根堆堆的最小元素

按定義,堆中每次都刪除第0個數據。為了便於重建堆,實際的操作是將最后一個數據的值賦給根結點,堆的元素個數-1,然后再從根結點開始進行一次從上向下的調整。調整時先在左右兒子結點中找最小的,如果父結點比這個最小的子結點還小說明不需要調整了,反之將父結點和它交換后再考慮后面的結點。相當於從根結點將一個數據的“下沉”過程。

圖4

堆的操作:創建堆

對於葉子節點,不用調整次序,根據滿二叉樹的性質,葉子節點比內部節點的個數多1.所以i=n/2 -1 ,不用從n開始。就是從最后一個有葉子結點的結點開始。

堆排序

如果從小到大排序,創建大堆建好之后堆中第0個數據是堆中最大的數據。取出這個數據,放在數組最后一個元素上,將當前元素數-1,再執行下堆的刪除操作。這樣堆中第0個數據又是堆中最大的數據,重復上述步驟直至堆中只有一個數據時,數組元素就已經有序。

小根堆的實現

#include <iostream>
using namespace std;

const int DefaultSize = 50;

template<typename T>
class MinHeap
{
public:
	//構造函數:建立空堆
	MinHeap(int sz=DefaultSize)
	{
		maxHeapSize = (DefaultSize < sz) ? sz : DefaultSize;
		heap = new T[maxHeapSize];
		currentSize = 0;
	}

	//構造函數通過一個數組建立堆
	MinHeap(T arr[],int n)
	{
		maxHeapSize = (DefaultSize < n) ? n : DefaultSize;
		heap = new T[maxHeapSize];
		for(int i=0;i<n;i++)
		{
			heap[i] = arr[i];
		}
		currentSize = n;
		int currentPos = (currentSize - 2) / 2;	//找最初調整位置:最后分支結點
		while (currentPos>=0)	//自底向上逐步擴大形成堆
		{
			siftDowm(currentPos, currentSize - 1);	//局部自上向下下滑調整
			currentPos--;	//再向前換一個分支結點
		}
	}

	//將x插入到最小堆中
	bool Insert(const T& x)
	{
		if(currentSize==maxHeapSize)
		{
			cout << "Heap Full!" << endl;
			return false;
		}
		heap[currentSize] = x;	//插入
		siftUp(currentSize);	//向上調整
		currentSize++;	//堆計數+1
		return true;
	}

	bool RemoveMin(T& x)
	{
		if(!currentSize)
		{
			cout << "Heap Empty!" << endl;
			return false;
		}
		x = heap[0];	//返回最小元素
		heap[0] = heap[currentSize - 1];	//最后元素填補到根結點
		currentSize--;
		siftDowm(0, currentSize - 1);	//自上向下調整為堆
		return true;
	}

	void output()
	{
		for(int i=0;i<currentSize;i++)
		{
			cout << heap[i] << " ";
		}
		cout << endl;
	}

protected:

	//最小堆的下滑調整算法
	void siftDowm(int start, int end)	//從start到end下滑調整成為最小堆
	{
		int cur = start;
		int min_child = 2 * cur + 1;	//先記max_child是cur的左子女位置
		T temp = heap[cur];
		while (min_child <=end)
		{
			if (min_child<end&&heap[min_child]>heap[min_child + 1])	//找到左右孩子中最小的一個
				min_child++;

			if(temp<=heap[min_child])
				break;
			else
			{
				heap[cur] = heap[min_child];
				cur = min_child;
				min_child = 2 * min_child + 1;
			}
		}
		heap[cur] = temp;
	}

	//最小堆的上滑調整算法
	void siftUp(int start)	//從start到0上滑調整成為最小堆
	{
		int cur = start;
		int parent = (cur - 1) / 2;
		T temp = heap[cur];
		while (cur>0)
		{
			if(heap[parent]<=temp)
				break;
			else
			{
				heap[cur] = heap[parent];
				cur = parent;
				parent = (parent - 1) / 2;
			}
		}
		heap[cur] = temp;	//回放temp中暫存的元素
	}
private:	//存放最小堆中元素的數組
	T* heap;
	int currentSize;	//最小堆中當前元素個數
	int maxHeapSize;	//最小堆最多允許元素個數
};

//------------------------主函數-------------------------
int main(int argc, char* argv[])
{
	MinHeap<int> h;
	h.Insert(8);
	h.Insert(5);
	h.Insert(7);
	h.Insert(9);
	h.Insert(6);
	h.Insert(12);
	h.Insert(15);
	h.output();

	int out;
	cout << static_cast<int> (h.RemoveMin(out)) << endl;
	h.output();

	int arr[10] = { 15,19,13,12,18,14,10,17,20,11 };
	MinHeap<int> h1(arr,10);
	h1.output();
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM