From 013a1161d103e80d8530d8f175005982db1758f2 Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Thu, 15 Dec 2022 12:49:03 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leecode/dynamic/T27_LengthOfLIS.java | 134 ++++++++++++++ .../leecode/dynamic/T28_FindLengthOfLCIS.java | 164 ++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T27_LengthOfLIS.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T28_FindLengthOfLCIS.java diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T27_LengthOfLIS.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T27_LengthOfLIS.java new file mode 100644 index 0000000..7c42893 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T27_LengthOfLIS.java @@ -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; + } + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T28_FindLengthOfLCIS.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T28_FindLengthOfLCIS.java new file mode 100644 index 0000000..be9a212 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T28_FindLengthOfLCIS.java @@ -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 和 r(l < 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]max){ + max=count; + } + }else{ + count=1; + } + + } + return max; + } +}