二次排序树
一.定义
二叉排序树(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),右子树P(2))
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的左右结点的父结点。
1.创建节点
public class Node { int value; Node left;//左节点 Node right;//右节点 public Node(int value) { this.value = value; } /*添加节点的方法满足二叉排序树*/ public void add(Node node){ if (node==null){ return; } //判断传入节点和当前子树节点的值 if (node.value<this.value){ if (this.left ==null){//左子节点为空直接添加 this.left=node; }else { this.left.add(node);//递归左子树添加 } }else {//添加的节点大于当前节点的值 if (this.right==null){ this.right=node; }else { this.right.add(node);//递归右子树添加 } } } /*中序遍历*/ public void infixOrder(){ if (this.left!=null){ this.left.infixOrder(); } System.out.println(this); if (this.right!=null){ this.right.infixOrder(); } } /*查找要删除的节点*/ public Node search(int va1ue){ if (va1ue == this.value){ return this; }else if (va1ue<this.value){//左节点递归查找 if (this.left==null){ return null; } return this.left.search(va1ue); }else {//右子树递归查找 if (this.right==null){ return null; } return this.right.search(va1ue); } } /*查找删除节点的父节点*/ public Node searchParent(int value){ if ((this.left!=null &&this.left.value==value) ||(this.right!=null&&this.right.value==value)){ return this; }else {//如果查找的值小于当前节点,并且当前节点不为空 if (value <this.value && this.left!=null){ return this.left.searchParent(value);//左递归 }else if (value>=this.value && this.right!= null){ return this.right.searchParent(value); }else { return null; } } } @Override public String toString() { return "Node{" + "value=" + value + '}'; } }
2.建立二叉排序树
public class BinarySearchTree { private Node root; /*查找要删除的节点*/ public Node search(int va1ue){ if (root==null){ return null; }else { return root.search(va1ue); } } /*查找删除节点的父节点*/ public Node searchParent(int value){ if (root==null){ return null; }else { return root.searchParent(value); } } /** * * @description:TODO * @params:1.节点(当做二叉的根节点) * 1.删除最小节点 * @return: 当前节点作为二叉数根节点的最小节点值 * @author: 苏兴旺 * @time: 2020/3/14 12:38 */ public int delRightTree(Node node){ Node targe = node; //一直往左边找就是最小值 while (targe.left!=null){ targe = targe.left; } delNode(targe.value); //指向最小节点 return targe.value; } /*删除节点 * 1.找待删除的节点taget * 2.找到待删除节点的父节点parent * 3.确定 taget 是父节点 左节点 右节点 * 4.根据实际情况进行删除 * 左节点==null * 右节点==null * * 第二种:删除的节点只有一个子树 * 1.找待删除的节点taget * 2.找到待删除节点的父节点parent * 3.确定taget的子节点是左节点还是右子节点 * 4.taget是parent左子节点还是右子节点 * 5.如果taget有左子节点 * 5.1 如果taget是parent左节点 parent.左 = taget.左 * 5.2如果taget是parent右节点parent.右 = taget.左 * 6.如果taget有右子节点 * 6.1 如果taget是parent左节点 parent.左 = taget.右 * 6.2如果taget是parent右节点parent.右 = taget.右 * * 情况三删除有二个数的节点: *.1找待删除的节点taget * 2.找到待删除节点的父节点parent * 3.从taget 的右节点找到最小的节点 * 4.用临时变量保存保存最小节点temp * 5.删除该节点 * taget.value=temp*/ public void delNode(int value){ if (root == null){ return; }else { Node target = search(value); if (target == null){//找到删除的节点taget return; } //target 树只有一个root接电脑 if (root.left==null && root.right==null){ root=null; return; } //找到删除节点的父节点 Node parent = searchParent(value); if (target.left==null&&target.right==null){//为叶子节点 if (parent.left!=null &&parent.left.value==value){//判断target节点是父节点的左节点还是右节点 parent.left=null; }else if (parent.right!=null&& parent.right.value==value){ parent.right=null; } }else if (target.left!=null && target.right!=null){//情况三删除有二个数的节点: // 情况三删除有二个数的节点: //1.找待删除的节点taget //2.找到待删除节点的父节点parent // 3.从taget 的右节点找到最小的节点 // 4.用临时变量保存保存最小节点temp // 5.删除该节点 int minVal = delRightTree(target.right); target.value=minVal; }else {//删除的节点只有一个子树 if (target.left!=null){//如果要删除的有左子节点 if (parent.left.value==value){//target是父节点的左节点 parent.left=target.left; }else {//target是父节点的右节点 parent.right = target.left; } }else {//如果要删除的有右子节点 if (parent.left.value==value){//target是父节点的左节点 parent.left=target.right; }else {//target是父节点的右节点 parent.right=target.right; } } } } } /*添加方法*/ public void add(Node node){ if (root==null){ root = node; }else { root.add(node); } } /*中序遍历*/ public void infixOrder(){ if (root==null){ System.out.println("二叉排序树为空!!!"); }else { root.infixOrder(); } } }
3.进行测试
public class BinarySearchTreeDemo { public static void main(String[] args) { int[] arr = {7,3,10,12,5,1,9,0}; BinarySearchTree binarySearchTree = new BinarySearchTree(); /*循环添加节点到二叉排序树*/ for (int i = 0; i < arr.length; i++) { binarySearchTree.add(new Node(arr[i])); } /*中序遍历*/ binarySearchTree.infixOrder(); /*测试删除叶子节点*/ //binarySearchTree.delNode(2); //binarySearchTree.delNode(5); binarySearchTree.delNode(10); System.out.println("删除后"); binarySearchTree.infixOrder(); } }