堆(優先隊列)priority queue
特殊的隊列,取出元素的順序是依照元素的優先權(關鍵字)大小,而出元素進入隊列的先後順序
操作:查找最大值(最小值),刪除(最大值)
數組:
鏈表:
有序數組:
有序鏈表:
採用二叉搜索樹? NO
採用完全二叉樹 YES
堆的連個特性
結構性:用數組表示的完全二叉樹:
有序性:任一結點的關鍵字是其字樹所有結點的最大值(或最小值)
最大堆(MaxHeap)也稱大頂堆:最大值
最小堆(MinHeap)也稱“小頂堆”:最小值
從根節點到任意結點路徑上結點序列的有序性
操作:插入任意一個元素,刪除最 大值元素
最大堆的刪除:取出根節點(最大值)元素,同時刪除堆的一個結點
最大堆的建立:將已存在的N個元素按最大堆的要求存放在一個以爲數組中
#include <stdio.h>
#include <stdlib.h>
//最大堆
#define MaxData 1000 //哨兵,該值應該根據具體情況定義爲大於堆中所有可能元素的值
typedef int ElementType;
typedef struct HeapNode * MaxHeap;
struct HeapNode {
int Capacity; //堆的最大容量
int Size; //堆中當前元素個數
ElementType *Data; //用於存儲元素的數組
};
建一個空的最大堆:
//建立一個空的最大堆
MaxHeap InitHeap(int maxsize)
{
MaxHeap H = (MaxHeap)malloc(sizeof(struct HeapNode));
H->Data = (ElementType*)malloc(sizeof(struct HeapNode) * (maxsize + 1)); //堆中的元素是從下標爲1開始存儲的,但是爲了保證這個堆能存下maxsize個元素,所以要分配maxsize + 1個內存單元
H->Capacity = maxsize;
H->Size = 0;
H->Data[0] = MaxData; //將0下標的單元存儲哨兵
for (int i = 1; i < maxsize + 1; i++)
H->Data[i] = 0;
return H;
}
判斷堆是否已滿或是否爲空:
int IsEmpty(MaxHeap H)
{
return H->Size == 0;
}
//判斷堆是否已滿
int IsFull(MaxHeap H)
{
return H->Size == H->Capacity;
}
插入一個元素:
//最大堆中插入一個元素
void Insert(MaxHeap H, ElementType item)
{
int i;
if (IsFull(H))
{
printf("The heap is full\n");
return;
}
i = ++H->Size; //i爲插入後堆中最後一個元素的位置
for (; H->Data[i / 2] < item; i /= 2)
H->Data[i] = H->Data[i / 2]; //循環退出時,父節點的數據已經大於item, item已經找到了正確的位置
H->Data[i] = item; //將item賦給正確的下標單元
}
刪除一個元素:
ElementType Delete(MaxHeap H)
{
ElementType temp, Max;
int parent = 1, child;
if (IsEmpty(H))
{
printf("The heap is empty!\n");
return 0;
}
Max = H->Data[1]; //現將最大值即根節點的值記錄下來
temp = H->Data[H->Size--]; //用最後的結點元素暫時代替根節點的數據,然後將堆的數據大小減1
//如果2 * parent > 0,那麼說明parent已經是根節點了
for (; 2 * parent <= H->Size; parent = child)
{
child = 2 * parent;
//如果Child != H->Size說明child不是最後一個結點,該parent結點還有右節點,
//並且如果右節點的值大於左結點,那麼child++;
if (child != H->Size && (H->Data[child + 1] < H->Data[child]))
child++; //child指向左右子節點的較大者
if (temp > H->Data[child]) break; //如果孩子結點已經小於temp了, 說明找到了合適的位置
else
H->Data[parent] = H->Data[child];
}
H->Data[parent] = temp;
return Max;
}
最大堆的建立:
1.第一步,將N個元素按輸入順序存入二叉樹中,這一步需要求滿足完全二叉樹的結構特性,而不管其有序性
2.從最後一個右孩子的結點開始調整,做類似刪除元素時的下濾操作,逐個結點調整,直至根節點
//創建一個最大堆
MaxHeap CreateMaxHeap()
{
int dt, i = 0, j;
int parent, child;
ElementType X;
MaxHeap H = InitHeap(20); //先創建一個空的最大堆,然後往裏面填充元素
while (scanf_s("%d", &dt) != EOF)
{
H->Data[i + 1] = dt;
i++;
H->Size++;
}
for (j = i / 2; j >= 1; j--) //從i / 2開始逐一向下過濾
{
//下面的操作和刪除元素是一模一樣的,只是不用將的元素個數減一
X = H->Data[j];
for (parent = j; 2 * parent <= H->Size; parent = child)
{
child = 2 * parent;
if (child != H->Size && H->Data[child] < H->Data[child + 1])
child++;
if (H->Data[child] < X)
break;
else
H->Data[parent] = H->Data[child];
}
H->Data[parent] = X;
}
return H;
}
打印堆中的元素:
//打印堆中元素
void printHeap(MaxHeap H)
{
for (int i = 1; i <= H->Size; i++)
printf("%d ", H->Data[i]);
printf("\n");
}
先序建立樹:
void PreOrderCreateHeap(MaxHeap H, int index)
{
int dt;
scanf_s("%d", &dt);
if (dt == 0)
{
return;
}
H->Size++;
H->Data[index] = dt;
printf("please enter the left child of %d :", dt);
PreOrderCreateHeap(H, 2 * index);
printf("please enter the right child of %d: ", dt);
PreOrderCreateHeap(H, 2 * index + 1);
}
主函數:
int main()
{
MaxHeap H = CreateMaxHeap();
PreOrderCreateHeap(H, 1);
printHeap(H);
return 0;
}