leetcode笔记|dfs和回溯

搜索树

例1 判断搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

解1:暴力深搜,左每个节点值小于根,且左为搜索树

class Solution {
public:
    bool cmp(TreeNode* child,int val,bool op){
        bool flag=true;
        if(child!=nullptr){
            if(op)flag=flag&&(child->val<val)&&cmp(child->left,val,op)&&cmp(child->right,val,op);
            else flag=flag&&(child->val>val)&&cmp(child->left,val,op)&&cmp(child->right,val,op);
        }
        return flag;
    }
    bool isValidBST(TreeNode* root) {
        // if(root->left==nullptr&&root->right==nullptr)
        bool flag=true;
        if(root->left!=nullptr)
            flag= flag&&cmp(root->left,root->val,1)&&isValidBST(root->left);
        if(root->right!=nullptr)
            flag= flag&&cmp(root->right,root->val,0)&&isValidBST(root->right);
        return flag;
    }
};

解2:动态范围

class Solution {
public:
    bool cmp(TreeNode* child,long long lrgn,long long rrgn){
        if(child==nullptr)return true;
        if(child->val<=lrgn||child->val>=rrgn)return false;
        return cmp(child->left,lrgn,child->val)&&cmp(child->right,child->val,rrgn);
    }
    bool isValidBST(TreeNode* root) {
        return cmp(root, LONG_MIN, LONG_MAX);
    }
};

 解3:搜索二叉树中序遍历递增有序,用栈求出中序序列并比较栈顶与上一个值

class Solution {
public:
    bool isValidBST(TreeNode* root) {
        stack<TreeNode*> stack;
        long long inorder = (long long)INT_MIN - 1;

        while (!stack.empty() || root != nullptr) {
            while (root != nullptr) {
                stack.push(root);
                root = root -> left;
            }
            root = stack.top();
            stack.pop();
            // 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树
            if (root -> val <= inorder) {
                return false;
            }
            inorder = root -> val;
            root = root -> right;
        }
        return true;
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/validate-binary-search-tree/solutions/230256/yan-zheng-er-cha-sou-suo-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

例2 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

解1 暴力深搜,时间复杂度很高

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        if(nums.size()==1)return {{nums[0]}};
        if(nums.size()==2)return {{nums[0],nums[1]},{nums[1],nums[0]}};
        vector<vector<int>> result;
        for(auto i:nums){
            vector<int> tmp=nums;
            auto index=find(tmp.begin(),tmp.end(),i);
            tmp.erase(index);
            vector<vector<int>> resultTmp=permute(tmp);
            for(auto j:resultTmp){
                j.push_back(i);
                result.push_back(j);
            }
        }
        return result;
    }
};

 解2 回溯,利用上一步结果

解1dfs一步步组装,递归空间深度很大,且有重复分支(每得出一个结果都要从头组装)。

解2可以理解为利用前一步组装的结果(既定前缀),再选择元素填下一个位置,填完一次后回溯(通过弹出已填位置)。

因此回溯函数里的参数是:当前排列ans(需要填),nums数组(选择需要填进去的元素)

填的位置:ans的末尾;

填的元素:nums分为两部分,前ans.size()个为已填进去的元素,每次取nums的ans.size()位置的元素填,通过交换元素将下一个选择放到ans.size()位置

详细看题解:46. 全排列 - 力扣(LeetCode)

class Solution {
public:
    void swap(int a, int b,vector<int>& nums){
        int tmp=nums[a];
        nums[a]=nums[b];
        nums[b]=tmp;
    }
    void backtrack(vector<int>& nums,vector<int>& ans,vector<vector<int>>& resultBox){
        if(ans.size()==nums.size()){
            resultBox.push_back(ans);
            return;
        }
        int index=ans.size();
        for(int i=index;i<nums.size();i++){
            swap(i,index,nums);
            ans.push_back(nums[index]);
            backtrack(nums,ans,resultBox);
            ans.pop_back();
            swap(i,index,nums);
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> ans;
        vector<vector<int>> resultBox;
        backtrack(nums,ans,resultBox);
        return resultBox;
    }
};

组合

例1 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

class Solution {
public:
    string dict[8]={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};

    void backtrack(int depth,string curr,int h,vector<string>& result,string digits){
        if(curr.length()==h)result.push_back(curr);
        if(depth>=h)return;
        int digit=(digits[depth]-'0')-2;
        int n=(dict[digit]).length();
        for(int i=0;i<n;i++){
        curr=curr+dict[digit][i];
        backtrack(depth+1,curr,h,result,digits);
        curr.erase(depth,1);   
        }
    }

    vector<string> letterCombinations(string digits){
        if(!digits.length())return {};
        int h=digits.length();
        vector<string> result;
        backtrack(0,"",h,result,digits);
        return result;
    }
};

例2 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

回溯剪枝:

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
       sort(candidates.begin(),candidates.end());
    //    if(target<=0)return {};
       vector<int> tmp=candidates;
       vector<vector<int>> resultBox;
       for(auto i:candidates){
           if(target==i){resultBox.push_back({i});continue;};
           if(target<i)continue;
           vector<vector<int>> tmp2=combinationSum(tmp,target-i);
           if(!tmp2.empty()){
               for(auto j:tmp2){
                j.push_back(i);
                resultBox.push_back(j);
               }
           }
           vector<int>::iterator k = tmp.begin();
           tmp.erase(k);//删除第一个元素
       }
       return resultBox;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值