【查找结构 2】二叉查找树 [BST]

本文探讨了二叉查找树这一高效动态查找结构,包括其特点、操作及效率分析。通过实例展示了如何利用二叉查找树进行数据插入、删除及中序遍历,同时对比了不同数据集合对查找效率的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当所有的静态查找结构添加和删除一个数据的时候,整个结构都需要重建。这对于常常需要在查找过程中动态改变数据而言,是灾难性的。因此人们就必须去寻找高效的动态查找结构,我们在这讨论一个非常常用的动态查找树——二叉查找树 

 

二叉查找树的特点

 

下面的图就是两棵二叉查找树,我们可以总结一下他的特点:

(1) 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值

(2) 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
(3) 它的左、右子树也分别为二叉查找树

 

 

我们中序遍历这两棵树发现一个有序的数据序列: 【1  2  3  4  5  6  7  8 】

 

 

二叉查找树的操作

 

插入操作:

现在我们要查找一个数9,如果不存在则,添加进a图。我们看看二叉查找树动态添加的过程:

1). 数9和根节点4比较(9>4),则9放在节点4的右子树中。

2). 接着,9和节点5比较(9>5),则9放在节点5的右子树中。

3). 依次类推:直到9和节点8比较(9>8),则9放在节点8的右子树中,成为节点8的右孩子。

这个过程我们能够发现,动态添加任何一个数据,都会加在原树结构的叶子节点上,而不会重新建树。 由此可见,动态查找结构确实在这方面有巨大的优势。

 

删除操作:

如果二叉查找树中需要删除的结点左、右子树都存在,则删除的时候需要改变一些子树结构,但所需要付出的代价很小。

 

 

 

二叉查找树的效率分析

 

 

那么我们再来看看二叉查找树的效率问题

 

很显然,在a,b两图的二叉查找树结构中查找一个数据,并不需要遍历全部的节点元素,查找效率确实提高了。但是有一个很严重的问题:我们在a图中查找8需要比较5次数据,而在B图中只需要比较3次。更为严重的是:如果按有序序列[1 2 3 4 5 6 7 8]建立一颗二叉查找树,整棵树就退化成了一个线性结构(如c输入图:单支树),此时查找8需要比较8次数据,和顺序查找没有什么不同。

总结一下:最坏情况下,构成的二叉排序树蜕变为单支树,树的深度为n,其查找时间复杂度与顺序查找一样O(N)。最好的情况是二叉排序树的形态和折半查找的判定树相同,其平均查找长度和log2(N)成正比 (O(log2(n)))。

 

这说明:同样一组数据集合,不同的添加顺序会导致查找树的结构完全不一样,直接影响了查找效率。

 

下面是java实现的二叉查找树(说句老实话,没有指针的java实现数据结构真是复杂):

Java代码  收藏代码
  1. package net.hr.algorithm.search;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. /** 
  6.  * 二叉树节点结构 
  7.  * @author heartraid 
  8.  */  
  9. class BSTNode<E extends Comparable<E>>{  
  10.     /**结点关键字*/  
  11.     E key=null;  
  12.     /**直接父亲结点*/  
  13.     BSTNode<E> parent=null;  
  14.     /**结点左子树的根节点*/  
  15.     BSTNode<E> lchild=null;  
  16.     /**结点右子树的根节点*/  
  17.     BSTNode<E> rchild=null;  
  18.       
  19.     BSTNode(E k){  
  20.         this.key=k;  
  21.     }  
  22.   
  23. }  
  24. /** 
  25.  * 二叉查找树 Binary Search Tree(BST) 
  26.  * @author heartraid 
  27.  * 
  28.  */  
  29. public class BST<E extends Comparable<E>> {  
  30.     /**树根*/  
  31.     private BSTNode<E> root=null;  
  32.       
  33.     public BST(){  
  34.     }  
  35.       
  36.     /** 
  37.      * BST 查询关键字 
  38.      * @param key 关键字 
  39.      * @return 查询成功/true, 查询失败/false 
  40.      */  
  41.     public boolean search(E key){  
  42.         System.out.print("搜索关键字["+key+"]:");  
  43.         if(key==null||root==null){  
  44.             System.out.println("搜索失败");  
  45.             return false;  
  46.         }  
  47.         else{  
  48.             System.out.print("搜索路径[");  
  49.             if(searchBST(root,key)==null){  
  50.                 return false;  
  51.             }  
  52.             else return true;  
  53.                   
  54.         }  
  55.     }  
  56.     /** 
  57.      * BST插入关键字 
  58.      * @param key 关键字 
  59.      * @return 插入成功/true, 插入失败/false 
  60.      */  
  61.     public boolean insert(E key){  
  62.         System.out.print("插入关键字["+key+"]:");  
  63.         if(key==nullreturn false;  
  64.         if(root==null){  
  65.             System.out.println("插入到树根。");  
  66.             root=new BSTNode<E>(key);  
  67.             return true;  
  68.         }  
  69.         else{  
  70.             System.out.print("搜索路径[");  
  71.             return insertBST(root,key);  
  72.         }  
  73.     }  
  74.       
  75.     public boolean delete(E key){  
  76.         System.out.print("删除关键字["+key+"]:");  
  77.         if(key==null||root==null){  
  78.             System.out.println("删除失败");  
  79.             return false;  
  80.         }  
  81.         else{  
  82.             System.out.print("搜索路径[");  
  83.               
  84.             //定位到树中待删除的结点  
  85.             BSTNode<E> nodeDel=searchBST(root,key);  
  86.             if(nodeDel==null){  
  87.                 return false;  
  88.             }  
  89.             else{  
  90.                 //nodeDel的右子树为空,则只需要重接它的左子树  
  91.                 if(nodeDel.rchild==null){  
  92.                       
  93.                     BSTNode<E> parent=nodeDel.parent;  
  94.                     if(parent.lchild.key.compareTo(nodeDel.key)==0)  
  95.                         parent.lchild=nodeDel.lchild;  
  96.                     else  
  97.                         parent.rchild=nodeDel.lchild;  
  98.                 }  
  99.                 //左子树为空,则重接它的右子树  
  100.                 else if(nodeDel.lchild==null){  
  101.                     BSTNode<E> parent=nodeDel.parent;  
  102.                     if(parent.lchild.key.compareTo(nodeDel.key)==0)  
  103.                         parent.lchild=nodeDel.rchild;  
  104.                     else  
  105.                         parent.rchild=nodeDel.rchild;  
  106.                 }  
  107.                 //左右子树均不空  
  108.                 else{  
  109.                     BSTNode<E> q=nodeDel;  
  110.                     //先找nodeDel的左结点s  
  111.                     BSTNode<E> s=nodeDel.lchild;  
  112.                     //然后再向s的右尽头定位(这个结点将替代nodeDel),其中q一直定位在s的直接父亲结点  
  113.                     while(s.rchild!=null){   
  114.                         q=s;  
  115.                         s=s.rchild;  
  116.                     }  
  117.                     //换掉nodeDel的关键字为s的关键字  
  118.                     nodeDel.key=s.key;  
  119.                     //重新设置s的左子树  
  120.                     if(q!=nodeDel)   
  121.                         q.rchild=s.lchild;  
  122.                     else  
  123.                         q.lchild=s.lchild;  
  124.                 }  
  125.                 return true;  
  126.             }  
  127.         }  
  128.     }  
  129.       
  130.     /** 
  131.      * 递归查找关键子 
  132.      * @param node 树结点 
  133.      * @param key 关键字 
  134.      * @return 查找成功,返回该结点,否则返回null。 
  135.      */  
  136.     private BSTNode<E> searchBST(BSTNode<E> node, E key){  
  137.         if(node==null){  
  138.             System.out.println("].  搜索失败");  
  139.             return null;  
  140.         }  
  141.         System.out.print(node.key+" —>");  
  142.         //搜索到关键字  
  143.         if(node.key.compareTo(key)==0){  
  144.             System.out.println("].  搜索成功");  
  145.             return node;  
  146.         }  
  147.         //在左子树搜索  
  148.         else if(node.key.compareTo(key)>0){  
  149.                 return searchBST(node.lchild,key);  
  150.         }  
  151.         //在右子树搜索  
  152.         else{  
  153.             return searchBST(node.rchild,key);  
  154.         }  
  155.     }  
  156.       
  157.     /** 
  158.      * 递归插入关键字 
  159.      * @param node 树结点 
  160.      * @param key 树关键字 
  161.      * @return true/插入成功,false/插入失败 
  162.      */  
  163.     private boolean insertBST(BSTNode<E> node, E key){  
  164.         System.out.print(node.key+" —>");  
  165.         //在原树中找到相同的关键字,无需插入。  
  166.         if(node.key.compareTo(key)==0)   
  167.         {  
  168.             System.out.println("].  搜索有相同关键字,插入失败");  
  169.             return false;  
  170.         }  
  171.         else{  
  172.             //搜索node的左子树  
  173.             if(node.key.compareTo(key)>0){  
  174.                 //如果当前node的左子树为空,则将新结点key node插入到左孩子处  
  175.                 if(node.lchild==null) {  
  176.                     System.out.println("].  插入到"+node.key+"的左孩子");  
  177.                     BSTNode<E> newNode=new BSTNode<E>(key);   
  178.                     node.lchild=newNode;  
  179.                     newNode.parent=node;  
  180.                     return true;  
  181.                 }  
  182.                 //如果当前node的左子树存在,则继续递归左子树  
  183.                 else return insertBST(node.lchild, key);  
  184.             }  
  185.             //搜索node的右子树  
  186.             else{  
  187.                 if(node.rchild==null){  
  188.                     System.out.println("].  插入到"+node.key+"的右孩子");  
  189.                     BSTNode<E> newNode=new BSTNode<E>(key);   
  190.                     node.rchild=newNode;  
  191.                     newNode.parent=node;  
  192.                     return true;  
  193.                 }  
  194.                 else return insertBST(node.rchild,key);  
  195.             }  
  196.         }  
  197.           
  198.     }  
  199.     /** 
  200.      * 得到BST根节点 
  201.      * @return BST根节点f 
  202.      */  
  203.     public BSTNode<E> getRoot(){  
  204.         return this.root;  
  205.     }  
  206.     /** 
  207.      * 非递归中序遍历BST 
  208.      */  
  209.     public void InOrderTraverse(){  
  210.         if(root==null)  
  211.             return;  
  212.         BSTNode<E> node=root;  
  213.         ArrayList<BSTNode<E>> stack=new ArrayList<BSTNode<E>>();      
  214.         stack.add(node);  
  215.         while(!stack.isEmpty()){  
  216.             while(node.lchild!=null){  
  217.                 node=node.lchild;  
  218.                 stack.add(node);  
  219.             }  
  220.             if(!stack.isEmpty()){  
  221.                 BSTNode<E> topNode=stack.get(stack.size()-1);  
  222.                 System.out.print(topNode.key+" ");  
  223.                 stack.remove(stack.size()-1);  
  224.                 if(topNode.rchild!=null){  
  225.                     node=topNode.rchild;  
  226.                     stack.add(node);  
  227.                 }  
  228.             }  
  229.         }  
  230.           
  231.           
  232.     }  
  233.       
  234.     /** 
  235.      * 测试 
  236.      */  
  237.     public static void main(String[] args) {  
  238.         BST<Integer> tree=new BST<Integer>();  
  239.         tree.insert(new Integer(100));  
  240.         tree.insert(new Integer(52));  
  241.         tree.insert(new Integer(166));  
  242.         tree.insert(new Integer(74));  
  243.         tree.insert(new Integer(11));  
  244.         tree.insert(new Integer(13));  
  245.         tree.insert(new Integer(66));  
  246.         tree.insert(new Integer(121));  
  247.           
  248.                 tree.search(new Integer(11));  
  249.                 tree.InOrderTraverse();  
  250.           
  251.                 tree.delete(new Integer(11));  
  252.         tree.InOrderTraverse();  
  253.   
  254.     }  
  255.   
  256. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值