Java基础 - 伸展树(SplayTree)

本文深入探讨伸展树的基本操作及其实现方法,包括添加、删除、查询等,并提供了详细的代码示例。同时,文章还介绍了伸展树的合并与分离等高级操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

伸展树的基本操作有:

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( tree1.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]]


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

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

伸展树之图文解析;

伸展树-自顶向下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值