數據結構 二叉樹及遍歷二叉樹

概述

二叉樹,是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。

這裏寫圖片描述

二叉樹的特性

  • 在非空二叉樹中,第i層的結點總數不超過 , i>=1;
  • 深度爲h的二叉樹最多有 個結點(h>=1),最少有h個結點;
  • 對於任意一棵二叉樹,如果其葉結點數爲N0,而度數爲2的結點總數爲N2,則N0=N2+1;
  • 具有n個結點的完全二叉樹的深度爲
  • 有N個結點的完全二叉樹各結點如果用順序方式存儲,則結點之間有如下關係:
    若I爲結點編號則 如果I>1,則其父結點的編號爲I/2;
    如果2*I<=N,則其左兒子(即左子樹的根結點)的編號爲2*I;若2*I>N,則無左兒子;
    如果2*I+1<=N,則其右兒子的結點編號爲2*I+1;若2*I+1>N,則無右兒子。
  • 給定N個節點,能構成h(N)種不同的二叉樹。h(N)爲卡特蘭數的第N項。h(n)=C(2*n,n)/(n+1)。
  • 設有i個枝點,I爲所有枝點的道路長度總和,J爲葉的道路長度總和J=I+2i[4]

二叉樹的分類

斜樹

所有的結點都只有左子樹的二叉樹叫左斜樹。所有結點都是隻有右子樹的二叉樹叫右斜樹。這兩者統稱爲斜樹。所以,線性表的數據結構可以理解成樹的一種表達方式。

這裏寫圖片描述

滿二叉樹

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

這裏寫圖片描述

完全二叉樹

對一顆具有n個節點的二叉樹按層序編號,如果編號爲i(1<= i <=n)的節點與同樣深度的滿二叉樹中編號爲i的節點的位置完全相同,那麼這樣的樹,稱之爲完全二叉樹。

這裏寫圖片描述

二叉樹的存儲結構

這裏寫圖片描述

順序存儲結構:
這裏寫圖片描述

二叉鏈表:
這裏寫圖片描述

二叉樹的遍歷

二叉樹的數據結構中,常用的遍歷方式有三種:

  • 先序遍歷
  • 中序遍歷
  • 後序遍歷

當然,二叉樹還有一種不常見的遍歷方式-層序遍歷,這裏暫不做介紹。

在樹的遍歷中,我們通常使用的是通過遞歸方式來進行遍歷。

先序遍歷

規則是若二叉樹爲空,則空操作返回,否則先訪問跟結點,然後先序遍歷左子樹,再先序遍歷右子樹。

這裏寫圖片描述

代碼實現如下:

    /**
     * 先序遍歷
     * 
     * parent->left->right
     */
    public void preOrder(TreeNode node) {
        if (node == null) {
            return;
        }

        System.out.print(node.data + "\t");
        preOrder(node.leftChild);
        preOrder(node.rightChild);
    }

中序遍歷

規則是若樹爲空,則空操作返回,否則從根結點開始(注意並不是先訪問根結點),中序遍歷根結點的左子樹,然後是訪問根結點,最後中序遍歷右子樹。

這裏寫圖片描述

代碼實現如下:

    /**
     * 中序遍歷
     * 
     * left->parent->right
     */
    public void midOrder(TreeNode node) {
        if (node == null) {
            return;
        }

        midOrder(node.leftChild);
        System.out.print(node.data + "\t");
        midOrder(node.rightChild);
    }

後序遍歷

規則是若樹爲空,則空操作返回,否則從左到右先葉子後結點的方式遍歷訪問左右子樹,最後是訪問根結點。

這裏寫圖片描述

代碼實現如下:

    /**
     * 後序遍歷
     * 
     * left->right->parent
     */
    public void postOrder(TreeNode node) {
        if (node == null) {
            return;
        }

        postOrder(node.leftChild);
        postOrder(node.rightChild);
        System.out.print(node.data + "\t");
    }

Java實現二叉樹

package structdemo;

/**
 * 
 * 二叉樹
 * 
 * @author zhangke
 */
public class BinaryTree {

    // 創建二叉樹並調用遍歷方法
    public static void main(String[] args) {

        BinaryTree binaryTree = new BinaryTree();
        binaryTree.createBinaryTree();
        System.out.println("height:" + binaryTree.getHeight());
        System.out.println("size:" + binaryTree.getSize());

        binaryTree.preOrder(binaryTree.root);
        binaryTree.midOrder(binaryTree.root);
        binaryTree.postOrder(binaryTree.root);

    }

    /**
     * 根節點
     */
    private TreeNode root = null;

    public BinaryTree() {
        root = new TreeNode(1, "A");
    }

    /**
     * 構建二叉樹 
     * 
     *       A 
     *       
     *   B      C 
     * 
     * D    E       F
     */
    public void createBinaryTree() {
        TreeNode nodeB = new TreeNode(2, "B");
        TreeNode nodeC = new TreeNode(3, "C");
        TreeNode nodeD = new TreeNode(4, "D");
        TreeNode nodeE = new TreeNode(5, "E");
        TreeNode nodeF = new TreeNode(6, "F");
        root.leftChild = nodeB;
        root.rightChild = nodeC;
        nodeB.leftChild = nodeD;
        nodeB.rightChild = nodeE;
        nodeC.rightChild = nodeF;
    }

    /**
     * 獲取樹的深度
     * 
     * @return
     */
    public int getHeight() {
        return getHeight(root);
    }

    /**
     * 獲取指定結點的深度
     */
    private int getHeight(TreeNode node) {
        if (node == null) {
            return 0;
        }

        // 每當一個結點存在子結點時,Height就加一個
        int i = getHeight(node.leftChild);
        int j = getHeight(node.rightChild);
        return (i > j) ? (i + 1) : (j + 1);
    }

    /**
     * 獲取結點個數
     * 
     * @return
     */
    public int getSize() {
        return getSize(root);
    }

    private int getSize(TreeNode node) {
        if (node == null) {
            return 0;
        }
        return 1 + getSize(node.leftChild) + getSize(node.rightChild);
    }

    /**
     * 先序遍歷
     * 
     * parent->left->right
     */
    public void preOrder(TreeNode node) {
        if (node == null) {
            return;
        }

        System.out.print(node.data + "\t");
        preOrder(node.leftChild);
        preOrder(node.rightChild);
    }

    /**
     * 中序遍歷
     * 
     * left->parent->right
     */
    public void midOrder(TreeNode node) {
        if (node == null) {
            return;
        }

        midOrder(node.leftChild);
        System.out.print(node.data + "\t");
        midOrder(node.rightChild);
    }

    /**
     * 後序遍歷
     * 
     * left->right->parent
     */
    public void postOrder(TreeNode node) {
        if (node == null) {
            return;
        }

        postOrder(node.leftChild);
        postOrder(node.rightChild);
        System.out.print(node.data + "\t");
    }

    /**
     * 定義樹的結點
     *
     * @author zhangke
     */
    public class TreeNode {

        /**
         * 下標
         */
        private int index;
        /**
         * 數據
         */
        private String data;
        /**
         * 左孩子
         */
        private TreeNode leftChild;
        /**
         * 右孩子
         */
        private TreeNode rightChild;

        public TreeNode(int index, String data) {
            this.index = index;
            this.data = data;
        }
    }

}

常見面試題

已知,某樹的先序遍歷爲:{ 4, 2, 1 ,0, 3, 5, 9, 7, 6, 8 },中序遍歷爲: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },請畫出該樹。

在解題之前我們可以明確一個概念:二叉樹中序遍歷加任意一種其他遍歷方式都可以確定一棵樹。
解釋:因爲不論是先序遍歷、後序遍歷或者是層序遍歷,都只能確定一顆樹的根節點,在確定根節點後通過中序遍歷就能區分樹的左右子樹,這樣通過遞歸的思想就能確定一棵樹了。

解題步驟如下:
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章