Treap是用来排序(Sort)的一种数据结构(Data Structure)。
reap是随机查找二叉平衡树。 Treap发音为tree+ Heap。顾名思义, Treap把 BST和 Heap结合了起来。它和 BST一样满足许多优美的性质,而引入堆目的就是为了维护平衡。 Treap在 BST的基础上,添加了一个修正值。在满足 BST性质的上,Treap节点的修正值还满足最小堆性质。最小堆性质可以被描述为每个子树根节点都小于等于其子节点。
(1) Treap的特点 1. Treap简明易懂。Treap只有两种调整方式,左旋和右旋。而且即使没有严密的数学证明和分析,Treap的构造方法啊,平衡原理也是不难理解的。只要能够理解 BST和堆的思想,理解 Treap当然不在话下。 2. Treap易于编写。Treap只需维护一个满足堆序的修正值,修正值一经生成无需修改。相 比较其他各种平衡树, Treap拥有最少的调整方式,仅仅两种相互对称的旋转。所以 Treap当之无愧是最易于编码调试的一种平衡树。 3. Treap稳定性佳。Treap的平衡性虽不如 AVL,红黑树, SBT等平衡树,但是 Treap也不会退化,可以保证期望 O(logN)的深度。Treap的稳定性取决于随机数发生器。 4. Treap具有严密的数学证明。Treap期望 O(logN)的深度,是有严密的数学证明的。但这不是介绍的重点,大多略去。 5. Treap具有良好的实践效果。各种实际应用中, Treap的稳定性表现得相当出色,没有因为任何的构造出的数据而退化。于是在信息学竞赛中,不少选手习惯于使用 Treap,均取得了不俗的表现。
一棵treap是一棵修改了结点顺序的二叉查找树,如图,显示一个例子,通常树内的每个结点x都有一个关键字值key[x],另外,还要为结点分配priority[x],它是一个独立选取的随机数。
假设所有的优先级是不同的,所有的关键字也是不同的。treap的结点排列成让关键字遵循二叉查找树性质,并且优先级(有的地方也叫修正值,是一个随机数)遵循最小堆顺序性质:3.如果child是u的孩子,则priority[child] > priority[u].
4.如果priority[vi] < priority[vj].则vi相对于vj而言更接近根节点。
这两个性质的结合就是为什么这种树被称为“treap”的原因,因为它同时具有二叉查找树和堆的特征。(在关键字上它满足二叉排序树,在优先级上他满足小顶堆)。
用以下方式考虑treap会有帮助。假设插入关联关键字的结点x1,x2,...,xn到一棵treap内。结果的treap是将这些结点以它们的优先级(随机选取)的顺序插入一棵正常的二叉查找树形成的,亦即priority[xi] < priority[xj]表示xi在xj之前被插入。
在算法导论的12.4节中,其证明了随机构造的二叉查找树的期望高度为O(lgn),因而treap的期望高度亦是O(lgn)。
1.treap插入操作:
1.按照二叉树的插入方法,将结点插入到树中
2.根据堆的性质(我们这里为最小堆)和优先级的大小调整结点位置。
2.treap删除操作:
1.找到相应的结点
2.若该结点为叶子结点,则直接删除;
若该结点为只包含一个叶子结点的结点,则将其叶子结点赋值给它;
若该结点为其他情况下的节点,则进行相应的旋转,直到该结点为上述情况之一,然后进行删除。
要把一个Treap按大小分成两个Treap,只要在需要分开的位置加一个虚拟节点,然后旋至根节点删除,左右两个子树就是得出的两个Treap了。根据二叉搜索树的性质,这时左子树的所有节点都小于右子树的节点。时间相当于一次插入操作的复杂度,也就是O(log n)。
下面是代码实现:
package com.yc.tree;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Random;
public class THead >{
public class Node{
T data;
int priority; //随机权值(修改值fix)
Node parent;
Node left;
Node right;
public Node(T data, int priority, Node parent, Node left, Node right){
this.data = data;
this.priority = priority;
this.parent = parent;
this.left = left;
this.right = right;
}
public String toString(){
return "[data="+data+",priority="+priority+"]";
}
}
//根
private Node root;
//
private Random rd;
//
private static final int DEFAULT_RD = 1 << 10;
public THead(){
root = null;
}
public THead(T data){
rd = new Random();
root = new Node(data, rd.nextInt(DEFAULT_RD), null, null, null);
}
public THead(T data, int priority){
root = new Node(data, priority, null, null, null);
}
//按照随机的修改值添加节点
public void add(T data){
rd = new Random();
add(data, rd.nextInt(DEFAULT_RD));
}
//按照指定的修改值添加节点
public void add(T data, int priority){
Node newNode = new Node(data, priority, null, null, null);
if(root == null){
root = newNode;
}else{
//找新插入节点的所属的父节点
Node current = root;
Node parent = current;
int result = 0;
while(current != null){
parent = current;
result = data.compareTo(current.data);
if(result > 0){ //新插入节点的data域大于当前节点的data域(这里也埋下了当data域相等时会把新节点往左子树插)
current = current.right;
}else{
current = current.left;
}
}
if(result > 0){
parent.right = newNode;
}else{
parent.left = newNode;
}
newNode.parent = parent;
parent = null; //释放临时节点
reBalanceForAdd(newNode);
}
}
//删除节点
public void remove(T data){
Node node = getNode(data);
if(node != null){
if(node.left == null && node.right == null){ //如果是叶子节点
removeNeitherLeftAndRight(node);
}else if(node.left != null && node.right == null){ //只有左子节点
removeOnlyHasLeft(node);
}else if(node.right != null && node.left == null){ //只有右子节点
removeOnlyHasRight(node);
}else{ //既有左子树又有右子树
removeHaveLeftAndRight(node);
}
}
}
//最大值
public T maxData(){
Node current = root;
Node maxNode = current;
while(current != null){
maxNode = current;
current = current.right;
}
if(maxNode != null){
return maxNode.data;
}else{
return null;
}
}
//最小值
public T minData(){
Node current = root;
Node minNode = current;
while(current != null){
minNode = current;
current = current.left;
}
if(minNode != null){
return minNode.data;
}else{
return null;
}
}
//前驱
public T frontData(T data){
Node current = root;//当前节点
Node frontNode = null;//前驱节点
int result = 0;
while(current != null){
result = data.compareTo(current.data);
if(result == 0){
return current.data;
}else if(result > 0){
frontNode = current;
current = current.right;
}else{
current = current.left;
}
}
if(frontNode != null){
return frontNode.data;
}
return null;
}
//后继
public T descendantData(T data){
Node current = root;//当前节点
Node frontNode = null;//后继节点
int result = 0;
while(current != null){
result = data.compareTo(current.data);
if(result == 0){
return current.data;
}else if(result < 0){
frontNode = current;
current = current.left;
}else{
current = current.right;
}
}
if(frontNode != null){
return frontNode.data;
}
return null;
}
//8、当前节点子树的大小
//9、查找第K小元素
//10、求某个元素的排名
//对于上面的三个问题需要在Node节点添加两个域Size(表示子树中节点个数),ctn(表示子树中重复Data域的节点个数)
//这里就不做了,希望有兴趣的同学去完成
private void removeNeitherLeftAndRight(Node node){
if(root == node){
root = null;
}else{
if(node == node.parent.left){
node.parent.left = null;
node.parent = null;
}else{
node.parent.right = null;
node.parent = null;
}
}
}
private void removeOnlyHasLeft(Node node){
node.left.parent = node.parent;
if(node.parent != null){
if(node == node.parent.left){
node.parent.left = node.left;
}else{
node.parent.right = node.left;
}
}
if(root == node){
root = node.left;
}
node.parent = node.left = node.right = null;
}
private void removeOnlyHasRight(Node node){
node.right.parent = node.parent;
if(node.parent != null){
if(node == node.parent.left){
node.parent.left = node.right;
}else{
node.parent.right = node.right;
}
}
if(root == node){
root = node.right;
}
node.parent = node.left = node.right = null;
}
private void removeHaveLeftAndRight(Node node){
int result = 0;
while(node.left != null && node.right != null){
result = node.left.priority - node.right.priority;
if(result <= 0){ //左子节点的修改值小于等于右子节点,则进行右旋
type_right(node.left);
}else{
type_left(node.right);
}
}
if(node.left == null && node.right == null){ //如果是叶子节点
removeNeitherLeftAndRight(node);
}else if(node.left != null && node.right == null){ //只有左子节点
removeOnlyHasLeft(node);
}else if(node.right != null && node.left == null){ //只有右子节点
removeOnlyHasRight(node);
}
}
//添加节点后都THead进行修复
private void reBalanceForAdd(Node node) {
//1.递归
if(node.parent != null){
int result = node.priority - node.parent.priority;
if(result < 0){
if(node == node.parent.left){
type_right(node);
reBalanceForAdd(node);
}else if(node == node.parent.right){
type_left(node);
reBalanceForAdd(node);
}
}else{
return;
}
}
//2.循环(楼主不会写QAQ.)
/*int result = node.priority - node.parent.priority;
while(node.parent != null && result < 0){
if(result < 0){
if(node == node.parent.left){
type_right(node);
}else{
type_left(node);
}
}
}*/
}
/**
* 节点右旋
* @param node
*
* │ │
* p─┘ └─l
* ││ -> ││
* l─┘└ ─┘└─p
* ││ ││
* ─┘└ ─┘└
*
*/
private void type_right(Node l){
Node p = l.parent;
l.parent = p.parent;
if(p.parent != null){
if(p == p.parent.left){
p.parent.left = l;
}else{
p.parent.right = l;
}
}
p.left = l.right;
if(l.right != null){
l.right.parent = p;
}
l.right = p;
p.parent = l;
if(root == p){
root = l;
}
}
/**
* 节点左旋
* @param r
*
* │ │
* └─p r─┘
* ││ -> ││
* ─┘└─r p─┘└─
* ││ ││
* ─┘└─ ─┘└─
*/
private void type_left(Node r){
Node p = r.parent;
r.parent = p.parent;
if(p.parent != null){
if(p == p.parent.left){
p.parent.left = r;
}else{
p.parent.right = r;
}
}
p.right = r.left;
if(r.left != null){
r.left.parent = p;
}
r.left = p;
p.parent = r;
if(root == p){
root = r;
}
}
//获得指定元素的节点
private Node getNode(T data){
Node current = root;
if(current == null){
return null;
}else{
int result = 0;
while(current != null){
result = data.compareTo(current.data);
if(result > 0){
current = current.right;
}else if(result < 0){
current = current.left;
}else{
return current;
}
}
return null;
}
}
//广度优先遍历
public List breadthFirstSearch(){
return cBreadthFirstSearch(root);
}
private List cBreadthFirstSearch(Node node) {
List nodes = new ArrayList();
Deque deque = new ArrayDeque();
if(node != null){
deque.offer(node);
}
while(!deque.isEmpty()){
Node first = deque.poll();
nodes.add(first);
if(first.left != null){
deque.offer(first.left);
}
if(first.right != null){
deque.offer(first.right);
}
}
return nodes;
}
public static void main(String[] args) {
THead tree = new THead();
tree.add(12, 6);
tree.add(8, 12);
tree.add(23, 20);
tree.add(16, 3);
tree.add(45, 18);
tree.add(2, 7);
tree.add(9, 42);
tree.add(16, 15);
System.out.println( tree.breadthFirstSearch());
System.out.println("9的前驱是:" + tree.frontData(9));
System.out.println("9的后继是:" + tree.descendantData(9));
tree.remove(12);
System.out.println( tree.breadthFirstSearch());
}
}
[[data=16,priority=3], [data=12,priority=6], [data=45,priority=18], [data=2,priority=7], [data=16,priority=15], [data=23,priority=20], [data=8,priority=12], [data=9,priority=42]]
9的前驱是:9
9的后继是:9
[[data=16,priority=3], [data=2,priority=7], [data=45,priority=18], [data=8,priority=12], [data=23,priority=20], [data=16,priority=15], [data=9,priority=42]]