Java基础 - 伸展树(SplayTree)
2016-12-05 20:54
225 查看
伸展树的基本操作有:
1.查询
2.添加
3.删除
4.树的最大节点
5.树的最小节点
6.节点的前驱
7.节点的后继
8.合并
9.分离
下面是我完成的部分功能代码(除节点的后继,大家可以仿照求节点的前驱的方法来求后继,很简单):
下面是测试代码:
个人认为这是我写的最好的一个关于树,而且是平衡树的代码(因为前面毕竟写了那么多,傻子都会了)。
下面推荐几个大神的文章:
伸展树之图文解析;
伸展树-自顶向下。
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]]
个人认为这是我写的最好的一个关于树,而且是平衡树的代码(因为前面毕竟写了那么多,傻子都会了)。
下面推荐几个大神的文章:
伸展树之图文解析;
伸展树-自顶向下。
相关文章推荐
- 使用伸展树(SplayTree)统计单词频率的Java实现
- Java(JVM)虚拟机结构基础,和JAR文件包及jar命令详解
- Java动画编程基础第二部分
- 回复:学习java必须有c++基础么?
- (原创) 脚踏实地学Java之:基础篇
- Java 应了解的基础知识(转载)
- 基于Java的动画编程基础
- Java 101之线程基础
- Java Swing入门基础
- Java Swing入门基础 (转)
- Java动画编程基础第一部分
- Java基础之关键字
- 学java也快半年了,一直在看基础的东西,终于决定开始转J2ee了……
- Eclipse基础--java环境变量设置
- 第二讲 Java语言基础知识
- Java动画编程基础第四部分
- JAVA调试技术--基础
- Java初学及基础问题
- JAVA面试基础试题[转贴]
- Java 应了解的基础知识(转载)