这道题是lc的原题,字节面试官在这个基础上做了扩展, 原题描述是
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。 示例 2:
输入:nums = [0,1,0,3,2,3] 输出:4 示例 3:
输入:nums = [7,7,7,7,7,7,7] 输出:1
原题要求返回长度,但面试官要求返回最长递增子序列数组
思路肯定是动态规划, 定义dp[i] 为以i结尾的数组的最长递增子序列 但是怎么根据动态规划的dp数据和原数据找到最长上升子序列呢
一个思路是 维护一个 Map,key是数字,value是list表示最长子序列,转移状态时维护map;
后来想了下也不用维护一个额外的空间了
观察第一个例子 原数组
[10,9,2,5,3,7,101,18]
对应的dp数组为
[1,1,1,2,2,3,4,4]
观察到 只要找到 对应dp的序列为 4,3,2,1的 有一点 贪心算法的思想
我只要找到比我上一个小的值,我就贪心的的把当前值设置到返回值里边
看下最后的代码
public int[] lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
int res = 0;
Arrays.fill(dp, 1);
for(int i = 0; i < nums.length; i++) {
for(int j = 0; j < i; j++) {
if(nums[j] < nums[i]){
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
res = Math.max(res, dp[i]);
}
int[] resArr = new int[res];
int index = resArr.length-1;
for(int i=nums.length-1;i>=0;i--){
if(index==resArr.length-1&&dp[i]==res){
resArr[index] = nums[i];
index--;
res--;
continue;
}
if(index<resArr.length-1&&dp[i]==res&&nums[i]<resArr[index+1]){
resArr[index] = nums[i];
index--;
res--;
}
}
return resArr;
}
@Test
public void test(){
for (int i : lengthOfLIS(new int[]{10,9,2,5,3,7,101,18})) {
System.out.println(i);
}
}
总结一下,动态规划很喜欢考,而且可能会基于原题做一些扩展,考察代码能力和思路
另一道题 343. 整数拆分
那同样的。要求反回,具体是哪些数的乘积最大
输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
返回 一个list [3,3,4]; 怎么处理? hash映射解题
public List<Integer> integerBreak(int n) {
int[] dp = new int[n+1];
dp[2] = 1;
Map<Integer,List<Integer>> map = new HashMap<>();
map.put(2,new ArrayList<Integer>(){{add(1);add(1);}});
for(int i=3;i<=n;i++){
for(int j=1;j<i;j++){
dp[i] = Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
if(dp[i]==j*(i-j)){
List<Integer> list = new ArrayList<>();
list.add(j);
list.add(i-j);
map.put(i,list);
}
if(dp[i]==j*dp[i-j]){
List<Integer> list = map.get(i - j);
List<Integer> newList = new ArrayList<>();
newList.addAll(list);
newList.add(j);
map.put(i,newList);
}
}
}
return map.get(n);
}