二叉樹
94. 二叉樹的中序遍歷
難度中等483
給定一個二叉樹,返回它的中序 遍歷。
示例:
輸入: [1,null,2,3]
1
\
2
/
3
輸出: [1,3,2]
進階: 遞歸算法很簡單,你可以通過迭代算法完成嗎?
遞歸解答:
class Solution {
//保存結果
List<Integer> result=new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root!=null){
inorderTraversal(root.left);
result.add(root.val);
inorderTraversal(root.right);
}
return result;
}
}
迭代解答:
class Solution {
//保存結果
List<Integer> result=new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root!=null){
Stack<TreeNode> stack=new Stack<>();
while(!stack.empty()||root!=null){
if(root!=null){
stack.push(root);
root=root.left;
}else{
TreeNode pop=stack.pop();
result.add(pop.val);
root=pop.right;
}
}
}
return result;
}
}
95. 不同的二叉搜索樹 II
給定一個整數 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
解答:
class Solution {
public List<TreeNode> generateTrees(int n) {
if(n==0)
return new ArrayList<TreeNode>();
return createTree(1,n);
}
//生成範圍start~end的二叉搜索樹
public List<TreeNode> createTree(int start,int end){
//保存結果集
List<TreeNode> result=new ArrayList<>();
//不存在要添加一個null節點,不能省略,否則其中一個集合空,foreach遍歷時無法組合
if(start>end){
result.add(null);
return result;
}
//以i爲根節點生成二叉搜索樹
for(int i=start;i<=end;i++){
//i的不同左子樹集合
List<TreeNode> treeListL=createTree(start,i-1);
//i的不同右子樹集合
List<TreeNode> treeListR=createTree(i+1,end);
//全組合
for(TreeNode treeL:treeListL){
for(TreeNode treeR:treeListR){
TreeNode node=new TreeNode(i);
node.left=treeL;
node.right=treeR;
result.add(node);
}
}
}
return result;
}
}
96. 不同的二叉搜索樹
給定一個整數 n,求以 1 … n 爲節點組成的二叉搜索樹有多少種?
示例:
輸入: 3
輸出: 5
解釋:
給定 n = 3, 一共有 5 種不同結構的二叉搜索樹:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
解答:
public int numTrees(int n) {
//假設dp(N)爲以1,2...N爲節點組成的二叉搜索樹數量
//f(x)爲以x爲根節點的二叉搜索樹數量
//那麼dp(N)=f(1)+f(2)+...+f(N)
//f(x)=dp(x-1)*dp(N-x),因爲x左邊有x-1個節點,右邊有N-x個節點
//因此dp(N)=dp(0)*dp(N-1)+dp(1)*dp(N-2)+....+dp(N-1)*dp(0)
int[] dp=new int[n+1];
//初始化
dp[0]=1;
dp[1]=1;
//動態規劃更新
for(int N=2;N<=n;N++){
for(int x=1;x<=N;x++){
dp[N]+=dp[x-1]*dp[N-x];
}
}
return dp[n];
}
98. 驗證二叉搜索樹
給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹。
假設一個二叉搜索樹具有如下特徵:
- 節點的左子樹只包含小於當前節點的數。
- 節點的右子樹只包含大於當前節點的數。
- 所有左子樹和右子樹自身必須也是二叉搜索樹。
示例 1:
輸入:
2
/ \
1 3
輸出: true
解答:
class Solution {
//前驅節點
TreeNode pre=null;
//是否爲二叉搜索樹,默認true
boolean flag=true;
public boolean isValidBST(TreeNode root) {
if(root==null)
return true;
inorder(root);
return flag;
}
public void inorder(TreeNode root){
if(root!=null){
inorder(root.left);
if(pre!=null){
//二叉搜索樹中序排列是升序,如果小於等於說明不符合
if(root.val<=pre.val){
flag=false;
return;
}
}
//更新前驅節點
pre=root;
inorder(root.right);
}
}
}
100. 相同的樹
給定兩個二叉樹,編寫一個函數來檢驗它們是否相同。
如果兩個樹在結構上相同,並且節點具有相同的值,則認爲它們是相同的。
示例 1:
輸入: 1 1
/ \ / \
2 3 2 3
[1,2,3], [1,2,3]
輸出: true
示例 2:
輸入: 1 1
/ \
2 2
[1,2], [1,null,2]
輸出: false
解答:
public boolean isSameTree(TreeNode p, TreeNode q) {
//兩個樹都爲空則代表相同
if(p==null&&q==null)
return true;
//一個爲空,一個不爲空代表不同
if((p==null&&q!=null)||(p!=null&&q==null))
return false;
//如果值不相同則代表不同,值相同再分別比較左子樹和右子樹
if(p.val!=q.val)
return false;
return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
}
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
進階:
你可以運用遞歸和迭代兩種方法解決這個問題嗎?
遞歸解答:
public boolean isSymmetric(TreeNode root) {
//空樹肯定對稱
if(root==null)
return true;
//判斷左右子樹是否對稱
return isMirror(root.left,root.right);
}
public boolean isMirror(TreeNode p,TreeNode q) {
//兩個樹都爲空則代表相同
if(p==null&&q==null)
return true;
//一個爲空,一個不爲空代表不同
if((p==null&&q!=null)||(p!=null&&q==null))
return false;
//如果值不相同則代表不同,值相同再對稱比較p左q右/p右q左
if(p.val!=q.val)
return false;
return isMirror(p.left,q.right)&&isMirror(p.right,q.left);
}
迭代解答:
public boolean isSymmetric(TreeNode root) {
//空樹對稱
if(root==null)
return true;
//根節點沒有左右子樹,是對稱的
if(root.left==null&&root.right==null)
return true;
//根節點左右子樹只有一邊,不對稱
if((root.left!=null&&root.right==null)||(root.left==null&&root.right!=null))
return false;
//兩個棧,s1保存左子樹,s2保存右子樹
Stack<TreeNode> s1=new Stack<>();
Stack<TreeNode> s2=new Stack<>();
//左右子樹的根節點入棧
s1.push(root.left);
s2.push(root.right);
while(!s1.isEmpty()&&!s2.isEmpty()){
TreeNode pop1=s1.pop();
TreeNode pop2=s2.pop();
//節點值不同不對稱
if(pop1.val!=pop2.val)
return false;
//一邊節點有子樹,對應的另一邊沒有則不對稱,否則將對應子樹入棧
if((pop1.left!=null&&pop2.right==null)||(pop1.left==null&&pop2.right!=null))
return false;
if(pop1.left!=null&&pop2.right!=null){
s1.push(pop1.left);
s2.push(pop2.right);
}
if((pop1.right!=null&&pop2.left==null)||(pop1.right==null&&pop2.left!=null))
return false;
if(pop1.right!=null&&pop2.left!=null){
s1.push(pop1.right);
s2.push(pop2.left);
}
}
//兩個棧同時爲空說明全部匹配成功
return s1.isEmpty()&&s2.isEmpty();
}
102. 二叉樹的層序遍歷
給你一個二叉樹,請你返回其按 層序遍歷 得到的節點值。 (即逐層地,從左到右訪問所有節點)。
示例:
二叉樹:[3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回其層次遍歷結果:
[
[3],
[9,20],
[15,7]
]
解答:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
//保存結果集
List<List<Integer>> result=new ArrayList<>();
//保存節點
List<TreeNode> trees=new ArrayList<>();
//保存臨時結果
List<Integer> temp;
if(root!=null){
trees.add(root);
while(!trees.isEmpty()){
//計算當前層節點數,按數量全部出隊,再將其左右節點依次入隊
int n=trees.size();
temp=new ArrayList<>();
for(int i=0;i<n;i++){
TreeNode remove=trees.remove(0);
temp.add(remove.val);
//出隊節點的左右節點不爲空就入隊
if(remove.left!=null)
trees.add(remove.left);
if(remove.right!=null)
trees.add(remove.right);
}
//添加到結果集
result.add(temp);
}
}
return result;
}
}
103. 二叉樹的鋸齒形層次遍歷
給定一個二叉樹,返回其節點值的鋸齒形層次遍歷。(即先從左往右,再從右往左進行下一層遍歷,以此類推,層與層之間交替進行)。
例如:
給定二叉樹 [3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回鋸齒形層次遍歷如下:
[
[3],
[20,9],
[15,7]
]
解答:
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
//相比102題只需要添加一個boolean變量
List<List<Integer>> result=new ArrayList<>();
List<TreeNode> trees=new ArrayList<>();
List<Integer> temp;
//控制順序,是否從左到右
boolean leftToRight=true;
if(root!=null){
trees.add(root);
while(!trees.isEmpty()){
int n=trees.size();
temp=new ArrayList<>();
for(int i=0;i<n;i++){
TreeNode remove=trees.remove(0);
temp.add(remove.val);
if(remove.left!=null)
trees.add(remove.left);
if(remove.right!=null)
trees.add(remove.right);
}
//如果是從右往左要逆序
if(!leftToRight)
Collections.reverse(temp);
result.add(temp);
leftToRight=!leftToRight;
}
}
return result;
}
104. 二叉樹的最大深度
給定一個二叉樹,找出其最大深度。
二叉樹的深度爲根節點到最遠葉子節點的最長路徑上的節點數。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定二叉樹 [3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
解答:
public int maxDepth(TreeNode root) {
//空樹高度爲0
if(root==null)
return 0;
//左子樹高度
int hL=maxDepth(root.left);
//右子樹高度
int hR=maxDepth(root.right);
//返回左右子樹的最大高度加上自己的高度1
return Math.max(hL,hR)+1;
}
105. 從前序與中序遍歷序列構造二叉樹
難度中等426
根據一棵樹的前序遍歷與中序遍歷構造二叉樹。
注意:
你可以假設樹中沒有重複的元素。
例如,給出
前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]
返回如下的二叉樹:
3
/ \
9 20
/ \
15 7
解答:
class Solution {
//存儲中序信息,key是節點數值,value是索引
HashMap<Integer,Integer> map=new HashMap<>();
//存儲前序數組的引用
int[] pre;
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder==null||preorder.length==0)
return null;
pre=preorder;
//將中序信息存入map集合
for(int i=0;i<inorder.length;i++){
map.put(inorder[i],i);
}
return build(0,0,inorder.length-1);
}
//從前序遍歷中獲得當前節點,從中序遍歷獲得其左右節點
public TreeNode build(int preIndex,int inStart,int inEnd){
//臨界條件
if(inStart>inEnd)
return null;
//獲取先序遍歷的節點以及其在中序遍歷中的位置
int val=pre[preIndex];
int inIndex=map.get(val);
//構建根節點
TreeNode cur=new TreeNode(val);
//左子樹數量
int left=inIndex-inStart;
//遞歸構建左右子樹
cur.left=build(preIndex+1,inStart,inIndex-1);//前序遍歷中當前節點下一個就是左子樹節點
cur.right=build(preIndex+1+left,inIndex+1,inEnd);//前序中右子樹的根節點索引要跳過中序左子樹節點的數量
return cur;
}
}
106. 從中序與後序遍歷序列構造二叉樹
根據一棵樹的中序遍歷與後序遍歷構造二叉樹。
注意:
你可以假設樹中沒有重複的元素。
例如,給出
中序遍歷 inorder = [9,3,15,20,7]
後序遍歷 postorder = [9,15,7,20,3]
返回如下的二叉樹:
3
/ \
9 20
/ \
15 7
解答:
class Solution {
//存儲中序信息,key是節點數值,value是索引
HashMap<Integer,Integer> map=new HashMap<>();
//存儲後序數組的引用
int[] post;
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(postorder==null||postorder.length==0)
return null;
post=postorder;
//將中序信息存入map集合
for(int i=0;i<inorder.length;i++){
map.put(inorder[i],i);
}
return build(post.length-1,0,inorder.length-1);
}
//從後序遍歷中獲得當前節點,從中序遍歷獲得其左右節點
public TreeNode build(int postIndex,int inStart,int inEnd){
//臨界條件
if(inStart>inEnd)
return null;
//獲取後序遍歷的節點以及其在中序遍歷中的位置
int val=post[postIndex];
int inIndex=map.get(val);
//構建根節點
TreeNode cur=new TreeNode(val);
//右子樹數量
int right=inEnd-inIndex;
//遞歸構建左右子樹
cur.left=build(postIndex-1-right,inStart,inIndex-1);//後序遍歷中當前節點前一個就是右子樹節點
cur.right=build(postIndex-1,inIndex+1,inEnd);//後序中左子樹的根節點索引要跳過中序右子樹節點的數量
return cur;
}
}
107. 二叉樹的層次遍歷 II
給定一個二叉樹,返回其節點值自底向上的層次遍歷。 (即按從葉子節點所在層到根節點所在的層,逐層從左向右遍歷)
例如:
給定二叉樹 [3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回其自底向上的層次遍歷爲:
[
[15,7],
[9,20],
[3]
]
解答:
public List<List<Integer>> levelOrderBottom(TreeNode root) {
//保存結果集
LinkedList<List<Integer>> result=new LinkedList<>();
//保存節點
List<TreeNode> trees=new LinkedList<>();
//保存臨時結果
List<Integer> temp;
if(root!=null){
trees.add(root);
while(!trees.isEmpty()){
//計算當前層節點數,按數量全部出隊,再將其左右節點依次入隊
int n=trees.size();
temp=new LinkedList<>();
for(int i=0;i<n;i++){
TreeNode remove=trees.remove(0);
temp.add(remove.val);
//出隊節點的左右節點不爲空就入隊
if(remove.left!=null)
trees.add(remove.left);
if(remove.right!=null)
trees.add(remove.right);
}
//自底向上遍歷,將自頂向下的結果反向入隊即可
result.addFirst(temp);
}
}
return result;
}
108. 將有序數組轉換爲二叉搜索樹
將一個按照升序排列的有序數組,轉換爲一棵高度平衡二叉搜索樹。
本題中,一個高度平衡二叉樹是指一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過 1。
示例:
給定有序數組: [-10,-3,0,5,9],
一個可能的答案是:[0,-3,9,-10,null,5],它可以表示下面這個高度平衡二叉搜索樹:
0
/ \
-3 9
/ /
-10 5
解答:
public TreeNode sortedArrayToBST(int[] nums) {
if(nums.length==0)
return null;
return insert(nums,0,nums.length-1);
}
public TreeNode insert(int[] nums,int left,int right){
//遞歸停止條件
if(left>right)
return null;
//將中間節點作爲根節點
int mid=(left+right)/2;
TreeNode root=new TreeNode(nums[mid]);
//中間節點左邊作爲左子樹,因爲是升序數組,左邊肯定值更小
root.left=insert(nums,left,mid-1);
//中間節點右邊作爲右子樹
root.right=insert(nums,mid+1,right);
return root;
}
110. 平衡二叉樹
給定一個二叉樹,判斷它是否是高度平衡的二叉樹。
本題中,一棵高度平衡二叉樹定義爲:
一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過1。
示例 1:
給定二叉樹 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true
。
解答:
public boolean isBalanced(TreeNode root) {
//空樹是平衡二叉樹
if(root==null)
return true;
//左右子樹的高度差超過1
if(Math.abs(depth(root.left)-depth(root.right))>1)
return false;
//判斷左右子樹
return isBalanced(root.left)&&isBalanced(root.right);
}
//求當前節點高度
public int depth(TreeNode root) {
if(root==null)
return 0;
int hL=depth(root.left);
int hR=depth(root.right);
return Math.max(hL,hR)+1;
}
111. 二叉樹的最小深度
給定一個二叉樹,找出其最小深度。
最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定二叉樹 [3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回它的最小深度 2.
解答:
public int minDepth(TreeNode root) {
//空節點高度0
if(root==null)
return 0;
//沒有左右節點高度1
if(root.left==null&&root.right==null)
return 1;
int hL=minDepth(root.left);
int hR=minDepth(root.right);
//沒有左節點,高度爲右節點,反之
if(hL==0)
return hR+1;
if(hR==0)
return hL+1;
//左右節點都不爲空,返回最小值加上自己的高度1
return Math.min(hL,hR)+1;
}
112. 路徑總和
給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定如下二叉樹,以及目標和 sum = 22
,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true
, 因爲存在目標和爲 22 的根節點到葉子節點的路徑 5->4->11->2
。
解答:
public boolean hasPathSum(TreeNode root, int sum) {
//當前節點爲空,尋找失敗
if(root==null)
return false;
//當前節點爲葉子節點並且值和目標相等,尋找成功
if(root.val==sum&&root.left==null&&root.right==null)
return true;
//在左右子樹尋找
return hasPathSum(root.left,sum-root.val)||hasPathSum(root.right,sum-root.val);
}
113. 路徑總和 II
難度中等213
給定一個二叉樹和一個目標和,找到所有從根節點到葉子節點路徑總和等於給定目標和的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定如下二叉樹,以及目標和 sum = 22
,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
解答:
class Solution {
//保存結果
List<List<Integer>> result=new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int sum) {
path(root,sum,new ArrayList<>());
return result;
}
public void path(TreeNode root,int sum,List<Integer> list){
if(root!=null){
list.add(root.val);
//如果是葉子節點且值和sum相等,說明找到了
if(root.val==sum&&root.left==null&&root.right==null){
result.add(new ArrayList<>(list));
list.remove(list.size()-1);
}else{
//在左子樹和右子樹分別繼續查找
path(root.left,sum-root.val,list);
path(root.right,sum-root.val,list);
//查完後要移除末尾元素,回退之前的狀態,進行新的查找
list.remove(list.size()-1);
}
}
}
}
114. 二叉樹展開爲鏈表
給定一個二叉樹,原地將它展開爲鏈表。
例如,給定二叉樹
1
/ \
2 5
/ \ \
3 4 6
將其展開爲:
1
\
2
\
3
\
4
\
5
\
6
解答:
116. 填充每個節點的下一個右側節點指針
給定一個完美二叉樹,其所有葉子節點都在同一層,每個父節點都有兩個子節點。二叉樹定義如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每個 next 指針,讓這個指針指向其下一個右側節點。如果找不到下一個右側節點,則將 next 指針設置爲 NULL
。初始狀態下,所有 next 指針都被設置爲 NULL
。
示例:
解答:
public Node connect(Node root) {
if(root!=null){
//如果還有下一層(只需要判斷左右節點的一個,因爲是完美二叉樹)
if(root.left!=null){
//連接同一個父節點的兩個節點
root.left.next=root.right;
//連接不同父節點的兩個節點
if(root.next!=null)
root.right.next=root.next.left;
}
connect(root.left);
connect(root.right);
}
return root;
}
117. 填充每個節點的下一個右側節點指針 II
給定一個二叉樹
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每個 next 指針,讓這個指針指向其下一個右側節點。如果找不到下一個右側節點,則將 next 指針設置爲 NULL
。初始狀態下,所有 next 指針都被設置爲 NULL
。
示例:
輸入:root = [1,2,3,4,5,null,7]
輸出:[1,#,2,3,#,4,5,7,#]
解釋:給定二叉樹如圖 A 所示,你的函數應該填充它的每個 next 指針,以指向其下一個右側節點,如圖 B 所示。
解答:
public Node connect(Node root) {
//使用隊列進行層序遍歷,依次處理節點
Deque<Node> trees=new ArrayDeque<>();
if(root!=null){
trees.add(root);
while(!trees.isEmpty()){
int n=trees.size();
for(int i=0;i<n;i++){
Node remove=trees.removeFirst();
help(remove);
if(remove.left!=null)
trees.add(remove.left);
if(remove.right!=null)
trees.add(remove.right);
}
}
}
return root;
}
public void help(Node root){
if(root!=null){
if(root.left!=null&&root.right!=null)
root.left.next=root.right;
if(root.left!=null||root.right!=null) {
//保存當前節點的next節點、左右節點引用,使代碼簡潔一點
Node node=root.next;
Node rL=root.left;
Node rR=root.right;
//只要存在next節點
while (node != null) {
if (rR != null) {//當前節點優先連接右節點
if (node.left != null) {//next節點優先連接左節點
rR.next = node.left;
break;
} else if (node.right != null) {//next節點沒有左節點,判斷右節點
rR.next = node.right;
break;
}
}
//當前節點沒有右節點,判斷左節點
if (node.left != null) {//next節點優先連接左節點
rL.next = node.left;
break;
} else if (node.right != null) {//next節點沒有左節點,判斷右節點
rL.next = node.right;
break;
}
node = node.next;
}
}
}
}
129. 求根到葉子節點數字之和
給定一個二叉樹,它的每個結點都存放一個 0-9
的數字,每條從根到葉子節點的路徑都代表一個數字。
例如,從根到葉子節點路徑 1->2->3
代表數字 123
。
計算從根到葉子節點生成的所有數字之和。
說明: 葉子節點是指沒有子節點的節點。
示例 1:
輸入: [1,2,3]
1
/ \
2 3
輸出: 25
解釋:
從根到葉子節點路徑 1->2 代表數字 12.
從根到葉子節點路徑 1->3 代表數字 13.
因此,數字總和 = 12 + 13 = 25.
解答:
class Solution {
//保存結果
int sum=0;
//類似257題
public int sumNumbers(TreeNode root) {
search(root,0);
return sum;
}
//輔助函數
public void search(TreeNode root,int n){
if(root!=null){
int num=n*10+root.val;
if(root.left==null&&root.right==null) {
//到達葉子節點,拼接完值就添加到結果集
sum+=num;
}else {
//還沒到葉子節點
search(root.left, num);
search(root.right, num);
}
}
}
}
144. 二叉樹的前序遍歷
給定一個二叉樹,返回它的 前序 遍歷。
示例:
輸入: [1,null,2,3]
1
\
2
/
3
輸出: [1,2,3]
解答:
class Solution {
//保存結果
List<Integer> result=new ArrayList<>();
//遞歸很簡單,直接使用迭代算法
public List<Integer> preorderTraversal(TreeNode root) {
if(root!=null){
Stack<TreeNode> stack=new Stack<>();
while(!stack.empty()||root!=null){
if(root!=null){
result.add(root.val);
stack.push(root);
root=root.left;
}else{
TreeNode pop=stack.pop();
root=pop.right;
}
}
}
return result;
}
}
145. 二叉樹的後序遍歷
給定一個二叉樹,返回它的 後序 遍歷。
示例:
輸入: [1,null,2,3]
1
\
2
/
3
輸出: [3,2,1]
進階: 遞歸算法很簡單,你可以通過迭代算法完成嗎?
解答:
//類似二叉樹先序遍歷,只是利用隊列將結果逆序
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> result=new LinkedList<>();
if(root!=null){
Stack<TreeNode> stack=new Stack<>();
while(!stack.empty()||root!=null){
if(root!=null){
result.addFirst(root.val);
stack.push(root);
root=root.right;
}else{
TreeNode pop=stack.pop();
root=pop.left;
}
}
}
return result;
}
156. 上下翻轉二叉樹
給定一個二叉樹,其中所有的右節點要麼是具有兄弟節點(擁有相同父節點的左節點)的葉節點,要麼爲空,將此二叉樹上下翻轉並將它變成一棵樹, 原來的右節點將轉換成左葉節點。返回新的根。
例子:
輸入: [1,2,3,4,5]
1
/ \
2 3
/ \
4 5
輸出: 返回二叉樹的根 [4,5,2,#,#,3,1]
4
/ \
5 2
/ \
3 1
解答:
public TreeNode upsideDownBinaryTree(TreeNode root) {
//root是空直接返回
if(root==null)
return root;
if(root!=null){
//左節點爲空說明到了葉子節點,直接返回
if(root.left==null)
return root;
//否則新節點是對左節點翻轉的結果
TreeNode newRoot=upsideDownBinaryTree(root.left);
//找到新節點的最右子結點
TreeNode n=newRoot;
while(n.right!=null)
n=n.right;
//原節點的右節點作爲新節點的左節點
n.left=root.right;
//原節點作爲新節點的右節點
n.right=new TreeNode(root.val);
return newRoot;
}
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
解答:
class BSTIterator {
List<Integer> list=new ArrayList<>();
int[] arr=null;
int index=0;
public BSTIterator(TreeNode root) {
inorder(root);
arr=new int[list.size()];
for(int i=0;i<arr.length;i++)
arr[i]=list.get(i);
}
public int next() {
return arr[index++];
}
public boolean hasNext() {
return index<arr.length;
}
//中序遍歷保存升序序列
public void inorder(TreeNode root){
if(root!=null){
inorder(root.left);
list.add(root.val);
inorder(root.right);
}
}
}
199. 二叉樹的右視圖
給定一棵二叉樹,想象自己站在它的右側,按照從頂部到底部的順序,返回從右側所能看到的節點值。
示例:
輸入: [1,2,3,null,5,null,4]
輸出: [1, 3, 4]
解釋:
1 <---
/ \
2 3 <---
\ \
5 4 <---
解答:
class Solution {
List<Integer> result=new ArrayList<>();
public List<Integer> rightSideView(TreeNode root) {
//初始結果集大小0,所以深度也該設爲0
dfs(root,0);
return result;
}
//使用根-右-左的順序進行深度優先遍歷
public void dfs(TreeNode root,int depth){
if(root!=null){
//根據深度和當前結果集大小判斷是否是第一次訪問,即該層最右節點
if(depth==result.size())
result.add(root.val);
dfs(root.right,depth+1);
dfs(root.left,depth+1);
}
}
}
222. 完全二叉樹的節點個數
給出一個完全二叉樹,求出該樹的節點個數。
解答:
public int countNodes(TreeNode root) {
//空樹節點0
if(root==null)
return 0;
//沒有左子樹肯定也沒有右子樹
if(root.left==null)
return 1;
//左右子樹節點+1
return 1+countNodes(root.left)+countNodes(root.right);
}
226. 翻轉二叉樹
翻轉一棵二叉樹。
示例:
輸入:
4
/ \
2 7
/ \ / \
1 3 6 9
輸出:
4
/ \
7 2
/ \ / \
9 6 3 1
解答:
public TreeNode invertTree(TreeNode root) {
if(root!=null){
//翻轉左子樹賦值給當前節點的右節點,反之
TreeNode newLeft=invertTree(root.right);
TreeNode newRight=invertTree(root.left);
root.left=newLeft;
root.right=newRight;
}
return root;
}
230. 二叉搜索樹中第K小的元素
給定一個二叉搜索樹,編寫一個函數 kthSmallest
來查找其中第 k 個最小的元素。
說明:
你可以假設 k 總是有效的,1 ≤ k ≤ 二叉搜索樹元素個數。
示例 1:
輸入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
輸出: 1
解答:
class Solution {
//定義一個計數器
int count=0;
//保存結果
int num=0;
public int kthSmallest(TreeNode root, int k) {
count=k;
help(root);
return num;
}
//按中序遍歷搜索,第k個值就是第k小
public void help(TreeNode root){
if(root!=null){
help(root.left);
count--;
if(count==0){
num=root.val;
return;
}
help(root.right);
}
}
}
235. 二叉搜索樹的最近公共祖先
給定一個二叉搜索樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”
例如,給定如下二叉搜索樹: root = [6,2,8,0,4,7,9,null,null,3,5]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-X65GZ12A-1588937561384)(https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/14/binarysearchtree_improved.png)]
示例 1:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
輸出: 6
解釋: 節點 2 和節點 8 的最近公共祖先是 6。
解答:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//p、q的值都小於root,在左子樹尋找
if(root.val>p.val&&root.val>q.val)
return lowestCommonAncestor(root.left,p,q);
//p、q的值都大於root,在右子樹尋找
if(root.val<p.val&&root.val<q.val)
return lowestCommonAncestor(root.right,p,q);
//不是左右子樹,只能是根節點
return root;
}
236. 二叉樹的最近公共祖先
給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”
例如,給定如下二叉樹: root = [3,5,1,6,2,0,8,null,null,7,4]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KYT66fwZ-1588937561386)(https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/15/binarytree.png)]
示例 1:
輸入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
輸出: 3
解釋: 節點 5 和節點 1 的最近公共祖先是節點 3。
解答:
class Solution {
//保存結果
TreeNode res;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
canFind(root,p,q);
return res;
}
//以當前節點爲根搜索,能否找到p,q中的一個
public boolean canFind(TreeNode root,TreeNode p,TreeNode q){
if(root==null)
return false;
int sum=0;
//判斷當前節點
sum+=(root==p||root==q)?1:0;
//判斷左子樹能否找到
sum+=canFind(root.left,p,q)?1:0;
//判斷右子樹能否找到
sum+=canFind(root.right,p,q)?1:0;
//如果sum==2,說明p和q都找到了
if(sum==2)
res=root;
//sum不爲0,說明能找到其中一個
return sum>0;
}
}
250. 統計同值子樹
給定一個二叉樹,統計該二叉樹數值相同的子樹個數。
同值子樹是指該子樹的所有節點都擁有相同的數值。
示例:
輸入: root = [5,1,5,5,5,null,5]
5
/ \
1 5
/ \ \
5 5 5
輸出: 4
解答:
class Solution {
//保存同值子樹總和
int sum=0;
public int countUnivalSubtrees(TreeNode root) {
isSub(root);
return sum;
}
public boolean isSub(TreeNode root){
if(root==null)
return true;
boolean b1=isSub(root.left);//左子樹是否爲同值子樹
boolean b2=isSub(root.right);//右子樹是否爲同值子樹
//根節點和左右任意一個節點值不同就不是
if(root.left!=null&&root.left.val!=root.val)
return false;
if(root.right!=null&&root.right.val!=root.val)
return false;
//如果都滿足,加1
if(b1&&b2)
sum++;
return b1&&b2;
}
}
257. 二叉樹的所有路徑
給定一個二叉樹,返回所有從根節點到葉子節點的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
輸入:
1
/ \
2 3
\
5
輸出: ["1->2->5", "1->3"]
解釋: 所有根節點到葉子節點的路徑爲: 1->2->5, 1->3
解答:
class Solution {
//保存結果集
List<String> res=new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
search(root,new StringBuilder());
return res;
}
//輔助函數,利用StringBuilder拼接
public void search(TreeNode root,StringBuilder stringBuilder){
if(root!=null){
//每次創建一個新的StringBuilder,將上次的值作爲拼接初始值
StringBuilder sb = new StringBuilder(stringBuilder);
if(root.left==null&&root.right==null) {
//到達葉子節點,拼接完值就添加到結果集
sb.append(root.val);
res.add(sb.toString());
}else {
//還沒到葉子節點,拼接值之後加一個->
sb.append(root.val).append("->");
search(root.left, sb);
search(root.right, sb);
}
}
}
}
270. 最接近的二叉搜索樹值
給定一個不爲空的二叉搜索樹和一個目標值 target,請在該二叉搜索樹中找到最接近目標值 target 的數值。
注意:
- 給定的目標值 target 是一個浮點數
- 題目保證在該二叉搜索樹中只會存在一個最接近目標值的數
示例:
輸入: root = [4,2,5,1,3],目標值 target = 3.714286
4
/ \
2 5
/ \
1 3
輸出: 4
解答:
class Solution {
//保存最接近的值
Integer num=null;
public int closestValue(TreeNode root, double target) {
if(root!=null){
//如果最接近的值不存在或有更接近的值就更新
if(num==null||Math.abs(root.val-target)<Math.abs(num-target)){
num=root.val;
}
//在左右子樹繼續尋找
closestValue(root.left,target);
closestValue(root.right,target);
}
return num;
}
}
337. 打家劫舍 III
在上次打劫完一條街道之後和一圈房屋後,小偷又發現了一個新的可行竊的地區。這個地區只有一個入口,我們稱之爲“根”。 除了“根”之外,每棟房子有且只有一個“父“房子與之相連。一番偵察之後,聰明的小偷意識到“這個地方的所有房屋的排列類似於一棵二叉樹”。 如果兩個直接相連的房子在同一天晚上被打劫,房屋將自動報警。
計算在不觸動警報的情況下,小偷一晚能夠盜取的最高金額。
示例 1:
輸入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
輸出: 7
解釋: 小偷一晚能夠盜取的最高金額 = 3 + 3 + 1 = 7.
解答:
class Solution {
HashMap<TreeNode,Integer> map=new HashMap<>();
//計算root節點能偷取到的最大值
public int rob(TreeNode root) {
if(root==null)
return 0;
//避免重複計算,如果計算過直接返回結果
if(map.containsKey(root))
return map.get(root);
//profit1表示當前節點和四個孫子節點能偷的最大值
int profit1=root.val;
if(root.left!=null)
profit1+=rob(root.left.left)+rob(root.left.right);
if(root.right!=null)
profit1+=rob(root.right.left)+rob(root.right.right);
//不偷當前節點,那麼就取兩個孩子節點的最大值
int profit2=rob(root.left)+rob(root.right);
//比較兩種情況的最大值
int max=Math.max(profit1,profit2);
//存儲結果
map.put(root,max);
return max;
}
}
366. 尋找二叉樹的葉子節點
給你一棵二叉樹,請按以下要求的順序收集它的全部節點:
- 依次從左到右,每次收集並刪除所有的葉子節點
- 重複如上過程直到整棵樹爲空
示例:
輸入: [1,2,3,4,5]
1
/ \
2 3
/ \
4 5
輸出: [[4,5,3],[2],[1]]
解答:
class Solution {
List<List<Integer>> res=new ArrayList<>();//保存結果集
List<Integer> list=new ArrayList<>();//保存臨時結果
public List<List<Integer>> findLeaves(TreeNode root) {
if(root==null)
return res;
//仍存在葉子節點則遞歸處理
while(root.left!=null||root.right!=null){
help(root,null,list);
res.add(new ArrayList<>(list));
list.clear();
}
//只剩當前節點
list.add(root.val);
res.add(list);
return res;
}
//輔助方法,刪除所有葉子節點
public void help(TreeNode root,TreeNode pre, List<Integer> list){
if(root!=null){
//左右節點都爲空說明是葉子節點
if(root.left==null&&root.right==null){
if(pre!=null){
if(pre.left==root)//如果是左葉子則刪除
pre.left=null;
else if(pre.right==root)//如果是右葉子則刪除
pre.right=null;
}
list.add(root.val);
return;
}
//不是葉子節點則遞歸處理
help(root.left,root,list);
help(root.right,root,list);
}
}
}
404. 左葉子之和
計算給定二叉樹的所有左葉子之和。
示例:
3
/ \
9 20
/ \
15 7
在這個二叉樹中,有兩個左葉子,分別是 9 和 15,所以返回 24
解答:
public int sumOfLeftLeaves(TreeNode root) {
//空節點值0
if(root==null)
return 0;
int sum=0;
//如果左節點不爲空,左節點的左節點或右節點不爲空,遞歸往左子樹查找
if(root.left!=null&&(root.left.left!=null||root.left.right!=null))
sum+=sumOfLeftLeaves(root.left);
//如果右節點不爲空,右節點的左節點或右節點不爲空,遞歸往右子樹查找
if(root.right!=null&&(root.right.left!=null||root.right.right!=null))
sum+=sumOfLeftLeaves(root.right);
//如果左節點不爲空,左節點是葉子節點,就直接相加
if(root.left!=null&&root.left.left==null&&root.left.right==null)
sum+=root.left.val;
return sum;
}
429. N叉樹的層序遍歷
給定一個 N 叉樹,返回其節點值的層序遍歷。 (即從左到右,逐層遍歷)。
例如,給定一個 3叉樹
:
返回其層序遍歷:
[
[1],
[3,2,4],
[5,6]
]
解答:
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> result=new ArrayList<>();
List<Node> trees=new ArrayList<>();
List<Integer> temp;
if(root!=null){
trees.add(root);
while(!trees.isEmpty()){
int n=trees.size();
temp=new ArrayList<>();
for(int i=0;i<n;i++){
Node remove=trees.remove(0);
temp.add(remove.val);
//和二叉樹的層次遍歷只有此處不同
//遍歷孩子節點,不爲空就入隊
for(Node node:remove.children){
if(node!=null)
trees.add(node);
}
}
result.add(temp);
}
}
return result;
}
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
解答:
public int pathSum(TreeNode root, int sum) {
if(root==null)
return 0;
//結果是當前節點,左節點,右節點分別爲根的結果
return count(root,sum)+pathSum(root.left,sum)+pathSum(root.right,sum);
}
public int count(TreeNode root,int sum){
if(root==null)
return 0;
int res=root.val==sum?1:0;
//在左右子樹繼續尋找
return res+count(root.left,sum-root.val)+count(root.right,sum-root.val);
}
449. 序列化和反序列化二叉搜索樹
序列化是將數據結構或對象轉換爲一系列位的過程,以便它可以存儲在文件或內存緩衝區中,或通過網絡連接鏈路傳輸,以便稍後在同一個或另一個計算機環境中重建。
設計一個算法來序列化和反序列化二叉搜索樹。 對序列化/反序列化算法的工作方式沒有限制。 您只需確保二叉搜索樹可以序列化爲字符串,並且可以將該字符串反序列化爲最初的二叉搜索樹。
編碼的字符串應儘可能緊湊。
注意:不要使用類成員/全局/靜態變量來存儲狀態。 你的序列化和反序列化算法應該是無狀態的。
解答:
public class Codec {
//通過前序遍歷序列化
public String serialize(TreeNode root) {
if(root==null)
return null;
String str=preorder(root,new StringBuilder());
return str;
}
//前序遍歷的字符串
public String preorder(TreeNode root,StringBuilder sb){
if(root!=null){
sb.append(root.val).append(" ");
preorder(root.left,sb);
preorder(root.right,sb);
}
return sb.toString();
}
//反序列化
public TreeNode deserialize(String data) {
if(data==null)
return null;
//雙向隊列
LinkedList<Integer> nums = new LinkedList<>();
//將序列化的數字字符串轉化爲隊列
for(String s : data.split(" "))
nums.add(Integer.parseInt(s));
return helper(Integer.MIN_VALUE, Integer.MAX_VALUE, nums);
}
//前序遍歷反序列化
public TreeNode helper(Integer lower, Integer upper, LinkedList<Integer> nums) {
if (nums.isEmpty())
return null;
//獲取隊首節點值
int val = nums.getFirst();
//小於最小值或大於最大值返回null
if (val < lower || val > upper)
return null;
//移除隊首節點值
nums.removeFirst();
TreeNode root = new TreeNode(val);
//前序遍歷,下一個隊首節點就是當前節點的左節點
root.left = helper(lower, val, nums);
//左子樹構成後,下一個隊首節點就是當前節點的右節點
root.right = helper(val, upper, nums);
return root;
}
}
450. 刪除二叉搜索樹中的節點
給定一個二叉搜索樹的根節點 root 和一個值 key,刪除二叉搜索樹中的 key 對應的節點,並保證二叉搜索樹的性質不變。返回二叉搜索樹(有可能被更新)的根節點的引用。
一般來說,刪除節點可分爲兩個步驟:
- 首先找到需要刪除的節點;
- 如果找到了,刪除它。
說明: 要求算法時間複雜度爲 O(h),h 爲樹的高度。
示例:
root = [5,3,6,2,4,null,7]
key = 3
5
/ \
3 6
/ \ \
2 4 7
給定需要刪除的節點值是 3,所以我們首先找到 3 這個節點,然後刪除它。
一個正確的答案是 [5,4,6,2,null,null,7], 如下圖所示。
5
/ \
4 6
/ \
2 7
另一個正確答案是 [5,2,6,null,4,null,7]。
5
/ \
2 6
\ \
4 7
解答:
//主要思路是替換值,然後將要刪除的節點變爲葉子節點直接刪除
public TreeNode deleteNode(TreeNode root, int key) {
if(root!=null){
if(key<root.val){//key小於當前節點值,在左子樹刪除
root.left=deleteNode(root.left,key);
}else if(key>root.val){//key大於當前節點值,在右子樹刪除
root.right=deleteNode(root.right,key);
}else{
if(root.right!=null){//右子樹不爲空,將節點值替換爲右子樹最小值,再刪除該替換節點
root.val=getRightMin(root);
root.right=deleteNode(root.right,root.val);
}else if(root.left!=null){//左子樹不爲空,將節點值替換爲左子樹最大值,再刪除該替換節點
root.val=getLeftMax(root);
root.left=deleteNode(root.left,root.val);
}else{
root=null;
}
}
}
return root;
}
//找到右子樹的最小節點值
public int getRightMin(TreeNode root){
//從右子樹開始尋找
root=root.right;
//最小值一定在最左節點
while(root.left!=null)
root=root.left;
return root.val;
}
//找到左子樹的最大節點值
public int getLeftMax(TreeNode root){
//從左子樹開始尋找
root=root.left;
//最大值一定在最右節點
while(root.right!=null)
root=root.right;
return root.val;
}
501. 二叉搜索樹中的衆數
給定一個有相同值的二叉搜索樹(BST),找出 BST 中的所有衆數(出現頻率最高的元素)。
假定 BST 有如下定義:
- 結點左子樹中所含結點的值小於等於當前結點的值
- 結點右子樹中所含結點的值大於等於當前結點的值
- 左子樹和右子樹都是二叉搜索樹
例如:
給定 BST [1,null,2,2]
,
1
\
2
/
2
返回[2]
.
提示:如果衆數超過1個,不需考慮輸出順序
解答:
class Solution {
//存放衆數
List<Integer> result=new ArrayList<>();
//前驅節點值
int pre;
//當前節點值計數器
int count;
//當前某節點值出現最多的次數
int max;
public int[] findMode(TreeNode root) {
//空節點,不存在衆數
if(root==null)
return new int[0];
//初始化
pre=root.val;
count=0;
//輔助方法
help(root);
//將結果集中元素輸出
return result.stream().mapToInt(Integer::intValue).toArray();
}
//二叉搜索樹中序是升序序列,所以按中序遍歷
public void help(TreeNode root){
if(root==null)
return;
//遍歷左子樹
help(root.left);
//處理當前節點
if(pre==root.val){//當前節點值=前驅節點值,該值計數器+1
count++;
}else {//若不等於,更新前驅節點值,計數器初始化1
pre=root.val;
count=1;
}
if(count==max){//不止一個衆數的情況
result.add(pre);
}else if(count>max){//當前值出現次數最多,清空值集合並添加
result.clear();
result.add(pre);
max=count;
}
//遍歷右子樹
help(root.right);
}
}
508. 出現次數最多的子樹元素和
給你一個二叉樹的根結點,請你找出出現次數最多的子樹元素和。一個結點的「子樹元素和」定義爲以該結點爲根的二叉樹上所有結點的元素之和(包括結點本身)。
你需要返回出現次數最多的子樹元素和。如果有多個元素出現的次數相同,返回所有出現次數最多的子樹元素和(不限順序)。
示例 1:
輸入:
5
/ \
2 -3
返回 [2, -3, 4],所有的值均只出現一次,以任意順序返回所有值。
解答:
class Solution {
List<Integer> list=new ArrayList<>();
HashMap<Integer,Integer> map=new HashMap<>();//保存子樹和與次數的對應關係
int max=0;//保存最大次數
public int[] findFrequentTreeSum(TreeNode root) {
help(root);
//遍歷次數,如果次數等於最大次數就存入list
Set<Integer> keys = map.keySet();
for(int num:keys){
if(map.get(num)==max)
list.add(num);
}
//list轉爲int[]
int[] res=new int[list.size()];
for(int i=0;i<res.length;i++){
res[i]=list.get(i);
}
return res;
}
public int help(TreeNode root){
if(root==null)
return 0;
//當前子樹和等於自己的值加上左右子樹和
int val=root.val+help(root.left)+help(root.right);
//將子樹和以及對應出現次數存入map
int time=map.getOrDefault(val,0)+1;
map.put(val,time);
//更新出現最多的次數
if(time>max)
max=time;
return val;
}
}
513. 找樹左下角的值
給定一個二叉樹,在樹的最後一行找到最左邊的值。
示例 1:
輸入:
2
/ \
1 3
輸出:
1
解答:
class Solution {
int max=-1;//保存深度
int val=0;//保存最後一層最左節點值
public int findBottomLeftValue(TreeNode root) {
help(root,-1);
return val;
}
public void help(TreeNode root,int depth){
if(root!=null){
depth=depth+1;
//第一次大於最大深度的就是最左節點
if(depth>max){
max=depth;
val=root.val;
}
help(root.left,depth);
help(root.right,depth);
}
}
}
515. 在每個樹行中找最大值
您需要在二叉樹的每一行中找到最大的值。
示例:
輸入:
1
/ \
3 2
/ \ \
5 3 9
輸出: [1, 3, 9]
解答:
class Solution {
int max=-1;//保存最大深度
//key是深度,value是該深度的最大值
HashMap<Integer,Integer> map=new HashMap<>();
public List<Integer> largestValues(TreeNode root) {
List<Integer> list=new ArrayList<>();
help(root,-1);
for(int i=0;i<=max;i++){
list.add(map.get(i));
}
return list;
}
public void help(TreeNode root,int depth){
if(root!=null){
depth=depth+1;
//更新最大深度
if(depth>max){
max=depth;
}
//當前深度還沒有值或比當前值大
if(!map.containsKey(depth)||root.val>map.get(depth))
map.put(depth,root.val);
help(root.left,depth);
help(root.right,depth);
}
}
}
530. 二叉搜索樹的最小絕對差
給你一棵所有節點爲非負值的二叉搜索樹,請你計算樹中任意兩節點的差的絕對值的最小值。
示例:
輸入:
1
\
3
/
2
輸出:
1
解釋:
最小絕對差爲 1,其中 2 和 1 的差的絕對值爲 1(或者 2 和 3)。
解答:
class Solution {
//保存最小值
int min=Integer.MAX_VALUE;
//保存前驅節點值
int pre=0;
//絕對值最小,肯定是相鄰數之間產生,用中序遍歷輔助
public int getMinimumDifference(TreeNode root) {
pre=root.val;
inorder(root);
return min;
}
//中序遍歷,更新差值
public void inorder(TreeNode root){
if(root==null)
return;
inorder(root.left);
if(root.val!=pre){
int temp=Math.abs(root.val-pre);
if(temp<min)
min=temp;
pre=root.val;
}
inorder(root.right);
}
}
538. 把二叉搜索樹轉換爲累加樹
給定一個二叉搜索樹(Binary Search Tree),把它轉換成爲累加樹(Greater Tree),使得每個節點的值是原來的節點值加上所有大於它的節點值之和。
例如:
輸入: 原始二叉搜索樹:
5
/ \
2 13
輸出: 轉換爲累加樹:
18
/ \
20 13
解答:
class Solution {
//保存當前總和
int sum=0;
//從最右節點開始累加,採用逆中序遍歷
public TreeNode convertBST(TreeNode root) {
if(root!=null){
convertBST(root.right);
//累加
sum+=root.val;
//更新當前節點
root.val=sum;
convertBST(root.left);
}
return root;
}
}
543. 二叉樹的直徑
給定一棵二叉樹,你需要計算它的直徑長度。一棵二叉樹的直徑長度是任意兩個結點路徑長度中的最大值。這條路徑可能穿過也可能不穿過根結點。
示例 :
給定二叉樹
1
/ \
2 3
/ \
4 5
返回 3, 它的長度是路徑 [4,2,1,3] 或者 [5,2,1,3]。
**注意:**兩結點之間的路徑長度是以它們之間邊的數目表示。
解答:
class Solution {
//保存最長路徑
int max=0;
public int diameterOfBinaryTree(TreeNode root) {
if(root==null)
return 0;
maxDepth(root);
return max;
}
//相當於求最大深度時多了一行更新左右深度和的最大值更新操作
public int maxDepth(TreeNode root) {
if(root==null)
return 0;
int hL=maxDepth(root.left);
int hR=maxDepth(root.right);
//更新直徑最大值
max=Math.max(max,hL+hR);
return Math.max(hL,hR)+1;
}
}
559. N叉樹的最大深度
給定一個 N 叉樹,找到其最大深度。
最大深度是指從根節點到最遠葉子節點的最長路徑上的節點總數。
例如,給定一個 3叉樹
:
我們應返回其最大深度,3。
說明:
- 樹的深度不會超過
1000
。 - 樹的節點總不會超過
5000
。
解答:
public int maxDepth(Node root) {
if(root==null)
return 0;
//保存子樹節點數最大值
int depth=0;
//在子樹中尋找節點數的最大值,其實和二叉樹最大高度一樣,只是換了一個形式,使用for循環
for(Node node:root.children){
depth=Math.max(depth,maxDepth(node));
}
//返回子樹最大值加自身節點數1
return depth+1;
}
563. 二叉樹的坡度
給定一個二叉樹,計算整個樹的坡度。
一個樹的節點的坡度定義即爲,該節點左子樹的結點之和和右子樹結點之和的差的絕對值。空結點的的坡度是0。
整個樹的坡度就是其所有節點的坡度之和。
示例:
輸入:
1
/ \
2 3
輸出: 1
解釋:
結點的坡度 2 : 0
結點的坡度 3 : 0
結點的坡度 1 : |2-3| = 1
樹的坡度 : 0 + 0 + 1 = 1
解答:
class Solution {
//坡度和
int p=0;
public int findTilt(TreeNode root) {
if(root==null)
return 0;
postorder(root);
return p;
}
//後序遍歷計算某個節點包括自身的節點和,順便計算該節點的坡度值,累加到結果中
public int postorder(TreeNode root){
if(root==null)
return 0;
//左子樹的節點和
int sumL=postorder(root.left);
//右子樹的節點和
int sumR=postorder(root.right);
//累加當前節點坡度
p+=Math.abs(sumL-sumR);
//返回當前節點的節點和
return sumL+sumR+root.val;
}
}
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
返回 false。
解答:
public boolean isSubtree(TreeNode s, TreeNode t) {
if(s==null)
return false;
//以當前節點爲根,或者在左右子樹中繼續尋找
return isSame(s,t)||isSubtree(s.left,t)||isSubtree(s.right,t);
}
//判斷兩個分別以s和t作爲根節點的樹是否相同
public boolean isSame(TreeNode s,TreeNode t){
//都爲空爲true
if(s==null&&t==null)
return true;
//其中一個爲空另一個不爲空爲false
if((s!=null&&t==null)||(s==null&&t!=null))
return false;
//根節點值不同爲false
if(s.val!=t.val)
return false;
//比較對應的左子樹和右子樹
return isSame(s.left,t.left)&&isSame(s.right,t.right);
}
589. N叉樹的前序遍歷
給定一個 N 叉樹,返回其節點值的前序遍歷。
例如,給定一個 3叉樹
:
返回其前序遍歷: [1,3,5,6,2,4]
。
說明: 遞歸法很簡單,你可以使用迭代法完成此題嗎?
遞歸解答:
class Solution {
List<Integer> list=new ArrayList<>();
public List<Integer> preorder(Node root) {
if(root!=null){
list.add(root.val);
for(Node node:root.children){
preorder(node);
}
}
return list;
}
}
迭代解答:
class Solution {
List<Integer> list=new ArrayList<>();
public List<Integer> preorder(Node root) {
Stack<Node> stack=new Stack<>();
if(root!=null)
stack.push(root);
while (!stack.isEmpty()){
//出棧一個節點
Node pop = stack.pop();
//添加當前節點值到結果集
list.add(pop.val);
//將子節點逆序入棧
Collections.reverse(pop.children);
for(Node node:pop.children){
stack.push(node);
}
}
return list;
}
}
590. N叉樹的後序遍歷
難度簡單63
給定一個 N 叉樹,返回其節點值的後序遍歷。
例如,給定一個 3叉樹
:
返回其後序遍歷: [5,6,3,2,4,1]
.
說明: 遞歸法很簡單,你可以使用迭代法完成此題嗎?
遞歸解答:
class Solution {
List<Integer> list=new ArrayList<>();
public List<Integer> postorder(Node root) {
if(root!=null){
for(Node node:root.children){
postorder(node);
}
list.add(root.val);
}
return list;
}
}
迭代解答:
class Solution {
List<Integer> list=new ArrayList<>();
public List<Integer> postorder(Node root) {
Stack<Node> stack=new Stack<>();
if(root!=null)
stack.push(root);
while (!stack.isEmpty()){
//出棧一個節點
Node pop = stack.pop();
//添加當前節點值到結果集
list.add(pop.val);
//將子節點順序入棧
for(Node node:pop.children){
stack.push(node);
}
}
//將結果逆序即爲後序遍歷結果
Collections.reverse(list);
return list;
}
}
606. 根據二叉樹創建字符串
你需要採用前序遍歷的方式,將一個二叉樹轉換成一個由括號和整數組成的字符串。
空節點則用一對空括號 “()” 表示。而且你需要省略所有不影響字符串與原始二叉樹之間的一對一映射關係的空括號對。
示例 1:
輸入: 二叉樹: [1,2,3,4]
1
/ \
2 3
/
4
輸出: "1(2(4))(3)"
解釋: 原本將是“1(2(4)())(3())”,
在你省略所有不必要的空括號對之後,
它將是“1(2(4))(3)”。
解答:
public String tree2str(TreeNode t) {
if(t==null)
return "";
StringBuilder sb=new StringBuilder();
sb.append(t.val);
//左右子樹任意一個不爲空就要添加左子樹
if(t.left!=null||t.right!=null)
sb.append("(").append(tree2str(t.left)).append(")");
//右子樹空可以省略
if(t.right!=null)
sb.append("(").append(tree2str(t.right)).append(")");
return sb.toString();
}
617. 合併二叉樹
給定兩個二叉樹,想象當你將它們中的一個覆蓋到另一個上時,兩個二叉樹的一些節點便會重疊。
你需要將他們合併爲一個新的二叉樹。合併的規則是如果兩個節點重疊,那麼將他們的值相加作爲節點合併後的新值,否則不爲 NULL 的節點將直接作爲新二叉樹的節點。
示例 1:
輸入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
輸出:
合併後的樹:
3
/ \
4 5
/ \ \
5 4 7
注意: 合併必須從兩個樹的根節點開始。
解答:
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
//t1是空將t2作爲新節點
if(t1==null)
return t2;
//t1、t2都不爲空,將t2的節點值加到t1上,然後將合併的左子樹作爲t1的左節點,合併的右子樹作爲t1的右節點
if(t2!=null){
t1.val+=t2.val;
t1.left=mergeTrees(t1.left,t2.left);
t1.right=mergeTrees(t1.right,t2.right);
}
return t1;
}
623. 在二叉樹中增加一行
給定一個二叉樹,根節點爲第1層,深度爲 1。在其第 d
層追加一行值爲 v
的節點。
添加規則:給定一個深度值 d
(正整數),針對深度爲 d-1
層的每一非空節點 N
,爲 N
創建兩個值爲 v
的左子樹和右子樹。
將 N
原先的左子樹,連接爲新節點 v
的左子樹;將 N
原先的右子樹,連接爲新節點 v
的右子樹。
如果 d
的值爲 1,深度 d - 1 不存在,則創建一個新的根節點 v
,原先的整棵樹將作爲 v
的左子樹。
示例 1:
輸入:
二叉樹如下所示:
4
/ \
2 6
/ \ /
3 1 5
v = 1
d = 2
輸出:
4
/ \
1 1
/ \
2 6
/ \ /
3 1 5
解答:
public TreeNode addOneRow(TreeNode root, int v, int d) {
//d爲1的特殊情況,創建根節點,直接將原先的樹作爲左子樹
if(d==1){
TreeNode newRoot=new TreeNode(v);
newRoot.left=root;
return newRoot;
}
add(root,v,1,d);
return root;
}
//當depth=d-1時添加樹
public void add(TreeNode root,int v,int depth,int d){
//當前節點爲空不用再繼續找了
if(root==null)
return;
//達到目標深度
if(depth==d-1){
TreeNode v1=new TreeNode(v);
v1.left=root.left;
root.left=v1;
TreeNode v2=new TreeNode(v);
v2.right=root.right;
root.right=v2;
}else{//繼續找
add(root.left,v,depth+1,d);
add(root.right,v,depth+1,d);
}
}
637. 二叉樹的層平均值
給定一個非空二叉樹, 返回一個由每層節點平均值組成的數組.
示例 1:
輸入:
3
/ \
9 20
/ \
15 7
輸出: [3, 14.5, 11]
解釋:
第0層的平均值是 3, 第1層是 14.5, 第2層是 11. 因此返回 [3, 14.5, 11].
解答:
public List<Double> averageOfLevels(TreeNode root) {
//結果集
List<Double> result=new ArrayList<>();
//節點隊列
List<TreeNode> list=new ArrayList<>();
//頭結點入隊
list.add(root);
//保存每一層的節點和
double sum=0;
while(!list.isEmpty()){
sum=0;//初始化和
//每一層的節點數就是當前隊列元素數
int n=list.size();
for(int i=0;i<n;i++){
TreeNode remove=list.remove(0);
sum+=remove.val;//累加該層節點值
//如果有左右節點要入隊
if(remove.left!=null)
list.add(remove.left);
if(remove.right!=null)
list.add(remove.right);
}
//該層平均值
sum=sum/n;
result.add(sum);
}
return result;
}
652. 尋找重複的子樹
給定一棵二叉樹,返回所有重複的子樹。對於同一類的重複子樹,你只需要返回其中任意一棵的根結點即可。
兩棵樹重複是指它們具有相同的結構以及相同的結點值。
示例 1:
1
/ \
2 3
/ / \
4 2 4
/
4
下面是兩個重複的子樹:
2
/
4
和
4
因此,你需要以列表的形式返回上述重複子樹的根結點。
解答:
class Solution {
//全局變量用於提供唯一id
int n=1;
//保存內容和對應序號id的關係,內容相同id相同
HashMap<String,Integer> map1=new HashMap<>();
//保存唯一序號id和出現次數的關係,key是id,value是次數
HashMap<Integer,Integer> map2=new HashMap<>();
//結果集
List<TreeNode> list=new ArrayList<>();
public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
if(root!=null)
getID(root);
return list;
}
public int getID(TreeNode root){
if(root==null)
return 0;
//當前節點信息(轉爲字符串)
String unique=""+root.val+getID(root.left)+getID(root.right);
//計算唯一id
int id=map1.computeIfAbsent(unique,u->n++);
//獲取出現次數
int time=map2.getOrDefault(id,0)+1;
map2.put(id,time);
//只有第二次出現時添加,避免重複
if(time==2)
list.add(root);
return id;
}
}
653. 兩數之和 IV - 輸入 BST
給定一個二叉搜索樹和一個目標結果,如果 BST 中存在兩個元素且它們的和等於給定的目標結果,則返回 true。
案例 1:
輸入:
5
/ \
3 6
/ \ \
2 4 7
Target = 9
輸出: True
解答:
class Solution {
//保存節點值
HashSet<Integer> set=new HashSet<>();
public boolean findTarget(TreeNode root, int k) {
if(root!=null){
//如果集合存在一個加上當前節點值和爲k的值,就說明存在
if(set.contains(k-root.val))
return true;
//如果不存在,添加當前值,在左右子樹尋找
set.add(root.val);
return findTarget(root.left, k)||findTarget(root.right, k);
}
return false;
}
}
654. 最大二叉樹
給定一個不含重複元素的整數數組。一個以此數組構建的最大二叉樹定義如下:
- 二叉樹的根是數組中的最大元素。
- 左子樹是通過數組中最大值左邊部分構造出的最大二叉樹。
- 右子樹是通過數組中最大值右邊部分構造出的最大二叉樹。
通過給定的數組構建最大二叉樹,並且輸出這個樹的根節點。
示例 :
輸入:[3,2,1,6,0,5]
輸出:返回下面這棵樹的根節點:
6
/ \
3 5
\ /
2 0
\
1
解答:
public TreeNode constructMaximumBinaryTree(int[] nums) {
return buildTree(nums,0,nums.length-1);
}
public TreeNode buildTree(int[] nums,int start,int end){
if(start>end)//開始大於結尾說明空樹
return null;
else if(start==end)//相同的話直接創建節點返回
return new TreeNode(nums[start]);
else{
int index=getMaxIndex(nums,start,end);//計算當前最大值下標
TreeNode root=new TreeNode(nums[index]);//創建該節點
root.left=buildTree(nums,start,index-1);//下標以左創建左子樹
root.right=buildTree(nums,index+1,end);//下標以右創建右子樹
return root;
}
}
//計算nums數組從start到end範圍內的最大值的索引
public int getMaxIndex(int[] nums,int start,int end){
int index=start;
int max=nums[start];
for(int i=start+1;i<=end;i++){
if(nums[i]>max){
max=nums[i];
index=i;
}
}
return index;
}
662. 二叉樹最大寬度
給定一個二叉樹,編寫一個函數來獲取這個樹的最大寬度。樹的寬度是所有層中的最大寬度。這個二叉樹與**滿二叉樹(full binary tree)**結構相同,但一些節點爲空。
每一層的寬度被定義爲兩個端點(該層最左和最右的非空節點,兩端點間的null
節點也計入長度)之間的長度。
示例 1:
輸入:
1
/ \
3 2
/ \ \
5 3 9
輸出: 4
解釋: 最大值出現在樹的第 3 層,寬度爲 4 (5,3,null,9)。
解答:
class Solution {
//key是深度,value是該深度第一個出現的節點位置即最左節點
HashMap<Integer,Integer> map=new HashMap<>();
//當前最大寬度
int max=0;
public int widthOfBinaryTree(TreeNode root) {
help(root,0,0);
return max;
}
//前序遍歷
public void help(TreeNode root,int depth,int pos){
if(root!=null){
depth=depth+1;//當前深度
//如果當前深度還沒有存入map,說明找到了當前深度最左節點
if(!map.containsKey(depth))
map.put(depth,pos);
int len=pos-map.get(depth)+1;
if(len>max)
max=len;
help(root.left,depth,pos*2);
help(root.right,depth,pos*2+1);
}
}
}
669. 修剪二叉搜索樹
給定一個二叉搜索樹,同時給定最小邊界L
和最大邊界 R
。通過修剪二叉搜索樹,使得所有節點的值在[L, R]
中 (R>=L) 。你可能需要改變樹的根節點,所以結果應當返回修剪好的二叉搜索樹的新的根節點。
示例 1:
輸入:
1
/ \
0 2
L = 1
R = 2
輸出:
1
\
2
解答:
public TreeNode trimBST(TreeNode root, int L, int R) {
if(root!=null){
if(root.val==L) //當前節點=左臨界值,將左子樹剪掉
root.left=null;
else if(root.val==R)//當前節點=右臨界值,將右子樹剪掉
root.right=null;
else if(root.val<L)//當前節點<左臨界值,將右子樹作爲新的左節點
return trimBST(root.right,L,R);
else if(root.val>R)//當前節點>右臨界值,將左子樹作爲新的右節點
return trimBST(root.left,L,R);
if(root.left!=null)//左子樹不爲空,修剪
root.left=trimBST(root.left,L,R);
if(root.right!=null)//右子樹不爲空,修剪
root.right=trimBST(root.right,L,R);
}
return root;
}
671. 二叉樹中第二小的節點
給定一個非空特殊的二叉樹,每個節點都是正數,並且每個節點的子節點數量只能爲 2
或 0
。如果一個節點有兩個子節點的話,那麼這個節點的值不大於它的子節點的值。
給出這樣的一個二叉樹,你需要輸出所有節點中的**第二小的值。**如果第二小的值不存在的話,輸出 -1 。
示例 1:
輸入:
2
/ \
2 5
/ \
5 7
輸出: 5
說明: 最小的值是 2 ,第二小的值是 5 。
解答:
public int findSecondMinimumValue(TreeNode root) {
//節點空,或者左右節點都爲空,返回-1
if(root==null||(root.left==null&&root.right==null))
return -1;
//計算左節點的值,如果等於根節點就遞歸在左子樹計算,右節點同理
int left=root.left.val;
if(left==root.val)
left=findSecondMinimumValue(root.left);
int right=root.right.val;
if(right==root.val)
right=findSecondMinimumValue(root.right);
//左右節點值都有效,取較小的
if(left!=-1&&right!=-1)
return Math.min(left,right);
//左邊有效
if(left!=-1)
return left;
//右邊有效
return right;
}
687. 最長同值路徑
給定一個二叉樹,找到最長的路徑,這個路徑中的每個節點具有相同值。 這條路徑可以經過也可以不經過根節點。
注意:兩個節點之間的路徑長度由它們之間的邊數表示。
示例 1:
輸入:
5
/ \
4 5
/ \ \
1 1 5
輸出:
2
解答:
class Solution {
int max=0;//保存最大結果
public int longestUnivaluePath(TreeNode root) {
if(root==null)
return 0;
longPath(root);
return max;
}
//以當前節點值爲相同值,求左右子樹中相同值較多的節點數
public int longPath(TreeNode root){
if(root==null)
return 0;
//求左右子樹中相同節點數
int left=longPath(root.left);
int right=longPath(root.right);
int pathL=0;
int pathR=0;
//如果左節點值和當前值相同,左路徑就是左節點的路徑長+1
if(root.left!=null&&root.val==root.left.val)
pathL=left+1;
//如果右節點值和當前值相同,右路徑就是右節點的路徑長+1
if(root.right!=null&&root.val==root.right.val)
pathR=right+1;
//經過當前節點的最大路徑長,即當前最大路徑長度和當前節點左右路徑長度之和的較大值
max=Math.max(max,pathL+pathR);
//不經過當前節點,返回左右節點中相同節點較多的那個
return Math.max(pathL,pathR);
}
}
700. 二叉搜索樹中的搜索
給定二叉搜索樹(BST)的根節點和一個值。 你需要在BST中找到節點值等於給定值的節點。 返回以該節點爲根的子樹。 如果節點不存在,則返回 NULL。
例如,
給定二叉搜索樹:
4
/ \
2 7
/ \
1 3
和值: 2
你應該返回如下子樹:
2
/ \
1 3
在上述示例中,如果要找的值是 5
,但因爲沒有節點值爲 5
,我們應該返回 NULL
。
解答:
public TreeNode searchBST(TreeNode root, int val) {
if(root!=null){
//找到直接返回
if(root.val==val)
return root;
//在左子樹找到就返回,否則返回右子樹的結果
TreeNode res= searchBST(root.left,val);
if(res!=null)
return res;
return searchBST(root.right,val);
}
return root;
}
701. 二叉搜索樹中的插入操作
給定二叉搜索樹(BST)的根節點和要插入樹中的值,將值插入二叉搜索樹。 返回插入後二叉搜索樹的根節點。 保證原始二叉搜索樹中不存在新值。
注意,可能存在多種有效的插入方式,只要樹在插入後仍保持爲二叉搜索樹即可。 你可以返回任意有效的結果。
解答:
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root!=null){
if(val<root.val){
if(root.left==null)
root.left=new TreeNode(val);
else
insertIntoBST(root.left,val);
}else{
if(root.right==null)
root.right=new TreeNode(val);
else
insertIntoBST(root.right,val);
}
}
return root;
}
783. 二叉搜索樹節點最小距離
給定一個二叉搜索樹的根節點 root
,返回樹中任意兩節點的差的最小值。
和530題一樣。
814. 二叉樹剪枝
給定二叉樹根結點 root
,此外樹的每個結點的值要麼是 0,要麼是 1。
返回移除了所有不包含 1 的子樹的原二叉樹。
( 節點 X 的子樹爲 X 本身,以及所有 X 的後代。)
示例1:
輸入: [1,null,0,0,1]
輸出: [1,null,0,null,1]
解釋:
只有紅色節點滿足條件“所有不包含 1 的子樹”。
右圖爲返回的答案。
解答:
public TreeNode pruneTree(TreeNode root) {
if(root!=null){
if(root.val==0&&root.left==null&&root.right==null)
return null;
//左節點滿足條件就將其移除
TreeNode removeL=pruneTree(root.left);
if(removeL==null)
root.left=null;
//右節點滿足條件就將其移除
TreeNode removeR=pruneTree(root.right);
if(removeR==null)
root.right=null;
//左右節點都滿足條件且該節點爲0才滿足
if(root.val==0&&removeL==null&&removeR==null){
return null;
}
}
return root;
}
872. 葉子相似的樹
請考慮一顆二叉樹上所有的葉子,這些葉子的值按從左到右的順序排列形成一個 葉值序列 。
舉個例子,如上圖所示,給定一顆葉值序列爲 (6, 7, 4, 9, 8)
的樹。
如果有兩顆二叉樹的葉值序列是相同,那麼我們就認爲它們是 葉相似 的。
如果給定的兩個頭結點分別爲 root1
和 root2
的樹是葉相似的,則返回 true
;否則返回 false
。
解答:
class Solution {
//保存root1的葉子
List<Integer> res1 = new ArrayList<>();
//保存root2的葉子
List<Integer> res2 = new ArrayList<>();
public boolean leafSimilar(TreeNode root1, TreeNode root2) {
postOrder(root1,res1);
postOrder(root2,res2);
//兩個葉子集合長度不等一定不等
if(res1.size()!=res2.size())
return false;
//按順序比較元素
for (int i=0;i<res1.size();i++){
if(!res1.get(i).equals(res2.get(i)))
return false;
}
return true;
}
//後序遍歷
public void postOrder(TreeNode root,List<Integer> list){
if(root!=null){
postOrder(root.left,list);
postOrder(root.right,list);
//是葉子節點就添加值到集合
if(root.left==null&&root.right==null)
list.add(root.val);
}
}
}
894. 所有可能的滿二叉樹
滿二叉樹是一類二叉樹,其中每個結點恰好有 0 或 2 個子結點。
返回包含 N
個結點的所有可能滿二叉樹的列表。 答案的每個元素都是一個可能樹的根結點。
答案中每個樹的每個結點
都必須有 node.val=0
。
你可以按任何順序返回樹的最終列表。
示例:
輸入:7
輸出:[[0,0,0,null,null,0,0,null,null,0,0],[0,0,0,null,null,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,null,null,null,null,0,0],[0,0,0,0,0,null,null,0,0]]
解釋:
解答:
class Solution {
public List<TreeNode> allPossibleFBT(int n) {
if(n==0)
return new ArrayList<TreeNode>();
return createTree(n);
}
//跟95題類似
public List<TreeNode> createTree(int n){
//保存結果集
List<TreeNode> result=new ArrayList<>();
if(n==1){
result.add(new TreeNode(0));
return result;
}
//用n個節點構造滿二叉樹,除去根節點還有n-1個可用
//左邊1個,右邊n-2個、左邊3個,右邊n-4個、...左邊n-2個,右邊1個
for(int i=1;i<n-1;i+=2){
//左邊節點構造的左子樹
List<TreeNode> listL=createTree(i);
//右邊節點構造的右子樹
List<TreeNode> listR=createTree(n-1-i);
//左右子樹分別取出一個全組和
for(TreeNode nodeL:listL){
for(TreeNode nodeR:listR){
//節點全部爲0
TreeNode root=new TreeNode(0);
root.left=nodeL;
root.right=nodeR;
result.add(root);
}
}
}
return result;
}
}
897. 遞增順序查找樹
給你一個樹,請你 按中序遍歷 重新排列樹,使樹中最左邊的結點現在是樹的根,並且每個結點沒有左子結點,只有一個右子結點。
示例 :
輸入:[5,3,6,2,4,null,8,1,null,null,null,7,9]
5
/ \
3 6
/ \ \
2 4 8
/ / \
1 7 9
輸出:[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]
1
\
2
\
3
\
4
\
5
\
6
\
7
\
8
\
9
解答:
//根節點的前一個結點
TreeNode pre=new TreeNode(-1);
//因爲pre會移動,用cur保存根節點的前一個結點
TreeNode cur=pre;
//中序遍歷
public TreeNode increasingBST(TreeNode root) {
if(root!=null){
increasingBST(root.left);
//pre的右節點值是當前結點的值
pre.right=new TreeNode(root.val);
//pre移動到自己的右節點形成鏈
pre=pre.right;
increasingBST(root.right);
//返回初始結點的下一個即根節點
return cur.right;
}
return root;
}
938. 二叉搜索樹的範圍和
給定二叉搜索樹的根結點 root
,返回 L
和 R
(含)之間的所有結點的值的和。
二叉搜索樹保證具有唯一的值。
示例 1:
輸入:root = [10,5,15,3,7,null,18], L = 7, R = 15
輸出:32
解答:
class Solution {
//保存結果值
int sum=0;
//二叉樹的中序遍歷是遞增序列,所以使用中序序列遍歷
public int rangeSumBST(TreeNode root, int L, int R) {
if(root!=null){
//如果小於L,往左尋找沒有意義
if(root.val>L)
rangeSumBST(root.left,L,R);
if(root.val>=L&&root.val<=R)
sum+=root.val;
//如果大於R,往右尋找沒有意義
if(root.val<R)
rangeSumBST(root.right,L,R);
}
return sum;
}
}
965. 單值二叉樹
如果二叉樹每個節點都具有相同的值,那麼該二叉樹就是單值二叉樹。
只有給定的樹是單值二叉樹時,才返回 true
;否則返回 false
。
示例 1:
輸入:[1,1,1,1,1,null,1]
輸出:true
解答:
class Solution {
//保存單值
private int unique=0;
public boolean isUnivalTree(TreeNode root) {
if(root==null)
return true;
unique=root.val;
return isSame(root);
}
//當前節點和左右節點值是否一致
public boolean isSame(TreeNode root){
if(root==null)
return true;
if(root.val!=unique)
return false;
return isSame(root.left)&&isSame(root.right);
}
}
993. 二叉樹的堂兄弟節點
在二叉樹中,根節點位於深度 0
處,每個深度爲 k
的節點的子節點位於深度 k+1
處。如果二叉樹的兩個節點深度相同,但父節點不同,則它們是一對堂兄弟節點。我們給出了具有唯一值的二叉樹的根節點 root
,以及樹中兩個不同節點的值 x
和 y
。只有與值 x
和 y
對應的節點是堂兄弟節點時,才返回 true
。否則,返回 false
。
示例 1:
輸入:root = [1,2,3,4], x = 4, y = 3
輸出:false
解答:
class Solution {
int dep=0;
public boolean isCousins(TreeNode root, int x, int y) {
//第1個節點的深度值
depth(root,x,0);
int dx=dep;
//第2個節點的深度值
depth(root,y,0);
int dy=dep;
//深度值不等,返回false
if(dx!=dy)
return false;
//深度值相同,如果父節點不同就返回true,相同返回false
return isSameFather(root,x,y);
}
//計算節點的深度
public void depth(TreeNode root,int val,int depth){
if(root!=null){
if(root.val==val)
dep=depth;
else{
depth(root.left,val,depth+1);
depth(root.right,val,depth+1);
}
}
}
//判斷兩個節點的父節點是否相同
public boolean isSameFather(TreeNode root,int x,int y){
if(root!=null){
//x和y分別是左-右節點,返回false
if(root.left!=null&&root.left.val==x&&root.right!=null&&root.right.val==y)
return false;
//x和y分別是右-左節點,返回false
if(root.left!=null&&root.left.val==y&&root.right!=null&&root.right.val==x)
return false;
//判斷左右子樹
return isSameFather(root.left,x,y)&&isSameFather(root.right,x,y);
}
return true;
}
}
1008. 先序遍歷構造二叉樹
返回與給定先序遍歷 preorder
相匹配的二叉搜索樹(binary search tree)的根結點。
(回想一下,二叉搜索樹是二叉樹的一種,其每個節點都滿足以下規則,對於 node.left
的任何後代,值總 <
node.val
,而 node.right
的任何後代,值總 >
node.val
。此外,先序遍歷首先顯示節點的值,然後遍歷 node.left
,接着遍歷 node.right
。)
示例:
輸入:[8,5,1,7,10,12]
輸出:[8,5,10,1,7,null,12]
解答:
class Solution {
//全局變量,標識當前遍歷的節點索引
int index=0;
public TreeNode bstFromPreorder(int[] preorder) {
return help(preorder,Integer.MIN_VALUE,Integer.MAX_VALUE);
}
public TreeNode help(int[] preorder,int min,int max){
if(index==preorder.length)
return null;
//獲取當前節點值
int val=preorder[index];
//當前節點值不在範圍內
if(val<min||val>max)
return null;
TreeNode root=new TreeNode(val);
index++;//使用了一個節點,繼續下一個
//左節點的值肯定小於val,更新max
root.left=help(preorder,min,val);
//右節點的值肯定大於val,更新min
root.right=help(preorder,val,max);
return root;
}
}
1022. 從根到葉的二進制數之和
給出一棵二叉樹,其上每個結點的值都是 0
或 1
。每一條從根到葉的路徑都代表一個從最高有效位開始的二進制數。例如,如果路徑爲 0 -> 1 -> 1 -> 0 -> 1
,那麼它表示二進制數 01101
,也就是 13
。
對樹上的每一片葉子,我們都要找出從根到該葉子的路徑所表示的數字。
以 10^9 + 7
爲模,返回這些數字之和。
示例:
輸入:[1,0,1,0,1,0,1]
輸出:22
解釋:(100) + (101) + (110) + (111) = 4 + 5 + 6 + 7 = 22
解答:
class Solution {
public int sumRootToLeaf(TreeNode root) {
return add(root,0);
}
public int add(TreeNode root,int num){
if(root!=null){
//左移一位加上當前值
num=(num<<1)+root.val;
//返回累加到根節點的值
if(root.left==null&&root.right==null)
return num;
//返回左右節點值的和
return add(root.left,num)+add(root.right,num);
}
return 0;
}
}
1104. 二叉樹尋路
在一棵無限的二叉樹上,每個節點都有兩個子節點,樹中的節點 逐行 依次按 “之” 字形進行標記。
如下圖所示,在奇數行(即,第一行、第三行、第五行……)中,按從左到右的順序進行標記;
而偶數行(即,第二行、第四行、第六行……)中,按從右到左的順序進行標記。
給你樹上某一個節點的標號 label
,請你返回從根節點到該標號爲 label
節點的路徑,該路徑是由途經的節點標號所組成的。
示例 1:
輸入:label = 14
輸出:[1,3,4,14]
解答:
public List<Integer> pathInZigZagTree(int label) {
LinkedList<Integer> list=new LinkedList<>();
while(label!=0){
list.addFirst(label);
label=label/2;
}
if(list.size()%2==0){
for(int i=0;i<list.size()-1;i+=2){
//該層起始位置
int start = (int) Math.pow(2, i);
//該層偏移位置
int move=(int)Math.pow(2,i+1)-list.get(i)-1;
//正確的值
list.set(i,start+move);
}
}else{
for(int i=1;i<list.size()-1;i+=2){
//該層起始位置
int start = (int) Math.pow(2, i);
//該層偏移位置
int move=(int)Math.pow(2,i+1)-list.get(i)-1;
//正確的值
list.set(i,start+move);
}
}
return list;
}
1261. 在受污染的二叉樹中查找元素
給出一個滿足下述規則的二叉樹:
root.val == 0
- 如果
treeNode.val == x
且treeNode.left != null
,那麼treeNode.left.val == 2 * x + 1
- 如果
treeNode.val == x
且treeNode.right != null
,那麼treeNode.right.val == 2 * x + 2
現在這個二叉樹受到「污染」,所有的 treeNode.val
都變成了 -1
。
請你先還原二叉樹,然後實現 FindElements
類:
FindElements(TreeNode* root)
用受污染的二叉樹初始化對象,你需要先把它還原。bool find(int target)
判斷目標值target
是否存在於還原後的二叉樹中並返回結果。
示例 1:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fKiPB5eR-1588937561406)(https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2019/11/16/untitled-diagram-4-1.jpg)]
輸入:
["FindElements","find","find"]
[[[-1,null,-1]],[1],[2]]
輸出:
[null,false,true]
解釋:
FindElements findElements = new FindElements([-1,null,-1]);
findElements.find(1); // return False
findElements.find(2); // return True
解答:
class FindElements {
HashSet<Integer> set=new HashSet<>();
public FindElements(TreeNode root) {
recover(root,0);
}
public boolean find(int target) {
return set.contains(target);
}
public void recover(TreeNode root,int val){
if(root!=null){
root.val=val;
set.add(val);//將正確的值存儲在set集合
recover(root.left,2*val+1);
recover(root.right,2*val+2);
}
}
}
1302. 層數最深葉子節點的和
給你一棵二叉樹,請你返回層數最深的葉子節點的和。
示例:
輸入:root = [1,2,3,4,5,null,6,7,null,null,null,null,8]
輸出:15
解答:
class Solution {
int max=-1;//保存深度
int sum=0;//保存最後一層的節點和
public int deepestLeavesSum(TreeNode root) {
help(root,0);
return sum;
}
public void help(TreeNode root,int depth){
if(root!=null){
depth=depth+1;
if(depth>max){
max=depth;
sum=root.val;
}else if(depth==max){
sum+=root.val;
}
help(root.left,depth);
help(root.right,depth);
}
}
}
1305. 兩棵二叉搜索樹中的所有元素
給你 root1
和 root2
這兩棵二叉搜索樹。
請你返回一個列表,其中包含 兩棵樹 中的所有整數並按 升序 排序。.
示例 1:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-TcACeY30-1588937561408)(https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2019/12/29/q2-e1.png)]
輸入:root1 = [2,1,4], root2 = [1,0,3]
輸出:[0,1,1,2,3,4]
解答:
class Solution {
List<Integer> list=new ArrayList<>();
public List<Integer> getAllElements(TreeNode root1, TreeNode root2) {
inorder(root1);
inorder(root2);
//排序
Collections.sort(list);
List<Integer> res=new ArrayList<>(list);
return res;
}
//中序遍歷的結果較有序,時間更少
public void inorder(TreeNode root){
if(root!=null){
inorder(root.left);
list.add(root.val);
inorder(root.right);
}
}
}
1315. 祖父節點值爲偶數的節點和
給你一棵二叉樹,請你返回滿足以下條件的所有節點的值之和:
- 該節點的祖父節點的值爲偶數。(一個節點的祖父節點是指該節點的父節點的父節點。)
如果不存在祖父節點值爲偶數的節點,那麼返回 0
。
示例:
輸入:root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5]
輸出:18
解釋:圖中紅色節點的祖父節點的值爲偶數,藍色節點爲這些紅色節點的祖父節點。
解答:
class Solution {
int sum=0;
public int sumEvenGrandparent(TreeNode root) {
if(root==null)
return 0;
if(root.val%2==0){
//左節點不爲空
if(root.left!=null){
sum+=root.left.left==null?0:root.left.left.val;
sum+=root.left.right==null?0:root.left.right.val;
}
//右節點不爲空
if(root.right!=null){
sum+=root.right.left==null?0:root.right.left.val;
sum+=root.right.right==null?0:root.right.right.val;
}
}
sumEvenGrandparent(root.left);
sumEvenGrandparent(root.right);
return sum;
}
}
1325. 刪除給定值的葉子節點
給你一棵以 root
爲根的二叉樹和一個整數 target
,請你刪除所有值爲 target
的 葉子節點 。
注意,一旦刪除值爲 target
的葉子節點,它的父節點就可能變成葉子節點;如果新葉子節點的值恰好也是 target
,那麼這個節點也應該被刪除。
也就是說,你需要重複此過程直到不能繼續刪除。
示例 1:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KsvXWQm3-1588937561409)(https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/01/16/sample_1_1684.png)]
輸入:root = [1,2,3,2,null,2,4], target = 2
輸出:[1,null,3,null,4]
解釋:
上面左邊的圖中,綠色節點爲葉子節點,且它們的值與 target 相同(同爲 2 ),它們會被刪除,得到中間的圖。
有一個新的節點變成了葉子節點且它的值與 target 相同,所以將再次進行刪除,從而得到最右邊的圖。
解答:
//類似814題
public TreeNode removeLeafNodes(TreeNode root, int target) {
if(root!=null){
if(root.val==target&&root.left==null&&root.right==null)
return null;
TreeNode nodeL;
TreeNode nodeR;
if((nodeL=removeLeafNodes(root.left,target))==null)
root.left=null;
if((nodeR=removeLeafNodes(root.right,target))==null)
root.right=null;
if(root.val==target&&nodeL==null&&nodeR==null)
return null;
}
return root;
}
1367. 二叉樹中的列表
給你一棵以 root
爲根的二叉樹和一個 head
爲第一個節點的鏈表。
如果在二叉樹中,存在一條一直向下的路徑,且每個點的數值恰好一一對應以 head
爲首的鏈表中每個節點的值,那麼請你返回 True
,否則返回 False
。
一直向下的路徑的意思是:從樹中某個節點開始,一直連續向下的路徑。
示例 1:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5p4Peesi-1588937561410)(https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/02/29/sample_1_1720.png)]
輸入:head = [4,2,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
輸出:true
解釋:樹中藍色的節點構成了與鏈表對應的子路徑。
解答:
public boolean isSubPath(ListNode head, TreeNode root) {
if(root==null)
return false;
return isSub(head,root)||isSubPath(head, root.left)||isSubPath(head, root.right);
}
//以當前root爲根節點能否匹配
public boolean isSub(ListNode head,TreeNode root){
//鏈表爲空,匹配成功
if(head==null)
return true;
//鏈表不爲空但是root爲空,代表失敗了
if(root==null)
return false;
//節點值不同,失敗
if(root.val!=head.val)
return false;
//當前節點匹配成功,匹配下一個,左右子樹有一邊成功即可
return isSub(head.next,root.left)||isSub(head.next,root.right);
}
1379. 找出克隆二叉樹中的相同節點
給你兩棵二叉樹,原始樹 original
和克隆樹 cloned
,以及一個位於原始樹 original
中的目標節點 target
。
其中,克隆樹 cloned
是原始樹 original
的一個 副本 。
請找出在樹 cloned
中,與 target
相同 的節點,並返回對該節點的引用(在 C/C++ 等有指針的語言中返回 節點指針,其他語言返回節點本身)。
注意:
- 你 不能 對兩棵二叉樹,以及
target
節點進行更改。 - 只能 返回對克隆樹
cloned
中已有的節點的引用。
**進階:**如果樹中允許出現值相同的節點,你將如何解答?
示例 1:
輸入: tree = [7,4,3,null,null,6,19], target = 3
輸出: 3
解釋: 上圖畫出了樹 original 和 cloned。target 節點在樹 original 中,用綠色標記。答案是樹 cloned 中的黃顏色的節點(其他示例類似)。
解答:
public final TreeNode getTargetCopy(final TreeNode original, final TreeNode cloned, final TreeNode target) {
//是克隆樹,一個節點不爲空,另一個一定不爲空
if(original!=null){
if(original==target){
return cloned;
}
//沒找到,分別在左子樹找
TreeNode res=getTargetCopy(original.left,cloned.left,target);
if(res!=null)
return res;
//左子樹也沒有,返回右子樹尋找的結果
return getTargetCopy(original.right,cloned.right,target);
}
return original;
}
1430. Check If a String Is a Valid Sequence from Root to Leaves Path in a Binary Tree
Given a binary tree where each path going from the root to any leaf form a valid sequence, check if a given string is a valid sequence in such binary tree.
We get the given string from the concatenation of an array of integers arr
and the concatenation of all values of the nodes along a path results in a sequence in the given binary tree.
Example 1:
Input: root = [0,1,0,0,1,0,null,null,1,0,0], arr = [0,1,0,1]
Output: true
Explanation:
The path 0 -> 1 -> 0 -> 1 is a valid sequence (green color in the figure).
Other valid sequences are:
0 -> 1 -> 1 -> 0
0 -> 0 -> 0
Example 2:
Input: root = [0,1,0,0,1,0,null,null,1,0,0], arr = [0,0,1]
Output: false
Explanation: The path 0 -> 0 -> 1 does not exist, therefore it is not even a sequence.
Example 3:
Input: root = [0,1,0,0,1,0,null,null,1,0,0], arr = [0,1,1]
Output: false
Explanation: The path 0 -> 1 -> 1 is a sequence, but it is not a valid sequence.
解答:
public boolean isValidSequence(TreeNode root, int[] arr) {
return isValid(root,arr,0);
}
//輔助函數,前序遍歷dfs
public boolean isValid(TreeNode root,int[] arr,int index){
if(root==null||index==arr.length)//節點空或數組匹配完成但節點非空,匹配失敗
return false;
if(root.val!=arr[index])//節點值和數組值不同,匹配失敗
return false;
if(root.left==null&&root.right==null&&index==arr.length-1)//當前是葉子節點,並且數組匹配完畢
return true;
//左右子樹任意一邊匹配成功即可
return isValid(root.left,arr,index+1)||isValid(root.right,arr,index+1);
}
面試題 04.02. 最小高度樹
給定一個有序整數數組,元素各不相同且按升序排列,編寫一個算法,創建一棵高度最小的二叉搜索樹。
解答:和108題一樣。
面試題 04.03. 特定深度節點鏈表
給定一棵二叉樹,設計一個算法,創建含有某一深度上所有節點的鏈表(比如,若一棵樹的深度爲 D
,則會創建出 D
個鏈表)。返回一個包含所有深度的鏈表的數組。
示例:
輸入:[1,2,3,4,5,null,7,8]
1
/ \
2 3
/ \ \
4 5 7
/
8
輸出:[[1],[2,3],[4,5,7],[8]]
解答:
//思路跟二叉樹層序遍歷一樣
public ListNode[] listOfDepth(TreeNode root) {
List<ListNode> result=new ArrayList<>();
//保存節點
List<TreeNode> trees=new ArrayList<>();
//保存臨時結果
ListNode temp;
if(root!=null){
trees.add(root);
while(!trees.isEmpty()){
//計算當前層節點數,按數量全部出隊,再將其左右節點依次入隊
int n=trees.size();
temp=new ListNode(-1);
ListNode cur=temp;
for(int i=0;i<n;i++){
TreeNode remove=trees.remove(0);
cur.next=new ListNode(remove.val);
cur=cur.next;
//出隊節點的左右節點不爲空就入隊
if(remove.left!=null)
trees.add(remove.left);
if(remove.right!=null)
trees.add(remove.right);
}
//添加到結果集
result.add(temp.next);
}
}
ListNode[] res=new ListNode[result.size()];
for(int i=0;i<res.length;i++)
res[i]=result.get(i);
return res;
}
面試題 04.04. 檢查平衡性
實現一個函數,檢查二叉樹是否平衡。在這個問題中,平衡樹的定義如下:任意一個節點,其兩棵子樹的高度差不超過 1。
解答:和110題一樣。
面試題 04.05. 合法二叉搜索樹
實現一個函數,檢查一棵二叉樹是否爲二叉搜索樹。
解答:和98題一樣。
面試題 04.06. 後繼者
設計一個算法,找出二叉搜索樹中指定節點的“下一個”節點(也即中序後繼)。
如果指定節點沒有對應的“下一個”節點,則返回null
。
示例 1:
輸入: root = [2,1,3], p = 1
2
/ \
1 3
輸出: 2
解答:
class Solution {
//保存中序節點序列
List<TreeNode> list=new ArrayList<>();
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
inorder(root);
for(int i=0;i<list.size()-1;i++){
if(list.get(i)==p)
return list.get(i+1);
}
return null;
}
//中序遍歷保存升序序列
public void inorder(TreeNode root){
if(root!=null){
inorder(root.left);
list.add(root);
inorder(root.right);
}
}
}
面試題 04.08. 首個共同祖先
設計並實現一個算法,找出二叉樹中某兩個節點的第一個共同祖先。不得將其他的節點存儲在另外的數據結構中。注意:這不一定是二叉搜索樹。
解答:和236題一樣。
面試題 04.10. 檢查子樹
檢查子樹。你有兩棵非常大的二叉樹:T1,有幾萬個節點;T2,有幾萬個節點。設計一個算法,判斷 T2 是否爲 T1 的子樹。
如果 T1 有這麼一個節點 n,其子樹與 T2 一模一樣,則 T2 爲 T1 的子樹,也就是說,從節點 n 處把樹砍斷,得到的樹與 T2 完全相同。
解答:和572題一樣。
面試題 04.12. 求和路徑
給定一棵二叉樹,其中每個節點都含有一個整數數值(該值或正或負)。設計一個算法,打印節點數值總和等於某個給定值的所有路徑的數量。注意,路徑不一定非得從二叉樹的根節點或葉節點開始或結束,但是其方向必須向下(只能從父節點指向子節點方向)。
解答:和437題一樣。
面試題 17.12. BiNode
二叉樹數據結構TreeNode
可用來表示單向鏈表(其中left
置空,right
爲下一個鏈表節點)。實現一個方法,把二叉搜索樹轉換爲單向鏈表,要求值的順序保持不變,轉換操作應是原址的,也就是在原始的二叉搜索樹上直接修改。返回轉換後的單向鏈表的頭節點。
解答:和897題一樣。
面試題07. 重建二叉樹
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。
解答:和106題一樣。
面試題26. 樹的子結構
輸入兩棵二叉樹A和B,判斷B是不是A的子結構。(約定空樹不是任意一個樹的子結構)
B是A的子結構, 即 A中有出現和B相同的結構和節點值。
解答:
//和572題類似
public boolean isSubStructure(TreeNode s, TreeNode t) {
if(s==null||t==null)
return false;
//以當前節點爲根,或者在左右子樹中繼續尋找
return isSame(s,t)||isSubStructure(s.left,t)||isSubStructure(s.right,t);
}
//判斷兩個分別以s和t作爲根節點的樹是否相同
public boolean isSame(TreeNode s,TreeNode t){
if(s==null&&t!=null)
return false;
if(t==null)
return true;
//根節點值不同爲false
if(s.val!=t.val)
return false;
//比較對應的左子樹和右子樹
return isSame(s.left,t.left)&&isSame(s.right,t.right);
}
面試題27. 二叉樹的鏡像
請完成一個函數,輸入一個二叉樹,該函數輸出它的鏡像。
解答:和226題一樣。
面試題28. 對稱的二叉樹
請實現一個函數,用來判斷一棵二叉樹是不是對稱的。如果一棵二叉樹和它的鏡像一樣,那麼它是對稱的。
解答:和101題一樣。
面試題32 - I. 從上到下打印二叉樹
從上到下打印出二叉樹的每個節點,同一層的節點按照從左到右的順序打印。
例如:
給定二叉樹: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回:
[3,9,20,15,7]
解答:
public int[] levelOrder(TreeNode root) {
//保存結果集
List<Integer> result=new ArrayList<>();
//保存節點
List<TreeNode> trees=new ArrayList<>();
//保存臨時結果
if(root!=null){
trees.add(root);
while(!trees.isEmpty()){
//計算當前層節點數,按數量全部出隊,再將其左右節點依次入隊
int n=trees.size();
for(int i=0;i<n;i++){
TreeNode remove=trees.remove(0);
result.add(remove.val);
//出隊節點的左右節點不爲空就入隊
if(remove.left!=null)
trees.add(remove.left);
if(remove.right!=null)
trees.add(remove.right);
}
}
}
int[] res=new int[result.size()];
for(int i=0;i<res.length;i++){
res[i]=result.get(i);
}
return res;
}
面試題32 - II. 從上到下打印二叉樹 II
從上到下按層打印二叉樹,同一層的節點按從左到右的順序打印,每一層打印到一行。
解答:和102題一樣。
面試題32 - III. 從上到下打印二叉樹 III
請實現一個函數按照之字形順序打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右到左的順序打印,第三行再按照從左到右的順序打印,其他行以此類推。
解答:和103題一樣。
面試題34. 二叉樹中和爲某一值的路徑
輸入一棵二叉樹和一個整數,打印出二叉樹中節點值的和爲輸入整數的所有路徑。從樹的根節點開始往下一直到葉節點所經過的節點形成一條路徑。
解答:和113題一樣。
面試題54. 二叉搜索樹的第k大節點
給定一棵二叉搜索樹,請找出其中第k大的節點。
示例 1:
輸入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
輸出: 4
解答:
class Solution {
ArrayList<Integer> list=new ArrayList<>();
public int kthLargest(TreeNode root, int k) {
inorder(root);
return list.get(list.size()-k);
}
//中序遍歷,結果從小到大保存
public void inorder(TreeNode root){
if(root!=null){
inorder(root.left);
list.add(root.val);
inorder(root.right);
}
}
}
面試題55 - I. 二叉樹的深度
輸入一棵二叉樹的根節點,求該樹的深度。從根節點到葉節點依次經過的節點(含根、葉節點)形成樹的一條路徑,最長路徑的長度爲樹的深度。
解答:和104題一樣。
面試題55 - II. 平衡二叉樹
輸入一棵二叉樹的根節點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意節點的左右子樹的深度相差不超過1,那麼它就是一棵平衡二叉樹。
解答:和110題一樣。
面試題68 - I. 二叉搜索樹的最近公共祖先
給定一個二叉搜索樹, 找到該樹中兩個指定節點的最近公共祖先。
解答:和235題一樣。
面試題68 - II. 二叉樹的最近公共祖先
給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。
解答:和236題一樣。