leetcode | 二叉樹

144. 二叉樹的前序遍歷

  1. 遞歸

JAVA代碼java

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        return help(root, res);
    }
    public List<Integer> help (TreeNode root, List<Integer> list){
        if (root != null) {
            list.add(root.val);
            help(root.left, list);
            help(root.right, list);
        }
        return list;
        
    }    
}
  1. 迭代

借用棧
在這裏插入圖片描述
因爲二叉樹的前序遍歷有往回走的過程,所以考慮用棧結構。
同時先訪問左孩子後訪問右孩子,所以入棧順序爲先右孩子入棧後左孩子入棧。
JAVA代碼node

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        if (root==null) return new ArrayList<Integer>();
        
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);
        
        //訪問棧節點並彈出,先右孩子入棧,後左孩子入棧
        while(!stack.isEmpty()){
            TreeNode top = stack.pop();
            res.add(top.val);
            
            if (top.right!=null){
                stack.push(top.right);
            }
            if (top.left!=null){
                stack.push(top.left);
            }
        }
        return res;

    }
}

94. 二叉樹的中序遍歷

  1. 遞歸

JAVA代碼算法

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        res = help(root, res);
        return res;

    }
    
    public List<Integer> help(TreeNode root, List<Integer> list){
        if (root!=null){
            help(root.left, list);
            list.add(root.val);
            help(root.right, list);
            return list;
        }
        return list;
    }
}
  1. 迭代

仍是用棧
在這裏插入圖片描述數組

  • 訪問左側鏈的最後一個節點 (左側鏈節點入棧)
  • 轉向該節點的右子樹
  • 訪問該子樹上左側鏈的最後一個節點 (右子樹左側鏈節點入棧)

JAVA代碼bash

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) return res;
        
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
       
        while(!stack.isEmpty()){
            //左側鏈入棧:左孩子不爲空則入棧
            if (root.left != null){
                root = root.left;
                stack.push(root);
            }
            else{ //不然訪問左側鏈最後一個節點,即棧頂元素
                TreeNode top = stack.pop();
                res.add(top.val);
                if (top.right!=null){
                    root = top.right;
                    stack.push(root);
                }
            }
        }
        return res;
    }
}

145. 二叉樹的後序遍歷

  1. 遞歸

JAVA代碼網絡

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        res = help(root, res);
        return res;
    }
    
    public List<Integer> help(TreeNode root, List<Integer> list){
        if (root!=null){
            help(root.left,list);
            help(root.right, list);
            list.add(root.val);
        }
        return list;
    }
}
  1. 迭代

在這裏插入圖片描述
因爲前序 (中左右) 和中序 (左中右) 遍歷要求每一個節點要訪問兩遍,所以能夠用棧實現。
後序遍歷要求每一個節點訪問三次,所以須要借用兩個棧。
根右左 的逆序是 左右根, 即爲後序遍歷。
而前序遍歷爲 根左右,只要前序遍歷的過程改成先訪問右孩子再訪問左孩子,即爲根右左.
所以能夠考慮按照 根右左 的順序訪問節點,將訪問的節點裝入另外一個棧中再彈出,順序即爲左右根.
注意的地方:因爲須要獲得 根右左 的順序,所以入棧順序爲先入左孩子後入右孩子。數據結構

JAVA代碼函數

//迭代
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root==null) return res;
        
        Stack<TreeNode> stack1 = new Stack<>();  //根右左: 所以入棧順序爲先左孩子入棧,後右孩子入棧
        Stack<TreeNode> stack2 = new Stack<>();
        
        stack1.add(root);
        while(!stack1.isEmpty()){
            root = stack1.pop();
            stack2.add(root);
            if(root.left!=null){   //左孩子入棧
                stack1.add(root.left);
            }
            if(root.right!=null){  //右孩子入棧
                stack1.add(root.right);
            }
        }
        while(!stack2.isEmpty()){
            res.add(stack2.pop().val);
        }
        return res;
    }
}

層次遍歷

利用隊列:先進先出
若訪問當前節點,若左孩子存在,左孩子入棧;若右孩子存在,右孩子入棧.
在這裏插入圖片描述
JAVA代碼post

public class BTree_LevelOrder {
    public List<Integer> levelOrder(TreeNode root){
        List<Integer> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            root = queue.poll();
            res.add(root.val);
            if (root.left!=null) queue.offer(root.left);   //若左孩子不爲空,左孩子入隊
            if (root.right!=null) queue.offer(root.right); //若右孩子不爲空,右孩子入隊
        }
        return res;
    }
  }

時間複雜度:O(n)
空間複雜度:O(n)this

102. 二叉樹的層次遍歷

給定一個二叉樹,返回其按層次遍歷的節點值。 (即逐層地,從左到右訪問全部節點)。
例如:
給定二叉樹: [3,9,20,null,null,15,7],

3
   / \
  9  20
    /  \
   15   7

返回其層次遍歷結果:

[
  [3],
  [9,20],
  [15,7]
]

題解:

  1. 思路一

遞歸:先序遍歷
借用一個help遞歸函數,傳的值爲:當前節點,當前節點的層數

  • 根節點爲空,返回空的 List
  • 第0層只包含一個根節點 root.
  • 若當前節點的層數大於 list 的個數,則增長一個空的 list
  • 當前節點位於第幾層就將該節點放進第幾個list中.
  • 遞歸當前節點的左孩子和右孩子,此時層數增 1

JAVA代碼

class Solution {
    List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> levelOrder(TreeNode root) {
            if (root==null) return res;
            help(root, 0);
            return res;
    }
    
    public void help(TreeNode root, int level){
        if (res.size()-1<level){         //若最高層數小於當前層數,則res中再增長一個列表來裝這一層的節點值
            res.add(new ArrayList<Integer>());  
        }
        res.get(level).add(root.val);  //對應的list裝對應層的節點值
        
        if (root.left!=null){
            help(root.left, level+1);
        }
        if (root.right!=null){
            help(root.right, level+1);
        }
    }
}

時間複雜度:O(n)
空間複雜度: O(n) 輸出的數組包含n個值

  1. 思路二

BFS迭代,借用隊列

  • 根節點爲空時,返回空數組
  • 根節點不空時,初始節點爲root,入隊,層數爲0

    • 每到新的一層增長一個新的空數組
    • 當前層的節點即爲當前隊列中的全部節點,將隊列中的節點逐一彈出並放入數組中,並將當前節點的左孩子右孩子放入隊列中,level+1
class Solution {
        public List<List<Integer>> levelOrder(TreeNode root) {
            List<List<Integer>> res = new ArrayList<>();
            if (root==null) return res;
            
            Queue<TreeNode> queue = new LinkedList<>();
            queue.offer(root);
            int level = 0;  //初始時root爲第0層
            while(!queue.isEmpty()){
                res.add(new ArrayList<Integer>());
                
                //當前層節點即爲隊列中的節點
                int len = queue.size();
                //當前層節點加入數組
                for (int i=0; i<len; i++){
                    root = queue.poll();
                    res.get(level).add(root.val);
                    if (root.left!=null) queue.offer(root.left);
                    if (root.right!=null) queue.offer(root.right);
                }
                level++;
            }
            return res;
    }
}

104. 二叉樹的最大深度

給定一個二叉樹,找出其最大深度。
二叉樹的深度爲根節點到最遠葉子節點的最長路徑上的節點數。

說明: 葉子節點是指沒有子節點的節點。

示例:
給定二叉樹 [3,9,20,null,null,15,7],

3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。
題解:

  1. 思路一

遞歸
根節點的深度等於左右子樹中最深子樹的高度 +1
JAVA代碼

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right))+1;
    }
}

時間複雜度:O(n)
空間複雜度:O(n)

  1. 思路二

BFS迭代:層次遍歷,借用隊列
隊列的每一個元素記錄了:key:當前節點,value: 當前節點距離根節點的距離

  • 根節點爲空時,高度爲1
  • 根不爲空時,根節點的value爲1

每次,當前節點的左右孩子入隊時,距離增1
在這裏插入圖片描述
JAVA代碼

class Solution {
    public int maxDepth(TreeNode root) {
        Queue<Pair<TreeNode, Integer>> queue = new LinkedList<>(); //隊列形式{"節點","距離根節點的距離"}
        if (root==null) return 0;
        queue.offer(new Pair(root,1));
        int max = 1;
        while(!queue.isEmpty()){
            Pair<TreeNode,Integer> current = queue.poll();//當前節點
            root = current.getKey();
            int depth = current.getValue(); //當前節點距離根節點的距離
            max = Math.max(max, depth);

            if (root.left!=null)
                queue.offer(new Pair(root.left,depth+1));
            if (root.right!=null)
                queue.offer(new Pair(root.right, depth+1));
        }
        return max;
    }
}

時間複雜度:O(n)
空間複雜度:O(n)

  1. 思路三

DFS 迭代:先序遍歷,利用棧
棧的每一個元素記錄了:key:當前節點,value: 當前節點距離根節點的距離

  • 根節點爲空時,高度爲1
  • 根不爲空時,根節點的value爲1

每次,當前節點的右和左孩子入棧時,距離增1
在這裏插入圖片描述
JAVA代碼

//DFS: 先序遍歷
class Solution {
    public int maxDepth(TreeNode root) {
        Stack<Pair<TreeNode, Integer>> stack = new Stack<>(); //隊列形式{"節點","距離根節點的距離"}
        if (root==null) return 0;
        stack.push(new Pair(root,1));
        int max = 1;
        while(!stack.isEmpty()){
            Pair<TreeNode,Integer> current = stack.pop();//當前節點
            root = current.getKey();
            int depth = current.getValue(); //當前節點距離根節點的距離
            max = Math.max(max, depth);

            if (root.right!=null)
                stack.push(new Pair(root.right,depth+1));
            if (root.left!=null)
                stack.push(new Pair(root.left, depth+1));
        }
        return max;
    }
}

時間複雜度:O(n)
空間複雜度:徹底不平衡時最壞O(n) ,徹底平衡時最好O(logn)


111. 二叉樹的最小深度

給定一個二叉樹,找出其最小深度。

最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。

說明: 葉子節點是指沒有子節點的節點。

示例:

給定二叉樹 [3,9,20,null,null,15,7],

3
   / \
  9  20
    /  \
   15   7

返回它的最小深度 2.
題解:

  1. 遞歸

分三種狀況:

  • 當前節點只有左子樹,返回左子樹的高度+1
  • 當前節點只有右子樹,返回右子樹的高度+1
  • 當前節點又有左子樹又有右子樹,返回矮的那棵樹的高度+1
class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) return 0;
        //若是隻有右子樹,返回右子樹的高度+1
        if (root.left==null && root.right!=null) return minDepth(root.right)+1;
        //若是隻有左子樹,返回左子樹的高度+1
        if (root.right==null && root.left!=null) return minDepth(root.left)+1;
        //既有左子樹又有右子樹,返回左子樹和右子樹中較矮的高度+1
        return Math.min(minDepth(root.left),minDepth(root.right))+1;
    }
}

時間複雜度:O(n)
空間複雜度:最好O(logn),最壞O(logn)

  1. DFS先序迭代,相似104
  2. BFS隊列,相似104

285 二叉搜索樹中的中序後繼

  【題目】 如今有一種新的二叉樹節點類型以下:

  public class Node {
  public int value;
  public Node left;
  public Node right;
  public Node parent;
  public Node(int data) { this.value = data; }
  }

該結構比普通二叉樹節點結構多了一個指向父節點的parent指針。

  假設有一 棵Node類型的節點組成的二叉樹,
  樹中每一個節點的parent指針都正確地指向本身的父節點,
  頭節點的parent指向null。只給一個在二叉樹中的某個節點 node,
  請實現返回node的後繼節點的函數。在二叉樹的中序遍歷的序列中,node的下一個節點叫做node的後繼節。

解答:
在這裏插入圖片描述

  1. 後繼節點:按照中序遍歷的順序 左根右
  2. 若當前節點有右孩子,那麼它的後繼節點爲右子樹左側鏈的最後一個節點
  3. 若當前節點沒有右孩子,那麼一直找父節點,節點在父節點的左子樹中,則該父節點爲其後繼節點。

注意的地方:查看某一結點是不是某父節點左子樹的中的節點,只需設一個臨時指針p,初始時p爲當前節點,而後查看p的父節點的左孩子是否爲p, 如果,則該父節點爲後繼,不然p指向該父節點,繼續找下一個父節點,知道找到根節點。

前驅節點

  1. 若該節點有左孩子,則前驅爲左子樹最右的節點
  2. 若該節點沒有左孩子,那麼一直往上找一個父節點,節點在父節點的右子樹中,該父節點爲其前驅節點。

297. 二叉樹的序列化與反序列化

序列化是將一個數據結構或者對象轉換爲連續的比特位的操做,進而能夠將轉換後的數據存儲在一個文件或者內存中,同時也能夠經過網絡傳輸到另外一個計算機環境,採起相反方式重構獲得原數據。

請設計一個算法來實現二叉樹的序列化與反序列化。這裏不限定你的序列 / 反序列化算法執行邏輯,你只須要保證一個二叉樹能夠被序列化爲一個字符串而且將這個字符串反序列化爲原始的樹結構。

示例:

你能夠將如下二叉樹:

    1
   / \
  2   3
     / \
    4   5

序列化爲 "[1,2,3,null,null,4,5]"

提示: 這與 LeetCode 目前使用的方式一致,詳情請參閱 LeetCode 序列化二叉樹的格式。你並不是必須採起這種方式,你也能夠採用其餘的方法解決這個問題。

說明: 不要使用類的成員 / 全局 / 靜態變量來存儲狀態,你的序列化和反序列化算法應該是無狀態的。

解答:

  1. 思路一:

先序遍歷方式,遇到空時,將空字符也加入序列。

public class BinaryTree_Serialize_Deserialize {
        /******************二叉樹 => 字符串*********************/
        public static String serialize(TreeNode root){

            if (root==null) return "null,";
            String str = Integer.toString(root.val)+",";
            str += serialize(root.left);
            str += serialize(root.right);
            return str;

        }
        /*******************字符串 => 二叉樹*******************/
        public TreeNode deserialize(String data){
            String[] data_array = data.split(",");  //按照"," 切分
            Queue<String> queue = new LinkedList<>();
            //將元素裝入隊列
            for (int i=0; i<data_array.length; i++){
                queue.offer(data_array[i]);
            }
            return deserialize_preOrder(queue);
        }

        //中序方式將字符串轉化爲二叉樹
        public TreeNode deserialize_preOrder(Queue<String> queue){
            String val = queue.poll();
            if (val.equals("null")) return null;
            TreeNode root = new TreeNode(Integer.valueOf(val));
            root.left = deserialize_preOrder(queue);
            root.right = deserialize_preOrder(queue);
            return root;
        }
      }
  1. 思路二:

中序遍歷方式、後序遍歷

3.思路三:
層序遍歷

110. 平衡二叉樹

樹形動態規劃
給定一個二叉樹,判斷它是不是高度平衡的二叉樹。
本題中,一棵高度平衡二叉樹定義爲:
一個二叉樹每一個節點的左右兩個子樹的高度差的絕對值不超過1。
示例 1:

給定二叉樹 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7
返回 true 。

解答:

  1. 思路一

自底向下
(1) 判斷左子數是否平衡,若不是直接返回 false
(2)判斷右子樹是否平衡,若不是直接返回 false
(3)左子樹的高度
(4)右子樹的高度
若左平衡,右平衡, 且左子樹的高度與右子樹的高度差1, 則該子樹平衡.

class Solution {
    public boolean isBalanced(TreeNode root) {
        return recur(root) != -1;
    }
    
    public int recur(TreeNode root){
        if (root == null) return 0;  //根節點若爲空,則返回高度爲0
        int left = recur(root.left);
        if (left == -1) return -1; //若左子樹不平衡,返回-1
        int right = recur(root.right);
        if (right == -1) return -1; //若右子樹不平衡,返回-1
        
        //若左右平衡且高度相差小於2,則整顆樹平衡,高度爲較高子樹高度+1,
        //若左右平衡且高度相差小於2,則整棵樹不平衡,返回-1
        return Math.abs(left-right)<2? Math.max(left, right)+1: -1;
    }
}

時間複雜度 O(n)
空間複雜度 O(n)

  1. 思路二

自頂向下
每個節點左子樹高度與右子樹高度差都小於2,則整棵樹爲平衡樹。
須要求每一個節點的高度,形成冗餘。

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root==null) return true;
        return Math.abs(depth(root.left)-depth(root.right))<2 && isBalanced(root.left) && isBalanced(root.right);
    }
    
    public int depth(TreeNode root){
        if (root == null) return 0;
        return Math.max(depth(root.left),depth(root.right)) + 1;
    }
}

時間複雜度:O(nlogn)
空間複雜度:O(n)


958. 二叉樹的徹底性檢驗

給定一個二叉樹,肯定它是不是一個徹底二叉樹。

百度百科中對徹底二叉樹的定義以下:

若設二叉樹的深度爲 h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層全部的結點都連續集中在最左邊,這就是徹底二叉樹。(注:第 h 層可能包含 1~ 2h 個節點。)
示例 1:
在這裏插入圖片描述

輸入:[1,2,3,4,5,6]
輸出:true
解釋:最後一層前的每一層都是滿的(即,結點值爲 {1} 和 {2,3} 的兩層),且最後一層中的全部結點({4,5,6})都儘量地向左。

題解:
按層遍歷

    1. 一個節點沒有左孩子,有右孩子,必定不是徹底二叉樹,直接返回false
    1. 不知足1. 的條件下,一個節點不是左右孩子雙全時(有左沒右,或沒左沒右),且後面遇到的都是葉子,則是徹底二叉樹,返回true. 若後面不都是葉子,返回false

在這裏插入圖片描述
JAVA代碼

class Solution {
    public boolean isCompleteTree(TreeNode root) {
        if (root == null) return true;
        boolean leaf = false;
        
        //層遍歷
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){
            root = queue.poll();
            //3. 前面出現左右孩子雙全的節點,若當前節點不是葉子,返回false
            //2. 或者當前節點沒有左孩子,但有右孩子,返回false
            if ((leaf && (root.left!=null || root.right!=null))  //
                ||(root.left==null && root.right!=null)){
                return false;
            }
            
            if (root.left!=null) queue.offer(root.left);
            if (root.right != null) queue.offer(root.right);  
            else leaf = true;   //1. 當不是左右孩子雙全時,後面遍歷的節點必須是葉子的狀況開啓
        }
        return true;
    }
}

時間複雜度:每一個節點訪問一下 O(n)
空間複雜度:樹的最大寬度O(w)


222. 徹底二叉樹的節點個數

給出一個徹底二叉樹,求出該樹的節點個數。
說明:
徹底二叉樹的定義以下:在徹底二叉樹中,除了最底層節點可能沒填滿外,其他每層節點數都達到最大值,而且最下面一層的節點都集中在該層最左邊的若干位置。若最底層爲第 h 層,則該層包含 1~ 2h 個節點。
示例:

輸入: 
    1
   / \
  2   3
 / \  /
4  5 6
輸出: 6

題解:
來自左chengyun的思路

  1. 求出樹高h :只需遍歷左側鏈
  2. 求出右子樹的高度:
  • 等於h-1:

    • 左子樹是徹底二叉樹,左子樹+根節點個數:2^(h-1) -1+1
    • 右子樹遞歸計算節點個數
  • 等於h-2:

    • 右子樹是徹底二叉樹,右子樹+根節點個數:2^(h-2) -1+1
    • 左子樹遞歸計算節點個數

遞歸函數bfs: root-當前子樹根節點,level- 當前子樹根節點層數,h-樹總高,返回當前子樹總節點數。
計算子樹高度函數leLftLevel
在這裏插入圖片描述
JAVA代碼

class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) return 0;
        int h = leftLevel(root, 1);
        return bfs(root, 1, h);
    }
    public int bfs(TreeNode root, int level, int h){
        if (level == h) return 1; //注意的點
        //左子樹徹底二叉
        if (leftLevel(root.right, level+1) == h){
            return  (1<<(h-level)) + bfs(root.right, level+1, h);
        }
        else { //右子樹徹底二叉
            return (1<<(h-level-1))+bfs(root.left, level+1, h);
        }
    }
    
    //遍歷左側鏈,獲得子樹高度
    public static int leftLevel(TreeNode root, int level){
        while (root!=null){
            level++;
            root = root.left;
        }
        return level-1;
    }
}

路徑

112. 路徑總和

給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上全部節點值相加等於目標和。

說明: 葉子節點是指沒有子節點的節點。

示例:
給定以下二叉樹,以及目標和 sum = 22,

5
     / \
    4   8
   /   / \
  11  13  4
 /  \      \
7    2      1

返回 true, 由於存在目標和爲 22 的根節點到葉子節點的路徑 5->4->11->2。
解答:

  1. DFS遞歸:先序
  • 當根節點爲空時,返回false
  • 當根節點爲空時,

    • 當前節點不爲葉子,對左、右孩子調用hasPathSum函數:經過遞歸 left || right,其中sum值減去當前節點的權值。
    • 當前節點爲葉子,且sum值爲0時,爲有效路徑。不然路徑無效。
class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if (root==null) return false;
        sum -=root.val;
        if (root.left==null&& root.right==null && sum==0)
            return true;
        return hasPathSum(root.left, sum) || hasPathSum(root.right, sum);
    }
}

時間複雜度:O(n)
空間複雜度:最壞O(n), 最好O(logn)

  1. DFS迭代

借用棧,棧元素: {root, sum-val}
棧中存放節點及對應的sum,當前節點爲葉子且sum值爲0時,爲有效路徑。

  • 根節點爲空時,返回false
  • 根節點不爲空時,

    • 初始時,{root, sum-root.val} 入棧
    • 當棧不空時,先序遍歷方式右孩子先進,左孩子後進
      同時當前節點爲空,且sum值減到0 時,爲有效路徑。
      在這裏插入圖片描述

JAVA代碼

class Solution {
        public boolean hasPathSum(TreeNode root, int sum) {
        if (root == null) return false;
            
        Stack<Pair<TreeNode, Integer>> stack = new Stack<>();
        sum = sum - root.val;
        stack.push(new Pair(root,sum));

        while(!stack.isEmpty()){
            Pair<TreeNode, Integer> current = stack.pop();
            root = current.getKey();
            sum = current.getValue();
            
            if (root.left==null && root.right==null && sum==0){
                return true;
            }
            if (root.right!=null) stack.push(new Pair(root.right, sum-root.right.val));
            if (root.left!=null) stack.push(new Pair(root.left, sum-root.left.val));

        }
        return false;
    }
}

時間複雜度:O(n)
空間複雜度:最壞O(n), 最好O(logn)


437. 路徑總和 III

給定一個二叉樹,它的每一個結點都存放着一個整數值。

找出路徑和等於給定數值的路徑總數。

路徑不須要從根節點開始,也不須要在葉子節點結束,可是路徑方向必須是向下的(只能從父節點到子節點)。

二叉樹不超過1000個節點,且節點數值範圍是 [-1000000,1000000] 的整數。

示例:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

返回 3。和等於 8 的路徑有:

1.  5 -> 3
2.  5 -> 2 -> 1
3.  -3 -> 11

題解:
遞歸方法:
因爲路徑不必定從根節點開始,任何一個節點均可能開始,也不須要在葉子節點結束。
狀況分爲包括當前根節點的路徑和不包括根節點的路徑。

class Solution {
    public int pathSum(TreeNode root, int sum) {
        int num = 0;
        if (root == null) return num;
        if (root.val == sum) num +=1;
        
        //與root無關的路徑
        num += pathSum(root.left, sum);
        num += pathSum(root.right, sum);
        
        //與root有關的路徑,不能直接遞歸pathSum, 會出現斷路的狀況
        num += pathSum_root(root.left, sum-root.val);
        num += pathSum_root(root.right, sum-root.val);
        return num;
    }
    public int pathSum_root(TreeNode root, int sum) {
        if (root == null) return 0;
        int num = 0;
        if (root.val == sum) num += 1;
        
        //與root無關的路徑
        num += pathSum_root(root.left, sum-root.val);
        num += pathSum_root(root.right, sum-root.val);
        return num;
    }
}

【自我總結(不必定正確)】
時間複雜度:每一個節點都要做爲起點計算一次路徑 O(n), 每一個節點計算路徑時間複雜度O(logn)

因此時間複雜度爲 O(nlogn)
                  空間複雜度: O(n)

「須要進一步看題解,如今看不懂,後面再看」


687. 最長同值路徑

給定一個二叉樹,找到最長的路徑,這個路徑中的每一個節點具備相同值。 這條路徑能夠通過也能夠不通過根節點。
注意:兩個節點之間的路徑長度由它們之間的邊數表示。

示例 1:

輸入:

              5
             / \
            4   5
           / \   \
          1   1   5
輸出:

2

示例 2:

輸入:

              1
             / \
            4   5
           / \   \
          4   4   5
輸出:

2

還有其餘路徑題,別忘了。

543. 二叉樹的直徑

給定一棵二叉樹,你須要計算它的直徑長度。一棵二叉樹的直徑長度是任意兩個結點路徑長度中的最大值。這條路徑可能穿過也可能不穿過根結點。
示例 :
給定二叉樹

1
     / \
    2   3
   / \     
  4   5

返回 3, 它的長度是路徑 [4,2,1,3] 或者 [5,2,1,3]。
注意:兩結點之間的路徑長度是以它們之間邊的數目表示。

// 一條路徑的長度爲該路徑通過的節點數減一
//最長路徑:max{左子樹最長路徑,右子樹最長路徑,通過根節點的最長路徑}
//=》最長路徑:max{左子樹最長路徑,右子樹最長路徑,左子樹深度+右子樹深度}
class Solution {
    int diameter = 0;  //使用全局變量保存訪問過的節點中的最長路徑
    public int diameterOfBinaryTree(TreeNode root) {
        if (root==null) return 0;
        depth(root);
        return  diameter;
    }
    //求當前子樹高度
    public int  depth(TreeNode root){
        if (root==null) return 0;
        int left = depth(root.left);
        int right = depth(root.right);
        diameter = Math.max(left+right, diameter);
        return 1+Math.max(left,right);
    }
}

226. 翻轉二叉樹

翻轉一棵二叉樹。

示例:

輸入:

4
   /   \
  2     7
 / \   / \
1   3 6   9

輸出:

4
   /   \
  7     2
 / \   / \
9   6 3   1

617. 合併二叉樹

給定兩個二叉樹,想象當你將它們中的一個覆蓋到另外一個上時,兩個二叉樹的一些節點便會重疊。
你須要將他們合併爲一個新的二叉樹。合併的規則是若是兩個節點重疊,那麼將他們的值相加做爲節點合併後的新值,不然不爲 NULL 的節點將直接做爲新二叉樹的節點。

示例 1:

輸入: 
    Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
輸出: 
合併後的樹:
         3
        / \
       4   5
      / \   \ 
     5   4   7

注意: 合併必須從兩個樹的根節點開始。


572. 另外一個樹的子樹

給定兩個非空二叉樹 s 和 t,檢驗 s 中是否包含和 t 具備相同結構和節點值的子樹。s 的一個子樹包括 s 的一個節點和這個節點的全部子孫。s 也能夠看作它自身的一棵子樹。

示例 1:

給定的樹 s:

     3
    / \
   4   5
  / \
 1   2
給定的樹 t:

   4 
  / \
 1   2
返回 true,由於 t 與 s 的一個子樹擁有相同的結構和節點值。

示例 2:

給定的樹 s:

     3
    / \
   4   5
  / \
 1   2
    /
   0
給定的樹 t:

   4
  / \
 1   2

101. 對稱二叉樹

給定一個二叉樹,檢查它是不是鏡像對稱的。

例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。

1
   / \
  2   2
 / \ / \
3  4 4  3

可是下面這個 [1,2,2,null,3,null,3] 則不是鏡像對稱的:

1
   / \
  2   2
   \   \
   3    3

說明:

若是你能夠運用遞歸和迭代兩種方法解決這個問題,會很加分。


404. 左葉子之和

計算給定二叉樹的全部左葉子之和。

示例:

3
   / \
  9  20
    /  \
   15   7

在這個二叉樹中,有兩個左葉子,分別是 9 和 15,因此返回 24

337. 打家劫舍 III

在上次打劫完一條街道以後和一圈房屋後,小偷又發現了一個新的可行竊的地區。這個地區只有一個入口,咱們稱之爲「根」。 除了「根」以外,每棟房子有且只有一個「父「房子與之相連。一番偵察以後,聰明的小偷意識到「這個地方的全部房屋的排列相似於一棵二叉樹」。 若是兩個直接相連的房子在同一天晚上被打劫,房屋將自動報警。
計算在不觸動警報的狀況下,小偷一晚可以盜取的最高金額。

示例 1:

輸入: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \ 
     3   1

輸出: 7 
解釋: 小偷一晚可以盜取的最高金額 = 3 + 3 + 1 = 7.

示例 2:

輸入: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \ 
 1   3   1

輸出: 9
解釋: 小偷一晚可以盜取的最高金額 = 4 + 5 = 9.

671. 二叉樹中第二小的節點 (簡單)

給定一個非空特殊的二叉樹,每一個節點都是正數,而且每一個節點的子節點數量只能爲 2 或 0。若是一個節點有兩個子節點的話,那麼這個節點的值不大於它的子節點的值。
給出這樣的一個二叉樹,你須要輸出全部節點中的第二小的值。若是第二小的值不存在的話,輸出 -1 。

示例 1:

輸入: 
    2
   / \
  2   5
     / \
    5   7

輸出: 5
說明: 最小的值是 2 ,第二小的值是 5 。

示例 2:

輸入: 
    2
   / \
  2   2

輸出: -1
說明: 最小的值是 2, 可是不存在第二小的值。

637. 二叉樹的層平均值

給定一個非空二叉樹, 返回一個由每層節點平均值組成的數組.

示例 1:

輸入:
    3
   / \
  9  20
    /  \
   15   7
輸出: [3, 14.5, 11]
解釋:
第0層的平均值是 3,  第1層是 14.5, 第2層是 11. 所以返回 [3, 14.5, 11].

注意:節點值的範圍在32位有符號整數範圍內。
題解:
1. 思路一:DFS遞歸
遞歸遍歷二叉樹,用兩個數組分別記錄每一層的總值和節點的總個數.
須要一個新的遞歸函數來完成:help(),參數有:
root:當前節點值
sum: 每一層的總值,第i層的值累加到第i個元素中
count: 每一層節點總個數,第i層的個數累加到第i個元素中
i:當前節點所在層數,每次向下遍歷層數增1.

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> res = new ArrayList<>();
        //List<Double> sum = new ArrayList<>();  //節省一個變量空間
        List<Integer> count = new ArrayList<>();
        help(root, 0, res, count);
        for (int i=0; i<res.size(); i++){
            res.set(i, res.get(i)/count.get(i));
        }
        return res;
    }
    
    public void help(TreeNode root, int i, List<Double> sum, List<Integer> count){
        if (root==null) return ;
        if (i < sum.size()){
            sum.set(i, sum.get(i) + root.val);
            count.set(i, count.get(i) + 1);
        }else{
            sum.add(1.0*root.val);
            count.add(1);
        }
        help(root.left, i+1, sum, count);  //向下遍歷,層數+1
        help(root.right, i+1, sum, count);  //向下遍歷,層數+1
    }
}

時間複雜度:O(n)
空間複雜度:O(h)
2. BFS:借用隊列
使用兩個隊列:一個隊列存放當前層節點,一個臨時隊列存放下一層節點。
下一次層節點時,即訪問臨時隊列中存放的節點。

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> res = new ArrayList<>();
        if (root == null) return null;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){  //當前層節點不空
            int count = 0;
            long sum = 0;  //注意:sum爲長整型
            Queue<TreeNode> temp = new LinkedList<>();  //裝下一層節點的隊列
            while(!queue.isEmpty()){  //當前層節點不空
                root = queue.poll();
                sum += root.val;
                count++;
                //下一層節點入臨時隊列
                if (root.left!=null) temp.offer(root.left);
                if (root.right!=null) temp.offer(root.right);
            }
            //當前層平均值
            res.add(1.0*sum/count);
            //開始遍歷下一層
            queue = temp;
        }
        return res;
    }
}

時間複雜度:O(n)
空間複雜度:O(w)


513. 找樹左下角的值 (中等)

給定一個二叉樹,在樹的最後一行找到最左邊的值。

示例 1:

輸入:

    2
   / \
  1   3

輸出:
1

示例 2:

輸入:

        1
       / \
      2   3
     /   / \
    4   5   6
       /
      7

輸出:
7

注意: 您能夠假設樹(即給定的根節點)不爲 NULL。
題解:
1. 思路一:DFS遞歸
藉助一個二維數組,每一行元素保存對應層的全部結點,最後一行元素的第一個結點即爲最後一層的最左結點。

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        List<List<TreeNode>> array = new ArrayList<>(); 
        List<TreeNode> subArray = new ArrayList<>();
        help(root, 0, array, subArray);
        return array.get(array.size()-1).get(0).val;
    }
    public void help(TreeNode root, int i, List<List<TreeNode>> array, List<TreeNode> subArray){
        if (root==null) return;
        
        if (i<array.size()){
            subArray.add(root);
        }else{
            subArray = new ArrayList<>();
            subArray.add(root);
            array.add(subArray);
        }
        
        help(root.left, i+1, array, subArray);
        help(root.right, i+1, array, subArray);
        
    }
}

時間複雜度O(n)
空間複雜度O(n)
2.思路二:DFS
記錄深度達到最大時的第一個結點:當到達的深度大於目前的最大深度時,爲第一次到達該最大深度,最大深度更新。

class Solution {
    int maxLevel = -1;
    int leftVal = 0;
    public int findBottomLeftValue(TreeNode root) {
      help (root, 0);
      return leftVal;
    }
    public void help(TreeNode root, int i){
        if (root==null) return;
        
        if (i>maxLevel){
            maxLevel = i;
            leftVal = root.val;
        }
        help(root.left, i+1);
        help(root.right, i+1); 
    }
}

時間複雜度O(n)
空間複雜度O(h)
3. 思路三:BFS
利用隊列,每一層從右向左遍歷,最後遍歷到的最後一個結點即爲最後一層最左的結點。

class Solution {
    public int findBottomLeftValue(TreeNode root) {
      Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
            root = queue.poll();
            if (root.right!=null){
                queue.offer(root.right);
            }
            if (root.left!=null){
                queue.offer(root.left);
            }
        }
        return root.val;
    }
}

時間複雜度O(n)
時間複雜度O(w)


236. 二叉樹的最近公共祖先

1. 思路一: DFS遞歸

  • 當前左子樹有pq,則在左子樹找
  • 當前右子樹有pq,則在右子樹找
  • 不然當前節點就是最近公共祖先節點
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) return null;
        if (hasNode(root.left,p) && hasNode(root.left,q))  //左子樹有節點p和q,則最近祖先在左子樹找
            return lowestCommonAncestor(root.left,p,q);
        else if (hasNode(root.right,p) && hasNode(root.right,q))  //右子樹有節點p和q,則最近祖先在右子樹找
            return lowestCommonAncestor(root.right,p,q);
        return root;  //不然當前節點爲最近祖先節點
        
    }
    //子樹中是否存在節點t
    public boolean hasNode(TreeNode node, TreeNode t){
        if (node == null) return false;
        if (node.val == t.val) return true;
        return help(node.left,t) || help(node.right,t);
    }
}

時間複雜度:O(n^2)
空間複雜度:O(h)

  1. 思路二:

這個函數的功能有三個:給定兩個節點 pq

若是 pq都存在,則返回它們的公共祖先;
若是隻存在一個,則返回存在的一個;
若是 pq都不存在,則返回NULL

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) return null;
        
        if (root == p || root == q) return root;
        
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        
        if (left == null) return right;
        if (right == null) return left;
        if (left!=null && right!=null) return root;
        
        return null;
    }
}

時間複雜度: O(n)
空間複復雜度:O(h)


617 合併二叉樹

給定兩個二叉樹,想象當你將它們中的一個覆蓋到另外一個上時,兩個二叉樹的一些節點便會重疊。
你須要將他們合併爲一個新的二叉樹。合併的規則是若是兩個節點重疊,那麼將他們的值相加做爲節點合併後的新值,不然不爲 NULL 的節點將直接做爲新二叉樹的節點。

示例 1:

輸入: 
    Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
輸出: 
合併後的樹:
         3
        / \
       4   5
      / \   \ 
     5   4   7
注意: 合併必須從兩個樹的根節點開始。

遞歸

class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        TreeNode root = new TreeNode();
        if (t1 == null && t2 == null){
            return null;
        }
        if (t1 !=null && t2 == null){
            return t1;
        }
        else if(t1==null && t2!=null){
            return t2;
        }
        else if(t1!=null && t2!=null){
            root.val = t1.val + t2.val;
            root.left = mergeTrees(t1.left, t2.left);
            root.right = mergeTrees(t1.right, t2.right);
            return root;
        }
        return root;
    }
}

層次遍歷

class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        TreeNode root = new TreeNode();
        if (t1 == null && t2 == null) return null;
        if (t1 != null && t2 == null) return t1;
        if (t1 == null && t2 != null) return t2;
        
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(t1);
        queue.offer(t2);
        while(!queue.isEmpty()){
            TreeNode node1 = queue.poll();
            TreeNode node2 = queue.poll();
            node1.val += node2.val;
            if (node1.left==null && node2.left!=null){
                node1.left = node2.left;
            }
            else if(node1.left!=null && node2.left!=null){
                queue.offer(node1.left);
                queue.offer(node2.left);
            }
            
            if (node1.right==null && node2.right!=null){
                node1.right = node2.right;
            }
            else if (node1.right!=null && node2.right!=null){
                queue.offer(node1.right);
                queue.offer(node2.right);
            }
        }
        return t1;
    }
}
相關文章
相關標籤/搜索