1. 程式人生 > >樹相關

樹相關

目錄

144前序遍歷

思路:(迴圈前入棧、先右節點入棧)

  • 建棧,入棧,迴圈,只要棧不為空
    • 出棧,把值加入res。
    • 如果右不為空,入棧。左一樣。
List<Integer> res = new ArrayList<>();
if (root == null) {
    return res;
}

Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
    TreeNode curNode = stack.pop();
    res.add(curNode.val);
    // 下面用於非遞迴的反轉二叉樹
//    TreeNode tempNode = node.left;
//    node.left = node.right;
//    node.right = tempNode;

    if (curNode.right != null) {
        stack.push(curNode.right);
    }
    if (curNode.left != null) {
        stack.push(curNode.left);
    }
}
return res;

94中序遍歷(98驗證二叉搜尋樹、230二叉搜尋樹中第K小的元素)

思路:

  • 建棧、cur指標,不入棧迴圈,只要cur和棧不為空
    • 只要cur不為空,迴圈讓左子節點入棧,cur做相應移動
    • 出棧,把值加入res。
    • cur移動到右節點
// 設定計數器(二叉搜尋樹中第K小的元素)
// int cnt = 0;

List<Integer> res = new ArrayList<>();

if (root == null) {
    return res;
}

Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {

    // 先把左子節點都入棧
    /** 注意是cur != null,而不是cur.left != null */
    while (cur != null) {
        stack.push(cur);
        cur = cur.left;
    }

    cur = stack.pop();
    // (二叉搜尋樹中第K小的元素)
//      cnt++;
//      if (cnt == k) return cur.val;

    // (驗證二叉搜尋樹)
//      if (pre != null && cur.val <= pre.val) return false;
//      pre = cur;
    res.add(cur.val);
    cur = cur.right;
}
return res;

145後序遍歷

思路:

  • 建棧、cur和pre指標
  • pop依然在中間,但add前要判斷是否還有右節點或者之前就是右節點,否則把cur放回去,指向它的右節點。add後pre成為cur,cur變為null
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null;
TreeNode cur = root;

while (cur != null || !stack.isEmpty()) {
    while (cur != null) {
        stack.push(cur);
        cur = cur.left;
    }

    cur = stack.pop();
    if (cur.right == null || pre == cur.right) {
        res.add(cur.val);
        pre = cur;
        cur = null;
    } else {
        stack.push(cur);
        cur = cur.right;
    }
}

102/107層次遍歷(104二叉樹最大深度、103

二叉樹的鋸齒形層次遍歷)

思路:

  • 新建queue,入列
  • 迴圈,只要q不為空
    • 新建level連結串列,記錄本層元素的個數
    • 遍歷此層
      • 出列,加入res。
      • 如果左節點不為空,入列。右節點一樣。
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);

while (!queue.isEmpty()) {
    // 下面用於 maxDepth
//      res++;

List<Integer> level = new ArrayList<>();
// 注意,由於迴圈中對queue進行修改,其size不斷變化,所以這個值不能直接放到迴圈條件中
int length = queue.size();

// 下面用於 zigzagLevelOrder
//  boolean flag = result.size() % 2 == 0;

for (int i = 0; i < length; i++) { // 遍歷一層的node
    TreeNode node = queue.poll();
    level.add(node.val);

    // 下面用於 zigzagLevelOrder
//      if (flag) level.add(node.val);
//      else level.add(0, node.val);

    if (node.left != null) queue.offer(node.left);
    if (node.right != null) queue.offer(node.right);
}
// levelOrderBottom就 add(0, level)
// 不斷往0插入資料,舊資料就被擠到後面了
result.add(level);

105從前序與中序遍歷序列構造二叉樹

思路:

  • 新建preStart, preEnd, inStart, inEnd四個變數
  • 呼叫遞迴函式
    • 當“前序”或者“中序”中其中一個結束,就返回null
    • 取出pre中的preStart元素,新建節點,然後在中序陣列中找出該節點的index。根據這個index來劃分左右子樹對應“前序”和“中序”的邊界
// construct函式
if (preStart > preEnd || inStart > inEnd) {
    return null;
}

int val = preorder[preStart];
TreeNode p = new TreeNode(val);

int k = 0;
for (int i = 0; i < inorder.length; i++) {
    if (val == inorder[i]) {
        k = i;
        break;
    }
}

// 注意要減去inStart
p.left = construct(preorder, preStart + 1, preStart + (k - inStart),
        inorder, inStart, k - 1);
p.right = construct(preorder, preStart + (k - inStart) + 1, preEnd,
        inorder, k + 1, inEnd);

return p;

114二叉樹展開為連結串列

    1
   / \
  2   5
 / \   \
3   4   6

1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

只能說,從上面的觀察結果來看,可以先處理右節點,再到左節點的順序。

private TreeNode preNode = null;

public void flatten(TreeNode root) {
   if (root == null) {
        return;
    }
    flatten(root.right);
    flatten(root.left);
    root.right = preNode;
    root.left = null;
    preNode = root;
}

124二叉樹中的最大路徑和

思路:遞迴

  • 對左右節點遞迴呼叫函式,當節點為null時,返回0。如果返回的結果比0還小,可以不選該節點,所以以left為例,賦值為返回值及0中的最大值。
  • 返回左右節點值後,判斷res是否需要更新,比較值是左右即當前節點值的和。
  • 最後只能返回一條路近的值,即從左到當前節點還是從右到當前節點的和。

235/236二叉樹的最近公共祖先

236思路:

target是p和q的最近公共父節點,它有兩種情況
要麼p和q都不是target,要麼p或q為target
第一種情況:p和q會分佈在target的左右兩邊,所以左右返回的都不是null,返回當前即可
第二種情況:左右其中一個返回null,一個非null,直接返回非null即可
遞迴尋找,root絕對會等於null或者p、q中的一個

if (root == null || root == p || root == q) return root;

TreeNode left = lowestCommonAncestor1(root.left, p, q);
// 下面優化,說明左節點是一個非p或q的公共節點
// if (left != null && left != p && left != q) return left;
TreeNode right = lowestCommonAncestor1(root.right, p, q);

if (left != null && right != null) return root;

return left == null ? right : left;

235思路:

如果當前節點的值大於兩個引數節點,說明它們的公共父節點在左邊,反之。只有噹噹前節點的值處於兩個值之間,當前節點才是公共父節點。

if (root.val > p.val && root.val > q.val) {
    return lowestCommonAncestor2(root.left, p, q);
} else if (root.val < p.val && root.val < q.val) {
    return lowestCommonAncestor2(root.right, p, q);
} else {
    return root;
}