【专题二 --- 二叉树中的深搜】

二叉树中的深搜

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

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值