二叉查找(排序)树的分析与实现
二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树.
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的节点。
步骤:
若根结点的关键字值等于查找的关键字,成功。
否则,若小于根结点的关键字值,递归查左子树。
若大于根结点的关键字值,递归查右子树。
若子树为空,查找不成功。
平均情况分析(在成功查找两种的情况下):
在一般情况下,设 P(n,i)为它的左子树的结点个数为 i 时的平均查找长度。如图的结点个数为 n = 6 且 i = 3; 则 P(n,i)= P(6, 3) = [ 1+ ( P(3) + 1) * 3 + ( P(2) + 1) * 2 ] / 6= [ 1+ ( 5/3 + 1) * 3 + ( 3/2 + 1) * 2 ] / 6
注意:这里 P(3)、P(2) 是具有 3 个结点、2 个结点的二叉分类树的平均查找长度。 在一般情况,P(i)为具有 i 个结点二叉分类树的平均查找长度。
P(3) = (1+2+2)/ 3 = 5/3
P(2) = (1+2)/ 2 = 3/2∴ P(n,i)= [ 1+ ( P(i) + 1) * i + ( P(n-i-1) + 1) * (n-i-1) ] / n
∴ P(n)= P(n,i)/ n <= 2(1+I/n)lnn
因为 2(1+I/n)lnn≈1.38logn 故P(n)=O(logn)
与次优二叉树相对,二叉排序树是一种动态树表。其特点是:树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的节点时再进行插入。新插入的结点一定是一个新添加的叶子节点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。
插入算法
首先执行查找算法,找出被插结点的父亲结点。
判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。
若二叉树为空。则首先单独生成根结点。
注意:新插入的结点总是叶子结点。
删除结点
在二叉排序树删去一个结点,分三种情况讨论:
-
若p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。由于删去叶子结点不破坏整棵树的结构,则可以直接删除此子结点。
-
若p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点*f的左子树(当p是左子树)或右子树(当p是右子树)即可,作此修改也不破坏二叉排序树的特性。
-
若p结点的左子树和右子树均不空。在删去p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整,可以有两种做法:其一是令p的左子树为f的左/右(依p是f的左子树还是右子树而定)子树,s为p左子树的最右下的结点,而p的右子树为s的右子树;其二是令p的直接前驱(或直接后继)替代p,然后再从二叉排序树中删去它的直接前驱(或直接后继)-即让f的左子树(如果有的话)成为p左子树的最左下结点(如果有的话),再让f成为p的左右结点的父结点。
以下是基于Java语言的实现与测试:
package com.wxisme.binarysearchtree; import java.util.Scanner; /** * 二叉查找(排序)树的实现 * @time 2015/8/16 12:02 * @author wxisme * @param <T> */ public class BinarySearchTree<T extends Comparable> { private class TreeNode { private T data; private TreeNode left; private TreeNode right; private TreeNode parent; public TreeNode() {} public TreeNode(T data) { this.data = data; this.left = null; this.right = null; this.parent = null; } } private TreeNode root; private int nodeCount; private int depth; public BinarySearchTree() {} public BinarySearchTree(T data) { root = new TreeNode(data); } /** * 添加元素 * @param data */ public void add(T data) { TreeNode node = new TreeNode(data); if(root == null) { root = node; } else { addSearch(node, root); } nodeCount ++; } public void addSearch(TreeNode node, TreeNode root) { if(node.data.compareTo(root.data)<=0) { if(root.left == null) { root.left = node; node.parent = root; return; } else { addSearch(node, root.left); } } else { if(root.right == null) { root.right = node; node.parent = root; return; } else { addSearch(node, root.right); } } } /** * 查找节点 * @param data * @return */ public TreeNode search(T data) { TreeNode it = root; if(it == null) { System.out.println("树为空!"); return null; } else { while(it != null) { if(data.compareTo(it.data)<0) { it = it.left; } else if(data.compareTo(it.data)>0) { it =it.right; } else { return it; } } } return null; } /** * 删除元素 * @param data */ public void remove(T data) { TreeNode node = search(data); if(node == null) { System.out.println("不存在此节点!"); return ; } //左右子树均为空 if(node.left==null && node.right==null) { if(root == node) { root = null; } else { if(node == node.parent.left) { node.parent.left = null; } else { node.parent.right = null; } node.parent = null; } } //左子树不为空,右子树为空 else if(node.left!=null && node.right==null) { if(root == node) { root = node.left; root.parent = null; } else { if(node == node.parent.left) { node.parent.left = node.left; node.left.parent = node.parent; node.left = null; node.parent = null; } else { node.parent.right = node.left; node.left.parent = node.parent; node.left = null; node.parent = null; } } } //右子树不为空,左子树为空 else if(node.left==null && node.right!=null) { if(root == node) { root = node.right; root.parent = null; } else { if(node == node.parent.left) { node.parent.left = node.right; node.right.parent = node.parent; node.right = null; node.parent = null; } else { node.parent.right = node.right; node.right.parent = node.parent; node.right = null; node.parent = null; } } } //左右子树均不为空 else { //左子树中值最大的节点 TreeNode leftMaxNode = node.left; while(leftMaxNode.right != null) { leftMaxNode = leftMaxNode.right; } //将左子树中值最大的节点移到要删除的节点的位置上来。 leftMaxNode.parent.right = null; leftMaxNode.parent = node.parent; if(node == node.parent.left) { node.parent.left = leftMaxNode; } else { node.parent.right = leftMaxNode; } leftMaxNode.left = node.left; leftMaxNode.right = node.right; //释放要删除的节点的引用 node.parent = node.left = node.right = null; } nodeCount --; } /** * 节点的个数 * @return */ public int size() { return nodeCount; } /** * 销毁树 */ public void destroy() { root = null; nodeCount = 0; } /** * 中序遍历树 */ public void traversal() { System.out.print("中序遍历:"); inOrderTraversal(root); System.out.println(); } public void inOrderTraversal(TreeNode root) { if(root == null) { return ; } inOrderTraversal(root.left); System.out.print(root.data); inOrderTraversal(root.right); } /** * 测试 * @param args */ public static void main(String[] args) { BinarySearchTree<Integer> tree = new BinarySearchTree<Integer>(); Scanner scan = new Scanner(System.in); for(int i=0; i<10; i++) { tree.add(scan.nextInt()); } tree.traversal(); System.out.print("删除节点:"); tree.remove(scan.nextInt()); tree.traversal(); System.out.println("节点的个数:" + tree.size()); System.out.println("销毁树"); tree.destroy(); System.out.println("节点的个数:" + tree.size()); } }
测试结果:
5 9 6 3 2 8 7 4 1 0
中序遍历:0123456789
删除节点:7
中序遍历:012345689
节点的个数:9
销毁树
节点的个数:0
作者:Pickle
声明:对于转载分享我是没有意见的,出于对博客园社区和作者的尊重一定要保留原文地址哈。
致读者:坚持写博客不容易,写高质量博客更难,我也在不断的学习和进步,希望和所有同路人一道用技术来改变生活。觉得有点用就点个赞哈。