本人有点菜 ,如果有什么优化方法大家都可以说说
因为之前学的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