mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-09 03:34:02 +08:00
Merge branch 'master' into patch08
This commit is contained in:
@ -254,7 +254,7 @@
|
||||
33. [二叉树:构造一棵搜索树](./problems/0108.将有序数组转换为二叉搜索树.md)
|
||||
34. [二叉树:搜索树转成累加树](./problems/0538.把二叉搜索树转换为累加树.md)
|
||||
35. [二叉树:总结篇!(需要掌握的二叉树技能都在这里了)](./problems/二叉树总结篇.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<int ,int> dic= new Dictionary<int,int>();
|
||||
for(int i=0;i<nums.Length;i++){
|
||||
int imp= target-nums[i];
|
||||
if(dic.ContainsKey(imp)&&dic[imp]!=i){
|
||||
return new int[]{i, dic[imp]};
|
||||
}
|
||||
if(!dic.ContainsKey(nums[i])){
|
||||
dic.Add(nums[i],i);
|
||||
}
|
||||
}
|
||||
return new int[]{0, 0};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;i<len(nums);i++{
|
||||
dp[i]=i
|
||||
for j:=0;j<i;j++{
|
||||
if nums[j]+j>i{
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -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>) -> 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) {
|
||||
|
@ -112,8 +112,8 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n\log n)$ ,有一个快排
|
||||
* 空间复杂度:$O(1)$,我没有算result数组(返回值所需容器占的空间)
|
||||
* 时间复杂度:O(nlog n) ,有一个快排
|
||||
* 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间
|
||||
|
||||
|
||||
## 总结
|
||||
|
@ -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 + n -offset; ++j) {
|
||||
res[startX][j] = count++;
|
||||
for (j = start; j < n - loop; j++) {
|
||||
res[start][j] = count++;
|
||||
}
|
||||
|
||||
// 模拟右侧从上到下
|
||||
for (; i<startX + n -offset; ++i) {
|
||||
for (i = start; i < n - loop; i++) {
|
||||
res[i][j] = count++;
|
||||
}
|
||||
|
||||
// 模拟下侧从右到左
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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<List<Integer>> 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]] {
|
||||
|
@ -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) {
|
||||
|
@ -238,7 +238,7 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
# 总结
|
||||
## 总结
|
||||
|
||||
这次我们又深度剖析了一道二叉树的“简单题”,大家会发现,真正的把题目搞清楚其实并不简单,leetcode上accept了和真正掌握了还是有距离的。
|
||||
|
||||
@ -248,7 +248,7 @@ public:
|
||||
|
||||
如果已经做过这道题目的同学,读完文章可以再去看看这道题目,思考一下,会有不一样的发现!
|
||||
|
||||
# 相关题目推荐
|
||||
## 相关题目推荐
|
||||
|
||||
这两道题目基本和本题是一样的,只要稍加修改就可以AC。
|
||||
|
||||
|
@ -82,6 +82,26 @@ public:
|
||||
}
|
||||
};
|
||||
```
|
||||
```CPP
|
||||
# 递归法
|
||||
class Solution {
|
||||
public:
|
||||
void order(TreeNode* cur, vector<vector<int>>& result, int depth)
|
||||
{
|
||||
if (cur == nullptr) return;
|
||||
if (result.size() == depth) result.push_back(vector<int>());
|
||||
result[depth].push_back(cur->val);
|
||||
order(cur->left, result, depth + 1);
|
||||
order(cur->right, result, depth + 1);
|
||||
}
|
||||
vector<vector<int>> levelOrder(TreeNode* root) {
|
||||
vector<vector<int>> result;
|
||||
int depth = 0;
|
||||
order(root, result, depth);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
python3代码:
|
||||
|
||||
|
@ -377,22 +377,22 @@ class solution {
|
||||
|
||||
```java
|
||||
class solution {
|
||||
public list<list<integer>> pathsum(treenode root, int targetsum) {
|
||||
list<list<integer>> res = new arraylist<>();
|
||||
public List<List<Integer>> pathsum(TreeNode root, int targetsum) {
|
||||
List<List<Integer>> res = new ArrayList<>();
|
||||
if (root == null) return res; // 非空判断
|
||||
|
||||
list<integer> path = new linkedlist<>();
|
||||
|
||||
List<Integer> path = new LinkedList<>();
|
||||
preorderdfs(root, targetsum, res, path);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void preorderdfs(treenode root, int targetsum, list<list<integer>> res, list<integer> path) {
|
||||
public void preorderdfs(TreeNode root, int targetsum, List<List<Integer>> res, List<Integer> 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;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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:
|
||||
|
||||
> 版本一:
|
||||
|
@ -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
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -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>) -> 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
|
||||
})
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -136,19 +136,19 @@ java:
|
||||
class Solution {
|
||||
public int evalRPN(String[] tokens) {
|
||||
Deque<Integer> 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();
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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 <int> set = new HashSet<int>();
|
||||
while(n != 1 && !set.Contains(n)) { //判断避免循环
|
||||
set.Add(n);
|
||||
n = getSum(n);
|
||||
}
|
||||
return n == 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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是当前节点
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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)
|
||||
|
@ -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.赎金信
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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>) -> 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) => {
|
||||
|
@ -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]);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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<i32>, 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];
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -429,7 +429,50 @@ const rob = root => {
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
> 记忆化后序遍历
|
||||
|
||||
```typescript
|
||||
const memory: Map<TreeNode, number> = 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)
|
||||
|
@ -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) {
|
||||
|
@ -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<int> one = Insert(nums1);
|
||||
HashSet<int> two = Insert(nums2);
|
||||
one.IntersectWith(two);
|
||||
return one.ToArray();
|
||||
}
|
||||
public HashSet<int> Insert(int[] nums){
|
||||
HashSet<int> one = new HashSet<int>();
|
||||
foreach(int num in nums){
|
||||
one.Add(num);
|
||||
}
|
||||
return one;
|
||||
}
|
||||
|
||||
```
|
||||
## 相关题目
|
||||
|
||||
* 350.两个数组的交集 II
|
||||
|
@ -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<i32>, target: i32) -> i32 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -93,7 +93,7 @@ public:
|
||||
};
|
||||
```
|
||||
* 时间复杂度:O(nlog n) ,有一个快排
|
||||
* 空间复杂度:O(1)
|
||||
* 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间
|
||||
|
||||
大家此时会发现如此复杂的一个问题,代码实现却这么简单!
|
||||
|
||||
|
@ -105,8 +105,8 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n\log n)$,因为有一个快排
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(nlog n),因为有一个快排
|
||||
* 空间复杂度:O(1),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间
|
||||
|
||||
可以看出代码并不复杂。
|
||||
|
||||
|
@ -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<int, int> dic = new Dictionary<int, int>();
|
||||
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;
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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<i32>, cookie: Vec<i32>) -> 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);
|
||||
|
@ -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;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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))
|
||||
|
@ -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){
|
||||
|
@ -242,6 +242,22 @@ func change(amount int, coins []int) int {
|
||||
}
|
||||
```
|
||||
|
||||
Rust:
|
||||
```rust
|
||||
pub fn change(amount: i32, coins: Vec<i32>) -> i32 {
|
||||
let amount = amount as usize;
|
||||
let coins = coins.iter().map(|&c|c as usize).collect::<Vec<usize>>();
|
||||
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];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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>) -> 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:
|
||||
|
||||
|
@ -279,7 +279,7 @@ class Solution:
|
||||
root.right = self.insertIntoBST(root.right, val)
|
||||
|
||||
# 返回更新后的以当前root为根节点的新树
|
||||
return roo
|
||||
return root
|
||||
```
|
||||
|
||||
**递归法** - 无返回值
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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]);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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(''));
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
/*
|
||||
**函数后序遍历二叉树。判断一个结点状态时,根据其左右孩子结点的状态进行判断
|
||||
|
@ -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(_ < _)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
@ -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<i32>, nums2: Vec<i32>) -> 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:
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -4,40 +4,40 @@
|
||||
</a>
|
||||
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
## 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<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
|
||||
|
||||

|
||||
|
||||
最后红框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:
|
||||
|
@ -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/)
|
||||
|
||||
为什么可以用迭代法(非递归的方式)来实现二叉树的前后中序遍历呢?
|
||||
|
||||
|
@ -99,9 +99,9 @@ void traversal(TreeNode* cur, vector<int>& 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/)
|
||||
|
||||
可能有同学感觉前后中序遍历的递归太简单了,要打迭代法(非递归),别急,我们明天打迭代法,打个通透!
|
||||
|
||||
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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));
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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));
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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();
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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();
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -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。<br>
|
||||
在pa指向链表A时,即pa为空之前,pa经过链表A不相交部分和相交部分,走过的长度为a+c;<br>
|
||||
pa指向链表B后,在移动相交节点之前经过链表B不相交部分,走过的长度为b,总合为a+c+b。<br>
|
||||
同理,pb走过长度的总合为b+c+a。二者相等,即pa与pb可同时到达相交起始节点。 <br>
|
||||
该方法可避免计算具体链表长度。
|
||||
```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;
|
||||
}
|
||||
};
|
||||
```
|
@ -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<lenB,headB(listB)链表往前移动-gap步
|
||||
var gap = lenA - lenB
|
||||
if (gap > 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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
Reference in New Issue
Block a user