组合 排列 记忆化搜索

Java知识点

//Arrays.fill()
Arrays.fill(array, content);

//StringBuilder删除某个位置的字符
sb.deleteCharAt(index)

一 题目列表

其实就是在做深度优先搜索(遍历)遍历保存路径 然后随时检查当前路径是否符合条件 满足就加在结果中

1 组合

39 Combination Sum
40 Combination Sum II
216 Combination Sum III
78 Subsets
90 Subsets II
lt680. Split String

2 排列

46 Permutations
47 Permutations II
lt10 String Permutation II
51 N-Queens
52 N-Queens II

3 记忆化搜索

44 Wildcard Matching
10 Regular Expression Matching
140 Word Break II

二 组合

39. Combination Sum

元素可以重复使用 求和是target组合

class Solution {
    public List> combinationSum(int[] candidates, int target) {
        List> result = new ArrayList<>();
        Arrays.sort(candidates);
        helper(candidates, 0, new ArrayList(), result, target);
        return result;
    }
    private void helper(int[] candidates, int index, List subset, List> result, int target){
        //深度拷贝
        if(target==0)
            result.add(new ArrayList(subset));
        if(index==candidates.length)
            return;
        for(int i=index; i0 && candidates[i]==candidates[i-1])
                continue;
            if(candidates[i]>target)
                return;
            subset.add(candidates[i]);
            //可以重复使用 所以i不更新
            helper(candidates, i, subset, result, target-candidates[i]);
            subset.remove(subset.size()-1);
            
        }
    }
}

40. Combination Sum II

元素不可以重复使用 求和是target组合

class Solution {
    public List> combinationSum2(int[] candidates, int target) {
        List> result = new ArrayList<>();
        if(candidates==null || candidates.length==0)
            return result;
        Arrays.sort(candidates);
        helper(candidates, 0, new ArrayList(), result, target);
        return result;
    }
    private void helper(int[] candidates, int index, List subset, List> result, int target){
        if(target==0)
            result.add(new ArrayList(subset));
        if(index==candidates.length)
            return;
        for(int i=index; iindex && candidates[i]==candidates[i-1])
                continue;
            if(target

216. Combination Sum III

求k个1-9不同的数组合 和是n

class Solution {
    public List> combinationSum3(int k, int n) {
        List> result = new ArrayList<>();
        if(n>45 || k>9 || k<=0)
            return result;
        helper(1, new ArrayList(), result, n, k);
        return result;
    }
    private void helper(int index, List subset, List> result, int target, int k){
        if(target==0 && k==0)
            result.add(new ArrayList(subset));
        if(index==10)
            return;
        for(int i=index; i<=9; i++){
            if(target

78. Subsets

没有重复

class Solution {
    public List> subsets(int[] nums) {
        List> result = new ArrayList<>();
        if(nums==null || nums.length==0)
            return result;
        helper(nums, 0, new ArrayList(), result);
        return result;
    }
    private void helper(int[] nums, int index, ArrayList subset, List> result){
        result.add(new ArrayList<>(subset));
        if(index>=nums.length)
            return;
        for(int i=index; i

90. Subsets II

又重复

class Solution {
    public List> subsetsWithDup(int[] nums) {
        List> result = new ArrayList<>();
        if(nums==null || nums.length==0)
            return result;
        Arrays.sort(nums);
        helper(nums, 0, new ArrayList(), result);
        return result;
    }
    private void helper(int[] nums, int index, List subset, List> result){
        result.add(new ArrayList( subset));
        if(index==nums.length)
            return;
        for(int i=index; iindex && nums[i]==nums[i-1])
                continue;
            subset.add(nums[i]);
            helper(nums, i+1, subset, result);
            subset.remove(subset.size()-1);
        }
    }
}

lt680. Split String

Given the string "123"
return [["1","2","3"],["12","3"],["1","23"]]

public class Solution {
    /*
     * @param : a string to be split
     * @return: all possible split string array
     */
    public List> splitString(String s) {
        // write your code here
        List> result = new ArrayList<>();
        if(s==null )
            return result;
        helper(s, 0, new ArrayList(), result);
        return result;
    }
    private void helper(String s, int index, ArrayList subset, List> result){
        if(index==s.length()){
            result.add(new ArrayList(subset));
            return;
        }
        for(int i=index; i

组合

46. Permutations

无重复元素

class Solution {
    public List> permute(int[] nums) {
        List> result = new ArrayList<>();
        if(nums==null || nums.length==0)
            return result;
        boolean[] visited = new boolean[nums.length];
        helper(nums, visited, new ArrayList<>(), result);
        return result;
    }
    private void helper(int[] nums, boolean[] visited, List subset, List> result){
        if(subset.size()==nums.length){
            result.add(new ArrayList(subset));
            return;
        }
        for(int i=0; i

47. Permutations II

有重复元素

class Solution {
    public List> permuteUnique(int[] nums) {
        List> result = new ArrayList<>();
        if(nums==null || nums.length==0)
            return result;
        Arrays.sort(nums);
        boolean[] visited = new boolean[nums.length];
        helper(nums, visited, new ArrayList<>(), result);
        return result;
    }
    private void helper(int[] nums, boolean[] visited, List subset, List> result){
        if(subset.size()==nums.length){
            result.add(new ArrayList(subset));
            return;
        }
        for(int i=0; i0 && nums[i]==nums[i-1] && !visited[i-1])
                continue;
            if(visited[i])
                continue;
            visited[i]=true;
            subset.add(nums[i]);
            helper(nums, visited, subset, result);
            subset.remove(subset.size()-1);
            visited[i]=false;
        }
    }
}

lt10. String Permutation II

public class Solution {
//Given "aabb", return ["aabb", "abab", "baba", "bbaa", "abba", "baab"].
    /**
     * @param str: A string
     * @return: all permutations
     */
    public List stringPermutation2(String str) {
    public List stringPermutation2(String str) {
        // write your code here
        List results = new ArrayList<>();
        str = sort(str);
        boolean[] visited = new boolean[str.length()];
        helper(str, visited, new StringBuilder(), results);
        return results;
    }
    private String sort(String str){
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        return new String(chars);
    }
    private void helper(String str, boolean[] visited, StringBuilder sb, List results){
        if(sb.length()==str.length()){
            results.add(new String(sb.toString()));
            return;
        }
        for(int i=0; i0 && str.charAt(i)==str.charAt(i-1) && visited[i-1]==false)
                continue;
            sb.append(str.charAt(i));
            visited[i] = true;
            helper(str, visited, sb, results);
            visited[i] = false;
            sb.deleteCharAt(sb.length()-1);
        }
    }
}

51. N-Queens

返回所有棋盘布局

//先用0 - (n-1)的排列表示一个棋盘布局
//0 - (n-1)的排列 index代表row 值代表col
//优点在于 只用考虑斜线冲突 不会有同行或同列冲突
//相当于在找所有排列 只不过每次添加的时候考察是否存在冲突
//然后把这些排列转成棋盘布局
class Solution {
    public List> solveNQueens(int n) {
        List> numListLists = new ArrayList<>();
        Set cols = new HashSet();
        Set left = new HashSet();
        Set right = new HashSet();
        helper(n, cols, left, right, new ArrayList(), numListLists);
        return toChessBoards(numListLists, n);
    }
    private void helper(int n, Set cols, Set left, Set right, List list, List> lists){
        if(list.size() == n){
            lists.add(new ArrayList(list));
            return;
        }
        for(int i=1; i<=n; i++){
            if(cols.contains(i))
                continue;
            if(left.contains(list.size()+i))
                continue;
            if(right.contains(list.size()-i))
                continue;
            left.add(list.size()+i);
            right.add(list.size()-i);
            cols.add(i);
            list.add(i);
            helper(n, cols, left, right, list, lists);
            list.remove(list.size()-1);
            left.remove(list.size()+i);
            right.remove(list.size()-i);
            cols.remove(i);
        }
    }
    private List> toChessBoards(List> numListLists, int n){
        List> results = new ArrayList<>();
        char[] chars = new char[n];
        Arrays.fill(chars, '.');
        for(List numList: numListLists){
            List result = new ArrayList<>();
            for(Integer col: numList){
                chars[col-1] = 'Q';
                String temp = new String(chars);
                result.add(temp);
                chars[col-1] = '.';
            }
            results.add(result);
        }
        return results;
    }
}

52. N-Queens II

求可能的布局个数

// \冲突 差相等 n*n  可能的值 -(n-1) - (n-1) 所以加n对应到2n长度的d1
// /冲突 和相等 n*n 可能的值 0-2(n-1) 直接对应长度2n的d2
public class Solution {
    int count = 0;
    public int totalNQueens(int n) {
        Set cols = new HashSet();
        Set left = new HashSet();
        Set right = new HashSet();
        helper(n, cols, left, right, new ArrayList());
        return count;
    }
    private void helper(int n, Set cols, Set left, Set right, List list){
        if(list.size() == n){
            count++;
        }
        for(int i=1; i<=n; i++){
            if(cols.contains(i) || left.contains(list.size()+i) || right.contains(list.size()-i))
                continue;
            left.add(list.size()+i);
            right.add(list.size()-i);
            cols.add(i);
            list.add(i);
            helper(n, cols, left, right, list);
            list.remove(list.size()-1);
            left.remove(list.size()+i);
            right.remove(list.size()-i);
            cols.remove(i);
        }
    }
}

记忆化搜索

44. Wildcard Matching

(0,0)->(1,1)(1,0)(0,1)
(1,0)->(2,1)(1,1)(2,0)
(0,1)->(1,2)(1,1)(0,2)
可以发现已经有重复 所以使用记忆化搜索避免重复
本质上是在搜索过程中会遇到已访问过的节点
'?' Matches any single character.
'' Matches any sequence of characters (including the empty sequence).
时间复杂度为O(s.length()
p.length())

//时间复杂度为O(s.length()*p.length())
class Solution {
     public boolean isMatch(String s, String p) {
        if (s == null || p == null) {
            return false;
        }
        boolean[][] memo = new boolean[s.length()][p.length()];
        boolean[][] visited = new boolean[s.length()][p.length()];
        return isMatchHelper(s, 0, p, 0, memo, visited);
    }
    
    private boolean isMatchHelper(String s, int sIndex,
                                  String p, int pIndex,
                                  boolean[][] memo,
                                  boolean[][] visited) {
        // 如果 p 从pIdex开始是空字符串了,那么 s 也必须从 sIndex 是空才能匹配上
        if (pIndex == p.length()) {
            return sIndex == s.length();
        }
        
        // 如果 s 从 sIndex 是空,那么p 必须全是 * 
        if (sIndex == s.length()) {
            return allStar(p, pIndex);
        }
        
        if (visited[sIndex][pIndex]) {
            return memo[sIndex][pIndex];
        }
        
        char sChar = s.charAt(sIndex);
        char pChar = p.charAt(pIndex);
        boolean match;
        
        if (pChar == '*') {
            match = isMatchHelper(s, sIndex, p, pIndex + 1, memo, visited) ||
                isMatchHelper(s, sIndex + 1, p, pIndex, memo, visited);
        } else {
            match = charMatch(sChar, pChar) &&
                isMatchHelper(s, sIndex + 1, p, pIndex + 1, memo, visited);
        }
        
        visited[sIndex][pIndex] = true;
        memo[sIndex][pIndex] = match;
        return match;
    }
    
    private boolean charMatch(char sChar, char pChar) {
        return (sChar == pChar || pChar == '?');
    }
    
    private boolean allStar(String p, int pIndex) {
        for (int i = pIndex; i < p.length(); i++) {
            if (p.charAt(i) != '*') {
                return false;
            }
        }
        return true;
    }
}

10. Regular Expression Matching

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

class Solution {
    public boolean isMatch(String s, String p) {
        boolean[][] visited = new boolean[s.length()][p.length()];
        boolean[][] memo = new boolean[s.length()][p.length()];
        return helper(s, 0, p, 0, visited, memo);
    }
    public boolean helper(String s,
                          int indexS,
                          String p,
                          int indexP,
                          boolean[][] visited,
                          boolean[][] memo){
        if(indexS==s.length()){
            return checkEmpty(p, indexP);
        }
        if(indexP==p.length()){
            return indexS==s.length();
        }
        if(visited[indexS][indexP]){
            return memo[indexS][indexP];
        }
        boolean match = false;
        if(indexP

131. Palindrome Partitioning

求把一个字符串切割成所有字串都是回文串的所有方式
Input: "aab"
Output:
[ ["aa","b"], ["a","a","b"] ]
abaxxxx
如果不用memo xxxx重复计算
如果用了memo 在第一次迭代xxxx的结果已经计算出来 下次遇到直接返回结果就可以
Time complexity: O(n(2^n))
For a string with length n, there will be (n - 1) intervals between chars.
For every interval, we can cut it or not cut it, so there will be 2^(n - 1) ways to partition the string.
For every partition way, we need to check if it is palindrome, which is O(n).
So the time complexity is O(n
(2^n))
解的个数*构造一个解的时间

class Solution {
    public List> partition(String s) {
        // return withMemo(s);
        return withoutMemo(s);
    }
    public List> withMemo(String s) {
        List> result = new ArrayList<>();
        if(s==null || s.length()==0)
            return result;
        Map>> map = new HashMap<>();
        return help(s, 0, map);
    }
    private List> help(String s, int index, Map>> memo){
        if(index==s.length()){
            List> result = new ArrayList<>();
            result.add(new ArrayList<>());
            return result;
        }
        if(memo.containsKey(index)){
            return memo.get(index);
        }
        List> results = new ArrayList<>();
        for(int i=index; i> suffixs = help(s, i+1, memo);
            
            for(List list: suffixs){
                List result = new ArrayList<>(list);
                result.add(0, prefix);
                results.add(result);
            }
        }
        memo.put(index, results);
        return results;
    }
    
 
    public List> withoutMemo(String s) {
        List> result = new ArrayList<>();
        if(s==null || s.length()==0)
            return result;
        helper(s, 0, new ArrayList(), result);
        return result;
    }
    private void helper(String s, int index, List subset, List> result){
        if(index==s.length()){
            result.add(new ArrayList(subset));
            return;
        }
        for(int i=index; i

140. Word Break II

最坏情况 所有拆法都符合要求 一共2^(n-1)种拆法 所以最坏时间复杂度O(2^n)
记忆化的优点在于
"aaaaab", with wordDict = ["a", "aa", "aaa", "aaaa", "aaaaa", "aaaaa"],
map中 key
在第一次迭代之后 aaab的分割方案已经求过 在第二次迭代可以使用结果

class Solution {
    public List wordBreak(String s, List wordDict) {
        List result = new ArrayList<>();
        if(s==null || wordDict==null)
            return result;
        //key是string value是所有这个string能够被拆分成的词组组合list
        Map> memo = new HashMap<>();
        return helper(s, wordDict, memo);
    }
    private List helper(String s, List wordDict, Map> memo){
        if(memo.containsKey(s))
            return memo.get(s);
        List result = new ArrayList<>();
        if(s.length()==0)
            return result;
        if(wordDict.contains(s))
            result.add(s);
        for (int len = 1; len < s.length(); ++len){
            String word = s.substring(0, len);
            if (!wordDict.contains(word)) {
                continue;
            }
            String suffix = s.substring(len);
            List segmentations = helper(suffix, wordDict, memo);
            
            for (String segmentation: segmentations){
                result.add(word + " " + segmentation);
            }
        }
        memo.put(s, result);
        return result;
    }
}

139. Word Break

class Solution {
    public boolean wordBreak(String s, List wordDict) {
        Set set = new HashSet<>();
        for(String string : wordDict)
            set.add(string);
        return bfsMethod(s, set);
        // return dfsMethod(s, set);
    }
    public boolean dfsMethod(String s, Set dict) {
        // DFS
        Set set = new HashSet();
        return dfs(s, 0, dict, set);
    }
    
    private boolean dfs(String s, int index, Set dict, Set set){
        // base case
        if(index == s.length()) return true;
        // check memory
        //原理是如果曾经访问过 这里再次访问 说明后面的结果肯定是false
        if(set.contains(index)) return false;
        // recursion
        for(int i = index+1;i <= s.length();i++){
            String t = s.substring(index, i);
            if(dict.contains(t))
                if(dfs(s, i, dict, set))
                    return true;
                else
                    set.add(i);
        }
        set.add(index);
        return false;
    }
    
    public boolean bfsMethod(String s, Set dict) {
        if (dict.contains(s)) return true;
        Queue queue = new LinkedList();
        queue.offer(0);
        // use a set to record checked index to avoid repeated work.
        // This is the key to reduce the running time to O(N^2).
        Set visited = new HashSet();
        visited.add(0);
        while (!queue.isEmpty()) {
            int curIdx = queue.poll();
            for (int i = curIdx+1; i <= s.length(); i++) {
                if (visited.contains(i)) 
                    continue;
                if (dict.contains(s.substring(curIdx, i))) {
                    if (i == s.length()) 
                        return true;
                    queue.offer(i);
                    visited.add(i);
                }
            }
        }
        return false;
    }
}









































你可能感兴趣的:(组合 排列 记忆化搜索)