优先队列(堆)
Posted cs0915
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了优先队列(堆)相关的知识,希望对你有一定的参考价值。
二叉堆
性质
1)结构性质
二叉堆是完全二叉树,完全二叉树的结点编号是有规律的,可以用数组来表示。从i=1开始放置元素(i=0用于放置一个比堆中所有元素都大/小的值,作为标志),对于数组i上的元素,其左儿子在位置2i上,右儿子在2i+1上,其父亲则在i/2上
2)堆序性
使操作被快速执行的性质,由于想要快速找出最小元,因此最小元应在根上,若考虑任意子树也是一个堆,那么任意结点就应该小于它的所有后裔
堆操作
1)插入
在下一个位置创建一个空穴,如果X可以放进该空穴而不破坏堆的序,则插入完成。否则,把空穴的父结点的元素移到该空穴,这样,空穴就朝着根的方向上行一步,继续该过程直到X能放入空穴为止。——称为上滤。
2)删除最小元
当删除掉最小元时,在根结点处产生一个空穴。我们将空穴的两个儿子中较小者移入空穴,这样就把空穴下推了一层。重复该步骤直到最后一个元素可以放进空穴中。——称为下滤。
左式堆
结点X的零路径长:定义为从结点X到一个没有两个儿子的节点的最短路径长,即到有一个儿子的结点或没有儿子的结点的最短路径长。记作Npl(X),其中Npl(NULL)=-1。
性质
1)任一结点的零路径长比它的诸儿子结点的零路径长的最小值多1.
2)对于堆中的每一个结点X,左儿子的零路径长至少与右儿子的零路径长一样大。
操作
1)合并
左式堆的基本操作是合并(Merge)。插入是合并的特殊情形,可以把插入看作是单节点堆与一个大的堆的合并。可以使用递归的解法来完成合并,输入两个堆H1、H2,如果两个堆中有一个是空的,那么返回另一个堆。否则,将具有大的根植的堆与具有小的根值的堆的右子堆合并。同时还需保证递归后的堆是左式的,若不满足则交换左右儿子,并更新零路径长,新的零路径长是新的右儿子的零路径长加1。
2)删除最小/最大值
即删除掉根节点,将根节点的左右儿子合并。
1 #include <iostream> 2 using namespace std; 3 4 struct treeNode 5 { 6 int data; 7 struct treeNode* left; 8 struct treeNode* right; 9 int npl;//记结点的零路径长 10 }; 11 typedef struct treeNode* tree; 12 13 tree Merge1(tree H1, tree H2); 14 tree Merge(tree H1, tree H2); 15 //找最小值 16 int FindMin(tree H) 17 { 18 return H->data; 19 } 20 //判断是否为空 21 bool Isempty(tree H) 22 { 23 return H == NULL; 24 } 25 //交换左右子堆 26 void SwapChild(tree H) 27 { 28 tree tmp; 29 tmp = H->left; 30 H->left = H->right; 31 H->right = tmp; 32 } 33 //插入 34 tree insert(tree H, int data) 35 { 36 tree Node = (tree)malloc(sizeof(treeNode)); 37 Node->data = data; 38 Node->left = Node->right = NULL; 39 Node->npl = 0; 40 H = Merge(Node, H); 41 return H; 42 } 43 //合并左式堆的驱动例程 44 tree Merge(tree H1, tree H2) 45 { 46 if (H1 == NULL) 47 return H2; 48 else if (H2 == NULL) 49 return H1; 50 else 51 { 52 if (H1->data < H2->data) 53 return Merge1(H1, H2); 54 else 55 return Merge1(H2, H1); 56 } 57 } 58 //合并左式堆的实际例程 59 tree Merge1(tree H1, tree H2) 60 { 61 if (H1->left == NULL) 62 H1->left = H2; 63 else 64 { 65 H1->right = Merge(H1->right, H2); 66 if (H1->left->npl < H1->right->npl) 67 SwapChild(H1); 68 H1->npl = H1->right->npl + 1; 69 } 70 return H1; 71 } 72 //删除 73 tree DeleteMin(tree H) 74 { 75 tree LeafHeap, RightHeap; 76 if (Isempty(H)) 77 { 78 cout << "Priority is empty" << endl; 79 return H; 80 } 81 LeafHeap = H->left; RightHeap = H->right; 82 free(H); 83 return Merge(LeafHeap, RightHeap); 84 } 85 tree initialize(int data[],int n) 86 { 87 tree H = NULL; 88 for (int i = 0; i < n; i++) 89 H=insert(H, data[i]); 90 return H; 91 } 92 //中序遍历 93 void MidOrder(tree H) 94 { 95 if (H != NULL) 96 { 97 MidOrder(H->left); 98 cout << H->data << " "; 99 MidOrder(H->right); 100 } 101 } 102 int main() 103 { 104 int data1[] = { 3,6,10,2,4 }; 105 int data2[] = { 4,7,3,8,5 }; 106 tree H1, H2; 107 H1 = initialize(data1, 5); 108 H2 = initialize(data2, 5); 109 MidOrder(H1); cout << endl; 110 MidOrder(H2); cout << endl; 111 H1=DeleteMin(H1); 112 MidOrder(H1); 113 system("pause"); 114 return 0; 115 }
运行结果:
斜堆
斜堆是具有堆序的二叉树,但是不存在对树的结构限制,即斜堆的右路径可以任意长。
斜堆的基本操作也是合并,合并和左式堆的操作方式差不多,不同的是,对于左式堆,是在左右儿子堆序不满足左式堆性质要求时进行左右儿子的交换,对于斜堆,交换是无条件的。
以上是关于优先队列(堆)的主要内容,如果未能解决你的问题,请参考以下文章