diff --git a/problems/0203.移除链表元素.md b/problems/0203.移除链表元素.md index 9235d47e..f3724fc2 100644 --- a/problems/0203.移除链表元素.md +++ b/problems/0203.移除链表元素.md @@ -304,8 +304,7 @@ var removeElements = function(head, val) { }; ``` -Swift: - +Swift: ```swift /** * Definition for singly-linked list. diff --git a/problems/0206.翻转链表.md b/problems/0206.翻转链表.md index 8bb359bd..0c850d52 100644 --- a/problems/0206.翻转链表.md +++ b/problems/0206.翻转链表.md @@ -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) diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 57edd13e..1161e158 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -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=list[j],那么我们可以在list[j]对应的子序列中删除最后j-i个元素得到长度与list[i]相同的子序列,其最右端的值maxlist[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<=jnums[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 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(Listlist, 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) 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]b{ + return a + } + return b +} +``` Javascript: ```Javascript diff --git a/problems/0707.设计链表.md b/problems/0707.设计链表.md index 0aa038e8..33b02e56 100644 --- a/problems/0707.设计链表.md +++ b/problems/0707.设计链表.md @@ -948,6 +948,70 @@ class MyLinkedList { } ``` +Swift +```Swift +class MyLinkedList { + var size = 0 + let head: ListNode + + /** Initialize your data structure here. */ + init() { + head = ListNode(-1) + } + + /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */ + func get(_ index: Int) -> Int { + if size > 0 && index < size { + var tempHead = head + for _ in 0.. size { + return + } + let idx = (index >= 0 ? index : 0) + var tempHead = head + for _ in 0 ..< idx { + tempHead = tempHead.next! + } + let currentNode = tempHead.next + let newNode = ListNode(val, currentNode) + tempHead.next = newNode + size += 1 + } + + /** Delete the index-th node in the linked list, if the index is valid. */ + func deleteAtIndex(_ index: Int) { + if size > 0 && index < size { + var tempHead = head + for _ in 0 ..< index { + tempHead = tempHead.next! + } + tempHead.next = tempHead.next!.next + size -= 1 + } + } +} +``` ----------------------- diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md index c13efc3a..851c2657 100644 --- a/problems/背包理论基础01背包-1.md +++ b/problems/背包理论基础01背包-1.md @@ -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物品。 代码初始化如下: ```