大白話講解二叉堆

歡迎關注我,學習資料免費分享給你哦!還有其他超多學習資源,都是我自己學習過的,經過過濾之後的資源,免去你還在因爲擁有大量資源不知如何入手的糾結,讓你體系化學習。
在這裏插入圖片描述

二叉堆

二叉堆是一棵完全二叉樹,什麼是完全二叉樹呢?簡單來說,就是按照層的順序,對樹的節點標號,然後按照層次遍歷的順序來遍歷,得到的結果是按照順序來標號的,不能出現斷點,這就是一個完全二叉樹。這麼說比較抽象,舉個例子來說。

在這裏插入圖片描述
這個二叉樹的層次遍歷結果是0,1,2,3,4,5,6,7,8.所以是一個完全二叉樹。

在這裏插入圖片描述
這裏這棵二叉樹進行層次遍歷,得到就是0,1,2,3,4,5,6,7,10.編號不是連續的,就不是一棵完全二叉樹。

堆就是一棵完全二叉樹,不過因爲完全二叉樹的特性,在實現它的時候往往不是使用鏈表,而是使用數組來實現,我們可以把上面的二叉樹的節點標號與數組的下標對應起來。二叉樹的根節點標號從1開始,存儲的數組也從下標1開始存儲二叉樹,一一對應,這種情況下,父節點的數組下標爲i,則左子樹的數組下標爲2*i,右子樹的下標爲2*i+1(如果沒有超出數組的界限的情況下)

在這裏插入圖片描述

上圖的二叉樹將被存儲爲下面的數組

在這裏插入圖片描述

在程序裏就是如下的訪問子節點和父節點。

int heap[10];
//訪問第三個節點的父節點
heap[3/2];
//訪問第三個節點的左子節點
heap[3*2]
//訪問第三個節點的右子節點
heap[3*2+1]

如果是最大堆,那麼父節點都要比兩個子節點的數值大,相應的最小堆就是父節點比兩個子節點都要小。而哨兵的作用就是充當邊界檢測的角色。如果是最大堆,那麼這個值比堆中的數據都要大,如果是最小堆,那麼這個值比堆中所有數據都要小。

堆的程序實現
堆的結構定義
typedef int Element;
typedef struct HeapNode
{
	int heapsize; //堆的最大尺寸,也就是數組的最大值
	int size;//堆的當前大小
	Element *data;//數組指針,動態分配數組的大小
};
typedef struct HeapNode* Heap;//堆的指針定義
#define MIN_DATA -20000 //這裏實現最小堆,哨兵是最小值,堆中數據的範圍是-10000,10000,這個值隨需要更改
創建一個heapsize大小的堆
Heap creatHeap(int heapsize)
{
	Heap heap;
	heap=(Heap)malloc(sizeof(struct HeapNode));
	if(heap==NULL)
	{
		printf("內存不足");
		return NULL;
	}
	heap->data=(Element*)malloc(sizeof(Element)*(heapsize+1));
	if(heap->data==NULL)
	{
		printf("內存不足");
		return NULL;
	}
	heap->heapsize=heapsize;
	heap->size=0;
	heap->data[0]=MIN_DATA;
	return heap;
}
判斷堆空或者堆滿

這個比較簡單,因爲堆是一個數組,如果size變量爲0,表示堆空。如果size變量等於堆數組的最大值,那麼就是堆滿。

int isFull(Heap heap)
{
	return heap->size==heap->heapsize;
}
int isEmpty(Heap heap)
{
	return heap->size==0;
}
堆的插入操作

堆的插入操作就是首先將size變量加1,當前節點i=size+1,然後訪問它的父節點i/2,比較插入的元素和父節點的元素哪個元素小(這裏以最小堆爲例),如果新插入的元素比較小,那麼就將父節點i/2的值賦值到當前節點i。此時將i更新(i=i/2),然後去比較此時i的節點的父節點(i/2,相當於(size+1)/4)與插入的元素,直到比較到哨兵,因爲哨兵是最小值,所以插入的元素會放到數組下標的0的位置。如果比較到其中某個位置時,插入的節點比父節點的大,那麼就把插入的節點放到當前位置。以圖說話。

在這裏插入圖片描述

假設有如上的最小堆,堆目前的size是5,如果插入一個元素15,則size加1,相當於把31後面的數組位置空出來,等待插入,然後比較i=6的父節點i/2,此時是16,因爲15小於16,所以16就需要下移到數組下標爲6的位置了。

在這裏插入圖片描述

只是將16複製到了數組下標爲6的地方,數組下標爲3的值沒有改變,還是16.然後此時i移動到位置3,比較i=3的父節點i/2,此時是數組的下標1的數據,13<15,說明符合最小堆的定義,父節點小於子節點,所以15應該插入到數組下標爲3的地方。插入完成。

在這裏插入圖片描述

在比如在此時直接插入20元素,size加1等於7,它的父節點的值爲15,比20小,所以20直接放到數組下標爲7的位置。

插入操作的程序如下:

void insert(Heap heap,Element x)
{
	int i=0;
	if(isFull(heap))
	{
		printf("堆滿了");
		return;
	}
	for(i=++heap->size;x<heap->data[i/2];i=i/2) //這裏因爲有哨兵,所以當i等於0時,是一定會結束循環的,如果不使用哨兵,需要判斷一下i爲0,否則導致死循環
	{
		heap->data[i]=heap->data[i/2];
	}
	heap->data[i]=x;
}
堆的刪除操作

堆的刪除操作就是將數組的下標1的數據返回,然後重新構造堆,將size減1,並將最後一個數據找到合理的位置插入。就是從根節點開始(也就是下標1)找到兩個子節點的最小值,與最後一個數據比較,如果最後一個數據比兩個子節點的最小值還要小的話,那麼就把這個數據複製到這兩個子節點的父節點;如果最後一個數據比兩個子節點的最小值還要大的話,那麼就將兩個子節點的最小值節點複製到它的父節點,然後以這個子節點作爲父節點,重複上述過程,直到找到合適位置。
在這裏插入圖片描述

假設執行刪除操作,13刪除,最後一個元素16被取出,size減1,比較13的子節點21和15,15位最小的,那麼比較15和16,發現15小,所以將15複製到13的位置。

在這裏插入圖片描述

此時以數組下標3作爲父節點,比較它的兩個子節點,但是發現已經沒有了,所以把16複製到數組下標3的位置就完成了刪除操作。

假設第一次刪除13時,它的兩個子節點都比16大,那麼直接將16複製到數組下標爲1的地方,就完成了刪除。這就是堆的刪除操作的執行過程。

程序代碼如下:

Element deleteHeap(Heap heap)
 {
	int child,parent;
	Element x,num;
	if(isEmpty(heap))
	{
        printf("堆空");
		return MIN_DATA;
	}
	x=heap->data[1];//保存要刪除的元素,作爲返回值
	num=heap->data[heap->size--];//堆的尺寸小1,將這個數值保存以免丟失
	for(parent=1;parent*2<=heap->size;parent=child)
	{
		child=2*parent;
        //尋找兩個子節點中最小的一個,&&前面的判斷是防止child已經是堆的最後一個數據,加1後超出堆的範圍
		if((child+1)<=heap->size&&heap->data[child]>heap->data[child+1])
		{
			child=child+1;
		}
        //比較兩個子節點的最小值和要原堆中最後一個元素,如果最後一個元素小,說明找到了插入的位置,結束循環
		if(num<=heap->data[child])
		{
			break;
		}
		else
		{
			heap->data[parent]=heap->data[child];
		}
	}
	heap->data[parent]=num;
	return x;
 }
堆的構建

將一個數組變成最小堆,思路就是從根本一個子樹一個子樹的逐漸變換,假設數組大小爲size,那麼它的父節點就是i=size/2,以i爲起點,執行與刪除操作相同的下濾操作,直到i變成0,就完成了堆的構建,其實就是堆排序的思路。

void perdown(Heap heap,int d)
 {
	int child,parent;
	Element x;
	x=heap->data[d];
	for(parent=d;parent*2<=heap->size;parent=child)
	{
		child=2*parent;
		if((child+1)<=heap->size&&heap->data[child]>heap->data[child+1])
		{
			child=child+1;
		}
		if(x<=heap->data[child])
		{
			break;
		}
		else
		{
			heap->data[parent]=heap->data[child];
		}
	}
	heap->data[parent]=x;
 }
 void buildHeap(Heap heap)
 {
	 int i=0;
	 for(i=heap->size/2;i>0;i--)
	 {
		perdown(heap,i);
	 }
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章