左偏树

左偏树(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;//5
	for (int i = 0; i < n; i++) {//8 4 5 6 2
		cin >> te;
		a.insert(te);
	}
	a.postOrder();//8 6 5 4 2
	cout << endl;
	a.pop();
	a.postOrder();//8 6 5 4
	cout << 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 100005
class 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;//5
	for (int i = 0; i < n; i++) {//8 4 5 6 2
		cin >> te;
		root = tree->insert(root, te);
	}
	tree->postOrder(root);//8 6 5 4 2
	cout << endl;
	root = tree->pop(root);
	tree->postOrder(root);//8 6 5 4
	cout << endl;
	return 0;
}




  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值