最長上升子序列
我們用
dp[i] 表示數組中索引爲 i 結尾的子數組的最長上升子序列的長度
那麼我們要求整體最長子序列就是
dp[nums.length - 1]
分析子問題
dp[nums.length - 1]的子問題就是dp[nums.length - 2],以此類推
分析狀態轉移,怎麼由子問題推導得到原問題的最優解
題目意思就是要我們求得最長上升子序列的長度,對於子序列跟子數組的不同之處就在於,子序列可以跳過,元素和元素在數組中不一定是連續排列的,比如
[2,5,3,7,101]是子序列
[10,101]也是子序列
題目讓我們求最長的一個上升子序列的長度
我們先遍歷所有子數組
for(int i = 0;i < nums.length;i++){
for(int j = 0;j < i;j++){
//以上表達的是區間[j,i]的子數組
}
}
條件,上升
對於子問題如何能得到原問題
這就涉及到如何判斷上升這個條件了,顯然,這個很簡單
nums[i] > nums[j]
nums[i] 可以接在 nums[j]之後(此題要求嚴格遞增),此情況下最長上升子序列長度爲 dp[j]+1
nums[i] <= nums[j]
nums[i] 無法接在 nums[j] 之後,此情況上升子序列不成立,跳過。
所以:
public int lengthOfLIS(int[] nums) {
int len = nums.length;
if(len == 0){
return 0;
}
int[] dp = new int[len];
int maxLen = 1;
for(int i = 0;i < len;i++){
int tempLen = 1;
for(int j = 0;j < i;j++){
if(nums[i] > nums[j]){
tempLen = Math.max(dp[j] + 1,tempLen);
}
}
//記錄以索引i結尾的子數組的最長上升子序列長度
dp[i] = tempLen;
//記錄此時的全局最優解
if(tempLen > maxLen){
maxLen = tempLen;
}
}
return maxLen;
}
最長連續遞增序列
這道題和上面的最長上升子序列的區別在於,這道題要求的連續上升,即
temp = 1;//記錄長度
if nums[i] > nums[i - 1]
temp++ //只有連續遞增長度纔會增加
else
temp = 1; //否則只能回到初始值,下一次循環
解答:
/**
* 最長連續連續遞增序列
* @param nums
* @return
*/
public int findLengthOfLCIS(int[] nums) {
int max = 1;
int temp = 1;
for(int i = 1;i < nums.length;i++){
if(nums[i] > nums[i - 1]){
temp++;
max = Math.max(max,temp);
}else{
temp = 1;
}
}
return max;
}
最長遞增子序列的個數
這道題目和最長上升子序列問題很相似,但是這道題目要求記錄最長上升子序列的組合模式
比如:
[1,3,5,4,7]中[1,3,5,7]和[1,3,4,7]都是最長的上升子序列,長度都爲4,但是有兩種組合模式
難點就在於記錄組合數
所以我們用多一個dp數組來記錄這個組合數
combination[i]:表示以索引i結尾的子數組的最大的最大上升子序列的組合數
combination[i]初始化都賦值爲1,只要有數字,那麼至少都是1。
分析狀態轉移:區間是[ i,j ]
if nums[i] <= nums[j]:
不是上升序列,直接跳過
else //nums[i] > nums[j]
if dp[j] + 1 == dp[j]
//則找到了長度和之前一樣的上升子序列
combination[i] += combination[j]//更新以i結尾的組合數
else if dp[j] + 1 > dp[j]
//找到了更長的上升序列
dp[i] = dp[j] + 1//更新長度
combination[i] = combination[j];//更新以i結尾的組合數
參考圖:https://leetcode-cn.com/problems/longest-consecutive-sequence/solution/tao-lu-jie-jue-zui-chang-zi-xu-lie-deng-yi-lei-wen/
/**
* 最長遞增子序列數
* @param nums
* @return
*/
public int findNumberOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int[] dp = new int[nums.length];
int[] combination = new int[nums.length];
Arrays.fill(dp, 1);
Arrays.fill(combination, 1);
int max = 1, res = 0;
for (int i = 1; i < dp.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
if (dp[j] + 1 > dp[i]) {
//找到了更長的上升序列
dp[i] = dp[j] + 1; //更新dp[i]長度
combination[i] = combination[j];//更新以i結尾的組合數
} else if (dp[j] + 1 == dp[i]) {
//找到了和之前存的最長序列長度一樣的序列
combination[i] += combination[j];//更新組合數
}
}
}
max = Math.max(max, dp[i]);
}
//統計長度爲最長上升序列長度的子序列組合數
for (int i = 0; i < nums.length; i++){
if (dp[i] == max){
res += combination[i];
}
}
return res;
}
最長連續序列
最長連續序列和最長連續遞增序列相似,但是這道題元素和元素之間要求連續,即 1 ,2 ,3 ,4…
思路也和最長連續遞增序列相似:
temp = 1;//記錄長度
if nums[i] > nums[i - 1] + 1
temp++ //只有連續遞增長度纔會增加
else
temp = 1; //否則只能回到初始值,下一次循環
最長連續遞增序列思路:
temp = 1;//記錄長度
if nums[i] > nums[i - 1]
temp++ //只有連續遞增長度纔會增加
else
temp = 1; //否則只能回到初始值,下一次循環
實現
public int longestConsecutive1(int[] nums) {
if(nums.length == 0){
return 0;
}
int currentMax = 1;
int max = 0;
Arrays.sort(nums);
for(int i = 1;i < nums.length;i++){
if(nums[i] != nums[i - 1]){
if(nums[i] == nums[i - 1] + 1){
currentMax++;
}else{
max = Math.max(max,currentMax);
currentMax = 1;
}
}
}
return Math.max(currentMax,max);
}
最長和諧子序列
這道題目先統計所有元素的出現次數
//統計各個元素出現次數
for(int i : nums){
map.put(i,map.getOrDefault(i,0) + 1);
}
再遍歷一次數組,以當前遍歷到的元素爲最小值,那麼最大值則是 i+1,判斷map中是否存在該元素,有則計算最大值和最小值的數量
/**
* 最長和諧子序列
* @param nums
* @return
*/
public int findLHS(int[] nums) {
if(nums.length == 0){
return 0;
}
Map<Integer,Integer> map = new HashMap<>();
//統計各個元素出現次數
for(int i : nums){
map.put(i,map.getOrDefault(i,0) + 1);
}
int max = 1;
int temp = 1;
for(int i : nums){
//以當前遍歷元素爲最小值,那麼最大值則爲i + 1,判斷map中是否有該元素
if(map.containsKey(i + 1)){
temp = map.get(i) + map.get(i + 1);
max = Math.max(temp,max);
}
}
return max;
}