1. 程式人生 > 實用技巧 >牛客多校第三場補題

牛客多校第三場補題

1. 樹的定義

樹的特點:

  • 每個結點有0個或者多個子結點
  • 沒有父結點的結點為根節點
  • 每一個非根結點只有一個父結點
  • 每個結點及其後代結點整體上可以看做一棵樹,稱為當前結點的父結點的一個子樹

2. 樹的相關術語

  • 結點的度:一個結點含有的子樹的個數稱為結點的度(也可以理解為子結點的個數)
  • 葉結點:度為0的結點稱為葉結點
  • 分支結點:與葉結點相反,度不為0的結點稱為分支結點
  • 結點的層次:從根結點開始,根結點的層次為1,根的直接後繼層次為2,以此類推
  • 樹的度:樹中所有結點的度的最大值
  • 樹的深度:樹中結點的最大層次

3. 二叉樹的基本定義

      二叉樹就是度不超過2的樹(每個結點多有兩個子結點)

  • 滿二叉樹:一個二叉樹,每一層的結點都達到最大值,則這個二叉樹就是滿二叉樹

  • 完全二叉樹:葉節點只能出現在下層和次下層,並且下面一層的結點都集中在該層左邊的若干位置的二叉樹

4. 二叉樹的API設計

5.二叉樹的程式碼實現

public class BinaryTree <Key extends Comparable<Key>,Value>{
    //記錄根結點
    private Node root;
    private int N;

    private class Node{
        public Key key;
        public Value value;
        public Node left;
        public Node right;
        public Node(Key key, Value value, Node left, Node right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }
    //獲取樹中元素的個數
    public int size(){
        return N;
    }
    //向樹中新增元素key-value
    public void put(Key key,Value value){
        root = put(root,key,value);
    }
    //向指定樹X中新增元素key-value
    public Node put(Node x,Key key,Value value){
        //如果x子樹為空
        if (x == null){
            N++;
            return new Node(key,value,null,null);
        }
        //如果X子樹不為空
        int cmp = key.compareTo(x.key);
        if (cmp>0){
            x.right = put(x.right,key,value);
        }else if (cmp<0){
            x.left = put(x.left,key,value);
        }else {
            x.value = value;
        }
        return x;
    }
    //查詢樹種指定key的對應的value
    public Value get(Key key){
        return get(root,key);
    }
    //從指定樹中查詢key對應的值
    public Value get(Node x ,Key key){
        //x樹為null
        if (x == null){
            return null;
        }
        //x樹不為null
        int cmp = key.compareTo(x.key);
        if (cmp>0){
            return get(x.right,key);
        }else if (cmp<0){
            return get(x.left,key);
        }else {
            return x.value;
        }
    }
    //刪除樹中key對應的value
    public void delete(Key key){
        delete(root,key);
    }
    //刪除指定樹中key對應的value,並返回刪除後的新樹
    public Node delete(Node x,Key key){
        N--;
        //x樹為null
        if (x == null){
            return null;
        }
        //x樹不為null
        int cmp = key.compareTo(x.key);
        if (cmp>0){
            x.right = delete(x.right,key);
        }else if (cmp<0){
            x.left = delete(x.left,key);
        }else {
            if (x.right==null){
                return x.left;
            }
            if (x.left==null){
                return x.right;
            }
            Node minNode = x.right;
            while (minNode.left!=null){
                minNode = minNode.left;

            }
            Node n = x.right;
            while (n.left!=null){
                if (n.left.left==null){
                    n.left = null;
                }else {
                    n = n.left;
                }
            }
            minNode.left = x.left;
            minNode.right = x.right;
            x = minNode;
        }
        return null;
    }
}

6.二叉樹的遍歷

1. 前序遍歷

      先訪問根結點,然後再訪問左子樹,後訪問右子樹

2. 前序遍歷程式碼實現
    //獲取整個樹中所有的鍵
    public Queue<Key> preErgodic(){
        Queue<Key> keys = new Queue<>();
        preErgodic(root,keys);
        return keys;
    }
    //獲取指定樹x的所有鍵,並放到keys佇列中
    private void preErgodic(Node x,Queue<Key> keys){
        if (x==null){
            return;
        }
        //把x結點的key放入到keys中
        keys.enqueue(x.key);
        //遞迴遍歷x結點的左子樹
        if (x.left!=null){
            preErgodic(x.left,keys);
        }
        //遞迴遍歷x結點的右子樹
        if (x.right!=null){
            preErgodic(x.right,keys);
        }
    }
3. 中序遍歷

      先訪問左子樹,中間訪問根節點,後訪問右子樹

4. 中序遍歷程式碼實現
    public Queue<Key> midErgodic(){
        Queue<Key> keys = new Queue<>();
        midErgodic(root,keys);
        return null;
    }
    public void midErgodic(Node x,Queue<Key> keys){
        if (x==null){
            return;
        }
        //先遞迴,把左子樹中的鍵放到keys中
        if (x.left!=null){
            midErgodic(x.left,keys);
        }
        //把當前結點的x的鍵放到keys中
        keys.enqueue(x.key);
        //再遞迴,把右子樹中的鍵放到keys中
        if (x.right!=null){
            midErgodic(x.right,keys);
        }
    }

5. 後序遍歷

      先訪問左子樹,再訪問右子樹,後訪問根節點

6. 後序遍歷程式碼實現
    public Queue<Key> afterErgodic(){
        Queue<Key> keys = new Queue<>();
        afterErgodic(root,keys);
        return keys;
    }

    public void afterErgodic(Node x,Queue<Key> keys){
        if (x==null){
            return;
        }
        if (x.left!=null){
            afterErgodic(x.left,keys);
        }
        if (x.right!=null){
            afterErgodic(x.right,keys);
        }
        keys.enqueue(x.key);
    }
7. 層序遍歷

      所謂的層序遍歷,就是從根節點(第一層)開始,依次向下,獲取每一層所有結點的值

8. 層序遍歷程式碼實現
    public Queue<Key> layerErgodic(){
        Queue<Key> keys = new Queue<>();
        Queue<Node> nodes = new Queue<>();
        nodes.enqueue(root);
        while (!nodes.isEmpty()){
            Node n = nodes.dequeue();
            keys.enqueue(n.key);
            if (n.left!=null){
                nodes.enqueue(n.left);
            }
            if (n.right!=null){
                nodes.enqueue(n.right);
            }
        }
        return keys;
    }

7.二叉樹的最大深度程式碼實現

    public int maxDepth(){
        return maxDepth(root);
    }

    public int maxDepth(Node x){
        if (x ==null){
            return 0;
        }
        int max = 0;
        int maxL = 0;
        int maxR = 0;
        //計算左子樹最大深度
        if (x.left!=null){
            maxL = maxDepth(x.left);
        }
        //計算右子樹最大深度
        if (x.right!=null){
            maxR = maxDepth(x.right);
        }
        //比較左子樹的最大深度和右子樹的最大深度,取較大值
        max = maxL>maxR?maxL+1:maxR+1;
        return max;
    }