参考1:算法导论333页
一、Treaps介绍
参考2: http://www.cnblogs.com/huangxincheng/archive/2012/07/30/2614484.html
1. 为什么要用Treaps
1) Treap简明易懂。Treap只有两种调整方式,左旋和右旋。而且即使没有严密的数学证明和分析,Treap的构造方法啊,平衡原理也是不难理解的。只要能够理解 BST和堆的思想,理解Treap当然不在话下。
2) Treap易于编写。Treap只需维护一个满足堆序的修正值,修正值一经生成无需修改。相比较其他各种平衡树, Treap拥有最少的调整方式,仅仅两种相互对称的旋转。所以 Treap当之无愧是最易于编码调试的一种平衡树。
3) Treap稳定性佳。Treap的平衡性虽不如 AVL,红黑树,SBT等平衡树,但是 Treap也不会退化,可以保证期望 O(logN)的深度。
4) Treap具有严密的数学证明。Treap期望O(logN)的深度,是有严密的数学证明的。但这不是介绍的重点,大多略去。
5) Treap具有良好的实践效果。各种实际应用中, Treap的稳定性表现得相当出色,没有因为任何的构造出的数据而退化。于是在信息学竞赛中,不少选手习惯于使用 Treap,均取得了不俗的表现。
二、Treaps的java实现
参考1:http://www.blogjava.net/xmatthew/archive/2012/05/16/347297.html
参考2:http://www.cnblogs.com/huangxincheng/archive/2012/07/30/2614484.html
1、节点类
注意,这里的节点类中的prioity其实应该是随机生成的,但是这里给注释掉了,同时开放了setPrioity(int prioity)函数用于人工设置prioity的值,方便模拟。
/**
* @author 曹艳丰
*类说明:主要既要有key,又要有prioity
*/
public class TreapsNode<T> {
private int key;
private T satelliteData;
private int prioity;
private TreapsNode<T> leftChild,rightChild;
/*构造一个没有左右子树的treapNod作为treap的root节点*/
public TreapsNode(int key,T satelliteData) {
// TODO 自动生成的构造函数存根
// prioity=new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE);
// prioity=new Random(System.currentTimeMillis()).nextInt(20);
prioity=new Random().nextInt(20);
this.key=key;
this.satelliteData=satelliteData;
leftChild=rightChild=null;
}
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
public T getSatelliteData() {
return satelliteData;
}
public void setSatelliteData(T satelliteData) {
this.satelliteData = satelliteData;
}
public TreapsNode<T> getLeftChild() {
return leftChild;
}
public void setLeftChild(TreapsNode<T> leftChild) {
this.leftChild = leftChild;
}
public TreapsNode<T> getRightChild() {
return rightChild;
}
public void setRightChild(TreapsNode<T> rightChild) {
this.rightChild = rightChild;
}
public int getPrioity() {
return prioity;
}
public void setPrioity(int prioity) {
this.prioity = prioity;
}
@Override
public String toString() {
// TODO 自动生成的方法存根
return "key--"+key+";satelliteData"+satelliteData+";prioity"+prioity;
}
}
2、treaps的主体,实现了add()函数
/**
* @author 曹艳丰
* 类说明:只实现了add()函数
*/
public class Treap<T> {
TreapsNode<T> root;
public Treap() {
// TODO 自动生成的构造函数存根
root=null;
}
public Treap(TreapsNode<T> root) {
// TODO 自动生成的构造函数存根
this.root=root;
}
/*插入过程,只用判断两种情况 */
public TreapsNode<T> add(TreapsNode<T> root,TreapsNode<T> node){
if (root==null) {
root=new TreapsNode<T>(node.getKey(), node.getSatelliteData());
}
if (node.getKey()<root.getKey()) {
TreapsNode<T> tempNode=add(root.getLeftChild(), node);
root.setLeftChild(tempNode);
//插入点为最后一次递归,根据小根堆性质,需要”左左情况旋转”
if (root.getLeftChild().getPrioity()<root.getPrioity()) {
root=rightRotate(root);
}
}
if (node.getKey()>root.getKey()) {
TreapsNode<T> tempNode=add(root.getRightChild(), node);
root.setRightChild(tempNode);
//根据小根堆性质,需要”右右情况旋转”
if (root.getRightChild().getPrioity()<root.getPrioity()) {
root=leftRotate(root);
}
}
// if (node.getKey()==root.getKey()) {
// root.setLeftChild(node);
// }
return root;
}
public void add(TreapsNode<T> node){
root=add(root, node);
}
/*右旋*/
public TreapsNode<T> rightRotate(TreapsNode<T> node){
/*tempNode是插入的节点,node是其父节点*/
TreapsNode<T> tempNode=node.getLeftChild();
node.setLeftChild(tempNode.getRightChild());
tempNode.setRightChild(node);
return tempNode;
}
/*左旋*/
public TreapsNode<T> leftRotate(TreapsNode<T> node){
/*tempNode是插入的节点,node是其父节点*/
TreapsNode<T> tempNode=node.getRightChild();
node.setRightChild(tempNode.getLeftChild());
tempNode.setLeftChild(node);
return tempNode;
}
public void inOrderTreeWalk(TreapsNode<T> node) {
if (node != null) {
inOrderTreeWalk(node.getLeftChild());
System.out.println(node.getKey() + ":"
+ node.getSatelliteData());
inOrderTreeWalk(node.getRightChild());
}
}
/*☆较简单——前序遍历递归过程 */
private void recursePreOrder(TreapsNode<T> node) {
if (node != null) {
System.out.println(node.getKey() + ":"
+ node.getSatelliteData());
recursePreOrder(node.getLeftChild());
recursePreOrder(node.getRightChild());
}
}
/*☆较简单——前序遍历递归过程 */
public void recursePreOrder() {
recursePreOrder(root);
}
public void inOrderTreeWalk() {
inOrderTreeWalk(root);
}
}
3、测试
public class TreapsTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成的方法存根
TreapsNode<String> root=new TreapsNode<String>(7, "G");
root.setPrioity(4);
TreapsNode<String> node1=new TreapsNode<String>(2, "B");
node1.setPrioity(2);
TreapsNode<String> node2=new TreapsNode<String>(1, "A");
node2.setPrioity(1);
TreapsNode<String> node3=new TreapsNode<String>(5, "E");
node3.setPrioity(23);
TreapsNode<String> node5=new TreapsNode<String>(8, "H");
node5.setPrioity(5);
TreapsNode<String> node6=new TreapsNode<String>(11, "K");
node6.setPrioity(63);
TreapsNode<String> node8=new TreapsNode<String>(9, "I");
node8.setPrioity(73);
Treap<String> treap=new Treap<>(root);
treap.add(node1);
treap.add(node2);
treap.add(node3);
treap.add(node5);
treap.add(node6);
treap.add(node8);
treap.recursePreOrder();
}
}
**************************************************************
控制台输出:前序和中序符合算法导论中335页图13.10(a)