BST(二叉排序树)Java实现 洛谷P5076

本人有点菜 ,如果有什么优化方法大家都可以说说

因为之前学的C++的数据结构,现在来写Java的数据结构没有指针,真的有点头疼,感觉有点难我太菜了 ,写了2天都,下面来给大家介绍一下什么是二叉排序树。
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。 // 这个点我们可以实现有相等的结点,那是不是说我们写的是一棵假的二叉排序树

我们这里实现了一些二叉树别的功能
(1)我们可以根据输入的排名,找到具体的值
(2)我们可以根据值,找到他的排名
(3)即使数上没有这个值,我们也可以返回他的排名

首先就是二叉排序树的建立过程,即插入过程,这里我们首先用BNode类来存储二叉树的结点,用BiTree类来实现二叉排序树的具体方法
来看代码:
主类:

public class bitreesort {
	static int n;
	static int INF = 0X7fffffff;
	public static void main(String[] args) throws IOException{
		StreamTokenizer re = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter pr = new PrintWriter(new OutputStreamWriter(System.out));
		BiTree bitree = new BiTree();
		bitree.root = new BNode(-1); // 为根结点创建空间
		int datas[] = new int[]{9,3,1,7,8,6,10,17,13,7}; // 这里我们有2个7 也就是第一次删除的时候只会删掉一个,第二次才会彻底删掉这个结点哦
		for(int i = 0;i < datas.length;i++)
			bitree.insert(bitree.root, datas[i]);
		bitree.prefound(bitree.root);
		bitree.delete(bitree.root,7);
		System.out.println();
		bitree.prefound(bitree.root);
		bitree.delete(bitree.root,7);
		System.out.println();
		bitree.prefound(bitree.root);
		// 下面是为了洛谷一个题目的代码,如果不是写题的话这里我们可以不看
//		re.nextToken(); n = (int)re.nval;
//		for(int i = 0;i < n;i++){
//			re.nextToken(); int a = (int)re.nval;
//			re.nextToken(); int b = (int)re.nval;
//			if(a == 1)
//				pr.println(bitree.keytorank(bitree.root, b));
//			if(a == 2)
//				pr.println(bitree.ranktokey(bitree.root, b));
//			if(a == 3)
//				pr.println(bitree.prenode(bitree.root, b,-INF));
//			if(a == 4)
//				pr.println(bitree.aftnode(bitree.root,b,INF));
//			if(a == 5)
//				bitree.insert(bitree.root, b);
//		}
		pr.flush();
	}
}

BNode类

class BNode{
	int key; // 权值
	int cnt; // 这个值的点有多少个
	int size; // 这颗树有多少个结点  为了根据rank找值或者根据值找rank使用
	BNode lchild; // 这个就不解释了
	BNode rchild;
	BNode(int k){
		this.key = k;
		this.lchild = null;
		this.rchild = null;
	}
}

插入功能的实现:

void insert(BNode node,int data){ // 插入
		node.size++;
		if(node.key == -1){ // 如果这个值为我们标记的一个值,表示是空的
		// 那么我们从这里建立这颗二叉树
		// 大家千万不要在这里写如果node == null  然后写  node = new...之类的,这样就炸了,具体我也不太会解释,期待大佬的解释
			node.key = data;
			node.cnt++;
		}
		// 下面的代码就很简单啦,大家自己研究一下,可以画画图
		if(node.key < data){
			if(node.rchild == null){
				node.rchild = new BNode(data);
				node.rchild.cnt++;
				node.rchild.size = 1;
			}
			else
				insert(node.rchild,data);
		}
		else if(node.key == data){
			node.cnt++;
		}
		else{
			if(node.lchild == null){
				node.lchild = new BNode(data);
				node.lchild.cnt++;
				node.lchild.size = 1;
			}
			else
				insert(node.lchild,data);
		}
	}

根据具体的值返回他的排名
我们先看图,这个时候这个二叉树是什么样的呢,画图有点丑,见谅
红色的字表示我们最开始设置的size变量,我们来看具体有什么用
假如我们要找的是17的排名
(1)根结点出发,17 > 9 故在他的右边找,而此时我们能确定比他小的值有9左子树的size+1,即为6
(2)然后到10, 17 > 10 继续向右找,此时比他小的值变成了 6+ (10) 的左子树的size+1(cnt) = 7
(3)找到17,此时他的排名为7+ (17) 左子树的size+1(cnt) = 9;
找左子树的排名就很简单啦,大家可以自己试试
在这里插入图片描述
下面是代码

int keytorank(BNode root,int x){ // 根据值返回排名
		if(root == null) // 这个是为了实现树上没这个值,我们也可以返回排名的功能
			return 1;
		else if(root.key == x){
			if(root.lchild != null)
			    return root.lchild.size+1;
			else
				return 1;
		}
		else if(root.key > x){
			return keytorank(root.lchild,x);
		}
		else{
			if(root.lchild != null)
			    return keytorank(root.rchild,x)+root.lchild.size+root.cnt;
			else
				return keytorank(root.rchild,x)+root.cnt;
		}
	}

根据排名返回值
这个其实跟上面那个实现挺相近,大家也可以看这个图 对,还是这个丑不拉几的图
假如我们要找排名为8的数
(1)与根结点比较,8 > 根结点左子树size,8也大于根结点左子树size+根结点的个数(cnt),所以在右子树中寻找排名为(8-根结点左子树size-根结点的个数(cnt))即为:8-5-1 = 2 的数
(2)2 > 10的左子树size+10的cnt , 所以在10的右子树找 2-1的排名,就这样找阿找就可以找到13
在这里插入图片描述
来看代码

int ranktokey(BNode root,int x){ // 根据排名返回值
		if(root.lchild != null){
			if(root.lchild.size >= x) // 左子树已经大于等于排名 直接去左子树中找
				return ranktokey(root.lchild,x);
			if(root.lchild.size + root.cnt >= x) // 左子树小于排名,但是加上根结点个数大于等于排名,说明这个数就是这个根结点,返回
				return root.key;
			// 左子树加上根结点都小于 排名, 在右子树中找 排名-左子树大小-根结点cnt
			return ranktokey(root.rchild,x-root.lchild.size-root.cnt);
		}
		else{ // 如果左子树不存在的话 , 大家自己看看吧
			if(root.cnt >= x)
			   return root.key;
			return ranktokey(root.rchild,x-root.cnt);
		}
	}

找前驱结点
这个挺简单的,就自己看看吧

int prenode(BNode root,int x,int ans){ // 前驱
		if(root.key >= x){
			if(root.lchild != null)
				return prenode(root.lchild,x,ans);
			else
				return ans;
		}
		else{
			if(root.rchild != null)
				return prenode(root.rchild,x,root.key);
			else
				return root.key;
		}
	}

找后继结点

int aftnode(BNode root,int x,int ans){ // 后继
		if(root.key <= x){
			if(root.rchild != null)
				return aftnode(root.rchild,x,ans);
			else
				return ans;
		}
		else{
			if(root.lchild != null)
				return aftnode(root.lchild,x,root.key);
			else
				return root.key;
		}
	}

删除结点 !! 重点
删除结点众所周知我们要分成 3种情况
(1)有左子树,没有右子树
(2)没有右子树,有左子树
(3)左右子树都有

boolean delete(BNode root,int x){ // 删除结点
		BNode node = root; // 定义个结点指向他
		BNode parentNode = null; // 这个结点的双亲结点
		while(node != null){ // 非递归找到这个我们要删除的结点
			if(x < node.key){
				parentNode = node;
				node = node.lchild;
			}
			else if(x > node.key){
				parentNode = node;
				node = node.rchild;
			}
			else
				break;
		}
		if(node == null) // 如果为null  这个结点不存在 删除失败
			return false;
		if(node.rchild == null && node.lchild != null){ // 左边不空,右边空,直接嫁接即可
			if(node.cnt > 1) // 这里是如果这个结点有多个,那我们删一个即可
				node.cnt--;
			else{
				if(parentNode != null){ // 如果要删除的结点的双亲结点不为空
					if(parentNode.lchild == node) // 判断删除的结点为他的左孩子还是右孩子,然后直接嫁接
						parentNode.lchild = node.lchild;
					else
						parentNode.rchild = node.lchild;
				}
				else
					root = node.lchild;
			}
		}
		// 这个同理  就不在说明了
		else if(node.lchild == null && node.rchild != null){ // 左边为空,右边不空,嫁接 
			if(node.cnt > 1) 
				node.cnt--;
			else{
				if(parentNode != null){
					if(parentNode.lchild == node)
						parentNode.lchild = node.rchild;
					else
						parentNode.rchild = node.rchild;
				}
				else
					root = node.rchild;
			}
		}
		// 如果左右子树都存在的话,我们可以有2个选择,第一种就是找到前驱结点,第二种就是找到后继结点,替换掉他即可
		else{ // 两边都不为空 这里采用找前驱结点
			if(node.cnt > 1)
				node.cnt --;
			else{
				BNode replaceNode = node.lchild; // 要删除结点的前驱结点
				BNode replaceParentNode = node; // 要删除结点的前驱结点的双亲结点
				while(replaceNode.rchild != null){ // 找到直接前驱
					replaceParentNode = replaceNode;
					replaceNode = replaceNode.rchild;
				}
				// 这里我们还要分类讨论一下,具体大家可以画画图
				if(replaceParentNode == node) // 如果要删的结点是前驱的父结点
					replaceParentNode.lchild = replaceNode.lchild;
				else
					replaceParentNode.rchild = replaceNode.lchild;
				node.key = replaceNode.key;
			}
		}
		return true;
	}

遍历

	void prefound(BNode root){
		if(root != null){
			prefound(root.lchild);
			System.out.print(root.key+" ");
			prefound(root.rchild);
		}
	}

贴一下全代码

package 二叉树;
import java.io.*;
public class bitreesort {
	static int n;
	static int INF = 0X7fffffff;
	public static void main(String[] args) throws IOException{
		StreamTokenizer re = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter pr = new PrintWriter(new OutputStreamWriter(System.out));
		BiTree bitree = new BiTree();
		bitree.root = new BNode(-1); // 为根结点创建空间
		int datas[] = new int[]{9,3,1,7,8,6,10,17,13,7};
		for(int i = 0;i < datas.length;i++)
			bitree.insert(bitree.root, datas[i]);
		bitree.prefound(bitree.root);
		bitree.delete(bitree.root,7);
		System.out.println();
		bitree.prefound(bitree.root);
		bitree.delete(bitree.root,7);
		System.out.println();
		bitree.prefound(bitree.root);
//		re.nextToken(); n = (int)re.nval;
//		for(int i = 0;i < n;i++){
//			re.nextToken(); int a = (int)re.nval;
//			re.nextToken(); int b = (int)re.nval;
//			if(a == 1)
//				pr.println(bitree.keytorank(bitree.root, b));
//			if(a == 2)
//				pr.println(bitree.ranktokey(bitree.root, b));
//			if(a == 3)
//				pr.println(bitree.prenode(bitree.root, b,-INF));
//			if(a == 4)
//				pr.println(bitree.aftnode(bitree.root,b,INF));
//			if(a == 5)
//				bitree.insert(bitree.root, b);
//		}
		pr.flush();
	}
}
class BNode{
	int key;
	int cnt;
	int size;
	BNode lchild;
	BNode rchild;
	BNode(int k){
		this.key = k;
		this.lchild = null;
		this.rchild = null;
	}
}
class BiTree{
	BNode root;
	void insert(BNode node,int data){ // 插入
		node.size++;
		if(node.key == -1){
			node.key = data;
			node.cnt++;
		}
		if(node.key < data){
			if(node.rchild == null){
				node.rchild = new BNode(data);
				node.rchild.cnt++;
				node.rchild.size = 1;
			}
			else
				insert(node.rchild,data);
		}
		else if(node.key == data){
			node.cnt++;
		}
		else{
			if(node.lchild == null){
				node.lchild = new BNode(data);
				node.lchild.cnt++;
				node.lchild.size = 1;
			}
			else
				insert(node.lchild,data);
		}
	}
	int keytorank(BNode root,int x){ // 根据值返回排名
		if(root == null)
			return 1;
		else if(root.key == x){
			if(root.lchild != null)
			    return root.lchild.size+1;
			else
				return 1;
		}
		else if(root.key > x){
			return keytorank(root.lchild,x);
		}
		else{
			if(root.lchild != null)
			    return keytorank(root.rchild,x)+root.lchild.size+root.cnt;
			else
				return keytorank(root.rchild,x)+root.cnt;
		}
	}
	int ranktokey(BNode root,int x){ // 根据排名返回值
		if(root.lchild != null){
			if(root.lchild.size >= x)
				return ranktokey(root.lchild,x);
			if(root.lchild.size + root.cnt >= x)
				return root.key;
			return ranktokey(root.rchild,x-root.lchild.size-root.cnt);
		}
		else{
			if(root.cnt >= x)
			   return root.key;
			return ranktokey(root.rchild,x-root.cnt);
		}
	}
	int prenode(BNode root,int x,int ans){ // 前驱
		if(root.key >= x){
			if(root.lchild != null)
				return prenode(root.lchild,x,ans);
			else
				return ans;
		}
		else{
			if(root.rchild != null)
				return prenode(root.rchild,x,root.key);
			else
				return root.key;
		}
	}
	int aftnode(BNode root,int x,int ans){ // 后继
		if(root.key <= x){
			if(root.rchild != null)
				return aftnode(root.rchild,x,ans);
			else
				return ans;
		}
		else{
			if(root.lchild != null)
				return aftnode(root.lchild,x,root.key);
			else
				return root.key;
		}
	}
	boolean delete(BNode root,int x){ // 删除结点
		BNode node = root; 
		BNode parentNode = null;
		while(node != null){
			if(x < node.key){
				parentNode = node;
				node = node.lchild;
			}
			else if(x > node.key){
				parentNode = node;
				node = node.rchild;
			}
			else
				break;
		}
		if(node == null)
			return false;
		if(node.rchild == null && node.lchild != null){ // 左边不空,右边空,直接嫁接即可
			if(node.cnt > 1)
				node.cnt--;
			else{
				if(parentNode != null){
					if(parentNode.lchild == node)
						parentNode.lchild = node.lchild;
					else
						parentNode.rchild = node.lchild;
				}
				else
					root = node.lchild;
			}
		}
		else if(node.lchild == null && node.rchild != null){ // 左边为空,右边不空,嫁接
			if(node.cnt > 1)
				node.cnt--;
			else{
				if(parentNode != null){
					if(parentNode.lchild == node)
						parentNode.lchild = node.rchild;
					else
						parentNode.rchild = node.rchild;
				}
				else
					root = node.rchild;
			}
		}
		else{ // 两边都不为空 这里采用找前驱结点
			if(node.cnt > 1)
				node.cnt --;
			else{
				BNode replaceNode = node.lchild;
				BNode replaceParentNode = node;
				while(replaceNode.rchild != null){ // 找到直接前驱
					replaceParentNode = replaceNode;
					replaceNode = replaceNode.rchild;
				}
				if(replaceParentNode == node) // 如果要删的结点是前驱的父结点
					replaceParentNode.lchild = replaceNode.lchild;
				else
					replaceParentNode.rchild = replaceNode.lchild;
				node.key = replaceNode.key;
			}
		}
		return true;
	}
	void prefound(BNode root){
		if(root != null){
			prefound(root.lchild);
			System.out.print(root.key+" ");
			prefound(root.rchild);
		}
	}
}

OK 到这里大功告成 这个树的功能全部实现完成啦 ! 这个东西我写了挺久的,如果大佬看见有什么意见跪求- -,另外 本人对Java的引用传递还是有点懵 比如实现删除功能时,node结点指向root结点,他们两个是个什么关系呢,node结点在寻找要删结点的时候的改变 并没有影响root结点,但是在后面的改变中又同时改变了root结点,跪求大佬解惑啊 555555

发布了32 篇原创文章 · 获赞 5 · 访问量 862

猜你喜欢

转载自blog.csdn.net/shizhuba/article/details/105066641