Subsets/Combination/Permutation

什么时候需要sort 什么时候需要index parameter

Sort: 提前Break

小技巧:可以把上次的结果传入参数 prev

重复元素的处理 :sort然后结合visited array对结果判断 默认相同的元素之间存在位置关系

Difference between combination and permutation

78. Subsets

public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        Arrays.sort(nums);
        helper(res,new ArrayList(),0,nums);
        return res;
    }
    public void helper(List<List<Integer>> res,List<Integer> cur,int start,int[] nums){
        res.add(new ArrayList(cur));
        for(int i = start;i < nums.length;i++){
            cur.add(nums[i]);
            helper(res,cur,i+1,nums);
            cur.remove(cur.size()-1);
        }
    }
}

77. Combinations

Do not need to sort this time because the numbers are organized 1....n

public class Solution {
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if(k > n) return res;
        combine(res,new ArrayList<Integer>(), 1, n, k);
        return res;
    }
    public void combine(List<List<Integer>> res,List<Integer>list, int start, int n,int k){
        if(list.size() == k){
            res.add(new ArrayList<Integer>(list));
            return;
        }
        for(int i = start; i <= n;i++){
            list.add(i);
            combine(res,list,i+1,n,k);
            list.remove(list.size()-1);
        }
    }
}

39. Combination Sum

重复的元素可以多选几次 所以下一次进入的index应该是不变的

public class Solution {
    //for every number in the array, add or not add
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
         Arrays.sort(candidates);
         List<List<Integer>> res = new ArrayList<List<Integer>>();
         sum(res,new ArrayList<Integer>(),candidates,target,0);
         return res;
    }
    public void sum(List<List<Integer>> res, List<Integer> list, int candidates[],int target,int start){
        if(target == 0){
            res.add(new LinkedList<Integer>(list));
            return;
        }
        if(target > 0){
            for(int i = start;i < candidates.length;i++){
                if(target < candidates[i]) return;
                list.add(candidates[i]);
                sum(res,list,candidates,target-candidates[i],i);
                list.remove(list.size()-1);
            }
        }
    }
}

40. Combination Sum II

每个元素选一次 index参数加1 /重复元素处理

每次copy长度为K的List O(k)一共有指数个组合 生成这些组合O(2^n) 所以一共的复杂度 O(K*2^n)

/** Skip the duplicated values **/

public class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        Arrays.sort(candidates);
        helper(res,new ArrayList<Integer>(),target,0,candidates);
        return res;
    }
    public void helper(List<List<Integer>> res,List<Integer> cur,int target,int start,int[] arr){
        if(target == 0){
            res.add(new ArrayList<Integer>(cur));
            return;
        }
        if(target < 0 || start >= arr.length){
            return;
        }
        //Iterate
        for(int i = start;i < arr.length;i++){
            if(i > start && arr[i-1] == arr[i])continue;
            if(arr[i] > target)break;
            cur.add(arr[i]);
            helper(res,cur,target - arr[i],i+1,arr);
            cur.remove(cur.size()-1);
        }
    }
}

377. Combination Sum IV

Return the number of ways.. reminds of Dynamic Programming! If using DFS directly here it would get TLE so need to use HashMap to record the internal result

public class Solution {
    public int combinationSum4(int[] nums, int target) {
        return dfs(nums, 0, target, new HashMap<Integer, Integer>());
    }

    private int dfs(int[] nums, int curSum, int target, HashMap<Integer, Integer> map){
        if(curSum > target) return 0;
        if(curSum == target) return 1;

        if(map.containsKey(curSum)) return map.get(curSum);

        int count = 0;
        for(int i = 0; i < nums.length; i++){
            count += dfs(nums, curSum + nums[i], target, map);
        }

        map.put(curSum, count);
        return count;
    }
}

Dynamic Programming:

Not the same as backpack problem, we are computing the number of ways instead of the max/min use the target value as the outer loop

public class Solution {
    public int combinationSum4(int[] nums, int target) {
        // dp[sum] = number of ways to get sum 
        int[] dp = new int[target + 1];

        // initialize, one way to get 0 sum with 0 coins
        dp[0] = 1;

        for(int j = 1; j <= target; j++){
            for(int i = 0; i < nums.length; i++){
                if(j - nums[i] >= 0) dp[j] += dp[j - nums[i]]; 
            }
        }

        return dp[target];
    }
}

254. Factor Combinations

循环Same here, traverse through the possible candidate and compute the leftovers. Since we are adding element in sorted order no need to care about duplicate.

public class Solution {
    // the factors are from [2,n) so we try divide each of them, if there is no left over, add to the list and then we continue until the result is 0 but the first version contains duplicate result such as <2,2,3>
        //how to avoid that? --总结怎么在reccursion中去除重复

    public List<List<Integer>> getFactors(int n) {
        if(n==1){
            return new ArrayList();
        }
        List<List<Integer>> result = new ArrayList();
        helper(result,new ArrayList(),2,n);
        return result;
    }
    public void helper(List<List<Integer>> result,List<Integer>list,int start,int n){
        if(n <=1 ){
            //排除只有一种的情况 比如元素本身
            if(list.size() > 1 ) result.add(new ArrayList(list));
            return;
        }
        //每一层递减 比如传入8,则下一次8的factor就要从2~7中找 然而实际上还可以再缩小范围
        for(int i = start;i <= (int)Math.sqrt(n);i++){
            if(n%i==0){
                list.add(i);
                helper(result,list,i,n/i);
                list.remove(list.size()-1);
            }
        }
        //还是要检查n,因为上面的循环只是到平方根
        list.add(n);
        helper(result,list,n,1);
        list.remove(list.size()-1);
    }

}

46. Permutations

public class Solution { //加了一个boolean
   public List<List<Integer>> permute(int[] num) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if(num.length == 0 || num == null) return res;

        boolean[] visited = new boolean[num.length];  
        helper(num,res,new ArrayList<Integer>(),visited);
        return res;
    }
    public void helper(int[] num, List<List<Integer>> res, ArrayList<Integer> item,boolean[] visited){
        if(item.size() == num.length){
            res.add(new ArrayList<Integer>(item));
        }

        for(int i = 0; i < num.length;i++){
            if(!visited[i]){
                item.add(num[i]);
                visited[i] = true;
                helper(num,res,item,visited);
                item.remove(item.size()-1);
                visited[i] = false;
            }
        }
    }
 }

247. Strobogrammatic Number II

又是一个让我们枚举出所有结果的题 还是通过两个坐标左右夹击 枚举出结果 用数组表示
字典 判断什么时候到达了中间的点(只能用1,0,8) 以及0不能作为数字的开头

public class Solution {
    public List<String> findStrobogrammatic(int n) {
        List<String> list = new ArrayList<>();
        char[] num1 = {'0','1','8','6','9'};
        char[] num2 = {'0','1','8','9','6'};
        char[] number = new char[n];

        dfs(list, number, num1, num2, 0);

        return list;
    }

    private void dfs(List<String> list, char[] number, char[] num1, char[] num2, int index){
        int left = index;
        int right = number.length - index - 1;

        if(left > right){
            list.add(new String(number));
            return;
        }
        // We can fill in 0,1,8 only
        if(left == right){
            for(int i = 0; i < 3; i++){
                number[left] = num1[i];
                dfs(list, number, num1, num2, index + 1);
            }
        } else {
            for(int i = 0; i < num1.length; i++){
                if(index == 0 && i == 0) continue;
                number[left] = num1[i];
                number[right] = num2[i];
                dfs(list, number, num1, num2, index + 1);
            }
        }
    }

还有一种写法 可以参考248的helper

248. Strobogrammatic Number III

让计算在给定的range里的strobogrammatic个数 利用247里的方法 枚举出所有在给定的数的

位数范围内的数 然后一一判断在边缘的数是否在范围之内 这是以一种很笨的方法了。。

public class Solution {
    //0,1,6,8,9
    //we could find the total number of items smaller than a number 
    public int strobogrammaticInRange(String low, String high) {
        List<String> res = new ArrayList<String>();
        int count = 0;
        //产生在位数之间的
        for(int i = low.length();i <= high.length();i++){
            res.addAll(helper(i,i));
        }
        for(String s: res){
            if((s.length() == low.length() && s.compareTo(low) < 0)||((s.length() == high.length() && s.compareTo(high)>0)))continue;
            count++;
        }
        return count;
    }
    //特定位数的Strobogrammatic Number 以2为步长向里面循环
    public List<String> helper(int idx,int n){
        if(idx == 0)return new ArrayList<String>(Arrays.asList(""));
        if(idx == 1)return new ArrayList<String>(Arrays.asList("1","0","8"));
        List<String> sub = helper(idx - 2,n);
        //when can we add 0 to the start and end, whenever we are not processing the last step 
        List<String> res  = new ArrayList<String>();
        for(int i = 0;i < sub.size();i++){
            String cur = sub.get(i);
            if(idx < n)res.add("0" + cur + "0");
            res.add("1" + cur + "1");
            res.add("8" + cur+ "8");
            res.add("6" + cur+ "9");
            res.add("9" + cur+ "6");
        }
        return res;
    }
}

results matching ""

    No results matching ""