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

伸展树(三)之 Java的实现

2014-04-01 22:18 2031 查看

概要

前面分别通过C和C++实现了伸展树,本章给出伸展树的Java版本。基本算法和原理都与前两章一样。
1. 伸展树的介绍
2. 伸展树的Java实现(完整源码)
3. 伸展树的Java测试程序

转载请注明出处:https://www.geek-share.com/detail/2607361883.html

更多内容: 数据结构与算法系列 目录

(01) 伸展树(一)之 图文解析 和 C语言的实现
(02) 伸展树(二)之 C++的实现
(03) 伸展树(三)之 Java的实现

伸展树的介绍

伸展树(Splay Tree)是特殊的二叉查找树。
它的特殊是指,它除了本身是棵二叉查找树之外,它还具备一个特点: 当某个节点被访问时,伸展树会通过旋转使该节点成为树根。这样做的好处是,下次要访问该节点时,能够迅速的访问到该节点。

伸展树的Java实现

1. 基本定义

public class SplayTree<T extends Comparable<T>> {

private SplayTreeNode<T> mRoot;    // 根结点

public class SplayTreeNode<T extends Comparable<T>> {
T key;                // 关键字(键值)
SplayTreeNode<T> left;    // 左孩子
SplayTreeNode<T> right;    // 右孩子

public SplayTreeNode() {
this.left = null;
this.right = null;
}

public SplayTreeNode(T key, SplayTreeNode<T> left, SplayTreeNode<T> right) {
this.key = key;
this.left = left;
this.right = right;
}
}

...
}


SplayTree是伸展树,而SplayTreeNode是伸展树节点。在此,我将SplayTreeNode定义为SplayTree的内部类。在伸展树SplayTree中包含了伸展树的根节点mRoot。SplayTreeNode包括的几个组成元素:
(01) key -- 是关键字,是用来对伸展树的节点进行排序的。
(02) left -- 是左孩子。
(03) right -- 是右孩子。

2. 旋转

旋转是伸展树中需要重点关注的,它的代码如下:

/*
* 旋转key对应的节点为根节点,并返回根节点。
*
* 注意:
*   (a):伸展树中存在"键值为key的节点"。
*          将"键值为key的节点"旋转为根节点。
*   (b):伸展树中不存在"键值为key的节点",并且key < tree.key。
*      b-1 "键值为key的节点"的前驱节点存在的话,将"键值为key的节点"的前驱节点旋转为根节点。
*      b-2 "键值为key的节点"的前驱节点存在的话,则意味着,key比树中任何键值都小,那么此时,将最小节点旋转为根节点。
*   (c):伸展树中不存在"键值为key的节点",并且key > tree.key。
*      c-1 "键值为key的节点"的后继节点存在的话,将"键值为key的节点"的后继节点旋转为根节点。
*      c-2 "键值为key的节点"的后继节点不存在的话,则意味着,key比树中任何键值都大,那么此时,将最大节点旋转为根节点。
*/
private SplayTreeNode<T> splay(SplayTreeNode<T> tree, T key) {
if (tree == null)
return tree;

SplayTreeNode<T> N = new SplayTreeNode<T>();
SplayTreeNode<T> l = N;
SplayTreeNode<T> r = N;
SplayTreeNode<T> c;

for (;;) {

int cmp = key.compareTo(tree.key);
if (cmp < 0) {

if (tree.left == null)
break;

if (key.compareTo(tree.left.key) < 0) {
c = tree.left;                           /* rotate right */
tree.left = c.right;
c.right = tree;
tree = c;
if (tree.left == null)
break;
}
r.left = tree;                               /* link right */
r = tree;
tree = tree.left;
} else if (cmp > 0) {

if (tree.right == null)
break;

if (key.compareTo(tree.right.key) > 0) {
c = tree.right;                          /* rotate left */
tree.right = c.left;
c.left = tree;
tree = c;
if (tree.right == null)
break;
}

l.right = tree;                              /* link left */
l = tree;
tree = tree.right;
} else {
break;
}
}

l.right = tree.left;                                /* assemble */
r.left = tree.right;
tree.left = N.right;
tree.right = N.left;

return tree;
}

public void splay(T key) {
mRoot = splay(mRoot, key);
}


上面的代码的作用:将"键值为key的节点"旋转为根节点,并返回根节点。它的处理情况共包括:
(a):伸展树中存在"键值为key的节点"。
将"键值为key的节点"旋转为根节点。
(b):伸展树中不存在"键值为key的节点",并且key < tree->key。
b-1) "键值为key的节点"的前驱节点存在的话,将"键值为key的节点"的前驱节点旋转为根节点。
b-2) "键值为key的节点"的前驱节点存在的话,则意味着,key比树中任何键值都小,那么此时,将最小节点旋转为根节点。
(c):伸展树中不存在"键值为key的节点",并且key > tree->key。
c-1) "键值为key的节点"的后继节点存在的话,将"键值为key的节点"的后继节点旋转为根节点。
c-2) "键值为key的节点"的后继节点不存在的话,则意味着,key比树中任何键值都大,那么此时,将最大节点旋转为根节点。

下面列举个例子分别对a进行说明。

在下面的伸展树中查找10,,共包括"右旋" --> "右链接" --> "组合"这3步。

/**
* Java 语言: 伸展树
*
* @author skywang
* @date 2014/02/03
*/
public class SplayTreeTest {

private static final int arr[] = {10,50,40,30,20,60};

public static void main(String[] args) {
int i, ilen;
SplayTree<Integer> tree=new SplayTree<Integer>();

System.out.print("== 依次添加: ");
ilen = arr.length;
for(i=0; i<ilen; i++) {
System.out.print(arr[i]+" ");
tree.insert(arr[i]);
}

System.out.print("\n== 前序遍历: ");
tree.preOrder();

System.out.print("\n== 中序遍历: ");
tree.inOrder();

System.out.print("\n== 后序遍历: ");
tree.postOrder();
System.out.println();

System.out.println("== 最小值: "+ tree.minimum());
System.out.println("== 最大值: "+ tree.maximum());
System.out.println("== 树的详细信息: ");
tree.print();

i = 30;
System.out.printf("\n== 旋转节点(%d)为根节点\n", i);
tree.splay(i);
System.out.printf("== 树的详细信息: \n");
tree.print();

// 销毁二叉树
tree.clear();
}
}


View Code

在二叉查找树的Java实现中,使用了泛型,也就意味着它支持任意类型;但是该类型必须要实现Comparable接口。

伸展树的Java测试程序

伸展树的测试程序运行结果如下:

== 依次添加: 10 50 40 30 20 60
== 前序遍历: 60 30 20 10 50 40
== 中序遍历: 10 20 30 40 50 60
== 后序遍历: 10 20 40 50 30 60
== 最小值: 10
== 最大值: 60
== 树的详细信息:
60 is root
30 is 60's   left child
20 is 30's   left child
10 is 20's   left child
50 is 30's  right child
40 is 50's   left child

== 旋转节点(30)为根节点
== 树的详细信息:
30 is root
20 is 30's   left child
10 is 20's   left child
60 is 30's  right child
50 is 60's   left child
40 is 50's   left child


测试程序的主要流程是:新建伸展树,然后向伸展树中依次插入10,50,40,30,20,60。插入完毕这些数据之后,伸展树的节点是60;此时,再旋转节点,使得30成为根节点。
依次插入10,50,40,30,20,60示意图如下:



将30旋转为根节点的示意图如下:

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