目录
有重复项数字的所有排列
给出一组可能包含重复项的数字,返回该组数字的所有排列。结果以字典序升序排列。
import java.util.*;
public class Solution {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
ArrayList<Integer> c = new ArrayList<>();
public ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
for(int i = 0; i < num.length; i++){
c.add(num[i]);
}
dfs(0);
Collections.sort(res, new Comparator<ArrayList<Integer>>() {
@Override
public int compare(ArrayList<Integer> o1, ArrayList<Integer> o2) {
for (int i = 0; i < o1.size(); i++) {
if(o1.get(i) == o2.get(i)) continue;
else if(o1.get(i) > o2.get(i)) return 1;
else return -1;
}
return 0;
}
});
return res;
}
public void dfs(int x){
if(x == c.size() - 1){
res.add(new ArrayList<>(c));
return;
}
HashSet<Integer> set = new HashSet<>();
for(int i = x; i < c.size(); i++){
if(set.contains(c.get(i))) continue;
set.add(c.get(i));
swap(i,x);
dfs(x+1);
swap(i,x);
}
}
public void swap(int a, int b) {
int tmp = c.get(a);
c.set(a, c.get(b));
c.set(b, tmp);
}
}
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
} else{
for(int i = 0; i < nums.length; i++){
if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;
used[i] = true;
tempList.add(nums[i]);
backtrack(list, tempList, nums, used);
used[i] = false;
tempList.remove(tempList.size() - 1);
}
}
}
组合总和2
给出一组候选数 c 和一个目标数 t ,找出候选数中起来和等于 t 的所有组合。
c 中的每个数字在一个组合中只能使用一次。
import java.util.*;
// 菜鸡解法
public class Solution {
private ArrayList<ArrayList<Integer>> ansLists;
private int[] num;
private int target;
public ArrayList<ArrayList<Integer>> combinationSum2(int[] num, int target) {
// 组合要求不重复,需要与排列区分
ArrayList<ArrayList<Integer>> ansLists = new ArrayList<>();
if(num==null || num.length==0) return ansLists;
this.ansLists = ansLists;
this.num = num;
this.target = target;
// 排序方便防重复、剪枝
Arrays.sort(num);
dfs(new ArrayList<>(), -1, 0);
return this.ansLists;
}
// 组合防止重复只能往后取
public void dfs(ArrayList<Integer> list, int currIdx, int currSum) {
if(currIdx==num.length-1 && currSum!=target) return;
if(currSum==target) {
ansLists.add(new ArrayList<Integer>(list));
return;
}
for(int nextIdx=currIdx+1; nextIdx<num.length; ) {
// 剪枝
if(currSum+num[nextIdx]>target) break;
// 回溯
list.add(num[nextIdx]);
dfs(list, nextIdx, currSum+num[nextIdx]);
list.remove(list.size()-1);
// 滑动到下一个不同值,防止重复
nextIdx++;
while(nextIdx<num.length && num[nextIdx-1]==num[nextIdx]) {
nextIdx++;
}
}
}
}
组合总和
给定一个无重复元素的正整数数组 candidates
和一个正整数 target
,找出 candidates
中所有可以使数字和为目标数 target
的唯一组合。
candidates
中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target
的唯一组合数少于 150
个。
public List<List<Integer>> combinationSum(int[] nums, int target) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, target, 0);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){
if(remain < 0) return;
else if(remain == 0) list.add(new ArrayList<>(tempList));
else{
for(int i = start; i < nums.length; i++){
tempList.add(nums[i]);
backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements
tempList.remove(tempList.size() - 1);
}
}
}
集合的所有子集
现在有一个没有重复元素的整数集合S,求S的所有子集
注意:
你给出的子集中的元素必须按升序排列
给出的解集中不能出现重复的元素
import java.util.*;
public class Solution {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
ArrayList<Integer> ls = new ArrayList();
public ArrayList<ArrayList<Integer>> subsets(int[] S) {
//保证了不会重复访问
int[] used = new int[S.length];
Arrays.sort(S);
dfs(S,used,0);
return res;
}
private void dfs(int[] a,int[] used,int start) {
//这一步没有return; 保证了将每个遍历到的都输出一遍
res.add(new ArrayList(ls));
for(int i = start; i < a.length; i++) {
if(used[i] == 1) continue;
ls.add(a[i]);
used[i] = 1;
dfs(a,used,i);
ls.remove(ls.size()-1);
used[i] = 0;
}
}
}
子集2
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入:nums = [0] 输出:[[],[0]]
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
helper(res,new ArrayList<>(),nums,0);
return res;
}
public void helper(List<List<Integer>> res, List<Integer> ls, int[] nums, int pos) {
res.add(new ArrayList<>(ls));
for(int i=pos;i<nums.length;i++) {
if(i>pos&&nums[i]==nums[i-1]) continue;
ls.add(nums[i]);
helper(res,ls,nums,i+1);
ls.remove(ls.size()-1);
}
}
}
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
helper(res,new ArrayList<>(),nums,0,false);
return res;
}
public void helper(List<List<Integer>> res, List<Integer> ls, int[] nums, int pos, boolean choosePre) {
if(pos==nums.length) {
res.add(new ArrayList<>(ls));
return;
}
helper(res,ls,nums,pos+1,false);
if(pos>=1&&nums[pos]==nums[pos-1]&&!choosePre) return;
ls.add(nums[pos]);
helper(res,ls,nums,pos+1,true);
ls.remove(ls.size()-1);
}
}
数字字符串转化成IP地址
现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。
例如:
给出的字符串为"25525522135",
返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)
import java.util.*;
public class Solution { //很巧妙的dfs,调了一早上
ArrayList<String> res = new ArrayList<>();
ArrayList<String> track = new ArrayList<>();
public ArrayList<String> restoreIpAddresses (String s) {
if(s.length() < 4) return new ArrayList<>();
dfs(s,0);
return res;
}
public void dfs(String s,int start){
int len = s.length();
if(track.size() == 4 && start != len)//不合法
return;
if(track.size() == 4 && start == len){
res.add(track.get(0) + "." + track.get(1) + "."
+ track.get(2) + "."+track.get(3));
}
for(int i = start,j = i + 1;j < len + 1 && j - i <= 3;j++){
if(validate(s.substring(i,j))) { //数字合法
track.add(s.substring(i,j));
dfs(s,j); //向下搜
track.remove(track.size() - 1);
}
}
}
private boolean validate(String s) {
if(s.equals("0")) return true;
if(s.startsWith("0")) return false;
return Integer.parseInt(s) <= 255;
}
}
括号生成
给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。
例如,给出n=3,解集为:
"((()))", "(()())", "(())()", "()()()", "()(())"
import java.util.ArrayList;
public class Solution {
/**
* 题目:括号生成
* 给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。
* 例如,给出n=3,解集为:
* "((()))", "(()())", "(())()", "()()()", "()(())",
*/
ArrayList<String> list = new ArrayList<>();
public ArrayList<String> generateParenthesis (int n) {
// n:表示有n对括号,也就是n个左括号'(',n个右括号')'
// 一个""空字符串,要么加一个左括号,要么加一个右括号
// 当n个左右括号都加完了,那就是一个结果出来了
// 条件一:左括号数量一定是先达到n,所以就不能再加左括号了,left>n,就错了
// 条件二:肯定有左才有右,如果右括号先放一定错,所以要保证left >= right
process(n, 0, 0, "");
return list;
}
public void process(int n, int left, int right, String cur) {
// 如果左括号数量>n,或者左括号数量大于右括号,直接return
if (left > n || right > left) {
return;
}
// 满足条件,放入list中
if (left == n && right == left) {
list.add(cur);
return;
}
//要么继续加左括号,要么继续加右括号
//加一个左括号,左括号数量+1
process(n, left + 1, right, cur + "(");
//加一个右括号,右括号数量+1
process(n, left, right + 1, cur + ")");
}
}
全排列
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1] 输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1] 输出:[[1]]
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
// Arrays.sort(nums); // not necessary
backtrack(list, new ArrayList<>(), nums);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
} else{
for(int i = 0; i < nums.length; i++){
if(tempList.contains(nums[i])) continue; // element already exists, skip
tempList.add(nums[i]);
backtrack(list, tempList, nums);
tempList.remove(tempList.size() - 1);
}
}
}
组合
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
示例 2:
输入:n = 1, k = 1 输出:[[1]]
public static List<List<Integer>> combine(int n, int k) {
List<List<Integer>> combs = new ArrayList<List<Integer>>();
combine(combs, new ArrayList<Integer>(), 1, n, k);
return combs;
}
public static void combine(List<List<Integer>> combs, List<Integer> comb, int start, int n, int k) {
if(k==0) {
combs.add(new ArrayList<Integer>(comb));
return;
}
for(int i=start;i<=n;i++) {
comb.add(i);
combine(combs, comb, i+1, n, k-1);
comb.remove(comb.size()-1);
}
}
路径总和2
给你二叉树的根节点 root
和一个整数目标和 targetSum
,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
public List<List<Integer>> pathSum(TreeNode root, int sum){
List<List<Integer>> result = new LinkedList<List<Integer>>();
List<Integer> currentResult = new LinkedList<Integer>();
pathSum(root,sum,currentResult,result);
return result;
}
public void pathSum(TreeNode root, int sum, List<Integer> currentResult,
List<List<Integer>> result) {
if (root == null)
return;
currentResult.add(new Integer(root.val));
if (root.left == null && root.right == null && sum == root.val) {
result.add(new LinkedList(currentResult));
currentResult.remove(currentResult.size() - 1);//don't forget to remove the last integer
return;
} else {
pathSum(root.left, sum - root.val, currentResult, result);
pathSum(root.right, sum - root.val, currentResult, result);
}
currentResult.remove(currentResult.size() - 1);
}
分割回文串
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
示例 1:
输入:s = "aab" 输出:[["a","a","b"],["aa","b"]]
示例 2:
输入:s = "a" 输出:[["a"]]
public class Solution {
public List<List<String>> partition(String s) {
List<List<String>> res = new ArrayList<List<String>>();
List<String> list = new ArrayList<String>();
dfs(s,0,list,res);
return res;
}
public void dfs(String s, int pos, List<String> list, List<List<String>> res){
if(pos==s.length()) res.add(new ArrayList<String>(list));
else{
for(int i=pos;i<s.length();i++){
if(isPal(s,pos,i)){
list.add(s.substring(pos,i+1));
dfs(s,i+1,list,res);
list.remove(list.size()-1);
}
}
}
}
public boolean isPal(String s, int low, int high){
while(low<high) if(s.charAt(low++)!=s.charAt(high--)) return false;
return true;
}
}