LeetCode(二)DFS+回溯专题

LeetCode 17. Letter Combinations of a Phone Number

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

class Solution {
public:
    
    vector<string> ans;
    
    char ops[10][10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    
    vector<string> letterCombinations(string digits) {
        dfs(digits, 0, "");
        return ans;
    }
    
    void dfs(string &digits, int u, string path)
    {
        if(u == digits.size())
        {
            if(path.size()) ans.push_back(path);//需判断边界情况
            return ; //很重要!!!
        }
        int v = digits[u] - '0';//当前位
        for(int i = 0; ops[v][i]; i++)
        {
            dfs(digits,u + 1, path + ops[v][i]);
        }
    }
};

LeetCode 46. Permutations

给定一个没有重复数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

class Solution {
public:
    
    vector<vector<int>> ans; //答案
    vector<int> path;//方案
    vector<bool> st;//没有使用过的数字
    
    vector<vector<int>> permute(vector<int>& nums) {
        st = vector<bool>(nums.size(), false);//初始化
        dfs(nums, 0);
        return ans;
    }
    
    void dfs(vector<int>& nums, int u)
    {
        if(u == nums.size())
        {
            ans.push_back(path);
            return;//重要
        }
        for(int i = 0; i < nums.size(); i++)
            if(!st[i])
            {
                st[i] = true, path.push_back(nums[i]);
                dfs(nums, u + 1);
                st[i] = false, path.pop_back();//恢复现场
            }
            
    }
    
};

LeetCode 47. Permutations II

给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

class Solution {
public:
    
    vector<vector<int>> ans;
    vector<int> path;
    vector<bool> st;//该位置有没有被占用
    
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end()); //从小到大排序
        path = vector<int>(nums.size()); //初始化写法
        st = vector<bool>(nums.size(), false);
        dfs(nums, 0, 0);//第三位 相同元素相对位置
        return ans;        
    }
    
    void dfs(vector<int> &nums, int u, int start)
    {
        if(u == nums.size())
        {
            ans.push_back(path);
            return;
        }
        for(int i = start; i < path.size(); i++) //i从start开始循环
        {
            if(st[i]) continue;//该位置被用过,循环下一位
            path[i] = nums[u], st[i] = true;
            if(u + 1 < nums.size() && nums[u + 1] == nums[u]) 
                dfs(nums, u + 1, i + 1);//从i+1开始循环保证相对顺序不变
            else dfs(nums, u + 1, 0);//没必要调序,则从0开始循环
            st[i] = false;
        }
    }
    
};

LeetCode 78. Subsets

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> ans;
        for(int i = 0; i < (1 << n); i++)
        {
            vector<int> path;
            for(int j = 0; j < n; j++) //枚举是不是1
                if(i >> j & 1) //判断第j位是不是1
                    path.push_back(nums[j]);//push nums[j] 是1 push第 j位
            ans.push_back(path);
        }
        return ans;
    }
};

LeetCode 90. Subsets II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

class Solution {
public:
    
    vector<vector<int>> ans;
    vector<int> path;//当前方案
    
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        dfs(nums, 0);
        return ans;        
    }
    
    void dfs(vector<int> &nums, int u)
    {
        if(u == nums.size())
        {
            ans.push_back(path);
            return;
        }
        
        int k = u;
        while(k < nums.size() && nums[k] == nums[u]) k++;//枚举重复位
        dfs(nums, k);//对重复位下一位操作
        for(int i = u; i < k; i++) //对重复位操作
        {
            path.push_back(nums[i]);
            dfs(nums, k);
        }
        //恢复现场
        while(path.size() && path.back() == nums[u]) path.pop_back();
    }
};

LeetCode 39. Combination Sum

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

示例 2:

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

class Solution {
public:
    
    vector<vector<int>> ans;
    vector<int> path;
    
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        dfs(candidates, 0, target);
        return ans;       
    }
    
    void dfs(vector<int>&candidates, int u, int target)
    {
        if(u == candidates.size())
        {
            if(!target) ans.push_back(path);//target减为0 记录方案
            return;
        }
        dfs(candidates, u + 1, target);
        while(target >= candidates[u])
        {
            path.push_back(candidates[u]);
            target -= candidates[u];
            dfs(candidates, u + 1, target);
        }
        //恢复现场
        while(path.size() && path.back() == candidates[u]) path.pop_back();
    }
};

LeetCode 40. Combination Sum II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]

class Solution {
public:
    
    vector<vector<int>> ans;
    vector<int> path;
    
    vector<vector<int>> combinationSum2(vector<int>& a, int target) {
        sort(a.begin(), a.end());
        dfs(a, path, 0, target);
        return ans;        
    }
    
    void dfs(vector<int>&a, vector<int>&path, int u, int target)
    {        
        if(!target)
        {
            ans.push_back(path);
            return;
        }
        if(u == a.size()) return; //顺序要在后面
        
        int k = u + 1;
        while(k < a.size() && a[k] == a[u]) k++;
        
        dfs(a, path, k, target);
        int value = a[u];
        for(int i = value, j = u; j < k && i <= target; i += value, j++)
        {
            path.push_back(value);
            dfs(a, path, k, target - i); //从开始下一次循环
        }
        while(!path.empty() && path.back() == value) 
            path.pop_back();//恢复现场
        
    }
};

LeetCode 216. Combination Sum III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

所有数字都是正整数。
解集不能包含重复的组合。

示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

class Solution {
public:
    vector<vector<int>> ans;
    vector<vector<int>> combinationSum3(int k, int n) {
        
        for(int i = 0; i < (1 << 9); i++)//512
        {
            vector<int> path;
            int sum = 0;
            for(int j = 0; j < 9; j++)
                if(i >> j & 1)
                {
                    path.push_back(j + 1);
                    sum += j + 1;
                }
            if(sum == n && path.size() == k)
            {
                ans.push_back(path);
            }
        }
        return ans;
    }
};

LeetCode 22. Generate Parentheses

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

例如,给出 n = 3,生成结果为:

[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]

class Solution {
public:
    int m;
    vector<string> ans;
    vector<string> generateParenthesis(int n) {
        m = n;
        dfs("", 0, 0);
        return ans;        
    }
    void dfs(string path, int l, int r)//左 右 括号数
    {
        if(l == m && r == m)
        {
            ans.push_back(path);
            return;
        }
        if(l < m) dfs(path + '(', l + 1, r); //l < m
        if(l > r) dfs(path + ')', l, r + 1);
    }
};

LeetCode 473. Matchsticks to Square

还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。

输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

示例 1:

输入: [1,1,2,2,2]
输出: true

解释: 能拼成一个边长为2的正方形,每边两根火柴。

示例 2:

输入: [3,3,3,3,4]
输出: false

解释: 不能用所有火柴拼成一个正方形。

注意:

给定的火柴长度和在 0 到 10^9之间。
火柴数组的长度不超过15。

class Solution {
public:
    
    int side;//每条边长度
    vector<bool> st;//每根棍是不是被用
    
    bool makesquare(vector<int>& nums) {
        if(nums.empty()) return false;
        int sum = 0;
        for(auto &x : nums) sum += x;
        if(sum % 4) return false;
        side = sum / 4;
        //先搜大木棍
        sort(nums.begin(), nums.end());
        st = vector<bool>(nums.size(), false);
        return dfs(nums, 0, side, nums.size() - 1);
    }
    
    bool dfs(vector<int>&nums, int count, int sum, int start)
    {
        if(!sum)
        {
            if( ++ count == 4) return true;
            return dfs(nums, count, side, nums.size() - 1);//拼下一个木棍
        }
        for(int i = start; i >= 0; i --)
            if(!st[i] && sum >= nums[i])
            {
                //剪枝1 上一根木棍失败 当前根与上一根一样 也会失败
                if(i + 1 < nums.size() && !st[i + 1] && nums[i + 1] == nums[i]) continue;
                st[i] = true;
                if(dfs(nums, count, sum - nums[i], i - 1)) return true;//有解返回true
                st[i] = false;
                //剪枝2 失败的木棍是开头或结尾
                if(nums[i] == sum || sum == side) return false;
            }
            return false;
    }
};

LeetCode 52. N-Queens II

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
LeetCode(二)DFS+回溯专题_第1张图片
上图为 8 皇后问题的一种解法。

给定一个整数 n,返回 n 皇后不同的解决方案的数量。

示例:

输入: 4
输出: 2
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],

["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]

class Solution {
public:
    
    int ans, n;
    vector<bool> col, diag, anti_diag;
    
    int totalNQueens(int _n) {
        n = _n;
        col = vector<bool>(n, false);
        diag = anti_diag = vector<bool>(2 * n, false);
        dfs(0); //第0行开始
        return ans;        
    }
    
    void dfs(int u)
    {
        if(u == n)
        {
            ans++;
            return;
        }
        for(int i = 0; i < n; i++)
            //如何判断是否在对角上呢?正对角就是相加之和一样的,负对角就是相减只差一样的
            if(!col[i] && !diag[u + i] && !anti_diag[u - i + n])//会变负数 再加个n
            {
                col[i] = diag[u + i] = anti_diag[u - i + n] = true;
                dfs(u + 1);
                col[i] = diag[u + i] = anti_diag[u - i + n] = false;
            }
    }
};

LeetCode 37. Sudoku Solver

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

空白格用 ‘.’ 表示。

Note:

给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。

class Solution {
public:
    
    bool row[9][9], col[9][9], sq[3][3][9];//row 第一行 1-9存不存在数字
    
    void solveSudoku(vector<vector<char>>& board) {
        memset(row, 0, sizeof row);
        memset(col, 0, sizeof col);
        memset(sq, 0, sizeof sq);
        for(int i = 0; i < 9; i++)
            for(int j = 0; j < 9; j++)
                if(board[i][j] != '.')
                {
                    int v = board[i][j] - '1';
                    row[i][v] = col[j][v] = sq[i/3][j/3][v] = true;
                }
        dfs(board, 0, 0);
    }
    
    bool dfs(vector<vector<char>>&board, int x, int y)
    {
        if(y == 9) y = 0, x++;
        if(x == 9) return true;
        
        if(board[x][y] != '.') return dfs(board, x, y + 1);
        
        for(int i = 0; i < 9; i ++)
            if(board[x][y] == '.' && !row[x][i] && !col[y][i] && !sq[x/3][y/3][i])
            {
                board[x][y] = '1' + i;
                row[x][i] = col[y][i] = sq[x/3][y/3][i] = true;
                if(dfs(board, x, y + 1)) return true;
                board[x][y] = '.';
                row[x][i] = col[y][i] = sq[x/3][y/3][i] = false;

            }
        return false;            
    }
};

LeetCode 282. Expression Add Operators

给定一个仅包含数字 0-9 的字符串和一个目标值,在数字之间添加二元运算符(不是一元)+、- 或 * ,返回所有能够得到目标值的表达式。

示例 1:

输入: num = “123”, target = 6
输出: [“1+2+3”, “123”]

示例 2:

输入: num = “232”, target = 8
输出: [“23+2", "2+32”]

示例 3:

输入: num = “105”, target = 5
输出: [“1*0+5”,“10-5”]

示例 4:

输入: num = “00”, target = 0
输出: [“0+0”, “0-0”, “0*0”]

示例 5:

输入: num = “3456237490”, target = 9191
输出: []

class Solution {
public:
    
    vector<string> ans;
    
    vector<string> addOperators(string num, int target) {
        dfs(num, "", 0, 0, 0, target);//从第0位开始 和为0 积为0
        return ans;        
    }
    
    void dfs(string &num, string path, int pos, long long sum, long long mul, int target)
    {
        if(pos == num.size())
        {
            if(sum == target) ans.push_back(path);
            return;
        }
        
        for(int i = pos; i < num.size(); i++) //从前枚举到最后一位
        {
            if(i > pos && num[pos] == '0') break;
            long long v = 0;
            for(int j = pos; j <= i; j++) v = v * 10 + num[j] - '0'; //多位数
            string number = num.substr(pos, i - pos + 1);
            if(!pos) //当前位是0
            {
                dfs(num, number, i + 1, v, v, target);
            }
            else
            {
                dfs(num, path + '+' + number, i + 1, sum + v, v, target);
                dfs(num, path + '-' + number, i + 1, sum - v, -v, target);
                dfs(num, path + '*' + number, i + 1, sum - mul + mul * v, mul * v, target);
            }
        }
    }
};	

LeetCode 301. Remove Invalid Parentheses

删除最小数量的无效括号,使得输入的字符串有效,返回所有可能的结果。

说明: 输入可能包含了除 ( 和 ) 以外的字符。

示例 1:

输入: “()())()”
输出: ["()()()", “(())()”]

示例 2:

输入: “(a)())()”
输出: ["(a)()()", “(a())()”]

示例 3:

输入: “)(”
输出: [""]

class Solution {
public:
    
    set<string> ans;
    
    vector<string> removeInvalidParentheses(string s) {
        int left = 0, right = 0;
        for(auto c : s)
            if(c == '(') 
            {
                left++;
            }
            else if(c == ')')
            {
                if(left) left--;
                else right ++;
            }
        dfs(s, 0, left, right);
        return vector<string>(ans.begin(), ans.end());//把所有方案放到set里面判重       
        
    }
    
    bool check(string s) //检查左右括号数目是不是相等
    {
        int cnt = 0;
        for(auto c : s)
            if(c == '(')
                cnt ++;
            else if(c == ')')
            {
                cnt --;
                if(cnt < 0) return false;
            }
        return cnt == 0; 
    }
    
    void dfs(string s, int u, int left, int right)
    {
        if(u == s.size())
        {
            if(check(s)) ans.insert(s); //合法记录答案
            return;
        }
        dfs(s, u + 1, left, right);//不删的情况,枚举下一位
        if(left > 0 && s[u] == '(')
        {
            string rans = s;//备份
            s.erase(s.begin() + u);
            dfs(s, u, left - 1, right);
            s = rans;//恢复现场
        }
        if(right > 0 && s[u] == ')')
        {
            string rans = s;//备份
            s.erase(s.begin() + u);
            dfs(s, u, left, right - 1);
            s = rans;//恢复现场
        }
    }
};	

你可能感兴趣的:(LeetCode刷题)