左偏树

来源:互联网 发布:mac傻瓜剪辑视频软件 编辑:程序博客网 时间:2024/04/20 14:26

左偏树(Leftist Tree)是一种可并堆(Mergeable Heap) ,它除了支持优先队列的三个基本操作(插入,删除堆顶,取最小节点),还支持一个很特殊的操作——合并操作。左偏树一般不是平衡树。

左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树指针( left, right)外,还有两个属性:键值和距离(dist)。键值是用于比较节点的大小。距离则是如下定义的: 
节点i称为外节点(externalnode),当且仅当节点i的左子树或右子树为空( left(i) = NULL或right(i) = NULL );节点i的距离(dist(i))是节点i到它的后代中,最近的外节点所经过的边数。特别的,如果节点i本身是外节点,则它的距离为0;而空节点的距离规定为-1 (dist(NULL) =-1)。在本文中,有时也提到一棵左偏树的距离,这指的是该树根节点的距离。 
左偏树满足左偏性质:

节点的左子节点的距离不小于右子节点的距离

还有一个性质:左偏树的根节点的距离值不超过log(|V|),操作的复杂度也为log(|V|)

合并操作:

采用递归的方式合并两棵左偏树:

1.如果有一棵树为空,返回另外一棵树

2.将根节点键值较小的树与较大树的右子树合并

3.如果合并后违反了左偏规则,即右儿子距离大于左儿子距离,交换两个儿子,并且更新当前节点距离值

插入操作:

可视为与一棵只有一个节点的左偏树合并

删除操作:

删除根,合并其左右子树


普通的二叉堆,由于完全二叉树的性质以及数组的实现,不支持堆合并。出于堆合并的需求,提出了左偏树,这里的左偏以及具体规则,目的是快速访问最小节点以及在对树修改后快速的恢复堆性质。

问题来了,为何不直接用二叉树直接用来实现可合并堆,直接不把堆实现为完全二叉树就可以了......原因是,如果不提出一个规则,怎么合并,和左儿子还是右儿子?而且直接用二叉树的话,在插入过程中,树的形状不可知,复杂度可能会变的很大。左偏树明确提出了合并规则,将根节点键值较小的树与较大树的右子树合并,而且其规则:节点的左子节点的距离不小于右子节点的距离 就是基于合并规则提出的,为了递归的去和右子树合并的过程中,走过的路程少,递归层数少,从而复杂度低。至于为何用距离而不是用平常意义上的深度,原因也和不断和右子树合并有关,考虑深度的话,没啥意义

代码:

可以指针实现和数组实现

指针实现:写了人基于C++类封装的,未封装的略

// 左偏树.cpp//以最大堆为例#include "stdafx.h"#include <cstdio>#include <cstring>#include <iostream>using namespace std;class TreeNode {private:int data;int dist;TreeNode *left, *right;public:TreeNode(int te):data(te),dist(0),left(NULL),right(NULL){}static TreeNode *Merge(TreeNode *p,TreeNode *w);static void Insert(TreeNode *&root,int te);void Delete(TreeNode *&root);void postOrder();};TreeNode *TreeNode::Merge(TreeNode *p,TreeNode *w) {if (p == NULL) {return w;}else if (w == NULL) {return p;}TreeNode *x = p->data > w->data ? p : w;TreeNode *y = x == p ? w : p;x->right = TreeNode::Merge(x->right, y);if (x->left == NULL ||x->left->dist < x->right->dist) {TreeNode *temp = x->left;x->left = x->right;x->right = temp;}if (x->right == NULL) {x->dist = 0;}else {x->dist = x->right->dist + 1;}return x;}void TreeNode::Insert(TreeNode *&root,int te) {TreeNode *p = new TreeNode(te);if (root == NULL) {root = p;}else {root = TreeNode::Merge(root, p);}}void TreeNode::Delete(TreeNode *&root) {TreeNode *te = root;root = Merge(root->left, root->right);delete te;}void TreeNode::postOrder() {if (this == NULL) {return;}this->left->postOrder();this->right->postOrder();cout << " "<<this->data;}int main(){int te,n;TreeNode *leftistTree = NULL;cin >> n;for (int i = 0; i < n; i++) {cin >> te;    TreeNode::Insert(leftistTree,te);}leftistTree->postOrder();cout << endl;leftistTree->Delete(leftistTree);leftistTree->postOrder();cout << endl;    return 0;}

上面不好,修改了一下:

#include "stdafx.h"#include <cstdio>#include <cstring>#include <iostream>#include <fstream>#include <sstream>using namespace std;class TreeNode {private:int data;int dist;TreeNode *left, *right;public:TreeNode(int te) :data(te), dist(0), left(NULL), right(NULL) {}TreeNode *&merge(TreeNode *&p, TreeNode *&w);static void insert(TreeNode *&root, int te);TreeNode *&pop(TreeNode *&root);int top();void postOrder();};TreeNode *&TreeNode::merge(TreeNode *&p, TreeNode *&w) {if (p == NULL) {return w;}else if (w == NULL) {return p;}TreeNode *&x = p->data < w->data ? p : w;TreeNode *&y = x == p ? w : p;x->right = TreeNode::merge(x->right, y);if (x->left == NULL || x->left->dist < x->right->dist) {swap(x->left,x->right);}if (x->right == NULL) {x->dist = 0;}else {x->dist = x->right->dist + 1;}p = x;return p;}void TreeNode::insert(TreeNode *&root, int te) {TreeNode *p = new TreeNode(te);if (root == NULL) {root = p;}else {root = root->merge(root, p);}}TreeNode *&TreeNode::pop(TreeNode *&root) {TreeNode *te = root;root = merge(root->left, root->right);delete te;return root;}int TreeNode::top() {return this->data;}void TreeNode::postOrder() {if (this == NULL) {return;}this->left->postOrder();this->right->postOrder();cout << " " << this->data;}class LeftistTree {private:TreeNode *root;public:LeftistTree():root(NULL){}   void insert(int te) {   TreeNode::insert(root, te);   }   void pop() {   if (root == NULL) {   cerr << "ERROR" << endl;   }   root = root->pop(root);   }   void top() {   if (root == NULL) {   cerr << "ERROR" << endl;   return;   }   cout << root->top() << endl;   }   void merge(LeftistTree *&tree2) {   root = root->merge(root,tree2->root);   }   void postOrder() {   if (root == NULL) {   return;   }   root->postOrder();   }};int main() {int te, n;LeftistTree a;cin >> n;//5for (int i = 0; i < n; i++) {//8 4 5 6 2cin >> te;a.insert(te);}a.postOrder();//8 6 5 4 2cout << endl;a.pop();a.postOrder();//8 6 5 4cout << endl;return 0;}

指针实现的,一个左偏树的类,对应一个左偏树,而数组实现的,可有多个左偏树,节点不为其他节点的孩子的,即为一个左偏树的树根。以下的insert,top,pop,merge都要求以左偏树堆顶的编号代表这一左偏树。

数组实现:写的也是基于C++类封装的,未封装的略

#include "stdafx.h"#include <cstdio>#include <cstring>#include <iostream>#include <fstream>#include <sstream>using namespace std;#define MAXN 100005class LeftistTree {private:int len;int data[MAXN], left[MAXN], right[MAXN], dist[MAXN];public:LeftistTree():len(0) {memset(left, -1, sizeof(left));memset(right, -1, sizeof(right));memset(dist, -1, sizeof(dist));}int merge(int x, int y) {if (x == -1) {return y;}if (y == -1) {return x;}int a = data[x] < data[y] ? x : y;int b = a == x ? y : x;right[a] = merge(right[a], b);if (dist[left[a]] < dist[right[a]]) {swap(left[a], right[a]);}dist[a] = dist[right[a]] + 1;return a;}int insert(int x, int value) {//x指的是左偏树的堆顶data[len] = value;dist[len] = 0;return merge(x, len++);}int pop(int x) {return merge(left[x], right[x]);}int top(int x) {return data[x];}void postOrder(int x) {if (x == -1) {return;}postOrder(left[x]);postOrder(right[x]);cout << " " << data[x];}};int main() {int n,te,root = -1;LeftistTree *tree = new LeftistTree();cin >> n;//5for (int i = 0; i < n; i++) {//8 4 5 6 2cin >> te;root = tree->insert(root, te);}tree->postOrder(root);//8 6 5 4 2cout << endl;root = tree->pop(root);tree->postOrder(root);//8 6 5 4cout << endl;return 0;}




原创粉丝点击