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);
}