圖解數據結構(8)——二叉堆

轉載自http://www.cppblog.com/guogangj/archive/2009/10/29/99729.html

首先說說數據結構概念——堆(Heap),其實也沒什麼大不了,簡單地說就是一種有序隊列而已,普通的隊列是先入先出,而二叉堆是:最小先出。

這不是很簡單麼?如果這個隊列是用數組實現的話那用打擂臺的方式從頭到尾找一遍,把最小的拿出來不就行了?行啊,可是出隊的操作是很頻繁的,而每次都得打一遍擂臺,那就低效了,打擂臺的時間複雜度爲Ο(n),那如何不用從頭到尾fetch一遍就出隊呢?二叉堆能比較好地解決這個問題,不過之前先介紹一些概念。

完全樹(Complete Tree):從下圖中看出,在第n層深度被填滿之前,不會開始填第n+1層深度,還有一定是從左往右填滿。

再來一棵完全三叉樹:

這樣有什麼好處呢?好處就是能方便地把指針省略掉,用一個簡單的數組來表示一棵樹,如圖:

那麼下面介紹二叉堆:二叉堆是一種完全二叉樹,其任意子樹的左右節點(如果有的話)的鍵值一定比根節點大,上圖其實就是一個二叉堆。

你一定發覺了,最小的一個元素就是數組第一個元素,那麼二叉堆這種有序隊列如何入隊呢?看圖:

假設要在這個二叉堆裏入隊一個單元,鍵值爲2,那隻需在數組末尾加入這個元素,然後儘可能把這個元素往上挪,直到挪不動,經過了這種複雜度爲Ο(logn)的操作,二叉堆還是二叉堆。

那如何出隊呢?也不難,看圖:

出隊一定是出數組的第一個元素,這麼來第一個元素以前的位置就成了空位,我們需要把這個空位挪至葉子節點,然後把數組最後一個元素插入這個空位,把這個“空位”儘量往上挪。這種操作的複雜度也是Ο(logn),比Ο(n)強多了吧?

常用堆的幾個功能(一下都是以小頂堆爲例(即結點的值都比左右孩子的小))


1)維護堆,向下更新

void min_heapify(int i)    //以i爲根,向下更新,使得數組成爲小頂堆 
{
	int l = i << 1;          //左孩子位子
	int r = l + 1;           //右孩子位子
	int min = i;
	if(l <= length && heap[l] < heap[min])   //比較大小
	{
		min = l;
	}
	if(r <= length && heap[r] < heap[min])
	{
		min = r;
	}
	if(i != min)       //如果最小的值不是i節點,就交換
	{
		int tmp = heap[i];
		heap[i] = heap[min];
		heap[min] = tmp;
		min_heapify(min);     //繼續往下更新
	}
}

2)維護堆,向上更新

void sinkup(int i)    //以i爲根,向上更新,使得數組成爲小頂堆 
{
	int parent = i >> 1;    //找父節點
	if(heap[parent] > heap[i])    //交換
	{
		int tmp = heap[i];
		heap[i] = heap[parent];
		heap[parent] = tmp;
		if(parent > 1)
			sinkup(parent);//繼續向上更新
	}
}

3) 建堆

void min_build()   //建堆 
{
	for(int i = length / 2;i >= 1;i--)    //length爲數組長度   
	{
		min_heapify(i);          //從length / 2開始向上更新,不信繪圖看看
	}
}

4) 取最小值,即隊頭元素a[1]

int popheap()
{   
	int min ;
	min=heap[1];       //取隊頭元素
	heap[1] = heap[length];     //把尾元素放到頭
	length--;          
	min_heapify(1);        //向下更新,維護堆
	return min;
}

5)進堆

void pushheap(int x)
{
	length++;        
       	heap[length] = x; // 插到數組最後一個 
      	sinkup(length);     //向上更新,維護堆
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章