diff --git a/README.md b/README.md index 38418322..4e2993d8 100644 --- a/README.md +++ b/README.md @@ -254,7 +254,7 @@ 33. [二叉树:构造一棵搜索树](./problems/0108.将有序数组转换为二叉搜索树.md) 34. [二叉树:搜索树转成累加树](./problems/0538.把二叉搜索树转换为累加树.md) 35. [二叉树:总结篇!(需要掌握的二叉树技能都在这里了)](./problems/二叉树总结篇.md) - + ## 回溯算法 题目分类大纲如下: diff --git a/problems/0001.两数之和.md b/problems/0001.两数之和.md index 141e66f3..6969c2e2 100644 --- a/problems/0001.两数之和.md +++ b/problems/0001.两数之和.md @@ -274,6 +274,7 @@ class Solution { } } ``` + Scala: ```scala object Solution { @@ -296,5 +297,26 @@ object Solution { } } ``` + +C#: +```csharp +public class Solution { + public int[] TwoSum(int[] nums, int target) { + Dictionary dic= new Dictionary(); + for(int i=0;i diff --git a/problems/0015.三数之和.md b/problems/0015.三数之和.md index bfde6b35..cc184c87 100644 --- a/problems/0015.三数之和.md +++ b/problems/0015.三数之和.md @@ -313,54 +313,36 @@ func threeSum(nums []int)[][]int{ javaScript: ```js -/** - * @param {number[]} nums - * @return {number[][]} - */ - -// 循环内不考虑去重 var threeSum = function(nums) { - const len = nums.length; - if(len < 3) return []; - nums.sort((a, b) => a - b); - const resSet = new Set(); - for(let i = 0; i < len - 2; i++) { - if(nums[i] > 0) break; - let l = i + 1, r = len - 1; + const res = [], len = nums.length + // 将数组排序 + nums.sort((a, b) => a - b) + for (let i = 0; i < len; i++) { + let l = i + 1, r = len - 1, iNum = nums[i] + // 数组排过序,如果第一个数大于0直接返回res + if (iNum > 0) return res + // 去重 + if (iNum == nums[i - 1]) continue while(l < r) { - const sum = nums[i] + nums[l] + nums[r]; - if(sum < 0) { l++; continue }; - if(sum > 0) { r--; continue }; - resSet.add(`${nums[i]},${nums[l]},${nums[r]}`); - l++; - r--; + let lNum = nums[l], rNum = nums[r], threeSum = iNum + lNum + rNum + // 三数之和小于0,则左指针向右移动 + if (threeSum < 0) l++ + else if (threeSum > 0) r-- + else { + res.push([iNum, lNum, rNum]) + // 去重 + while(l < r && nums[l] == nums[l + 1]){ + l++ + } + while(l < r && nums[r] == nums[r - 1]) { + r-- + } + l++ + r-- + } } } - return Array.from(resSet).map(i => i.split(",")); -}; - -// 去重优化 -var threeSum = function(nums) { - const len = nums.length; - if(len < 3) return []; - nums.sort((a, b) => a - b); - const res = []; - for(let i = 0; i < len - 2; i++) { - if(nums[i] > 0) break; - // a去重 - if(i > 0 && nums[i] === nums[i - 1]) continue; - let l = i + 1, r = len - 1; - while(l < r) { - const sum = nums[i] + nums[l] + nums[r]; - if(sum < 0) { l++; continue }; - if(sum > 0) { r--; continue }; - res.push([nums[i], nums[l], nums[r]]) - // b c 去重 - while(l < r && nums[l] === nums[++l]); - while(l < r && nums[r] === nums[--r]); - } - } - return res; + return res }; ``` TypeScript: diff --git a/problems/0019.删除链表的倒数第N个节点.md b/problems/0019.删除链表的倒数第N个节点.md index 813e9b02..c36900bc 100644 --- a/problems/0019.删除链表的倒数第N个节点.md +++ b/problems/0019.删除链表的倒数第N个节点.md @@ -39,7 +39,7 @@ 分为如下几步: -* 首先这里我推荐大家使用虚拟头结点,这样方面处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: [链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html) +* 首先这里我推荐大家使用虚拟头结点,这样方便处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: [链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html) * 定义fast指针和slow指针,初始值为虚拟头结点,如图: @@ -289,6 +289,28 @@ func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { return dummyHead.next } ``` - +Scala: +```scala +object Solution { + def removeNthFromEnd(head: ListNode, n: Int): ListNode = { + val dummy = new ListNode(-1, head) // 定义虚拟头节点 + var fast = head // 快指针从头开始走 + var slow = dummy // 慢指针从虚拟头开始头 + // 因为参数 n 是不可变量,所以不能使用 while(n>0){n-=1}的方式 + for (i <- 0 until n) { + fast = fast.next + } + // 快指针和满指针一起走,直到fast走到null + while (fast != null) { + slow = slow.next + fast = fast.next + } + // 删除slow的下一个节点 + slow.next = slow.next.next + // 返回虚拟头节点的下一个 + dummy.next + } +} +``` -----------------------
diff --git a/problems/0024.两两交换链表中的节点.md b/problems/0024.两两交换链表中的节点.md index ce75e0d7..2289c229 100644 --- a/problems/0024.两两交换链表中的节点.md +++ b/problems/0024.两两交换链表中的节点.md @@ -311,7 +311,29 @@ func swapPairs(_ head: ListNode?) -> ListNode? { return dummyHead.next } ``` - +Scala: +```scala +// 虚拟头节点 +object Solution { + def swapPairs(head: ListNode): ListNode = { + var dummy = new ListNode(0, head) // 虚拟头节点 + var pre = dummy + var cur = head + // 当pre的下一个和下下个都不为空,才进行两两转换 + while (pre.next != null && pre.next.next != null) { + var tmp: ListNode = cur.next.next // 缓存下一次要进行转换的第一个节点 + pre.next = cur.next // 步骤一 + cur.next.next = cur // 步骤二 + cur.next = tmp // 步骤三 + // 下面是准备下一轮的交换 + pre = cur + cur = tmp + } + // 最终返回dummy虚拟头节点的下一个,return可以省略 + dummy.next + } +} +``` -----------------------
diff --git a/problems/0027.移除元素.md b/problems/0027.移除元素.md index 590cf0b9..4b50d666 100644 --- a/problems/0027.移除元素.md +++ b/problems/0027.移除元素.md @@ -81,7 +81,7 @@ public: **双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。** -后序都会一一介绍到,本题代码如下: +后续都会一一介绍到,本题代码如下: ```CPP // 时间复杂度:O(n) @@ -328,6 +328,20 @@ int removeElement(int* nums, int numsSize, int val){ return slow; } ``` - +Scala: +```scala +object Solution { + def removeElement(nums: Array[Int], `val`: Int): Int = { + var slow = 0 + for (fast <- 0 until nums.length) { + if (`val` != nums(fast)) { + nums(slow) = nums(fast) + slow += 1 + } + } + slow + } +} +``` -----------------------
diff --git a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md index dfd90b82..260462c2 100644 --- a/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md +++ b/problems/0034.在排序数组中查找元素的第一个和最后一个位置.md @@ -480,7 +480,52 @@ var searchRange = function(nums, target) { return [-1, -1]; }; ``` +### Scala +```scala +object Solution { + def searchRange(nums: Array[Int], target: Int): Array[Int] = { + var left = getLeftBorder(nums, target) + var right = getRightBorder(nums, target) + if (left == -2 || right == -2) return Array(-1, -1) + if (right - left > 1) return Array(left + 1, right - 1) + Array(-1, -1) + } + // 寻找左边界 + def getLeftBorder(nums: Array[Int], target: Int): Int = { + var leftBorder = -2 + var left = 0 + var right = nums.length - 1 + while (left <= right) { + var mid = left + (right - left) / 2 + if (nums(mid) >= target) { + right = mid - 1 + leftBorder = right + } else { + left = mid + 1 + } + } + leftBorder + } + + // 寻找右边界 + def getRightBorder(nums: Array[Int], target: Int): Int = { + var rightBorder = -2 + var left = 0 + var right = nums.length - 1 + while (left <= right) { + var mid = left + (right - left) / 2 + if (nums(mid) <= target) { + left = mid + 1 + rightBorder = left + } else { + right = mid - 1 + } + } + rightBorder + } +} +``` -----------------------
diff --git a/problems/0035.搜索插入位置.md b/problems/0035.搜索插入位置.md index 9a770703..6c04e7de 100644 --- a/problems/0035.搜索插入位置.md +++ b/problems/0035.搜索插入位置.md @@ -316,8 +316,52 @@ func searchInsert(_ nums: [Int], _ target: Int) -> Int { return right + 1 } ``` +### Scala +```scala +object Solution { + def searchInsert(nums: Array[Int], target: Int): Int = { + var left = 0 + var right = nums.length - 1 + while (left <= right) { + var mid = left + (right - left) / 2 + if (target == nums(mid)) { + return mid + } else if (target > nums(mid)) { + left = mid + 1 + } else { + right = mid - 1 + } + } + right + 1 + } +} +``` +### PHP +```php +// 二分法(1):[左闭右闭] +function searchInsert($nums, $target) +{ + $n = count($nums); + $l = 0; + $r = $n - 1; + while ($l <= $r) { + $mid = floor(($l + $r) / 2); + if ($nums[$mid] > $target) { + // 下次搜索在左区间:[$l,$mid-1] + $r = $mid - 1; + } else if ($nums[$mid] < $target) { + // 下次搜索在右区间:[$mid+1,$r] + $l = $mid + 1; + } else { + // 命中返回 + return $mid; + } + } + return $r + 1; +} +``` ----------------------- diff --git a/problems/0039.组合总和.md b/problems/0039.组合总和.md index 98b37b84..e10a827f 100644 --- a/problems/0039.组合总和.md +++ b/problems/0039.组合总和.md @@ -370,18 +370,17 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int) ```js var combinationSum = function(candidates, target) { const res = [], path = []; - candidates.sort(); // 排序 + candidates.sort((a,b)=>a-b); // 排序 backtracking(0, 0); return res; function backtracking(j, sum) { - if (sum > target) return; if (sum === target) { res.push(Array.from(path)); return; } for(let i = j; i < candidates.length; i++ ) { const n = candidates[i]; - if(n > target - sum) continue; + if(n > target - sum) break; path.push(n); sum += n; backtracking(i, sum); diff --git a/problems/0040.组合总和II.md b/problems/0040.组合总和II.md index de13e031..34ac64e6 100644 --- a/problems/0040.组合总和II.md +++ b/problems/0040.组合总和II.md @@ -508,22 +508,27 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int) */ var combinationSum2 = function(candidates, target) { const res = []; path = [], len = candidates.length; - candidates.sort(); + candidates.sort((a,b)=>a-b); backtracking(0, 0); return res; function backtracking(sum, i) { - if (sum > target) return; if (sum === target) { res.push(Array.from(path)); return; } - let f = -1; for(let j = i; j < len; j++) { const n = candidates[j]; - if(n > target - sum || n === f) continue; + if(j > i && candidates[j] === candidates[j-1]){ + //若当前元素和前一个元素相等 + //则本次循环结束,防止出现重复组合 + continue; + } + //如果当前元素值大于目标值-总和的值 + //由于数组已排序,那么该元素之后的元素必定不满足条件 + //直接终止当前层的递归 + if(n > target - sum) break; path.push(n); sum += n; - f = n; backtracking(sum, j + 1); path.pop(); sum -= n; diff --git a/problems/0045.跳跃游戏II.md b/problems/0045.跳跃游戏II.md index 4caff042..4e3ab24a 100644 --- a/problems/0045.跳跃游戏II.md +++ b/problems/0045.跳跃游戏II.md @@ -217,18 +217,26 @@ class Solution: ### Go ```Go func jump(nums []int) int { - dp:=make([]int ,len(nums)) - dp[0]=0 + dp := make([]int, len(nums)) + dp[0] = 0//初始第一格跳跃数一定为0 - for i:=1;ii{ - dp[i]=min(dp[j]+1,dp[i]) - } - } - } - return dp[len(nums)-1] + for i := 1; i < len(nums); i++ { + dp[i] = i + for j := 0; j < i; j++ { + if nums[j] + j >= i {//nums[j]为起点,j为往右跳的覆盖范围,这行表示从j能跳到i + dp[i] = min(dp[j] + 1, dp[i])//更新最小能到i的跳跃次数 + } + } + } + return dp[len(nums)-1] +} + +func min(a, b int) int { + if a < b { + return a + } else { + return b + } } ``` diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md index d0680a3b..73cac244 100644 --- a/problems/0053.最大子序和.md +++ b/problems/0053.最大子序和.md @@ -140,7 +140,7 @@ public: ## 其他语言版本 -### Java +### Java ```java class Solution { public int maxSubArray(int[] nums) { @@ -180,7 +180,7 @@ class Solution { } ``` -### Python +### Python ```python class Solution: def maxSubArray(self, nums: List[int]) -> int: @@ -195,7 +195,7 @@ class Solution: return result ``` -### Go +### Go ```go func maxSubArray(nums []int) int { @@ -212,6 +212,20 @@ func maxSubArray(nums []int) int { } ``` +### Rust +```rust +pub fn max_sub_array(nums: Vec) -> i32 { + let mut max_sum = i32::MIN; + let mut curr = 0; + for n in nums.iter() { + curr += n; + max_sum = max_sum.max(curr); + curr = curr.max(0); + } + max_sum +} +``` + ### Javascript: ```Javascript var maxSubArray = function(nums) { diff --git a/problems/0056.合并区间.md b/problems/0056.合并区间.md index b44d602c..92b66473 100644 --- a/problems/0056.合并区间.md +++ b/problems/0056.合并区间.md @@ -112,8 +112,8 @@ public: }; ``` -* 时间复杂度:$O(n\log n)$ ,有一个快排 -* 空间复杂度:$O(1)$,我没有算result数组(返回值所需容器占的空间) +* 时间复杂度:O(nlog n) ,有一个快排 +* 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间 ## 总结 diff --git a/problems/0059.螺旋矩阵II.md b/problems/0059.螺旋矩阵II.md index 93735895..22229302 100644 --- a/problems/0059.螺旋矩阵II.md +++ b/problems/0059.螺旋矩阵II.md @@ -130,57 +130,37 @@ Java: ```Java class Solution { public int[][] generateMatrix(int n) { + int loop = 0; // 控制循环次数 int[][] res = new int[n][n]; + int start = 0; // 每次循环的开始点(start, start) + int count = 1; // 定义填充数字 + int i, j; - // 循环次数 - int loop = n / 2; - - // 定义每次循环起始位置 - int startX = 0; - int startY = 0; - - // 定义偏移量 - int offset = 1; - - // 定义填充数字 - int count = 1; - - // 定义中间位置 - int mid = n / 2; - while (loop > 0) { - int i = startX; - int j = startY; - + while (loop++ < n / 2) { // 判断边界后,loop从1开始 // 模拟上侧从左到右 - for (; j startY; j--) { + for (; j >= loop; j--) { res[i][j] = count++; } // 模拟左侧从下到上 - for (; i > startX; i--) { + for (; i >= loop; i--) { res[i][j] = count++; } - - loop--; - - startX += 1; - startY += 1; - - offset += 2; + start++; } if (n % 2 == 1) { - res[mid][mid] = count; + res[start][start] = count; } return res; @@ -564,6 +544,57 @@ int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){ return ans; } ``` +Scala: +```scala +object Solution { + def generateMatrix(n: Int): Array[Array[Int]] = { + var res = Array.ofDim[Int](n, n) // 定义一个n*n的二维矩阵 + var num = 1 // 标志当前到了哪个数字 + var i = 0 // 横坐标 + var j = 0 // 竖坐标 + while (num <= n * n) { + // 向右:当j不越界,并且下一个要填的数字是空白时 + while (j < n && res(i)(j) == 0) { + res(i)(j) = num // 当前坐标等于num + num += 1 // num++ + j += 1 // 竖坐标+1 + } + i += 1 // 下移一行 + j -= 1 // 左移一列 + + // 剩下的都同上 + + // 向下 + while (i < n && res(i)(j) == 0) { + res(i)(j) = num + num += 1 + i += 1 + } + i -= 1 + j -= 1 + + // 向左 + while (j >= 0 && res(i)(j) == 0) { + res(i)(j) = num + num += 1 + j -= 1 + } + i -= 1 + j += 1 + + // 向上 + while (i >= 0 && res(i)(j) == 0) { + res(i)(j) = num + num += 1 + i -= 1 + } + i += 1 + j += 1 + } + res + } +} +``` -----------------------
diff --git a/problems/0062.不同路径.md b/problems/0062.不同路径.md index 4a9af129..f59b7be8 100644 --- a/problems/0062.不同路径.md +++ b/problems/0062.不同路径.md @@ -273,7 +273,7 @@ public: return dp[m-1][n-1]; } -``` +``` ### Python ```python @@ -347,7 +347,35 @@ var uniquePaths = function(m, n) { }; ``` +### TypeScript + +```typescript +function uniquePaths(m: number, n: number): number { + /** + dp[i][j]: 到达(i, j)的路径数 + dp[0][*]: 1; + dp[*][0]: 1; + ... + dp[i][j]: dp[i - 1][j] + dp[i][j - 1]; + */ + const dp: number[][] = new Array(m).fill(0).map(_ => []); + for (let i = 0; i < m; i++) { + dp[i][0] = 1; + } + for (let i = 0; i < n; i++) { + dp[0][i] = 1; + } + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[m - 1][n - 1]; +}; +``` + ### C + ```c //初始化dp数组 int **initDP(int m, int n) { diff --git a/problems/0063.不同路径II.md b/problems/0063.不同路径II.md index a40cceda..d09ea0e6 100644 --- a/problems/0063.不同路径II.md +++ b/problems/0063.不同路径II.md @@ -352,7 +352,38 @@ var uniquePathsWithObstacles = function(obstacleGrid) { }; ``` -C +### TypeScript + +```typescript +function uniquePathsWithObstacles(obstacleGrid: number[][]): number { + /** + dp[i][j]: 到达(i, j)的路径数 + dp[0][*]: 用u表示第一个障碍物下标,则u之前为1,u之后(含u)为0 + dp[*][0]: 同上 + ... + dp[i][j]: obstacleGrid[i][j] === 1 ? 0 : dp[i-1][j] + dp[i][j-1]; + */ + const m: number = obstacleGrid.length; + const n: number = obstacleGrid[0].length; + const dp: number[][] = new Array(m).fill(0).map(_ => new Array(n).fill(0)); + for (let i = 0; i < m && obstacleGrid[i][0] === 0; i++) { + dp[i][0] = 1; + } + for (let i = 0; i < n && obstacleGrid[0][i] === 0; i++) { + dp[0][i] = 1; + } + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + if (obstacleGrid[i][j] === 1) continue; + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[m - 1][n - 1]; +}; +``` + +### C + ```c //初始化dp数组 int **initDP(int m, int n, int** obstacleGrid) { diff --git a/problems/0070.爬楼梯.md b/problems/0070.爬楼梯.md index da19ea0e..34d41441 100644 --- a/problems/0070.爬楼梯.md +++ b/problems/0070.爬楼梯.md @@ -308,7 +308,58 @@ var climbStairs = function(n) { }; ``` +TypeScript + +> 爬2阶 + +```typescript +function climbStairs(n: number): number { + /** + dp[i]: i阶楼梯的方法种数 + dp[1]: 1; + dp[2]: 2; + ... + dp[i]: dp[i - 1] + dp[i - 2]; + */ + const dp: number[] = []; + dp[1] = 1; + dp[2] = 2; + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +}; +``` + +> 爬m阶 + +```typescript +function climbStairs(n: number): number { + /** + 一次可以爬m阶 + dp[i]: i阶楼梯的方法种数 + dp[1]: 1; + dp[2]: 2; + dp[3]: dp[2] + dp[1]; + ... + dp[i]: dp[i - 1] + dp[i - 2] + ... + dp[max(i - m, 1)]; 从i-1加到max(i-m, 1) + */ + const m: number = 2; // 本题m为2 + const dp: number[] = new Array(n + 1).fill(0); + dp[1] = 1; + dp[2] = 2; + for (let i = 3; i <= n; i++) { + const end: number = Math.max(i - m, 1); + for (let j = i - 1; j >= end; j--) { + dp[i] += dp[j]; + } + } + return dp[n]; +}; +``` + ### C + ```c int climbStairs(int n){ //若n<=2,返回n diff --git a/problems/0070.爬楼梯完全背包版本.md b/problems/0070.爬楼梯完全背包版本.md index 2286de2d..0f482bb7 100644 --- a/problems/0070.爬楼梯完全背包版本.md +++ b/problems/0070.爬楼梯完全背包版本.md @@ -199,6 +199,28 @@ var climbStairs = function(n) { }; ``` +TypeScript: + +```typescript +function climbStairs(n: number): number { + const m: number = 2; // 本题m为2 + const dp: number[] = new Array(n + 1).fill(0); + dp[0] = 1; + // 遍历背包 + for (let i = 1; i <= n; i++) { + // 遍历物品 + for (let j = 1; j <= m; j++) { + if (j <= i) { + dp[i] += dp[i - j]; + } + } + } + return dp[n]; +}; +``` + + + -----------------------
diff --git a/problems/0077.组合.md b/problems/0077.组合.md index 4560c5b7..9e0398ab 100644 --- a/problems/0077.组合.md +++ b/problems/0077.组合.md @@ -27,7 +27,7 @@ 也可以直接看我的B站视频:[带你学透回溯算法-组合问题(对应力扣题目:77.组合)](https://www.bilibili.com/video/BV1ti4y1L7cv#reply3733925949) -# 思路 +## 思路 本题这是回溯法的经典题目。 @@ -232,7 +232,7 @@ void backtracking(参数) { **对比一下本题的代码,是不是发现有点像!** 所以有了这个模板,就有解题的大体方向,不至于毫无头绪。 -# 总结 +## 总结 组合问题是回溯法解决的经典问题,我们开始的时候给大家列举一个很形象的例子,就是n为100,k为50的话,直接想法就需要50层for循环。 @@ -242,7 +242,7 @@ void backtracking(参数) { 接着用回溯法三部曲,逐步分析了函数参数、终止条件和单层搜索的过程。 -# 剪枝优化 +## 剪枝优化 我们说过,回溯法虽然是暴力搜索,但也有时候可以有点剪枝优化一下的。 @@ -324,7 +324,7 @@ public: }; ``` -# 剪枝总结 +## 剪枝总结 本篇我们准对求组合问题的回溯法代码做了剪枝优化,这个优化如果不画图的话,其实不好理解,也不好讲清楚。 @@ -334,10 +334,10 @@ public: -# 其他语言版本 +## 其他语言版本 -## Java: +### Java: ```java class Solution { List> result = new ArrayList<>(); @@ -366,6 +366,8 @@ class Solution { } ``` +### Python + Python2: ```python class Solution(object): @@ -395,7 +397,6 @@ class Solution(object): return result ``` -## Python ```python class Solution: def combine(self, n: int, k: int) -> List[List[int]]: @@ -432,7 +433,7 @@ class Solution: ``` -## javascript +### javascript 剪枝: ```javascript @@ -456,7 +457,7 @@ const combineHelper = (n, k, startIndex) => { } ``` -## TypeScript +### TypeScript ```typescript function combine(n: number, k: number): number[][] { @@ -479,7 +480,7 @@ function combine(n: number, k: number): number[][] { -## Go +### Go ```Go var res [][]int func combine(n int, k int) [][]int { @@ -534,7 +535,7 @@ func backtrack(n,k,start int,track []int){ } ``` -## C +### C ```c int* path; int pathTop; @@ -642,7 +643,7 @@ int** combine(int n, int k, int* returnSize, int** returnColumnSizes){ } ``` -## Swift +### Swift ```swift func combine(_ n: Int, _ k: Int) -> [[Int]] { diff --git a/problems/0096.不同的二叉搜索树.md b/problems/0096.不同的二叉搜索树.md index 41fcb8fe..25561b50 100644 --- a/problems/0096.不同的二叉搜索树.md +++ b/problems/0096.不同的二叉搜索树.md @@ -227,7 +227,33 @@ const numTrees =(n) => { }; ``` -C: +TypeScript + +```typescript +function numTrees(n: number): number { + /** + dp[i]: i个节点对应的种树 + dp[0]: -1; 无意义; + dp[1]: 1; + ... + dp[i]: 2 * dp[i - 1] + + (dp[1] * dp[i - 2] + dp[2] * dp[i - 3] + ... + dp[i - 2] * dp[1]); 从1加到i-2 + */ + const dp: number[] = []; + dp[0] = -1; // 表示无意义 + dp[1] = 1; + for (let i = 2; i <= n; i++) { + dp[i] = 2 * dp[i - 1]; + for (let j = 1, end = i - 1; j < end; j++) { + dp[i] += dp[j] * dp[end - j]; + } + } + return dp[n]; +}; +``` + +### C + ```c //开辟dp数组 int *initDP(int n) { diff --git a/problems/0101.对称二叉树.md b/problems/0101.对称二叉树.md index e4e232c8..1eb43589 100644 --- a/problems/0101.对称二叉树.md +++ b/problems/0101.对称二叉树.md @@ -238,7 +238,7 @@ public: }; ``` -# 总结 +## 总结 这次我们又深度剖析了一道二叉树的“简单题”,大家会发现,真正的把题目搞清楚其实并不简单,leetcode上accept了和真正掌握了还是有距离的。 @@ -248,7 +248,7 @@ public: 如果已经做过这道题目的同学,读完文章可以再去看看这道题目,思考一下,会有不一样的发现! -# 相关题目推荐 +## 相关题目推荐 这两道题目基本和本题是一样的,只要稍加修改就可以AC。 diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index ab8f2e57..5f69f53d 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -82,6 +82,26 @@ public: } }; ``` +```CPP +# 递归法 +class Solution { +public: + void order(TreeNode* cur, vector>& result, int depth) + { + if (cur == nullptr) return; + if (result.size() == depth) result.push_back(vector()); + result[depth].push_back(cur->val); + order(cur->left, result, depth + 1); + order(cur->right, result, depth + 1); + } + vector> levelOrder(TreeNode* root) { + vector> result; + int depth = 0; + order(root, result, depth); + return result; + } +}; +``` python3代码: diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md index 41463ec1..d3eec16b 100644 --- a/problems/0112.路径总和.md +++ b/problems/0112.路径总和.md @@ -377,22 +377,22 @@ class solution { ```java class solution { - public list> pathsum(treenode root, int targetsum) { - list> res = new arraylist<>(); + public List> pathsum(TreeNode root, int targetsum) { + List> res = new ArrayList<>(); if (root == null) return res; // 非空判断 - - list path = new linkedlist<>(); + + List path = new LinkedList<>(); preorderdfs(root, targetsum, res, path); return res; } - public void preorderdfs(treenode root, int targetsum, list> res, list path) { + public void preorderdfs(TreeNode root, int targetsum, List> res, List path) { path.add(root.val); // 遇到了叶子节点 if (root.left == null && root.right == null) { // 找到了和为 targetsum 的路径 if (targetsum - root.val == 0) { - res.add(new arraylist<>(path)); + res.add(new ArrayList<>(path)); } return; // 如果和不为 targetsum,返回 } @@ -1006,6 +1006,126 @@ func traversal(_ cur: TreeNode?, count: Int) { } ``` +## C +> 0112.路径总和 +递归法: +```c +bool hasPathSum(struct TreeNode* root, int targetSum){ + // 递归结束条件:若当前节点不存在,返回false + if(!root) + return false; + // 若当前节点为叶子节点,且targetSum-root的值为0。(当前路径上的节点值的和满足条件)返回true + if(!root->right && !root->left && targetSum == root->val) + return true; + + // 查看左子树和右子树的所有节点是否满足条件 + return hasPathSum(root->right, targetSum - root->val) || hasPathSum(root->left, targetSum - root->val); +} +``` + +迭代法: +```c +// 存储一个节点以及当前的和 +struct Pair { + struct TreeNode* node; + int sum; +}; + +bool hasPathSum(struct TreeNode* root, int targetSum){ + struct Pair stack[1000]; + int stackTop = 0; + + // 若root存在,则将节点和值封装成一个pair入栈 + if(root) { + struct Pair newPair = {root, root->val}; + stack[stackTop++] = newPair; + } + + // 当栈不为空时 + while(stackTop) { + // 出栈栈顶元素 + struct Pair topPair = stack[--stackTop]; + // 若栈顶元素为叶子节点,且和为targetSum时,返回true + if(!topPair.node->left && !topPair.node->right && topPair.sum == targetSum) + return true; + + // 若当前栈顶节点有左右孩子,计算和并入栈 + if(topPair.node->left) { + struct Pair newPair = {topPair.node->left, topPair.sum + topPair.node->left->val}; + stack[stackTop++] = newPair; + } + if(topPair.node->right) { + struct Pair newPair = {topPair.node->right, topPair.sum + topPair.node->right->val}; + stack[stackTop++] = newPair; + } + } + return false; +} +``` +> 0113.路径总和 II +```c +int** ret; +int* path; +int* colSize; +int retTop; +int pathTop; + +void traversal(const struct TreeNode* const node, int count) { + // 若当前节点为叶子节点 + if(!node->right && !node->left) { + // 若当前path上的节点值总和等于targetSum。 + if(count == 0) { + // 复制当前path + int *curPath = (int*)malloc(sizeof(int) * pathTop); + memcpy(curPath, path, sizeof(int) * pathTop); + // 记录当前path的长度为pathTop + colSize[retTop] = pathTop; + // 将当前path加入到ret数组中 + ret[retTop++] = curPath; + } + return; + } + + // 若节点有左/右孩子 + if(node->left) { + // 将左孩子的值加入path中 + path[pathTop++] = node->left->val; + traversal(node->left, count - node->left->val); + // 回溯 + pathTop--; + } + if(node->right) { + // 将右孩子的值加入path中 + path[pathTop++] = node->right->val; + traversal(node->right, count - node->right->val); + // 回溯 + --pathTop; + } +} + +int** pathSum(struct TreeNode* root, int targetSum, int* returnSize, int** returnColumnSizes){ + // 初始化数组 + ret = (int**)malloc(sizeof(int*) * 1000); + path = (int*)malloc(sizeof(int*) * 1000); + colSize = (int*)malloc(sizeof(int) * 1000); + retTop = pathTop = 0; + *returnSize = 0; + + // 若根节点不存在,返回空的ret + if(!root) + return ret; + // 将根节点加入到path中 + path[pathTop++] = root->val; + traversal(root, targetSum - root->val); + + // 设置返回ret数组大小,以及其中每个一维数组元素的长度 + *returnSize = retTop; + *returnColumnSizes = colSize; + + return ret; +} +``` + ----------------------- diff --git a/problems/0121.买卖股票的最佳时机.md b/problems/0121.买卖股票的最佳时机.md index f0bc3b97..a2498bb6 100644 --- a/problems/0121.买卖股票的最佳时机.md +++ b/problems/0121.买卖股票的最佳时机.md @@ -426,6 +426,46 @@ var maxProfit = function(prices) { }; ``` +TypeScript: + +> 贪心法 + +```typescript +function maxProfit(prices: number[]): number { + if (prices.length === 0) return 0; + let buy: number = prices[0]; + let profitMax: number = 0; + for (let i = 1, length = prices.length; i < length; i++) { + profitMax = Math.max(profitMax, prices[i] - buy); + buy = Math.min(prices[i], buy); + } + return profitMax; +}; +``` + +> 动态规划 + +```typescript +function maxProfit(prices: number[]): number { + /** + dp[i][0]: 第i天持有股票的最大现金 + dp[i][1]: 第i天不持有股票的最大现金 + */ + const length = prices.length; + if (length === 0) return 0; + const dp: number[][] = []; + dp[0] = [-prices[0], 0]; + for (let i = 1; i < length; i++) { + dp[i] = []; + dp[i][0] = Math.max(dp[i - 1][0], -prices[i]); + dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]); + } + return dp[length - 1][1]; +}; +``` + + + -----------------------
diff --git a/problems/0122.买卖股票的最佳时机II(动态规划).md b/problems/0122.买卖股票的最佳时机II(动态规划).md index 5a165a14..12b21fde 100644 --- a/problems/0122.买卖股票的最佳时机II(动态规划).md +++ b/problems/0122.买卖股票的最佳时机II(动态规划).md @@ -295,6 +295,42 @@ const maxProfit = (prices) => { } ``` +TypeScript: + +> 动态规划 + +```typescript +function maxProfit(prices: number[]): number { + /** + dp[i][0]: 第i天持有股票 + dp[i][1]: 第i天不持有股票 + */ + const length: number = prices.length; + if (length === 0) return 0; + const dp: number[][] = new Array(length).fill(0).map(_ => []); + dp[0] = [-prices[0], 0]; + for (let i = 1; i < length; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]); + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]); + } + return dp[length - 1][1]; +}; +``` + +> 贪心法 + +```typescript +function maxProfit(prices: number[]): number { + let resProfit: number = 0; + for (let i = 1, length = prices.length; i < length; i++) { + if (prices[i] > prices[i - 1]) { + resProfit += prices[i] - prices[i - 1]; + } + } + return resProfit; +}; +``` + ----------------------- diff --git a/problems/0123.买卖股票的最佳时机III.md b/problems/0123.买卖股票的最佳时机III.md index 56ade343..67c99497 100644 --- a/problems/0123.买卖股票的最佳时机III.md +++ b/problems/0123.买卖股票的最佳时机III.md @@ -352,6 +352,36 @@ const maxProfit = prices => { }; ``` +TypeScript: + +> 版本一 + +```typescript +function maxProfit(prices: number[]): number { + /** + dp[i][0]: 无操作; + dp[i][1]: 第一次买入; + dp[i][2]: 第一次卖出; + dp[i][3]: 第二次买入; + dp[i][4]: 第二次卖出; + */ + const length: number = prices.length; + if (length === 0) return 0; + const dp: number[][] = new Array(length).fill(0) + .map(_ => new Array(5).fill(0)); + dp[0][1] = -prices[0]; + dp[0][3] = -prices[0]; + for (let i = 1; i < length; i++) { + dp[i][0] = dp[i - 1][0]; + dp[i][1] = Math.max(dp[i - 1][1], -prices[i]); + dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]); + dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]); + dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]); + } + return Math.max(dp[length - 1][2], dp[length - 1][4]); +}; +``` + Go: > 版本一: diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md index 10b747cb..7a702898 100644 --- a/problems/0131.分割回文串.md +++ b/problems/0131.分割回文串.md @@ -454,31 +454,36 @@ var partition = function(s) { ```typescript function partition(s: string): string[][] { - function isPalindromeStr(s: string, left: number, right: number): boolean { - while (left < right) { - if (s[left++] !== s[right--]) { - return false; + const res: string[][] = [] + const path: string[] = [] + const isHuiwen = ( + str: string, + startIndex: number, + endIndex: number + ): boolean => { + for (; startIndex < endIndex; startIndex++, endIndex--) { + if (str[startIndex] !== str[endIndex]) { + return false } } - return true; + return true } - function backTracking(s: string, startIndex: number, route: string[]): void { - let length: number = s.length; - if (length === startIndex) { - resArr.push(route.slice()); - return; + const rec = (str: string, index: number): void => { + if (index >= str.length) { + res.push([...path]) + return } - for (let i = startIndex; i < length; i++) { - if (isPalindromeStr(s, startIndex, i)) { - route.push(s.slice(startIndex, i + 1)); - backTracking(s, i + 1, route); - route.pop(); + for (let i = index; i < str.length; i++) { + if (!isHuiwen(str, index, i)) { + continue } + path.push(str.substring(index, i + 1)) + rec(str, i + 1) + path.pop() } } - const resArr: string[][] = []; - backTracking(s, 0, []); - return resArr; + rec(s, 0) + return res }; ``` diff --git a/problems/0135.分发糖果.md b/problems/0135.分发糖果.md index 72f3dd56..3456a04c 100644 --- a/problems/0135.分发糖果.md +++ b/problems/0135.分发糖果.md @@ -126,11 +126,11 @@ public: ## 其他语言版本 -### Java +### Java ```java class Solution { - /** - 分两个阶段 + /** + 分两个阶段 1、起点下标1 从左往右,只要 右边 比 左边 大,右边的糖果=左边 + 1 2、起点下标 ratings.length - 2 从右往左, 只要左边 比 右边 大,此时 左边的糖果应该 取本身的糖果数(符合比它左边大) 和 右边糖果数 + 1 二者的最大值,这样才符合 它比它左边的大,也比它右边大 */ @@ -160,7 +160,7 @@ class Solution { } ``` -### Python +### Python ```python class Solution: def candy(self, ratings: List[int]) -> int: @@ -213,6 +213,25 @@ func findMax(num1 int ,num2 int) int{ } ``` +### Rust +```rust +pub fn candy(ratings: Vec) -> i32 { + let mut candies = vec![1i32; ratings.len()]; + for i in 1..ratings.len() { + if ratings[i - 1] < ratings[i] { + candies[i] = candies[i - 1] + 1; + } + } + + for i in (0..ratings.len()-1).rev() { + if ratings[i] > ratings[i + 1] { + candies[i] = candies[i].max(candies[i + 1] + 1); + } + } + candies.iter().sum() +} +``` + ### Javascript: ```Javascript var candy = function(ratings) { @@ -229,7 +248,7 @@ var candy = function(ratings) { candys[i] = Math.max(candys[i], candys[i + 1] + 1) } } - + let count = candys.reduce((a, b) => { return a + b }) diff --git a/problems/0139.单词拆分.md b/problems/0139.单词拆分.md index ac834f04..5b4e92b9 100644 --- a/problems/0139.单词拆分.md +++ b/problems/0139.单词拆分.md @@ -345,6 +345,48 @@ const wordBreak = (s, wordDict) => { } ``` +TypeScript: + +> 动态规划 + +```typescript +function wordBreak(s: string, wordDict: string[]): boolean { + const dp: boolean[] = new Array(s.length + 1).fill(false); + dp[0] = true; + for (let i = 1; i <= s.length; i++) { + for (let j = 0; j < i; j++) { + const tempStr: string = s.slice(j, i); + if (wordDict.includes(tempStr) && dp[j] === true) { + dp[i] = true; + break; + } + } + } + return dp[s.length]; +}; +``` + +> 记忆化回溯 + +```typescript +function wordBreak(s: string, wordDict: string[]): boolean { + // 只需要记忆结果为false的情况 + const memory: boolean[] = []; + return backTracking(s, wordDict, 0, memory); + function backTracking(s: string, wordDict: string[], startIndex: number, memory: boolean[]): boolean { + if (startIndex >= s.length) return true; + if (memory[startIndex] === false) return false; + for (let i = startIndex + 1, length = s.length; i <= length; i++) { + const str: string = s.slice(startIndex, i); + if (wordDict.includes(str) && backTracking(s, wordDict, i, memory)) + return true; + } + memory[startIndex] = false; + return false; + } +}; +``` + ----------------------- diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md index e8ca950d..f8e62d45 100644 --- a/problems/0142.环形链表II.md +++ b/problems/0142.环形链表II.md @@ -370,7 +370,31 @@ ListNode *detectCycle(ListNode *head) { } ``` - +Scala: +```scala +object Solution { + def detectCycle(head: ListNode): ListNode = { + var fast = head // 快指针 + var slow = head // 慢指针 + while (fast != null && fast.next != null) { + fast = fast.next.next // 快指针一次走两步 + slow = slow.next // 慢指针一次走一步 + // 如果相遇,fast快指针回到头 + if (fast == slow) { + fast = head + // 两个指针一步一步的走,第一次相遇的节点必是入环节点 + while (fast != slow) { + fast = fast.next + slow = slow.next + } + return fast + } + } + // 如果fast指向空值,必然无环返回null + null + } +} +``` -----------------------
diff --git a/problems/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md index fd3d69aa..6ce7e2f9 100644 --- a/problems/0150.逆波兰表达式求值.md +++ b/problems/0150.逆波兰表达式求值.md @@ -136,19 +136,19 @@ java: class Solution { public int evalRPN(String[] tokens) { Deque stack = new LinkedList(); - for (int i = 0; i < tokens.length; ++i) { - if ("+".equals(tokens[i])) { // leetcode 内置jdk的问题,不能使用==判断字符串是否相等 + for (String s : tokens) { + if ("+".equals(s)) { // leetcode 内置jdk的问题,不能使用==判断字符串是否相等 stack.push(stack.pop() + stack.pop()); // 注意 - 和/ 需要特殊处理 - } else if ("-".equals(tokens[i])) { + } else if ("-".equals(s)) { stack.push(-stack.pop() + stack.pop()); - } else if ("*".equals(tokens[i])) { + } else if ("*".equals(s)) { stack.push(stack.pop() * stack.pop()); - } else if ("/".equals(tokens[i])) { + } else if ("/".equals(s)) { int temp1 = stack.pop(); int temp2 = stack.pop(); stack.push(temp2 / temp1); } else { - stack.push(Integer.valueOf(tokens[i])); + stack.push(Integer.valueOf(s)); } } return stack.pop(); diff --git a/problems/0188.买卖股票的最佳时机IV.md b/problems/0188.买卖股票的最佳时机IV.md index 61c558a1..27eb38c3 100644 --- a/problems/0188.买卖股票的最佳时机IV.md +++ b/problems/0188.买卖股票的最佳时机IV.md @@ -409,5 +409,27 @@ var maxProfit = function(k, prices) { }; ``` +TypeScript: + +```typescript +function maxProfit(k: number, prices: number[]): number { + const length: number = prices.length; + if (length === 0) return 0; + const dp: number[][] = new Array(length).fill(0) + .map(_ => new Array(k * 2 + 1).fill(0)); + for (let i = 1; i <= k; i++) { + dp[0][i * 2 - 1] = -prices[0]; + } + for (let i = 1; i < length; i++) { + for (let j = 1; j < 2 * k + 1; j++) { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + Math.pow(-1, j) * prices[i]); + } + } + return dp[length - 1][2 * k]; +}; +``` + + + -----------------------
diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md index dfe1f3a0..a828b9a9 100644 --- a/problems/0198.打家劫舍.md +++ b/problems/0198.打家劫舍.md @@ -189,6 +189,29 @@ const rob = nums => { }; ``` +TypeScript: + +```typescript +function rob(nums: number[]): number { + /** + dp[i]: 前i个房屋能偷到的最大金额 + dp[0]: nums[0]; + dp[1]: max(nums[0], nums[1]); + ... + dp[i]: max(dp[i-1], dp[i-2]+nums[i]); + */ + const length: number = nums.length; + if (length === 1) return nums[0]; + const dp: number[] = []; + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + for (let i = 2; i < length; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + return dp[length - 1]; +}; +``` + diff --git a/problems/0202.快乐数.md b/problems/0202.快乐数.md index 741a735a..be8686f7 100644 --- a/problems/0202.快乐数.md +++ b/problems/0202.快乐数.md @@ -385,5 +385,61 @@ bool isHappy(int n){ return bHappy; } ``` + +Scala: +```scala +object Solution { + // 引入mutable + import scala.collection.mutable + def isHappy(n: Int): Boolean = { + // 存放每次计算后的结果 + val set: mutable.HashSet[Int] = new mutable.HashSet[Int]() + var tmp = n // 因为形参是不可变量,所以需要找到一个临时变量 + // 开始进入循环 + while (true) { + val sum = getSum(tmp) // 获取这个数每个值的平方和 + if (sum == 1) return true // 如果最终等于 1,则返回true + // 如果set里面已经有这个值了,说明进入无限循环,可以返回false,否则添加这个值到set + if (set.contains(sum)) return false + else set.add(sum) + tmp = sum + } + // 最终需要返回值,直接返回个false + false + } + + def getSum(n: Int): Int = { + var sum = 0 + var tmp = n + while (tmp != 0) { + sum += (tmp % 10) * (tmp % 10) + tmp = tmp / 10 + } + sum + } + + +C#: +```csharp +public class Solution { + private int getSum(int n) { + int sum = 0; + //每位数的换算 + while (n > 0) { + sum += (n % 10) * (n % 10); + n /= 10; + } + return sum; + } + public bool IsHappy(int n) { + HashSet set = new HashSet(); + while(n != 1 && !set.Contains(n)) { //判断避免循环 + set.Add(n); + n = getSum(n); + } + return n == 1; + } +} +``` -----------------------
diff --git a/problems/0203.移除链表元素.md b/problems/0203.移除链表元素.md index 751553e2..67776529 100644 --- a/problems/0203.移除链表元素.md +++ b/problems/0203.移除链表元素.md @@ -478,6 +478,36 @@ impl Solution { } } ``` - +Scala: +```scala +/** + * Definition for singly-linked list. + * class ListNode(_x: Int = 0, _next: ListNode = null) { + * var next: ListNode = _next + * var x: Int = _x + * } + */ +object Solution { + def removeElements(head: ListNode, `val`: Int): ListNode = { + if (head == null) return head + var dummy = new ListNode(-1, head) // 定义虚拟头节点 + var cur = head // cur 表示当前节点 + var pre = dummy // pre 表示cur前一个节点 + while (cur != null) { + if (cur.x == `val`) { + // 相等,就删除那么cur的前一个节点pre执行cur的下一个 + pre.next = cur.next + } else { + // 不相等,pre就等于当前cur节点 + pre = cur + } + // 向下迭代 + cur = cur.next + } + // 最终返回dummy的下一个,就是链表的头 + dummy.next + } +} +``` -----------------------
diff --git a/problems/0206.翻转链表.md b/problems/0206.翻转链表.md index 941928ba..25b16907 100644 --- a/problems/0206.翻转链表.md +++ b/problems/0206.翻转链表.md @@ -496,6 +496,40 @@ struct ListNode* reverseList(struct ListNode* head){ return reverse(NULL, head); } ``` +Scala: +双指针法: +```scala +object Solution { + def reverseList(head: ListNode): ListNode = { + var pre: ListNode = null + var cur = head + while (cur != null) { + var tmp = cur.next + cur.next = pre + pre = cur + cur = tmp + } + pre + } +} +``` +递归法: +```scala +object Solution { + def reverseList(head: ListNode): ListNode = { + reverse(null, head) + } + + def reverse(pre: ListNode, cur: ListNode): ListNode = { + if (cur == null) { + return pre // 如果当前cur为空,则返回pre + } + val tmp: ListNode = cur.next + cur.next = pre + reverse(cur, tmp) // 此时cur成为前一个节点,tmp是当前节点 + } +} +``` -----------------------
diff --git a/problems/0209.长度最小的子数组.md b/problems/0209.长度最小的子数组.md index d31cba3f..fbef7692 100644 --- a/problems/0209.长度最小的子数组.md +++ b/problems/0209.长度最小的子数组.md @@ -400,6 +400,54 @@ class Solution { } } ``` +Scala: + +滑动窗口: +```scala +object Solution { + def minSubArrayLen(target: Int, nums: Array[Int]): Int = { + var result = Int.MaxValue // 返回结果,默认最大值 + var left = 0 // 慢指针,当sum>=target,向右移动 + var sum = 0 // 窗口值的总和 + for (right <- 0 until nums.length) { + sum += nums(right) + while (sum >= target) { + result = math.min(result, right - left + 1) // 产生新结果 + sum -= nums(left) // 左指针移动,窗口总和减去左指针的值 + left += 1 // 左指针向右移动 + } + } + // 相当于三元运算符,return关键字可以省略 + if (result == Int.MaxValue) 0 else result + } +} +``` + +暴力解法: +```scala +object Solution { + def minSubArrayLen(target: Int, nums: Array[Int]): Int = { + import scala.util.control.Breaks + var res = Int.MaxValue + var subLength = 0 + for (i <- 0 until nums.length) { + var sum = 0 + Breaks.breakable( + for (j <- i until nums.length) { + sum += nums(j) + if (sum >= target) { + subLength = j - i + 1 + res = math.min(subLength, res) + Breaks.break() + } + } + ) + } + // 相当于三元运算符 + if (res == Int.MaxValue) 0 else res + } +} +``` -----------------------
diff --git a/problems/0213.打家劫舍II.md b/problems/0213.打家劫舍II.md index 8e569e46..9e698d01 100644 --- a/problems/0213.打家劫舍II.md +++ b/problems/0213.打家劫舍II.md @@ -165,7 +165,30 @@ const robRange = (nums, start, end) => { return dp[end] } ``` +TypeScript: + +```typescript +function rob(nums: number[]): number { + const length: number = nums.length; + if (length === 0) return 0; + if (length === 1) return nums[0]; + return Math.max(robRange(nums, 0, length - 2), + robRange(nums, 1, length - 1)); +}; +function robRange(nums: number[], start: number, end: number): number { + if (start === end) return nums[start]; + const dp: number[] = []; + dp[start] = nums[start]; + dp[start + 1] = Math.max(nums[start], nums[start + 1]); + for (let i = start + 2; i <= end; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + return dp[end]; +} +``` + Go: + ```go // 打家劫舍Ⅱ 动态规划 // 时间复杂度O(n) 空间复杂度O(n) diff --git a/problems/0242.有效的字母异位词.md b/problems/0242.有效的字母异位词.md index 080166fd..8fd9c604 100644 --- a/problems/0242.有效的字母异位词.md +++ b/problems/0242.有效的字母异位词.md @@ -307,6 +307,52 @@ impl Solution { } } ``` + + +Scala: +```scala +object Solution { + def isAnagram(s: String, t: String): Boolean = { + // 如果两个字符串的长度不等,直接返回false + if (s.length != t.length) return false + val record = new Array[Int](26) // 记录每个单词出现了多少次 + // 遍历字符串,对于s字符串单词对应的记录+=1,t字符串对应的记录-=1 + for (i <- 0 until s.length) { + record(s(i) - 97) += 1 + record(t(i) - 97) -= 1 + } + // 如果不等于则直接返回false + for (i <- 0 until 26) { + if (record(i) != 0) { + return false + } + } + // 如果前面不返回false,说明匹配成功,返回true,return可以省略 + true + } +} +``` + + +C#: +```csharp + public bool IsAnagram(string s, string t) { + int sl=s.Length,tl=t.Length; + if(sl!=tl) return false; + int[] a = new int[26]; + for(int i = 0; i < sl; i++){ + a[s[i] - 'a']++; + a[t[i] - 'a']--; + } + foreach (int i in a) + { + if (i != 0) + return false; + } + return true; + } +``` + ## 相关题目 * 383.赎金信 diff --git a/problems/0279.完全平方数.md b/problems/0279.完全平方数.md index 9bad2085..5b15639c 100644 --- a/problems/0279.完全平方数.md +++ b/problems/0279.完全平方数.md @@ -355,5 +355,24 @@ var numSquares2 = function(n) { }; ``` +TypeScript: + +```typescript +function numSquares(n: number): number { + const goodsNum: number = Math.floor(Math.sqrt(n)); + const dp: number[] = new Array(n + 1).fill(Infinity); + dp[0] = 0; + for (let i = 1; i <= goodsNum; i++) { + const tempVal: number = i * i; + for (let j = tempVal; j <= n; j++) { + dp[j] = Math.min(dp[j], dp[j - tempVal] + 1); + } + } + return dp[n]; +}; +``` + + + -----------------------
diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index dfdd5125..cfa8ae12 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -168,6 +168,56 @@ func lengthOfLIS(nums []int ) int { } ``` +```go +// 动态规划求解 +func lengthOfLIS(nums []int) int { + // dp数组的定义 dp[i]表示取第i个元素的时候,表示子序列的长度,其中包括 nums[i] 这个元素 + dp := make([]int, len(nums)) + + // 初始化,所有的元素都应该初始化为1 + for i := range dp { + dp[i] = 1 + } + + ans := dp[0] + for i := 1; i < len(nums); i++ { + for j := 0; j < i; j++ { + if nums[i] > nums[j] { + dp[i] = max(dp[i], dp[j] + 1) + } + } + if dp[i] > ans { + ans = dp[i] + } + } + return ans +} + +func max(x, y int) int { + if x > y { + return x + } + return y +} +``` + +Rust: +```rust +pub fn length_of_lis(nums: Vec) -> i32 { + let mut dp = vec![1; nums.len() + 1]; + let mut result = 1; + for i in 1..nums.len() { + for j in 0..i { + if nums[j] < nums[i] { + dp[i] = dp[i].max(dp[j] + 1); + } + result = result.max(dp[i]); + } + } + result +} +``` + Javascript ```javascript const lengthOfLIS = (nums) => { diff --git a/problems/0309.最佳买卖股票时机含冷冻期.md b/problems/0309.最佳买卖股票时机含冷冻期.md index f3e7541b..f037fe85 100644 --- a/problems/0309.最佳买卖股票时机含冷冻期.md +++ b/problems/0309.最佳买卖股票时机含冷冻期.md @@ -325,6 +325,66 @@ const maxProfit = (prices) => { }; ``` +TypeScript: + +> 版本一,与本文思路一致 + +```typescript +function maxProfit(prices: number[]): number { + /** + dp[i][0]: 持股状态; + dp[i][1]: 无股状态,当天为非冷冻期; + dp[i][2]: 无股状态,当天卖出; + dp[i][3]: 无股状态,当天为冷冻期; + */ + const length: number = prices.length; + const dp: number[][] = new Array(length).fill(0).map(_ => []); + dp[0][0] = -prices[0]; + dp[0][1] = dp[0][2] = dp[0][3] = 0; + for (let i = 1; i < length; i++) { + dp[i][0] = Math.max( + dp[i - 1][0], + Math.max(dp[i - 1][1], dp[i - 1][3]) - prices[i] + ); + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][3]); + dp[i][2] = dp[i - 1][0] + prices[i]; + dp[i][3] = dp[i - 1][2]; + } + const lastEl: number[] = dp[length - 1]; + return Math.max(lastEl[1], lastEl[2], lastEl[3]); +}; +``` + +> 版本二,状态定义略有不同,可以帮助理解 + +```typescript +function maxProfit(prices: number[]): number { + /** + dp[i][0]: 持股状态,当天买入; + dp[i][1]: 持股状态,当天未买入; + dp[i][2]: 无股状态,当天卖出; + dp[i][3]: 无股状态,当天未卖出; + + 买入有冷冻期限制,其实就是状态[0]只能由前一天的状态[3]得到; + 如果卖出有冷冻期限制,其实就是[2]由[1]得到。 + */ + const length: number = prices.length; + const dp: number[][] = new Array(length).fill(0).map(_ => []); + dp[0][0] = -prices[0]; + dp[0][1] = -Infinity; + dp[0][2] = dp[0][3] = 0; + for (let i = 1; i < length; i++) { + dp[i][0] = dp[i - 1][3] - prices[i]; + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0]); + dp[i][2] = Math.max(dp[i - 1][0], dp[i - 1][1]) + prices[i]; + dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2]); + } + return Math.max(dp[length - 1][2], dp[length - 1][3]); +}; +``` + + + -----------------------
diff --git a/problems/0322.零钱兑换.md b/problems/0322.零钱兑换.md index 3a8d0662..fc0490c8 100644 --- a/problems/0322.零钱兑换.md +++ b/problems/0322.零钱兑换.md @@ -220,7 +220,7 @@ class Solution: for j in range(coin, amount + 1): dp[j] = min(dp[j], dp[j - coin] + 1) return dp[amount] if dp[amount] < amount + 1 else -1 - + def coinChange1(self, coins: List[int], amount: int) -> int: '''版本二''' # 初始化 @@ -302,6 +302,24 @@ func min(a, b int) int { ``` +Rust: + +```rust +pub fn coin_change(coins: Vec, amount: i32) -> i32 { + let amount = amount as usize; + let mut dp = vec![i32::MAX; amount + 1]; + dp[0] = 0; + for i in 0..coins.len() { + for j in coins[i] as usize..=amount { + if dp[j - coins[i] as usize] != i32::MAX { + dp[j] = dp[j].min(dp[j - coins[i] as usize] + 1); + } + } + } + if dp[amount] == i32::MAX { -1 } else { dp[amount] } +} +``` + Javascript: ```javascript const coinChange = (coins, amount) => { @@ -322,7 +340,21 @@ const coinChange = (coins, amount) => { } ``` +TypeScript: +```typescript +function coinChange(coins: number[], amount: number): number { + const dp: number[] = new Array(amount + 1).fill(Infinity); + dp[0] = 0; + for (let i = 0; i < coins.length; i++) { + for (let j = coins[i]; j <= amount; j++) { + if (dp[j - coins[i]] === Infinity) continue; + dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); + } + } + return dp[amount] === Infinity ? -1 : dp[amount]; +}; +``` -----------------------
diff --git a/problems/0337.打家劫舍III.md b/problems/0337.打家劫舍III.md index a4d8f6b2..6f50723d 100644 --- a/problems/0337.打家劫舍III.md +++ b/problems/0337.打家劫舍III.md @@ -429,7 +429,50 @@ const rob = root => { }; ``` +### TypeScript + +> 记忆化后序遍历 + +```typescript +const memory: Map = new Map(); +function rob(root: TreeNode | null): number { + if (root === null) return 0; + if (memory.has(root)) return memory.get(root); + // 不取当前节点 + const res1: number = rob(root.left) + rob(root.right); + // 取当前节点 + let res2: number = root.val; + if (root.left !== null) res2 += rob(root.left.left) + rob(root.left.right); + if (root.right !== null) res2 += rob(root.right.left) + rob(root.right.right); + const res: number = Math.max(res1, res2); + memory.set(root, res); + return res; +}; +``` + +> 状态标记化后序遍历 + +```typescript +function rob(root: TreeNode | null): number { + return Math.max(...robNode(root)); +}; +// [0]-不偷当前节点能获得的最大金额; [1]-偷~~ +type MaxValueArr = [number, number]; +function robNode(node: TreeNode | null): MaxValueArr { + if (node === null) return [0, 0]; + const leftArr: MaxValueArr = robNode(node.left); + const rightArr: MaxValueArr = robNode(node.right); + // 不偷 + const val1: number = Math.max(leftArr[0], leftArr[1]) + + Math.max(rightArr[0], rightArr[1]); + // 偷 + const val2: number = leftArr[0] + rightArr[0] + node.val; + return [val1, val2]; +} +``` + ### Go + ```go // 打家劫舍Ⅲ 动态规划 // 时间复杂度O(n) 空间复杂度O(logn) diff --git a/problems/0343.整数拆分.md b/problems/0343.整数拆分.md index 4a7ba6ab..279f1d71 100644 --- a/problems/0343.整数拆分.md +++ b/problems/0343.整数拆分.md @@ -274,7 +274,33 @@ var integerBreak = function(n) { }; ``` -C: +### TypeScript + +```typescript +function integerBreak(n: number): number { + /** + dp[i]: i对应的最大乘积 + dp[2]: 1; + ... + dp[i]: max( + 1 * dp[i - 1], 1 * (i - 1), + 2 * dp[i - 2], 2 * (i - 2), + ..., (i - 2) * dp[2], (i - 2) * 2 + ); + */ + const dp: number[] = new Array(n + 1).fill(0); + dp[2] = 1; + for (let i = 3; i <= n; i++) { + for (let j = 1; j <= i - 2; j++) { + dp[i] = Math.max(dp[i], j * dp[i - j], j * (i - j)); + } + } + return dp[n]; +}; +``` + +### C + ```c //初始化DP数组 int *initDP(int num) { diff --git a/problems/0349.两个数组的交集.md b/problems/0349.两个数组的交集.md index 45f19b6e..f7dab3d7 100644 --- a/problems/0349.两个数组的交集.md +++ b/problems/0349.两个数组的交集.md @@ -313,6 +313,69 @@ int* intersection1(int* nums1, int nums1Size, int* nums2, int nums2Size, int* re } ``` +Scala: + +正常解法: +```scala +object Solution { + def intersection(nums1: Array[Int], nums2: Array[Int]): Array[Int] = { + // 导入mutable + import scala.collection.mutable + // 临时Set,用于记录数组1出现的每个元素 + val tmpSet: mutable.HashSet[Int] = new mutable.HashSet[Int]() + // 结果Set,存储最终结果 + val resSet: mutable.HashSet[Int] = new mutable.HashSet[Int]() + // 遍历nums1,把每个元素添加到tmpSet + nums1.foreach(tmpSet.add(_)) + // 遍历nums2,如果在tmpSet存在就添加到resSet + nums2.foreach(elem => { + if (tmpSet.contains(elem)) { + resSet.add(elem) + } + }) + // 将结果转换为Array返回,return可以省略 + resSet.toArray + } +} +``` +骚操作1: +```scala +object Solution { + def intersection(nums1: Array[Int], nums2: Array[Int]): Array[Int] = { + // 先转为Set,然后取交集,最后转换为Array + (nums1.toSet).intersect(nums2.toSet).toArray + } +} +``` +骚操作2: +```scala +object Solution { + def intersection(nums1: Array[Int], nums2: Array[Int]): Array[Int] = { + // distinct去重,然后取交集 + (nums1.distinct).intersect(nums2.distinct) + } +} + + +C#: +```csharp + public int[] Intersection(int[] nums1, int[] nums2) { + if(nums1==null||nums1.Length==0||nums2==null||nums1.Length==0) + return new int[0]; //注意数组条件 + HashSet one = Insert(nums1); + HashSet two = Insert(nums2); + one.IntersectWith(two); + return one.ToArray(); + } + public HashSet Insert(int[] nums){ + HashSet one = new HashSet(); + foreach(int num in nums){ + one.Add(num); + } + return one; + } + +``` ## 相关题目 * 350.两个数组的交集 II diff --git a/problems/0377.组合总和Ⅳ.md b/problems/0377.组合总和Ⅳ.md index aaf27e61..1d808a3a 100644 --- a/problems/0377.组合总和Ⅳ.md +++ b/problems/0377.组合总和Ⅳ.md @@ -221,7 +221,27 @@ const combinationSum4 = (nums, target) => { }; ``` +TypeScript: + +```typescript +function combinationSum4(nums: number[], target: number): number { + const dp: number[] = new Array(target + 1).fill(0); + dp[0] = 1; + // 遍历背包 + for (let i = 1; i <= target; i++) { + // 遍历物品 + for (let j = 0, length = nums.length; j < length; j++) { + if (i >= nums[j]) { + dp[i] += dp[i - nums[j]]; + } + } + } + return dp[target]; +}; +``` + Rust + ```Rust impl Solution { pub fn combination_sum4(nums: Vec, target: i32) -> i32 { diff --git a/problems/0383.赎金信.md b/problems/0383.赎金信.md index 8e4cbbf8..75dafb72 100644 --- a/problems/0383.赎金信.md +++ b/problems/0383.赎金信.md @@ -114,23 +114,25 @@ Java: ```Java class Solution { public boolean canConstruct(String ransomNote, String magazine) { - //记录杂志字符串出现的次数 - int[] arr = new int[26]; - int temp; - for (int i = 0; i < magazine.length(); i++) { - temp = magazine.charAt(i) - 'a'; - arr[temp]++; + // 定义一个哈希映射数组 + int[] record = new int[26]; + + // 遍历 + for(char c : magazine.toCharArray()){ + record[c - 'a'] += 1; } - for (int i = 0; i < ransomNote.length(); i++) { - temp = ransomNote.charAt(i) - 'a'; - //对于金信中的每一个字符都在数组中查找 - //找到相应位减一,否则找不到返回false - if (arr[temp] > 0) { - arr[temp]--; - } else { + + for(char c : ransomNote.toCharArray()){ + record[c - 'a'] -= 1; + } + + // 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符 + for(int i : record){ + if(i < 0){ return false; } } + return true; } } @@ -360,6 +362,7 @@ impl Solution { } } ``` + Scala: 版本一: 使用数组作为哈希表 @@ -387,7 +390,7 @@ object Solution { } } ``` -版本二: 使用HashMap + ```scala object Solution { import scala.collection.mutable @@ -422,6 +425,25 @@ object Solution { true } } + + +C#: +```csharp +public bool CanConstruct(string ransomNote, string magazine) { + if(ransomNote.Length > magazine.Length) return false; + int[] letters = new int[26]; + foreach(char c in magazine){ + letters[c-'a']++; + } + foreach(char c in ransomNote){ + letters[c-'a']--; + if(letters[c-'a']<0){ + return false; + } + } + return true; + } + ``` -----------------------
diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 6e93ae8e..eb6601e1 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -417,6 +417,163 @@ var canPartition = function(nums) { ``` +C: +二维dp: +```c +/** +1. dp数组含义:dp[i][j]为背包重量为j时,从[0-i]元素和最大值 +2. 递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]) +3. 初始化:dp[i][0]初始化为0。因为背包重量为0时,不可能放入元素。dp[0][j] = nums[0],当j >= nums[0] && j < target时 +4. 遍历顺序:先遍历物品,再遍历背包 +*/ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +int getSum(int* nums, int numsSize) { + int sum = 0; + + int i; + for(i = 0; i < numsSize; ++i) { + sum += nums[i]; + } + return sum; +} + +bool canPartition(int* nums, int numsSize){ + // 求出元素总和 + int sum = getSum(nums, numsSize); + // 若元素总和为奇数,则不可能得到两个和相等的子数组 + if(sum % 2) + return false; + + // 若子数组的和等于target,则nums可以被分割 + int target = sum / 2; + // 初始化dp数组 + int dp[numsSize][target + 1]; + // dp[j][0]都应被设置为0。因为当背包重量为0时,不可放入元素 + memset(dp, 0, sizeof(int) * numsSize * (target + 1)); + + int i, j; + // 当背包重量j大于nums[0]时,可以在dp[0][j]中放入元素nums[0] + for(j = nums[0]; j <= target; ++j) { + dp[0][j] = nums[0]; + } + + for(i = 1; i < numsSize; ++i) { + for(j = 1; j <= target; ++j) { + // 若当前背包重量j小于nums[i],则其值等于只考虑0到i-1物品时的值 + if(j < nums[i]) + dp[i][j] = dp[i - 1][j]; + // 否则,背包重量等于在背包中放入num[i]/不放入nums[i]的较大值 + else + dp[i][j] = MAX(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]); + } + } + // 判断背包重量为target,且考虑到所有物品时,放入的元素和是否等于target + return dp[numsSize - 1][target] == target; +} +``` +滚动数组: +```c +/** +1. dp数组含义:dp[j]为背包重量为j时,其中可放入元素的最大值 +2. 递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]) +3. 初始化:均初始化为0即可 +4. 遍历顺序:先遍历物品,再后序遍历背包 +*/ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +int getSum(int* nums, int numsSize) { + int sum = 0; + + int i; + for(i = 0; i < numsSize; ++i) { + sum += nums[i]; + } + return sum; +} + +bool canPartition(int* nums, int numsSize){ + // 求出元素总和 + int sum = getSum(nums, numsSize); + // 若元素总和为奇数,则不可能得到两个和相等的子数组 + if(sum % 2) + return false; + // 背包容量 + int target = sum / 2; + + // 初始化dp数组,元素均为0 + int dp[target + 1]; + memset(dp, 0, sizeof(int) * (target + 1)); + + int i, j; + // 先遍历物品,后遍历背包 + for(i = 0; i < numsSize; ++i) { + for(j = target; j >= nums[i]; --j) { + dp[j] = MAX(dp[j], dp[j - nums[i]] + nums[i]); + } + } + + // 查看背包容量为target时,元素总和是否等于target + return dp[target] == target; +} +``` + +TypeScript: + +> 一维数组,简洁 + +```typescript +function canPartition(nums: number[]): boolean { + const sum: number = nums.reduce((pre, cur) => pre + cur); + if (sum % 2 === 1) return false; + const bagSize: number = sum / 2; + const goodsNum: number = nums.length; + const dp: number[] = new Array(bagSize + 1).fill(0); + for (let i = 0; i < goodsNum; i++) { + for (let j = bagSize; j >= nums[i]; j--) { + dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]); + } + } + return dp[bagSize] === bagSize; +}; +``` + +> 二维数组,易懂 + +```typescript +function canPartition(nums: number[]): boolean { + /** + weightArr = nums; + valueArr = nums; + bagSize = sum / 2; (sum为nums各元素总和); + 按照0-1背包处理 + */ + const sum: number = nums.reduce((pre, cur) => pre + cur); + if (sum % 2 === 1) return false; + const bagSize: number = sum / 2; + const weightArr: number[] = nums; + const valueArr: number[] = nums; + const goodsNum: number = weightArr.length; + const dp: number[][] = new Array(goodsNum) + .fill(0) + .map(_ => new Array(bagSize + 1).fill(0)); + for (let i = weightArr[0]; i <= bagSize; i++) { + dp[0][i] = valueArr[0]; + } + for (let i = 1; i < goodsNum; i++) { + for (let j = 0; j <= bagSize; j++) { + if (j < weightArr[i]) { + dp[i][j] = dp[i - 1][j]; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weightArr[i]] + valueArr[i]); + } + } + } + return dp[goodsNum - 1][bagSize] === bagSize; +}; +``` + + ----------------------- diff --git a/problems/0435.无重叠区间.md b/problems/0435.无重叠区间.md index dc89d80b..66aa1244 100644 --- a/problems/0435.无重叠区间.md +++ b/problems/0435.无重叠区间.md @@ -93,7 +93,7 @@ public: }; ``` * 时间复杂度:O(nlog n) ,有一个快排 -* 空间复杂度:O(1) +* 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间 大家此时会发现如此复杂的一个问题,代码实现却这么简单! diff --git a/problems/0452.用最少数量的箭引爆气球.md b/problems/0452.用最少数量的箭引爆气球.md index 2ab14b61..d4bbe961 100644 --- a/problems/0452.用最少数量的箭引爆气球.md +++ b/problems/0452.用最少数量的箭引爆气球.md @@ -105,8 +105,8 @@ public: }; ``` -* 时间复杂度:$O(n\log n)$,因为有一个快排 -* 空间复杂度:$O(1)$ +* 时间复杂度:O(nlog n),因为有一个快排 +* 空间复杂度:O(1),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间 可以看出代码并不复杂。 diff --git a/problems/0454.四数相加II.md b/problems/0454.四数相加II.md index d4aba8fa..bfdee26e 100644 --- a/problems/0454.四数相加II.md +++ b/problems/0454.四数相加II.md @@ -318,6 +318,7 @@ impl Solution { } ``` + Scala: ```scala object Solution { @@ -353,6 +354,33 @@ object Solution { res } } + +C#: +```csharp +public int FourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { + Dictionary dic = new Dictionary(); + foreach(var i in nums1){ + foreach(var j in nums2){ + int sum = i + j; + if(dic.ContainsKey(sum)){ + dic[sum]++; + }else{ + dic.Add(sum, 1); + } + + } + } + int res = 0; + foreach(var a in nums3){ + foreach(var b in nums4){ + int sum = a+b; + if(dic.TryGetValue(-sum, out var result)){ + res += result; + } + } + } + return res; + } ``` -----------------------
diff --git a/problems/0455.分发饼干.md b/problems/0455.分发饼干.md index d95a407a..17db4a85 100644 --- a/problems/0455.分发饼干.md +++ b/problems/0455.分发饼干.md @@ -106,7 +106,7 @@ public: ## 其他语言版本 -### Java +### Java ```java class Solution { // 思路1:优先考虑饼干,小饼干先喂饱小胃口 @@ -145,7 +145,7 @@ class Solution { } ``` -### Python +### Python ```python class Solution: # 思路1:优先考虑胃饼干 @@ -166,13 +166,13 @@ class Solution: s.sort() start, count = len(s) - 1, 0 for index in range(len(g) - 1, -1, -1): # 先喂饱大胃口 - if start >= 0 and g[index] <= s[start]: + if start >= 0 and g[index] <= s[start]: start -= 1 count += 1 return count ``` -### Go +### Go ```golang //排序后,局部最优 func findContentChildren(g []int, s []int) int { @@ -191,7 +191,27 @@ func findContentChildren(g []int, s []int) int { } ``` -### Javascript +### Rust +```rust +pub fn find_content_children(children: Vec, cookie: Vec) -> i32 { + let mut children = children; + let mut cookies = cookie; + children.sort(); + cookies.sort(); + + let (mut child, mut cookie) = (0usize, 0usize); + while child < children.len() && cookie < cookies.len() { + // 优先选择最小饼干喂饱孩子 + if children[child] <= cookies[cookie] { + child += 1; + } + cookie += 1 + } + child as i32 +} +``` + +### Javascript ```js var findContentChildren = function(g, s) { g = g.sort((a, b) => a - b) @@ -203,7 +223,7 @@ var findContentChildren = function(g, s) { result++ index-- } - } + } return result }; @@ -251,7 +271,7 @@ function findContentChildren(g: number[], s: number[]): number { }; ``` -### C +### C ```c int cmp(int* a, int* b) { @@ -261,7 +281,7 @@ int cmp(int* a, int* b) { int findContentChildren(int* g, int gSize, int* s, int sSize){ if(sSize == 0) return 0; - + //将两个数组排序为升序 qsort(g, gSize, sizeof(int), cmp); qsort(s, sSize, sizeof(int), cmp); diff --git a/problems/0474.一和零.md b/problems/0474.一和零.md index 964df4a8..d38ce03f 100644 --- a/problems/0474.一和零.md +++ b/problems/0474.一和零.md @@ -323,6 +323,129 @@ const findMaxForm = (strs, m, n) => { }; ``` +TypeScript: + +> 滚动数组,二维数组法 + +```typescript +type BinaryInfo = { numOfZero: number, numOfOne: number }; +function findMaxForm(strs: string[], m: number, n: number): number { + const goodsNum: number = strs.length; + const dp: number[][] = new Array(m + 1).fill(0) + .map(_ => new Array(n + 1).fill(0)); + for (let i = 0; i < goodsNum; i++) { + const { numOfZero, numOfOne } = countBinary(strs[i]); + for (let j = m; j >= numOfZero; j--) { + for (let k = n; k >= numOfOne; k--) { + dp[j][k] = Math.max(dp[j][k], dp[j - numOfZero][k - numOfOne] + 1); + } + } + } + return dp[m][n]; +}; +function countBinary(str: string): BinaryInfo { + let numOfZero: number = 0, + numOfOne: number = 0; + for (let s of str) { + if (s === '0') { + numOfZero++; + } else { + numOfOne++; + } + } + return { numOfZero, numOfOne }; +} +``` + +> 传统背包,三维数组法 + +```typescript +type BinaryInfo = { numOfZero: number, numOfOne: number }; +function findMaxForm(strs: string[], m: number, n: number): number { + /** + dp[i][j][k]: 前i个物品中, 背包的0容量为j, 1容量为k, 最多能放的物品数量 + */ + const goodsNum: number = strs.length; + const dp: number[][][] = new Array(goodsNum).fill(0) + .map(_ => new Array(m + 1) + .fill(0) + .map(_ => new Array(n + 1).fill(0)) + ); + const { numOfZero, numOfOne } = countBinary(strs[0]); + for (let i = numOfZero; i <= m; i++) { + for (let j = numOfOne; j <= n; j++) { + dp[0][i][j] = 1; + } + } + for (let i = 1; i < goodsNum; i++) { + const { numOfZero, numOfOne } = countBinary(strs[i]); + for (let j = 0; j <= m; j++) { + for (let k = 0; k <= n; k++) { + if (j < numOfZero || k < numOfOne) { + dp[i][j][k] = dp[i - 1][j][k]; + } else { + dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - numOfZero][k - numOfOne] + 1); + } + } + } + } + return dp[dp.length - 1][m][n]; +}; +function countBinary(str: string): BinaryInfo { + let numOfZero: number = 0, + numOfOne: number = 0; + for (let s of str) { + if (s === '0') { + numOfZero++; + } else { + numOfOne++; + } + } + return { numOfZero, numOfOne }; +} +``` + +> 回溯法(会超时) + +```typescript +function findMaxForm(strs: string[], m: number, n: number): number { + /** + 思路:暴力枚举strs的所有子集,记录符合条件子集的最大长度 + */ + let resMax: number = 0; + backTrack(strs, m, n, 0, []); + return resMax; + function backTrack( + strs: string[], m: number, n: number, + startIndex: number, route: string[] + ): void { + if (startIndex === strs.length) return; + for (let i = startIndex, length = strs.length; i < length; i++) { + route.push(strs[i]); + if (isValidSubSet(route, m, n)) { + resMax = Math.max(resMax, route.length); + backTrack(strs, m, n, i + 1, route); + } + route.pop(); + } + } +}; +function isValidSubSet(strs: string[], m: number, n: number): boolean { + let zeroNum: number = 0, + oneNum: number = 0; + strs.forEach(str => { + for (let s of str) { + if (s === '0') { + zeroNum++; + } else { + oneNum++; + } + } + }); + return zeroNum <= m && oneNum <= n; +} +``` + ----------------------- diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index 99b76834..8ce1f6f1 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -351,6 +351,25 @@ const findTargetSumWays = (nums, target) => { }; ``` +TypeScript: + +```typescript +function findTargetSumWays(nums: number[], target: number): number { + const sum: number = nums.reduce((pre, cur) => pre + cur); + if (Math.abs(target) > sum) return 0; + if ((target + sum) % 2 === 1) return 0; + const bagSize: number = (target + sum) / 2; + const dp: number[] = new Array(bagSize + 1).fill(0); + dp[0] = 1; + for (let i = 0; i < nums.length; i++) { + for (let j = bagSize; j >= nums[i]; j--) { + dp[j] += dp[j - nums[i]]; + } + } + return dp[bagSize]; +}; +``` + ----------------------- diff --git a/problems/0496.下一个更大元素I.md b/problems/0496.下一个更大元素I.md index f9dfa308..02339677 100644 --- a/problems/0496.下一个更大元素I.md +++ b/problems/0496.下一个更大元素I.md @@ -244,6 +244,39 @@ class Solution: ``` Go: + +> 未精简版本 +```go +func nextGreaterElement(nums1 []int, nums2 []int) []int { + res := make([]int, len(nums1)) + for i := range res { res[i] = -1 } + m := make(map[int]int, len(nums1)) + for k, v := range nums1 { m[v] = k } + + stack := []int{0} + for i := 1; i < len(nums2); i++ { + top := stack[len(stack)-1] + if nums2[i] < nums2[top] { + stack = append(stack, i) + } else if nums2[i] == nums2[top] { + stack = append(stack, i) + } else { + for len(stack) != 0 && nums2[i] > nums2[top] { + if v, ok := m[nums2[top]]; ok { + res[v] = nums2[i] + } + stack = stack[:len(stack)-1] + if len(stack) != 0 { + top = stack[len(stack)-1] + } + } + stack = append(stack, i) + } + } + return res +} +``` +> 精简版本 ```go func nextGreaterElement(nums1 []int, nums2 []int) []int { res := make([]int, len(nums1)) diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md index d339940c..1d17784d 100644 --- a/problems/0509.斐波那契数.md +++ b/problems/0509.斐波那契数.md @@ -245,7 +245,29 @@ var fib = function(n) { }; ``` +TypeScript + +```typescript +function fib(n: number): number { + /** + dp[i]: 第i个斐波那契数 + dp[0]: 0; + dp[1]:1; + ... + dp[i] = dp[i - 1] + dp[i - 2]; + */ + const dp: number[] = []; + dp[0] = 0; + dp[1] = 1; + for (let i = 2; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +}; +``` + ### C + 动态规划: ```c int fib(int n){ diff --git a/problems/0518.零钱兑换II.md b/problems/0518.零钱兑换II.md index e72c5f85..b6593438 100644 --- a/problems/0518.零钱兑换II.md +++ b/problems/0518.零钱兑换II.md @@ -242,6 +242,22 @@ func change(amount int, coins []int) int { } ``` +Rust: +```rust +pub fn change(amount: i32, coins: Vec) -> i32 { + let amount = amount as usize; + let coins = coins.iter().map(|&c|c as usize).collect::>(); + let mut dp = vec![0usize; amount + 1]; + dp[0] = 1; + for i in 0..coins.len() { + for j in coins[i]..=amount { + dp[j] += dp[j - coins[i]]; + } + } + dp[amount] as i32 +} +``` + Javascript: ```javascript const change = (amount, coins) => { @@ -258,6 +274,21 @@ const change = (amount, coins) => { } ``` +TypeScript: + +```typescript +function change(amount: number, coins: number[]): number { + const dp: number[] = new Array(amount + 1).fill(0); + dp[0] = 1; + for (let i = 0, length = coins.length; i < length; i++) { + for (let j = coins[i]; j <= amount; j++) { + dp[j] += dp[j - coins[i]]; + } + } + return dp[amount]; +}; +``` + ----------------------- diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md index e941d242..4f571d09 100644 --- a/problems/0674.最长连续递增序列.md +++ b/problems/0674.最长连续递增序列.md @@ -218,6 +218,7 @@ class Solution: return result ``` + > 贪心法: ```python class Solution: @@ -236,6 +237,63 @@ class Solution: ``` Go: +> 动态规划: +```go +func findLengthOfLCIS(nums []int) int { + if len(nums) == 0 {return 0} + res, count := 1, 1 + for i := 0; i < len(nums)-1; i++ { + if nums[i+1] > nums[i] { + count++ + }else { + count = 1 + } + if count > res { + res = count + } + } + return res +} +``` + +> 贪心算法: +```go +func findLengthOfLCIS(nums []int) int { + if len(nums) == 0 {return 0} + dp := make([]int, len(nums)) + for i := 0; i < len(dp); i++ { + dp[i] = 1 + } + res := 1 + for i := 0; i < len(nums)-1; i++ { + if nums[i+1] > nums[i] { + dp[i+1] = dp[i] + 1 + } + if dp[i+1] > res { + res = dp[i+1] + } + } + return res +} +``` + +Rust: +```rust +pub fn find_length_of_lcis(nums: Vec) -> i32 { + if nums.is_empty() { + return 0; + } + let mut result = 1; + let mut dp = vec![1; nums.len()]; + for i in 1..nums.len() { + if nums[i - 1] < nums[i] { + dp[i] = dp[i - 1] + 1; + result = result.max(dp[i]); + } + } + result +} +``` Javascript: diff --git a/problems/0701.二叉搜索树中的插入操作.md b/problems/0701.二叉搜索树中的插入操作.md index df6a3954..50e39ade 100644 --- a/problems/0701.二叉搜索树中的插入操作.md +++ b/problems/0701.二叉搜索树中的插入操作.md @@ -279,7 +279,7 @@ class Solution: root.right = self.insertIntoBST(root.right, val) # 返回更新后的以当前root为根节点的新树 - return roo + return root ``` **递归法** - 无返回值 diff --git a/problems/0704.二分查找.md b/problems/0704.二分查找.md index 55625130..1e474f9a 100644 --- a/problems/0704.二分查找.md +++ b/problems/0704.二分查找.md @@ -610,7 +610,48 @@ public class Solution{ } } ``` +**Scala:** +(版本一)左闭右闭区间 +```scala +object Solution { + def search(nums: Array[Int], target: Int): Int = { + var left = 0 + var right = nums.length - 1 + while (left <= right) { + var mid = left + ((right - left) / 2) + if (target == nums(mid)) { + return mid + } else if (target < nums(mid)) { + right = mid - 1 + } else { + left = mid + 1 + } + } + -1 + } +} +``` +(版本二)左闭右开区间 +```scala +object Solution { + def search(nums: Array[Int], target: Int): Int = { + var left = 0 + var right = nums.length + while (left < right) { + val mid = left + (right - left) / 2 + if (target == nums(mid)) { + return mid + } else if (target < nums(mid)) { + right = mid + } else { + left = mid + 1 + } + } + -1 + } +} +``` -----------------------
diff --git a/problems/0707.设计链表.md b/problems/0707.设计链表.md index 37ce15ad..dcdb53f4 100644 --- a/problems/0707.设计链表.md +++ b/problems/0707.设计链表.md @@ -1154,7 +1154,75 @@ class MyLinkedList { } ``` +Scala: +```scala +class ListNode(_x: Int = 0, _next: ListNode = null) { + var next: ListNode = _next + var x: Int = _x +} +class MyLinkedList() { + + var size = 0 // 链表尺寸 + var dummy: ListNode = new ListNode(0) // 虚拟头节点 + + // 获取第index个节点的值 + def get(index: Int): Int = { + if (index < 0 || index >= size) { + return -1; + } + var cur = dummy + for (i <- 0 to index) { + cur = cur.next + } + cur.x // 返回cur的值 + } + + // 在链表最前面插入一个节点 + def addAtHead(`val`: Int) { + addAtIndex(0, `val`) + } + + // 在链表最后面插入一个节点 + def addAtTail(`val`: Int) { + addAtIndex(size, `val`) + } + + // 在第index个节点之前插入一个新节点 + // 如果index等于链表长度,则说明新插入的节点是尾巴 + // 如果index等于0,则说明新插入的节点是头 + // 如果index>链表长度,则说明为空 + def addAtIndex(index: Int, `val`: Int) { + if (index > size) { + return + } + var loc = index // 因为参数index是val不可变类型,所以需要赋值给一个可变类型 + if (index < 0) { + loc = 0 + } + size += 1 //链表尺寸+1 + var pre = dummy + for (i <- 0 until loc) { + pre = pre.next + } + val node: ListNode = new ListNode(`val`, pre.next) + pre.next = node + } + // 删除第index个节点 + def deleteAtIndex(index: Int) { + if (index < 0 || index >= size) { + return + } + size -= 1 + var pre = dummy + for (i <- 0 until index) { + pre = pre.next + } + pre.next = pre.next.next + } + +} +``` ----------------------- diff --git a/problems/0714.买卖股票的最佳时机含手续费.md b/problems/0714.买卖股票的最佳时机含手续费.md index 2f27d6ea..b27631c6 100644 --- a/problems/0714.买卖股票的最佳时机含手续费.md +++ b/problems/0714.买卖股票的最佳时机含手续费.md @@ -293,6 +293,50 @@ var maxProfit = function(prices, fee) { }; ``` +TypeScript: + +> 贪心 + +```typescript +function maxProfit(prices: number[], fee: number): number { + if (prices.length === 0) return 0; + let minPrice: number = prices[0]; + let profit: number = 0; + for (let i = 1, length = prices.length; i < length; i++) { + if (minPrice > prices[i]) { + minPrice = prices[i]; + } + if (minPrice + fee < prices[i]) { + profit += prices[i] - minPrice - fee; + minPrice = prices[i] - fee; + } + } + return profit; +}; +``` + +> 动态规划 + +```typescript +function maxProfit(prices: number[], fee: number): number { + /** + dp[i][1]: 第i天不持有股票的最大所剩现金 + dp[i][0]: 第i天持有股票的最大所剩现金 + */ + const length: number = prices.length; + const dp: number[][] = new Array(length).fill(0).map(_ => []); + dp[0][1] = 0; + dp[0][0] = -prices[0]; + for (let i = 1, length = prices.length; i < length; i++) { + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee); + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]); + } + return Math.max(dp[length - 1][0], dp[length - 1][1]); +}; +``` + + + -----------------------
diff --git a/problems/0738.单调递增的数字.md b/problems/0738.单调递增的数字.md index c8ce8a2b..4e4079a7 100644 --- a/problems/0738.单调递增的数字.md +++ b/problems/0738.单调递增的数字.md @@ -225,6 +225,28 @@ var monotoneIncreasingDigits = function(n) { }; ``` +### TypeScript + +```typescript +function monotoneIncreasingDigits(n: number): number { + let strArr: number[] = String(n).split('').map(i => parseInt(i)); + const length = strArr.length; + let flag: number = length; + for (let i = length - 2; i >= 0; i--) { + if (strArr[i] > strArr[i + 1]) { + strArr[i] -= 1; + flag = i + 1; + } + } + for (let i = flag; i < length; i++) { + strArr[i] = 9; + } + return parseInt(strArr.join('')); +}; +``` + + + -----------------------
diff --git a/problems/0739.每日温度.md b/problems/0739.每日温度.md index 5f53e412..58edd489 100644 --- a/problems/0739.每日温度.md +++ b/problems/0739.每日温度.md @@ -34,7 +34,7 @@ 那么单调栈的原理是什么呢?为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢? -单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素的元素,优点是只需要遍历一次。 +单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素大的元素,优点是只需要遍历一次。 在使用单调栈的时候首先要明确如下几点: @@ -233,7 +233,7 @@ class Solution { } ``` Python: -``` Python3 +```python class Solution: def dailyTemperatures(self, temperatures: List[int]) -> List[int]: answer = [0]*len(temperatures) @@ -277,8 +277,36 @@ func dailyTemperatures(t []int) []int { } ``` -> 单调栈法 +> 单调栈法(未精简版本) +```go +func dailyTemperatures(temperatures []int) []int { + res := make([]int, len(temperatures)) + // 初始化栈顶元素为第一个下标索引0 + stack := []int{0} + + for i := 1; i < len(temperatures); i++ { + top := stack[len(stack)-1] + if temperatures[i] < temperatures[top] { + stack = append(stack, i) + } else if temperatures[i] == temperatures[top] { + stack = append(stack, i) + } else { + for len(stack) != 0 && temperatures[i] > temperatures[top] { + res[top] = i - top + stack = stack[:len(stack)-1] + if len(stack) != 0 { + top = stack[len(stack)-1] + } + } + stack = append(stack, i) + } + } + return res +} +``` + +> 单调栈法(精简版本) ```go // 单调递减栈 func dailyTemperatures(num []int) []int { diff --git a/problems/0746.使用最小花费爬楼梯.md b/problems/0746.使用最小花费爬楼梯.md index c356955a..5931fc8a 100644 --- a/problems/0746.使用最小花费爬楼梯.md +++ b/problems/0746.使用最小花费爬楼梯.md @@ -266,7 +266,30 @@ var minCostClimbingStairs = function(cost) { }; ``` +### TypeScript + +```typescript +function minCostClimbingStairs(cost: number[]): number { + /** + dp[i]: 走到第i阶需要花费的最少金钱 + dp[0]: cost[0]; + dp[1]: cost[1]; + ... + dp[i]: min(dp[i - 1], dp[i - 2]) + cost[i]; + */ + const dp: number[] = []; + const length: number = cost.length; + dp[0] = cost[0]; + dp[1] = cost[1]; + for (let i = 2; i <= length; i++) { + dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; + } + return Math.min(dp[length - 1], dp[length - 2]); +}; +``` + ### C + ```c int minCostClimbingStairs(int* cost, int costSize){ //开辟dp数组,大小为costSize diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md index 35c3ccdc..9a510a1b 100644 --- a/problems/0968.监控二叉树.md +++ b/problems/0968.监控二叉树.md @@ -476,7 +476,35 @@ var minCameraCover = function(root) { }; ``` +### TypeScript + +```typescript +function minCameraCover(root: TreeNode | null): number { + /** 0-无覆盖, 1-有摄像头, 2-有覆盖 */ + type statusCode = 0 | 1 | 2; + let resCount: number = 0; + if (recur(root) === 0) resCount++; + return resCount; + function recur(node: TreeNode | null): statusCode { + if (node === null) return 2; + const left: statusCode = recur(node.left), + right: statusCode = recur(node.right); + let resStatus: statusCode = 0; + if (left === 0 || right === 0) { + resStatus = 1; + resCount++; + } else if (left === 1 || right === 1) { + resStatus = 2; + } else { + resStatus = 0; + } + return resStatus; + } +}; +``` + ### C + ```c /* **函数后序遍历二叉树。判断一个结点状态时,根据其左右孩子结点的状态进行判断 diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md index 24276bcf..0e79a3d6 100644 --- a/problems/0977.有序数组的平方.md +++ b/problems/0977.有序数组的平方.md @@ -358,7 +358,41 @@ class Solution { } } ``` +Scala: +双指针: +```scala +object Solution { + def sortedSquares(nums: Array[Int]): Array[Int] = { + val res: Array[Int] = new Array[Int](nums.length) + var top = nums.length - 1 + var i = 0 + var j = nums.length - 1 + while (i <= j) { + if (nums(i) * nums(i) <= nums(j) * nums(j)) { + // 当左侧平方小于等于右侧,res数组顶部放右侧的平方,并且top下移,j左移 + res(top) = nums(j) * nums(j) + top -= 1 + j -= 1 + } else { + // 当左侧平方大于右侧,res数组顶部放左侧的平方,并且top下移,i右移 + res(top) = nums(i) * nums(i) + top -= 1 + i += 1 + } + } + res + } +} +``` +骚操作(暴力思路): +```scala +object Solution { + def sortedSquares(nums: Array[Int]): Array[Int] = { + nums.map(x=>{x*x}).sortWith(_ < _) + } +} +``` ----------------------- diff --git a/problems/1002.查找常用字符.md b/problems/1002.查找常用字符.md index 36937b0b..075b5ef1 100644 --- a/problems/1002.查找常用字符.md +++ b/problems/1002.查找常用字符.md @@ -418,6 +418,38 @@ char ** commonChars(char ** words, int wordsSize, int* returnSize){ return ret; } ``` - +Scala: +```scala +object Solution { + def commonChars(words: Array[String]): List[String] = { + // 声明返回结果的不可变List集合,因为res要重新赋值,所以声明为var + var res = List[String]() + var hash = new Array[Int](26) // 统计字符出现的最小频率 + // 统计第一个字符串中字符出现的次数 + for (i <- 0 until words(0).length) { + hash(words(0)(i) - 'a') += 1 + } + // 统计其他字符串出现的频率 + for (i <- 1 until words.length) { + // 统计其他字符出现的频率 + var hashOtherStr = new Array[Int](26) + for (j <- 0 until words(i).length) { + hashOtherStr(words(i)(j) - 'a') += 1 + } + // 更新hash,取26个字母最小出现的频率 + for (k <- 0 until 26) { + hash(k) = math.min(hash(k), hashOtherStr(k)) + } + } + // 根据hash的结果转换输出的形式 + for (i <- 0 until 26) { + for (j <- 0 until hash(i)) { + res = res :+ (i + 'a').toChar.toString + } + } + res + } +} +``` -----------------------
diff --git a/problems/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md index 79767deb..202534da 100644 --- a/problems/1005.K次取反后最大化的数组和.md +++ b/problems/1005.K次取反后最大化的数组和.md @@ -209,6 +209,22 @@ var largestSumAfterKNegations = function(nums, k) { return a + b }) }; + +// 版本二 (优化: 一次遍历) +var largestSumAfterKNegations = function(nums, k) { + nums.sort((a, b) => Math.abs(b) - Math.abs(a)); // 排序 + let sum = 0; + for(let i = 0; i < nums.length; i++) { + if(nums[i] < 0 && k-- > 0) { // 负数取反(k 数量足够时) + nums[i] = -nums[i]; + } + sum += nums[i]; // 求和 + } + if(k % 2 > 0) { // k 有多余的(k若消耗完则应为 -1) + sum -= 2 * nums[nums.length - 1]; // 减去两倍的最小值(因为之前加过一次) + } + return sum; +}; ``` diff --git a/problems/1035.不相交的线.md b/problems/1035.不相交的线.md index 0602e111..279ed816 100644 --- a/problems/1035.不相交的线.md +++ b/problems/1035.不相交的线.md @@ -111,7 +111,6 @@ class Solution: Golang: ```go - func maxUncrossedLines(A []int, B []int) int { m, n := len(A), len(B) dp := make([][]int, m+1) @@ -140,7 +139,26 @@ func max(a, b int) int { } ``` +Rust: +```rust +pub fn max_uncrossed_lines(nums1: Vec, nums2: Vec) -> i32 { + let (n, m) = (nums1.len(), nums2.len()); + let mut last = vec![0; m + 1]; // 记录滚动数组 + let mut dp = vec![0; m + 1]; + for i in 1..=n { + dp.swap_with_slice(&mut last); + for j in 1..=m { + if nums1[i - 1] == nums2[j - 1] { + dp[j] = last[j - 1] + 1; + } else { + dp[j] = last[j].max(dp[j - 1]); + } + } + } + dp[m] +} +``` JavaScript: diff --git a/problems/1049.最后一块石头的重量II.md b/problems/1049.最后一块石头的重量II.md index ee0ddef2..3d256c3d 100644 --- a/problems/1049.最后一块石头的重量II.md +++ b/problems/1049.最后一块石头的重量II.md @@ -277,5 +277,26 @@ var lastStoneWeightII = function (stones) { }; ``` +TypeScript: + +```typescript +function lastStoneWeightII(stones: number[]): number { + const sum: number = stones.reduce((pre, cur) => pre + cur); + const bagSize: number = Math.floor(sum / 2); + const weightArr: number[] = stones; + const valueArr: number[] = stones; + const goodsNum: number = weightArr.length; + const dp: number[] = new Array(bagSize + 1).fill(0); + for (let i = 0; i < goodsNum; i++) { + for (let j = bagSize; j >= weightArr[i]; j--) { + dp[j] = Math.max(dp[j], dp[j - weightArr[i]] + valueArr[i]); + } + } + return sum - dp[bagSize] * 2; +}; +``` + + + -----------------------
diff --git a/problems/1143.最长公共子序列.md b/problems/1143.最长公共子序列.md index fdcc7619..ecedf89b 100644 --- a/problems/1143.最长公共子序列.md +++ b/problems/1143.最长公共子序列.md @@ -4,40 +4,40 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 1143.最长公共子序列 +## 1143.最长公共子序列 [力扣题目链接](https://leetcode-cn.com/problems/longest-common-subsequence/) -给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。 +给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。 -一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 +一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 -例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 +例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 -若这两个字符串没有公共子序列,则返回 0。 +若这两个字符串没有公共子序列,则返回 0。 -示例 1: +示例 1: -输入:text1 = "abcde", text2 = "ace" -输出:3 -解释:最长公共子序列是 "ace",它的长度为 3。 +输入:text1 = "abcde", text2 = "ace" +输出:3 +解释:最长公共子序列是 "ace",它的长度为 3。 -示例 2: -输入:text1 = "abc", text2 = "abc" -输出:3 -解释:最长公共子序列是 "abc",它的长度为 3。 +示例 2: +输入:text1 = "abc", text2 = "abc" +输出:3 +解释:最长公共子序列是 "abc",它的长度为 3。 -示例 3: -输入:text1 = "abc", text2 = "def" -输出:0 -解释:两个字符串没有公共子序列,返回 0。 +示例 3: +输入:text1 = "abc", text2 = "def" +输出:0 +解释:两个字符串没有公共子序列,返回 0。 -提示: +提示: * 1 <= text1.length <= 1000 * 1 <= text2.length <= 1000 输入的字符串只含有小写英文字符。 -## 思路 +## 思路 本题和[动态规划:718. 最长重复子数组](https://programmercarl.com/0718.最长重复子数组.html)区别在于这里不要求是连续的了,但要有相对顺序,即:"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。 @@ -45,21 +45,21 @@ 1. 确定dp数组(dp table)以及下标的含义 -dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j] +dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j] -有同学会问:为什么要定义长度为[0, i - 1]的字符串text1,定义为长度为[0, i]的字符串text1不香么? +有同学会问:为什么要定义长度为[0, i - 1]的字符串text1,定义为长度为[0, i]的字符串text1不香么? 这样定义是为了后面代码实现方便,如果非要定义为为长度为[0, i]的字符串text1也可以,大家可以试一试! 2. 确定递推公式 -主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1]不相同 +主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1]不相同 -如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1; +如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1; 如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。 -即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); +即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 代码如下: @@ -71,9 +71,9 @@ if (text1[i - 1] == text2[j - 1]) { } ``` -3. dp数组如何初始化 +3. dp数组如何初始化 -先看看dp[i][0]应该是多少呢? +先看看dp[i][0]应该是多少呢? test1[0, i-1]和空串的最长公共子序列自然是0,所以dp[i][0] = 0; @@ -101,7 +101,7 @@ vector> dp(text1.size() + 1, vector(text2.size() + 1, 0)); ![1143.最长公共子序列1](https://img-blog.csdnimg.cn/20210210150215918.jpg) -最后红框dp[text1.size()][text2.size()]为最终结果 +最后红框dp[text1.size()][text2.size()]为最终结果 以上分析完毕,C++代码如下: @@ -158,7 +158,7 @@ class Solution: for i in range(1, len2): for j in range(1, len1): # 开始列出状态转移方程 if text1[j-1] == text2[i-1]: - dp[i][j] = dp[i-1][j-1]+1 + dp[i][j] = dp[i-1][j-1]+1 else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) return dp[-1][-1] @@ -189,10 +189,32 @@ func longestCommonSubsequence(text1 string, text2 string) int { func max(a,b int)int { if a>b{ - return a + return a } return b } + +``` + +Rust: +```rust +pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { + let (n, m) = (text1.len(), text2.len()); + let (s1, s2) = (text1.as_bytes(), text2.as_bytes()); + let mut dp = vec![0; m + 1]; + let mut last = vec![0; m + 1]; + for i in 1..=n { + dp.swap_with_slice(&mut last); + for j in 1..=m { + dp[j] = if s1[i - 1] == s2[j - 1] { + last[j - 1] + 1 + } else { + last[j].max(dp[j - 1]) + }; + } + } + dp[m] +} ``` Javascript: diff --git a/problems/二叉树的迭代遍历.md b/problems/二叉树的迭代遍历.md index 8164724b..13ba5f1e 100644 --- a/problems/二叉树的迭代遍历.md +++ b/problems/二叉树的迭代遍历.md @@ -11,9 +11,9 @@ 看完本篇大家可以使用迭代法,再重新解决如下三道leetcode上的题目: -* 144.二叉树的前序遍历 -* 94.二叉树的中序遍历 -* 145.二叉树的后序遍历 +* [144.二叉树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/) +* [94.二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/) +* [145.二叉树的后序遍历](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/) 为什么可以用迭代法(非递归的方式)来实现二叉树的前后中序遍历呢? diff --git a/problems/二叉树的递归遍历.md b/problems/二叉树的递归遍历.md index 612f2394..186c39d3 100644 --- a/problems/二叉树的递归遍历.md +++ b/problems/二叉树的递归遍历.md @@ -99,9 +99,9 @@ void traversal(TreeNode* cur, vector& vec) { 此时大家可以做一做leetcode上三道题目,分别是: -* 144.二叉树的前序遍历 -* 145.二叉树的后序遍历 -* 94.二叉树的中序遍历 +* [144.二叉树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/) +* [145.二叉树的后序遍历](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/) +* [94.二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/) 可能有同学感觉前后中序遍历的递归太简单了,要打迭代法(非递归),别急,我们明天打迭代法,打个通透! diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md index fe940b4c..a40a92ab 100644 --- a/problems/背包理论基础01背包-1.md +++ b/problems/背包理论基础01背包-1.md @@ -380,48 +380,125 @@ func main() { ### javascript ```js -function testweightbagproblem (wight, value, size) { - const len = wight.length, - dp = array.from({length: len + 1}).map( - () => array(size + 1).fill(0) - ); - - for(let i = 1; i <= len; i++) { - for(let j = 0; j <= size; j++) { - if(wight[i - 1] <= j) { - dp[i][j] = math.max( - dp[i - 1][j], - value[i - 1] + dp[i - 1][j - wight[i - 1]] - ) - } else { - dp[i][j] = dp[i - 1][j]; - } +function testWeightBagProblem (weight, value, size) { + // 定义 dp 数组 + const len = weight.length, + dp = Array(len).fill().map(() => Array(size + 1).fill(0)); + + // 初始化 + for(let j = weight[0]; j <= size; j++) { + dp[0][j] = value[0]; } - } -// console.table(dp); - - return dp[len][size]; -} - -function testWeightBagProblem2 (wight, value, size) { - const len = wight.length, - dp = Array(size + 1).fill(0); - for(let i = 1; i <= len; i++) { - for(let j = size; j >= wight[i - 1]; j--) { - dp[j] = Math.max(dp[j], value[i - 1] + dp[j - wight[i - 1]]); + // weight 数组的长度len 就是物品个数 + for(let i = 1; i < len; i++) { // 遍历物品 + for(let j = 0; j <= size; j++) { // 遍历背包容量 + if(j < weight[i]) dp[i][j] = dp[i - 1][j]; + else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); + } } - } - return dp[size]; -} + console.table(dp) + + return dp[len - 1][size]; +} function test () { - console.log(testWeightBagProblem([1, 3, 4, 5], [15, 20, 30, 55], 6)); + console.log(testWeightBagProblem([1, 3, 4, 5], [15, 20, 30, 55], 6)); } test(); ``` + +### C +```c +#include +#include +#include + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define ARR_SIZE(a) (sizeof((a)) / sizeof((a)[0])) +#define BAG_WEIGHT 4 + +void backPack(int* weights, int weightSize, int* costs, int costSize, int bagWeight) { + // 开辟dp数组 + int dp[weightSize][bagWeight + 1]; + memset(dp, 0, sizeof(int) * weightSize * (bagWeight + 1)); + + int i, j; + // 当背包容量大于物品0的重量时,将物品0放入到背包中 + for(j = weights[0]; j <= bagWeight; ++j) { + dp[0][j] = costs[0]; + } + + // 先遍历物品,再遍历重量 + for(j = 1; j <= bagWeight; ++j) { + for(i = 1; i < weightSize; ++i) { + // 如果当前背包容量小于物品重量 + if(j < weights[i]) + // 背包物品的价值等于背包不放置当前物品时的价值 + dp[i][j] = dp[i-1][j]; + // 若背包当前重量可以放置物品 + else + // 背包的价值等于放置该物品或不放置该物品的最大值 + dp[i][j] = MAX(dp[i - 1][j], dp[i - 1][j - weights[i]] + costs[i]); + } + } + + printf("%d\n", dp[weightSize - 1][bagWeight]); +} + +int main(int argc, char* argv[]) { + int weights[] = {1, 3, 4}; + int costs[] = {15, 20, 30}; + backPack(weights, ARR_SIZE(weights), costs, ARR_SIZE(costs), BAG_WEIGHT); + return 0; +} +``` + + +### TypeScript + +```typescript +function testWeightBagProblem( + weight: number[], + value: number[], + size: number +): number { + /** + * dp[i][j]: 前i个物品,背包容量为j,能获得的最大价值 + * dp[0][*]: u=weight[0],u之前为0,u之后(含u)为value[0] + * dp[*][0]: 0 + * ... + * dp[i][j]: max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]); + */ + const goodsNum: number = weight.length; + const dp: number[][] = new Array(goodsNum) + .fill(0) + .map((_) => new Array(size + 1).fill(0)); + for (let i = weight[0]; i <= size; i++) { + dp[0][i] = value[0]; + } + for (let i = 1; i < goodsNum; i++) { + for (let j = 1; j <= size; j++) { + if (j < weight[i]) { + dp[i][j] = dp[i - 1][j]; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); + } + } + } + return dp[goodsNum - 1][size]; +} +// test +const weight = [1, 3, 4]; +const value = [15, 20, 30]; +const size = 4; +console.log(testWeightBagProblem(weight, value, size)); +``` + + + -----------------------
diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md index dabdfb2d..b66b74a6 100644 --- a/problems/背包理论基础01背包-2.md +++ b/problems/背包理论基础01背包-2.md @@ -137,6 +137,8 @@ dp[1] = dp[1 - weight[0]] + value[0] = 15 因为一维dp的写法,背包容量一定是要倒序遍历(原因上面已经讲了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。 +倒序遍历的原因是,本质上还是一个对二维数组的遍历,并且右下角的值依赖上一层左上角的值,因此需要保证左边的值仍然是上一层的,从右向左覆盖。 + (这里如果读不懂,就在回想一下dp[j]的定义,或者就把两个for循环顺序颠倒一下试试!) **所以一维dp数组的背包在遍历顺序上和二维其实是有很大差异的!**,这一点大家一定要注意。 @@ -315,6 +317,64 @@ function test () { test(); ``` +### C +```c +#include +#include + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define ARR_SIZE(arr) ((sizeof((arr))) / sizeof((arr)[0])) +#define BAG_WEIGHT 4 + +void test_back_pack(int* weights, int weightSize, int* values, int valueSize, int bagWeight) { + int dp[bagWeight + 1]; + memset(dp, 0, sizeof(int) * (bagWeight + 1)); + + int i, j; + // 先遍历物品 + for(i = 0; i < weightSize; ++i) { + // 后遍历重量。从后向前遍历 + for(j = bagWeight; j >= weights[i]; --j) { + dp[j] = MAX(dp[j], dp[j - weights[i]] + values[i]); + } + } + + // 打印最优结果 + printf("%d\n", dp[bagWeight]); +} + +int main(int argc, char** argv) { + int weights[] = {1, 3, 4}; + int values[] = {15, 20, 30}; + test_back_pack(weights, ARR_SIZE(weights), values, ARR_SIZE(values), BAG_WEIGHT); + return 0; +} +``` + +### TypeScript + +```typescript +function testWeightBagProblem( + weight: number[], + value: number[], + size: number +): number { + const goodsNum: number = weight.length; + const dp: number[] = new Array(size + 1).fill(0); + for (let i = 0; i < goodsNum; i++) { + for (let j = size; j >= weight[i]; j--) { + dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]); + } + } + return dp[size]; +} +const weight = [1, 3, 4]; +const value = [15, 20, 30]; +const size = 4; +console.log(testWeightBagProblem(weight, value, size)); + +``` + ----------------------- diff --git a/problems/背包问题理论基础多重背包.md b/problems/背包问题理论基础多重背包.md index a988db2c..712380f4 100644 --- a/problems/背包问题理论基础多重背包.md +++ b/problems/背包问题理论基础多重背包.md @@ -334,6 +334,64 @@ func Test_multiplePack(t *testing.T) { PASS ``` +TypeScript: + +> 版本一(改变数据源): + +```typescript +function testMultiPack() { + const bagSize: number = 10; + const weightArr: number[] = [1, 3, 4], + valueArr: number[] = [15, 20, 30], + amountArr: number[] = [2, 3, 2]; + for (let i = 0, length = amountArr.length; i < length; i++) { + while (amountArr[i] > 1) { + weightArr.push(weightArr[i]); + valueArr.push(valueArr[i]); + amountArr[i]--; + } + } + const goodsNum: number = weightArr.length; + const dp: number[] = new Array(bagSize + 1).fill(0); + // 遍历物品 + for (let i = 0; i < goodsNum; i++) { + // 遍历背包容量 + for (let j = bagSize; j >= weightArr[i]; j--) { + dp[j] = Math.max(dp[j], dp[j - weightArr[i]] + valueArr[i]); + } + } + console.log(dp); +} +testMultiPack(); +``` + +> 版本二(改变遍历方式): + +```typescript +function testMultiPack() { + const bagSize: number = 10; + const weightArr: number[] = [1, 3, 4], + valueArr: number[] = [15, 20, 30], + amountArr: number[] = [2, 3, 2]; + const goodsNum: number = weightArr.length; + const dp: number[] = new Array(bagSize + 1).fill(0); + // 遍历物品 + for (let i = 0; i < goodsNum; i++) { + // 遍历物品个数 + for (let j = 0; j < amountArr[i]; j++) { + // 遍历背包容量 + for (let k = bagSize; k >= weightArr[i]; k--) { + dp[k] = Math.max(dp[k], dp[k - weightArr[i]] + valueArr[i]); + } + } + } + console.log(dp); +} +testMultiPack(); +``` + + + -----------------------
diff --git a/problems/背包问题理论基础完全背包.md b/problems/背包问题理论基础完全背包.md index 3ec399f1..54e772e0 100644 --- a/problems/背包问题理论基础完全背包.md +++ b/problems/背包问题理论基础完全背包.md @@ -340,6 +340,27 @@ function test_completePack2() { } ``` +TypeScript: + +```typescript +// 先遍历物品,再遍历背包容量 +function test_CompletePack(): void { + const weight: number[] = [1, 3, 4]; + const value: number[] = [15, 20, 30]; + const bagSize: number = 4; + const dp: number[] = new Array(bagSize + 1).fill(0); + for (let i = 0; i < weight.length; i++) { + for (let j = weight[i]; j <= bagSize; j++) { + dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]); + } + } + console.log(dp); +} +test_CompletePack(); +``` + + + -----------------------
diff --git a/problems/链表理论基础.md b/problems/链表理论基础.md index 2fe9f14c..1a29c32a 100644 --- a/problems/链表理论基础.md +++ b/problems/链表理论基础.md @@ -210,6 +210,13 @@ type ListNode struct { } ``` +Scala: +```scala +class ListNode(_x: Int = 0, _next: ListNode = null) { + var next: ListNode = _next + var x: Int = _x +} +``` ----------------------- diff --git a/problems/面试题 02.07. 解法更新.md b/problems/面试题 02.07. 解法更新.md deleted file mode 100644 index 6115d02e..00000000 --- a/problems/面试题 02.07. 解法更新.md +++ /dev/null @@ -1,41 +0,0 @@ -# 双指针,不计算链表长度 -设置指向headA和headB的指针pa、pb,分别遍历两个链表,每次循环同时更新pa和pb。 -* 当链表A遍历完之后,即pa为空时,将pa指向headB; -* 当链表B遍历完之后,即pa为空时,将pb指向headA; -* 当pa与pb相等时,即指向同一个节点,该节点即为相交起始节点。 -* 若链表不相交,则pa、pb同时为空时退出循环,即如果链表不相交,pa与pb在遍历过全部节点后同时指向结尾空节点,此时退出循环,返回空。 -# 证明思路 -设链表A不相交部分长度为a,链表B不相交部分长度为b,两个链表相交部分长度为c。
-在pa指向链表A时,即pa为空之前,pa经过链表A不相交部分和相交部分,走过的长度为a+c;
-pa指向链表B后,在移动相交节点之前经过链表B不相交部分,走过的长度为b,总合为a+c+b。
-同理,pb走过长度的总合为b+c+a。二者相等,即pa与pb可同时到达相交起始节点。
-该方法可避免计算具体链表长度。 -```cpp -class Solution { -public: - ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { - //链表为空时,返回空指针 - if(headA == nullptr || headB == nullptr) return nullptr; - ListNode* pa = headA; - ListNode* pb = headB; - //pa与pb在遍历过全部节点后,同时指向结尾空节点时退出循环 - while(pa != nullptr || pb != nullptr){ - //pa为空时,将pa指向headB - if(pa == nullptr){ - pa = headB; - } - //pa为空时,将pb指向headA - if(pb == nullptr){ - pb = headA; - } - //pa与pb相等时,返回相交起始节点 - if(pa == pb){ - return pa; - } - pa = pa->next; - pb = pb->next; - } - return nullptr; - } -}; -``` diff --git a/problems/面试题02.07.链表相交.md b/problems/面试题02.07.链表相交.md index 2e7226de..0a38cc33 100644 --- a/problems/面试题02.07.链表相交.md +++ b/problems/面试题02.07.链表相交.md @@ -317,7 +317,55 @@ ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { } ``` - +Scala: +```scala +object Solution { + def getIntersectionNode(headA: ListNode, headB: ListNode): ListNode = { + var lenA = 0 // headA链表的长度 + var lenB = 0 // headB链表的长度 + var tmp = headA // 临时变量 + // 统计headA的长度 + while (tmp != null) { + lenA += 1; + tmp = tmp.next + } + // 统计headB的长度 + tmp = headB // 临时变量赋值给headB + while (tmp != null) { + lenB += 1 + tmp = tmp.next + } + // 因为传递过来的参数是不可变量,所以需要重新定义 + var listA = headA + var listB = headB + // 两个链表的长度差 + // 如果gap>0,lenA>lenB,headA(listA)链表往前移动gap步 + // 如果gap<0,lenA 0) { + // 因为不可以i-=1,所以可以使用for + for (i <- 0 until gap) { + listA = listA.next // 链表headA(listA) 移动 + } + } else { + gap = math.abs(gap) // 此刻gap为负值,取绝对值 + for (i <- 0 until gap) { + listB = listB.next + } + } + // 现在两个链表同时往前走,如果相等则返回 + while (listA != null && listB != null) { + if (listA == listB) { + return listA + } + listA = listA.next + listB = listB.next + } + // 如果链表没有相交则返回null,return可以省略 + null + } +} +``` -----------------------