數據結構專題(四)—— 樹

樹在計算機中佔着舉足輕重的地位,目前計算機中的文件系統,程序開發中的XML與HTML等都是樹這一數據結構的運用。甚至包括英雄聯盟的匹配系統也採用了樹,這章內容重點討論樹結構,並使用Java對一些常見的面試問題做個總結。由於關於樹的問題衆多,所以我準備分兩部分進行整理。

什麼是樹?樹的深度,高度,根節點,父節點,子節點等概念一張圖即可說明清楚


關於樹的編程問題有很多變形,但經過研究發現大部分解決思路的核心是遞歸思想

1. 樹的創建初始化

2. 求二叉樹的最大深度

3. 求二叉樹的最小深度

4. 求二叉樹中節點的個數

5. 求二叉樹中葉子節點的個數

6. 求二叉樹中第K層節點個數

7. 判斷二叉樹是否爲平衡二叉樹

8. 判斷二叉樹是否爲完全二叉樹

9. 判斷一個二叉樹是否爲鏡像

10. 求一個二叉樹的鏡像

11. 求兩個二叉樹最低公共祖先節點

12. 前序中序後序遍歷二叉樹

此處使用Java語言完成對上列各種問題的編程實現。

1.樹的創建初始化

//1. 樹的初始化

class TreeNode{

    int value;
    TreeNode left;
    TreeNode right;

    public TreeNode(int value){
        this.value = value;
    }
}

2. 求二叉樹的最大深度   

public int searchMaxDepth(TreeNode node){

    if(node == null){
        return 0;
    }
    
    int leftDepth = searchMaxDepth(node.left);
    int rightDepth = searchMaxDepth(node.right);
    
    return (leftDepth >= rightDepth ? leftDepth : rightDepth) + 1;
}

3. 求二叉樹的最小深度

public int searchMinDepth(TreeNode node){

        if(node == null){
            return 0;
        }

        if(node != null && node.left == null && node.right == null){
            return 1;
        }

        //若左節點爲空那麼就返回右邊節點的深度
        if(node.left == null){
            return searchMinDepth(node.right) + 1;
        }

        if(node.right == null){
            return searchMinDepth(node.left) + 1;
        }

        //若左右兩邊都存在節點則進入下一層進行比較
        int leftDepth = searchMinDepth(node.left);
        int rightDepth = searchMinDepth(node.right);

        return (leftDepth <= rightDepth ? leftDepth : rightDepth) + 1;

}

4.求二叉樹中節點的個數    

//思路:使用一種遍歷方式,創建一個計數器去統計
    public int nodeNum(TreeNode node){

        int count = 0;
        if(node == null){
            return 0;
        }

        //此處採用BFS的方式去遍歷,並且把節點存儲到一個隊列中
        Queue<TreeNode> list = new LinkedList<TreeNode>();
        list.add(node);

        while(!list.isEmpty()){

            if(node.left != null){
                list.add(node.left);
            }

            if(node.right != null){
                list.add(node.right);
            }

            list.poll();
            count++;
            node = list.peek();
        }

        return count;
    }

5. 求二叉樹中葉子節點的個數

 //思路:採用BFS遍歷樹,對每個節點的狀態進行判斷
    
    public int leafNum(TreeNode node){

        int count = 0;

        if(node == null){
            return 0;
        }

        if(node != null && node.left == null && node.right == null){
            return 1;
        }

        //遍歷的同時對每個節點的左右進行判斷
        Queue<TreeNode> list = new LinkedList<TreeNode>();
        list.add(node);

        while(!list.isEmpty()){

            if(node.left != null){
                list.add(node.left);
            }

            if(node.right != null){
                list.add(node.right);
            }

            if(list.peek().left == null && list.peek().right == null){
                count++;
            }

            list.poll();
            node = list.peek();
        }
        return count;
    }
    

6.求二叉樹第K層節點的個數

 //思路:遞歸 K其實可以理解爲遞歸K次,node才得以從root更新到第K層的節點

    public int KlevelNodeNum(TreeNode node, int k){

        //若該節點不存在或者K不符合條件則返回0
        if(node == null || k <= 0){
            return 0;
        }

        if(node != null && k == 1){
            return 1;
        }

        //要想得到第K層的節點數目就必須先知道第K-1層的節點是什麼狀態(即是否含有左右節點)
        return KlevelNodeNum(node.left, k - 1) + KlevelNodeNum(node.right, k - 1);

    }

7.判斷二叉樹是否爲平衡二叉樹(AVL樹)

    

//首先什麼條件下的二叉樹纔是平衡二叉樹?二叉樹中每個節點的左右子樹深度相差不超過1
//可以利用遞歸求每個節點的深度,將求得的深度進行比較即可

    public boolean isBalanced(TreeNode node){

        if(node == null){
            return true;
        }

        int leftDepth = searchDepth(node.left);
        int rightDepth = searchDepth(node.right);

        if(Math.abs(leftDepth - rightDepth) > 1){
            return false;
        }

        boolean leftBalanced = isBalanced(node.left);
        boolean rightBalanced = isBalanced(node.right);

        return leftBalanced && rightBalanced;

    }


    public int searchDepth(TreeNode node){

        if(node == null){
            return 0;
        }

        if(node != null && node.left == null && node.right == null){
            return 1;
        }

        int leftDepth = searchDepth(node.left);
        int rightDepth = searchDepth(node.right);

        return (leftDepth >= rightDepth ? leftDepth : rightDepth) + 1;
    }

8.判斷二叉樹是否爲完全二叉樹(CBT Complete binary tree)

//什麼是完全二叉樹?若設二叉樹的深度爲h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大數,
//第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。

    public boolean isCBT(TreeNode node){

        //通過BFS逐層遍歷樹,使用隊列進行存儲,對隊列中的每個節點進行判斷

        //空樹也可以屬於完全二叉樹
        if(node == null){
            return true;
        }

        Queue<TreeNode> list = new LinkedList<TreeNode>();
        list.add(node);

        while(!list.isEmpty()){
            if(node.left != null){
                list.add(node.left);
            }

            if(node.right != null){
                list.add(node.right);
            }

            //若左節點爲空,右節點不爲空,直接判定不是完全二叉樹
            if(node.left == null && node.right != null){
                return false;
            }

            //如果有左節點但沒有右節點,那麼該節點的所有子節點必須爲葉子節點,即該節點的子節點左右都爲空
            if(node.left != null && node.right == null){
                if(node.left.left != null || node.left.right != null){
                    return false;
                }
            }

            list.poll();
            node = list.peek();
        }
        return true;
    }

9. 判斷一棵二叉樹是否爲鏡像

    //思路:判斷方法其實是判斷二叉樹的子樹是否爲鏡像,可以使用遞歸的方式進行求解

    /*
    算法思想是:首先判斷這棵樹是否爲空樹,如果空樹則直接返回true

    如果不爲空,則在進行分類:
    case1:節點的左右子樹爲空,則直接返回true
    case2:節點的左右子樹有一個爲空,則直接返回false
    case3:節點的左右子樹均不爲空,則判斷節點的左右子節點的值是否相等
           並且判斷左節點的子左節點和右節點的右子節點是否對稱
           還有左節點的右子節點和右節點的左子節點是否對稱
    */

    public boolean isSymmetric(TreeNode node){

        if(node == null){
            return true;
        }

        if(node.left == null || node.right == null){
            return false;
        }

        //判斷兩棵子樹是否對稱
        return symmetric(node.left, node.right);
    }

    //比較兩棵樹是否是鏡像的
    public boolean symmetric(TreeNode left, TreeNode right){

        //case 1
        if(left == null && right == null){
            return true;
        }

        //case 2
        if(left == null || right == null){
            return false;
        }

        //case 3
        return left.value == right.value && symmetric(left.left, right.right) && symmetric(left.right, right.left);
    }

10.給出一個二叉樹,求解其鏡像。

    //思路:遞歸交換子樹的節點
    public void mirror(TreeNode node){

        if(node == null){
            return;
        }

        //進行交換節點操作
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;

        mirror(node.left);
        mirror(node.right);
    }

11. 求二叉搜索樹最低公共祖先節點 lowest common ancestor(LCA)

  //如果是二叉搜索樹,遞歸判斷

    //case 1 如果兩個節點分別在根節點的左右,那麼該根節點就是所求節點

    //case 2 兩個節點在根節點的同側,進行遞歸搜索同側的樹

    public TreeNode findLCA(TreeNode node, TreeNode p, TreeNode q){

        if(node == null){
            return null;
        }

        if(node.value == p.value || node.value == q.value){
            return node;
        }

        //當節點分別在node左右兩側,那麼根節點就是低公共祖先節點
        if(p.value < node.value && q.value > node.value){
            return node;
        }

        TreeNode treenode = null;
        //若兩節點均小於node,說明兩個節點都是位於左子樹,同理兩節點都大於node,那麼就說明兩節點都位於右子樹
        if(p.value < node.value && q.value < node.value){
            treenode = findLCA(node.left, p, q);
        }

        if(p.value >= node.value){
            treenode = findLCA(node.right, p, q);
        }

        return treenode;
    }

12. 前序中序後序遍歷二叉樹 pre-order in-order post-order

    //pre-order: root left right
    public void preorder(TreeNode node){

        if(node == null){
            return;
        }

        if(node != null){
            System.out.println(node.value);
            preorder(node.left);
            preorder(node.right);
        }
    }


    //inorder: left root right
    public void inorder(TreeNode node){
        
        if(node == null){
            return;
        }

        if(node != null){
            inorder(node.left);
            System.out.println(node.value);
            inorder(node.right);
        }
    }

    //post-order: left right root
    public void postorder(TreeNode node){

        if(node == null){
            return;
        }

        if(node != null){
            postorder(node.left);
            postorder(node.right);
            System.out.println(node.value);
        }
    }

 

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