您的位置:首页 > 编程语言 > Java开发

Java基础 - 伸展树(SplayTree)

2016-12-05 20:54 225 查看
伸展树的基本操作有:

1.查询

2.添加

3.删除

4.树的最大节点

5.树的最小节点

6.节点的前驱

7.节点的后继

8.合并

9.分离

下面是我完成的部分功能代码(除节点的后继,大家可以仿照求节点的前驱的方法来求后继,很简单):

package com.yc.tree;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

public class SplayTree <T extends Comparable<T>>{
public class Node{
T data;
Node parent;
Node left;
Node right;
public Node(T data, Node parent, Node left, Node right){
this.data = data;
this.parent = parent;
this.left = left;
this.right = right;
}
public String toString(){
return "[data="+data+"]";
}
}
//根节点
private Node root;

public SplayTree(){
root = null;
}
public SplayTree(T data){
root = new Node(data, null, null, null);
}
/**
* 添加节点
* @param data
*
* 添加节点:跟二叉排序树一样添加节点,不过与二叉排序树添加节点不同的是伸展树添加节点后,
* 会把新添加的节点旋转到根节点位置。
*/
public void add(T data){
Node current = root;
if(current == null){
root = new Node(data, null, null, null);
}
else{
int result = 0;
Node parent = current;
while(current != null){
parent = current;
result = data.compareTo(current.data);
if(result > 0){
current = current.right;
}else{
current = current.left;
}
}
Node newNode = new Node(data, parent, null, null);
if(result > 0){
parent.right = newNode;
}else{
parent.left = newNode;
}

splay(newNode, this);
}
}

/**
* 删除节点
* @param data
* 删除节点:从SplayTree中找出要删除的节点,然后将该节点旋转为根节点,然后再把此时的根节点的左子树中
* 的最大值节点(前驱)旋转为根节点的左子节点(这样根节点的左子节点的子节点只会有左子树,因为它最大),紧接着把根节点
* 的右节点当做根节点的左子节点的右子节点,最后在 删除根节点(也就是要删除的节点)。
*/
public void remove(T data){
//找到要删除的节点
Node del = find(data);
if(del == null){
return;
}else{
//把要删除的节点旋转为根节点
splay(del, this);
//找到此时根节点的前驱
Node frontNodeOfRoot = frontOfNode(root.data);
//把跟的前驱旋转为根节点的左子节点
splayToRootLeft(frontNodeOfRoot, this);
//
root.left.right = root.right;
if(root.right != null){
root.right.parent = root.left;
}
root = root.left;

root.parent.left = root.parent.right = null;
root.parent = null;
}
}
/**
* 将tree2合并到tree1为tree
* @param tree
* Join(tree2):与伸展树tree2合并。其中tree1的所有元素都小于tree2的所有元素。
* 首先,找到伸展树tree1中的最大元素x,再通过Splay(x,tree1)将x调整为tree1的根。
* 然后将tree2作为x节点的右子树。这样,就得到了新伸展树tree。
*/
public void join(SplayTree<T> tree){
//找到该树的最大节点
Node maxNode = maxNode();
//将maxNode调整为该树的根
splay(maxNode, this);
if(tree.minNode().data.compareTo(root.data) < 0){
try {
throw new Exception("合并的两棵树不满足要求:"+tree.minNode().data +" 小于 " + root.data);
} catch (Exception e) {
e.printStackTrace();
}
}else{
root.right = tree.root;
if(tree.root != null){
tree.root.parent = root;
}
}
}
/**
* Split(x):以x为界,将伸展树tree分离为两棵伸展树tree1和tree2,其中tree1中所有元素都小于x,
* tree2中的所有元素都大于x。首先执行Find(x),将元素x调整为伸展树的根节点,则x的左子树就是tree1,而右子树为tree2。
*/
public List<SplayTree<T>> split(T data){
List<SplayTree<T>> trees = new ArrayList<SplayTree<T>>();
SplayTree<T> tree1;
SplayTree<T> tree2;

Node node = find(data);
if(node == null){ //如果找不到等于该值的节点
//就把他的前驱节点给它
node = frontOfNode(data);
//将前驱节点旋转为根节点
splay(node, this);
tree1 = new SplayTree<T>();
tree2 = new SplayTree<T>();
tree1.root = root;
tree2.root = root.right;

if(root.right != null){
root.right.parent = null;
}
root.right = null;

trees.add(tree1);
trees.add(tree2);
}else{ //如果找到了该节点
//将该节点旋转为根节点
splay(node, this);
tree1 = new SplayTree<T>();
tree2 = new SplayTree<T>();
tree1.root = root.left;
tree2.root = root.right;

if(root.left != null){
root.left.parent = null;
}
if(root.right != null){
root.right.parent = null;
}
root.left = null;
root.right = null;

trees.add(tree1);
trees.add(tree2);
}
return trees;
}
//该树的最大节点
public Node maxNode(){
if(root == null){
return null;
}else{
Node current = root;
Node maxNode = null;
while(current != null){
maxNode = current;
current = current.right;
}
return maxNode;
}
}
//该树的最小节点
public Node minNode(){
if(root == null){
return null;
}else{
Node current = root;
Node minNode = null;
while(current != null){
minNode = current;
current = current.left;
}
return minNode;
}
}
//把根的前驱旋转为根节点的左子节点
private void splayToRootLeft(Node node, SplayTree<T> tree) {
if(node != null){
while(node.parent != root && node.parent != null){
if(node == node.parent.left){
if(node.parent.parent == null){
tree.zig(node);
}else if(node.parent.parent != null && node.parent == node.parent.parent.left){ //zig-zig
tree.zig_zig(node);
}else if(node.parent.parent != null && node.parent == node.parent.parent.right){
tree.zig_zag(node);
}
}else if(node == node.parent.right){
if(node.parent.parent == null){
tree.zag(node);
}else if(node.parent.parent != null && node.parent == node.parent.parent.right){ //zag-zag
tree.zag_zag(node);
}else if(node.parent.parent != null && node.parent == node.parent.parent.left){
tree.zag_zig(node);
}
}else{
//nikansha?
}
}
}
}
/**
* 添加节点后对树的改变(将新添加的节点旋转为根节点)
* @param node
* @param tree
*/
private void splay(Node node, SplayTree<T> tree) {//首先要明确一点,该方法只会在根不为空的判断里面出现
if(node != null){
while(node.parent != null){
if(node == node.parent.left){
if(node.parent.parent == null){
tree.zig(node);
}else if(node.parent.parent != null && node.parent == node.parent.parent.left){ //zig-zig
tree.zig_zig(node);
}else if(node.parent.parent != null && node.parent == node.parent.parent.right){
tree.zig_zag(node);
}
}else if(node == node.parent.right){
if(node.parent.parent == null){
tree.zag(node);
}else if(node.parent.parent != null && node.parent == node.parent.parent.right){ //zag-zag
tree.zag_zag(node);
}else if(node.parent.parent != null && node.parent == node.parent.parent.left){
tree.zag_zig(node);
}
}else{
//nikansha?
}
}
}
}

/**
*	搜索节点
* @param data
* @return
*/
private Node find(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 Node frontOfNode(T data){
Node node = find(data);
if(root == null){
return null;
}else{
Node current = node;
Node frontNode = null;  //前驱节点
int result = 0;
while(current != null){
result = node.data.compareTo(current.data);
if(result > 0){
frontNode = current;
current = current.right;
}else if(result < 0){
current = current.left;
}else{
return current;
}
}
return frontNode;
}
}
/**
* zig
* @param node
* 		root->  y			root->	x
* 				││					││
* 			x───┘└   ->			────┘└──y
* 			││							││
* 		────┘└						────┘└
*/
private void zig(Node x){
Node y = x.parent;
x.parent = y.parent;
root = x;
y.left = x.right;
if(x.right != null){
x.right.parent = y;
}
x.right = y;
y.parent = x;
}
/**
* zag(与zig相反)
* @param node
*/
private void zag(Node x){
Node y = x.parent;
x.parent = y.parent;
root = x;
y.right = x.left;
if(x.left != null){
x.left.parent = y;
}
x.left = y;
y.parent = x;
}
/**
* zig-zig
* @param x
* 				│		│
* 				z		x
* 				││		││
* 			y───┘└ 	-》	┘└──y
* 			││				││
* 		x───┘└ 				┘└─z
* 	───┘└ 					───┘└
*
*/
private void zig_zig(Node x){
Node y = x.parent;
Node z = y.parent;

x.parent = z.parent;
if (z.parent != null) {
if(z.parent.left == z){
z.parent.left = x;
}else{
z.parent.right = x;
}
}

y.left = x.right;
if(x.right != null){
x.right.parent = y;
}
z.left = y.right;
if(y.right != null){
y.right.parent = z;
}

y.right = z;
z.parent = y;

x.right = y;
y.parent = x;

if(x.parent == null){
root = x;
}
}
/**
* zag-zag(与zig-zig相反)
* @param x
*/
private void zag_zag(Node x){
Node y = x.parent;
Node z = y.parent;

x.parent = z.parent;
if (z.parent != null) {
if(z.parent.left == z){
z.parent.left = x;
}else{
z.parent.right = x;
}
}

y.right = x.left;
if(x.left != null){
x.left.parent = y;
}
z.right = y.left;
if(y.left != null){
y.left.parent = z;
}

y.left = z;
z.parent = y;

x.left = y;
y.parent = x;

if(x.parent == null){
root = x;
}
}
/**
* zig-zag(跟AVL树中的双向左旋一样?)
* @param x
* 		│					│
* 		z					x
* 		││					││
* 		┘└──y	-》		z───┘└──y
* 			││			┘└─		┘└─
* 		x───┘└─
* 		┘└─
*/
private void zig_zag(Node x){
Node y = x.parent;
Node z = y.parent;

x.parent = z.parent;
if(z.parent != null){
if(z == z.parent.left){
z.parent.left = x;
}else{
z.parent.right = x;
}
}

z.right = x.left;
if(x.left != null){
x.left.parent = z;
}
y.left = x.right;
if(x.right != null){
x.right.parent = y;
}

x.left = z;
z.parent = x;
x.right = y;
y.parent = x;

if(x.parent == null){
root = x;
}
}
/**
* zag-zig(跟zig-zag相反)
* @param x
*/
private void zag_zig(Node x){
Node y = x.parent;
Node z = y.parent;

x.parent = z.parent;
if(z.parent != null){
if(z == z.parent.left){
z.parent.left = x;
}else{
z.parent.right = x;
}
}

z.left = x.right;
if(x.right != null){
x.right.parent = z;
}
y.right = x.left;
if(x.left != null){
x.left.parent = y;
}

x.right = z;
z.parent = x;
x.left = y;
y.parent = x;

if(x.parent == null){
root = x;
}
}
//广度优先遍历
public List<Node> breadthFirstSearch(){
return cBreadthFirstSearch(root);
}
private List<Node> cBreadthFirstSearch(Node node) {
List<Node> nodes = new ArrayList<Node>();
Deque<Node> deque = new ArrayDeque<Node>();
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;
}
}


下面是测试代码:

package com.yc.test;

import java.util.List;

import com.yc.tree.SplayTree;

public class SplayTreeTest {
public static void main(String[] args) {
SplayTree<Integer> tree1 = new SplayTree<Integer>();
tree1.add(20);
System.out.println( tree1.breadthFirstSearch());
tree1.add(18);
System.out.println( tree1.breadthFirstSearch());
tree1.add(22);
System.out.println( tree1.breadthFirstSearch());
tree1.add(24);
System.out.println( t
eda9
ree1.breadthFirstSearch());
tree1.add(23);
System.out.println( tree1.breadthFirstSearch());

tree1.remove(22);
System.out.println( tree1.breadthFirstSearch());
System.out.println();

SplayTree<Integer> tree2 = new SplayTree<Integer>();
//tree2.add(23);
tree2.add(25);

tree1.join(tree2);
System.out.println(tree1.breadthFirstSearch());
System.out.println();

List<SplayTree<Integer>> trees = tree1.split(23);
System.out.println( "分离后的两棵树为:tree1=" + trees.get(0).breadthFirstSearch() + ",\ttree2=" + trees.get(1).breadthFirstSearch());
}

}
测试结果为:

[[data=20]]
[[data=18], [data=20]]
[[data=22], [data=20], [data=18]]
[[data=24], [data=22], [data=20], [data=18]]
[[data=23], [data=22], [data=24], [data=20], [data=18]]
[[data=20], [data=18], [data=23], [data=24]]

[[data=24], [data=23], [data=25], [data=20], [data=18]]

分离后的两棵树为:tree1=[[data=20], [data=18]],	tree2=[[data=24], [data=25]]


个人认为这是我写的最好的一个关于树,而且是平衡树的代码(因为前面毕竟写了那么多,傻子都会了)。

下面推荐几个大神的文章:

伸展树之图文解析;

伸展树-自顶向下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: