二叉樹

lanyu發表於2021-09-09

二叉樹(Binary Tree)是n(n>=0)個節點的有限集合,該集合或者為空集,或者有一個根節點和兩顆互不相交的分別稱為根節點的左子樹和右子樹的二叉樹組成。

特點

  • 每個節點最多有兩顆子樹。
  • 左子樹和右子樹是有順序的,次序不能任意顛倒。
  • 即使樹中某結點只有一個子樹,也要區分是左子樹還是右子樹。

在一棵二叉樹中,如果分支節點都存在左子樹和右子樹,並且所有葉子都在同一層上,這樣的二叉樹稱為滿二叉樹。

對一棵具有n個結點的二叉樹按層編號,如果編號為i(1<= i <=n)的結點與同樣深度的滿二叉樹編號為i的結點在二叉樹中位置完全相同,則這棵二叉樹稱為完全二叉樹。

1111775-9ef30862a7d7da6c.png
image.png

二叉樹的性質

  • 在二叉樹的第i層上至多有 2^(i-1)個結點
  • 深度為k的二叉樹至多有2^k - 1個結點
    如果有一層 至多有 2^0 - 1 = 1個結點
    如果有二層 至多有 1+2 = 2^2 - 1 個結點
  • 具有n個結點的完全二叉樹的深度為[log(2)n] +1 ([x] 不大於x的最大整數)
    -如果對一棵有n個結點的完全二叉樹的結點按層編號,對任一結點i
    1、如果 i= 1則結點i是二叉樹的根
    2、如果2i>n 則結點無左結點
    3、如果2
    i+1>n 則結點i無右結點

二叉樹的遍歷

  • 前序遍歷
1111775-0d3d55b351d9a919.png
image.png
 public void preOrderTraverse(){
        preOrderTraverse(root);
    }
    //前序遍歷  根 --> 左 --->右
    public void preOrderTraverse(Node root){
        if (root == null)return;
        System.out.println(root.val);
        preOrderTraverse(root.left);
        preOrderTraverse(root.right);
    }
  • 中序遍歷
1111775-75c93cab1d2b0e3d.png
image.png
// 左-->根--->右
    public void inOrderOrderTraverse(){
        inOrderOrderTraverse(root);
    }
    public void inOrderOrderTraverse(Node root){
        if (root == null)return;
        inOrderOrderTraverse(root.left);
        System.out.println(root.val);
        inOrderOrderTraverse(root.right);
    }
  • 後續遍歷


    1111775-e5d0e942f0623d30.png
    image.png
//左--->右--->中
public void postOrderOrderTraverse(){
        postOrderOrderTraverse(root);
    }
    public void postOrderOrderTraverse(Node root){
        if (root == null)return;
        postOrderOrderTraverse(root.left);
        postOrderOrderTraverse(root.right);
        System.out.println(root.val);
    }

//結點類

   private class Node{
        private String val;
        private Node left,right;

        public Node(String val) {
            this.val = val;
        }
    }

二叉查詢樹

一棵二叉查詢樹(BST)是一個二叉樹,其中每個結點都含有一個Comparable的鍵(以及相關的值)且每個節點的鍵都大於其左子樹的任意結點的鍵而小於右子樹的任意結點的鍵。

1111775-c65a6a8308459371.png
image.png
  • 結點類
適合查詢樹
private class Node{
        private Key key;    //按照key來排序
        private Value val;  // 相關聯的值
        private Node left,right;  //左右子結點
        private int size;         //結點個數
        public Node(Key key, Value val, int size) {
            this.key = key;
            this.val = val;
            this.size = size;
        }
    }
  • 插入子節點
    public void put(Key key,Value val){
        if (key == null)throw new IllegalArgumentException("calls put() with a null key");
        if (val == null){
            delete(key); // 刪除沒有值的結點
            return;
        }
        root = put(root,key,val);
    }
    //遞迴插入
    private Node put(Node x,Key key,Value val){
        if (x == null)return new Node(key,val,1);
        int cmp = key.compareTo(x.key);  // 比較一個結點的鍵
        if (cmp < 0)x.left = put(x.left,key,val);  // 小 則往左插入
        else if(cmp > 0)x.right = put(x.right,key,val); // 大 則往右插入
        else x.val = val;                                             // 相等則更新
        x.size = 1+size(x.left)+size(x.right);              //重新計算相關的結點的size
        return x;
    }

1111775-6542cbd8f2d2db48.png
image.png
  • 獲取鍵對應的值
    public Value get(Key key) {
        return get(root, key);
    }

    private Value get(Node x,Key key){
        if (key == null)throw new IllegalArgumentException("calls get() with a null key");
        if (x == null) return null;
        //二叉樹的分叉查詢
        int cmp = key.compareTo(x.key);
        // 左子樹
        if (cmp < 0)return get(x.left,key);
        // 右子樹
        else if(cmp > 0)return get(x.right,key);
        else return x.val;
    }

  • 最大鍵和最小鍵
    如果根結點的左連線為null,那麼一棵樹的二叉查詢樹中最小的鍵解釋根節點;如果非空,那麼樹中的最小鍵就是左子樹中的最小鍵。同樣道理適用於最大鍵
public Key min(){
        if (isEmpty()) throw new NoSuchElementException("calls min() with empty symbol table");
        return min(root).key;
    }

    private Node min(Node x){
        if (x.left == null)return x;
        else return min(x.left);
    }

    //最大
    public Key max(){
        if (isEmpty()) throw new NoSuchElementException("calls max() with empty symbol table");
        return max(root).key;
    }

    private Node max(Node x){
        if (x.right == null) return x;
        else return max(x.right);
    }
  • 刪除最大鍵和最小鍵

二叉查詢樹中最難實現的是delete()方法。即從符號表中刪除一個鍵值對。
刪除最小鍵所對應的鍵值對,就是不斷深入跟結點的左子樹直至出現一個空連結,然後將指向該節點的連結指向該節點的右子樹。

 public void deleteMin(){
        if (isEmpty()) throw new NoSuchElementException("Symbol table underflow");
        root = deleteMin(root);
    }
    private Node deleteMin(Node x){
        if (x.left == null) return x.right; // 借宿遞迴
        x.left = deleteMin(x.left);  // 進入遞迴
        x.size = size(x.left)+size(x.right)+1;
        return x;
    }
   public void deleteMax(){
       if (isEmpty()) throw new NoSuchElementException("Symbol table underflow");
       root = deleteMax(root);
   }
    private Node deleteMax(Node x){
        if (x.right == null)return x.left;
        x.right = deleteMax(x.right);
        x.size = size(x.left)+size(x.right)+1;
        return x;
    }
  • 刪除指定的鍵的鍵值對
    在刪除一個x結點後 需要用它的後續結點填補位置。因x可能有一個右子節點和左子結點。其右子節點就是其右子樹中最小的結點。因此具體的補位順序如下:
    1、將指向即將被刪除的結點的連結儲存為t
    2、將x指向它的後繼結點min(t.right)
    3、將x的右連結指向deleteMin(t.right),也就是在刪除後所有結點仍然都大於x.key的子二叉查詢樹
    4、將x的左連線設為t.left
    public void delete(Key key) {
        if (key == null) throw new IllegalArgumentException("calls delete() with a null key");
        root = delete(root, key);
    }
    private Node delete(Node x,Key key){
        if (x == null) return null;
        int cmp = key.compareTo(x.key);
        if (cmp < 0)x.left = delete(x.left,key);
        else if (cmp > 0)x.right = delete(x.right,key);  // 查詢到要刪除的鍵
        else {
            if (x.right == null)return x.left;
            if (x.left == null)return x.right;
            Node t =x;
            x = min(t.right);
            x.right = deleteMin(t.right );
            x.left = t.left;
        }
        x.size = size(x.left)+size(x.right);
        return x;
    }

相關文章