資料結構堆的實現以及堆的面試題
阿新 • • 發佈:2019-02-01
堆
堆的特點
堆有大堆和小堆之分
小堆:
①任意結點的關鍵碼均小於等於他的左右孩子的關鍵碼
②位於堆頂的結點的關鍵碼最小
③從根結點到每個結點的路徑上陣列元素組成的序列都是遞增的
大堆:
①任意結點的關鍵碼均大於等於他的左右孩子的關鍵碼
②位於堆頂的結點的關鍵碼最大
③從根結點到每個結點的路徑上陣列元素組成的序列都是遞減的
堆儲存在下標為0的陣列中,因此在堆中給定下標為i的結點時有:
①如果i=0,結點i是根節點,沒有雙親節點;否則結點i的雙親結點為
結點(i-1)/2
②如果2 * i + 1 <= n - 1(n為堆中結點個數),則結點i的左孩子為結
點2 * i + 1,否則結點i無左孩子
③如果2 * i + 2 <= n - 1(同上),則結點i的右孩子為結點2 * i + 2,
否則結點i無右孩子
堆的實現
heap.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int HPDataType;
typedef int (*PCompare)(HPDataType left, HPDataType right);//函式指標,用小堆就選Less,用大堆就選Greater
typedef struct Heap
{
HPDataType *_hp;
int _size;//堆中的元素個數
int _capacity;//堆的容量
PCompare _com;
}Heap;
// 小於比較
int Less(HPDataType left, HPDataType right);
// 大於比較
int Greater(HPDataType left, HPDataType right);
// 初始化堆
void InitHeap(Heap* hp);
// 建立堆
void CreateHeap(Heap* hp, int* array, int size, PCompare com);
//檢查擴容
void _CheckCapacity(Heap* hp);
// 在堆中插入元素data
void InsertHeap(Heap* hp, HPDataType data);
// 刪除堆頂的元素
void RemoveHeap(Heap* hp);
// 獲取堆中有效元素個數
int SizeHeap(Heap* hp);
// 檢測堆是否為空
int EmptyHeap(Heap* hp);
// 獲取堆頂元素
HPDataType TopHeap(Heap* hp);
// 銷燬堆
void DestroyHeap(Heap* hp);
// 向下調整
void AdjustDown(Heap* hp, int parent);
// 向上調整
void AdjustUp(Heap* hp, int child);
heap.c
#include "heap.h"
int Less(HPDataType left, HPDataType right)
{
return left < right;
}
int Greater(HPDataType left, HPDataType right)
{
return left > right;
}
void InitHeap(Heap* hp)
{
assert(hp);
hp->_hp = (HPDataType*)malloc(3 * sizeof(HPDataType));//初始化開闢多少都行
hp->_capacity = 3;
hp->_size = 0;
}
void Swap(HPDataType *left, HPDataType*right)
{
HPDataType tmp = 0;
tmp = *left;
*left = *right;
*right = tmp;
}
void AdjustUp(Heap* hp, int child)
{
assert(hp);
int parent = (child - 1) / 2;//找出父母結點下標
while (child)//只要child還為非零,就繼續迴圈
{
if (hp->_com(hp->_hp[child], hp->_hp[parent]))//是否需要父母結點
//與孩子結點交換關鍵碼
{
Swap(&hp->_hp[child], &hp->_hp[parent]);
child = parent;//此時孩子結點上移到父母結點
parent = (child - 1) / 2;//計算新的父母結點
}
else
return;
}
}
void AdjustDown(Heap* hp, int parent)
{
assert(hp);
if (NULL == hp)
return;
int child = parent * 2 + 1;//計算左孩子結點下標
while (child < hp->_size)//只要孩子結點下標不越界
{
if (child + 1 < hp->_size&&hp->_com(hp->_hp[child+1],hp->_hp[child]))
//是否需要左右孩子交換關鍵碼,我是以左孩子是否與父母交換關鍵碼為前提
{
Swap(&hp->_hp[child], &hp->_hp[child + 1]);
}
if (hp->_com(hp->_hp[child], hp->_hp[parent]))//是否左孩子與父母交換關鍵碼
{
Swap(&hp->_hp[child], &hp->_hp[parent]);
parent = child;//父母結點移到孩子結點,當做下一個父母結點
child = parent * 2 + 1;
}
else
return;
}
}
void CreateHeap(Heap* hp, int* array, int size, PCompare com)
{
assert(hp);
int root = (size - 2) / 2;//找到最後一個非葉子結點的結點
int i = 0;
hp->_hp = (HPDataType*)malloc(size * sizeof(HPDataType));
if (hp == NULL)
return;
for (i = 0; i < size; i++)
{
hp->_hp[i] = array[i];
}
hp->_capacity = size;
hp->_size = size;
hp->_com = com;
while (root>=0)//從當前結點一直向下調整到下標為零的結點
{
AdjustDown(hp,root);
root--;
}
}
void _CheckCapacity(Heap* hp)
{
assert(hp);
int i = 0;
HPDataType *new = NULL;
int newcapacity = hp->_capacity * 2;
new = (HPDataType*)malloc(newcapacity * sizeof(HPDataType));
if (new == NULL)
return;
for (i = 0; i < hp->_size; i++)
{
new[i] = hp->_hp[i];
}
free(hp->_hp);
hp->_hp = new;
hp->_capacity = newcapacity;
}
void InsertHeap(Heap* hp, HPDataType data)
{
assert(hp);
if (hp->_capacity == hp->_size)
_CheckCapacity(hp);
hp->_hp[hp->_size] = data;//一定要先插入後size++
AdjustUp(hp, hp->_size);
hp->_size++;
}
void RemoveHeap(Heap* hp)
{
assert(hp);
if (NULL == hp)
return;
hp->_hp[0] = hp->_hp[hp->_size - 1];
hp->_size--;//一定要先size--,後向下調整,否則他會把刪除的結點也調整了
AdjustDown(hp, 0);
}
int SizeHeap(Heap* hp)
{
assert(hp);
return hp->_size;
}
int EmptyHeap(Heap* hp)
{
assert(hp);
return 0 == hp->_size;
}
HPDataType TopHeap(Heap* hp)
{
assert(hp);
return hp->_hp[0];
}
void DestroyHeap(Heap* hp)
{
assert(hp);
if (hp)
{
free(hp);
hp->_capacity = 0;
hp->_size = 0;
}
}
優先順序佇列
heap.h
typedef struct PriorityQueue
{
Heap hp;
}PriorityQueue;
//初始化
void InitPriorityQueue(PriorityQueue* q);
//入佇列
void PushPriorityQueue(PriorityQueue* q, HPDataType data);
//出佇列
void PopPriorityQueue(PriorityQueue* q);
//取隊頭元素
void TopPriorityQueue(PriorityQueue* q);
//佇列長度
int SizePriorityQueue(PriorityQueue* q);
//判空
int EmptyPriorityQueue(PriorityQueue* q);
heap.c
void InitPriorityQueue(PriorityQueue* q)
{
InitHeap(&q->hp);
}
void PushPriorityQueue(PriorityQueue* q, HPDataType data)
{
InsertHeap(&q->hp, data);
}
void PopPriorityQueue(PriorityQueue* q)
{
RemoveHeap(&q->hp);
}
void TopPriorityQueue(PriorityQueue* q)
{
return TopHeap(&q->hp);
}
int SizePriorityQueue(PriorityQueue* q)
{
return SizeHeap(&q->hp);
}
int EmptyPriorityQueue(PriorityQueue* q)
{
return EmptyHeap(&q->hp);
}
堆排序(降序,用小堆)
堆排序的原理:
①將堆頂元素和堆中最後一個元素交換
②此時陣列最後一個元素已經排好序了,因此將堆中元素減少一個
③此時堆結構可能被破壞,再向下調整使其滿足堆的性質
④將以上三個步驟迴圈起來,直到排序完陣列中的所有元素
void _AdjustDown(int *array, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
if (child + 1 < size&&array[child + 1] < array[child])
{
Swap(&array[child + 1], &array[child]);
}
if (array[parent] > array[child])
{
Swap(&array[parent], &array[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void MakeHeap(int *array, int size)
{
int root = (size - 2) / 2;
while (root >= 0)
{
_AdjustDown(array,size, root);
root--;
}
}
void HeapSort(int *array, int size)
{
MakeHeap(array, size);//把傳過來的陣列建立成堆
while (size)//調整完所有元素
{
Swap(&array[0], &array[size - 1]);//交換堆頂元素和堆中最後一個元素
size--;//減少堆中元素一個
_AdjustDown(array, size, 0);//向下調整
}
}
測試
test.c
#include "heap.h"
int main()
{
//Heap hp;//被註釋了的是測試堆的實現的程式碼,沒被註釋的是測試堆排序的程式碼,優先順序佇列大家就自己測試吧
int i = 0;
int array[] = { 53,17,78,9,45,65,87,23,31 };
//InitHeap(&hp);
//CreateHeap(&hp, array, sizeof(array)/sizeof(array[0]), Less);
//for (i = 0; i < hp._size; i++)
//{
// printf("%d ", hp._hp[i]);
//}
//printf("\n");
//printf("%d ", TopHeap(&hp));
//printf("%d ", SizeHeap(&hp));
//printf("\n");
int size = sizeof(array) / sizeof(array[0]);
HeapSort(array,size);
for (i = 0; i < size; i++)
{
printf("%d ", array[i]);
}
printf("\n");
//InsertHeap(&hp,11);
//for (i = 0; i < hp._size; i++)
//{
// printf("%d ", hp._hp[i]);
//}
//printf("\n");
//printf("%d ", TopHeap(&hp));
//printf("%d ", SizeHeap(&hp));
//printf("\n");
//RemoveHeap(&hp);
//for (i = 0; i < hp._size; i++)
//{
// printf("%d ", hp._hp[i]);
//}
//printf("\n");
//printf("%d ", TopHeap(&hp));
//printf("%d ", SizeHeap(&hp));
system("pause");
return 0;
}