面試中對樹的考察多爲二叉樹、二叉查找樹等。常見的二叉樹操作有:
- 前序遍歷、中序遍歷、後續遍歷(遞歸、迭代)
- 層序遍歷
- 樹的高度
- 樹的路徑
- 二叉搜索樹
二叉樹遍歷
94.二叉樹的中序遍歷
中序遍歷定義:先左子樹,後根節點,再右子樹。首先給出簡單的遞歸解法。
/**
* 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> ret = new ArrayList<>();
inorder(root,ret);
return ret;
}
public void inorder(TreeNode root, List<Integer> list) {
if (root == null) {
return ;
}
inorder(root.left,list);
list.add(root.val);
inorder(root.right,list);
}
}
迭代解法使用棧,因爲其序爲左中右,所以將左子樹一直壓棧直到爲空,開始出棧,出棧的即爲根節點,加入解中,同時開始遍歷右子樹。
/**
* 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> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while (curr != null || !stack.empty()) {
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
ret.add(curr.val);
curr = curr.right;
}
return ret;
}
}
144.二叉樹的前序遍歷
前序遍歷定義:先根節點,後左子樹,再右子樹。遞歸解法與上一方法類似,只是改換了遞歸和加入結果集的順序。
/**
* 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> ret = new ArrayList<>();
preorder(root,ret);
return ret;
}
public void preorder(TreeNode root, List<Integer> list) {
if (root == null){
return;
}
list.add(root.val);
preorder(root.left,list);
preorder(root.right,list);
}
}
迭代解法仍然使用棧,相較中序遍歷的解法,只需改換加入結果集的位置。由於是先序遍歷,遍歷到那個節點便將其加入結果集,將左子樹持續壓棧直到爲空,出棧,遍歷出棧節點的右子樹。
/**
* 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> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while ( curr != null || !stack.empty() ) {
while (curr != null) {
ret.add(curr.val);
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
curr = curr.right;
}
return ret;
}
}
145.二叉樹的後序遍歷
後序遍歷遞歸定義:先左子樹,後右子樹,再根節點。遞歸解法
/**
* 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> postorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
postorder(root,ret);
return ret;
}
public void postorder(TreeNode root,List<Integer> list) {
if (root == null) {
return;
}
postorder(root.left,list);
postorder(root.right,list);
list.add(root.val);
}
}
迭代解法的難點在於,由於後序遍歷是左右根的順序,當從棧裏拿出根節點時需判斷此時其右子樹是否已經遍歷完成。在這裏我們使用一個標記,當一棵子樹遍歷完成時,記錄下他的根節點,作爲根節點右子樹遍歷完成的依據。
/**
* 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> postorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
TreeNode r = null; //記錄遍歷過的右子樹
while(curr != null || !stack.empty()) {
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.peek();
// 判斷右子樹是否遍歷完成
if (curr.right == null || curr.right == r) {
ret.add(curr.val);
r = curr;
curr = null;
stack.pop();
} else {
curr = curr.right;
}
}
return ret;
}
}
總的來說,以上三種遍歷都可用一樣的遞歸或迭代框架實現。遞歸只需改變加入結果集的位置;迭代首先都將全部左子樹壓入棧中,在出棧時,根據不同的遍歷選擇不同的操作。
102.二叉樹的層次遍歷
給定一個二叉樹,返回其按層次遍歷的節點值。 (即逐層地,從左到右訪問所有節點)。
給定二叉樹: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其層次遍歷結果:
[
[3],
[9,20],
[15,7]
]
二叉樹層次遍歷使用隊列,由於要將每一層放在一個列表中,使用棧的長度截取隊列中屬於一層的節點。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
if(root == null) {
return ret;
}
queue.offer(root);
while(!queue.isEmpty()) {
int size = queue.size();
List<Integer> list = new ArrayList<>();
while(size>0) {
TreeNode curr = queue.poll();
list.add(curr.val);
if(curr.left!=null) {
queue.offer(curr.left);
}
if(curr.right!=null) {
queue.offer(curr.right);
}
size--;
}
ret.add(list);
}
return ret;
}
}
105.從前序遍歷與中序遍歷序列構造二叉樹
根據一棵樹的前序遍歷與中序遍歷構造二叉樹。
注意:
你可以假設樹中沒有重複的元素。
例如,給出
前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]
返回如下的二叉樹:
3
/ \
9 20
/ \
15 7
遞歸構造。前序遍歷第一個值即爲當前樹的根節點,在中序遍歷中找到該根節點,左邊爲左子樹,右邊爲右子樹,遞歸構造,此時,前序遍歷數組的分割由中序遍歷確定的左右子樹大小來分割。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return dfs(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
}
public TreeNode dfs(int[] preorder,int prel,int prer,int[] inorder,int inl,int inr) {
if(prel > prer) {
return null;
}
TreeNode root = new TreeNode(preorder[prel]);
int index = 0;
for(int i=inl;i<=inr;i++) {
if(inorder[i] == preorder[prel]) { //從中序遍歷找分割點
index = i;
break;
}
}
//根據中序遍歷分割點計算前序遍歷分割點
root.left = dfs(preorder,prel+1,prel+index-inl,inorder,inl,index-1);
root.right = dfs(preorder,prel+index-inl+1,prer,inorder,index+1,inr);
return root;
}
}
106.從後序遍歷與中序遍歷序列構造二叉樹
根據一棵樹的中序遍歷與後序遍歷構造二叉樹。
注意:
你可以假設樹中沒有重複的元素。
例如,給出
中序遍歷 inorder = [9,3,15,20,7]
後序遍歷 postorder = [9,15,7,20,3]
返回如下的二叉樹:
3
/ \
9 20
/ \
15 7
與上一題一樣可以遞歸構造,此時,後序遍歷第一個值變爲樹的根,仍在inorder中搜索該根的位置,分爲左右兩子樹遞歸構造。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
return dfs(inorder,0,inorder.length-1,postorder,0,postorder.length-1);
}
public TreeNode dfs(int[] inorder,int inleft,int inright,int[] postorder,int postleft,int postright) {
if (inleft > inright) {
return null;
}
TreeNode root = new TreeNode(postorder[postright]);
int index = 0;
for(int i=inleft;i<=inright;i++) {
if(inorder[i] == postorder[postright]) {
index = i;
break;
}
}
root.left = dfs(inorder,inleft,index-1,postorder,postleft,postleft+index-inleft-1);
root.right = dfs(inorder,index+1,inright,postorder,postleft+index-inleft,postright-1);
return root;
}
}
297.二叉樹的序列化與反序列化
請設計一個算法來實現二叉樹的序列化與反序列化。這裏不限定你的序列 / 反序列化算法執行邏輯,你只需要保證一個二叉樹可以被序列化爲一個字符串並且將這個字符串反序列化爲原始的樹結構。
你可以將以下二叉樹:
1
/ \
2 3
/ \
4 5
序列化爲 "[1,2,3,null,null,4,5]"
可使用二叉樹層序遍歷的結果作爲序列化的內容,若某子樹爲null用“#”代表。層序遍歷使用隊列,同樣,在通過序列內容構建二叉樹時也使用隊列。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
Queue<TreeNode> queue = new LinkedList<>();
if(root != null) {
sb.append(root.val);
sb.append(",");
queue.offer(root);
} else {
return null;
}
while(!queue.isEmpty()) {
TreeNode p = queue.poll();
if(p.left == null) {
sb.append("#,");
} else {
sb.append(p.left.val);
sb.append(",");
queue.offer(p.left);
}
if(p.right == null) {
sb.append("#,");
} else {
sb.append(p.right.val);
sb.append(",");
queue.offer(p.right);
}
}
return sb.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data == null) {
return null;
}
String[] arr = data.split(",");
Queue<TreeNode> queue = new LinkedList<>();
TreeNode root = new TreeNode(Integer.valueOf(arr[0]));
queue.offer(root);
int index = 1;
while(!queue.isEmpty()) {
TreeNode curr = queue.poll();
if(arr[index++].equals("#")) {
curr.left = null;
} else {
curr.left = new TreeNode(Integer.valueOf(arr[index-1]));
queue.offer(curr.left);
}
if(arr[index++].equals("#")) {
curr.right = null;
} else {
curr.right = new TreeNode(Integer.valueOf(arr[index-1]));
queue.offer(curr.right);
}
}
return root;
}
}
111.二叉樹的最小深度
給定一個二叉樹,找出其最小深度。
最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
說明: 葉子節點是指沒有子節點的節點。
給定二叉樹 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最小深度 2.
層序遍歷,返回第一個左右子樹都爲null的節點深度。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root == null) {
return 0;
}
int level = 0;
queue.offer(root);
while(!queue.isEmpty()) {
int size = queue.size();
level++;
while(size>0) {
TreeNode curr = queue.poll();
if(curr.left == null && curr.right == null) {
return level;
}
if(curr.left != null) {
queue.offer(curr.left);
}
if(curr.right != null) {
queue.offer(curr.right);
}
size--;
}
}
return level;
}
}
104.二叉樹的最大深度
給定一個二叉樹,找出其最大深度。
二叉樹的深度爲根節點到最遠葉子節點的最長路徑上的節點數。
說明: 葉子節點是指沒有子節點的節點。
給定二叉樹 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
遞歸,返回左右子樹最大高度加一。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) {
return 0;
}
return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
}
}
二叉樹路徑問題
687.最長同值路徑
給定一個二叉樹,找到最長的路徑,這個路徑中的每個節點具有相同值。 這條路徑可以經過也可以不經過根節點。
注意:兩個節點之間的路徑長度由它們之間的邊數表示。
輸入:
5
/ \
4 5
/ \ \
1 1 5
輸出:
2
輸入:
1
/ \
4 5
/ \ \
4 4 5
輸出:
2
二叉樹問題多可以使用遞歸的方法解決。用遞歸函數返回以遞歸的根節點所在的最長路徑。用全局變量保存最大值。對根結節點,遞歸遍歷左子樹和右子樹,若根節點的值和左子樹值相等,即可在左子樹遞歸返回值上加1作爲當前值,因爲路徑上可以加上根節點,長度加一。遞歸函數返回值爲根節點通過左子樹、右子樹中路徑的較大值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int ret = 0;
public int longestUnivaluePath(TreeNode root) {
dfs(root);
return ret;
}
public int dfs(TreeNode root) {
if(root == null) {
return 0;
}
int left = dfs(root.left);
int right = dfs(root.right);
int ansleft = 0,ansright = 0;
if (root.left != null && root.left.val == root.val) {
ansleft = left + 1;
}
if (root.right != null && root.right.val == root.val){
ansright = right + 1;
}
ret = Math.max(ret,ansleft+ansright);
return Math.max(ansleft,ansright);
}
}
124.二叉樹中的最大路徑和
給定一個非空二叉樹,返回其最大路徑和。
本題中,路徑被定義爲一條從樹中任意節點出發,達到任意節點的序列。該路徑至少包含一個節點,且不一定經過根節點。
輸入: [1,2,3]
1
/ \
2 3
輸出: 6
輸入: [-10,9,20,null,null,15,7]
-10
/ \
9 20
/ \
15 7
輸出: 42
這道題的思路與上題差不多,使用遞歸函數返回經過參數節點的最大路徑和。遞歸計算左子樹和右子樹的最大路徑和。若子樹的最大路徑和是大於0的,那麼子樹對經過根的最大路徑和是有增益的,把他加到參數節點的值中,與全局最大值比較。遞歸函數的返回值爲,參數節點值,參數節點值+左子樹遞歸值,參數節點值+右子樹遞歸值中的最大值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int ret = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
dfs(root);
return ret;
}
public int dfs(TreeNode root) {
if (root == null) {
return 0;
}
int left = dfs(root.left);
int right = dfs(root.right);
int cur = root.val;
if(left>0) {
cur+=left;
}
if(right>0) {
cur+=right;
}
ret = Math.max(ret,cur);
return Math.max(root.val,Math.max(left+root.val,right+root.val));
}
}
面試題34.二叉樹中和爲某一值的路徑
輸入一棵二叉樹和一個整數,打印出二叉樹中節點值的和爲輸入整數的所有路徑。從樹的根節點開始往下一直到葉節點所經過的節點形成一條路徑。
給定如下二叉樹,以及目標和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
枚舉處滿足條件的路徑可以使用回溯算法。定義滿足加入結果集的條件,當前節點爲葉節點且值爲sum。否則若當前節點有左子樹,加入當前節點,回溯左子樹,去除當前節點。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> ret = new ArrayList<>();
if (root!=null) {
backtrack(ret,new ArrayList<>(),root,sum);
}
return ret;
}
public void backtrack(List<List<Integer>> ret,List<Integer> curr,TreeNode root,int sum) {
//定義滿足條件
if(root.left == null && root.right == null && sum==root.val) {
curr.add(root.val);
ret.add(new ArrayList<>(curr));
curr.remove(curr.size()-1);
}
if (root.left!=null){
curr.add(root.val);//加入當前
backtrack(ret,curr,root.left,sum-root.val);//回溯
curr.remove(curr.size()-1);//去掉當前
}
if (root.right!=null){
curr.add(root.val);
backtrack(ret,curr,root.right,sum-root.val);
curr.remove(curr.size()-1);
}
}
}
二叉搜索樹問題
98.驗證二叉搜索樹
給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹。
假設一個二叉搜索樹具有如下特徵:
節點的左子樹只包含小於當前節點的數。
節點的右子樹只包含大於當前節點的數。
所有左子樹和右子樹自身必須也是二叉搜索樹。
輸入:
2
/ \
1 3
輸出: true
輸入:
5
/ \
1 4
/ \
3 6
輸出: false
解釋: 輸入爲: [5,1,4,null,null,3,6]。根節點的值爲 5 ,但是其右子節點值爲 4 。
利用二叉搜索樹的性質,中序遍歷爲一個遞增序列。使用迭代方法中序遍歷二叉樹,不滿足遞增條件則返回false。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isValidBST(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
double pre = - Double.MAX_VALUE;
TreeNode curr = root;
while(curr!=null || !stack.empty()) {
while(curr!=null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
if(curr.val <= pre) {
return false;
}
pre = curr.val;
curr = curr.right;
}
return true;
}
}
96.不同的二叉搜索樹
給定一個整數 n,求以 1 ... n 爲節點組成的二叉搜索樹有多少種?
輸入: 3
輸出: 5
解釋:
給定 n = 3, 一共有 5 種不同結構的二叉搜索樹:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
動態規劃。定義數組dp,dp[i]代表以1..i爲節點組成的二叉搜索樹有多少。1..n爲節點,選擇其中一個節點j作爲根節點,可以構成二叉搜索樹的個數是由0..j-1構成的左子樹的個數乘以j+1..n構成的右子樹的個數。構成二叉搜索樹的個數與樹中的值無關只與節點個數有關,所以可以自底向上的計算。
class Solution {
public int numTrees(int n) {
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
for(int i=2;i<=n;i++) {
for(int j=0;j<i;j++) {
dp[i] += (dp[j] * dp[i-j-1]);
}
}
return dp[n];
}
}
95.不同的二叉搜索樹2
給定一個整數 n,生成所有由 1 ... n 爲節點所組成的二叉搜索樹。
輸入: 3
輸出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
解釋:
以上的輸出對應以下 5 種不同結構的二叉搜索樹:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
返回所有二叉搜索樹,使用遞歸。1..n爲節點,選擇其中一個節點j作爲根節點,遞歸計算1..j-1構成的二叉搜索樹和j+1..n構成的二叉搜索樹進行組合。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<TreeNode> generateTrees(int n) {
if(n==0) {
return new ArrayList<>();
}
return dfs(1,n+1);
}
//前閉後開
public List<TreeNode> dfs(int start,int end) {
if(start==end) {
return null;
}
List<TreeNode> ret = new ArrayList<>();
for(int i=start;i<end;i++) { //根節點
List<TreeNode> left = dfs(start,i); //左子樹
List<TreeNode> right = dfs(i+1,end); //右子樹
if(left!=null && right!=null) {
for(TreeNode leftnode:left) {
for(TreeNode rightnode:right) {
TreeNode root = new TreeNode(i);
root.left = leftnode;
root.right = rightnode;
ret.add(root);
}
}
} else if (left!=null) {
for(TreeNode leftnode:left) {
TreeNode root = new TreeNode(i);
root.left = leftnode;
root.right = null;
ret.add(root);
}
} else if (right!=null) {
for(TreeNode rightnode:right) {
TreeNode root = new TreeNode(i);
root.left = null;
root.right = rightnode;
ret.add(root);
}
} else {
TreeNode root = new TreeNode(i);
root.left = null;
root.right = null;
ret.add(root);
}
}
return ret;
}
}
108.將有序數組轉換爲二叉搜索樹
將一個按照升序排列的有序數組,轉換爲一棵高度平衡二叉搜索樹。
本題中,一個高度平衡二叉樹是指一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過 1。
給定有序數組: [-10,-3,0,5,9],
一個可能的答案是:[0,-3,9,-10,null,5],它可以表示下面這個高度平衡二叉搜索樹:
0
/ \
-3 9
/ /
-10 5
因爲需要構建平衡的二叉搜索樹,構建樹時,選擇數組最中間的數作爲根節點,左右子樹遞歸計算。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return dfs(nums,0,nums.length-1);
}
//前閉後閉
public TreeNode dfs(int[] nums,int start,int end) {
if(start>end) {
return null;
}
int mid = (start+end)/2;
TreeNode root = new TreeNode(nums[mid]);
root.left = dfs(nums,start,mid-1);
root.right = dfs(nums,mid+1,end);
return root;
}
}
173.二叉搜索樹迭代器
實現一個二叉搜索樹迭代器。你將使用二叉搜索樹的根節點初始化迭代器。
調用 next() 將返回二叉搜索樹中的下一個最小的數。
BSTIterator iterator = new BSTIterator(root);
iterator.next(); // 返回 3
iterator.next(); // 返回 7
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 9
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 15
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 20
iterator.hasNext(); // 返回 false
二叉樹的中序遍歷即可將二叉搜索樹中的數遞增輸出,因此本題將中序遍歷拆解,一次輸出一個。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class BSTIterator {
Stack<TreeNode> stack;
TreeNode curr;
public BSTIterator(TreeNode root) {
this.stack = new Stack<>();
this.curr = root;
}
/** @return the next smallest number */
public int next() {
int ret=0;
if(curr!=null || !stack.empty()) {
while(curr != null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
ret = curr.val;
curr = curr.right;
}
return ret;
}
/** @return whether we have a next smallest number */
public boolean hasNext() {
if(curr!=null || !stack.empty()) {
return true;
}
return false;
}
}
祖先問題
235.二叉搜索樹的最近公共祖先
給定一個二叉搜索樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”
例如,給定如下二叉搜索樹: root = [6,2,8,0,4,7,9,null,null,3,5]
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
輸出: 6
解釋: 節點 2 和節點 8 的最近公共祖先是 6。
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
輸出: 2
解釋: 節點 2 和節點 4 的最近公共祖先是 2, 因爲根據定義最近公共祖先節點可以爲節點本身。
可以利用二叉搜索樹的性質,知道p和q是否都在二叉樹的左子樹或右子樹上,若是則遞歸求解左子樹或右子樹,否則當前根節點即爲結果。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root.val > p.val && root.val > q.val) {
return lowestCommonAncestor(root.left,p,q);
} else if(root.val < p.val && root.val < q.val) {
return lowestCommonAncestor(root.right,p,q);
} else {
return root;
}
}
}
236.二叉樹的最近公共祖先
給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”
例如,給定如下二叉樹: root = [3,5,1,6,2,0,8,null,null,7,4]
輸入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
輸出: 3
解釋: 節點 5 和節點 1 的最近公共祖先是節點 3。
示例 2:
輸入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
輸出: 5
解釋: 節點 5 和節點 4 的最近公共祖先是節點 5。因爲根據定義最近公共祖先節點可以爲節點本身。
本題不能像二叉搜索樹一樣利用節點上的信息,但仍然可以使用遞歸求解。題解:【C++ 經典遞歸】思路非常好理解 時間複雜度O(n), 空間複雜度O(n)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
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;
}
}