mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 16:54:50 +08:00
Merge branch 'master' into master
This commit is contained in:
@ -304,8 +304,7 @@ var removeElements = function(head, val) {
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
Swift:
|
||||
```swift
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
|
@ -334,6 +334,43 @@ fun reverseList(head: ListNode?): ListNode? {
|
||||
}
|
||||
```
|
||||
|
||||
Swift:
|
||||
```swift
|
||||
/// 双指针法 (迭代)
|
||||
/// - Parameter head: 头结点
|
||||
/// - Returns: 翻转后的链表头结点
|
||||
func reverseList(_ head: ListNode?) -> ListNode? {
|
||||
if head == nil || head?.next == nil {
|
||||
return head
|
||||
}
|
||||
var pre: ListNode? = nil
|
||||
var cur: ListNode? = head
|
||||
var temp: ListNode? = nil
|
||||
while cur != nil {
|
||||
temp = cur?.next
|
||||
cur?.next = pre
|
||||
pre = cur
|
||||
cur = temp
|
||||
}
|
||||
return pre
|
||||
}
|
||||
|
||||
/// 递归
|
||||
/// - Parameter head: 头结点
|
||||
/// - Returns: 翻转后的链表头结点
|
||||
func reverseList2(_ head: ListNode?) -> ListNode? {
|
||||
return reverse(pre: nil, cur: head)
|
||||
}
|
||||
func reverse(pre: ListNode?, cur: ListNode?) -> ListNode? {
|
||||
if cur == nil {
|
||||
return pre
|
||||
}
|
||||
let temp: ListNode? = cur?.next
|
||||
cur?.next = pre
|
||||
return reverse(pre: cur, cur: temp)
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -33,7 +33,7 @@
|
||||
* 1 <= nums.length <= 2500
|
||||
* -10^4 <= nums[i] <= 104
|
||||
|
||||
|
||||
## 方法一 动态规划
|
||||
## 思路
|
||||
|
||||
最长上升子序列是动规的经典题目,这里dp[i]是可以根据dp[j] (j < i)推导出来的,那么依然用动规五部曲来分析详细一波:
|
||||
@ -190,10 +190,130 @@ const lengthOfLIS = (nums) => {
|
||||
};
|
||||
```
|
||||
*复杂度分析*
|
||||
- 时间复杂度:O(nlogn)。数组 nums 的长度为 n,我们依次用数组中的元素去更新 dp 数组,相当于插入最后递增的元素,而更新 dp 数组时需要进行 O(logn) 的二分搜索,所以总时间复杂度为 O(nlogn)。
|
||||
- 时间复杂度:O(n^2)。数组 nums 的长度为 n,我们依次用数组中的元素去遍历 dp 数组,而遍历 dp 数组时需要进行 O(n) 次搜索,所以总时间复杂度为 O(n^2)。
|
||||
- 空间复杂度:O(n),需要额外使用长度为 n 的 dp 数组。
|
||||
|
||||
|
||||
## 方法二 贪心策略+二分搜索
|
||||
|
||||
使用贪心策略和二分搜索可以进一步将算法时间复杂度将为O(nlogn)。
|
||||
|
||||
## 思路
|
||||
|
||||
为了使得到的子序列尽可能长,我们需要使序列上升得尽可能慢。
|
||||
|
||||
对于长度为n的数组 nums,我们从0到n-1依次遍历数组中的每个元素nums[i],更新在0到i范围内最长上升子序列的长度len,以及 在0到i范围内,上升子序列的长度为1到len时,对应长度子序列最右端的最小值,将结果保存在list中。实际编码过程中,list长度即为len。
|
||||
|
||||
## 可行性
|
||||
当我们遍历完数组nums中第n-1个元素时,list中保存的是0到n-1范围内最长上升子序列的长度,即为所求。
|
||||
|
||||
## 算法复杂度分析
|
||||
1. list中的元素是单调递增的。可以用反证法来证明:假设对于0<=i<j<len,有list[i]>=list[j],那么我们可以在list[j]对应的子序列中删除最后j-i个元素得到长度与list[i]相同的子序列,其最右端的值max<list[j]<=list[i],与list的定义矛盾。
|
||||
|
||||
2. 假设我们已经得到0到i-1范围内对应的list,我们可以在O(logn)的时间复杂度内更新list,得到0到i范围内的list。
|
||||
|
||||
1. if(nums[i]>list[len-1],此时,list中子序列长度为1到len的对应的最右端最小值不变,并新增长度为len+1的子序列,最右端的最小值为nums[i],时间复杂度O(1);
|
||||
|
||||
2. if(nums[i]<=list[len-1]),此时,我们可以在0到len-1范围内找到k,list[k]为>=nums[i]的最小值,由于list单调递增,所以我们可以使用二分搜索在O(logn)的时间复杂度内找到k。
|
||||
1. 对于0<=j<k,list[j]<nums[i]恒成立,对应list[j]的值不需要更新。
|
||||
2. 对于list[k],其值更新为nums[i],因为原本list[k]对应的子序列的倒数第二项的值可以=list[k-1]<nums[i]。
|
||||
3. 对于k<j<=len-1,对应的list[j]不需要更新。因为这些list[j]对应的子序列的倒数第二项的值>nums[i];
|
||||
|
||||
3. 综上,算法时间复杂度为O(nlogn),空间复杂度为O(n),需要O(n)的空间保存list。
|
||||
|
||||
代码如下
|
||||
|
||||
Java
|
||||
```java
|
||||
class Solution {
|
||||
public int lengthOfLIS(int[] nums) {
|
||||
int n = nums.length;
|
||||
if(n==0){return 0;}
|
||||
|
||||
List<Integer> list=new ArrayList<>();
|
||||
list.add(nums[0]);
|
||||
for (int i = 1; i < n; ++i) {
|
||||
if (nums[i] > list.get(list.size()-1)) {
|
||||
list.add(nums[i]);
|
||||
} else {
|
||||
int k=binarySearch(list,nums[i]);
|
||||
list.set(k,nums[i]);
|
||||
}
|
||||
}
|
||||
return list.size();
|
||||
}
|
||||
|
||||
int binarySearch(List<Integer>list, int num){
|
||||
int len=list.size();
|
||||
int l=0,r=len-1,ans=len-1;
|
||||
while(l<=r){
|
||||
int mid=l+(r-l)/2;
|
||||
if(list.get(mid)<num){
|
||||
l=mid+1;
|
||||
}else{
|
||||
r=mid-1;
|
||||
ans=mid;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实际运行过程中,list的长度不会超过n,所以我们可以用数组来模拟list,代码如下。
|
||||
|
||||
|
||||
Java
|
||||
```java
|
||||
class Solution {
|
||||
public int lengthOfLIS(int[] nums) {
|
||||
int n = nums.length;
|
||||
if(n==0){return 0;}
|
||||
|
||||
//初始化list,len记录list长度
|
||||
int[] list=new int[n];
|
||||
int len=0;
|
||||
|
||||
//添加元素到list并更新len的值
|
||||
list[len++]=nums[0];
|
||||
|
||||
for (int i = 1; i < n; ++i) {
|
||||
if (nums[i] > list[len-1]) {
|
||||
list[len++]=nums[i];
|
||||
} else {
|
||||
int k=binarySearch(list,len,nums[i]);
|
||||
list[k]=nums[i];
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int binarySearch(int[] list,int len, int num){
|
||||
|
||||
int l=0,r=len-1,ans=len-1;
|
||||
while(l<=r){
|
||||
int mid=l+(r-l)/2;
|
||||
if(list[mid]<num){
|
||||
l=mid+1;
|
||||
}else{
|
||||
r=mid-1;
|
||||
ans=mid;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -227,7 +227,34 @@ class Solution:
|
||||
return dp[n]
|
||||
```
|
||||
Go:
|
||||
|
||||
```golang
|
||||
func integerBreak(n int) int {
|
||||
/**
|
||||
动态五部曲
|
||||
1.确定dp下标及其含义
|
||||
2.确定递推公式
|
||||
3.确定dp初始化
|
||||
4.确定遍历顺序
|
||||
5.打印dp
|
||||
**/
|
||||
dp:=make([]int,n+1)
|
||||
dp[1]=1
|
||||
dp[2]=1
|
||||
for i:=3;i<n+1;i++{
|
||||
for j:=1;j<i-1;j++{
|
||||
// i可以差分为i-j和j。由于需要最大值,故需要通过j遍历所有存在的值,取其中最大的值作为当前i的最大值,在求最大值的时候,一个是j与i-j相乘,一个是j与dp[i-j].
|
||||
dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]))
|
||||
}
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
func max(a,b int) int{
|
||||
if a>b{
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
|
@ -948,6 +948,7 @@ class MyLinkedList {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
@ -1031,6 +1032,7 @@ class MyLinkedList {
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -103,7 +103,7 @@ dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包
|
||||
|
||||
那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
|
||||
|
||||
当j >= weight[0]是,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
|
||||
当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
|
||||
|
||||
代码初始化如下:
|
||||
```
|
||||
|
Reference in New Issue
Block a user