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/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/0035.搜索插入位置.md b/problems/0035.搜索插入位置.md index 9a770703..8a8f9706 100644 --- a/problems/0035.搜索插入位置.md +++ b/problems/0035.搜索插入位置.md @@ -318,6 +318,31 @@ func searchInsert(_ nums: [Int], _ target: Int) -> Int { ``` +### 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/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/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/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/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/0322.零钱兑换.md b/problems/0322.零钱兑换.md index 3a8d0662..43c735be 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) => { diff --git a/problems/0383.赎金信.md b/problems/0383.赎金信.md index 5d9e8295..6177cc41 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; } } 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/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..0e4a3987 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) => { 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/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/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/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..d6bc5520 100644 --- a/problems/背包理论基础01背包-1.md +++ b/problems/背包理论基础01背包-1.md @@ -380,28 +380,37 @@ 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]; - } - } - } +/** + * + * @param {Number []} weight + * @param {Number []} value + * @param {Number} size + * @returns + */ -// console.table(dp); +function testWeightBagProblem(weight, value, size) { +const len = weight.length, +dp = Array.from({length: len}).map( +() => Array(size + 1)) //JavaScript 数组是引用类型 +for(let i = 0; i < len; i++) { //初始化最左一列,即背包容量为0时的情况 +dp[i][0] = 0; +} +for(let j = 1; j < size+1; j++) { //初始化第0行, 只有一件物品的情况 +if(weight[0] <= j) { +dp[0][j] = value[0]; +} else { +dp[0][j] = 0; +} +} + +for(let i = 1; i < len; i++) { //dp[i][j]由其左上方元素推导得出 +for(let j = 1; j < size+1; 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[len][size]; +return dp[len-1][size] //满足条件的最大值 } function testWeightBagProblem2 (wight, value, size) {