二叉树中的深搜
6. 计算布尔二叉树的值(medium)
解法(递归):
C++ 算法代码:
class Solution {
public:
bool evaluateTree(TreeNode* root)
{
if (root->left == nullptr) return root->val == 0 ? false : true;
bool left = evaluateTree(root->left);
bool right = evaluateTree(root->right);
return root->val == 2 ? left | right : left & right;
}
};
7. 求根节点到叶节点数字之和(medium)
解法(dfs - 前序遍历):
C++ 算法代码:
class Solution
{
public:
int sumNumbers(TreeNode* root)
{
return dfs(root, 0);
}
int dfs(TreeNode* root, int presum)
{
presum = presum * 10 + root->val;
if (root->left == nullptr && root->right == nullptr)
return presum;
int ret = 0;
if (root->left) ret += dfs(root->left, presum);
if (root->right) ret += dfs(root->right, presum);
return ret;
}
};
另一种写法:由于我们在叶子节点的时候已经计算出了这条路径的和,于是我们可以设置一个全局变量,当为叶子节点的时候,我们就可以直接加到全局变量上去。
C++ 算法代码:
class Solution {
int total = 0;
public:
int sumNumbers(TreeNode* root) {
int sum = 0;
dfs(root, sum);
return total;
}
void dfs(TreeNode* root, int sum)
{
sum = sum * 10 + root->val;
if(root->left == nullptr && root->right == nullptr)
total += sum;
if(root->left)
dfs(root->left,sum);
if(root->right)
dfs(root->right,sum);
}
};
8. 二叉树剪枝(medium)
解法(dfs - 后序遍历):
C++ 算法代码:
class Solution
{
public:
TreeNode* pruneTree(TreeNode* root)
{
if (root == nullptr) return nullptr;
root->left = pruneTree(root->left);
root->right = pruneTree(root->right);
if (root->left == nullptr && root->right == nullptr && root->val == 0)
{
// 如果这里的根节点不是new出来的,就不需要delete
delete root; // 防⽌内泄漏
root = nullptr;
}
return root;
}
};
9. 验证⼆叉搜索树(medium)
解法(利⽤中序遍历):
C++ 算法代码:
class Solution
{
long prev = LONG_MIN;
public:
bool isValidBST(TreeNode* root)
{
if (root == nullptr) return true;
bool left = isValidBST(root->left);
// 剪枝
if (left == false) return false;
bool cur = false;
if (root->val > prev)
cur = true;
// 剪枝
if (cur == false) return false;
prev = root->val;
bool right = isValidBST(root->right);
return left && right && cur;
}
};
10. ⼆叉搜索树中第 k ⼩的元素(medium)
解法⼆(中序遍历 + 计数器剪枝):
C++ 算法代码:
class Solution {
int prev = 0;
public:
int kthSmallest(TreeNode* root, int k) {
dfs(root,k);
return prev;
}
void dfs(TreeNode* root, int& k)
{
if(root == nullptr) return;
if(k == 0) return;//剪枝
dfs(root->left,k);
--k;
if(k == 0)
{
prev = root->val;
return;//剪枝
}
dfs(root->right,k);
}
};
11. 二叉树的所有路径(easy)
解法(回溯):
C++ 算法代码:
class Solution
{
public:
vector<string> ret; // 记录结果
vector<string> binaryTreePaths(TreeNode* root)
{
string path;
if (root == nullptr) return ret;
dfs(root, path);
return ret;
}
void dfs(TreeNode* root, string path)
{
if (root == nullptr)//剪枝
return;
if(root->left == nullptr && root->right == nullptr)//叶子节点处理
{
path+=to_string(root->val);
ret.push_back(path);
}
else// 非叶子节点处理
{
path+=to_string(root->val);
path += "->";
}
dfs(root->left, path);
dfs(root->right, path);
}
};
C++ 算法代码优化:
class Solution
{
public:
vector<string> ret; // 记录结果
vector<string> binaryTreePaths(TreeNode* root)
{
string path;
if (root == nullptr) return ret;
dfs(root, path);
return ret;
}
void dfs(TreeNode* root, string path)
{
path += to_string(root->val);
if (root->left == nullptr && root->right == nullptr)
{
ret.push_back(path);
return;
}
path += "->";
if (root->left) dfs(root->left, path);//剪枝
if (root->right) dfs(root->right, path);//剪枝
}
};
细节:
为什么我们这里需要控制一下左不为空,右不为空的情况呢?因为在遍历的过程中我们的左子树为空,但是右树不为空,但是此时我们又不满足递归的出口,所以我们就会向下递归,此时左树为空就会出现空指针的情况,所以需要控制一下,如果我们的树是满二叉树,那就不需要考虑这种情况,同时我们这里还进行了剪枝。
穷举vs暴搜vs深搜vs回溯vs剪枝
什么是回溯算法
回溯算法的模板
void backtrack(vector<int>& path, vector<int>& choice, ...) {
// 满⾜结束条件
if (/* 满⾜结束条件 */) {
// 将路径添加到结果集中
res.push_back(path);
return;
}
// 遍历所有选择
for (int i = 0; i < choices.size(); i++) {
// 做出选择
path.push_back(choices[i]);
// 做出当前选择后继续搜索
backtrack(path, choices);
// 撤销选择
path.pop_back();
}
}
回溯算法的应用
- 组合问题
- 排列问题
- 子集问题
总结
12. 全排列(medium)
解法:
C++ 算法代码:
class Solution
{
vector<vector<int>> ret;
vector<int> path;
bool check[7];
public:
vector<vector<int>> permute(vector<int>& nums)
{
dfs(nums);
return ret;
}
void dfs(vector<int>& nums)
{
if (path.size() == nums.size())
{
ret.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++)
{
if (check[i] == false)
{
path.push_back(nums[i]);
check[i] = true;
dfs(nums);
// 回溯 -> 恢复现场
path.pop_back();
check[i] = false;
}
}
}
};
13. 子集(medium)
解法:
解法一:
C++代码:
class Solution {
vector<vector<int>> ret;
vector<int> path;
public:
vector<vector<int>> subsets(vector<int>& nums) {
dfs(nums,0);
return ret;
}
void dfs(vector<int>& nums, int i)
{
if(i == nums.size())
{
ret.push_back(path);
return;
}
//选
path.push_back(nums[i]);
dfs(nums,i+1);
path.pop_back();
//不选
dfs(nums,i+1);
}
};
解法二:
C++代码:
class Solution {
vector<vector<int>> ret;
vector<int> path;
public:
vector<vector<int>> subsets(vector<int>& nums) {
dfs(nums,0);
return ret;
}
void dfs(vector<int>& nums, int pos)
{
ret.push_back(path);//每次进入dfs都是一个结果
for(int i = pos; i < nums.size(); i++)
{
path.push_back(nums[i]);
dfs(nums,i+1);
path.pop_back();
}
}
};