在此特别感谢二叉搜索树的java实现 帖子的思路,更快吸收和实现这个知识点.
二叉树性质
- 在二叉树中的第i层至多有2i-1个节点(i>=1)
例如 一层1个,二层2个,三层4个,4层8个… - 深度为k的二叉树至多有2k-1个节点(k>=1)
例如一层1个,二层22-1=3个,三层23-1=15个… - 对任何一颗二叉树T,如果其终端节点数为n0,度为2 的节点数为n2,则n0=n2+1.
(怕忘,截个图先)
- 具有N个节点的完全二叉树的深度为|log2n|+1
- 太多了.截个图…
代码实现
主要复杂在添加时找节点
因为二叉树搜索树满足根节点值大于左节点,小于右节点,需要将插入的值,先同根节点比较,若大,则往右子树中进行查找,若小,则往左子树中进行查找。直到某个子节点。
/**
* 平淡无奇node类
**/
public class Node{
Node parent;
Node leftChild;
Node rightChild;
int val;
public Node(Node parent, Node leftChild, Node rightChild,int val) {
super();
this.parent = parent;
this.leftChild = leftChild;
this.rightChild = rightChild;
this.val = val;
}
public Node(int val){
this(null,null,null,val);
}
public Node(Node node,int val){
this(node,null,null,val);
}
}
package ***;
/**
* @Author: wsh
*/
public class BiTreeManage {
Node root;
private int size;
public boolean putVal(Integer value) {
if (root == null) {
root = new Node(value);
size++;
return true;
}
//找到插入的父节点
Node node = getAdapterNode(root, value);
Node newNode = new Node(value);
if (node.val > value) {
node.leftChild = newNode;
}else if(node.val<value){
node.rightChild = newNode;
}else{
System.out.println("插入了相等的值"+value);
}
newNode.parent=node;
size++;
return true;
}
public void print(){
print(root);
}
//前序 中序 后续其实就三行换换位置...
private void print(Node root){
if(root != null){
print(root.leftChild);
System.out.println(root.val);// 位置在中间,则中序,若在前面,则为先序,否则为后续
print(root.rightChild);
}
}
/**
* 查找要插入的父节点
*/
private Node getAdapterNode(Node node, int value) {
//不会走,因为调用方法已处理node==null
// if (node == null) {
// return node;
// }
//如果该节点的值大于值, 且左边允许插入
if (node.val > value && node.leftChild == null) {
System.out.println(String.format("在插入%s找到父节点%s的左侧为null",value,node.val));
return node;
}
if (node.val < value && node.rightChild == null) {
System.out.println(String.format("在插入%s找到父节点%s的右侧为null",value,node.val));
return node;
}
//暂不详
if (node.leftChild == null && node.rightChild == null) {
System.out.println("什么时候能执行呢");
return node;
}
if (node.val > value && node.leftChild != null) {
System.out.println(String.format("在插入%s时,父节点%s左侧不为null,继续找下一个节点",value,node.val));
return getAdapterNode(node.leftChild, value);
} else if (node.val < value && node.rightChild != null) {
System.out.println(String.format("在插入%s时,父节点%s右侧不为null,继续找下一个节点",value,node.val));
return getAdapterNode(node.rightChild, value);
}else{
//value相等时
return node ;
}
}
}
执行和输出
package ***;
/**
* @Author: wsh
*/
public class BiTreeTest {
public static void main(String[] args) {
BiTreeManage biTreeManage = new BiTreeManage();
biTreeManage.putVal(10);
biTreeManage.putVal(9);
biTreeManage.putVal(12);
biTreeManage.putVal(8);
biTreeManage.putVal(7);
biTreeManage.putVal(11);
biTreeManage.putVal(13);
biTreeManage.putVal(10);
biTreeManage.print();
}
}
在插入9找到父节点10的左侧为null
在插入12找到父节点10的右侧为null
在插入8时,父节点10左侧不为null,继续找下一个节点
在插入8找到父节点9的左侧为null
在插入7时,父节点10左侧不为null,继续找下一个节点
在插入7时,父节点9左侧不为null,继续找下一个节点
在插入7找到父节点8的左侧为null
在插入11时,父节点10右侧不为null,继续找下一个节点
在插入11找到父节点12的左侧为null
在插入13时,父节点10右侧不为null,继续找下一个节点
在插入13找到父节点12的右侧为null
错误 :插入了相等的值10
7
8
9
10
11
12
13
删除操作
删除情况分析
删除时也有些复杂, 这里也引用一下链接中的图
- 简单情况: 最后节点(无左无右图中DEG) / 仅有一侧子节点(左节点或右节点 图 BF)
处理比较简单,直接替换上去就好了.感觉和双向链表操作差不多 - 复杂情况: 左右都有节点
要删除的话,引用了一个后继节点概念,在网上找了找两个说明,结合一起看
1.一个节点在整棵树中的后继节点必满足,大于该节点值得所有节点集合中值最小的那个节点,即为后继节点
2.后继节点:就是中序遍历排序中相对的后一个
找该节点找右分支(都比它大)上里面最小的值,都在右节点的左节点,
比如A点,先找比它大的CEFG,再从里面找最小的,你会发现只有E
或者采用中序遍历,嗯…后继节点和前驱节点(没理解前序中序后续遍历的先去看概念,重复一遍是一遍)
删除代码
在BiTreeManage.java
中在原有的插入输出putVal()
/print()
基础上添加即可
public boolean delete(int val) {
Node node = getNode(val);
if (node == null) {
return false;
}
Node parent = node.parent;
Node leftChild = node.leftChild;
Node rightChild = node.rightChild;
//判断不同情况
//第一种最简单,左右都没有子节点,父节点对应侧置空即可
if (leftChild == null && rightChild == null) {
if (parent != null) {
if (parent.leftChild == node) {
parent.leftChild = null;
} else {
//} else if (parent.rightChild == node) {
//要么在父节点左边 要么右边 没有其他情况,
// 因为本来parent和node就是有关系的
parent.rightChild = null;
}
} else {
//父节点为null 根节点
root = null;
}
//可有可无
node = null;
return true;
} else if (leftChild == null && rightChild != null) {
//左侧为空 右侧不为空 直接类似链表操作
return leftOrRightNull(val, parent, rightChild);
} else if (rightChild != null && rightChild == null) {
//同上一种情况 差不多 就把值换了一下
return leftOrRightNull(val, parent, leftChild);
} else if (leftChild != null && rightChild != null) {
//最复杂的情况,置换方法
//找到后继节点
Node successor = getSuccessor(node);
//将最小的删除掉
//这里既是是root根节点,依旧可以执行.注意给参数是successor.val
boolean delete = delete(successor.val);
//把值直接给当前的node
if (delete) {
node.val = successor.val;
}
successor = null;
return true;
}
return false;
}
/**
* 查找后继节点
// 10
// / \
// 9 12
// /\ / \
// 7 8 11 13
*
* 这里代码和原文https://www.cnblogs.com/qm-article/p/9279655.html
* 稍微有些不一样,因为删除了部分代码,排除其他拓展情况
* 原文中 Node parent =node.parent 走不到这里..
*/
private Node getSuccessor(Node node) {
Node rightChild = node.rightChild;
//查找最小左侧节点的元素
while (rightChild.leftChild != null) {
//顺着左侧一直找下去,这样是最小的一个
//结合上面树形注释,假设删除10 找到的最小就是11
//回到上个置换方法:把10删除,把11放到10位置,二叉树依然成立
rightChild = rightChild.leftChild;
}
return rightChild;
}
/**
* 左右子树 其中有一个为null 的情况
*/
private boolean leftOrRightNull(int val, Node parent, Node child) {
if (parent != null && parent.val > val) {
//收获: 根据判断大小,判断当前删除位置 在父节点的左边
parent.leftChild = child;
} else if (parent != null && parent.val < val) {
//删除的节点在父节点的右边
parent.rightChild = child;
} else {
//父节点为null 根节点,直接顶上就行
root = child;
}
return true;
}
/**
* 查找
*/
public Node getNode(int val) {
Node temp = root;
int t;
do {
t = temp.val - val;
if (t > 0) {
temp = temp.leftChild;
} else if (t < 0) {
temp = temp.rightChild;
} else {
return temp;
}
} while (temp != null);
return null;
}
照常输出来一遍 看看效果,这次把树根据图形化一下…
public static void main(String[] args) {
BiTreeManage biTreeManage = new BiTreeManage();
biTreeManage.putVal(10);
biTreeManage.putVal(9);
biTreeManage.putVal(12);
biTreeManage.putVal(8);
biTreeManage.putVal(7);
biTreeManage.putVal(11);
biTreeManage.putVal(13);
biTreeManage.putVal(10);
biTreeManage.print();
// 依次put以后
// 10
// / \
// 9 12
// /\ / \
// 7 8 11 13
System.out.println("删除:");
biTreeManage.delete(10);
//删除以后
// 11
// / \
// 9 12
// /\ \
// 7 8 13
biTreeManage.print();
}
输出
在插入9找到父节点10的左侧为null
在插入12找到父节点10的右侧为null
在插入8时,父节点10左侧不为null,继续找下一个节点
在插入8找到父节点9的左侧为null
在插入7时,父节点10左侧不为null,继续找下一个节点
在插入7时,父节点9左侧不为null,继续找下一个节点
在插入7找到父节点8的左侧为null
在插入11时,父节点10右侧不为null,继续找下一个节点
在插入11找到父节点12的左侧为null
在插入13时,父节点10右侧不为null,继续找下一个节点
在插入13找到父节点12的右侧为null
错误 :插入了相等的值10
7
8
9
10
11
12
13
删除:
7
8
9
11
12
13
不足和收获
- 不足
- 插入时候需要按照一定顺序插入,才能实现平衡平衡平衡二叉树,要不然就像一个根节点是root的左右两条链表
- 没有任何思路,完全参考了其他人的想法
- 收获
- 一步步全部抄下来,过思路,脑海中各种推演,写完以后思路上至少是顺畅的,虽说盲打一遍肯定有bug,也有条理多了
- 插入和删除看似思路简单,实操起来,发现有很多顾虑的地方,踏踏实实