數據結構之樹——1. 樹的基本概念、二叉搜索樹學習

2024年2月6日 21点热度 0人点赞

1.1. 樹的基本概念

樹是n(n>=0)個節點的有限集,當n為0時,稱為空樹。對於任意一棵非空樹,應滿足:

1) 有且僅有一個特定的稱為根的節點

2)當n>1時,其餘節點可分為m(m>0)個互不相交的有限集,每個集合本省又是一棵樹,並且稱為根的子樹

由此可見,樹的定義是遞歸的,即在樹的定義中有用到了自身,樹是一種遞歸的數據結構,樹作為一種邏輯結構,同時也是一種分層結構,具有以下特點:

1)樹的根節點沒有前驅,除根節點外的所有節點有且隻有一個前驅

2)樹中所有節點可以有零個或多個後繼

對於n個節點的樹,有n-1條邊。

1.2. 二叉樹的概念

二叉樹本質上也是樹,它的特點是,每個節點至多隻有兩棵子樹,並且二叉樹的子樹有左右之分,它是有序樹,如果將其左右子樹顛倒,那麼會成為另一棵不同的二叉樹,二叉樹的5種基本形態如下:

1.3. 幾種特殊的二叉樹

1)斜樹

所有節點都隻有左子樹的二叉樹,叫作左斜樹,所有節點都隻有右子樹的二叉樹叫作右斜樹。這兩者統稱為斜樹。

2)滿二叉樹

一棵高度為h,且含有2^h - 1個節點的二叉樹,稱為滿二叉樹,即樹的每層都含有最多的節點,滿二叉樹的葉子節點集中在二叉樹的最下一層,並且除葉子節點之外的每個節點,度數均為2。假設編號從根節點開始為1,自上而下,從左到右,那麼,對於編號為n的節點,如果有父節點, 父節點為n/2,如果有左孩子,左孩子為2n,如果有右孩子,右孩子為2n 1, 如下圖所示:

3)完全二叉樹

對於高度為h,有n個節點的二叉樹,除了第h層外,其他各層(1~h-1)的節點數都達到最大格式(即1~h-1層是一個滿二叉樹),第h層所有節點都連續集中在最左邊,這就是完全二叉樹。

2. 二叉搜索樹

二叉搜索樹有以下特點:

1)左子樹上所有節點的值均小於它的根節點的值

2)右子樹上所有節點的值均大於它的根節點的值

3)任意節點的左右子樹也分別為二叉搜索樹

4)沒有鍵值相等的節點

2.1. 二叉搜索樹的插入

1)對於插入,如果一開始為空樹,那麼新建一個節點,作為該樹的根節點。

2)當不為空樹時,判斷插入的數字,與當前根節點的大小進行比較,如果比當前值小,插入到左子樹

3)當不為空樹時,判斷插入的數字,與當前根節點的大小進行比較,如果比當前值大,插入到右子樹

2.2. 二叉搜索樹的查找

1)判斷當前節點是否為空,為空直接返回

2)當前節點不為空,判斷查詢值域當前節點值的大小,如果比當前值小,進入左子樹查詢

3)當前節點不為空,判斷查詢值域當前節點值的大小,如果比當前值大,進入右子樹查詢

2.3. 二叉搜索樹的刪除

1)判斷當前節點是否為空,為空直接返回

2)當前節點不為空,刪除值小於當前節點值,進入左子樹刪除

3)當前節點不為空,刪除值大於當前節點值,進入右子樹刪除

4)當前節點不為空,刪除值等於當前值:

4.1)如果當前節點左右子樹都為空,直接刪除當前節點

4.2)如果當前節點左子樹不為空,右子樹為空,使用左節點取代當前節點

4.3)如果當前節點右子樹不為空,左子樹為空,使用右節點取代當前節點

4.4)左右子樹都不為空,選擇左子樹的最右節點或右子樹的最左節點,代替當前節點

2.4. 遍歷

對於樹的遍歷,一般有前序遍歷、中序遍歷、後序遍歷這幾種方式。

1)前序遍歷:對樹按照根、左、右的規律進行訪問,如下圖所示,遍歷結果為F,B,A,D,C,E,G,I,H

2) 中序遍歷:對樹,按照左、根、右的規律進行訪問。如下圖所示,遍歷結果為:A,B,C,D,E,F,G,H,I

3) 後序遍歷:對樹,按照左、右、根的規律進行訪問。如下圖所示,遍歷結果為:A, C, E, D, B,H, I, G, F

2.5. 二叉搜索樹示例代碼

完整代碼如下:

public class BinarySearchTree {
    class Node {
        private int val;
        private Node left;
        private Node right;
        public Node(int val) {
            this.val = val;
            this.left = null;
            this.right = null;
        }
        public void setVal(int val) {
            this.val = val;
        }
        public int getVal() {
            return this.val;
        }
        public Node getLeft() {
            return left;
        }
        public void setLeft(Node left) {
            this.left = left;
        }
        public Node getRight() {
            return right;
        }
        public void setRight(Node right) {
            this.right = right;
        }
    }
    // 根節點
    private Node root;
    private Node insert(Node root, int val) {
        if (root == null) {
            root = new Node(val);
            return root;
        }
        if (root.val < val) {
            root.right = insert(root.right, val);
        } else if(root.val > val) {
            root.left = insert(root.left, val);
        }
        return root;
    }
    private Node search(Node root, int val) {
        if (root == null) {
            return null;
        }
        if (root.val < val) {
            return search(root.right, val);
        } else if (root.val > val) {
            return search(root.left, val);
        }
        return root;
    }
    private Node remove(Node root, int val) {
        if (root == null) {
            return null;
        }
        if (root.val < val) {
            return remove(root.right, val);
        } else if (root.val > val) {
            return remove(root.left, val);
        }
        if (root.left == null && root.right == null) {
            root = null;
            return root;
        } else if (root.left == null) {
            Node temp = root;
            root = root.right;
            temp = null;
            return root;
        } else if (root.right == null) {
            Node temp = root;
            root = root.left;
            temp = null;
            return root;
        } else {
            Node min = findMin(root.right);
            root.val = min.val;
            root.right = remove(root.right, root.val);
        }
        return root;
    }
    private Node findMin(Node t) {
        while (t.left != null) {
            t = t.left;
        }
        return t;
    }
    private void preOrderTraversal(Node t) {
        if (t == null) {
            return;
        }
        System.out.println(t.val);
        preOrderTraversal(t.left);
        preOrderTraversal(t.right);
    }
    private void midOrderTraversal(Node t) {
        if (t == null) {
            return;
        }
        midOrderTraversal(t.left);
        System.out.println(t.val);
        midOrderTraversal(t.right);
    }
    private void postOrderTraversal(Node t) {
        if (t == null) {
            return;
        }
        postOrderTraversal(t.left);
        postOrderTraversal(t.right);
        System.out.println(t.val);
    }
    public BinarySearchTree() {
    }
    public void insert(int val) {
        root = insert(root, val);
    }
    public Node search(int val) {
        return search(root, val);
    }
    public void remove(int val) {
        root = remove(root, val);
    }
    public void preOrderTraversal() {
        postOrderTraversal(this.root);
    }
    public void midOrderTraversal() {
        midOrderTraversal(this.root);
    }
    public void postOrderTraversal() {
        postOrderTraversal(this.root);
    }
}