天天看點

LeetCode刷題之旅(中等 -4):100. 相同的樹

2019年9月15日

繼續彌補《資料結構與算法》的不足,今天抽中了“樹”。決定先把基礎題做一遍,打好基礎。

目錄

​​題目:​​

​​解決方法1:​​

​​解決思路:​​

​​性能結果:​​

​​解決方法2:​​

​​思路:深度優先周遊​​

​​解決方法3:​​

​​疊代​​

​​複雜度分析​​

​​遞歸​​

​​複雜度分析​​

題目:

LeetCode刷題之旅(中等 -4):100. 相同的樹

二叉樹基礎概念:

樹是一種抽象資料類型(ADT)或是實作這種抽象資料類型的資料結構,用來模拟具有樹狀結構性質的資料集合。它是由 n(n>0)n(n>0) 個有限節點組成一個具有層次關系的集合。

把它叫做「樹」是因為它看起來像一棵倒挂的樹,也就是說它是根朝上,而葉朝下的。

它具有以下的特點:

  • 每個節點都隻有有限個子節點或無子節點;
  • 沒有父節點的節點稱為根節點;
  • 每一個非根節點有且隻有一個父節點;
  • 除了根節點外,每個子節點可以分為多個不相交的子樹;
  • 樹裡面沒有環路。
LeetCode刷題之旅(中等 -4):100. 相同的樹

解決方法1:

package leetCode.tree;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * REMARK 相同的樹
 *
 * @author: mmb
 * @date: 19-9-15
 */
public class TwoSameTreeSolution {

    public static void main(String[] args){
        TreeNode p = getATree();
//      TreeNode q = getATree();
        TreeNode q = getATreeV2();
        System.out.println("result = " + isSameTree(p, q));

    }

    /**
     * 比較兩棵樹是否相同
     */
    public static boolean isSameTree(TreeNode p, TreeNode q){
        List<Integer> tree1AllNodes = getAllNodes(p);
        List<Integer> tree2AllNodes = getAllNodes(q);
        System.out.println("tree1AllNodes = " + tree1AllNodes);
        System.out.println("tree2AllNodes = " + tree2AllNodes);
        return compareTwoTree(tree1AllNodes, tree2AllNodes);
    }

    /**
     * 比較兩棵樹的結點清單
     */
    private static boolean compareTwoTree(List<Integer> tree1AllNodes, List<Integer> tree2AllNodes) {
        // 兩棵樹的節點要一樣
        if (tree1AllNodes.size() == tree2AllNodes.size()){
            int countTime = 0;
            int tree1NotNullCount = 0;
            int tree2NotNullCount = 0;
            Iterator<Integer> iterator1 = tree1AllNodes.iterator();
            Iterator<Integer> iterator2 = tree2AllNodes.iterator();
            // 逐個周遊兩棵樹的節點
            while (iterator1.hasNext()){
                Integer tree1Node = iterator1.next();
                Integer tree2Node = iterator2.next();

                // 兩棵樹的節點都不為空
                if (tree1Node != null && tree2Node != null){
                    if (tree1Node.equals(tree2Node)){
                        countTime++;
                        continue;
                    } else {
                        break;
                    }
                }
                // 兩棵樹的節點都為空
                else if (tree1Node == null && tree2Node == null) {
                    continue;
                }
                // 其中一棵樹節點為空
                else {
                    break;
                }
            }

            // tree1的非空節點
            for (Integer node : tree1AllNodes){
                if (node != null){
                    tree1NotNullCount++;
                }
            }
            // tree2的非空節點
            for (Integer node : tree2AllNodes){
                if (node != null){
                    tree2NotNullCount++;
                }
            }

            // tree1和tree2和節點比較相同次數一樣
            if (tree1NotNullCount == countTime && tree2NotNullCount == countTime){
                return true;
            }
        }
        return false;
    }

    /**
     * 擷取所有節點資料
     */
    private static List<Integer> getAllNodes(TreeNode node){
        // 前序周遊,遞歸擷取所有節點資訊
        ArrayList<Integer> treeNodes = new ArrayList<>();
        if(node != null){
            treeNodes.add(node.val);
            if (node.left != null){
                List<Integer> allLeftNodes = getAllNodes(node.left);
                treeNodes.addAll(allLeftNodes);
            } else if (node.right != null){
                treeNodes.add(null);
            }

            if (node.right != null){
                List<Integer> allRightNodes = getAllNodes(node.right);
                treeNodes.addAll(allRightNodes);
            }

        }
        return treeNodes;
    }

    /**
     * 建立一棵樹
     */
    public static TreeNode getATree(){
        TreeNode headNode = new TreeNode(1);
        TreeNode leftNode = new TreeNode(2);
        TreeNode rightNode = new TreeNode(3);
        headNode.setLeft(leftNode);
        headNode.setRight(rightNode);
        return headNode;
    }
    public static TreeNode getATreeV2(){
        TreeNode headNode = new TreeNode(1);
        TreeNode leftNode = new TreeNode(2);
        TreeNode rightNode = new TreeNode(3);
        headNode.setLeft(leftNode);
        headNode.setRight(rightNode);
        return headNode;
    }

}      

解決思路:

  • 對于給定的兩棵樹,我是首先想到的是遞歸周遊(這裡使用前序周遊:根節點-左子樹-右子樹),也就是說将樹的所有節點全部拿出到list裡面;第二步,就是逐個對比list的節點是否相同;
  • 這種方法比較笨,我這種初學者往往會往死胡同裡面走;這時候算法展現出優越性了。。。

性能結果:

LeetCode刷題之旅(中等 -4):100. 相同的樹

解決方法2:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null) 
            return true;
        if(p == null || q == null) 
            return false;
        if(p.val != q.val) 
            return false;
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}

作者:guanpengchn
      

思路:深度優先周遊

終止條件與傳回值:

  • 當兩棵樹的目前節點都為 null 時傳回 true
  • 當其中一個為 null 另一個不為 null 時傳回 false
  • 當兩個都不為空但是值不相等時,傳回 false

執行過程:當滿足終止條件時進行傳回,不滿足時分别判斷左子樹和右子樹是否相同,其中要注意代碼中的短路效應

時間複雜度:O(n),n 為樹的節點個數

解決方法3:

疊代

  • 最簡單的政策是使用遞歸。檢查p和q節點是否不是空,它們的值是否相等。如果所有檢查都正常,則遞歸地為子節點執行相同操作。
class Solution {
  public boolean isSameTree(TreeNode p, TreeNode q) {
    // p 和 q 均為 null時
    if (p == null && q == null) return true;
    // p 或 q 有一個為null時
    if (q == null || p == null) return false;
    if (p.val != q.val) return false;
    return isSameTree(p.right, q.right) &&
            isSameTree(p.left, q.left);
  }
}

作者:gaohanghang
      

複雜度分析

時間複雜度:O(N),其中N是樹中的節點數,因為每個節點隻通路一次。

空間複雜度:O(log(N)):

在最佳情況下即completely balanced tree空間複雜度是O(log(N)),

在最壞情況下 completely unbalanced tree空間複雜度是O(N),和遞歸次數保持一緻。

遞歸

  • 思路:從根開始,在每次疊代時将目前節點彈出deque。
  • 然後執行與方法1中相同的檢查:p 和 q 不為空,p.val 和 q.val 相等,如果檢查正常,則push子節點。
class Solution {
  public boolean check(TreeNode p, TreeNode q) {
    // p and q are null
    if (p == null && q == null) return true;
    // one of p and q is null
    if (q == null || p == null) return false;
    if (p.val != q.val) return false;
    return true;
  }

  public boolean isSameTree(TreeNode p, TreeNode q) {
    if (p == null && q == null) return true;
    if (!check(p, q)) return false;

    // init deques
    ArrayDeque<TreeNode> deqP = new ArrayDeque<TreeNode>();
    ArrayDeque<TreeNode> deqQ = new ArrayDeque<TreeNode>();
    deqP.addLast(p);
    deqQ.addLast(q);

    while (!deqP.isEmpty()) {
      p = deqP.removeFirst();
      q = deqQ.removeFirst();

      if (!check(p, q)) return false;
      if (p != null) {
        // in Java nulls are not allowed in Deque
        if (!check(p.left, q.left)) return false;
        if (p.left != null) {
          deqP.addLast(p.left);
          deqQ.addLast(q.left);
        }
        if (!check(p.right, q.right)) return false;
        if (p.right != null) {
          deqP.addLast(p.right);
          deqQ.addLast(q.right);
        }
      }
    }
    return true;
  }
}

作者:gaohanghang
      

複雜度分析

繼續閱讀