题目链接:
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是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]]
说明:
- 给定数组的长度不会超过15。
- 数组中的整数范围是 [-100,100]。
- 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。
这道题其实跟组合的题目差不多,只是多了一个递增的条件而已。我认为这道题的难点在于去重。把这道题抽象成树形结构比较好理解,如图:
在图中可以看出,同一父节点下的同层上使用过的元素就不能在使用了,这就意味着从属于同一父节点的每一层都要有一个专属的去重数组used,以达到同一父节点下的同层上使用过的元素不再重复使用
由于是回溯递归,所以可以使用递归四部曲:
递归作用:从nums中索引为startIndex开始取序列,判断是否符合条件
递归参数:nums意义同题意,startIndex是这一层取数的起始下标
终止条件:startIndex >= nums.length
单层逻辑:判断目前序列是否满足条件,再从startIndex取这一层的数,取完再取下一层,记得回溯
回溯算法再操作完这一层后要记得对各个量进行初始化,这是回溯算法的特点
代码如下:
class Solution {
List<List<Integer>> result = null;
List<Integer> path = null;
void findSubsequences1(int[] nums, int startIndex){
//递归作用:从nums中索引为startIndex开始取序列,判断是否符合条件
//递归参数:nums意义同题意,startIndex是这一层取数的起始下标
//终止条件:startIndex >= nums.length
//单层逻辑:判断目前序列是否满足条件,再从startIndex取这一层的数,取完再取下一层,记得回溯
if(path.size() >= 2) result.add(new ArrayList(path));
if(startIndex >= nums.length) return;
boolean[] used = new boolean[201];//哈希去重,每一层都要单独定义一个used去重,因为题目规定-100 <= nums[i] <= 100,所以定义一个容量201的
for(int i = startIndex; i < nums.length; i++){
if((path.size() > 0 && nums[i] < path.get(path.size() - 1)) || used[nums[i] + 100] == true)//不递增或者重复都要cotinue
continue;
path.add(nums[i]);
used[nums[i] + 100] = true;//表示这一层已经取过nums[i],这一层不能再取重复的nums[i]了。这个数据用于判断这一层的去重,不要在回溯的时候初始化
findSubsequences1(nums, i + 1);
path.remove(path.size() - 1);//回溯
}
}
public List<List<Integer>> findSubsequences(int[] nums) {
result = new ArrayList<>();
path = new ArrayList<>();
findSubsequences1(nums, 0);
return result;
}
}