二叉排序树与平衡二叉树

二叉排序树 :

特点: 
1、如果它的左子树不空,那么左子树上的所有结点值均小于它的根结点值; 
2、如果它的右子树不空,那么右子树上的所有结点值均大于它的根结点值; 
3、它的左右子树也分别为二叉查找树 
如下如所示二叉查找树: 
《二叉排序树与平衡二叉树》

二叉查找树的插入和删除都非常的方便,很好的解决了折半查找添加删除所带来的问题。

那么它的效率又如何呢?

很显然,二叉查找树查找一个数据,不需要遍历全部的节点,查找效率确实提高了。但是,也有一个很严重的问题,我在a图中查找8需要比较5次,而在b图中查找8需要3次,更为严重的是,我的二叉查找树是c图,如果再查找8,那将会如何呢?很显然,整棵树就退化成了一个线性结构,此时再查找8,就和顺序查找没什么区别了。

时间复杂度分析:最坏的情况下和顺序查找相同,是O(N),最好的情况下和折半查找相同,是O(logN)。进过研究发现,在随机的情况下,二叉排序树的平均查找长度和logn是等数量级的。然而,在某些情况下(有人研究表明,这种概率大约占46.5%)尚需在构成二叉排序树的过程中进行”平衡化”处理,成为二叉平衡树。

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

二叉排序树的一些基本操作,包括,添加节点,删除节点,获取做大的节点,获取最小的节点,得到任意孩子节点的父节点等。

/** * 向二叉排序树中添加节点 * @param root * @param value * @return 二叉排数树根节点 */
    public static TreeNode insertTreeNode(TreeNode root,int value){
        if(root == null){
            root = new TreeNode(value);
        }else if(root.value>value){
            root.left = insertTreeNode(root.left,value);
        }else if(root.value<value){
            root.right =insertTreeNode(root.right,value);
        }
        return root;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
/** * 在二叉排数树中查找,关键字为key的节点 * @param root * @param key * @return 二叉排数树中关键字为key的节点 */
    public static TreeNode serachTreeNode(TreeNode root,int key){
        if(root == null){
            return null;
        }
        if(key>root.value)
            return serachTreeNode(root.right,key);  
        else if(key<root.value)
            return serachTreeNode(root.left,key);  
        else  
            return root;  
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
//获取父节点
public static TreeNode getParentTreeNode (TreeNode root,int key){
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if(root==null || root.value==key){
            return null;
        }
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode current = queue.poll();
            if(current.left!=null){
                if(current.left.value==key)
                    return current;
                else
                    queue.add(current.left);
            }
            if(current.right!=null){
                if(current.right.value==key)
                    return current;
                else
                    queue.add(current.right);
            }
        }
        return null;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
//获得关键字最大的节点
    public static TreeNode getMaxTreeNode(TreeNode root){
        if(root == null || root.right==null)
            return root;
        if(root.right != null){
            root = getMaxTreeNode(root.right);
        }
        return root;
    }
    //获得关键字最小的节点
    public static TreeNode getMinTreeNode(TreeNode root){
        if(root == null || root.left==null)
            return root;
        if(root.left != null){
            root = getMinTreeNode(root.left);
        }
        return root;
    }
    //打印排序二叉树
    public static void printInOrder(TreeNode root){
        if(root!=null){
            printInOrder(root.left);
            System.out.print(root.value+" ");
            printInOrder(root.right);           
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

最后关于二叉排序树的删除相对比较复杂,可以分下面三种情况讨论:

(1)需要删除的节点下并没有其他子节点,其为叶子节点。

(2)需要删除的节点下有一个子节点(左或右)。

(3)需要删除的节点下有两个子节点(既左右节点都存在)。

《二叉排序树与平衡二叉树》

  • 针对第一种情况:删除叶子节点,例如删除4,只需判断4是它的父节点的左孩子,还是右孩子。如图中,4则为其父节点3的右孩子,所以直接将3的右孩子置为空。

  • 针对第二种情况:要删除的节点有一个孩子节点,若其为其父节点的右孩子,则让其父节点的右指针指向其孩子节点;若其为其父节点的左孩子,则让其父节点的左指针指向其孩子节点。

  • 针对第三种情况:要删除的节点既有左孩子又有右孩子,例如节点2,则我们先在需要删除的节点的右子树中,找到一个最小的值(因为右子树中的节点的值一定大于根节点)4。然后,用找到的最小的值与需要删除的节点的值替换。然后,再将最小值的原节点进行删除.

public static void deleteTreeNode(TreeNode root,TreeNode pNode){
        TreeNode parent = getParentTreeNode(root,pNode.value);
        //该节点是叶子节点
        if(pNode.left==null && pNode.right==null){
            //该节点是父节点的左孩子
            if(parent.left==pNode)
                parent.left=null;
            else
                parent.right=null;
        //该节点只有右孩子
        }else if(pNode.left==null&&pNode.right!=null){
            //该节点为其父节点的左孩子
            if(pNode == parent.left ){
                parent.left = pNode.right;
            }else{
                parent.right = pNode.right;
            }
        //该节点只有右孩子
        }else if(pNode.left!=null&&pNode.right==null){
            if(pNode ==parent.left ){
                parent.left = pNode.left;
            }else{
                parent.right = pNode.left;
            }
        //该节点左右孩子都存在
        }else if(pNode.left!=null&&pNode.right!=null){
            /* * 我们先在需要删除的节点的右子树中,找到一个最小的值(因为右子树中的节点的值一定大于根节点)。 * 然后,用找到的最小的值与需要删除的节点的值替换。然后,再将最小值的原节点进行删除 * */           
            TreeNode minNode=getMinTreeNode(pNode.right);
            deleteTreeNode(root,minNode);
            TreeNode pNpdeparent = getParentTreeNode(root,pNode.value);
            if(pNpdeparent.left==pNode){
                pNpdeparent.left=minNode;
                minNode.left = pNode.left;
                minNode.right = pNode.right;
            }               
            else{
                pNpdeparent.right=minNode;
                minNode.left = pNode.left;
                minNode.right = pNode.right;
            }               
        }
    }   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

最后附上有关二叉排序树的基本操作的代码:(可直接运行)

输入:两行,第一行为二叉树节点的个数,第二行为各个节点的值(不重复) 
10 
89 67 34 30 40 50 44 55 66 77 
输出:逐步按输入删除节点(除根节点)后二叉排序树的中序遍历(有序) 
30 34 40 44 50 55 66 67 77 89 
30 34 40 44 50 55 66 77 89 
30 40 44 50 55 66 77 89 
40 44 50 55 66 77 89 
44 50 55 66 77 89 
44 55 66 77 89 
55 66 77 89 
66 77 89 
77 89 
89

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.LinkedList;
import java.util.Queue;

class TreeNode {
    public int value;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(int value){
        this.value = value;
        this.left = this.right = null;
    }
}
public class BinarySearchTree {
    /**
     * 向二叉排序树中添加节点
     * @param root
     * @param value
     * @return 二叉排数树根节点
     */
    public static TreeNode insertTreeNode(TreeNode root,int value){
        if(root == null){
            root = new TreeNode(value);
        }else if(root.value>value){
            root.left = insertTreeNode(root.left,value);
        }else if(root.value<value){
            root.right =insertTreeNode(root.right,value);
        }
        return root;
    }
    /**
     * 在二叉排数树中查找,关键字为key的节点
     * @param root
     * @param key
     * @return 二叉排数树中关键字为key的节点
     */
    public static TreeNode serachTreeNode(TreeNode root,int key){
        if(root == null){
            return null;
        }
        if(key>root.value)
            return serachTreeNode(root.right,key);  
        else if(key<root.value)
            return serachTreeNode(root.left,key);  
        else  
            return root;  
    }
    public static void deleteTreeNode(TreeNode root,TreeNode pNode){
        TreeNode parent = getParentTreeNode(root,pNode.value);
        //该节点是叶子节点
        if(pNode.left==null && pNode.right==null){
            //该节点是父节点的左孩子
            if(parent.left==pNode)
                parent.left=null;
            else
                parent.right=null;
        //该节点只有右孩子
        }else if(pNode.left==null&&pNode.right!=null){
            //该节点为其父节点的左孩子
            if(pNode == parent.left ){
                parent.left = pNode.right;
            }else{
                parent.right = pNode.right;
            }
        //该节点只有右孩子
        }else if(pNode.left!=null&&pNode.right==null){
            if(pNode ==parent.left ){
                parent.left = pNode.left;
            }else{
                parent.right = pNode.left;
            }
        //该节点左右孩子都存在
        }else if(pNode.left!=null&&pNode.right!=null){
            /*
             * 我们先在需要删除的节点的右子树中,找到一个最小的值(因为右子树中的节点的值一定大于根节点)。
             * 然后,用找到的最小的值与需要删除的节点的值替换。然后,再将最小值的原节点进行删除
             * */           
            TreeNode minNode=getMinTreeNode(pNode.right);
            deleteTreeNode(root,minNode);
            TreeNode pNpdeparent = getParentTreeNode(root,pNode.value);
            if(pNpdeparent.left==pNode){
                pNpdeparent.left=minNode;
                minNode.left = pNode.left;
                minNode.right = pNode.right;
            }               
            else{
                pNpdeparent.right=minNode;
                minNode.left = pNode.left;
                minNode.right = pNode.right;
            }               
        }
    }   
    //获取父节点
    /*
      public static TreeNode getParentTreeNode(TreeNode root,int key){
            if(root!=null){
                if(root.left!=null && root.right!=null){
                    if(root.left.value == key || root.right.value ==key){
                        return root;
                    }else{
                        TreeNode tempRoot= getParentTreeNode(root.left,key);
                        if(tempRoot!=null)
                            return tempRoot;
                        root = getParentTreeNode(root.right,key);
                    }
                }else if(root.left==null && root.right!=null){
                    if(root.right.value ==key){
                        return root;
                    }else{
                        root = getParentTreeNode(root.right,key);
                    }
                }else if(root.left!=null && root.right==null){
                    if(root.left.value ==key){
                        return root;
                    }else{
                        root = getParentTreeNode(root.left,key);
                    }
                }
                else {
                    return null;
                }
            }
            return root;
        }
        */
    public static TreeNode getParentTreeNode (TreeNode root,int key){
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if(root==null || root.value==key){
            return null;
        }
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode current = queue.poll();
            if(current.left!=null){
                if(current.left.value==key)
                    return current;
                else
                    queue.add(current.left);
            }
            if(current.right!=null){
                if(current.right.value==key)
                    return current;
                else
                    queue.add(current.right);
            }
        }
        return null;
    }
    //获得关键字最大的节点
    public static TreeNode getMaxTreeNode(TreeNode root){
        if(root == null || root.right==null)
            return root;
        if(root.right != null){
            root = getMaxTreeNode(root.right);
        }
        return root;
    }
    //获得关键字最小的节点
    public static TreeNode getMinTreeNode(TreeNode root){
        if(root == null || root.left==null)
            return root;
        if(root.left != null){
            root = getMinTreeNode(root.left);
        }
        return root;
    }
    //打印排序二叉树
    public static void printInOrder(TreeNode root){
        if(root!=null){
            printInOrder(root.left);
            System.out.print(root.value+" ");
            printInOrder(root.right);           
        }
    }
    public static void main(String[] args) throws IOException {
        StreamTokenizer cin = new StreamTokenizer (new BufferedReader(new InputStreamReader(System.in)));
        while(cin.nextToken()!=cin.TT_EOF){
            int n =(int)cin.nval;
            int []input = new int[n];
            for(int i=0;i<n;i++){
                cin.nextToken();
                input[i] = (int)cin.nval;
            }
            TreeNode root = null;
            for(int i=0;i<n;i++){
                root = insertTreeNode(root,input[i]);
            }
            printInOrder(root);
            for(int i=1;i<input.length;i++){
                TreeNode pNpde = serachTreeNode(root,input[i]);
                deleteTreeNode(root,pNpde);
                System.out.print("\n");
                printInOrder(root);
            }
        }
    }
}

平衡二叉树

由于二叉排序树的查找效率是在O(n)~O(logn)之间,为了提高二叉排序树的查找效率,于是很多人想到通过调节二叉排序树使之变化成为左右子树之间的深度绝对值之差不超过1的平衡二叉树。

最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。

关于二叉排序树这篇文章介绍的比较好:二叉排序树的介绍

  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/shan1991fei/article/details/73921016
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞