111. 二叉樹的最小深度
給定一個二叉樹,找出其最小深度。
最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定二叉樹 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最小深度 2.
class Solution {
public:
int minDepth(TreeNode* root) {
if(root == NULL)
return 0;
if(root->left == NULL && root->right != NULL)
{
return 1+minDepth(root->right);
}
if(root->left != NULL && root->right == NULL)
{
return 1+minDepth(root->left);
}
return min(minDepth(root->right), minDepth(root->left))+1;
}
};
226. 翻轉二叉樹
翻轉一棵二叉樹。
示例:
輸入:
4
/ \
2 7
/ \ / \
1 3 6 9
輸出:
4
/ \
7 2
/ \ / \
9 6 3 1
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL)
return root;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
非遞歸解法
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL)
return root;
stack<TreeNode*> Tstack;
Tstack.push(root);
while(!Tstack.empty())
{
TreeNode* p = Tstack.top();
Tstack.pop();
if(p->left || p->right)
swap(p->left, p->right);
if(p->left)
Tstack.push(p->left);
if(p->right)
Tstack.push(p->right);
}
return root;
}
};
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
示例 3:
輸入: 1 1
/ \ / \
2 1 1 2
[1,2,1], [1,1,2]
輸出: false
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == NULL && q == NULL)
return true;
if(p == NULL || q == NULL || 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
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(!root)
return true;
return helper(root->left, root->right);
}
bool helper(TreeNode *left, TreeNode *right)
{
if(!left && !right)
return true;
else if(left && right && left->val == right->val)
return helper(left->left, right->right) && helper(left->right, right->left);
else
return false;
}
};
222. 完全二叉樹的節點個數
給出一個完全二叉樹,求出該樹的節點個數。
說明:
完全二叉樹的定義如下:在完全二叉樹中,除了最底層節點可能沒填滿外,其餘每層節點數都達到最大值,並且最下面一層的節點都集中在該層最左邊的若干位置。若最底層爲第 h 層,則該層包含 1~ 2h 個節點。
示例:
輸入:
1
/ \
2 3
/ \ /
4 5 6
輸出: 6
class Solution {
public:
int countNodes(TreeNode* root) {
int hleft = 0, hright = 0;
TreeNode* left = root, *right = root;
while(left)
{
hleft++;
left = left->left;
}
while(right)
{
hright++;
right = right->right;
}
if(hleft == hright)
return pow(2, hleft) - 1;
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
計算任何樹的結點個數
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == NULL)
return 0;
int left = countNodes(root->left);
int right = countNodes(root->right);
return left + right + 1;
}
};
110. 平衡二叉樹
給定一個二叉樹,判斷它是否是高度平衡的二叉樹。
本題中,一棵高度平衡二叉樹定義爲:
一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過1。
示例 1:
給定二叉樹 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。
示例 2:
給定二叉樹 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
class Solution {
public:
int depth(TreeNode* root)
{
if(root == NULL)
return 0;
return max(depth(root->left), depth(root->right)) + 1;
}
bool isBalanced(TreeNode* root) {
if(!root)
return true;
if(abs(depth(root->left) - depth(root->right)) > 1)
return false;
return isBalanced(root->left) && isBalanced(root->right);
}
};
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(checkTree(root) != -1)
return true;
return false;
}
int checkTree(TreeNode *root)
{
if(!root)
return 0;
int left = checkTree(root->left);
int right = checkTree(root->right);
if(abs(left - right) > 1 || left < 0 || right < 0)
return -1;
return max(left, right) +1;
}
};
112. 路徑總和
給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定如下二叉樹,以及目標和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因爲存在目標和爲 22 的根節點到葉子節點的路徑 5->4->11->2。
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(!root)
return false;
if(!root->left && !root->right)
return root->val == sum ;
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
}
};
404. 左葉子之和
計算給定二叉樹的所有左葉子之和。
示例:
3
/ \
9 20
/ \
15 7
在這個二叉樹中,有兩個左葉子,分別是 9 和 15,所以返回 24
class Solution {
public:
void helper(TreeNode* root, int &sum)
{
if(root == NULL)
return;
if(root->left && root->left->left == NULL && root->left->right == NULL)
sum = sum + root->left->val;
helper(root->left, sum);
helper(root->right, sum);
}
int sumOfLeftLeaves(TreeNode* root) {
int sum = 0;
helper(root, sum);
return sum;
}
};
257. 二叉樹的所有路徑
給定一個二叉樹,返回所有從根節點到葉子節點的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
輸入:
1
/ \
2 3
\
5
輸出: ["1->2->5", "1->3"]
解釋: 所有根節點到葉子節點的路徑爲: 1->2->5, 1->3
第一種方法遞歸
class Solution {
public:
void dfs(TreeNode* root, vector<string> &ret, string temp)
{
if(root->left == NULL & root->right == NULL)
{
ret.push_back(temp);
return ;
}
if(root->left)
dfs(root->left, ret, temp + "->" + to_string(root->left->val));
if(root->right)
dfs(root->right, ret, temp + "->" + to_string(root->right->val));
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
if(root == NULL)
return ret;
dfs(root, ret, to_string(root->val));
return ret;
}
};
第二種方法DFS
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> tstack;
stack<string> path;
vector<string> ret;
string temp = "";
if(root == NULL)
return ret;
tstack.push(root);
path.push(to_string(root->val));
while(!tstack.empty())
{
TreeNode* node = tstack.top();
string temp = path.top();
tstack.pop();
path.pop();
if(node->left == NULL && node->right == NULL)
{
ret.push_back(temp);
}
if(node->left)
{
tstack.push(node->left);
path.push(temp + "->" + to_string(node->left->val));
}
if(node->right)
{
path.push(temp + "->" + to_string(node->right->val));
tstack.push(node->right);
}
}
return ret;
}
};
第三種方法BFS
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
queue<TreeNode*> tqueue;
queue<string> path;
vector<string> ret;
string temp = "";
if(root == NULL)
return ret;
tqueue.push(root);
path.push(to_string(root->val));
while(!tqueue.empty())
{
TreeNode* node = tqueue.front();
string temp = path.front();
tqueue.pop();
path.pop();
if(node->left == NULL && node->right == NULL)
{
ret.push_back(temp);
}
if(node->left)
{
tqueue.push(node->left);
path.push(temp + "->" + to_string(node->left->val));
}
if(node->right)
{
path.push(temp + "->" + to_string(node->right->val));
tqueue.push(node->right);
}
}
return ret;
}
};
第四種方法
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
if(root == NULL)
return ret;
if(root->left==NULL && root->right==NULL)
{
ret.push_back(to_string(root->val));
return ret;
}
vector<string> left = binaryTreePaths(root->left);
for(int i = 0; i < left.size(); i++)
{
ret.push_back(to_string(root->val) + "->" + left[i]);
}
vector<string> right = binaryTreePaths(root->right);
for(int i = 0; i < right.size(); i++)
{
ret.push_back(to_string(root->val) + "->" + right[i]);
}
return ret;
}
};
113. 路徑總和 II
給定一個二叉樹和一個目標和,找到所有從根節點到葉子節點路徑總和等於給定目標和的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定如下二叉樹,以及目標和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
思路:分別往左子樹,右子樹遞歸遍歷所有路徑,每次遞歸就減去相應的節點值,到了葉子結點如果剩餘值與葉子結點值相等,則該條路徑符合要求,記錄下該條路徑,不符合的中間結果就pop掉。
class Solution {
public:
void helper(TreeNode* root, int sum, vector<int> &temp, vector<vector<int>> &ret)
{
if(!root)
return;
temp.push_back(root->val);
if(root->left == NULL && root->right == NULL && root->val == sum)
{
ret.push_back(temp);
}
helper(root->left, sum-root->val, temp, ret);
helper(root->right, sum-root->val, temp, ret);
temp.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<int> temp;
vector<vector<int>>ret;
helper(root, sum, temp, ret);
return ret;
}
};
129. 求根到葉子節點數字之和
給定一個二叉樹,它的每個結點都存放一個 0-9 的數字,每條從根到葉子節點的路徑都代表一個數字。
例如,從根到葉子節點路徑 1->2->3 代表數字 123。
計算從根到葉子節點生成的所有數字之和。
說明: 葉子節點是指沒有子節點的節點。
示例 1:
輸入: [1,2,3]
1
/ \
2 3
輸出: 25
解釋:
從根到葉子節點路徑 1->2 代表數字 12.
從根到葉子節點路徑 1->3 代表數字 13.
因此,數字總和 = 12 + 13 = 25.
示例 2:
輸入: [4,9,0,5,1]
4
/ \
9 0
/ \
5 1
輸出: 1026
解釋:
從根到葉子節點路徑 4->9->5 代表數字 495.
從根到葉子節點路徑 4->9->1 代表數字 491.
從根到葉子節點路徑 4->0 代表數字 40.
因此,數字總和 = 495 + 491 + 40 = 1026.
思路:分別往左子樹,右子樹遞歸遍歷所有路徑。到葉子節點算出數組中的值
class Solution {
public:
void helper(TreeNode* root, vector<int> &temp, int &sum)
{
if(!root)
return;
temp.push_back(root->val);
if(root->left == NULL && root->right == NULL)
{
int ret = 0;
for(int i = 0; i < temp.size();i++)
ret = ret*10 + temp[i];
sum = sum + ret;
}
helper(root->left, temp, sum);
helper(root->right, temp, sum);
temp.pop_back();
}
int sumNumbers(TreeNode* root) {
if(root == NULL)
return 0;
vector<int> temp;
int sum = 0;
helper(root, temp, sum);
return sum;
}
};
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 helper(TreeNode* node, int sum)
{
if(node == NULL)
return 0;
int res = 0;
if(node->val == sum)
res = res+1;
res += helper(node->left, sum-node->val);
res += helper(node->right, sum-node->val);
return res;
}
int pathSum(TreeNode* root, int sum) {
if(root == NULL)
return 0;
int res = helper(root, sum);//以根節點爲起始點的和爲sum的路徑
res += pathSum(root->left, sum);//左右子樹和爲sum的路徑
res += pathSum(root->right, sum);
return res;
}
};
235. 二叉搜索樹的最近公共祖先
給定一個二叉搜索樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”
例如,給定如下二叉搜索樹: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
輸出: 6
解釋: 節點 2 和節點 8 的最近公共祖先是 6。
示例 2:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
輸出: 2
解釋: 節點 2 和節點 4 的最近公共祖先是 2, 因爲根據定義最近公共祖先節點可以爲節點本身。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root)
return NULL;
if(root->val < p->val && root->val < q->val)
return lowestCommonAncestor(root->right, p, q);
if(root->val > p->val && root->val > q->val)
return lowestCommonAncestor(root->left, p, q);
return root;
}
};
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root)
return NULL;
while(root)
{
if(root->val < p->val && root->val < q->val)
root= root->right;
else if(root->val > p->val && root->val > q->val)
root = root->left;
else
return root;
}
return NULL;
}
};
98. 驗證二叉搜索樹
給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹。
假設一個二叉搜索樹具有如下特徵:
節點的左子樹只包含小於當前節點的數。
節點的右子樹只包含大於當前節點的數。
所有左子樹和右子樹自身必須也是二叉搜索樹。
示例 1:
輸入:
2
/ \
1 3
輸出: true
示例 2:
輸入:
5
/ \
1 4
/ \
3 6
輸出: false
解釋: 輸入爲: [5,1,4,null,null,3,6]。
根節點的值爲 5 ,但是其右子節點值爲 4 。
第一種方法:我們需要在遍歷樹的同時保留結點的上界與下界,在比較時不僅比較子結點的值,也要與上下界比較
class Solution {
public:
bool dfs(TreeNode *root, long min, long max)
{
if(!root)
return true;
if(root->val <= min || root->val >= max)
return false;
return dfs(root->left, min, root->val) && dfs(root->right, root->val, max);
}
bool isValidBST(TreeNode* root) {
return dfs(root, LONG_MIN, LONG_MAX);
}
};
第二種方法:中序遍歷,採用迭代法中序遍歷
class Solution {
public:
bool isValidBST(TreeNode* root) {
stack<TreeNode*> tstack;
long flag = LONG_MIN;
while(!tstack.empty() || root != NULL)
{
while(root)
{
tstack.push(root);
root= root->left;
}
root = tstack.top();
tstack.pop();
if(root->val <= flag)
return false;
flag = root->val;
root = root->right;
}
return true;
}
};
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
刪除的節點有三種情況
- 刪除的節點無子節點。直接刪除該節點
- 刪除的節點有且僅有一個子節點。子節點替換刪除節點
- 刪除的節點同時有兩個節點。一種是從刪除節點的右分支查找最小值,賦值給刪除節點,再去右分支刪除找到的這個最小值;另一種是從刪除節點的左分支查找最大值,賦值給刪除節點,再去左分支刪除這個最大值。
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root == NULL)
return NULL;
if(root->val > key)
root->left = deleteNode(root->left, key);
else if(root->val < key)
root->right = deleteNode(root->right, key);
else
{
if(root->left && root->right)
{
TreeNode * temp = root->right;
while(temp->left)
temp = temp->left;
root->val = temp->val;
root->right = deleteNode(root->right, temp->val);
}
else
{
TreeNode *temp = root;
if(!root->left)
root = root->right;
else if(!root->right)
root = root->left;
delete temp;
}
}
return root;
}
};
108. 將有序數組轉換爲二叉搜索樹
將一個按照升序排列的有序數組,轉換爲一棵高度平衡二叉搜索樹。
本題中,一個高度平衡二叉樹是指一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過 1。
示例:
給定有序數組: [-10,-3,0,5,9],
一個可能的答案是:[0,-3,9,-10,null,5],它可以表示下面這個高度平衡二叉搜索樹:
0
/ \
-3 9
/ /
-10 5
思路:找到數組的中點作爲樹的根節點,遞歸數組的左半邊和右半邊,分別作爲樹的左子樹和右子樹。
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return binaryBuildTree(nums, 0, nums.size()-1);
}
TreeNode *binaryBuildTree(vector<int> nums, int left, int right)
{
if(left > right)
return NULL;
int mid = (left + right) /2;
TreeNode *cur = new TreeNode(nums[mid]);
cur->left = binaryBuildTree(nums, left, mid - 1);
cur->right = binaryBuildTree(nums, mid+1, right);
return cur;
}
};
230. 二叉搜索樹中第K小的元素
給定一個二叉搜索樹,編寫一個函數 kthSmallest 來查找其中第 k 個最小的元素。
說明:
你可以假設 k 總是有效的,1 ≤ k ≤ 二叉搜索樹元素個數。
示例 1:
輸入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
輸出: 1
示例 2:
輸入: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
輸出: 3
第一種方法:使用遞歸的方法求出左子樹的節點個數,個數等於k-1就返回根節點,大於就去左子樹尋找,小於就去右子樹尋找。
class Solution {
public:
int calTreeSize(TreeNode *root)
{
if(root == NULL)
return 0;
return 1 + calTreeSize(root->left) + calTreeSize(root->right);
}
int kthSmallest(TreeNode* root, int k) {
if(root == NULL)
return -1;
int leftsize = calTreeSize(root->left);
if(leftsize + 1 == k)
return root->val;
else if(leftsize >= k)
return kthSmallest(root->left, k);
else
return kthSmallest(root->right, k-leftsize-1);
}
};
第二種方法:使用中序遍歷,求出第k個節點
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
if(root == NULL)
return -1;
stack<TreeNode*> ret;
TreeNode *p = root;
while(p != NULL || !ret.empty())
{
while(p)
{
ret.push(p);
p = p->left;
}
p = ret.top();
if(--k == 0)
return p->val;
ret.pop();
p = p->right;
}
return -1;
}
};
236. 二叉樹的最近公共祖先
給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”
例如,給定如下二叉樹: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
輸入: 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。因爲根據定義最近公共祖先節點可以爲節點本身。
思路:通過返回值是否爲NULL來標識是否找到相應的節點
p,q在左右子樹,當前的根節點就是公共的祖先節點;如果p,q同在左子樹或者右子樹,公共的祖先節點是p或者q
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root || !p || !q)
return NULL;
if(root == p || root == q)
return root;
TreeNode *parentleft = lowestCommonAncestor(root->left, p, q);
TreeNode *parentright = lowestCommonAncestor(root->right, p, q);
if(parentleft && parentright)
return root;
return parentleft ? parentleft : parentright;
}
};
求二叉樹中最遠的兩個節點的距離
求兩個節點之間最遠的距離:
兩個節點都是葉子結點
一個是葉子結點一個是根節點
思路:
(1)如果具有最遠距離的兩個節點經過了根節點,那麼最遠的距離就是左邊最深的深度加上右邊最深的深度之和。
(2)如果具有最遠距離的兩個節點之間的路徑不經過根節點,那麼最遠的距離就在根節點的其中一個子樹上的兩個葉子結點。
int GetFarDistance()
{
int distance = -1;
_Height(_root,distance);
return distance;
}
protected:
int _Height(Node* root, int& distance)
{
if (root == NULL)
{
return 0;
}
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
if (leftH+rightH > distance)
{
distance = leftH + rightH;
}
return leftH > rightH ? leftH+1 : rightH+1;
}
求第k層節點的個數
int get_k_level(Node* root, int k)
{
if(root == NULL || k <= 0)
return 0;
if(root != NULL && k == 1)
return 1;
return get_k_level(root->left, k-1) + get_k_level(root->right, k-1);
}