[数据结构]P2.1 二叉搜索树

二叉树就是每个节点最多有两个分叉的树。这里我们写一写一个典型的例子二叉搜索树,它存在的实际意义是什么呢?

在P1.1链表中,我们清楚了链表的优势是善于删除添加节点,但是其取值很慢;数组的优势是善于取值,但是不利于删除添加节点。

而二叉搜索树,正是两者的折中方案。首先,它是树状结构,因此它便于插入和删除节点,需要花费LogN级别的时间,其次,它

在每一个节点上都满足`左子树 <= 当前节点 <= 右子树`,因此便于使用二叉搜索,所以查找数据和取数据也是LogN级别的。

 

时间比较 链表 二叉搜索树 数组
取值 N LogN C
查找 N LogN 有序数组:LogN 无序数组:N
插入和删除 C LogN N

 

 

 

 

 

 

常见的二叉搜索树操作有:

1.插入新节点

2.按值查找节点

3.删除节点

4.三种遍历方式

5.二叉搜索树的宽度

6.二叉搜索树最大距离

 

TODO : 待解析

 

Java代码:

 

package ds4.binaryTree;

import java.util.ArrayDeque;
import java.util.Queue;

/**
 * 二叉搜索树
 */
public class BinarySearchTree {

    static class Node {
        long data;
        Node left;
        Node right;

        public Node(long data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return "N["+data+"]";
        }
    }

    static class Tree{
        Node root;
        Tree(){}

        /**
         * 添加节点: 时间消耗LogN
         * @param node
         */
        public void addNode(Node node){
            if(node == null){
                return;
            }

            if(this.root == null){
                this.root = node;
                return;
            }
            Node current = root;
            Node parent = current;
            while(current != null){
                parent = current;
                if(current.data >= node.data){
                    current = current.left;
                }else{
                    current = current.right;
                }
            }

            if(parent.data >= node.data){
                parent.left = node;
            }else{
                parent.right = node;
            }
        }

        /**
         * 查找节点
         * 时间消耗LogN
         * @param data
         * @return
         */
        public Node fineNodeByVal(long data){
            Node result = null;
            if(this.root == null){
                return result;
            }

            Node current = this.root;
            while(current != null){
                if(current.data > data){
                    current = current.left;
                }else if(current.data < data){
                    current = current.right;
                }else{
                    result = current;
                    break;
                }
            }
            return result;
        }

        /**
         * 删除某个节点
         * @param node
         */
        public void removeNode(Node node){
            if(this.root == null){
                return;
            }

            if(node == null){
                return;
            }

            if(this.root == node){
                this.root = null;
                node.left = node.right = null;
                return;
            }


            // 寻找其父节点
            Node current = root;
            Node parent = current;
            while(current != null){
                if(current == node){
                    break;
                }
                parent = current;
                if(current.data < node.data){
                    current = current.right;
                }else{
                    current = current.left;
                }
            }
            if(current == null){
                System.out.println("没有找到这个节点!");
                return;
            }

            if(node.left == null && node.right == null){ //左右都为空
                // 找到其父节点直接删除即可
                if(parent.left == null){
                    parent.left = null;
                }else{
                    parent.right = null;
                }
                return;
            }else if(node.left != null && node.right == null){ // 左子树不为空,而右子树为空
                // 找到其父节点直接嫁接
                if(parent.left == null){
                    parent.left = node.left;
                }else{
                    parent.right = node.left;
                }

            }else if(node.right != null && node.left == null){ // 右子树不为空,左子树为空
                //找到父节点直接嫁接
                if(parent.right == node){
                    parent.right = node.right;
                }else{
                    parent.left = node.right;
                }
            }else{ // 左右子树都不为空
                //找到左枝最靠右节点,删除;并找到带删除父节点,将待删除节点用左枝最右节点替代
                Node current1 = node;
                Node parent1 = node;
                while (current1.right != null){
                    parent1 = current1;
                    current1 = current1.right;
                }
                if(parent1.right == current1){
                    parent1.right = null;
                }else{
                    parent1.left = null;
                }
                // 到这里current1为最右左节点,parent1为current1的父节点,已经解除了父子关系
                current1.left = node.left;
                current1.right = node.right;

                if(parent.right == current){
                    parent.right = current1;
                }else{
                    parent.left = current1;
                }

                node.left = null;
                node.right = null;
            }
        }

        /**
         * 先根遍历
         */
        public void firstRootIndex(Node root){
            if(root != null){
                System.out.println(root.data);
                if(root.left != null){
                    firstRootIndex(root.left);
                }

                if(root.right != null){
                    firstRootIndex(root.right);
                }
            }
        }

        /**
         * 后根遍历
         * @param root
         */
        public void lastRootIndex(Node root){
            if(root != null){
                if(root.left != null){
                    lastRootIndex(root.left);
                }
                if(root.right != null){
                    lastRootIndex(root.right);
                }
                System.out.println(root.data);
            }
        }

        /**
         * 中序遍历
         * @param root
         */
        public void midRootIndex(Node root){
            if(root != null){
                if(root.left != null){
                    lastRootIndex(root.left);
                }
                System.out.println(root.data);
                if(root.right != null){
                    lastRootIndex(root.right);
                }
            }
        }

        /**
         * 层序遍历
         * @param root
         */
        public void layerIndex(Node root){
            // 1.当前层
            Queue<Node> currentLayer = new ArrayDeque<Node>();
            // 2.临时存储下一层节点
            Queue<Node> sonLayer = new ArrayDeque<Node>();

            if(this.root == null){
                return;
            }else{
                currentLayer.add(this.root);
            }

            while(currentLayer.size() != 0){
                // 1.当前层节点出队列下一层入队列
                sonLayer.clear();
                while(currentLayer.size() != 0){
                    Node node = currentLayer.poll();
                    // 2.遍历本层节点
                    System.out.print(node);

                    if(node.left != null){
                        sonLayer.add(node.left);
                    }
                    if(node.right != null){
                        sonLayer.add(node.right);
                    }
                }
                System.out.println();
                // 3.将sonLayer赋给currentLayer,sonLayer重新创建对象
                currentLayer = sonLayer;
                sonLayer = new ArrayDeque<Node>();
            }

        }


        /**
         * 计算树的宽度
         * 也就是树中节点数最多的层次对应的节点数
         * 思路1: 进行一次树的遍历,也就是过一遍树的所有节点,将每一层的数据存储在Map数据结构中
         * 思路2: 设置队列进行层序遍历,将这层的节点数量计算一下,然后出队列,下一层的节点入队列
         *
         * 思路2提供了除去以上三种遍历方式以外的第三种遍历方式
         */
        public int computeWidth(){
            int maxSize = 0;
            // 1.当前层
            Queue<Node> currentLayer = new ArrayDeque<Node>();
            // 2.临时存储下一层节点
            Queue<Node> sonLayer = new ArrayDeque<Node>();

            if(this.root == null){
                return maxSize;
            }else{
                currentLayer.add(this.root);
            }

            while(currentLayer.size() != 0){
                // 1. 计算当前层size
                int currentSize = currentLayer.size();
                maxSize = maxSize>currentSize?maxSize:currentSize;
                // 2.当前层节点出队列下一层入队列
                sonLayer.clear();
                while(currentLayer.size() != 0){
                    Node node = currentLayer.poll();
                    if(node.left != null){
                        sonLayer.add(node.left);
                    }
                    if(node.right != null){
                        sonLayer.add(node.right);
                    }
                }
                // 3.将sonLayer赋给currentLayer,sonLayer重新创建对象
                currentLayer = sonLayer;
                sonLayer = new ArrayDeque<Node>();
            }

            return maxSize;
        }

        /**
         * 计算二叉树中两点的最大距离
         * 思路1: 左枝树长 + 右枝树长 算法:节省时间,占空间
         * 思路2: 迭代 算法:节省空间,占用时间
         */
        public int computeMaxDistance(){
            if(this.root == null){
                return 0;
            }

            return computeDepth(this.root.left) + computeDepth(this.root.right);
        }


        /**
         * 计算二叉树的深度
         * 和刚才计算宽度的思路一样,只是用深度来计算
         * @return
         */
        public int computeDepth(Node root){
            int depth = 0;
            // 1.当前层
            Queue<Node> currentLayer = new ArrayDeque<Node>();
            // 2.临时存储下一层节点
            Queue<Node> sonLayer = new ArrayDeque<Node>();

            if(root == null){
                return depth;
            }else{
                currentLayer.add(root);
            }

            while(currentLayer.size() != 0){
                // 1. 计算当前层depth
                depth ++;
                // 2.当前层节点出队列下一层入队列
                sonLayer.clear();
                while(currentLayer.size() != 0){
                    Node node = currentLayer.poll();
                    if(node.left != null){
                        sonLayer.add(node.left);
                    }
                    if(node.right != null){
                        sonLayer.add(node.right);
                    }
                }
                // 3.将sonLayer赋给currentLayer,sonLayer重新创建对象
                currentLayer = sonLayer;
                sonLayer = new ArrayDeque<Node>();
            }

            return depth;
        }


    }




    public static void main(String[] args) {
        Tree tree = new Tree();
        tree.addNode(new Node(100));
        tree.addNode(new Node(200));
        tree.addNode(new Node(20));
        tree.addNode(new Node(177));
        Node nodeToRemove = new Node(88);
        tree.addNode(nodeToRemove);
        tree.addNode(new Node(90));
        tree.addNode(new Node(60));
        // 层序遍历
        System.out.println("层次遍历");
        tree.layerIndex(tree.root);
        // 先根遍历
        System.out.println("先根遍历");
        tree.firstRootIndex(tree.root);
        // 计算宽度与深度
        tree.addNode(new Node(188));
        System.out.println("width:"+tree.computeWidth());
        System.out.println("depth:"+tree.computeDepth(tree.root));
        // 删除节点
        tree.removeNode(nodeToRemove);
        // 层序遍历
        System.out.println("层序遍历");
        tree.layerIndex(tree.root);
        // 后根遍历
        System.out.println("后根遍历");
        tree.lastRootIndex(tree.root);
        System.out.println("最大距离:"+tree.computeMaxDistance());

    }
}

 result:

层次遍历
N[100]
N[20]N[200]
N[88]N[177]
N[60]N[90]
先根遍历
100
20
88
60
90
200
177
width:3
depth:4
层序遍历
N[100]
N[20]N[200]
N[90]N[177]
N[60]N[188]
后根遍历
60
90
20
188
177
200
100
最大距离:6

 

posted @ 2019-04-18 19:46  yosql473  阅读(233)  评论(0编辑  收藏  举报