二叉查找树
完整代码:
https://github.com/problemin/Algorithm/blob/master/src/Tree/BSTree.java
二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树。
- 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若右子树不空,则右子树上所有结点的值均大于它的根结点的值
- 左、右子树也分别为二叉排序树;
这样的树有一个特点:所有节点在x轴的映射是有序的。
一棵查找树的主要功能有:插入,查找,删除。
除此之外还需要一些辅助功能,如节点统计等。
闲话少说,我们从头来缕。
先定义一棵树,包含了基本结构
public class BSTree{
/*
*根节点以及节点的定义
*此处我们先简单定义一个可以用的就行,因为太复杂容易造成思维混乱
*/
private Node root;
class Node{
int key;//用来寻找位置的依据,索引
Node left,right;
public Node(int key){
this.key = key;
}
}
public void add(int key){}//添加节点
public void get(int key){}//查询节点
public void remove(int key){}//删除节点
}
ok,现在让我们挨个把功能实现了。
添加节点
迭代写法步骤为:
1.先看有没有根节点,如果没有那我们就要先造一个根节点了。
2.有了根节点,我们接下来的插入就要进行二分了,新建一个current,让他等于root。
其实就是从root开始,如果插入值比当前节点小,那么current=current.left;
如果比当前节点大,那么current=current.right;
如果相等了,就覆盖掉。
如果当前节点为null,因为找到这了,四下无人,那就说明这就是新节点该待的位置
迭代写法
public void add(int key){
if(root == null){
root = new Node(key);
}else{
Node current = root;
while(current!=null){
if(key<current.key){
if(current.left!=null){
current = current.left;
}else{
current.left = new Node(key);
return;
}
}else if(key>current.key){
if(current.right!=null){
current = current.right;
}else{
current.right = new Node(key);
return;
}
}else{
//此处是相等的情况,由于我们的树现在只有键,没有值,所以没有必要处理
System.out.println("重复覆盖");
}
}
}
}
递归写法
public void add(int key){
root = add(root,key);
}
public Node add(Node current,int key){
if(current == null) return new Node(key);
if(key<current.key){
current.left = add(current.left,key);
}else if(key>current.key){
current.right = add(current.right,key);
}else{
//此处是相等的情况,由于我们的树现在只有键,没有值,所以没有必要处理
System.out.println("重复覆盖");
}
return current;
}
现在我们就可以插入节点了,赶紧写一个查询看看能不能查出来
查找节点
查找的话思路跟插入差不多了,就是不断的二分,如果相等了就返回,如果遍历完了没有返回,说明没找到,那么返回空
public Node search(int key){
Node current = root;
while(current!=null){
if(key<current.key){
current = current.left;
}else if(key>current.key){
current = current.right;
}else{
System.out.println("找到节点:"+current.key);
return current;
}
}
System.out.println("找不到该节点");
return null;
}
删除节点
删除节点就有一点复杂了,因为删除完一个节点后,如何处理其子节点是个问题。
删除节点时共有如图中3,4,10节点三种情况。
- 需要删除的节点下并没有其他子节点。
- 需要删除的节点下有一个子节点(左或右)。
- 需要删除的节点下有两个子节点(既左右节点都存在)。
对于节点4,很简单,节点6.left = null即可。
对于节点10,有一个子节点14,则8.right=14即可。
复杂之处在于节点3。对于3这种节点(有两个子节点),我们要先在其右子树中找到最小的一个节点,然后用这个节点替换他。
因为3节点右子树中的最小节点比3节点大,即比3节点左子树所有节点大,又比3节点右子树其他节点小,所以替换之后就可以满足二叉树的性质。
那么问题来了:如何找到要删除节点的右子树中的最小节点?
如何找到子树中的最小节点
通过二叉搜索树的性质,我们可以知道,最小的节点一定在最树的最左侧。
如果一个节点没有左子树,那么该节点为以该节点为根的子树中最小的绩点。
public Node min(Node node){
if(node.left == null) return node;
else return min(node.left);
}
如何删除最小节点
好了现在可以找到最小节点了,那再替换之后我们还需要在原来位置删除该最小节点。
思路就是找到最左侧的节点,用其右子树的根节点替换他。
public Node deleteMin(Node node){
if(node.left == null) return node.right;
node.left = deleteMin(node.left);
return node;
}
现在开始正式删除,由于删除需要处理父子节点之间的关系,用递归会别叫好写,所以此处用递归来实现了。
public void remove(int key){
root = remove(root,key);
}
public Node remove(Node current,int key){
//如果Current==null,说明找到最后都没有找到要删除的节点
if(current == null) {
System.out.println("没有要删除的节点");
return null;
}
if(key<current.key){
//如果要删除的键比当前键小,那么去左子树里做删除操作,并把操作完的左子树根节点返回
current.left = remove(current.left,key);
}else if(key>current.key){
//如果要删除的键比当前键大,那么去右子树里做删除操作,并把操作完的右左子树根节点返回
current.right = remove(current.right,key);
}else {
//找到要删除的键,进行删除操作
//1.如果左子树为空,那么把右子树根节点返回作为新的根节点
if(current.left == null) return current.right;
//2.如果右子树为空,那么把左子树根节点返回作为新的根节点
if(current.right == null) return current.left;
//3.如果有两个子节点
//保存要删除的当前节点
Node temp = current;
//用右子树最小节点替换当前节点
current = min(current.right);
//将旧节点的左节点接到新节点上
current.left = temp.left;
//由于右子树中的最小节点已经被替换过来了,所以要删除,删除后我们把新的右子树根节点返回过来接到新节点上
current.right = deleteMin(temp.right);
System.out.println("删除节点:"+key);
}
return current;
}