【算法导论学习-25】 二叉树专题3:Treaps

参考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)

时间: 2025-01-13 23:08:20

【算法导论学习-25】 二叉树专题3:Treaps的相关文章

算法导论学习---红黑树具体解释之插入(C语言实现)

前面我们学习二叉搜索树的时候发如今一些情况下其高度不是非常均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜索树,它通过在每一个结点附加颜色位和路径上的一些约束条件能够保证在最坏的情况下基本动态集合操作的时间复杂度为O(nlgn).以下会总结红黑树的性质,然后分析红黑树的插入操作,并给出一份完整代码. 先给出红黑树的结点定义: #define RED 1 #define BLACK 0 ///红黑树结点定义,与普通

算法导论学习---红黑树详解之插入(C语言实现)

前面我们学习二叉搜索树的时候发现在一些情况下其高度不是很均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜索树,它通过在每个结点附加颜色位和路径上的一些约束条件可以保证在最坏的情况下基本动态集合操作的时间复杂度为O(nlgn).下面会总结红黑树的性质,然后分析红黑树的插入操作,并给出一份完整代码. 先给出红黑树的结点定义: #define RED 1 #define BLACK 0 ///红黑树结点定义,与普通的二

【算法导论学习-016】两个已排过序的等长数组的中位数(median of two sorted arrays)

问题来源 <算法导论>P223 9.3-8: Let X[1..n] and Y[1..n] be two arrays, each containing nnumbers already in sorted order. Give an O(lgn)-time algorithm to find themedian of all 2n elements in arrays X and Y. 翻译过来即:求两个等长(n个元素)的已排序数组A和B的中位数 方案1:对两个数组进行归并直到统计到第n

【算法导论学习-015】数组中选择第i小元素(Selection in expected linear time)

1.算法思想 问题描述:从数组array中找出第i小的元素(要求array中没有重复元素的情况),这是个经典的"线性时间选择(Selection in expected linear time)"问题. 思路:算法导论215页9.2 Selection in expect linear time 2.java实现 思路:算法导论216页伪代码 /*期望为线性时间的选择算法,输入要求,array中没有重复的元素*/ public static int randomizedSelect(i

算法导论 学习资源

学习的过程会遇到些问题,发现了一些比较好的资源,每章都会看下别人写的总结,自己太懒了,先记录下别人写的吧,呵呵. 1  Tanky Woo的,每次差不多都看他的 <算法导论>学习总结 - 1.前言 <算法导论>学习总结 - 2.第一章 && 第二章 && 第三章 <算法导论>学习总结 - 3.第四章 && 第五章 <算法导论>学习总结 - 4.第六章(1) 堆排序 <算法导论>学习总结 - 5.第六

【算法导论学习-014】计数排序(CountingSortTest)

参考:<算法导论>P194页 8.2节 Counting sort 1.Counting sort的条件 待排序数全部分布在0~k之间,且k是已知数:或者分布在min~max之间,等价于分布在0~max-min之间,max和min是已知数. 2.java 实现 /** * 创建时间:2014年8月17日 下午3:22:14 项目名称:Test * * @author Cao Yanfeng * @since JDK 1.6.0_21 类说明: 计数排序法,复杂度O(n), 条件:所有数分布在0

【算法导论学习-015】基数排序(Radix sort)

1.<算法导论>P197页 8.3节Radix sort 2.java实现 这里仅仅对[算法导论学习-014]计数排序 的参数进行了修改,同时仅仅修改了一行代码. /** * 创建时间:2014年8月17日 下午4:05:48 * 项目名称:Test * @author Cao Yanfeng * @since JDK 1.6.0_21 * 类说明: 利用计数排序实现基数排序 * 条件:待排序的所有数位数相同,注意,即便不相同,也可以认为是最多那个位数,如下面的例子可以认为都是3位数 */ p

【算法导论学习-012】n个数随机等概率的抽样m个

算法法导论>P129页课后题5.3-7 suppose we want to create a random sample of the set {1,2,3,-,n}, thatis, an m-element subset S, where0≤m≤n, such that each m-subset is equally likely to be created. One waywould be to set A[i]=i for i=1,2,3,-,n, call RANDOMIZE-IN

【算法导论学习笔记】第3章:函数的增长

????原创博客,转载请注明:http://www.cnblogs.com/wuwenyan/ ? 当算法的输入n非常大的时候,对于算法复杂度的分析就显得尤为重要,虽然有时我们能通过一定的方法得到较为精确的运行时间,但是很多时候,或者说绝大多数时候,我们并不值得去花精力求得多余的精度,因为精确运行时间中的倍增常量和低阶项已经被输入规模本身的影响所支配.我们需要关心的是输入规模无限增加,在极限中,运行时间是如何随着输入规模增大而增加的,通常来说,在极限情况下渐进地更优的算法在除很小的输入外的所有情