一、491.递增子序列
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
示例:
- 输入: [4, 6, 7, 7]
- 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
为了有鲜明的对比,我用[4, 7, 6, 7]这个数组来举例,抽象为树形结构如图:
class Solution {
List<List<Integer>>result=new ArrayList<>();
LinkedList<Integer>path=new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backTracking(nums,0);
return result;
}
private void backTracking(int[] nums,int startIndex)
{
if(path.size()>1)
{
result.add(new ArrayList<>(path));
}
HashMap<Integer,Integer> map = new HashMap<>();
for(int i=startIndex;i<nums.length;i++)
{
if(!path.isEmpty()&&nums[i]<path.getLast())
{
continue;
}
if(map.getOrDefault(nums[i],0)>=1)
{
continue;
}
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
path.add(nums[i]);
backTracking(nums,i+1);
path.removeLast();
}
}
}
二、46.全排列
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
- 输入: [1,2,3]
- 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
我以[1,2,3]为例,抽象成树形结构如下:
此时可以感受出排列问题的不同:
- 每层都是从0开始搜索而不是startIndex
- 需要used数组记录path里都放了哪些元素了
class Solution {
List<List<Integer>> result=new ArrayList<>();
LinkedList<Integer>path=new LinkedList<>();
boolean []used;
public List<List<Integer>> permute(int[] nums) {
used=new boolean[nums.length];
permuteHelper(nums);
return result;
}
private void permuteHelper(int [] nums)
{
if(path.size()==nums.length)
{
result.add(new ArrayList<>(path));
return ;
}
for(int i=0;i<nums.length;i++)
{
if(used[i]==true)
{
continue;
}
used[i]=true;
path.add(nums[i]);
permuteHelper(nums);
used[i]=false;
path.removeLast();
}
}
}
三、47.全排列 II
需要强调的是去重一定要对元素进行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。
我以示例中的 [1,1,2]为例 (为了方便举例,已经排序)抽象为一棵树,去重过程如图:
图中我们对同一树层,前一位(也就是nums[i-1])如果使用过,那么就进行去重。
一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果。
class Solution {
List<List<Integer>>result=new ArrayList<>();
LinkedList<Integer> path=new LinkedList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
boolean [] used=new boolean[nums.length];
Arrays.fill(used,false);
Arrays.sort(nums);
backTracking(nums,used);
return result;
}
private void backTracking(int[] nums,boolean []used)
{
if(path.size()>=nums.length)
{
result.add(new ArrayList<>(path));
return;
}
for(int i=0;i<nums.length;i++)
{
if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false)
{
continue;
}
if(used[i]==true) continue;
if(used[i]==false)
{
used[i]=true;
path.add(nums[i]);
backTracking(nums,used);
used[i]=false;
path.removeLast();
}
}
}
}