class Solution {
List<List<Integer>> res=new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
if(nums==null ||nums.length==0){
return res;
}
LinkedList<Integer> track=new LinkedList<>();
backTrack(nums,track);
return res;
}
public void backTrack(int[] nums,LinkedList<Integer> track){
if(track.size()==nums.length){
res.add(new LinkedList(track));
return;
}
for(int i=0;i<nums.length;i++){
if(track.contains(nums[i])){
continue;
}
track.add(nums[i]);
backTrack(nums,track);
track.removeLast();
}
}
}
class Solution {
List<List<Integer>> res=new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
if(nums==null || nums.length==0){
return res;
}
LinkedList<Integer> track=new LinkedList<>();
backTrack(nums,track,0);
return res;
}
//剪枝:使用一个index索引限定每次只能继续向下寻找,不能往数组前面遍历,以免出现重复
public void backTrack(int[] nums,LinkedList<Integer> track,int index){
res.add(new LinkedList(track));
for(int i=index;i<nums.length;i++){
track.add(nums[i]);
backTrack(nums,track,i+1);
track.removeLast();
}
}
}
class Solution {
List<List<Integer>> res=new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
if(n==0 || k==0){
return res;
}
LinkedList<Integer> track=new LinkedList<>();
backTrack(n,k,track,1);
return res;
}
public void backTrack(int n,int k,LinkedList<Integer> track,int index){
if(track.size()==k){
res.add(new LinkedList(track));
return;
}
for(int i=index;i<=n;i++){
track.add(i);
backTrack(n,k,track,i+1);
track.removeLast();
}
}
}
class Solution {
List<List<Integer>> res=new LinkedList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
if(nums==null || nums.length==0){
return res;
}
Arrays.sort(nums);//排序是剪枝的关键
LinkedList<Integer> track=new LinkedList<>();
boolean[] used=new boolean[nums.length];
backTrack(nums,track,used);
return res;
}
public void backTrack(int[] nums,LinkedList<Integer> track,boolean[] used){
if(track.size()==nums.length){
res.add(new LinkedList(track));
}
for(int i=0;i<nums.length;i++){
if(used[i]){
continue;
}
//剪枝,去除相同的值出现的分支
if(i>0 && nums[i]==nums[i-1] && used[i-1]==false){
continue;
}
track.add(nums[i]);
used[i]=true;
backTrack(nums,track,used);
track.removeLast();
used[i]=false;
}
}
}
对于括号合法性的判断,主要是借助「栈」这种数据结构,而对于括号的生成,一般都要利用回溯递归的思想
class Solution {
//采用回溯算法,列举出所有可能的字符串,从其中选出符合条件的字符串
List<String> res=new LinkedList<>();
public List<String> generateParenthesis(int n) {
if(n<=0){
return res;
}
StringBuilder sb=new StringBuilder();
backTrack(n,n,sb);
return res;
}
//left和right表示剩余可用的左括号数量和右括号数量
public void backTrack(int left,int right,StringBuilder sb){
//字符串中左括号数量一定大于等于右括号数量,所以当左括号剩余数量大于右括号时,生成的括号一定是不合法的
if(left>right) return;
//剩余个数小于0,说明所使用的左括号或者右括号超过了提供的数量,也不合法
if(left<0 || right<0) return;
//左右括号数量同时用完,满足条件
if(left==0 && right==0){
res.add(new String(sb.toString()));
return;
}
//添加左括号并回溯
sb.append('(');
backTrack(left-1,right,sb);
sb.deleteCharAt(sb.length()-1);
//添加右括号并回溯
sb.append(')');
backTrack(left,right-1,sb);
sb.deleteCharAt(sb.length()-1);
}
}
class Solution {
public boolean canPartitionKSubsets(int[] nums, int k) {
//排除基本情况:
if(k>nums.length) return false;
int sum=0;
for(int i:nums){
sum+=i;
}
if(sum%k!=0) return false;
//回溯法,将数组中的元素装进k个桶:
//1.每个桶中的数值和
int target=sum/k;
//2.记录用过的数字,如果已经装进一个桶,则不能再装进别的桶
boolean[] used=new boolean[nums.length];
//3.调用回溯函数,后面两个0:一个是桶中数值和的初始值,一个是索引位置初始值
return backTrack(nums,k,target,used,0,0);
}
public boolean backTrack(int[] nums,int k,int target,boolean[] used,int bucket,int start){
//如果k为0,则说明已经将k个桶都装好了
if(k==0) return true;
//如果bucket==target,说明当前桶装好了,需要去判断下一个桶
if(bucket==target){
return backTrack(nums,k-1,target,used,0,0);
}
//当前桶操作
for(int i=start;i<nums.length;i++){
//如果其他桶用过,跳过
if(used[i]) continue;
//如果当前元素加上桶中元素和大于target,跳过
if(nums[i]+bucket>target) continue;
//将当前元素装入桶中
bucket+=nums[i];
used[i]=true;
if(backTrack(nums,k,target,used,bucket,i+1)){
return true;
}
bucket-=nums[i];
used[i]=false;
}
//穷举了所有数字,都无法装满当前桶
return false;
}
}
每个桶要遍历n个数字,选择「装入」或「不装入」,组合的结果有2n种;而我们有k个桶,所以总的时间复杂度为O(k*2n)。