标签:调整 null 复杂度 ptr 基本操作 关系 解决 使用 heap
d-堆
类似于二叉堆,但是它有d个儿子,此时,d-堆比二叉堆要浅很多,因此插入操作更快了,但是相对的删除操作更耗时。因为,需要在d个儿子中找到最大的,但是很多算法中插入操作要远多于删除操作,因此,这种加速是现实的。
除了不能执行find去查找一般的元素外,两个堆的合并也很困难。
左式堆
左式堆可以有效的解决上面说的堆合并的问题。合并就涉及插入删除,很显然使用数组不合适,因此,左式堆使用指针来实现。左式堆和二叉堆的区别:左式堆是不平衡的。它两个重要属性:键值和零距离
零距离(英文名NPL,即Null Path Length)则是从一个节点到一个没有两个儿子的节点(只有0个或1个儿子的节点)的路径长度。具有0个或1个儿子的节点的NPL为0,NULL节点的NPL为-1。
typedef int Type; typedef struct _LeftistNode{ Type val; int npl; // 零路经长度(Null Path Length) struct _LeftistNode *left; // 左孩子 struct _LeftistNode *right; // 右孩子 }LeftistNode, *LeftistHeap;
合并
合并操作是左倾堆的重点。插入式合并的特殊情况。
合并两个左倾堆(最小堆)的基本思想如下:
实现时,通过递归自底向上合并并调整使得满足左式堆的性质。
LeftistNode* mergeLeftist(LeftistHeap x, LeftistHeap y){ if (x == nullptr)return y; if (y == nullptr)return x; LeftistHeap l, r;//以l为根,l较小 if (x->val < y->val){ l = x; r = y; } else { l = y; r = x; } l->right = mergeLeftist(l->right,r);//合并l->right和r if (!l->left || l->left->npl < l->right->npl){//判断是否需要交换左右子树 LeftistHeap temp = l->left; l->left = l->right; l->right = temp; } //更新npl if (!l->right || !l->left)l->npl = 0; else l->npl = l->left->npl > l->right->npl ? l->right->npl + 1 : l->left->npl + 1; return l; }
合并左式堆的操作可以看出来,它的时间复杂度和有路径的长成正比,因此复杂度O(logn)
添加节点就可以看做是一个左式堆和一个单点的左式堆合并;
删除树根节点可以看做是删除树根后,左右子树的两个左式堆合并;
因此,他们都可以通过合并来实现。它对应的复杂度也是O(logn)
插入和删除的实现:
斜堆
斜堆是左式堆的自调节形式,左式堆和斜堆的关系类似于伸展树和AVL树的关系。斜堆具有堆序的性质,但是没有结构的限制,这样的话一次的操作最坏的情况时O(n),但是连续m次操作总的复杂度O(mlogn)。
与左式堆相同,斜堆的基本操作也是合并操作。但是斜堆没有零距离的属性,合并的方法也有区别:
斜堆的结构
typedef int Type; typedef struct _SkewNode{ Type val; struct _SkewNode *left; // 左孩子 struct _SkewNode *right; // 右孩子 }SkewNode, *SkewHeap;
合并的实现
SkewNode* mergeSkewHeap(SkewHeap x, SkewHeap y){ if (x == nullptr)return y; if (y == nullptr)return x; SkewHeap l, r;//以l为根,l较小 if (x->val < y->val){ l = x; r = y; } else { l = y; r = x; } SkewNode* temp = mergeSkewHeap(l->right, r);//合并l->right和r l->right = l->left;//交换左右子树 l->left = temp; return l; }
同样的道理,插入和删除根节点的操作都可以使用合并来实现。
标签:调整 null 复杂度 ptr 基本操作 关系 解决 使用 heap
原文地址:http://www.cnblogs.com/yeqluofwupheng/p/7450643.html