【查找结构】二叉查找树
来源:互联网 发布:游族网络无限工作室 编辑:程序博客网 时间:2024/03/29 14:55
当所有的静态查找结构添加和删除一个数据的时候,整个结构都需要重建。这对于常常需要在查找过程中动态改变数据而言,是灾难性的。因此人们就必须去寻找高效的动态查找结构,我们在这讨论一个非常常用的动态查找树——二叉查找树。
二叉查找树的特点
下面的图就是两棵二叉查找树,我们可以总结一下他的特点:
(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的右孩子。
这个过程我们能够发现,动态添加任何一个数据,都会加在原树结构的叶子节点上,而不会重新建树。 由此可见,动态查找结构确实在这方面有巨大的优势。
删除操作:
如果二叉查找树中需要删除的结点左、右子树都存在,则删除的时候需要改变一些子树结构,但所需要付出的代价很小。
具体的插入,删除算法请参加《数据结构算法与应用——搜索树》P5-8。[该章节已经上传到《查找结构专题(6):动态查找树比较 》中]。
二叉查找树的效率分析
那么我们再来看看二叉查找树的效率问题
很显然,在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实现数据结构真是复杂):
package net.hr.algorithm.search;import java.util.ArrayList;/** * 二叉树节点结构 * @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;elseparent.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;elseparent.rchild=nodeDel.rchild;}//左右子树均不空else{BSTNode<E> q=nodeDel;//先找nodeDel的左结点sBSTNode<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;elseq.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.search(new Integer(11)); tree.InOrderTraverse(); tree.delete(new Integer(11));tree.InOrderTraverse();}}
原文地址
- 【查找结构】二叉查找树
- 【查找结构】平衡二叉查找树
- 【查找结构 2】二叉查找树 [BST]
- 【查找结构 2】二叉查找树 [BST]
- 查找--二叉查找树
- 【查找结构3】平衡二叉查找树 [AVL]
- 【查找结构3】平衡二叉查找树 [AVL]
- 【查找结构3】平衡二叉查找树 [AVL]
- 【查找结构3】平衡二叉查找树 [AVL]
- 【查找结构3】平衡二叉查找树 [AVL]
- 【查找结构3】平衡二叉查找树 [AVL]
- 链式结构实现二叉查找树(二叉排序树)
- 各种二叉树和查找结构总结
- 查找之二叉树查找
- 查找之二叉树查找
- 查找:二叉查找树总结
- 查找算法 顺序查找 二分查找 二叉树查找 算法
- 二叉树查找树...
- Erlang虚拟机基础设施dtrace探测点介绍和使用
- 嵌入式Linux下的实时性增强方案
- Java合并流SequenceInputStream
- Asp.net web用户控件javascript中获取服务器控件ID
- 使用 Named Pipes 创建有效的连接字符串
- 【查找结构】二叉查找树
- 理解$HandlerLabel HANDLER $HandleLabel 宏
- Nginx 0.8.x + PHP 5.2.13(FastCGI)搭建胜过Apache十倍的Web服务器(第6版)
- 可阻塞的队列——ArrayBlockingQueue
- 为自定义对话框添加菜单
- reltool使用指南
- 如何在sourceforge开源项目网站下载开源项目
- Android消息通知(notification)和PendingIntent传值
- http中的Content-Type