二叉树和递归:
递归,是使用计算机解决问题的一种重要的思考方式。而二叉树由于其天然的递归结构,使得基于二叉树的算法,均拥有着递归性质。使用二叉树,是研究学习递归算法的最佳入门方式。在这一章里,我们就来看一看二叉树中的递归算法。
二叉树天然的递归结构
问题1:Maximum Depth of Binary Tree(求最大深度)
/// Recursive
/// Time Complexity: O(n), where n is the nodes' number in the tree
/// Space Complexity: O(h), where h is the height of the tree
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL)
return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
};
问题2: Minimum Depth of Binary Tree(最低深度)
/// Recursive
/// Time Complexity: O(n)
/// Space Complexity: O(h)
class Solution {
public:
int minDepth(TreeNode* root) {
if(root == NULL)
return 0;
if(root->left == NULL && root->right == NULL)
return 1;
int ret = INT_MAX;
if(root->left != NULL)
ret = min(ret, 1 + minDepth(root->left));
if(root->right != NULL)
ret = min(ret, 1 + minDepth(root->right));
return ret;
}
};
问题3:Invert Binary Tree(反转二叉树)
/// Recursive
/// Time Complexity: O(n), where n is the node's number of the tree
/// Space Complexity: O(h), where h is the height of the tree
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL)
return NULL;
invertTree(root->left);
invertTree(root->right);
swap(root->left, root->right);
return root;
}
};
问题4:Symmetric Tree(判断是否是对称树)
/// Recursive
/// No need to revert one child tree
/// See if the two child trees of the root are mirror directly
///
/// Time Complexity: O(n)
/// Space Complexity: O(h)
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (root == NULL){
return true;
}
return is_mirror(root, root);
}
private:
bool is_mirror(TreeNode* root1, TreeNode* root2){
if (root1 == NULL && root2 == NULL){
return true;
}
if (root1 == NULL || root2 == NULL){
return false;
}
if (root1->val != root2->val){
return false;
}
return is_mirror(root1->left,root2->right) && is_mirror(root1->right, root2->left);
}
};
问题5:Symmetric Tree(判断是否是对称树)
原题
Given a complete binary tree, count the number of nodes.
求完全二叉树的节点数目。注意完全二叉树和满二叉树Full Binary Tree的唯一区别是,完全二叉树最后一层的节点不满,而且假设最后一层有节点,都是从左边开始。 这样我们可以利用这个性质得到下面两个结论:
- 假如左子树高度等于右子树高度,则右子树为完全二叉树,左子树为满二叉树。
- 假如高度不等,则左字数为完全二叉树,右子树为满二叉树。
- 求高度的时候只往左子树来找。
/// Recursion
/// Time Complexity: O(h^2) where h is the height of the tree
/// Space Complexity: O(h)
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == NULL)
return 0;
int leftLeft = leftHeight(root->left);
int leftRight = rightHeight(root->left);
if(leftLeft == leftRight)
return 1 + ((1<<leftLeft) - 1) + countNodes(root->right);
assert(leftLeft == leftRight + 1);
return 1 + ((1<<rightRight) - 1) + countNodes(root->left);
}
private:
int leftHeight(TreeNode* root){
if(root == NULL)
return 0;
return 1 + leftHeight(root->left);
}
int rightHeight(TreeNode* root){
if(root == NULL)
return 0;
return 1 + rightHeight(root->right);
}
};
问题5:Balanced Binary Tree(判断是否是平衡二叉树)
求二叉树是否平衡,根据题目中的定义,高度平衡二叉树是每一个结点的两个子树的深度差不能超过1,那么我们肯定需要一个求各个点深度的函数,然后对每个节点的两个子树来比较深度差,时间复杂度为O(NlgN),代码如下:
class Solution {
public:
bool isBalanced(TreeNode *root) {
if (!root) return true;
if (abs(getDepth(root->left) - getDepth(root->right)) > 1) return false;
return isBalanced(root->left) && isBalanced(root->right);
}
int getDepth(TreeNode *root) {
if (!root) return 0;
return 1 + max(getDepth(root->left), getDepth(root->right));
}
};
上面那个方法正确但不是很高效,因为每一个点都会被上面的点计算深度时访问一次,我们可以进行优化。方法是如果我们发现子树不平衡,则不计算具体的深度,而是直接返回-1。那么优化后的方法为:对于每一个节点,我们通过checkDepth方法递归获得左右子树的深度,如果子树是平衡的,则返回真实的深度,若不平衡,直接返回-1,此方法时间复杂度O(N),空间复杂度O(H),参见代码如下:
class Solution {
public:
bool isBalanced(TreeNode *root) {
if (checkDepth(root) == -1) return false;
else return true;
}
int checkDepth(TreeNode *root) {
if (!root) return 0;
int left = checkDepth(root->left);
if (left == -1) return -1;
int right = checkDepth(root->right);
if (right == -1) return -1;
int diff = abs(left - right);
if (diff > 1) return -1;
else return 1 + max(left, right);
}
};
问题6:Path Sum
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
/// Recursive
/// Time Complexity: O(n), where n is the nodes' number of the tree
/// Space Complexity: O(h), where h is the height of the tree
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if (!root){
return false;
}
if (root->left == NULL && root->right == NULL){
return sum == root->val;
}
return hasPathSum(root->left, sum-root->val) || hasPathSum(root->right,sum-root->val);
}
};
问题6:Sum of Left Leaves
Find the sum of all left leaves in a given binary tree.
Example:
3
/ \
9 20
/ \
15 7
There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.
这道题让我们求一棵二叉树的所有左子叶的和,那么看到这道题我们知道这肯定是考二叉树的遍历问题,那么最简洁的写法肯定是用递归,由于我们只需要累加左子叶之和,那么我们在进入递归函数的时候需要知道当前结点是否是左子节点,如果是左子节点,而且该左子节点再没有子节点了说明其是左子叶,那么我们将其值加入结果res中,我们用一个bool型的变量,如果为true说明当前结点是左子节点,若为false则说明是右子节点,不做特殊处理,整个来说就是个递归的先序遍历的写法,参见代码如下:
/// Recursion
/// Time Complexity: O(n)
/// Space Complexity: O(h)
/// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
private:
int res = 0;
public:
int sumOfLeftLeaves(TreeNode* root) {
if(!root) return 0;
dfs(root, false);
return res;
}
private:
void dfs(TreeNode* node, bool isLeft){
if(!node->left && !node->right){
if(isLeft) res += node->val;
return;
}
if(node->left) dfs(node->left, true);
if(node->right) dfs(node->right, false);
}
};
问题7:Binary Tree Paths
/// Recursive
/// Time Complexity: O(n), where n is the node's number in the tree
/// Space Complexity: O(h), where h is the height of the tree
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
//递归的终止条件
if(root == NULL)
return res;
//只有根节点
if(root->left == NULL && root->right == NULL){
res.push_back(to_string(root->val));
return res;
}
vector<string> leftPaths = binaryTreePaths(root->left);//获得左子树所有路径
for(int i = 0 ; i < leftPaths.size() ; i ++)
res.push_back(to_string(root->val) + "->" + leftPaths[i]);
vector<string> rightPaths = binaryTreePaths(root->right);//得右子树所有路径
for(int i = 0 ; i < rightPaths.size() ; i ++)
res.push_back(to_string(root->val) + "->" + rightPaths[i]);
return res;
}
};
问题8:Path Sum II
原题
Given a binary tree and a sum, find all root-to-leaf paths where each path’s sum equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
Return:
[
[5,4,11,2],
[5,8,4,5]
]
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<vector<int>> res;
if(!root)
return res;
vector<int> tres;
dfs(root, tres, 0, sum, res);
return res;
}
private:
/**
*进行深度优先遍历
* @param node 当前节点
* @param tres 把路径保存到这
* @param tsum 临时和,用来判断找到的路径的和是否和总和相同
* @param sum 总和(已知)
* @param res 所有路径保存到这
*/
void dfs(TreeNode* node, vector<int>& tres, int tsum,
int sum, vector<vector<int>>& res){
tres.push_back(node->val);
tsum += node->val;
//只有根节点的时候或者是已经没有子结点,就是已经遍历完了
if(!node->left && !node->right){
if(tsum == sum)
res.push_back(tres);
}
else {
if (node->left)
dfs(node->left, tres, tsum, sum, res);
if (node->right)
dfs(node->right, tres, tsum, sum, res);
}
//把tres容器里面的元素删除
tres.pop_back();
return;
}
};
问题9:Sum Root to Leaf Numbers
Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.
An example is the root-to-leaf path 1->2->3 which represents the number 123.
Find the total sum of all root-to-leaf numbers.
Note: A leaf is a node with no children.
Example:
Input: [1,2,3]
1
/ \
2 3
Output: 25
Explanation:
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Therefore, sum = 12 + 13 = 25.
class Solution {
public:
int sumNumbers(TreeNode* root) {
if(!root) return 0;
int res = 0;
dfs(root, 0, res);
return res;
}
private:
void dfs(TreeNode* node, int tnum, int& sum){
//把新的结点*10倍再加上原来的结点
tnum = tnum * 10 + node->val;
if(!node->left && !node->right)
sum += tnum;
else{
if(node->left)
dfs(node->left, tnum, sum);
if(node->right)
dfs(node->right, tnum, sum);
}
}
};
问题10:Path Sum III
You are given a binary tree in which each node contains an integer value.
Find the number of paths that sum to a given value.
The path does not need to start or end at the root or a leaf, but it must go downwards
(traveling only from parent nodes to child nodes).
The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.
Example:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
Return 3. The paths that sum to 8 are:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
/// Recursive
/// Time Complexity: O(n), where n is the node's number of the tree
/// Space Complexity: O(h), where h is the height of the tree
class Solution {
public:
/**
* 以root为根节点的二叉树中,寻找和为sum的路径
* @param root
* @param sum
* @return 这样的路径的个数
*/
int pathSum(TreeNode* root, int sum) {
if(root == NULL)
return 0;
return findPath(root, sum) //寻找包含root结点的路径
+ pathSum(root->left , sum) //在node的左右结点中寻找不包含root
+ pathSum(root->right , sum);
}
private:
/**
* 在以node为根节点的二叉树中,寻找包含node的路径,和为sum
* @param node
* @param num
* @return 这样的路径个数
*/
int findPath(TreeNode* node, int num){
if(node == NULL)
return 0;
int res = 0;
//不能直接返回1,如果有负数就不一样了
if(node->val == num)
res += 1;
//node在此路径上
res += findPath(node->left , num - node->val);
res += findPath(node->right , num - node->val);
return res;
}
};
问题11:Lowest Common Ancestor of a Binary Search Tree
原题
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”
Given binary search tree: root = [6,2,8,0,4,7,9,null,null,3,5]
Example 1:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.
Example 2:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
Output: 2
Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.
/// Recursive
/// Time Complexity: O(lgn), where n is the node's number of the tree
/// Space Complexity: O(h), where h is the height of the tree
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
assert(p && q);
if(!root) return root;
if(p->val < root->val && q->val < root->val)
return lowestCommonAncestor(root->left, p, q);
if(p->val > root->val && q->val > root->val)
return lowestCommonAncestor(root->right, p, q);
assert(p->val == root->val || q->val == root->val
|| (root->val - p->val) * (root->val - q->val) < 0);
return root;
}
};
问题13:Validate Binary Search Tree(判断是否是二分搜索树)
/// Using inOrder traverse
/// Store all elements in an vector
///
/// Time Complexity: O(n)
/// Space Complexity: O(n)
class Solution {
public:
bool isValidBST(TreeNode* root) {
vector<int> vec;
//中序遍历
inOrder(root, vec);
for(int i = 1 ; i < vec.size() ; i ++)
//二分搜索树的定义
if(vec[i-1] >= vec[i])
return false;
return true;
}
private:
void inOrder(TreeNode* node, vector<int>& vec){
if(node == NULL)
return;
inOrder(node->left, vec);
vec.push_back(node->val);
inOrder(node->right, vec);
}
};
问题14:Delete Node in a BST(删除二分查找树的一个结点)
原题
递归的解法,首先判断根节点是否为空。由于 BST 的左<根<右的性质,使得可以快速定位到要删除的结点,对于当前结点值不等于 key 的情况,根据大小关系对其左右子结点分别调用递归函数。若当前结点就是要删除的结点,先判断若有一个子结点不存在,就将 root 指向另一个结点,如果左右子结点都不存在,那么 root 就赋值为空了,也正确。难点就在于处理左右子结点都存在的情况,需要在右子树找到最小值,即右子树中最左下方的结点,然后将该最小值赋值给 root,然后再在右子树中调用递归函数来删除这个值最小的结点,参见代码如下:
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root)
return NULL;
if(key < root->val){
root->left = deleteNode(root->left, key);
return root;
}
if(key > root->val){
root->right = deleteNode(root->right, key);
return root;
}
if(!root->right) return root->left;
if(!root->left) return root->right;
TreeNode* p = root, *minnode = root->right;
while(minnode->left){
p = minnode;
minnode = minnode->left;
}
root->val = minnode->val;
root->right = deleteMinNode(root->right);//在右子树中调用递归函数来删除这个值最小的结点
return root;
}
private:
TreeNode* deleteMinNode(TreeNode* root){
if(!root->left) return root->right;
root->left = deleteMinNode(root->left);
return root;
}
};
问题15:Convert Sorted Array to Binary Search Tree(给定一个有序数组建立一棵二分搜索树)
/// Recursion
/// Time Complexity: O(n)
/// Space Complexity: O(logn)
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
if (nums.size() == 0){
return NULL;
}
return buildTree(nums,0,nums.size()-1);
}
private:
TreeNode* buildTree(const vector<int>& nums, int l, int r){
if (l > r){
return NULL;
}
if (l == r){
return new TreeNode(nums[l]);
}
int mid= (l+r)/2;
TreeNode* treeNode = new TreeNode(nums[mid]);
treeNode->left = buildTree(nums,l, mid-1);
treeNode->right = buildTree(nums,mid+1,r);
return treeNode;
}
};
问题15:Kth Smallest Element in a BST
原题
Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.
Note:
You may assume k is always valid, 1 ≤ k ≤ BST’s total elements.
Example 1:
Input: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
Output: 1
Example 2:
Input: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
Output: 3
/// Inorder Traversal
/// Time Complexity: O(n)
/// Space Complexity: O(h) where h is the height of the BST
class Solution {
private:
int index;
public:
int kthSmallest(TreeNode* root, int k) {
index = 0;
return kthSmallestNode(root, k)->val;
}
private:
TreeNode* kthSmallestNode(TreeNode* node, int k){
if(node == NULL)
return NULL;
TreeNode* res = kthSmallestNode(node->left, k);
if(res) return res;
index ++;
if(index == k)
return node;
return kthSmallestNode(node->right, k);
}
};