樹相關
阿新 • • 發佈:2018-12-11
目錄
- 144前序遍歷
- 94中序遍歷(98驗證二叉搜尋樹、230二叉搜尋樹中第K小的元素)
- 145後序遍歷
- 102/107層次遍歷(104二叉樹最大深度、103
- 105從前序與中序遍歷序列構造二叉樹
- 114二叉樹展開為連結串列
- 124二叉樹中的最大路徑和
- 235/236二叉樹的最近公共祖先
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;
}