Leetcode:Combinations组合数&&Permutations排列数
来源:互联网 发布:linux sendmail smtp 编辑:程序博客网 时间:2024/05/22 06:29
组合数
Given two integers n and k, return all possible combinations of k numbers out of 1 … n.
For example,If n = 4 and k = 2, a solution is:[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4],]
解析:首先我的想法是k个轮次,每一轮给每个组合加一个数。i从1到k遍历,第一次就是[[1],[2],[3]],为了减少次数,剪去n-last小于还需加入的个数的情况,(last表示每个组合中最后面即最大的数)去掉加入即使加入当前值,后果的个数也不够的了情况。
public static List<List<Integer>> mycombine(int n, int k) { List<List<Integer>> combineList = new ArrayList<List<Integer>>(); for (int i = 1; i <= n; i++) { List<Integer> tmp = new ArrayList<>(); tmp.add(i); combineList.add(tmp); } int length = combineList.size(); if (k == 1) return combineList; else for (int i = 1 ; i < k ;i++) combineList.remove(--length); //根据k的大小,删除倒数k-1个元素 int curlength = 1; while (--k > 0) { for (int i = 0; i < combineList.size(); i++) { List<Integer> tmp = combineList.get(i); if (tmp.size() == curlength) { //因为不断向List里面添加先组合,加入的是已经在当前轮插入过的,就不需要再插入了 int last = tmp.get(curlength - 1); if (last < n && (n-last)>=k) { //相当于减枝,后面不够了 last++; tmp.add(last); combineList.set(i, tmp); } while (last < n && (n-last)>=k) { //如果最后一个元素仍然小于n,那么还可以换一个大的继续插入。 这时候要先拷贝出来,再删除最后一个刚插入的元素,然后插入新的 last++; List<Integer> tmp1 = new ArrayList<>(); tmp1.addAll(combineList.get(i)); tmp1.remove(curlength); tmp1.add(last); combineList.add(tmp1); } } } curlength++; } return combineList; }
用循环次数比较恐怖,所以当我们看到这题 首先的第一想法应该是用递归。
//LinkedList 和 ArrayList都实现了 List接口, LinkedList更适用于插入 //递归 //Basically, this solution follows the idea of the mathematical formula C(n,k)=C(n-1,k-1)+C(n-1,k). //Here C(n,k) is divided into two situations. Situation one, number n is selected, so we only need to select k-1 from n-1 next. Situation two, number n is not selected, and the rest job is selecting k from n-1. //C(n,k)表示从n个数中取k个,取n的情况:C(n-1,k-1),不取n:C(n-1,k),所以 C(n,k)=C(n-1,k-1)+C(n-1,k) public static List<List<Integer>> combine(int n, int k) { if (k == n || k == 0) { List<Integer> row = new LinkedList<>(); for (int i = 1 ; i <=k ; i++) row.add(i); return new LinkedList<>(Arrays.asList(row)); } List<List<Integer>> result = combine(n-1,k-1); result.forEach(e -> e.add(n)); result.addAll(combine(n-1,k)); return result; }
DFS and Backtracking:这个递归是一层一层深入下去的,首先看以1开头的组合数,之后是2开头,以此对n个数做了n次DFS。复杂度为O(n!),第一次是对n个数DFS ,第二次是对n-1个数DFS…… 另外再加剪枝操作。
//DFS 和 Backtracking //C++ 可以通过值传递curr和引用传递res, 达到DFS的目的 //但是 java对于List的传递 都是引用,故我们采用在最终add到res里是,使用new ArrayList<Integer>(curr) 来根据curr创建一个新的list变量 public static List<List<Integer>> DFScombine(int n,int k) { List<List<Integer>> res = new ArrayList<>(); if (n<=0) return res; List<Integer> curr = new ArrayList<>(); DFS(res,curr,n,k,1); return res; } private static void DFS(List<List<Integer>> res, List<Integer> curr, int n, int k, int level) { if (curr.size() == k) { res.add(new ArrayList<Integer>(curr)); return; } if (curr.size() > k) return; for (int i = level; i <= n ; i++) { curr.add(i); DFS(res,curr,n,k,i+1); curr.remove(curr.size()-1); //回溯 } }
组合数之和:
Given a set of candidate numbers (C) (without duplicates) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [2, 3, 6, 7] and target 7, A solution set is:[ [7], [2, 2, 3]]
这和上面那题差不多,不同的是可以重复的选一个元素,关系到递归的参数
public List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> result = new ArrayList<>(); if (target < 0) return result; List<Integer> curr = new ArrayList<>(); Arrays.sort(candidates); //排序,在这里不排序好像也没差 DFS(result, curr, candidates, target, 0); return result; }private void DFS(List<List<Integer>> result, List<Integer> curr, int[] candidates, int target, int start) { if (target == 0) { result.add(new ArrayList<>(curr)); return; } else if (target < 0) return; else for (int i = start; i < candidates.length; i++) { curr.add(candidates[i]); DFS(result, curr, candidates, target - candidates[i], i); //因为可以重复 curr.remove(curr.size() - 1); } }
组合数之和2
Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
给出的数组中含有重复的元素,但是我们的解不能有重复
//可以利用Set集合的特性 //组合数2和组合数1比:区别在于组合不能重复,dfs能遍历出所有的情况,会存在重复的。 所以我利用了Set的特性先把组合数存下来,再转类型 public List<List<Integer>> mycombinationSum2(int[] candidates, int target) { Set<List<Integer>> result = new HashSet<>(); List<List<Integer>> lists = new ArrayList<>(); if (target < 0) return lists; Arrays.sort(candidates); List<Integer> curr = new ArrayList<>(); myDFS(result,curr,candidates,target,0); lists.addAll(result); return lists; } private void myDFS(Set<List<Integer>> result, List<Integer> curr, int[] candidates, int target, int start) { if (target == 0) { result.add(new ArrayList<>(curr)); return; }else if (target<0) return; else for (int i = start ; i <candidates.length ; i ++) { curr.add(candidates[i]); myDFS(result,curr,candidates,target - candidates[i],i+1); curr.remove(curr.size() - 1); } }
public List<List<Integer>> combinationSum2(int[] candidates, int target) { List<List<Integer>> result = new ArrayList<>(); if (target < 0 ) return result; List<Integer> curr = new ArrayList<>(); Arrays.sort(candidates); DFS(result,curr,candidates,target,0); return result; } private void DFS(List<List<Integer>> result, List<Integer> curr, int[] candidates, int target, int start) { if (target == 0 ) { result.add(new ArrayList<>(curr)); return; }else if (target < 0) return; else for (int i = start ; i < candidates.length ; i++) { if (i > start && candidates[i] == candidates[i-1]) continue; //可以避免重复 curr.add(candidates[i]); DFS(result,curr,candidates,target-candidates[i],i+1); curr.remove(curr.size() - 1); //回溯 } } }/*画个图好好理解这层循环的意思,这层循环就是遍历第i层的所有情况 由start到end 假设当我们递归到n层(B节点)时达到了条件,此时回溯到n-1层(A节点),而在第n层被(remove)出去的那个点就是candi[i],此时i++,即下一个元素如何candi[i]相等,那么就相当于A节点的下一个儿子节点和上一个大小一样,那就会导致路径相同 例子比如 递归到[3,2] 然后回溯去掉了2,变成[3],此时下一个元素又是2,那么加进去就是重复的[3,2]*/
排列数
Given a collection of distinct numbers, return all possible permutations.
For example,[1,2,3] have the following permutations:[ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
解题思路:这个问题和组合数一样,首先我们想到的是用DFS递归求解,对数组中的每个元素,找到以它为首节点的排列。唯一的不同在于,这里需要另外一个数组来表示元素的访问与否,访问时,标为true,访问结束时,标回false。
public static List<List<Integer>> permute(int[] nums) { List<List<Integer>> result = new ArrayList<>(); Arrays.sort(nums); List<Integer> curr = new ArrayList<>(); //Set是无序的,remove时给index,不一定会删除哪个 DFS(result,curr,nums); return result; } private static void DFS(List<List<Integer>> result, List<Integer> curr, int[] nums) { if (curr.size() == nums.length) { result.add(new ArrayList<>(curr)); return; } for (int i = 0 ; i < nums.length ; i ++) { if (curr.contains(nums[i])) continue; //跳过已经含有的 curr.add(nums[i]); DFS(result,curr,nums); curr.remove(curr.size() - 1); } }
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
For example,[1,1,2] have the following unique permutations:[ [1,1,2], [1,2,1], [2,1,1]]
解题思路:与上题有两点不同:1.数组中有重复的元素。2.答案中不能有重复的排列
这就需要我们过滤,相同的排列了。可以先对数组排序,当我们求出以某个元素为首的排列时,后面和这个元素相同的元素都可以pass掉了。这在组合数中已经屡试不爽了。(remove出去的节点和接下来add进去的节点是同一层的。)
public static List<List<Integer>> permuteUnique(int[] nums) {// Set<List<Integer>> result = new HashSet<>(); // 使用Set 可以去重 List<List<Integer>> result = new ArrayList<>(); if (nums.length == 0) return result; Arrays.sort(nums); //这里必须先排序,不然无法通过 while(i+1<nums.length && nums[i] == nums[i+1]) i++; 来去重 //若使用Set 则可以不需要排序 List<Integer> curr = new ArrayList<>(); boolean[] flag = new boolean[nums.length]; //默认为false DFS(result,curr,nums,flag); return result; } private static void DFS(List<List<Integer>> result, List<Integer> curr, int[] nums, boolean[] flag) { if (curr.size() == nums.length) { result.add(new ArrayList<>(curr)); return; } for (int i = 0 ; i < nums.length ; i ++) { if (flag[i] == false) { flag[i] = true; curr.add(nums[i]); DFS(result,curr,nums,flag); curr.remove(curr.size() - 1); flag[i] = false; while(i+1<nums.length && nums[i] == nums[i+1]) i++; } } }
- Leetcode:Combinations组合数&&Permutations排列数
- LeetCode(Combinations) 求组合数
- (每日算法)LeetCode --- Combinations (组合数)
- Leetcode中的组合排列问题:Permutations,Combinations,Letter Combinations of a Phone Number
- Combinations(组合数)
- poj--1306 Combinations(组合数)
- 77 Combinations(组合数)
- 排列数与组合数
- 生成组合、排列数
- 【LeetCode】Permutations && Combinations 排列组合
- [Leetcode]Combinations & Permutations
- LightOJ 1005 Rooks (排列数 组合数)
- 组合数和排列数的关系
- [C++]LeetCode: 115 Permutations (求一组数的全排列)
- LeetCode之组合数
- 【LeetCode-面试算法经典-Java实现】【077-Combinations(组合数)】
- LintCode/LeetCode全排列系列问题--Permutations I 和 II, N-Queens I 和 II,数独问题
- 求组合数和全排列
- SQL server中转换大小写快捷键
- STM32使用JLINK或STLink下载程序需要用的的引脚
- Valid Perfect Square
- mongodb读写性能分析
- Android 图片压缩
- Leetcode:Combinations组合数&&Permutations排列数
- 工作三年的Java程序员需要掌握哪些知识?
- Unity插件NGUI实现背包系统
- iOS JSONModel使用
- mongo驱动连接
- 深入探索 Java 热部署
- PHP HTTP/HTTPS请求(支持get和post)
- 扇贝面试经历
- mongodb角色权限