目录
二叉查找树的特点
1. 如果左树不为空,那么左树上所有结点的值小于根结点的值
2. 如果右树不为空,那么右树上所有节点的值大于根结点的值
3. 左树和右树均是二叉查找树,并且树上的结点的值都不相同
二叉查找树的操作
插入操作
将9插入到a图的二叉树
1> 9大于4,插入到4的右树
2> 9大于5,插入到5的右树,接着9还是大于6,将9插入到6的右树
3> 9大于7,插入到7的右树,9大于8,插入到8的右树
在此过程中,动态增加数据都会加在原有数据的叶子节点上。
删除操作
1. 如果这个结点的左树存在,右树不存在
例如
当删除的节点在左树:
例如,删除a图的节点3,只需要将结点4和结点1构成关系。表示为 parent.left = node.left
当删除的结点在右树
例如,删除c图的结点2,只需要将结点1和结点3构成关系,表为parent.right = node.rigtht;
2. 如果这个结点的右树存在,左树不存在,和上面类似。
3. 当删除的结点,左树和右树都存在。
如果删除结点5,需要一个结点能够替换5,结点4和结点6都能够满足要求。
如何找到这个结点替换删除的结点?
当前结点的左结点的走右树,一直找到最右结点 或者是 当前结点的右结点,走左树找到的最左结点。
源码中展示的删除方法,是按照 "当前结点的左结点,走右树,找到最右结点" 的方式,编码的。
二叉查找树的效率分析
很显然,在a,b两图的二叉查找树结构中查找一个数据,并不需要遍历全部的节点元素,查找效率确实提高了。但是有一个很严重的问题:我们在a图中查找8需要比较5次数据,而在B图中只需要比较3次。更为严重的是:如果按有序序列[1 2 3 4 5 6 7 8]建立一颗二叉查找树,整棵树就退化成了一个线性结构(如c输入图:单支树),此时查找8需要比较8次数据,和顺序查找没有什么不同。
最坏的情况,时间复杂度算法是O(N),最好的情况和折半查找的时间复杂度相同,为O(log2 (N))
源码
package net.lingala.zip4j.test;
import java.util.ArrayList;
import com.sun.corba.se.impl.logging.InterceptorsSystemException;
/**
* 二叉树节点结构
* @author heartraid
*/
class BSTNode<E extends Comparable<E>>{
/**结点关键字*/
E key=null;
/**直接父亲结点*/
BSTNode<E> parent=null;
/**结点左子树的根节点*/
BSTNode<E> lchild=null;
/**结点右子树的根节点*/
BSTNode<E> rchild=null;
BSTNode(E k){
this.key=k;
}
}
/**
* 二叉查找树 Binary Search Tree(BST)
* @author heartraid
*
*/
public class BST<E extends Comparable<E>> {
/**树根*/
private BSTNode<E> root=null;
public BST(){
}
/**
* BST 查询关键字
* @param key 关键字
* @return 查询成功/true, 查询失败/false
*/
public boolean search(E key){
System.out.print("搜索关键字["+key+"]:");
if(key==null||root==null){
System.out.println("搜索失败");
return false;
}
else{
System.out.print("搜索路径[");
if(searchBST(root,key)==null){
return false;
}
else return true;
}
}
/**
* BST插入关键字
* @param key 关键字
* @return 插入成功/true, 插入失败/false
*/
public boolean insert(E key){
System.out.print("插入关键字["+key+"]:");
if(key==null) return false;
if(root==null){
System.out.println("插入到树根。");
root=new BSTNode<E>(key);
return true;
}
else{
System.out.print("搜索路径[");
return insertBST(root,key);
}
}
public boolean delete(E key){
System.out.print("删除关键字["+key+"]:");
if(key==null||root==null){
System.out.println("删除失败");
return false;
}
else{
System.out.print("搜索路径[");
//定位到树中待删除的结点
BSTNode<E> nodeDel=searchBST(root,key);
if(nodeDel==null){
return false;
}
else{
//nodeDel的右子树为空,则只需要重接它的左子树
if(nodeDel.rchild==null){
BSTNode<E> parent=nodeDel.parent;
if(parent.lchild.key.compareTo(nodeDel.key)==0)
parent.lchild=nodeDel.lchild;
else
parent.rchild=nodeDel.lchild;
}
//左子树为空,则重接它的右子树
else if(nodeDel.lchild==null){
BSTNode<E> parent=nodeDel.parent;
if(parent.lchild.key.compareTo(nodeDel.key)==0)
parent.lchild=nodeDel.rchild;
else
parent.rchild=nodeDel.rchild;
}
//左右子树均不空
else{
BSTNode<E> q=nodeDel;
//先找nodeDel的左结点s
BSTNode<E> s=nodeDel.lchild;
//然后再向s的右尽头定位(这个结点将替代nodeDel),其中q一直定位在s的直接父亲结点
while(s.rchild!=null){
q=s;
s=s.rchild;
}
//换掉nodeDel的关键字为s的关键字
nodeDel.key=s.key;
//重新设置s的左子树
if(q!=nodeDel)
q.rchild=s.lchild;
else
q.lchild=s.lchild;
}
return true;
}
}
}
/**
* 递归查找关键子
* @param node 树结点
* @param key 关键字
* @return 查找成功,返回该结点,否则返回null。
*/
private BSTNode<E> searchBST(BSTNode<E> node, E key){
if(node==null){
System.out.println("]. 搜索失败");
return null;
}
System.out.print(node.key+" —>");
//搜索到关键字
if(node.key.compareTo(key)==0){
System.out.println("]. 搜索成功");
return node;
}
//在左子树搜索
else if(node.key.compareTo(key)>0){
return searchBST(node.lchild,key);
}
//在右子树搜索
else{
return searchBST(node.rchild,key);
}
}
/**
* 递归插入关键字
* @param node 树结点
* @param key 树关键字
* @return true/插入成功,false/插入失败
*/
private boolean insertBST(BSTNode<E> node, E key){
System.out.print(node.key+" —>");
//在原树中找到相同的关键字,无需插入。
if(node.key.compareTo(key)==0)
{
System.out.println("]. 搜索有相同关键字,插入失败");
return false;
}
else{
//搜索node的左子树
if(node.key.compareTo(key)>0){
//如果当前node的左子树为空,则将新结点key node插入到左孩子处
if(node.lchild==null) {
System.out.println("]. 插入到"+node.key+"的左孩子");
BSTNode<E> newNode=new BSTNode<E>(key);
node.lchild=newNode;
newNode.parent=node;
return true;
}
//如果当前node的左子树存在,则继续递归左子树
else return insertBST(node.lchild, key);
}
//搜索node的右子树
else{
if(node.rchild==null){
System.out.println("]. 插入到"+node.key+"的右孩子");
BSTNode<E> newNode=new BSTNode<E>(key);
node.rchild=newNode;
newNode.parent=node;
return true;
}
else return insertBST(node.rchild,key);
}
}
}
/**
* 得到BST根节点
* @return BST根节点f
*/
public BSTNode<E> getRoot(){
return this.root;
}
/**
* 非递归中序遍历BST
*/
public void InOrderTraverse(){
if(root==null)
return;
BSTNode<E> node=root;
ArrayList<BSTNode<E>> stack=new ArrayList<BSTNode<E>>();
stack.add(node);
while(!stack.isEmpty()){
while(node.lchild!=null){
node=node.lchild;
stack.add(node);
}
if(!stack.isEmpty()){
BSTNode<E> topNode=stack.get(stack.size()-1);
System.out.print(topNode.key+" ");
stack.remove(stack.size()-1);
if(topNode.rchild!=null){
node=topNode.rchild;
stack.add(node);
}
}
}
}
/**
* 测试
*/
public static void main(String[] args) {
BST<Integer> tree=new BST<Integer>();
tree.insert(new Integer(100));
tree.insert(new Integer(52));
tree.insert(new Integer(166));
tree.insert(new Integer(74));
tree.insert(new Integer(11));
tree.insert(new Integer(13));
tree.insert(new Integer(66));
tree.insert(new Integer(121));
tree.insert(new Integer(10));
tree.search(new Integer(11));
tree.InOrderTraverse();
tree.delete(new Integer(11));
tree.InOrderTraverse();
}
}
参考博客:https://blog.csdn.net/zxnsirius/article/details/52131433?utm_source=blogxgwz0 (关于删除结点的查找方法)
http://hxraid.iteye.com/blog/609312(二叉查找树介绍)