leecode更新

This commit is contained in:
markilue 2022-12-15 12:49:03 +08:00
parent c2963545b3
commit 013a1161d1
2 changed files with 298 additions and 0 deletions

View File

@ -0,0 +1,134 @@
package com.markilue.leecode.dynamic;
import org.junit.Test;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.dynamic
*@Author: dingjiawen
*@CreateTime: 2022-12-15 09:48
*@Description:
* TODO 力扣300题 最长递增子序列
* 给你一个整数数组 nums 找到其中最长严格递增子序列的长度
* 子序列 是由数组派生而来的序列删除或不删除数组中的元素而不改变其余元素的顺序例如[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列
*@Version: 1.0
*/
public class T27_LengthOfLIS {
@Test
public void test() {
int[] nums = {0, 5,8, 4,6,7, 12, 2};
System.out.println(lengthOfLIS1(nums));
}
/**
* 代码随想录思路
* TODO 动态规划五部曲:
* (1)dp定义:dp[i]表示i之前包括i的以nums[i]结尾最长上升子序列的长度
* (2)dp状态转移方程:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
* 注意这里不是要dp[i] dp[j] + 1进行比较而是我们要取dp[j] + 1的最大值
* (3)dp初始化:dp[0]=1
* (4)dp遍历顺序:从前往后
* (5)dp举例推导:[0,1,0,3,2]为例:
* [0 1 2 3 4]
* i=1 1 2 1 1 1
* i=2 1 2 1 1 1
* i=3 1 2 1 3 1
* i=4 1 2 1 3 3
* 感觉本质上还是暴力解法与常规两次for一致 时间复杂度O(n^2)
* 速度击败72.45%内存击败46.73%
* @param nums
* @return
*/
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp, 1);
int result = 0;
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
if (dp[i] > result) result = dp[i]; // 取长的子序列
}
return result;
}
/**
* 官方贪心+二分查找法:本质上是我们希望每次在上升子序列最后加上的那个数尽可能的小
* 时间复杂度O(nlog(n)) 遍历数组复杂度n,二分查找插入dp数组复杂度log(n)
* 速度击败99.63%内存击败15.78% 2ms
* {0, 5, 8, 4, 6, 7, 12, 2}为例
* i=1:d={0}
* i=2:d={0,5}
* i=3:d={0,5,8}
* i=4:d={0,4,8}
* i=5:d={0,4,6}
* i=6:d={0,4,6,7}
* i=7:d={0,4,6,7,12}
* i=8:d={0,2,6,7,12}
* @param nums
* @return
*/
public int lengthOfLIS1(int[] nums) {
int len = 1, n = nums.length;
if (n == 0) {
return 0;
}
int[] d = new int[n + 1];
d[len] = nums[0];
for (int i = 1; i < n; ++i) {
if (nums[i] > d[len]) {
d[++len] = nums[i];
} else {
int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 此时要更新 d[1]所以这里将 pos 设为 0
while (l <= r) {//寻找最后一个比num[i]小的数
int mid = (l + r) >> 1;
if (d[mid] < nums[i]) {
pos = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
d[pos + 1] = nums[i];
}
}
return len;
}
/**
* 题解1ms法本质上还是贪心甚至没有用二分查找
* 速度击败99.93%内存击败5.8% 1ms
* @param nums
* @return
*/
public int lengthOfLIS2(int[] nums) {
int N = nums.length;
//end[i]表示i+1长度的递增子序列的最小值
int[] end = new int[N];
end[0] = nums[0];
int index = 0;
for(int i=1; i< N;i++){
if(nums[i] > end[index]){
end[++index] = nums[i];
} else {
for (int j = 0; j <= index; j++) {
if (nums[i] <= end[j]) {
end[j] = nums[i];
break;
}
}
}
}
return index + 1;
}
}

View File

@ -0,0 +1,164 @@
package com.markilue.leecode.dynamic;
import org.junit.Test;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.dynamic
*@Author: dingjiawen
*@CreateTime: 2022-12-15 11:58
*@Description:
* TODO leetcode674题 最长连续递增序列
* 给定一个未经排序的整数数组找到最长且 连续递增的子序列并返回该序列的长度
* 连续递增的子序列 可以由两个下标 l rl < r确定
* 如果对于每个 l <= i < r都有 nums[i] < nums[i + 1]
* 那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列
*@Version: 1.0
*/
public class T28_FindLengthOfLCIS {
@Test
public void test(){
int[] nums = {1, 3, 5,5,6, 4, 7};
System.out.println(findLengthOfLCIS2(nums));
}
/**
* 思路核心在于连续递增所以甚至比T27更简单更好找规律
* 这里先直接使用贪心一次遍历
* 速度击败99.96%内存击败51.38% 1ms
* @param nums
* @return
*/
public int findLengthOfLCIS(int[] nums) {
int min = nums[0];
int nowLength=1;
int maxLength = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] <= min) {
nowLength=1;
}else {
nowLength+=1;
maxLength=Math.max(maxLength,nowLength);
}
min = nums[i];
}
return maxLength;
}
/**
* 思路核心在于连续递增所以甚至比T27更简单更好找规律
* 这里尝试动归一次遍历
* TODO 动归五部曲:
* (1)dp定义:dp[i]表示使用num[0-i]得到的最长连续子序列
* (2)dp状态转移方程:dp[i]=num[i]>num[i-1]?max(dp[i-1],now+1),max(dp[i-1],now)
* (3)dp初始化:dp[0]=1
* (4)dp遍历顺序:从前往后
* (5)dp举例推导:{1, 3, 5,5,6, 4, 7}为例
* dp=[1,2,3,3,3,3,3]
* 速度击败99.96%内存击败32.74% 1ms
* @param nums
* @return
*/
public int findLengthOfLCIS1(int[] nums) {
int nowLength=1;
int[] dp = new int[nums.length];
dp[0]=1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[i-1]) {
nowLength+=1;
}else {
nowLength=1;
}
dp[i]=Math.max(dp[i-1],nowLength);
}
return dp[nums.length-1];
}
/**
* 代码随想录思路自己的思路还是有点贪心的意思代码随想录不会
*速度击败99.96%内存击败42.32% 1ms
* @param nums
* @return
*/
public int findLengthOfLCIS2(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp,1);
int result=1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[i-1]) {
dp[i]=dp[i-1]+1;
}
if (dp[i] > result) result = dp[i];
}
return result;
}
/**
* 思路动态规划滚动数组优化
* TODO 动归五部曲:
* (1)dp定义:dp[i]表示以num[i]结尾得到的最长连续子序列
* (2)dp状态转移方程:if nums[i + 1] > nums[i] 得到dp[i]=dp[i-1]+1
* (3)dp初始化:dp[i]=1
* (4)dp遍历顺序:从前往后
* (5)dp举例推导:{1, 3, 5,5,6, 4, 7}为例
* dp=[1,2,3,1,2,1,2]
* 速度击败99.96%内存击败32.74% 1ms
* @param nums
* @return
*/
public int findLengthOfLCIS3(int[] nums) {
int nowLength=1;
int dp0=1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[i-1]) {
nowLength+=1;
}else {
nowLength=1;
}
dp0=Math.max(dp0,nowLength);
}
return dp0;
}
/**
* 题解中0ms方法:比之前的快在count=1不需要在判断一次if(count>max)
* 速度击败100%内存击败38.73%
* @param nums
* @return
*/
public int findLengthOfLCIS5(int[] nums) {
int left=0,right,count=1,max=1;
for (right = 1; right < nums.length; right++,left++) {
if(nums[left]<nums[right]){
count++;
if(count>max){
max=count;
}
}else{
count=1;
}
}
return max;
}
}