优先队列(堆)

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 }

运行结果:

技术图片

 

 

斜堆

斜堆是具有堆序的二叉树,但是不存在对树的结构限制,即斜堆的右路径可以任意长。

斜堆的基本操作也是合并,合并和左式堆的操作方式差不多,不同的是,对于左式堆,是在左右儿子堆序不满足左式堆性质要求时进行左右儿子的交换,对于斜堆,交换是无条件的。

以上是关于优先队列(堆)的主要内容,如果未能解决你的问题,请参考以下文章

数据结构--优先队列(堆排序)

堆和优先级队列2:java实现堆和优先级队列

优先队列(堆)·二项队列

Dijkstra算法介绍及其优先队列优化和斐波那契堆优化

LeetCode 堆(优先级队列) 相关题目

数据结构 Java数据结构 ---- 堆(优先级队列)