Merge branch 'master' into master

This commit is contained in:
程序员Carl
2022-08-01 10:57:27 +08:00
committed by GitHub
121 changed files with 4888 additions and 661 deletions

View File

@@ -523,6 +523,10 @@
[点此这里](https://github.com/youngyangyang04/leetcode-master/graphs/contributors)查看LeetCode-Master的所有贡献者。感谢他们补充了LeetCode-Master的其他语言版本让更多的读者收益于此项目。
# Star 趋势
[![Star History Chart](https://api.star-history.com/svg?repos=youngyangyang04/leetcode-master&type=Date)](https://star-history.com/#youngyangyang04/leetcode-master&Date)
# 关于作者
大家好我是程序员Carl哈工大师兄《代码随想录》作者先后在腾讯和百度从事后端技术研发CSDN博客专家。对算法和C++后端技术有一定的见解利用工作之余重新刷leetcode。

View File

@@ -24,7 +24,9 @@
## 思路
很明显暴力的解法是两层for循环查找时间复杂度是$O(n^2)$
建议看一下我录的这期视频:[梦开始的地方Leetcode1.两数之和](https://www.bilibili.com/video/BV1aT41177mK),结合本题解来学习,事半功倍
很明显暴力的解法是两层for循环查找时间复杂度是O(n^2)。
建议大家做这道题目之前,先做一下这两道
* [242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html)
@@ -32,7 +34,16 @@
[242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://www.programmercarl.com/0349.两个数组的交集.html)这道题目是通过set作为哈希表来解决哈希问题。
本题呢则要使用map那么来看一下使用数组和set来做哈希法的局限。
首先我在强调一下 **什么时候使用哈希法**,当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。
本题呢,我就需要一个集合来存放我们遍历过的元素,然后在遍历数组的时候去询问这个集合,某元素是否遍历过,也就是 是否出现在这个集合。
那么我们就应该想到使用哈希法了。
因为本地,我们不仅要知道元素有没有遍历过,还有知道这个元素对应的下标,**需要使用 key value结构来存放key来存元素value来存下标那么使用map正合适**。
再来看一下使用数组和set来做哈希法的局限。
* 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
* set是一个集合里面放的元素只能是一个key而两数之和这道题目不仅要判断y是否存在而且还要记录y的下标位置因为要返回x 和 y的下标。所以set 也不能用。
@@ -43,20 +54,38 @@ C++中map有三种类型
|映射 |底层实现 | 是否有序 |数值是否可以重复 | 能否更改数值|查询效率 |增删效率|
|---|---| --- |---| --- | --- | ---|
|std::map |红黑树 |key有序 |key不可重复 |key不可修改 | $O(\log n)$|$O(\log n)$ |
|std::multimap | 红黑树|key有序 | key可重复 | key不可修改|$O(\log n)$ |$O(\log n)$ |
|std::unordered_map |哈希表 | key无序 |key不可重复 |key不可修改 |$O(1)$ | $O(1)$|
|std::map |红黑树 |key有序 |key不可重复 |key不可修改 | O(log n)|O(log n) |
|std::multimap | 红黑树|key有序 | key可重复 | key不可修改|O(log n) |O(log n) |
|std::unordered_map |哈希表 | key无序 |key不可重复 |key不可修改 |O(1) | O(1)|
std::unordered_map 底层实现为哈希表std::map 和std::multimap 的底层实现是红黑树。
同理std::map 和std::multimap 的key也是有序的这个问题也经常作为面试题考察对语言容器底层的理解。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://www.programmercarl.com/哈希表理论基础.html)。
**这道题目中并不需要key有序选择std::unordered_map 效率更高!**
**这道题目中并不需要key有序选择std::unordered_map 效率更高!** 使用其他语言的录友注意了解一下自己所用语言的数据结构就行。
解题思路动画如下:
接下来需要明确两点:
![](https://code-thinking.cdn.bcebos.com/gifs/1.两数之和.gif)
* **map用来做什么**
* **map中key和value分别表示什么**
map目的用来存放我们访问过的元素因为遍历数组的时候需要记录我们之前遍历过哪些元素和对应的下表这样才能找到与当前元素相匹配的也就是相加等于target
接下来是map中key和value分别表示什么。
这道题 我们需要 给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标。
那么判断元素是否出现这个元素就要作为key所以数组中的元素作为key有key对应的就是valuevalue用来存下标。
所以 map中的存储结构为 {key数据元素value数组元素对应的下表}。
在遍历数组的时候只需要向map去查询是否有和目前遍历元素比配的数值如果有就找到的匹配对如果没有就把目前遍历的元素放进map中因为map存放的就是我们访问过的元素。
过程如下:
![过程一](https://code-thinking-1253855093.file.myqcloud.com/pics/20220711202638.png)
![过程二](https://code-thinking-1253855093.file.myqcloud.com/pics/20220711202708.png)
C++代码:
@@ -66,18 +95,31 @@ public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map <int,int> map;
for(int i = 0; i < nums.size(); i++) {
auto iter = map.find(target - nums[i]);
// 遍历当前元素并在map中寻找是否有匹配的key
auto iter = map.find(target - nums[i]);
if(iter != map.end()) {
return {iter->second, i};
}
map.insert(pair<int, int>(nums[i], i));
// 如果没找到匹配对就把访问过的元素和下标加入到map中
map.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
```
## 总结
本题其实有四个重点:
* 为什么会想到用哈希表
* 哈希表为什么用map
* 本题map是用来存什么的
* map中的key和value用来存什么的
把这四点想清楚了,本题才算是理解透彻了。
很多录友把这道题目 通过了但都没想清楚map是用来做什么的以至于对代码的理解其实是 一知半解的。
## 其他语言版本
@@ -250,30 +292,6 @@ func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
}
```
PHP:
```php
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function twoSum($nums, $target) {
if (count($nums) == 0) {
return [];
}
$table = [];
for ($i = 0; $i < count($nums); $i++) {
$temp = $target - $nums[$i];
if (isset($table[$temp])) {
return [$table[$temp], $i];
}
$table[$nums[$i]] = $i;
}
return [];
}
}
```
Scala:
```scala
@@ -317,6 +335,20 @@ public class Solution {
}
```
Dart:
```dart
List<int> twoSum(List<int> nums, int target) {
var tmp = [];
for (var i = 0; i < nums.length; i++) {
var rest = target - nums[i];
if(tmp.contains(rest)){
return [tmp.indexOf(rest), i];
}
tmp.add(nums[i]);
}
return [0 , 0];
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -39,7 +39,7 @@
去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
时间复杂度可以做到$O(n^2)$,但还是比较费时的,因为不好做剪枝操作。
时间复杂度可以做到O(n^2),但还是比较费时的,因为不好做剪枝操作。
大家可以尝试使用哈希法写一写,就知道其困难的程度了。
@@ -85,7 +85,7 @@ public:
**其实这道题目使用哈希法并不十分合适**因为在去重的操作中有很多细节需要注意在面试中很难直接写出没有bug的代码。
而且使用哈希法 在使用两层for循环的时候能做的剪枝操作很有限虽然时间复杂度是$O(n^2)$也是可以在leetcode上通过但是程序的执行时间依然比较长 。
而且使用哈希法 在使用两层for循环的时候能做的剪枝操作很有限虽然时间复杂度是O(n^2)也是可以在leetcode上通过但是程序的执行时间依然比较长 。
接下来我来介绍另一个解法:双指针法,**这道题目使用双指针法 要比哈希法高效一些**,那么来讲解一下具体实现的思路。
@@ -95,13 +95,13 @@ public:
拿这个nums数组来举例首先将数组排序然后有一层for循环i从下标0的地方开始同时定一个下标left 定义在i+1的位置上定义下标right 在数组结尾的位置上。
依然还是在数组中找到 abc 使得a + b +c =0我们这里相当于 a = nums[i] b = nums[left] c = nums[right]。
依然还是在数组中找到 abc 使得a + b +c =0我们这里相当于 a = nums[i]b = nums[left]c = nums[right]。
接下来如何移动left 和right呢 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了因为数组是排序后了所以right下标就应该向左移动这样才能让三数之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了left 就向右移动才能让三数之和大一些直到left与right相遇为止
时间复杂度$O(n^2)$
时间复杂度O(n^2)。
C++代码代码如下
@@ -118,13 +118,13 @@ public:
if (nums[i] > 0) {
return result;
}
// 错误去重方法,将会漏掉-1,-1,2 这种情况
// 错误去重a方法,将会漏掉-1,-1,2 这种情况
/*
if (nums[i] == nums[i + 1]) {
continue;
}
*/
// 正确去重方法
// 正确去重a方法
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
@@ -136,17 +136,11 @@ public:
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
*/
if (nums[i] + nums[left] + nums[right] > 0) {
right--;
// 当前元素不合适了,可以去重
while (left < right && nums[right] == nums[right + 1]) right--;
} else if (nums[i] + nums[left] + nums[right] < 0) {
left++;
// 不合适,去重
while (left < right && nums[left] == nums[left - 1]) left++;
} else {
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else {
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 去重逻辑应该放在找到一个三元组之后
// 去重逻辑应该放在找到一个三元组之后对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
@@ -162,6 +156,78 @@ public:
};
```
## 去重逻辑的思考
### a的去重
说道去重其实主要考虑三个数的去重 a, b ,c, 对应的就是 nums[i]nums[left]nums[right]
a 如果重复了怎么办a是nums里遍历的元素那么应该直接跳过去
但这里有一个问题是判断 nums[i] nums[i + 1]是否相同还是判断 nums[i] nums[i-1] 是否相同
有同学可能想这不都一样吗
其实不一样
都是和 nums[i]进行比较是比较它的前一个还是比较他的后一个
如果我们的写法是 这样
```C++
if (nums[i] == nums[i + 1]) { // 去重操作
continue;
}
```
那就我们就把 三元组中出现重复元素的情况直接pass掉了。 例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1那这组数据就pass了。
**我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!**
所以这里是有两个重复的维度。
那么应该这么写:
```C++
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
```
这么写就是当前使用 nums[i],我们判断前一位是不是一样的元素,在看 {-1, -1 ,2} 这组数据,当遍历到 第一个 -1 的时候,只要前一位没有-1那么 {-1, -1 ,2} 这组数据一样可以收录到 结果集里。
这是一个非常细节的思考过程。
### b与c的去重
很多同学写本题的时候,去重的逻辑多加了 对right 和left 的去重:(代码中注释部分)
```C++
while (right > left) {
if (nums[i] + nums[left] + nums[right] > 0) {
right--;
// 去重 right
while (left < right && nums[right] == nums[right + 1]) right--;
} else if (nums[i] + nums[left] + nums[right] < 0) {
left++;
// 去重 left
while (left < right && nums[left] == nums[left - 1]) left++;
} else {
}
}
```
但细想一下,这种去重其实对提升程序运行效率是没有帮助的。
拿right去重为例即使不加这个去重逻辑依然根据 `while (right > left) ` 和 `if (nums[i] + nums[left] + nums[right] > 0)` 去完成right-- 的操作。
多加了 ` while (left < right && nums[right] == nums[right + 1]) right--;` 这一行代码,其实就是把 需要执行的逻辑提前执行了,但并没有减少 判断的逻辑。
最直白的思考过程就是right还是一个数一个数的减下去的所以在哪里减的都是一样的。
所以这种去重 是可以不加的。 仅仅是 把去重的逻辑提前了而已。
# 思考题
@@ -554,6 +620,71 @@ func threeSum(_ nums: [Int]) -> [[Int]] {
}
```
Rust:
```Rust
// 哈希解法
use std::collections::HashSet;
impl Solution {
pub fn three_sum(nums: Vec<i32>) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut nums = nums;
nums.sort();
let len = nums.len();
for i in 0..len {
if nums[i] > 0 { break; }
if i > 0 && nums[i] == nums[i - 1] { continue; }
let mut set = HashSet::new();
for j in (i + 1)..len {
if j > i + 2 && nums[j] == nums[j - 1] && nums[j] == nums[j - 2] { continue; }
let c = 0 - (nums[i] + nums[j]);
if set.contains(&c) {
result.push(vec![nums[i], nums[j], c]);
set.remove(&c);
} else { set.insert(nums[j]); }
}
}
result
}
}
```
```Rust
// 双指针法
use std::collections::HashSet;
impl Solution {
pub fn three_sum(nums: Vec<i32>) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut nums = nums;
nums.sort();
let len = nums.len();
for i in 0..len {
if nums[i] > 0 { return result; }
if i > 0 && nums[i] == nums[i - 1] { continue; }
let (mut left, mut right) = (i + 1, len - 1);
while left < right {
if nums[i] + nums[left] + nums[right] > 0 {
right -= 1;
// 去重
while left < right && nums[right] == nums[right + 1] { right -= 1; }
} else if nums[i] + nums[left] + nums[right] < 0 {
left += 1;
// 去重
while left < right && nums[left] == nums[left - 1] { left += 1; }
} else {
result.push(vec![nums[i], nums[left], nums[right]]);
// 去重
right -= 1;
left += 1;
while left < right && nums[right] == nums[right + 1] { right -= 1; }
while left < right && nums[left] == nums[left - 1] { left += 1; }
}
}
}
result
}
}
```
C:
```C
//qsort辅助cmp函数

View File

@@ -454,6 +454,49 @@ function letterCombinations(digits: string): string[] {
};
```
## Rust
```Rust
impl Solution {
fn backtracking(result: &mut Vec<String>, s: &mut String, map: &[&str; 10], digits: &String, index: usize) {
let len = digits.len();
if len == index {
result.push(s.to_string());
return;
}
// 在保证不会越界的情况下使用unwrap()将Some()中的值提取出来
let digit= digits.chars().nth(index).unwrap().to_digit(10).unwrap() as usize;
let letters = map[digit];
for i in letters.chars() {
s.push(i);
Self::backtracking(result, s, &map, &digits, index+1);
s.pop();
}
}
pub fn letter_combinations(digits: String) -> Vec<String> {
if digits.len() == 0 {
return vec![];
}
const MAP: [&str; 10] = [
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
];
let mut result: Vec<String> = Vec::new();
let mut s: String = String::new();
Self::backtracking(&mut result, &mut s, &MAP, &digits, 0);
result
}
}
```
## C
```c

View File

@@ -35,11 +35,11 @@
[15.三数之和](https://programmercarl.com/0015.三数之和.html)的双指针解法是一层for循环num[i]为确定值然后循环内有left和right下标作为双指针找到nums[i] + nums[left] + nums[right] == 0。
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值依然是循环内有left和right下标作为双指针找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况三数之和的时间复杂度是$O(n^2)$,四数之和的时间复杂度是$O(n^3)$
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值依然是循环内有left和right下标作为双指针找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况三数之和的时间复杂度是O(n^2)四数之和的时间复杂度是O(n^3) 。
那么一样的道理,五数之和、六数之和等等都采用这种解法。
对于[15.三数之和](https://programmercarl.com/0015.三数之和.html)双指针法就是将原本暴力$O(n^3)$的解法,降为$O(n^2)$的解法,四数之和的双指针解法就是将原本暴力$O(n^4)$的解法,降为$O(n^3)$的解法。
对于[15.三数之和](https://programmercarl.com/0015.三数之和.html)双指针法就是将原本暴力O(n^3)的解法降为O(n^2)的解法四数之和的双指针解法就是将原本暴力O(n^4)的解法降为O(n^3)的解法。
之前我们讲过哈希表的经典题目:[454.四数相加II](https://programmercarl.com/0454.四数相加II.html)相对于本题简单很多因为本题是要求在一个集合中找出四个数相加等于target同时四元组不能重复。
@@ -47,14 +47,13 @@
我们来回顾一下,几道题目使用了双指针法。
双指针法将时间复杂度:$O(n^2)$的解法优化为 $O(n)$的解法。也就是降一个数量级,题目如下:
双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:
* [27.移除元素](https://programmercarl.com/0027.移除元素.html)
* [15.三数之和](https://programmercarl.com/0015.三数之和.html)
* [18.四数之和](https://programmercarl.com/0018.四数之和.html)
操作链表:
链表相关双指针题目:
* [206.反转链表](https://programmercarl.com/0206.翻转链表.html)
* [19.删除链表的倒数第N个节点](https://programmercarl.com/0019.删除链表的倒数第N个节点.html)
@@ -72,21 +71,21 @@ public:
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
// 剪枝处理
if (nums[k] > target && (nums[k] >= 0 || target >= 0)) {
// 剪枝处理
if (nums[k] > target && nums[k] >= 0 && target >= 0) {
break; // 这里使用break统一通过最后的return返回
}
// 去重
// 对nums[k]去重
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k + 1; i < nums.size(); i++) {
// 2级剪枝处理
if (nums[k] + nums[i] > target && (nums[k] + nums[i] >= 0 || target >= 0)) {
break;
}
// 正确去重方法
// 2级剪枝处理
if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0 && target >= 0) {
break;
}
// 对nums[i]去重
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
@@ -94,18 +93,14 @@ public:
int right = nums.size() - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if (nums[k] + nums[i] > target - (nums[left] + nums[right])) {
if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
right--;
// 当前元素不合适了,可以去重
while (left < right && nums[right] == nums[right + 1]) right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if (nums[k] + nums[i] < target - (nums[left] + nums[right])) {
} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {
left++;
// 不合适,去重
while (left < right && nums[left] == nums[left - 1]) left++;
} else {
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
// 去重逻辑应该放在找到一个四元组之后
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
@@ -140,6 +135,11 @@ class Solution {
for (int i = 0; i < nums.length; i++) {
// nums[i] > target 直接返回, 剪枝操作
if (nums[i] > 0 && nums[i] > target) {
return result;
}
if (i > 0 && nums[i - 1] == nums[i]) {
continue;
}
@@ -153,7 +153,7 @@ class Solution {
int left = j + 1;
int right = nums.length - 1;
while (right > left) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum > target) {
right--;
} else if (sum < target) {
@@ -522,6 +522,51 @@ public class Solution
}
}
```
Rust:
```Rust
impl Solution {
pub fn four_sum(nums: Vec<i32>, target: i32) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut nums = nums;
nums.sort();
let len = nums.len();
for k in 0..len {
// 剪枝
if nums[k] > target && (nums[k] > 0 || target > 0) { break; }
// 去重
if k > 0 && nums[k] == nums[k - 1] { continue; }
for i in (k + 1)..len {
// 剪枝
if nums[k] + nums[i] > target && (nums[k] + nums[i] >= 0 || target >= 0) { break; }
// 去重
if i > k + 1 && nums[i] == nums[i - 1] { continue; }
let (mut left, mut right) = (i + 1, len - 1);
while left < right {
if nums[k] + nums[i] > target - (nums[left] + nums[right]) {
right -= 1;
// 去重
while left < right && nums[right] == nums[right + 1] { right -= 1; }
} else if nums[k] + nums[i] < target - (nums[left] + nums[right]) {
left += 1;
// 去重
while left < right && nums[left] == nums[left - 1] { left += 1; }
} else {
result.push(vec![nums[k], nums[i], nums[left], nums[right]]);
// 去重
while left < right && nums[right] == nums[right - 1] { right -= 1; }
while left < right && nums[left] == nums[left + 1] { left += 1; }
left += 1;
right -= 1;
}
}
}
}
result
}
}
```
Scala:
```scala
object Solution {

View File

@@ -402,6 +402,38 @@ bool isValid(char * s){
```
C#:
```csharp
public class Solution {
public bool IsValid(string s) {
var len = s.Length;
if(len % 2 == 1) return false; // 字符串长度为单数,直接返回 false
// 初始化栈
var stack = new Stack<char>();
// 遍历字符串
for(int i = 0; i < len; i++){
// 当字符串为左括号时,进栈对应的右括号
if(s[i] == '('){
stack.Push(')');
}else if(s[i] == '['){
stack.Push(']');
}else if(s[i] == '{'){
stack.Push('}');
}
// 当字符串为右括号时,当栈为空(无左括号) 或者 出栈字符不是当前的字符
else if(stack.Count == 0 || stack.Pop() != s[i])
return false;
}
// 如果栈不为空,例如“((()”右括号少于左括号返回false
if (stack.Count > 0)
return false;
// 上面的校验都满足则返回true
else
return true;
}
}
```
PHP:
```php
// https://www.php.net/manual/zh/class.splstack.php

View File

@@ -18,6 +18,8 @@
## 思路
针对本题重点难点我录制了B站讲解视频[帮你把链表细节学清楚! | LeetCode24. 两两交换链表中的节点](https://www.bilibili.com/video/BV1YT411g7br),相信结合视频在看本篇题解,更有助于大家对链表的理解。
这道题目正常模拟就可以了。
建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。
@@ -63,8 +65,8 @@ public:
};
```
* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$
* 时间复杂度O(n)
* 空间复杂度O(1)
## 拓展
@@ -254,20 +256,19 @@ TypeScript
```typescript
function swapPairs(head: ListNode | null): ListNode | null {
const dummyHead: ListNode = new ListNode(0, head);
let cur: ListNode = dummyHead;
while(cur.next !== null && cur.next.next !== null) {
const tem: ListNode = cur.next;
const tem1: ListNode = cur.next.next.next;
cur.next = cur.next.next; // step 1
cur.next.next = tem; // step 2
cur.next.next.next = tem1; // step 3
cur = cur.next.next;
}
return dummyHead.next;
}
const dummyNode: ListNode = new ListNode(0, head);
let curNode: ListNode | null = dummyNode;
while (curNode && curNode.next && curNode.next.next) {
let firstNode: ListNode = curNode.next,
secNode: ListNode = curNode.next.next,
thirdNode: ListNode | null = curNode.next.next.next;
curNode.next = secNode;
secNode.next = firstNode;
firstNode.next = thirdNode;
curNode = firstNode;
}
return dummyNode.next;
};
```
Kotlin:

View File

@@ -183,28 +183,24 @@ class Solution {
Python
```python
```python3
class Solution:
"""双指针法
时间复杂度O(n)
空间复杂度O(1)
"""
@classmethod
def removeElement(cls, nums: List[int], val: int) -> int:
fast = slow = 0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
# 当 fast 指针遇到要删除的元素时停止赋值
# slow 指针停止移动, fast 指针继续前进
fast += 1
return slow
def removeElement(self, nums: List[int], val: int) -> int:
if nums is None or len(nums)==0:
return 0
l=0
r=len(nums)-1
while l<r:
while(l<r and nums[l]!=val):
l+=1
while(l<r and nums[r]==val):
r-=1
nums[l], nums[r]=nums[r], nums[l]
print(nums)
if nums[l]==val:
return l
else:
return l+1
```
@@ -219,6 +215,7 @@ func removeElement(nums []int, val int) int {
res++
}
}
nums=nums[:res]
return res
}
```

View File

@@ -685,7 +685,21 @@ class Solution {
```
Python3
```python
//暴力解法
class Solution(object):
def strStr(self, haystack, needle):
"""
:type haystack: str
:type needle: str
:rtype: int
"""
m,n=len(haystack),len(needle)
for i in range(m):
if haystack[i:i+n]==needle:
return i
return -1
```
```python
// 方法一
class Solution:
@@ -1241,5 +1255,49 @@ function getNext(&$next, $s){
}
}
```
Rust:
> 前缀表统一不减一
```Rust
impl Solution {
pub fn get_next(next: &mut Vec<usize>, s: &Vec<char>) {
let len = s.len();
let mut j = 0;
for i in 1..len {
while j > 0 && s[i] != s[j] {
j = next[j - 1];
}
if s[i] == s[j] {
j += 1;
}
next[i] = j;
}
}
pub fn str_str(haystack: String, needle: String) -> i32 {
let (haystack_len, needle_len) = (haystack.len(), needle.len());
if haystack_len == 0 { return 0; }
if haystack_len < needle_len { return -1;}
let (haystack, needle) = (haystack.chars().collect::<Vec<char>>(), needle.chars().collect::<Vec<char>>());
let mut next: Vec<usize> = vec![0; haystack_len];
Self::get_next(&mut next, &needle);
let mut j = 0;
for i in 0..haystack_len {
while j > 0 && haystack[i] != needle[j] {
j = next[j - 1];
}
if haystack[i] == needle[j] {
j += 1;
}
if j == needle_len {
return (i - needle_len + 1) as i32;
}
}
return -1;
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -171,13 +171,13 @@ class Solution(object):
i = n-2
while i >= 0 and nums[i] >= nums[i+1]:
i -= 1
if i > -1: // i==-1,不存在下一个更大的排列
j = n-1
while j >= 0 and nums[j] <= nums[i]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
start, end = i+1, n-1
while start < end:
nums[start], nums[end] = nums[end], nums[start]
@@ -190,6 +190,26 @@ class Solution(object):
## Go
```go
//卡尔的解法
func nextPermutation(nums []int) {
for i:=len(nums)-1;i>=0;i--{
for j:=len(nums)-1;j>i;j--{
if nums[j]>nums[i]{
//交换
nums[j],nums[i]=nums[i],nums[j]
reverse(nums,0+i+1,len(nums)-1)
return
}
}
}
reverse(nums,0,len(nums)-1)
}
//对目标切片指定区间的反转方法
func reverse(a []int,begin,end int){
for i,j:=begin,end;i<j;i,j=i+1,j-1{
a[i],a[j]=a[j],a[i]
}
}
```
## JavaScript

View File

@@ -7,6 +7,9 @@
# 34. 在排序数组中查找元素的第一个和最后一个位置
[力扣链接](https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/)
给定一个按照升序排列的整数数组 nums和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target返回 [-1, -1]。
@@ -584,5 +587,48 @@ object Solution {
```
### Kotlin
```kotlin
class Solution {
fun searchRange(nums: IntArray, target: Int): IntArray {
var index = binarySearch(nums, target)
// 没找到,返回[-1, -1]
if (index == -1) return intArrayOf(-1, -1)
var left = index
var right = index
// 寻找左边界
while (left - 1 >=0 && nums[left - 1] == target){
left--
}
// 寻找右边界
while (right + 1 <nums.size && nums[right + 1] == target){
right++
}
return intArrayOf(left, right)
}
// 二分查找常规写法
fun binarySearch(nums: IntArray, target: Int): Int {
var left = 0;
var right = nums.size - 1
while (left <= right) {
var middle = left + (right - left)/2
if (nums[middle] > target) {
right = middle - 1
}
else {
if (nums[middle] < target) {
left = middle + 1
}
else {
return middle
}
}
}
// 没找到,返回-1
return -1
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -142,7 +142,7 @@ public:
```
* 时间复杂度O(log n)
* 间复杂度O(1)
* 间复杂度O(1)
效率如下:
![35_搜索插入位置2](https://img-blog.csdnimg.cn/2020121623272877.png)
@@ -283,6 +283,28 @@ var searchInsert = function (nums, target) {
};
```
### TypeScript
```typescript
// 第一种二分法
function searchInsert(nums: number[], target: number): number {
const length: number = nums.length;
let left: number = 0,
right: number = length - 1;
while (left <= right) {
const mid: number = Math.floor((left + right) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] === target) {
return mid;
} else {
right = mid - 1;
}
}
return right + 1;
};
```
### Swift
```swift

View File

@@ -602,5 +602,100 @@ func solveSudoku(_ board: inout [[Character]]) {
}
```
### Scala
详细写法:
```scala
object Solution {
def solveSudoku(board: Array[Array[Char]]): Unit = {
backtracking(board)
}
def backtracking(board: Array[Array[Char]]): Boolean = {
for (i <- 0 until 9) {
for (j <- 0 until 9) {
if (board(i)(j) == '.') { // 必须是为 . 的数字才放数字
for (k <- '1' to '9') { // 这个位置放k是否合适
if (isVaild(i, j, k, board)) {
board(i)(j) = k
if (backtracking(board)) return true // 找到了立刻返回
board(i)(j) = '.' // 回溯
}
}
return false // 9个数都试完了都不行就返回false
}
}
}
true // 遍历完所有的都没返回false说明找到了
}
def isVaild(x: Int, y: Int, value: Char, board: Array[Array[Char]]): Boolean = {
// 行
for (i <- 0 until 9 ) {
if (board(i)(y) == value) {
return false
}
}
// 列
for (j <- 0 until 9) {
if (board(x)(j) == value) {
return false
}
}
// 宫
var row = (x / 3) * 3
var col = (y / 3) * 3
for (i <- row until row + 3) {
for (j <- col until col + 3) {
if (board(i)(j) == value) {
return false
}
}
}
true
}
}
```
遵循Scala至简原则写法
```scala
object Solution {
def solveSudoku(board: Array[Array[Char]]): Unit = {
backtracking(board)
}
def backtracking(board: Array[Array[Char]]): Boolean = {
// 双重for循环 + 循环守卫
for (i <- 0 until 9; j <- 0 until 9 if board(i)(j) == '.') {
// 必须是为 . 的数字才放数字,使用循环守卫判断该位置是否可以放置当前循环的数字
for (k <- '1' to '9' if isVaild(i, j, k, board)) { // 这个位置放k是否合适
board(i)(j) = k
if (backtracking(board)) return true // 找到了立刻返回
board(i)(j) = '.' // 回溯
}
return false // 9个数都试完了都不行就返回false
}
true // 遍历完所有的都没返回false说明找到了
}
def isVaild(x: Int, y: Int, value: Char, board: Array[Array[Char]]): Boolean = {
// 行,循环守卫进行判断
for (i <- 0 until 9 if board(i)(y) == value) return false
// 列,循环守卫进行判断
for (j <- 0 until 9 if board(x)(j) == value) return false
// 宫,循环守卫进行判断
var row = (x / 3) * 3
var col = (y / 3) * 3
for (i <- row until row + 3; j <- col until col + 3 if board(i)(j) == value) return false
true // 最终没有返回false就说明该位置可以填写true
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -291,7 +291,7 @@ class Solution:
for i in range(start_index, len(candidates)):
sum_ += candidates[i]
self.path.append(candidates[i])
self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取所以不是i-1
self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取所以不是i+1
sum_ -= candidates[i] # 回溯
self.path.pop() # 回溯
```

View File

@@ -640,8 +640,44 @@ func min(a,b int)int{
}
```
单调栈解法
```go
func trap(height []int) int {
if len(height) <= 2 {
return 0
}
st := make([]int, 1, len(height)) // 切片模拟单调栈st存储的是高度数组下标
var res int
for i := 1; i < len(height); i++ {
if height[i] < height[st[len(st)-1]] {
st = append(st, i)
} else if height[i] == height[st[len(st)-1]] {
st = st[:len(st)-1] // 比较的新元素和栈顶的元素相等,去掉栈中的,入栈新元素下标
st = append(st, i)
} else {
for len(st) != 0 && height[i] > height[st[len(st)-1]] {
top := st[len(st)-1]
st = st[:len(st)-1]
if len(st) != 0 {
tmp := (min(height[i], height[st[len(st)-1]]) - height[top]) * (i - st[len(st)-1] - 1)
res += tmp
}
}
st = append(st, i)
}
}
return res
}
func min(x, y int) int {
if x >= y {
return y
}
return x
}
```
### JavaScript:
```javascript

View File

@@ -279,7 +279,31 @@ function jump(nums: number[]): number {
};
```
### Scala
```scala
object Solution {
def jump(nums: Array[Int]): Int = {
if (nums.length == 0) return 0
var result = 0 // 记录走的最大步数
var curDistance = 0 // 当前覆盖最远距离下标
var nextDistance = 0 // 下一步覆盖最远距离下标
for (i <- nums.indices) {
nextDistance = math.max(nums(i) + i, nextDistance) // 更新下一步覆盖最远距离下标
if (i == curDistance) {
if (curDistance != nums.length - 1) {
result += 1
curDistance = nextDistance
if (nextDistance >= nums.length - 1) return result
} else {
return result
}
}
}
result
}
}
```

View File

@@ -359,6 +359,36 @@ function permute(nums: number[]): number[][] {
};
```
### Rust
```Rust
impl Solution {
fn backtracking(result: &mut Vec<Vec<i32>>, path: &mut Vec<i32>, nums: &Vec<i32>, used: &mut Vec<bool>) {
let len = nums.len();
if path.len() == len {
result.push(path.clone());
return;
}
for i in 0..len {
if used[i] == true { continue; }
used[i] = true;
path.push(nums[i]);
Self::backtracking(result, path, nums, used);
path.pop();
used[i] = false;
}
}
pub fn permute(nums: Vec<i32>) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut path: Vec<i32> = Vec::new();
let mut used = vec![false; nums.len()];
Self::backtracking(&mut result, &mut path, &nums, &mut used);
result
}
}
```
### C
```c
@@ -456,6 +486,36 @@ func permute(_ nums: [Int]) -> [[Int]] {
}
```
### Scala
```scala
object Solution {
import scala.collection.mutable
def permute(nums: Array[Int]): List[List[Int]] = {
var result = mutable.ListBuffer[List[Int]]()
var path = mutable.ListBuffer[Int]()
def backtracking(used: Array[Boolean]): Unit = {
if (path.size == nums.size) {
// 如果path的长度和nums相等那么可以添加到结果集
result.append(path.toList)
return
}
// 添加循环守卫,只有当当前数字没有用过的情况下才进入回溯
for (i <- nums.indices if used(i) == false) {
used(i) = true
path.append(nums(i))
backtracking(used) // 回溯
path.remove(path.size - 1)
used(i) = false
}
}
backtracking(new Array[Boolean](nums.size)) // 调用方法
result.toList // 最终返回结果集的List形式
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -422,5 +422,43 @@ int** permuteUnique(int* nums, int numsSize, int* returnSize, int** returnColumn
}
```
### Scala
```scala
object Solution {
import scala.collection.mutable
def permuteUnique(nums: Array[Int]): List[List[Int]] = {
var result = mutable.ListBuffer[List[Int]]()
var path = mutable.ListBuffer[Int]()
var num = nums.sorted // 首先对数据进行排序
def backtracking(used: Array[Boolean]): Unit = {
if (path.size == num.size) {
// 如果path的size等于num了那么可以添加到结果集
result.append(path.toList)
return
}
// 循环守卫,当前元素没被使用过就进入循环体
for (i <- num.indices if used(i) == false) {
// 当前索引为0不存在和前一个数字相等可以进入回溯
// 当前索引值和上一个索引不相等,可以回溯
// 前一个索引对应的值没有被选,可以回溯
// 因为Scala没有continue只能将逻辑反过来写
if (i == 0 || (i > 0 && num(i) != num(i - 1)) || used(i-1) == false) {
used(i) = true
path.append(num(i))
backtracking(used)
path.remove(path.size - 1)
used(i) = false
}
}
}
backtracking(new Array[Boolean](nums.length))
result.toList
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -455,7 +455,7 @@ var solveNQueens = function(n) {
};
```
## TypeScript
### TypeScript
```typescript
function solveNQueens(n: number): string[][] {
@@ -683,5 +683,77 @@ char *** solveNQueens(int n, int* returnSize, int** returnColumnSizes){
}
```
### Scala
```scala
object Solution {
import scala.collection.mutable
def solveNQueens(n: Int): List[List[String]] = {
var result = mutable.ListBuffer[List[String]]()
def judge(x: Int, y: Int, maze: Array[Array[Boolean]]): Boolean = {
// 正上方
var xx = x
while (xx >= 0) {
if (maze(xx)(y)) return false
xx -= 1
}
// 左边
var yy = y
while (yy >= 0) {
if (maze(x)(yy)) return false
yy -= 1
}
// 左上方
xx = x
yy = y
while (xx >= 0 && yy >= 0) {
if (maze(xx)(yy)) return false
xx -= 1
yy -= 1
}
xx = x
yy = y
// 右上方
while (xx >= 0 && yy < n) {
if (maze(xx)(yy)) return false
xx -= 1
yy += 1
}
true
}
def backtracking(row: Int, maze: Array[Array[Boolean]]): Unit = {
if (row == n) {
// 将结果转换为题目所需要的形式
var path = mutable.ListBuffer[String]()
for (x <- maze) {
var tmp = mutable.ListBuffer[String]()
for (y <- x) {
if (y == true) tmp.append("Q")
else tmp.append(".")
}
path.append(tmp.mkString)
}
result.append(path.toList)
return
}
for (j <- 0 until n) {
// 判断这个位置是否可以放置皇后
if (judge(row, j, maze)) {
maze(row)(j) = true
backtracking(row + 1, maze)
maze(row)(j) = false
}
}
}
backtracking(0, Array.ofDim[Boolean](n, n))
result.toList
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -144,7 +144,61 @@ var totalNQueens = function(n) {
};
```
TypeScript
```typescript
// 0-该格为空1-该格有皇后
type GridStatus = 0 | 1;
function totalNQueens(n: number): number {
let resCount: number = 0;
const chess: GridStatus[][] = new Array(n).fill(0)
.map(_ => new Array(n).fill(0));
backTracking(chess, n, 0);
return resCount;
function backTracking(chess: GridStatus[][], n: number, startRowIndex: number): void {
if (startRowIndex === n) {
resCount++;
return;
}
for (let j = 0; j < n; j++) {
if (checkValid(chess, startRowIndex, j, n) === true) {
chess[startRowIndex][j] = 1;
backTracking(chess, n, startRowIndex + 1);
chess[startRowIndex][j] = 0;
}
}
}
};
function checkValid(chess: GridStatus[][], i: number, j: number, n: number): boolean {
// 向上纵向检查
let tempI: number = i - 1,
tempJ: number = j;
while (tempI >= 0) {
if (chess[tempI][tempJ] === 1) return false;
tempI--;
}
// 斜向左上检查
tempI = i - 1;
tempJ = j - 1;
while (tempI >= 0 && tempJ >= 0) {
if (chess[tempI][tempJ] === 1) return false;
tempI--;
tempJ--;
}
// 斜向右上检查
tempI = i - 1;
tempJ = j + 1;
while (tempI >= 0 && tempJ < n) {
if (chess[tempI][tempJ] === 1) return false;
tempI--;
tempJ++;
}
return true;
}
```
C
```c
//path[i]为在i行path[i]列上存在皇后
int *path;

View File

@@ -333,8 +333,41 @@ function maxSubArray(nums: number[]): number {
};
```
### Scala
**贪心**
```scala
object Solution {
def maxSubArray(nums: Array[Int]): Int = {
var result = Int.MinValue
var count = 0
for (i <- nums.indices) {
count += nums(i) // count累加
if (count > result) result = count // 记录最大值
if (count <= 0) count = 0 // 一旦count为负则count归0
}
result
}
}
```
**动态规划**
```scala
object Solution {
def maxSubArray(nums: Array[Int]): Int = {
var dp = new Array[Int](nums.length)
var result = nums(0)
dp(0) = nums(0)
for (i <- 1 until nums.length) {
dp(i) = math.max(nums(i), dp(i - 1) + nums(i))
result = math.max(result, dp(i)) // 更新最大值
}
result
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -186,6 +186,24 @@ const maxSubArray = nums => {
};
```
Scala:
```scala
object Solution {
def maxSubArray(nums: Array[Int]): Int = {
var dp = new Array[Int](nums.length)
var result = nums(0)
dp(0) = nums(0)
for (i <- 1 until nums.length) {
dp(i) = math.max(nums(i), dp(i - 1) + nums(i))
result = math.max(result, dp(i)) // 更新最大值
}
result
}
}
```
TypeScript
```typescript
@@ -205,6 +223,5 @@ function maxSubArray(nums: number[]): number {
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -171,6 +171,30 @@ class Solution:
return res
```
```python3
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
r=len(matrix)
if r == 0 or len(matrix[0])==0:
return []
c=len(matrix[0])
res=matrix[0]
if r>1:
for i in range (1,r):
res.append(matrix[i][c-1])
for j in range(c-2, -1, -1):
res.append(matrix[r-1][j])
if c>1:
for i in range(r-2, 0, -1):
res.append(matrix[i][0])
M=[]
for k in range(1, r-1):
e=matrix[k][1:-1]
M.append(e)
return res+self.spiralOrder(M)
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -193,7 +193,22 @@ function canJump(nums: number[]): boolean {
};
```
### Scala
```scala
object Solution {
def canJump(nums: Array[Int]): Boolean = {
var cover = 0
if (nums.length == 1) return true // 如果只有一个元素,那么必定到达
var i = 0
while (i <= cover) { // i表示下标当前只能够走cover步
cover = math.max(i + nums(i), cover)
if (cover >= nums.length - 1) return true // 说明可以覆盖到终点,直接返回
i += 1
}
false // 如果上面没有返回就是跳不到
}
}
```
-----------------------

View File

@@ -22,9 +22,6 @@
* 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
* 注意输入类型已于2019年4月15日更改。 请重置默认代码定义以获取新方法签名。
提示:
* intervals[i][0] <= intervals[i][1]
## 思路
@@ -136,24 +133,38 @@ public:
### Java
```java
/**
时间复杂度 O(NlogN) 排序需要O(NlogN)
空间复杂度 O(logN) java 的内置排序是快速排序 需要 O(logN)空间
*/
class Solution {
public int[][] merge(int[][] intervals) {
List<int[]> res = new LinkedList<>();
Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0]));
//按照左边界排序
Arrays.sort(intervals, (x, y) -> Integer.compare(x[0], y[0]));
//initial start 是最小左边界
int start = intervals[0][0];
int rightmostRightBound = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] > intervals[i - 1][1]) {
res.add(new int[]{start, intervals[i - 1][1]});
//如果左边界大于最大右边界
if (intervals[i][0] > rightmostRightBound) {
//加入区间 并且更新start
res.add(new int[]{start, rightmostRightBound});
start = intervals[i][0];
rightmostRightBound = intervals[i][1];
} else {
intervals[i][1] = Math.max(intervals[i][1], intervals[i - 1][1]);
//更新最大右边界
rightmostRightBound = Math.max(rightmostRightBound, intervals[i][1]);
}
}
res.add(new int[]{start, intervals[intervals.length - 1][1]});
res.add(new int[]{start, rightmostRightBound});
return res.toArray(new int[res.size()][]);
}
}
}
```
```java
// 版本2
@@ -286,7 +297,37 @@ function merge(intervals: number[][]): number[][] {
};
```
### Scala
```scala
object Solution {
import scala.collection.mutable
def merge(intervals: Array[Array[Int]]): Array[Array[Int]] = {
var res = mutable.ArrayBuffer[Array[Int]]()
// 排序
var interval = intervals.sortWith((a, b) => {
a(0) < b(0)
})
var left = interval(0)(0)
var right = interval(0)(1)
for (i <- 1 until interval.length) {
if (interval(i)(0) <= right) {
left = math.min(left, interval(i)(0))
right = math.max(right, interval(i)(1))
} else {
res.append(Array[Int](left, right))
left = interval(i)(0)
right = interval(i)(1)
}
}
res.append(Array[Int](left, right))
res.toArray // 返回res的Array形式
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -598,5 +598,30 @@ object Solution {
}
}
```
C#
```csharp
public class Solution {
public int[][] GenerateMatrix(int n) {
int[][] answer = new int[n][];
for(int i = 0; i < n; i++)
answer[i] = new int[n];
int start = 0;
int end = n - 1;
int tmp = 1;
while(tmp < n * n)
{
for(int i = start; i < end; i++) answer[start][i] = tmp++;
for(int i = start; i < end; i++) answer[i][end] = tmp++;
for(int i = end; i > start; i--) answer[end][i] = tmp++;
for(int i = end; i > start; i--) answer[i][start] = tmp++;
start++;
end--;
}
if(n % 2 == 1) answer[n / 2][n / 2] = tmp;
return answer;
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -374,6 +374,30 @@ function uniquePaths(m: number, n: number): number {
};
```
### Rust
```Rust
impl Solution {
pub fn unique_paths(m: i32, n: i32) -> i32 {
let m = m as usize;
let n = n as usize;
let mut dp = vec![vec![0; n]; m];
for i in 0..m {
dp[i][0] = 1;
}
for j in 0..n {
dp[0][j] = 1;
}
for i in 1..m {
for j in 1..n {
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
dp[m-1][n-1]
}
}
```
### C
```c
@@ -412,5 +436,21 @@ int uniquePaths(int m, int n){
}
```
### Scala
```scala
object Solution {
def uniquePaths(m: Int, n: Int): Int = {
var dp = Array.ofDim[Int](m, n)
for (i <- 0 until m) dp(i)(0) = 1
for (j <- 1 until n) dp(0)(j) = 1
for (i <- 1 until m; j <- 1 until n) {
dp(i)(j) = dp(i - 1)(j) + dp(i)(j - 1)
}
dp(m - 1)(n - 1)
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -384,6 +384,42 @@ function uniquePathsWithObstacles(obstacleGrid: number[][]): number {
};
```
### Rust
```Rust
impl Solution {
pub fn unique_paths_with_obstacles(obstacle_grid: Vec<Vec<i32>>) -> i32 {
let m: usize = obstacle_grid.len();
let n: usize = obstacle_grid[0].len();
if obstacle_grid[0][0] == 1 || obstacle_grid[m-1][n-1] == 1 {
return 0;
}
let mut dp = vec![vec![0; n]; m];
for i in 0..m {
if obstacle_grid[i][0] == 1 {
break;
}
else { dp[i][0] = 1; }
}
for j in 0..n {
if obstacle_grid[0][j] == 1 {
break;
}
else { dp[0][j] = 1; }
}
for i in 1..m {
for j in 1..n {
if obstacle_grid[i][j] == 1 {
continue;
}
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
dp[m-1][n-1]
}
}
```
### C
```c
@@ -440,5 +476,37 @@ int uniquePathsWithObstacles(int** obstacleGrid, int obstacleGridSize, int* obst
}
```
### Scala
```scala
object Solution {
import scala.util.control.Breaks._
def uniquePathsWithObstacles(obstacleGrid: Array[Array[Int]]): Int = {
var (m, n) = (obstacleGrid.length, obstacleGrid(0).length)
var dp = Array.ofDim[Int](m, n)
// 比如break、continue这些流程控制需要使用breakable
breakable(
for (i <- 0 until m) {
if (obstacleGrid(i)(0) != 1) dp(i)(0) = 1
else break()
}
)
breakable(
for (j <- 0 until n) {
if (obstacleGrid(0)(j) != 1) dp(0)(j) = 1
else break()
}
)
for (i <- 1 until m; j <- 1 until n; if obstacleGrid(i)(j) != 1) {
dp(i)(j) = dp(i - 1)(j) + dp(i)(j - 1)
}
dp(m - 1)(n - 1)
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -401,6 +401,38 @@ int climbStairs(int n){
}
```
### Scala
```scala
object Solution {
def climbStairs(n: Int): Int = {
if (n <= 2) return n
var dp = new Array[Int](n + 1)
dp(1) = 1
dp(2) = 2
for (i <- 3 to n) {
dp(i) = dp(i - 1) + dp(i - 2)
}
dp(n)
}
}
```
优化空间复杂度:
```scala
object Solution {
def climbStairs(n: Int): Int = {
if (n <= 2) return n
var (a, b) = (1, 2)
for (i <- 3 to n) {
var tmp = a + b
a = b
b = tmp
}
b // 最终返回b
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -535,6 +535,56 @@ func backtrack(n,k,start int,track []int){
}
```
### Rust
```Rust
impl Solution {
fn backtracking(result: &mut Vec<Vec<i32>>, path: &mut Vec<i32>, n: i32, k: i32, startIndex: i32) {
let len= path.len() as i32;
if len == k{
result.push(path.to_vec());
return;
}
for i in startIndex..= n {
path.push(i);
Self::backtracking(result, path, n, k, i+1);
path.pop();
}
}
pub fn combine(n: i32, k: i32) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut path: Vec<i32> = Vec::new();
Self::backtracking(&mut result, &mut path, n, k, 1);
result
}
}
```
剪枝
```Rust
impl Solution {
fn backtracking(result: &mut Vec<Vec<i32>>, path: &mut Vec<i32>, n: i32, k: i32, startIndex: i32) {
let len= path.len() as i32;
if len == k{
result.push(path.to_vec());
return;
}
// 此处剪枝
for i in startIndex..= n - (k - len) + 1 {
path.push(i);
Self::backtracking(result, path, n, k, i+1);
path.pop();
}
}
pub fn combine(n: i32, k: i32) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut path: Vec<i32> = Vec::new();
Self::backtracking(&mut result, &mut path, n, k, 1);
result
}
}
```
### C
```c
int* path;

View File

@@ -261,6 +261,32 @@ function combine(n: number, k: number): number[][] {
};
```
Rust:
```Rust
impl Solution {
fn backtracking(result: &mut Vec<Vec<i32>>, path: &mut Vec<i32>, n: i32, k: i32, startIndex: i32) {
let len= path.len() as i32;
if len == k{
result.push(path.to_vec());
return;
}
// 此处剪枝
for i in startIndex..= n - (k - len) + 1 {
path.push(i);
Self::backtracking(result, path, n, k, i+1);
path.pop();
}
}
pub fn combine(n: i32, k: i32) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut path: Vec<i32> = Vec::new();
Self::backtracking(&mut result, &mut path, n, k, 1);
result
}
}
```
C:
```c

View File

@@ -373,6 +373,60 @@ func subsets(_ nums: [Int]) -> [[Int]] {
}
```
## Scala
思路一: 使用本题解思路
```scala
object Solution {
import scala.collection.mutable
def subsets(nums: Array[Int]): List[List[Int]] = {
var result = mutable.ListBuffer[List[Int]]()
var path = mutable.ListBuffer[Int]()
def backtracking(startIndex: Int): Unit = {
result.append(path.toList) // 存放结果
if (startIndex >= nums.size) {
return
}
for (i <- startIndex until nums.size) {
path.append(nums(i)) // 添加元素
backtracking(i + 1)
path.remove(path.size - 1) // 删除
}
}
backtracking(0)
result.toList
}
}
```
思路二: 将原问题转换为二叉树,针对每一个元素都有**选或不选**两种选择,直到遍历到最后,所有的叶子节点即为本题的答案:
```scala
object Solution {
import scala.collection.mutable
def subsets(nums: Array[Int]): List[List[Int]] = {
var result = mutable.ListBuffer[List[Int]]()
def backtracking(path: mutable.ListBuffer[Int], startIndex: Int): Unit = {
if (startIndex == nums.length) {
result.append(path.toList)
return
}
path.append(nums(startIndex))
backtracking(path, startIndex + 1) // 选择元素
path.remove(path.size - 1)
backtracking(path, startIndex + 1) // 不选择元素
}
backtracking(mutable.ListBuffer[Int](), 0)
result.toList
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -434,6 +434,63 @@ func subsetsWithDup(_ nums: [Int]) -> [[Int]] {
}
```
### Scala
不使用userd数组:
```scala
object Solution {
import scala.collection.mutable
def subsetsWithDup(nums: Array[Int]): List[List[Int]] = {
var result = mutable.ListBuffer[List[Int]]()
var path = mutable.ListBuffer[Int]()
var num = nums.sorted // 排序
def backtracking(startIndex: Int): Unit = {
result.append(path.toList)
if (startIndex >= num.size){
return
}
for (i <- startIndex until num.size) {
// 同一树层重复的元素不进入回溯
if (!(i > startIndex && num(i) == num(i - 1))) {
path.append(num(i))
backtracking(i + 1)
path.remove(path.size - 1)
}
}
}
backtracking(0)
result.toList
}
}
```
使用Set去重:
```scala
object Solution {
import scala.collection.mutable
def subsetsWithDup(nums: Array[Int]): List[List[Int]] = {
var result = mutable.Set[List[Int]]()
var num = nums.sorted
def backtracking(path: mutable.ListBuffer[Int], startIndex: Int): Unit = {
if (startIndex == num.length) {
result.add(path.toList)
return
}
path.append(num(startIndex))
backtracking(path, startIndex + 1) // 选择
path.remove(path.size - 1)
backtracking(path, startIndex + 1) // 不选择
}
backtracking(mutable.ListBuffer[Int](), 0)
result.toList
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -659,6 +659,48 @@ func restoreIpAddresses(_ s: String) -> [String] {
}
```
## Scala
```scala
object Solution {
import scala.collection.mutable
def restoreIpAddresses(s: String): List[String] = {
var result = mutable.ListBuffer[String]()
if (s.size < 4 || s.length > 12) return result.toList
var path = mutable.ListBuffer[String]()
// 判断IP中的一个字段是否为正确的
def isIP(sub: String): Boolean = {
if (sub.size > 1 && sub(0) == '0') return false
if (sub.toInt > 255) return false
true
}
def backtracking(startIndex: Int): Unit = {
if (startIndex >= s.size) {
if (path.size == 4) {
result.append(path.mkString(".")) // mkString方法可以把集合里的数据以指定字符串拼接
return
}
return
}
// subString
for (i <- startIndex until startIndex + 3 if i < s.size) {
var subString = s.substring(startIndex, i + 1)
if (isIP(subString)) { // 如果合法则进行下一轮
path.append(subString)
backtracking(i + 1)
path = path.take(path.size - 1)
}
}
}
backtracking(0)
result.toList
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -227,7 +227,7 @@ const numTrees =(n) => {
};
```
TypeScript
### TypeScript
```typescript
function numTrees(n: number): number {
@@ -282,5 +282,22 @@ int numTrees(int n){
}
```
### Scala
```scala
object Solution {
def numTrees(n: Int): Int = {
var dp = new Array[Int](n + 1)
dp(0) = 1
for (i <- 1 to n) {
for (j <- 1 to i) {
dp(i) += dp(j - 1) * dp(i - j)
}
}
dp(n)
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -240,6 +240,46 @@ Go
JavaScript
TypeScript:
> 递归法-先序遍历
```typescript
function isSameTree(p: TreeNode | null, q: TreeNode | null): boolean {
if (p === null && q === null) return true;
if (p === null || q === null) return false;
if (p.val !== q.val) return false;
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
};
```
> 迭代法-层序遍历
```typescript
function isSameTree(p: TreeNode | null, q: TreeNode | null): boolean {
const queue1: (TreeNode | null)[] = [],
queue2: (TreeNode | null)[] = [];
queue1.push(p);
queue2.push(q);
while (queue1.length > 0 && queue2.length > 0) {
const node1 = queue1.shift(),
node2 = queue2.shift();
if (node1 === null && node2 === null) continue;
if (
(node1 === null || node2 === null) ||
node1!.val !== node2!.val
) return false;
queue1.push(node1!.left);
queue1.push(node1!.right);
queue2.push(node2!.left);
queue2.push(node2!.right);
}
return true;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -437,6 +437,31 @@ class Solution:
return True
```
层次遍历
```python
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
if not root:
return True
que = [root]
while que:
this_level_length = len(que)
for i in range(this_level_length // 2):
# 要么其中一个是None但另外一个不是
if (not que[i] and que[this_level_length - 1 - i]) or (que[i] and not que[this_level_length - 1 - i]):
return False
# 要么两个都不是None
if que[i] and que[i].val != que[this_level_length - 1 - i].val:
return False
for i in range(this_level_length):
if not que[i]: continue
que.append(que[i].left)
que.append(que[i].right)
que = que[this_level_length:]
return True
```
## Go
```go

View File

@@ -2629,21 +2629,21 @@ JavaScript
var minDepth = function(root) {
if (root === null) return 0;
let queue = [root];
let deepth = 0;
let depth = 0;
while (queue.length) {
let n = queue.length;
deepth++;
depth++;
for (let i=0; i<n; i++) {
let node = queue.shift();
// 如果左右节点都是null则该节点深度最小
// 如果左右节点都是null(在遇见的第一个leaf节点上),则该节点深度最小
if (node.left === null && node.right === null) {
return deepth;
return depth;
}
node.left && queue.push(node.left);;
node.right && queue.push (node.right);
node.right && queue.push(node.right);
}
}
return deepth;
return depth;
};
```

View File

@@ -294,14 +294,13 @@ class solution {
/**
* 递归法
*/
public int maxdepth(treenode root) {
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftdepth = maxdepth(root.left);
int rightdepth = maxdepth(root.right);
return math.max(leftdepth, rightdepth) + 1;
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}
```
@@ -311,23 +310,23 @@ class solution {
/**
* 迭代法,使用层序遍历
*/
public int maxdepth(treenode root) {
public int maxDepth(TreeNode root) {
if(root == null) {
return 0;
}
deque<treenode> deque = new linkedlist<>();
Deque<TreeNode> deque = new LinkedList<>();
deque.offer(root);
int depth = 0;
while (!deque.isempty()) {
while (!deque.isEmpty()) {
int size = deque.size();
depth++;
for (int i = 0; i < size; i++) {
treenode poll = deque.poll();
if (poll.left != null) {
deque.offer(poll.left);
TreeNode node = deque.poll();
if (node.left != null) {
deque.offer(node.left);
}
if (poll.right != null) {
deque.offer(poll.right);
if (node.right != null) {
deque.offer(node.right);
}
}
}

View File

@@ -584,35 +584,29 @@ tree2 的前序遍历是[1 2 3] 后序遍历是[3 2 1]。
```java
class Solution {
Map<Integer, Integer> map; // 方便根据数值查找位置
public TreeNode buildTree(int[] inorder, int[] postorder) {
return buildTree1(inorder, 0, inorder.length, postorder, 0, postorder.length);
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
return findNode(inorder, 0, inorder.length, postorder,0, postorder.length); // 前闭后开
}
public TreeNode buildTree1(int[] inorder, int inLeft, int inRight,
int[] postorder, int postLeft, int postRight) {
// 没有元素了
if (inRight - inLeft < 1) {
public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {
// 参数里的范围都是前闭后开
if (inBegin >= inEnd || postBegin >= postEnd) { // 不满足左闭右开,说明没有元素,返回空树
return null;
}
// 只有一个元素了
if (inRight - inLeft == 1) {
return new TreeNode(inorder[inLeft]);
}
// 后序数组postorder里最后一个即为根结点
int rootVal = postorder[postRight - 1];
TreeNode root = new TreeNode(rootVal);
int rootIndex = 0;
// 根据根结点的值找到该值在中序数组inorder里的位置
for (int i = inLeft; i < inRight; i++) {
if (inorder[i] == rootVal) {
rootIndex = i;
break;
}
}
// 根据rootIndex划分左右子树
root.left = buildTree1(inorder, inLeft, rootIndex,
postorder, postLeft, postLeft + (rootIndex - inLeft));
root.right = buildTree1(inorder, rootIndex + 1, inRight,
postorder, postLeft + (rootIndex - inLeft), postRight - 1);
int rootIndex = map.get(postorder[postEnd - 1]); // 找到后序遍历的最后一个元素在中序遍历中的位置
TreeNode root = new TreeNode(inorder[rootIndex]); // 构造结点
int lenOfLeft = rootIndex - inBegin; // 保存中序左子树个数,用来确定后序数列的个数
root.left = findNode(inorder, inBegin, rootIndex,
postorder, postBegin, postBegin + lenOfLeft);
root.right = findNode(inorder, rootIndex + 1, inEnd,
postorder, postBegin + lenOfLeft, postEnd - 1);
return root;
}
}
@@ -622,31 +616,29 @@ class Solution {
```java
class Solution {
Map<Integer, Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
return helper(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
public TreeNode helper(int[] preorder, int preLeft, int preRight,
int[] inorder, int inLeft, int inRight) {
// 递归终止条件
if (inLeft > inRight || preLeft > preRight) return null;
// val 为前序遍历第一个的值,也即是根节点的值
// idx 为根据根节点的值来找中序遍历的下标
int idx = inLeft, val = preorder[preLeft];
TreeNode root = new TreeNode(val);
for (int i = inLeft; i <= inRight; i++) {
if (inorder[i] == val) {
idx = i;
break;
}
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
// 根据 idx 来递归找左右子树
root.left = helper(preorder, preLeft + 1, preLeft + (idx - inLeft),
inorder, inLeft, idx - 1);
root.right = helper(preorder, preLeft + (idx - inLeft) + 1, preRight,
inorder, idx + 1, inRight);
return findNode(preorder, 0, preorder.length, inorder, 0, inorder.length); // 前闭后开
}
public TreeNode findNode(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin, int inEnd) {
// 参数里的范围都是前闭后开
if (preBegin >= preEnd || inBegin >= inEnd) { // 不满足左闭右开,说明没有元素,返回空树
return null;
}
int rootIndex = map.get(preorder[preBegin]); // 找到前序遍历的第一个元素在中序遍历中的位置
TreeNode root = new TreeNode(inorder[rootIndex]); // 构造结点
int lenOfLeft = rootIndex - inBegin; // 保存中序左子树个数,用来确定前序数列的个数
root.left = findNode(preorder, preBegin + 1, preBegin + lenOfLeft + 1,
inorder, inBegin, rootIndex);
root.right = findNode(preorder, preBegin + lenOfLeft + 1, preEnd,
inorder, rootIndex + 1, inEnd);
return root;
}
}

View File

@@ -531,40 +531,26 @@ class Solution:
迭代法:
```python
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
st = []
def isBalanced(self, root: Optional[TreeNode]) -> bool:
if not root:
return True
st.append(root)
while st:
node = st.pop() #中
if abs(self.getDepth(node.left) - self.getDepth(node.right)) > 1:
return False
if node.right:
st.append(node.right) #右(空节点不入栈)
if node.left:
st.append(node.left) #左(空节点不入栈)
return True
def getDepth(self, cur):
st = []
if cur:
st.append(cur)
depth = 0
result = 0
while st:
node = st.pop()
height_map = {}
stack = [root]
while stack:
node = stack.pop()
if node:
st.append(node) #中
st.append(None)
depth += 1
if node.right: st.append(node.right) #右
if node.left: st.append(node.left) #左
stack.append(node)
stack.append(None)
if node.left: stack.append(node.left)
if node.right: stack.append(node.right)
else:
node = st.pop()
depth -= 1
result = max(result, depth)
return result
real_node = stack.pop()
left, right = height_map.get(real_node.left, 0), height_map.get(real_node.right, 0)
if abs(left - right) > 1:
return False
height_map[real_node] = 1 + max(left, right)
return True
```

View File

@@ -287,6 +287,79 @@ const connect = root => {
};
```
## TypeScript
命名空间Node与typescript中内置类型冲突这里改成了NodePro
> 递归法:
```typescript
class NodePro {
val: number
left: NodePro | null
right: NodePro | null
next: NodePro | null
constructor(val?: number, left?: NodePro, right?: NodePro, next?: NodePro) {
this.val = (val === undefined ? 0 : val)
this.left = (left === undefined ? null : left)
this.right = (right === undefined ? null : right)
this.next = (next === undefined ? null : next)
}
}
function connect(root: NodePro | null): NodePro | null {
if (root === null) return null;
root.next = null;
recur(root);
return root;
};
function recur(node: NodePro): void {
if (node.left === null || node.right === null) return;
node.left.next = node.right;
node.right.next = node.next && node.next.left;
recur(node.left);
recur(node.right);
}
```
> 迭代法:
```typescript
class NodePro {
val: number
left: NodePro | null
right: NodePro | null
next: NodePro | null
constructor(val?: number, left?: NodePro, right?: NodePro, next?: NodePro) {
this.val = (val === undefined ? 0 : val)
this.left = (left === undefined ? null : left)
this.right = (right === undefined ? null : right)
this.next = (next === undefined ? null : next)
}
}
function connect(root: NodePro | null): NodePro | null {
if (root === null) return null;
const queue: NodePro[] = [];
queue.push(root);
while (queue.length > 0) {
for (let i = 0, length = queue.length; i < length; i++) {
const curNode: NodePro = queue.shift()!;
if (i === length - 1) {
curNode.next = null;
} else {
curNode.next = queue[0];
}
if (curNode.left !== null) queue.push(curNode.left);
if (curNode.right !== null) queue.push(curNode.right);
}
}
return root;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -91,8 +91,8 @@ public:
};
```
* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$
* 时间复杂度O(n)
* 空间复杂度O(1)
### 动态规划
@@ -133,8 +133,9 @@ public:
## 其他语言版本
Java:
### Java:
贪心:
```java
// 贪心思路
class Solution {
@@ -148,6 +149,7 @@ class Solution {
}
```
动态规划:
```java
class Solution { // 动态规划
public int maxProfit(int[] prices) {
@@ -169,8 +171,8 @@ class Solution { // 动态规划
}
```
Python:
### Python:
贪心:
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
@@ -180,7 +182,7 @@ class Solution:
return result
```
python动态规划
动态规划:
```python
class Solution:
def maxProfit(self, prices: List[int]) -> int:
@@ -194,7 +196,7 @@ class Solution:
return dp[-1][1]
```
Go:
### Go:
```golang
//贪心算法
@@ -231,7 +233,7 @@ func maxProfit(prices []int) int {
}
```
Javascript:
### Javascript:
贪心
```Javascript
@@ -268,7 +270,7 @@ const maxProfit = (prices) => {
};
```
TypeScript
### TypeScript
```typescript
function maxProfit(prices: number[]): number {
@@ -280,7 +282,7 @@ function maxProfit(prices: number[]): number {
};
```
C:
### C:
贪心:
```c
int maxProfit(int* prices, int pricesSize){
@@ -318,5 +320,22 @@ int maxProfit(int* prices, int pricesSize){
}
```
### Scala
贪心:
```scala
object Solution {
def maxProfit(prices: Array[Int]): Int = {
var result = 0
for (i <- 1 until prices.length) {
if (prices(i) > prices(i - 1)) {
result += prices(i) - prices(i - 1)
}
}
result
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -3,6 +3,9 @@
<img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20210924105952.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 129. 求根节点到叶节点数字之和
[力扣题目链接](https://leetcode.cn/problems/sum-root-to-leaf-numbers/)
@@ -245,6 +248,29 @@ class Solution:
```
Go
```go
func sumNumbers(root *TreeNode) int {
sum = 0
travel(root, root.Val)
return sum
}
func travel(root *TreeNode, tmpSum int) {
if root.Left == nil && root.Right == nil {
sum += tmpSum
} else {
if root.Left != nil {
travel(root.Left, tmpSum*10+root.Left.Val)
}
if root.Right != nil {
travel(root.Right, tmpSum*10+root.Right.Val)
}
}
}
```
JavaScript
```javascript
var sumNumbers = function(root) {
@@ -289,7 +315,40 @@ var sumNumbers = function(root) {
};
```
TypeScript
```typescript
function sumNumbers(root: TreeNode | null): number {
if (root === null) return 0;
let resTotal: number = 0;
const route: number[] = [];
route.push(root.val);
recur(root, route);
return resTotal;
function recur(node: TreeNode, route: number[]): void {
if (node.left === null && node.right === null) {
resTotal += listToSum(route);
return;
}
if (node.left !== null) {
route.push(node.left.val);
recur(node.left, route);
route.pop();
};
if (node.right !== null) {
route.push(node.right.val);
recur(node.right, route);
route.pop();
};
}
function listToSum(nums: number[]): number {
return Number(nums.join(''));
}
};
```
C:
```c
//sum记录总和
int sum;

View File

@@ -676,5 +676,50 @@ impl Solution {
}
}
```
## Scala
```scala
object Solution {
import scala.collection.mutable
def partition(s: String): List[List[String]] = {
var result = mutable.ListBuffer[List[String]]()
var path = mutable.ListBuffer[String]()
// 判断字符串是否回文
def isPalindrome(start: Int, end: Int): Boolean = {
var (left, right) = (start, end)
while (left < right) {
if (s(left) != s(right)) return false
left += 1
right -= 1
}
true
}
// 回溯算法
def backtracking(startIndex: Int): Unit = {
if (startIndex >= s.size) {
result.append(path.toList)
return
}
// 添加循环守卫,如果当前分割是回文子串则进入回溯
for (i <- startIndex until s.size if isPalindrome(startIndex, i)) {
path.append(s.substring(startIndex, i + 1))
backtracking(i + 1)
path = path.take(path.size - 1)
}
}
backtracking(0)
result.toList
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -206,6 +206,55 @@ public:
## Java
```java
class Solution {
public int minCut(String s) {
if(null == s || "".equals(s)){
return 0;
}
int len = s.length();
// 1.
// 记录子串[i..j]是否是回文串
boolean[][] isPalindromic = new boolean[len][len];
// 从下到上,从左到右
for(int i = len - 1; i >= 0; i--){
for(int j = i; j < len; j++){
if(s.charAt(i) == s.charAt(j)){
if(j - i <= 1){
isPalindromic[i][j] = true;
} else{
isPalindromic[i][j] = isPalindromic[i + 1][j - 1];
}
} else{
isPalindromic[i][j] = false;
}
}
}
// 2.
// dp[i] 表示[0..i]的最小分割次数
int[] dp = new int[len];
for(int i = 0; i < len; i++){
//初始考虑最坏的情况。 1个字符分割0次 len个字符分割 len - 1次
dp[i] = i;
}
for(int i = 1; i < len; i++){
if(isPalindromic[0][i]){
// s[0..i]是回文了,那 dp[i] = 0, 一次也不用分割
dp[i] = 0;
continue;
}
for(int j = 0; j < i; j++){
// 按文中的思路,不清楚就拿 "ababa" 为例,先写出 isPalindromic 数组,再进行求解
if(isPalindromic[j + 1][i]){
dp[i] = Math.min(dp[i], dp[j] + 1);
}
}
}
return dp[len - 1];
}
}
```
## Python
@@ -240,6 +289,7 @@ class Solution:
## Go
```go
```
## JavaScript

View File

@@ -471,5 +471,73 @@ int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
}
```
### Scala
暴力解法:
```scala
object Solution {
def canCompleteCircuit(gas: Array[Int], cost: Array[Int]): Int = {
for (i <- cost.indices) {
var rest = gas(i) - cost(i)
var index = (i + 1) % cost.length // index为i的下一个节点
while (rest > 0 && i != index) {
rest += (gas(index) - cost(index))
index = (index + 1) % cost.length
}
if (rest >= 0 && index == i) return i
}
-1
}
}
```
贪心算法,方法一:
```scala
object Solution {
def canCompleteCircuit(gas: Array[Int], cost: Array[Int]): Int = {
var curSum = 0
var min = Int.MaxValue
for (i <- gas.indices) {
var rest = gas(i) - cost(i)
curSum += rest
min = math.min(min, curSum)
}
if (curSum < 0) return -1 // 情况1: gas的总和小于cost的总和不可能到达终点
if (min >= 0) return 0 // 情况2: 最小值>=0从0号出发可以直接到达
// 情况3: min为负值从后向前看能把min填平的节点就是出发节点
for (i <- gas.length - 1 to 0 by -1) {
var rest = gas(i) - cost(i)
min += rest
if (min >= 0) return i
}
-1
}
}
```
贪心算法,方法二:
```scala
object Solution {
def canCompleteCircuit(gas: Array[Int], cost: Array[Int]): Int = {
var curSum = 0
var totalSum = 0
var start = 0
for (i <- gas.indices) {
curSum += (gas(i) - cost(i))
totalSum += (gas(i) - cost(i))
if (curSum < 0) {
start = i + 1 // 起始位置更新
curSum = 0 // curSum从0开始
}
}
if (totalSum < 0) return -1 // 说明怎么走不可能跑一圈
start
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -324,6 +324,31 @@ function candy(ratings: number[]): number {
};
```
### Scala
```scala
object Solution {
def candy(ratings: Array[Int]): Int = {
var candyVec = new Array[Int](ratings.length)
for (i <- candyVec.indices) candyVec(i) = 1
// 从前向后
for (i <- 1 until candyVec.length) {
if (ratings(i) > ratings(i - 1)) {
candyVec(i) = candyVec(i - 1) + 1
}
}
// 从后向前
for (i <- (candyVec.length - 2) to 0 by -1) {
if (ratings(i) > ratings(i + 1)) {
candyVec(i) = math.max(candyVec(i), candyVec(i + 1) + 1)
}
}
candyVec.sum // 求和
}
}
```
-----------------------

View File

@@ -7,6 +7,8 @@
# 141. 环形链表
[力扣题目链接](https://leetcode.cn/problems/linked-list-cycle/submissions/)
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1则在该链表中没有环。注意pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
@@ -103,7 +105,7 @@ class Solution:
return False
```
## Go
### Go
```go
func hasCycle(head *ListNode) bool {
@@ -139,6 +141,23 @@ var hasCycle = function(head) {
};
```
### TypeScript
```typescript
function hasCycle(head: ListNode | null): boolean {
let slowNode: ListNode | null = head,
fastNode: ListNode | null = head;
while (fastNode !== null && fastNode.next !== null) {
slowNode = slowNode!.next;
fastNode = fastNode.next.next;
if (slowNode === fastNode) return true;
}
return false;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -24,6 +24,8 @@
## 思路
为了易于大家理解,我录制讲解视频:[B站把环形链表讲清楚 ](https://www.bilibili.com/video/BV1if4y1d7ob)。结合视频在看本篇题解,事半功倍。
这道题目,不仅考察对链表的操作,而且还需要一些数学运算。
主要考察两知识点:
@@ -301,13 +303,13 @@ function detectCycle(head: ListNode | null): ListNode | null {
let slowNode: ListNode | null = head,
fastNode: ListNode | null = head;
while (fastNode !== null && fastNode.next !== null) {
slowNode = (slowNode as ListNode).next;
slowNode = slowNode!.next;
fastNode = fastNode.next.next;
if (slowNode === fastNode) {
slowNode = head;
while (slowNode !== fastNode) {
slowNode = (slowNode as ListNode).next;
fastNode = (fastNode as ListNode).next;
slowNode = slowNode!.next;
fastNode = fastNode!.next;
}
return slowNode;
}

View File

@@ -6,6 +6,8 @@
# 143.重排链表
[力扣题目链接](https://leetcode.cn/problems/reorder-list/submissions/)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210726160122.png)
## 思路
@@ -465,7 +467,81 @@ var reorderList = function(head, s = [], tmp) {
}
```
### TypeScript
> 辅助数组法:
```typescript
function reorderList(head: ListNode | null): void {
if (head === null) return;
const helperArr: ListNode[] = [];
let curNode: ListNode | null = head;
while (curNode !== null) {
helperArr.push(curNode);
curNode = curNode.next;
}
let node: ListNode = head;
let left: number = 1,
right: number = helperArr.length - 1;
let count: number = 0;
while (left <= right) {
if (count % 2 === 0) {
node.next = helperArr[right--];
} else {
node.next = helperArr[left++];
}
count++;
node = node.next;
}
node.next = null;
};
```
> 分割链表法:
```typescript
function reorderList(head: ListNode | null): void {
if (head === null || head.next === null) return;
let fastNode: ListNode = head,
slowNode: ListNode = head;
while (fastNode.next !== null && fastNode.next.next !== null) {
slowNode = slowNode.next!;
fastNode = fastNode.next.next;
}
let head1: ListNode | null = head;
// 反转后半部分链表
let head2: ListNode | null = reverseList(slowNode.next);
// 分割链表
slowNode.next = null;
/**
直接在head1链表上进行插入
head1 链表长度一定大于或等于head2,
因此在下面的循环中只要head2不为null, head1 一定不为null
*/
while (head2 !== null) {
const tempNode1: ListNode | null = head1!.next,
tempNode2: ListNode | null = head2.next;
head1!.next = head2;
head2.next = tempNode1;
head1 = tempNode1;
head2 = tempNode2;
}
};
function reverseList(head: ListNode | null): ListNode | null {
let curNode: ListNode | null = head,
preNode: ListNode | null = null;
while (curNode !== null) {
const tempNode: ListNode | null = curNode.next;
curNode.next = preNode;
preNode = curNode;
curNode = tempNode;
}
return preNode;
}
```
### C
方法三:反转链表
```c
//翻转链表

View File

@@ -326,6 +326,40 @@ func evalRPN(_ tokens: [String]) -> Int {
}
```
C#:
```csharp
public int EvalRPN(string[] tokens) {
int num;
Stack<int> stack = new Stack<int>();
foreach(string s in tokens){
if(int.TryParse(s, out num)){
stack.Push(num);
}else{
int num1 = stack.Pop();
int num2 = stack.Pop();
switch (s)
{
case "+":
stack.Push(num1 + num2);
break;
case "-":
stack.Push(num2 - num1);
break;
case "*":
stack.Push(num1 * num2);
break;
case "/":
stack.Push(num2 / num1);
break;
default:
break;
}
}
}
return stack.Pop();
}
```
PHP
```php

View File

@@ -79,7 +79,7 @@ void removeExtraSpaces(string& s) {
逻辑很简单从前向后遍历遇到空格了就erase。
如果不仔细琢磨一下erase的时间复杂还以为以上的代码是O(n)的时间复杂度呢。
如果不仔细琢磨一下erase的时间复杂还以为以上的代码是O(n)的时间复杂度呢。
想一下真正的时间复杂度是多少一个erase本来就是O(n)的操作erase实现原理题目[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)最优的算法来移除元素也要O(n)。
@@ -864,7 +864,58 @@ function reverseString(&$s, $start, $end) {
return ;
}
```
Rust:
```Rust
// 根据C++版本二思路进行实现
// 函数名根据Rust编译器建议由驼峰命名法改为蛇形命名法
impl Solution {
pub fn reverse(s: &mut Vec<char>, mut begin: usize, mut end: usize){
while begin < end {
let temp = s[begin];
s[begin] = s[end];
s[end] = temp;
begin += 1;
end -= 1;
}
}
pub fn remove_extra_spaces(s: &mut Vec<char>) {
let mut slow: usize = 0;
let len = s.len();
// 注意这里不能用for循环不然在里面那个while循环中对i的递增会失效
let mut i: usize = 0;
while i < len {
if !s[i].is_ascii_whitespace() {
if slow != 0 {
s[slow] = ' ';
slow += 1;
}
while i < len && !s[i].is_ascii_whitespace() {
s[slow] = s[i];
slow += 1;
i += 1;
}
}
i += 1;
}
s.resize(slow, ' ');
}
pub fn reverse_words(s: String) -> String {
let mut s = s.chars().collect::<Vec<char>>();
Self::remove_extra_spaces(&mut s);
let len = s.len();
Self::reverse(&mut s, 0, len - 1);
let mut start = 0;
for i in 0..=len {
if i == len || s[i].is_ascii_whitespace() {
Self::reverse(&mut s, start, i - 1);
start = i + 1;
}
}
s.iter().collect::<String>()
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -315,6 +315,36 @@ class Solution {
}
```
Rust:
```Rust
use std::collections::HashSet;
impl Solution {
pub fn get_sum(mut n: i32) -> i32 {
let mut sum = 0;
while n > 0 {
sum += (n % 10) * (n % 10);
n /= 10;
}
sum
}
pub fn is_happy(n: i32) -> bool {
let mut n = n;
let mut set = HashSet::new();
loop {
let sum = Self::get_sum(n);
if sum == 1 {
return true;
}
if set.contains(&sum) {
return false;
} else { set.insert(sum); }
n = sum;
}
}
}
```
C:
```C
typedef struct HashNodeTag {
@@ -338,53 +368,6 @@ static inline int calcSquareSum(int num) {
return sum;
}
#define HASH_TABLE_SIZE (32)
bool isHappy(int n){
int sum = n;
int index = 0;
bool bHappy = false;
bool bExit = false;
/* allocate the memory for hash table with chaining method*/
HashNode ** hashTable = (HashNode **)calloc(HASH_TABLE_SIZE, sizeof(HashNode));
while(bExit == false) {
/* check if n has been calculated */
index = hash(n, HASH_TABLE_SIZE);
HashNode ** p = hashTable + index;
while((*p) && (bExit == false)) {
/* Check if this num was calculated, if yes, this will be endless loop */
if((*p)->key == n) {
bHappy = false;
bExit = true;
}
/* move to next node of the same index */
p = &((*p)->next);
}
/* put n intot hash table */
HashNode * newNode = (HashNode *)malloc(sizeof(HashNode));
newNode->key = n;
newNode->next = NULL;
*p = newNode;
sum = calcSquareSum(n);
if(sum == 1) {
bHappy = true;
bExit = true;
}
else {
n = sum;
}
}
return bHappy;
}
```
Scala:
```scala

View File

@@ -397,18 +397,18 @@ function removeElements(head: ListNode | null, val: number): ListNode | null {
```typescript
function removeElements(head: ListNode | null, val: number): ListNode | null {
let dummyHead = new ListNode(0, head);
let pre: ListNode = dummyHead, cur: ListNode | null = dummyHead.next;
// 删除非头部节点
// 添加虚拟节点
const data = new ListNode(0, head);
let pre = data, cur = data.next;
while (cur) {
if (cur.val === val) {
pre.next = cur.next;
pre.next = cur.next
} else {
pre = cur;
}
cur = cur.next;
}
return head.next;
return data.next;
};
```
@@ -487,17 +487,19 @@ RUST:
// }
impl Solution {
pub fn remove_elements(head: Option<Box<ListNode>>, val: i32) -> Option<Box<ListNode>> {
let mut head = head;
let mut dummy_head = ListNode::new(0);
let mut cur = &mut dummy_head;
while let Some(mut node) = head {
head = std::mem::replace(&mut node.next, None);
if node.val != val {
cur.next = Some(node);
let mut dummyHead = Box::new(ListNode::new(0));
dummyHead.next = head;
let mut cur = dummyHead.as_mut();
// 使用take()替换std::men::replace(&mut node.next, None)达到相同的效果,并且更普遍易读
while let Some(nxt) = cur.next.take() {
if nxt.val == val {
cur.next = nxt.next;
} else {
cur.next = Some(nxt);
cur = cur.next.as_mut().unwrap();
}
}
dummy_head.next
dummyHead.next
}
}
```
@@ -532,5 +534,39 @@ object Solution {
}
}
```
Kotlin:
```kotlin
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/
class Solution {
fun removeElements(head: ListNode?, `val`: Int): ListNode? {
// 使用虚拟节点令该节点指向head
var dummyNode = ListNode(-1)
dummyNode.next = head
// 使用cur遍历链表各个节点
var cur = dummyNode
// 判断下个节点是否为空
while (cur.next != null) {
// 符合条件,移除节点
if (cur.next.`val` == `val`) {
cur.next = cur.next.next
}
// 不符合条件,遍历下一节点
else {
cur = cur.next
}
}
// 注意:返回的不是虚拟节点
return dummyNode.next
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -156,6 +156,28 @@ var isIsomorphic = function(s, t) {
};
```
## TypeScript
```typescript
function isIsomorphic(s: string, t: string): boolean {
const helperMap1: Map<string, string> = new Map();
const helperMap2: Map<string, string> = new Map();
for (let i = 0, length = s.length; i < length; i++) {
let temp1: string | undefined = helperMap1.get(s[i]);
let temp2: string | undefined = helperMap2.get(t[i]);
if (temp1 === undefined && temp2 === undefined) {
helperMap1.set(s[i], t[i]);
helperMap2.set(t[i], s[i]);
} else if (temp1 !== t[i] || temp2 !== s[i]) {
return false;
}
}
return true;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -19,6 +19,8 @@
# 思路
本题我录制了B站视频[帮你拿下反转链表 | LeetCode206.反转链表](https://www.bilibili.com/video/BV1nB4y1i7eL),相信结合视频在看本篇题解,更有助于大家对链表的理解。
如果再定义一个新的链表,实现链表元素的反转,其实这是对内存空间的浪费。
其实只需要改变链表的next指针的指向直接将链表反转 ,而不用重新定义一个新的链表,如图所示:
@@ -420,6 +422,41 @@ fun reverseList(head: ListNode?): ListNode? {
return pre
}
```
```kotlin
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/
class Solution {
fun reverseList(head: ListNode?): ListNode? {
// temp用来存储临时的节点
var temp: ListNode?
// cur用来遍历链表
var cur: ListNode? = head
// pre用来作为链表反转的工具
// pre是比pre前一位的节点
var pre: ListNode? = null
while (cur != null) {
// 临时存储原本cur的下一个节点
temp = cur.next
// 使cur下一节点地址为它之前的
cur.next = pre
// 之后随着cur的遍历移动pre
pre = cur;
// 移动cur遍历链表各个节点
cur = temp;
}
// 由于开头使用pre为null,所以cur等于链表本身长度+1
// 此时pre在cur前一位所以此时pre为头节点
return pre;
}
}
```
Swift
```swift

View File

@@ -19,7 +19,7 @@
# 思路
为了易于大家理解,我特意录制了[拿下滑动窗口! | LeetCode 209 长度最小的子数组](https://www.bilibili.com/video/BV1tZ4y1q7XE)
为了易于大家理解,我特意录制了B站视频[拿下滑动窗口! | LeetCode 209 长度最小的子数组](https://www.bilibili.com/video/BV1tZ4y1q7XE)
## 暴力解法
@@ -179,8 +179,27 @@ class Solution:
index += 1
return 0 if res==float("inf") else res
```
```python3
#滑动窗口
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
if nums is None or len(nums)==0:
return 0
lenf=len(nums)+1
total=0
i=j=0
while (j<len(nums)):
total=total+nums[j]
j+=1
while (total>=target):
lenf=min(lenf,j-i)
total=total-nums[i]
i+=1
if lenf==len(nums)+1:
return 0
else:
return lenf
```
Go
```go
func minSubArrayLen(target int, nums []int) int {
@@ -417,6 +436,38 @@ class Solution {
}
}
```
滑动窗口
```kotlin
class Solution {
fun minSubArrayLen(target: Int, nums: IntArray): Int {
// 左边界 和 右边界
var left: Int = 0
var right: Int = 0
// sum 用来记录和
var sum: Int = 0
// result记录一个固定值便于判断是否存在的这样的数组
var result: Int = Int.MAX_VALUE
// subLenth记录长度
var subLength = Int.MAX_VALUE
while (right < nums.size) {
// 从数组首元素开始逐次求和
sum += nums[right++]
// 判断
while (sum >= target) {
var temp = right - left
// 每次和上一次比较求出最小数组长度
subLength = if (subLength > temp) temp else subLength
// sum减少左边界右移
sum -= nums[left++]
}
}
// 如果subLength为初始值则说明长度为0否则返回subLength
return if(subLength == result) 0 else subLength
}
}
```
Scala:
滑动窗口:
@@ -465,6 +516,27 @@ object Solution {
}
}
```
C#:
```csharp
public class Solution {
public int MinSubArrayLen(int s, int[] nums) {
int n = nums.Length;
int ans = int.MaxValue;
int start = 0, end = 0;
int sum = 0;
while (end < n) {
sum += nums[end];
while (sum >= s)
{
ans = Math.Min(ans, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return ans == int.MaxValue ? 0 : ans;
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -212,7 +212,7 @@ public:
# 总结
开篇就介绍了本题与[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)的区别,相对来说加了元素总和的限制,如果做完[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)再做本题在合适不过。
开篇就介绍了本题与[77.组合](https://programmercarl.com/0077.组合.html)的区别,相对来说加了元素总和的限制,如果做完[77.组合](https://programmercarl.com/0077.组合.html)再做本题在合适不过。
分析完区别,依然把问题抽象为树形结构,按照回溯三部曲进行讲解,最后给出剪枝的优化。
@@ -411,6 +411,35 @@ function combinationSum3(k: number, n: number): number[][] {
};
```
## Rust
```Rust
impl Solution {
fn backtracking(result: &mut Vec<Vec<i32>>, path:&mut Vec<i32>, targetSum:i32, k: i32, mut sum: i32, startIndex: i32) {
let len = path.len() as i32;
if len == k {
if sum == targetSum {
result.push(path.to_vec());
}
return;
}
for i in startIndex..=9 {
sum += i;
path.push(i);
Self::backtracking(result, path, targetSum, k, sum, i+1);
sum -= i;
path.pop();
}
}
pub fn combination_sum3(k: i32, n: i32) -> Vec<Vec<i32>> {
let mut result: Vec<Vec<i32>> = Vec::new();
let mut path: Vec<i32> = Vec::new();
Self::backtracking(&mut result, &mut path, n, k, 0, 1);
result
}
}
```
## C
```c

View File

@@ -900,6 +900,41 @@ class MyStack() {
```
C#:
```csharp
public class MyStack {
Queue<int> queue1;
Queue<int> queue2;
public MyStack() {
queue1 = new Queue<int>();
queue2 = new Queue<int>();
}
public void Push(int x) {
queue2.Enqueue(x);
while(queue1.Count != 0){
queue2.Enqueue(queue1.Dequeue());
}
Queue<int> queueTemp;
queueTemp = queue1;
queue1 = queue2;
queue2 = queueTemp;
}
public int Pop() {
return queue1.Count > 0 ? queue1.Dequeue() : -1;
}
public int Top() {
return queue1.Count > 0 ? queue1.Peek() : -1;
}
public bool Empty() {
return queue1.Count == 0;
}
}
```
PHP
> 双对列
```php

View File

@@ -470,25 +470,14 @@ func invertTree(root *TreeNode) *TreeNode {
使用递归版本的前序遍历
```javascript
var invertTree = function(root) {
//1. 首先使用递归版本的前序遍历实现二叉树翻转
//交换节点函数
const inverNode=function(left,right){
let temp=left;
left=right;
right=temp;
//需要重新给root赋值一下
root.left=left;
root.right=right;
// 终止条件
if (!root) {
return null;
}
//确定递归函数的参数和返回值inverTree=function(root)
//确定终止条件
if(root===null){
return root;
}
//确定节点处理逻辑 交换
inverNode(root.left,root.right);
invertTree(root.left);
invertTree(root.right);
// 交换左右节点
const rightNode = root.right;
root.right = invertTree(root.left);
root.left = invertTree(rightNode);
return root;
};
```

View File

@@ -497,6 +497,49 @@ void myQueueFree(MyQueue* obj) {
```
C#:
```csharp
public class MyQueue {
Stack<int> inStack;
Stack<int> outStack;
public MyQueue() {
inStack = new Stack<int>();// 负责进栈
outStack = new Stack<int>();// 负责出栈
}
public void Push(int x) {
inStack.Push(x);
}
public int Pop() {
dumpstackIn();
return outStack.Pop();
}
public int Peek() {
dumpstackIn();
return outStack.Peek();
}
public bool Empty() {
return inStack.Count == 0 && outStack.Count == 0;
}
// 处理方法:
// 如果outStack为空那么将inStack中的元素全部放到outStack中
private void dumpstackIn(){
if (outStack.Count != 0) return;
while(inStack.Count != 0){
outStack.Push(inStack.Pop());
}
}
}
```
PHP:
```php
// SplStack 类通过使用一个双向链表来提供栈的主要功能。[PHP 5 >= 5.3.0, PHP 7, PHP 8]
@@ -583,5 +626,6 @@ class MyQueue() {
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -218,65 +218,115 @@ class Solution {
```python
#数组模拟
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
length = 0
tmp = head
while tmp: #求链表长度
length += 1
tmp = tmp.next
result = [0] * length
tmp = head
index = 0
while tmp: #链表元素加入数组
result[index] = tmp.val
index += 1
tmp = tmp.next
i, j = 0, length - 1
while i < j: # 判断回文
if result[i] != result[j]:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
list=[]
while head:
list.append(head.val)
head=head.next
l,r=0, len(list)-1
while l<=r:
if list[l]!=list[r]:
return False
i += 1
j -= 1
return True
l+=1
r-=1
return True
#反转后半部分链表
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head == None or head.next == None:
return True
slow, fast = head, head
while fast and fast.next:
pre = slow
slow = slow.next
fast = fast.next.next
pre.next = None # 分割链表
cur1 = head # 前半部分
cur2 = self.reverseList(slow) # 反转后半部分总链表长度如果是奇数cur2比cur1多一个节点
while cur1:
if cur1.val != cur2.val:
return False
cur1 = cur1.next
cur2 = cur2.next
return True
def isPalindrome(self, head: Optional[ListNode]) -> bool:
fast = slow = head
def reverseList(self, head: ListNode) -> ListNode:
cur = head
pre = None
while(cur!=None):
temp = cur.next # 保存一下cur的下一个节点
cur.next = pre # 反转
pre = cur
cur = temp
return pre
# find mid point which including (first) mid point into the first half linked list
while fast and fast.next:
fast = fast.next.next
slow = slow.next
node = None
# reverse second half linked list
while slow:
slow.next, slow, node = node, slow.next, slow
# compare reversed and original half; must maintain reversed linked list is shorter than 1st half
while node:
if node.val != head.val:
return False
node = node.next
head = head.next
return True
```
## Go
### Go
```go
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
//方法一,使用数组
func isPalindrome(head *ListNode) bool{
//计算切片长度,避免切片频繁扩容
cur,ln:=head,0
for cur!=nil{
ln++
cur=cur.Next
}
nums:=make([]int,ln)
index:=0
for head!=nil{
nums[index]=head.Val
index++
head=head.Next
}
//比较回文切片
for i,j:=0,ln-1;i<=j;i,j=i+1,j-1{
if nums[i]!=nums[j]{return false}
}
return true
}
// 方法二,快慢指针
func isPalindrome(head *ListNode) bool {
if head==nil&&head.Next==nil{return true}
//慢指针,找到链表中间分位置,作为分割
slow:=head
fast:=head
//记录慢指针的前一个节点,用来分割链表
pre:=head
for fast!=nil && fast.Next!=nil{
pre=slow
slow=slow.Next
fast=fast.Next.Next
}
//分割链表
pre.Next=nil
//前半部分
cur1:=head
//反转后半部分总链表长度如果是奇数cur2比cur1多一个节点
cur2:=ReverseList(slow)
//开始两个链表的比较
for cur1!=nil{
if cur1.Val!=cur2.Val{return false}
cur1=cur1.Next
cur2=cur2.Next
}
return true
}
//反转链表
func ReverseList(head *ListNode) *ListNode{
var pre *ListNode
cur:=head
for cur!=nil{
tmp:=cur.Next
cur.Next=pre
pre=cur
cur=tmp
}
return pre
}
```
### JavaScript
@@ -319,72 +369,61 @@ var isPalindrome = function(head) {
};
```
### TypeScript
> 数组模拟
```typescript
function isPalindrome(head: ListNode | null): boolean {
// 方法1利用栈结构 将所有元素添加到栈中
// 声明栈
let stack:number[] = []
// 保存一份头结点 后续需要移动头结点
let dummy:ListNode = head
// 遍历链表将所有元素添加到数栈中
while(dummy){
stack.push(dummy.val)
dummy = dummy.next
const helperArr: number[] = [];
let curNode: ListNode | null = head;
while (curNode !== null) {
helperArr.push(curNode.val);
curNode = curNode.next;
}
// 利用栈结构的先进先出后出的特性判断是否会文
while(head){
if(head.val!== stack.pop()) return false
head = head.next
let left: number = 0,
right: number = helperArr.length - 1;
while (left < right) {
if (helperArr[left++] !== helperArr[right--]) return false;
}
return true
// 方法2翻转后半部分链表
// 封装翻转链表函数
function reverseList(node:ListNode){
let pre:ListNode = null
while(node){
// 借助第三个变量 进行翻转 有疑问可以看链表篇(https://www.programmercarl.com/链表理论基础.html#单链表)
let temp:ListNode = node.next
node.next = pre
pre = node
node = temp
}
return pre
}
// 快慢指针 需要注意的我们需要保证slow指针指向在要分隔节点的上一个节点因为是单链表无法找到链表前面的节点(也可以使用js代码的方法新增个变量用去记录前一个节点)
let fast:ListNode = head.next
let slow:ListNode = head
while(fast){
fast = fast.next
//只有fast有值的情况下才继续移动 不然代码会出现异常
if(fast) {
fast = fast.next
slow = slow.next
}
}
// 分隔链表
let list2Head:ListNode = slow.next
// 断开slow节点的next指针
slow.next = null
// 将后半部分进行翻转
let list2 = reverseList(list2Head)
// 最后进行迭代对比节点的值即可
while(head && list2){
if(head.val != list2.val) return false
head = head.next
list2 = list2.next
}
return true
return true;
};
```
> 反转后半部分链表
```typescript
function isPalindrome(head: ListNode | null): boolean {
if (head === null || head.next === null) return true;
let fastNode: ListNode | null = head,
slowNode: ListNode = head,
preNode: ListNode = head;
while (fastNode !== null && fastNode.next !== null) {
preNode = slowNode;
slowNode = slowNode.next!;
fastNode = fastNode.next.next;
}
preNode.next = null;
let cur1: ListNode | null = head;
let cur2: ListNode | null = reverseList(slowNode);
while (cur1 !== null) {
if (cur1.val !== cur2!.val) return false;
cur1 = cur1.next;
cur2 = cur2!.next;
}
return true;
};
function reverseList(head: ListNode | null): ListNode | null {
let curNode: ListNode | null = head,
preNode: ListNode | null = null;
while (curNode !== null) {
let tempNode: ListNode | null = curNode.next;
curNode.next = preNode;
preNode = curNode;
curNode = tempNode;
}
return preNode;
}
```

View File

@@ -756,5 +756,46 @@ class MyQueue{
}
```
C#:
```csharp
class myDequeue{
private LinkedList<int> linkedList = new LinkedList<int>();
public void Enqueue(int n){
while(linkedList.Count > 0 && linkedList.Last.Value < n){
linkedList.RemoveLast();
}
linkedList.AddLast(n);
}
public int Max(){
return linkedList.First.Value;
}
public void Dequeue(int n){
if(linkedList.First.Value == n){
linkedList.RemoveFirst();
}
}
}
myDequeue window = new myDequeue();
List<int> res = new List<int>();
public int[] MaxSlidingWindow(int[] nums, int k) {
for(int i = 0; i < k; i++){
window.Enqueue(nums[i]);
}
res.Add(window.Max());
for(int i = k; i < nums.Length; i++){
window.Dequeue(nums[i-k]);
window.Enqueue(nums[i]);
res.Add(window.Max());
}
return res.ToArray();
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -27,6 +27,8 @@
## 思路
本题B站视频讲解版[学透哈希表数组使用有技巧Leetcode242.有效的字母异位词](https://www.bilibili.com/video/BV1YG411p7BA)
先看暴力的解法两层for循环同时还要记录字符是否重复出现很明显时间复杂度是 O(n^2)。
暴力的方法这里就不做介绍了,直接看一下有没有更优的方式。
@@ -125,8 +127,6 @@ class Solution:
if record[i] != 0:
#record数组如果有的元素不为零0说明字符串s和t 一定是谁多了字符或者谁少了字符。
return False
#如果有一个元素不为零则可以判断字符串s和t不是字母异位词
break
return True
```

View File

@@ -44,6 +44,8 @@ dp[i][j]第i天状态为j所剩的最多现金为dp[i][j]。
* 状态三:今天卖出了股票
* 状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!
![](https://img-blog.csdnimg.cn/518d5baaf33f4b2698064f8efb42edbf.png)
j的状态为
* 0状态一
@@ -57,7 +59,7 @@ j的状态为
**注意这里的每一个状态,例如状态一,是买入股票状态并不是说今天已经就买入股票,而是说保存买入股票的状态即:可能是前几天买入的,之后一直没操作,所以保持买入股票的状态**
2. 确定递推公式
1. 确定递推公式
达到买入股票状态状态一dp[i][0],有两个具体操作:

View File

@@ -335,5 +335,22 @@ int integerBreak(int n){
}
```
### Scala
```scala
object Solution {
def integerBreak(n: Int): Int = {
var dp = new Array[Int](n + 1)
dp(2) = 1
for (i <- 3 to n) {
for (j <- 1 until i - 1) {
dp(i) = math.max(dp(i), math.max(j * (i - j), j * dp(i - j)))
}
}
dp(n)
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -190,13 +190,13 @@ javaScript:
* @return {void} Do not return anything, modify s in-place instead.
*/
var reverseString = function(s) {
return s.reverse();
//Do not return anything, modify s in-place instead.
reverse(s)
};
var reverseString = function(s) {
var reverse = function(s) {
let l = -1, r = s.length;
while(++l < --r) [s[l], s[r]] = [s[r], s[l]];
return s;
};
```
@@ -238,6 +238,22 @@ func reverseString(_ s: inout [Character]) {
```
Rust:
```Rust
impl Solution {
pub fn reverse_string(s: &mut Vec<char>) {
let (mut left, mut right) = (0, s.len()-1);
while left < right {
let temp = s[left];
s[left] = s[right];
s[right] = temp;
left += 1;
right -= 1;
}
}
}
```
C:
```c
void reverseString(char* s, int sSize){

View File

@@ -141,13 +141,10 @@ class Solution {
}
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
// 根据map的value值正序排,相当于一个小顶堆
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
// 根据map的value值构建于一个大顶堆o1 - o2: 小顶堆, o2 - o1 : 大顶堆)
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o2.getValue() - o1.getValue());
for (Map.Entry<Integer, Integer> entry : entries) {
queue.offer(entry);
if (queue.size() > k) {
queue.poll();
}
}
for (int i = k - 1; i >= 0; i--) {
result[i] = queue.poll().getKey();
@@ -374,6 +371,44 @@ function topKFrequent(nums: number[], k: number): number[] {
};
```
C#:
```csharp
public int[] TopKFrequent(int[] nums, int k) {
//哈希表-标权重
Dictionary<int,int> dic = new();
for(int i = 0; i < nums.Length; i++){
if(dic.ContainsKey(nums[i])){
dic[nums[i]]++;
}else{
dic.Add(nums[i], 1);
}
}
//优先队列-从小到大排列
PriorityQueue<int,int> pq = new();
foreach(var num in dic){
pq.Enqueue(num.Key, num.Value);
if(pq.Count > k){
pq.Dequeue();
}
}
// //Stack-倒置
// Stack<int> res = new();
// while(pq.Count > 0){
// res.Push(pq.Dequeue());
// }
// return res.ToArray();
//数组倒装
int[] res = new int[k];
for(int i = k - 1; i >= 0; i--){
res[i] = pq.Dequeue();
}
return res;
}
```
Scala:
解法一: 优先级队列
@@ -416,6 +451,7 @@ object Solution {
.map(_._1)
}
}
```
-----------------------

View File

@@ -24,6 +24,8 @@
## 思路
关于本题,我录制了讲解视频:[学透哈希表set使用有技巧Leetcode349. 两个数组的交集](https://www.bilibili.com/video/BV1ba411S7wu),看视频配合题解,事半功倍。
这道题目主要要学会使用一种哈希数据结构unordered_set这个数据结构可以解决很多类似的问题。
注意题目特意说明:**输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序**
@@ -48,7 +50,8 @@ std::set和std::multiset底层实现都是红黑树std::unordered_set的底
思路如图所示:
![set哈希法](https://img-blog.csdnimg.cn/2020080918570417.png)
![set哈希法](https://code-thinking-1253855093.file.myqcloud.com/pics/20220707173513.png)
C++代码如下:
@@ -56,7 +59,7 @@ C++代码如下:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 存放结果
unordered_set<int> result_set; // 存放结果之所以用set是为了给结果集去重
unordered_set<int> nums_set(nums1.begin(), nums1.end());
for (int num : nums2) {
// 发现nums2的元素 在nums_set里又出现过
@@ -77,6 +80,36 @@ public:
不要小瞧 这个耗时,在数据量大的情况,差距是很明显的。
## 后记
本题后面 力扣改了 题目描述 和 后台测试数据,增添了 数值范围:
* 1 <= nums1.length, nums2.length <= 1000
* 0 <= nums1[i], nums2[i] <= 1000
所以就可以 使用数组来做哈希表了, 因为数组都是 1000以内的。
对应C++代码如下:
```c++
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 存放结果之所以用set是为了给结果集去重
int hash[1005] = {0}; // 默认数值为0
for (int num : nums1) { // nums1中出现的字母在hash数组中做记录
hash[num] = 1;
}
for (int num : nums2) { // nums2中出现话result记录
if (hash[num] == 1) {
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
```
## 其他语言版本
@@ -104,13 +137,8 @@ class Solution {
resSet.add(i);
}
}
int[] resArr = new int[resSet.size()];
int index = 0;
//将结果几何转为数组
for (int i : resSet) {
resArr[index++] = i;
}
return resArr;
return resSet.stream().mapToInt(x -> x).toArray();
}
}
```

View File

@@ -88,8 +88,8 @@ public:
};
```
* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$
* 时间复杂度O(n)
* 空间复杂度O(1)
## 思路2动态规划
@@ -138,8 +138,8 @@ public:
};
```
* 时间复杂度:$O(n^2)$
* 空间复杂度:$O(n)$
* 时间复杂度O(n^2)
* 空间复杂度O(n)
**进阶**
@@ -149,9 +149,9 @@ public:
* 每次更新`dp[i][1]`,则在`tree2`的`nums[i]`位置值更新为`dp[i][1]`
* 则dp转移方程中就没有必要j从0遍历到i-1可以直接在线段树中查询指定区间的值即可。
时间复杂度:$O(n\log n)$
时间复杂度O(nlog n)
空间复杂度:$O(n)$
空间复杂度O(n)
## 总结
@@ -228,6 +228,8 @@ class Solution {
### Python
**贪心**
```python
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
@@ -240,7 +242,30 @@ class Solution:
return res
```
**动态规划**
```python
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
# 0 i 作为波峰的最大长度
# 1 i 作为波谷的最大长度
# dp是一个列表列表中每个元素是长度为 2 的列表
dp = []
for i in range(len(nums)):
# 初始为[1, 1]
dp.append([1, 1])
for j in range(i):
# nums[i] 为波谷
if nums[j] > nums[i]:
dp[i][1] = max(dp[i][1], dp[j][0] + 1)
# nums[i] 为波峰
if nums[j] < nums[i]:
dp[i][0] = max(dp[i][0], dp[j][1] + 1)
return max(dp[-1][0], dp[-1][1])
```
### Go
```golang
func wiggleMaxLength(nums []int) int {
var count,preDiff,curDiff int
@@ -298,9 +323,33 @@ var wiggleMaxLength = function(nums) {
};
```
### Rust
**贪心**
```Rust
impl Solution {
pub fn wiggle_max_length(nums: Vec<i32>) -> i32 {
let len = nums.len() as usize;
if len <= 1 {
return len as i32;
}
let mut preDiff = 0;
let mut curDiff = 0;
let mut result = 1;
for i in 0..len-1 {
curDiff = nums[i+1] - nums[i];
if (preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0) {
result += 1;
preDiff = curDiff;
}
}
result
}
}
```
### C
**贪心**
```c
int wiggleMaxLength(int* nums, int numsSize){
if(numsSize <= 1)
@@ -326,6 +375,44 @@ int wiggleMaxLength(int* nums, int numsSize){
}
```
**动态规划**
```c
int max(int left, int right)
{
return left > right ? left : right;
}
int wiggleMaxLength(int* nums, int numsSize){
if(numsSize <= 1)
{
return numsSize;
}
// 0 i 作为波峰的最大长度
// 1 i 作为波谷的最大长度
int dp[numsSize][2];
for(int i = 0; i < numsSize; i++)
{
dp[i][0] = 1;
dp[i][1] = 1;
for(int j = 0; j < i; j++)
{
// nums[i] 为山谷
if(nums[j] > nums[i])
{
dp[i][1] = max(dp[i][1], dp[j][0] + 1);
}
// nums[i] 为山峰
if(nums[j] < nums[i])
{
dp[i][0] = max(dp[i][0], dp[j][1] + 1);
}
}
}
return max(dp[numsSize - 1][0], dp[numsSize - 1][1]);
}
```
### TypeScript
@@ -375,7 +462,31 @@ function wiggleMaxLength(nums: number[]): number {
};
```
### Scala
```scala
object Solution {
def wiggleMaxLength(nums: Array[Int]): Int = {
if (nums.length <= 1) return nums.length
var result = 1
var curDiff = 0 // 当前一对的差值
var preDiff = 0 // 前一对的差值
for (i <- 1 until nums.length) {
curDiff = nums(i) - nums(i - 1) // 计算当前这一对的差值
// 当 curDiff > 0 的情况preDiff <= 0
// 当 curDiff < 0 的情况preDiff >= 0
// 这两种情况算是两个峰值
if ((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)) {
result += 1 // 结果集加 1
preDiff = curDiff // 当前差值赋值给上一轮
}
}
result
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -31,7 +31,7 @@
## 思路
(这道题可以用双指针的思路来实现,时间复杂度就是$O(n)$
这道题可以用双指针的思路来实现时间复杂度就是O(n)
这道题应该算是编辑距离的入门题目,因为从题意中我们也可以发现,只需要计算删除的情况,不用考虑增加和替换的情况。
@@ -122,8 +122,8 @@ public:
};
```
* 时间复杂度:$O(n × m)$
* 空间复杂度:$O(n × m)$
* 时间复杂度O(n × m)
* 空间复杂度O(n × m)
## 总结

View File

@@ -354,8 +354,27 @@ function reconstructQueue(people: number[][]): number[][] {
};
```
### Scala
```scala
object Solution {
import scala.collection.mutable
def reconstructQueue(people: Array[Array[Int]]): Array[Array[Int]] = {
val person = people.sortWith((a, b) => {
if (a(0) == b(0)) a(1) < b(1)
else a(0) > b(0)
})
var que = mutable.ArrayBuffer[Array[Int]]()
for (per <- person) {
que.insert(per(1), per)
}
que.toArray
}
}
```
-----------------------

View File

@@ -183,7 +183,7 @@ public:
## 其他语言版本
Java
### Java
```Java
class Solution {
public boolean canPartition(int[] nums) {
@@ -316,7 +316,7 @@ class Solution {
}
}
```
Python
### Python
```python
class Solution:
def canPartition(self, nums: List[int]) -> bool:
@@ -329,7 +329,7 @@ class Solution:
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
return target == dp[target]
```
Go
### Go
```go
// 分割等和子集 动态规划
// 时间复杂度O(n^2) 空间复杂度O(n)
@@ -397,7 +397,7 @@ func canPartition(nums []int) bool {
}
```
javaScript:
### javaScript:
```js
var canPartition = function(nums) {
@@ -417,27 +417,11 @@ var canPartition = function(nums) {
```
TypeScript:
```ts
function canPartition(nums: number[]): boolean {
const sum: number = nums.reduce((a: number, b: number): number => a + b);
if (sum % 2 === 1) return false;
const target: number = sum / 2;
// dp[j]表示容量总数和为j的背包所能装下的数下标[0, i]之间任意取)的总和(<= 容量)的最大值
const dp: number[] = new Array(target + 1).fill(0);
const n: number = nums.length;
for (let i: number = 0; i < n; i++) {
for (let j: number = target; j >= nums[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
return dp[target] === target;
};
```
C:
### C:
二维dp
```c
/**
@@ -538,7 +522,7 @@ bool canPartition(int* nums, int numsSize){
}
```
TypeScript:
### TypeScript:
> 一维数组,简洁
@@ -593,7 +577,50 @@ function canPartition(nums: number[]): boolean {
};
```
### Scala
滚动数组:
```scala
object Solution {
def canPartition(nums: Array[Int]): Boolean = {
var sum = nums.sum
if (sum % 2 != 0) return false
var half = sum / 2
var dp = new Array[Int](half + 1)
// 遍历
for (i <- 0 until nums.length; j <- half to nums(i) by -1) {
dp(j) = math.max(dp(j), dp(j - nums(i)) + nums(i))
}
if (dp(half) == half) true else false
}
}
```
二维数组:
```scala
object Solution {
def canPartition(nums: Array[Int]): Boolean = {
var sum = nums.sum
if (sum % 2 != 0) return false
var half = sum / 2
var dp = Array.ofDim[Int](nums.length, half + 1)
// 初始化
for (j <- nums(0) to half) dp(0)(j) = nums(0)
// 遍历
for (i <- 1 until nums.length; j <- 1 to half) {
if (j - nums(i) >= 0) dp(i)(j) = nums(i) + dp(i - 1)(j - nums(i))
dp(i)(j) = math.max(dp(i)(j), dp(i - 1)(j))
}
// 如果等于half就返回ture否则返回false
if (dp(nums.length - 1)(half) == half) true else false
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -352,7 +352,27 @@ function eraseOverlapIntervals(intervals: number[][]): number {
};
```
### Scala
```scala
object Solution {
def eraseOverlapIntervals(intervals: Array[Array[Int]]): Int = {
var result = 0
var interval = intervals.sortWith((a, b) => {
a(1) < b(1)
})
var edge = Int.MinValue
for (i <- 0 until interval.length) {
if (edge <= interval(i)(0)) {
edge = interval(i)(1)
} else {
result += 1
}
}
result
}
}
```

View File

@@ -456,31 +456,42 @@ func deleteNode(root *TreeNode, key int) *TreeNode {
* @param {number} key
* @return {TreeNode}
*/
var deleteNode = function (root, key) {
if (root === null)
return root;
if (root.val === key) {
if (!root.left)
return root.right;
else if (!root.right)
return root.left;
else {
let cur = root.right;
while (cur.left) {
cur = cur.left;
}
cur.left = root.left;
root = root.right;
delete root;
return root;
}
}
if (root.val > key)
root.left = deleteNode(root.left, key);
if (root.val < key)
var deleteNode = function(root, key) {
if (!root) return null;
if (key > root.val) {
root.right = deleteNode(root.right, key);
return root;
return root;
} else if (key < root.val) {
root.left = deleteNode(root.left, key);
return root;
} else {
// 场景1: 该节点是叶节点
if (!root.left && !root.right) {
return null
}
// 场景2: 有一个孩子节点不存在
if (root.left && !root.right) {
return root.left;
} else if (root.right && !root.left) {
return root.right;
}
// 场景3: 左右节点都存在
const rightNode = root.right;
// 获取最小值节点
const minNode = getMinNode(rightNode);
// 将待删除节点的值替换为最小值节点值
root.val = minNode.val;
// 删除最小值节点
root.right = deleteNode(root.right, minNode.val);
return root;
}
};
function getMinNode(root) {
while (root.left) {
root = root.left;
}
return root;
}
```
迭代

View File

@@ -136,17 +136,29 @@ public:
### Java
```java
/**
时间复杂度 : O(NlogN) 排序需要 O(NlogN) 的复杂度
空间复杂度 : O(logN) java所使用的内置函数用的是快速排序需要 logN 的空间
*/
class Solution {
public int findMinArrowShots(int[][] points) {
if (points.length == 0) return 0;
Arrays.sort(points, (o1, o2) -> Integer.compare(o1[0], o2[0]));
//用x[0] - y[0] 会大于2147483647 造成整型溢出
Arrays.sort(points, (x, y) -> Integer.compare(x[0], y[0]));
//count = 1 因为最少需要一个箭来射击第一个气球
int count = 1;
for (int i = 1; i < points.length; i++) {
if (points[i][0] > points[i - 1][1]) {
//重叠气球的最小右边界
int leftmostRightBound = points[0][1];
//如果下一个气球的左边界大于最小右边界
for(int i = 1; i < points.length; i++){
if (points[i][0] > leftmostRightBound ) {
//增加一次射击
count++;
leftmostRightBound = points[i][1];
//不然就更新最小右边界
} else {
points[i][1] = Math.min(points[i][1],points[i - 1][1]);
leftmostRightBound = Math.min(leftmostRightBound , points[i][1]);
}
}
return count;
@@ -288,5 +300,30 @@ impl Solution {
}
}
```
### Scala
```scala
object Solution {
def findMinArrowShots(points: Array[Array[Int]]): Int = {
if (points.length == 0) return 0
// 排序
var point = points.sortWith((a, b) => {
a(0) < b(0)
})
var result = 1 // points不为空就至少需要一只箭
for (i <- 1 until point.length) {
if (point(i)(0) > point(i - 1)(1)) {
result += 1
} else {
point(i)(1) = math.min(point(i - 1)(1), point(i)(1))
}
}
result // 返回结果
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -18,14 +18,19 @@
**例如:**
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
* A = [ 1, 2]
* B = [-2,-1]
* C = [-1, 2]
* D = [ 0, 2]
输出:
2
**解释:**
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

View File

@@ -296,5 +296,26 @@ int findContentChildren(int* g, int gSize, int* s, int sSize){
}
```
### Scala
```scala
object Solution {
def findContentChildren(g: Array[Int], s: Array[Int]): Int = {
var result = 0
var children = g.sorted
var cookie = s.sorted
// 遍历饼干
var j = 0
for (i <- cookie.indices) {
if (j < children.size && cookie(i) >= children(j)) {
j += 1
result += 1
}
}
result
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -49,7 +49,7 @@
数组长度为len。
如果len % (len - (next[len - 1] + 1)) == 0 ,则说明 (数组长度-最长相等前后缀的长度) 正好可以被 数组的长度整除,说明该字符串有重复的子字符串。
如果len % (len - (next[len - 1] + 1)) == 0 ,则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。
**数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。**
@@ -63,7 +63,7 @@
next[len - 1] = 7next[len - 1] + 1 = 88就是此时字符串asdfasdfasdf的最长相同前后缀的长度。
(len - (next[len - 1] + 1)) 也就是: 12(字符串的长度) - 8(最长公共前后缀的长度) = 4 4正好可以被 12(字符串的长度) 整除所以说明有重复的子字符串asdf
(len - (next[len - 1] + 1)) 也就是: 12(字符串的长度) - 8(最长公共前后缀的长度) = 4 12 正好可以被 4 整除所以说明有重复的子字符串asdf
C++代码如下:(这里使用了前缀表统一减一的实现方式)
@@ -505,5 +505,37 @@ Swift:
}
```
Rust:
>前缀表统一不减一
```Rust
impl Solution {
pub fn get_next(next: &mut Vec<usize>, s: &Vec<char>) {
let len = s.len();
let mut j = 0;
for i in 1..len {
while j > 0 && s[i] != s[j] {
j = next[j - 1];
}
if s[i] == s[j] {
j += 1;
}
next[i] = j;
}
}
pub fn repeated_substring_pattern(s: String) -> bool {
let s = s.chars().collect::<Vec<char>>();
let len = s.len();
if len == 0 { return false; };
let mut next = vec![0; len];
Self::get_next(&mut next, &s);
if next[len - 1] != 0 && len % (len - (next[len - 1] )) == 0 { return true; }
return false;
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -163,7 +163,7 @@ public:
## 其他语言版本
Java
### Java
```Java
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
@@ -192,7 +192,7 @@ class Solution {
}
```
Python
### Python
```python
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
@@ -208,7 +208,7 @@ class Solution:
return dp[m][n]
```
Go
### Go
```go
func findMaxForm(strs []string, m int, n int) int {
// 定义数组
@@ -294,7 +294,7 @@ func getMax(a,b int)int{
}
```
Javascript
### Javascript
```javascript
const findMaxForm = (strs, m, n) => {
const dp = Array.from(Array(m+1), () => Array(n+1).fill(0));
@@ -323,7 +323,7 @@ const findMaxForm = (strs, m, n) => {
};
```
TypeScript
### TypeScript
> 滚动数组,二维数组法
@@ -446,7 +446,80 @@ function isValidSubSet(strs: string[], m: number, n: number): boolean {
}
```
### Scala
背包:
```scala
object Solution {
def findMaxForm(strs: Array[String], m: Int, n: Int): Int = {
var dp = Array.ofDim[Int](m + 1, n + 1)
var (oneNum, zeroNum) = (0, 0)
for (str <- strs) {
oneNum = 0
zeroNum = 0
for (i <- str.indices) {
if (str(i) == '0') zeroNum += 1
else oneNum += 1
}
for (i <- m to zeroNum by -1) {
for (j <- n to oneNum by -1) {
dp(i)(j) = math.max(dp(i)(j), dp(i - zeroNum)(j - oneNum) + 1)
}
}
}
dp(m)(n)
}
}
```
回溯法(超时)
```scala
object Solution {
import scala.collection.mutable
var res = Int.MinValue
def test(str: String): (Int, Int) = {
var (zero, one) = (0, 0)
for (i <- str.indices) {
if (str(i) == '1') one += 1
else zero += 1
}
(zero, one)
}
def travsel(strs: Array[String], path: mutable.ArrayBuffer[String], m: Int, n: Int, startIndex: Int): Unit = {
if (startIndex > strs.length) {
return
}
res = math.max(res, path.length)
for (i <- startIndex until strs.length) {
var (zero, one) = test(strs(i))
// 如果0的个数小于m1的个数小于n则可以回溯
if (zero <= m && one <= n) {
path.append(strs(i))
travsel(strs, path, m - zero, n - one, i + 1)
path.remove(path.length - 1)
}
}
}
def findMaxForm(strs: Array[String], m: Int, n: Int): Int = {
res = Int.MinValue
var path = mutable.ArrayBuffer[String]()
travsel(strs, path, m, n, 0)
res
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -522,5 +522,39 @@ func findSubsequences(_ nums: [Int]) -> [[Int]] {
```
## Scala
```scala
object Solution {
import scala.collection.mutable
def findSubsequences(nums: Array[Int]): List[List[Int]] = {
var result = mutable.ListBuffer[List[Int]]()
var path = mutable.ListBuffer[Int]()
def backtracking(startIndex: Int): Unit = {
// 集合元素大于1添加到结果集
if (path.size > 1) {
result.append(path.toList)
}
var used = new Array[Boolean](201)
// 使用循环守卫,当前层没有用过的元素才有资格进入回溯
for (i <- startIndex until nums.size if !used(nums(i) + 100)) {
// 如果path没元素或 当前循环的元素比path的最后一个元素大则可以进入回溯
if (path.size == 0 || (!path.isEmpty && nums(i) >= path(path.size - 1))) {
used(nums(i) + 100) = true
path.append(nums(i))
backtracking(i + 1)
path.remove(path.size - 1)
}
}
}
backtracking(0)
result.toList
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -156,9 +156,9 @@ dp[j] 表示填满j包括j这么大容积的包有dp[j]种方法
有哪些来源可以推出dp[j]呢?
不考虑nums[i]的情况下填满容量为j - nums[i]的背包有dp[j - nums[i]]种方法。
不考虑nums[i]的情况下填满容量为j的背包有dp[j]种方法。
那么只要搞到nums[i]的话凑成dp[j]就有dp[j - nums[i]] 种方法。
那么考虑nums[i]的话(只要搞到nums[i]凑成dp[j]就有dp[j - nums[i]] 种方法。
例如dp[j]j 为5
@@ -213,6 +213,7 @@ public:
if (abs(S) > sum) return 0; // 此时没有方案
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
int bagSize = (S + sum) / 2;
if (bagsize < 0) return 0;
vector<int> dp(bagSize + 1, 0);
dp[0] = 1;
for (int i = 0; i < nums.size(); i++) {
@@ -250,7 +251,7 @@ dp[j] += dp[j - nums[i]];
## 其他语言版本
Java
### Java
```java
class Solution {
public int findTargetSumWays(int[] nums, int target) {
@@ -271,7 +272,7 @@ class Solution {
}
```
Python
### Python
```python
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
@@ -287,7 +288,7 @@ class Solution:
return dp[bagSize]
```
Go
### Go
```go
func findTargetSumWays(nums []int, target int) int {
sum := 0
@@ -322,7 +323,7 @@ func abs(x int) int {
}
```
Javascript
### Javascript
```javascript
const findTargetSumWays = (nums, target) => {
@@ -352,6 +353,8 @@ const findTargetSumWays = (nums, target) => {
```
### TypeScript
TypeScript:
```ts
@@ -374,7 +377,25 @@ function findTargetSumWays(nums: number[], target: number): number {
};
```
### Scala
```scala
object Solution {
def findTargetSumWays(nums: Array[Int], target: Int): Int = {
var sum = nums.sum
if (math.abs(target) > sum) return 0 // 此时没有方案
if ((sum + target) % 2 == 1) return 0 // 此时没有方案
var bagSize = (sum + target) / 2
var dp = new Array[Int](bagSize + 1)
dp(0) = 1
for (i <- 0 until nums.length; j <- bagSize to nums(i) by -1) {
dp(j) += dp(j - nums(i))
}
dp(bagSize)
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -263,7 +263,7 @@ var fib = function(n) {
};
```
TypeScript
### TypeScript
```typescript
function fib(n: number): number {
@@ -342,5 +342,33 @@ pub fn fib(n: i32) -> i32 {
return fib(n - 1) + fib(n - 2);
}
```
### Scala
动态规划:
```scala
object Solution {
def fib(n: Int): Int = {
if (n <= 1) return n
var dp = new Array[Int](n + 1)
dp(1) = 1
for (i <- 2 to n) {
dp(i) = dp(i - 1) + dp(i - 2)
}
dp(n)
}
}
```
递归:
```scala
object Solution {
def fib(n: Int): Int = {
if (n <= 1) return n
fib(n - 1) + fib(n - 2)
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -289,7 +289,22 @@ function change(amount: number, coins: number[]): number {
};
```
Scala:
```scala
object Solution {
def change(amount: Int, coins: Array[Int]): Int = {
var dp = new Array[Int](amount + 1)
dp(0) = 1
for (i <- 0 until coins.length) {
for (j <- coins(i) to amount) {
dp(j) += dp(j - coins(i))
}
}
dp(amount)
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -63,6 +63,9 @@ public:
};
```
那么我们也可以实现自己的reverse函数其实和题目[344. 反转字符串](https://programmercarl.com/0344.反转字符串.html)道理是一样的。
下面我实现的reverse函数区间是左闭右闭区间代码如下
@@ -92,7 +95,24 @@ public:
```
另一种思路的解法
```CPP
class Solution {
public:
string reverseStr(string s, int k) {
int n = s.size(),pos = 0;
while(pos < n){
//剩余字符串大于等于k的情况
if(pos + k < n) reverse(s.begin() + pos, s.begin() + pos + k);
//剩余字符串不足k的情况
else reverse(s.begin() + pos,s.end());
pos += 2 * k;
}
return s;
}
};
```
@@ -389,5 +409,36 @@ object Solution {
}
}
```
Rust:
```Rust
impl Solution {
pub fn reverse(s: &mut Vec<char>, mut begin: usize, mut end: usize){
while begin < end {
let temp = s[begin];
s[begin] = s[end];
s[end] = temp;
begin += 1;
end -= 1;
}
}
pub fn reverse_str(s: String, k: i32) -> String {
let len = s.len();
let k = k as usize;
let mut s = s.chars().collect::<Vec<_>>();
for i in (0..len).step_by(2 * k) {
if i + k < len {
Self::reverse(&mut s, i, i + k - 1);
}
else {
Self::reverse(&mut s, i, len - 1);
}
}
s.iter().collect::<String>()
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -262,10 +262,10 @@ class Solution {
if (root1 == null) return root2;
if (root2 == null) return root1;
TreeNode newRoot = new TreeNode(root1.val + root2.val);
newRoot.left = mergeTrees(root1.left,root2.left);
newRoot.right = mergeTrees(root1.right,root2.right);
return newRoot;
root1.val += root2.val;
root1.left = mergeTrees(root1.left,root2.left);
root1.right = mergeTrees(root1.right,root2.right);
return root1;
}
}
```

View File

@@ -32,7 +32,7 @@
两层for循环遍历区间起始位置和终止位置然后判断这个区间是不是回文。
时间复杂度:$O(n^3)$
时间复杂度O(n^3)
## 动态规划
@@ -171,8 +171,8 @@ public:
};
```
* 时间复杂度:$O(n^2)$
* 空间复杂度:$O(n^2)$
* 时间复杂度O(n^2)
* 空间复杂度O(n^2)
## 双指针法
@@ -213,8 +213,8 @@ public:
};
```
* 时间复杂度:$O(n^2)$
* 空间复杂度:$O(1)$
* 时间复杂度O(n^2)
* 空间复杂度O(1)
## 其他语言版本

View File

@@ -244,6 +244,44 @@ var predictPartyVictory = function(senateStr) {
};
```
## TypeScript
```typescript
function predictPartyVictory(senate: string): string {
// 数量差Count(Radiant) - Count(Dire)
let deltaRDCnt: number = 0;
let hasR: boolean = true,
hasD: boolean = true;
const senateArr: string[] = senate.split('');
while (hasR && hasD) {
hasR = false;
hasD = false;
for (let i = 0, length = senateArr.length; i < length; i++) {
if (senateArr[i] === 'R') {
if (deltaRDCnt < 0) {
senateArr[i] = '';
} else {
hasR = true;
}
deltaRDCnt++;
} else if (senateArr[i] === 'D') {
if (deltaRDCnt > 0) {
senateArr[i] = '';
} else {
hasD = true;
}
deltaRDCnt--;
}
}
}
return hasR ? 'Radiant' : 'Dire';
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -36,7 +36,7 @@
## 思路
为了易于大家理解,我还录制了视频,可以看这里:[手把手带你撕出正确的二分法](https://www.bilibili.com/video/BV1fA4y1o715)
为了易于大家理解,我还录制了视频,可以看这里:[B站手把手带你撕出正确的二分法](https://www.bilibili.com/video/BV1fA4y1o715)
**这道题目的前提是数组为有序数组**,同时题目还强调**数组中无重复元素**,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。
@@ -220,19 +220,21 @@ class Solution:
(版本二)左闭右开区间
```python
class Solution:
```class Solution:
def search(self, nums: List[int], target: int) -> int:
left,right =0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid+1
elif nums[mid] > target:
right = mid
if nums is None or len(nums)==0:
return -1
l=0
r=len(nums)-1
while (l<=r):
m = round(l+(r-l)/2)
if nums[m] == target:
return m
elif nums[m] > target:
r=m-1
else:
return mid
return -1
l=m+1
return -1
```
**Go**

View File

@@ -26,6 +26,8 @@
# 思路
为了方便大家理解,我特意录制了视频:[帮你把链表操作学个通透LeetCode707.设计链表](https://www.bilibili.com/video/BV1FU4y1X7WD),结合视频在看本题解,事半功倍。
如果对链表的基础知识还不太懂,可以看这篇文章:[关于链表,你该了解这些!](https://programmercarl.com/链表理论基础.html)
如果对链表的虚拟头结点不清楚,可以看这篇文章:[链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html)

View File

@@ -153,7 +153,7 @@ public:
## 其他语言版本
Java
### Java
```java
// 贪心思路
class Solution {
@@ -198,7 +198,7 @@ class Solution { // 动态规划
Python
### Python
```python
class Solution: # 贪心思路
@@ -216,7 +216,7 @@ class Solution: # 贪心思路
return result
```
Go
### Go
```golang
func maxProfit(prices []int, fee int) int {
var minBuy int = prices[0] //第一天买入
@@ -241,7 +241,7 @@ func maxProfit(prices []int, fee int) int {
return res
}
```
Javascript:
### Javascript
```Javascript
// 贪心思路
var maxProfit = function(prices, fee) {
@@ -293,7 +293,7 @@ var maxProfit = function(prices, fee) {
};
```
TypeScript
### TypeScript
> 贪心
@@ -335,8 +335,28 @@ function maxProfit(prices: number[], fee: number): number {
};
```
### Scala
贪心思路:
```scala
object Solution {
def maxProfit(prices: Array[Int], fee: Int): Int = {
var result = 0
var minPrice = prices(0)
for (i <- 1 until prices.length) {
if (prices(i) < minPrice) {
minPrice = prices(i) // 比当前最小值还小
}
if (prices(i) > minPrice + fee) {
result += prices(i) - minPrice - fee
minPrice = prices(i) - fee
}
}
result
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -31,7 +31,7 @@ B: [3,2,1,4,7]
1. 确定dp数组dp table以及下标的含义
dp[i][j] 以下标i - 1为结尾的A和以下标j - 1为结尾的B最长重复子数组长度为dp[i][j]。
dp[i][j] 以下标i - 1为结尾的A和以下标j - 1为结尾的B最长重复子数组长度为dp[i][j]。 **特别注意** 以下标i - 1为结尾的A 标明一定是 以A[i-1]为结尾的字符串
此时细心的同学应该发现那dp[0][0]是什么含义呢总不能是以下标-1为结尾的A数组吧

View File

@@ -246,7 +246,37 @@ function monotoneIncreasingDigits(n: number): number {
```
### Scala
直接转换为了整数数组:
```scala
object Solution {
import scala.collection.mutable
def monotoneIncreasingDigits(n: Int): Int = {
var digits = mutable.ArrayBuffer[Int]()
// 提取每位数字
var temp = n // 因为 参数n 是不可变量所以需要赋值给一个可变量
while (temp != 0) {
digits.append(temp % 10)
temp = temp / 10
}
// 贪心
var flag = -1
for (i <- 0 until (digits.length - 1) if digits(i) < digits(i + 1)) {
flag = i
digits(i + 1) -= 1
}
for (i <- 0 to flag) digits(i) = 9
// 拼接
var res = 0
for (i <- 0 until digits.length) {
res += digits(i) * math.pow(10, i).toInt
}
res
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -288,6 +288,24 @@ function minCostClimbingStairs(cost: number[]): number {
};
```
### Rust
```Rust
use std::cmp::min;
impl Solution {
pub fn min_cost_climbing_stairs(cost: Vec<i32>) -> i32 {
let len = cost.len();
let mut dp = vec![0; len];
dp[0] = cost[0];
dp[1] = cost[1];
for i in 2..len {
dp[i] = min(dp[i-1], dp[i-2]) + cost[i];
}
min(dp[len-1], dp[len-2])
}
}
```
### C
```c
@@ -305,5 +323,35 @@ int minCostClimbingStairs(int* cost, int costSize){
return dp[i-1] < dp[i-2] ? dp[i-1] : dp[i-2];
}
```
### Scala
```scala
object Solution {
def minCostClimbingStairs(cost: Array[Int]): Int = {
var dp = new Array[Int](cost.length)
dp(0) = cost(0)
dp(1) = cost(1)
for (i <- 2 until cost.length) {
dp(i) = math.min(dp(i - 1), dp(i - 2)) + cost(i)
}
math.min(dp(cost.length - 1), dp(cost.length - 2))
}
}
```
第二种思路: dp[i] 表示爬到第i-1层所需的最小花费状态转移方程为: dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
```scala
object Solution {
def minCostClimbingStairs(cost: Array[Int]): Int = {
var dp = new Array[Int](cost.length + 1)
for (i <- 2 until cost.length + 1) {
dp(i) = math.min(dp(i - 1) + cost(i - 1), dp(i - 2) + cost(i - 2))
}
dp(cost.length)
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -317,7 +317,31 @@ function partitionLabels(s: string): number[] {
};
```
### Scala
```scala
object Solution {
import scala.collection.mutable
def partitionLabels(s: String): List[Int] = {
var hash = new Array[Int](26)
for (i <- s.indices) {
hash(s(i) - 'a') = i
}
var res = mutable.ListBuffer[Int]()
var (left, right) = (0, 0)
for (i <- s.indices) {
right = math.max(hash(s(i) - 'a'), right)
if (i == right) {
res.append(right - left + 1)
left = i + 1
}
}
res.toList
}
}
```
-----------------------

View File

@@ -0,0 +1,296 @@
看一下 算法4深搜是怎么讲的
# 797.所有可能的路径
本题是一道 原汁原味的 深度优先搜索dfs模板题那么用这道题目 来讲解 深搜最合适不过了。
接下来给大家详细讲解dfs
## dfs 与 bfs 区别
先来了解dfs的过程很多录友可能对dfs深度优先搜索bfs广度优先搜索分不清。
先给大家说一下两者大概的区别:
* dfs是可一个方向去搜不到黄河不回头直到遇到绝境了搜不下去了在换方向换方向的过程就涉及到了回溯
* bfs是先把本节点所连接的所有节点遍历一遍走到下一个节点的时候再把连接节点的所有节点遍历一遍搜索方向更像是广度四面八方的搜索过程。
当然以上讲的是,大体可以这么理解,接下来 我们详细讲解dfsbfs在用单独一篇文章详细讲解
## dfs 搜索过程
上面说道dfs是可一个方向搜不到黄河不回头。 那么我们来举一个例子。
如图一是一个无向图我们要搜索从节点1到节点6的所有路径。
![图一](https://code-thinking-1253855093.file.myqcloud.com/pics/20220707093643.png)
那么dfs搜索的第一条路径是这样的 假设第一次延默认方向就找到了节点6图二
![图二](https://code-thinking-1253855093.file.myqcloud.com/pics/20220707093807.png)
此时我们找到了节点6遇到黄河了是不是应该回头了那么应该再去搜索其他方向了。 如图三:
![图三](https://code-thinking-1253855093.file.myqcloud.com/pics/20220707094011.png)
路径2撤销了改变了方向走路径3红色线 接着也找到终点6。 那么撤销路径2改为路径3在dfs中其实就是回溯的过程这一点很重要很多录友都不理解dfs代码中回溯是用来干什么的
又找到了一条从节点1到节点6的路径又到黄河了此时再回头下图图四中路径4撤销回溯的过程改为路径5。
![图四](https://code-thinking-1253855093.file.myqcloud.com/pics/20220707094322.png)
又找到了一条从节点1到节点6的路径又到黄河了此时再回头下图图五路径6撤销回溯的过程改为路径7路径8 和 路径7路径9 结果发现死路一条,都走到了自己走过的节点。
![图五](https://code-thinking-1253855093.file.myqcloud.com/pics/20220707094813.png)
那么节点2所连接路径和节点3所链接的路径 都走过了撤销路径只能向上回退去选择撤销当初节点4的选择也就是撤销路径5改为路径10 。 如图图六:
![图六](https://code-thinking-1253855093.file.myqcloud.com/pics/20220707095232.png)
上图演示中,其实我并没有把 所有的 从节点1 到节点6的dfs深度优先搜索的过程都画出来那样太冗余了但 已经把dfs 关键的地方都涉及到了,关键就两点:
* 搜索方向,是认准一个方向搜,直到碰壁之后在换方向
* 换方向是撤销原路径,改为节点链接的下一个路径,回溯的过程。
## 代码框架
正式因为dfs搜索可一个方向并需要回溯所以用递归的方式来实现是最方便的。
很多录友对回溯很陌生,建议先看看码随想录,[回溯算法章节](https://programmercarl.com/回溯算法理论基础.html)。
有递归的地方就有回溯,那么回溯在哪里呢?
就地递归函数的下面,例如如下代码:
```
void dfs(参数) {
处理节点
dfs(图,选择的节点); // 递归
回溯,撤销处理结果
}
```
可以看到回溯操作就在递归函数的下面,递归和回溯是相辅相成的。
在讲解[二叉树章节](https://programmercarl.com/二叉树理论基础.html)的时候二叉树的递归法其实就是dfs而二叉树的迭代法就是bfs广度优先搜索
所以**dfsbfs其实是基础搜索算法也广泛应用与其他数据结构与算法中**。
我们在回顾一下[回溯法](https://programmercarl.com/回溯算法理论基础.html)的代码框架:
```
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
```
回溯算法其实就是dfs的过程这里给出dfs的代码框架
```
void dfs(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本节点所连接的其他节点) {
处理节点;
dfs(图,选择的节点); // 递归
回溯,撤销处理结果
}
}
```
可以发现dfs的代码框架和回溯算法的代码框架是差不多的。
下面我在用 深搜三部曲,来解读 dfs的代码框架。
## 深搜三部曲
在 [二叉树递归讲解](https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%80%92%E5%BD%92%E9%81%8D%E5%8E%86.html)中,给出了递归三部曲。
[回溯算法](https://programmercarl.com/回溯算法理论基础.html)讲解中,给出了 回溯三部曲。
其实深搜也是一样的,深搜三部曲如下:
1. 确认递归函数,参数
```
void dfs(参数)
```
通常我们递归的时候,我们递归搜索需要了解哪些参数,其实也可以在写递归函数的时候,发现需要什么参数,再去补充就可以。
一般情况,深搜需要 二维数组数组结构保存所有路径,需要一维数组保存单一路径,这种保存结果的数组,我们可以定义一个全局遍历,避免让我们的函数参数过多。
例如这样:
```
vector<vector<int>> result; // 保存符合条件的所有路径
vector<int> path; // 起点到终点的路径
void dfs (图,目前搜索的节点)
```
但这种写法看个人习惯,不强求。
2. 确认终止条件
终止条件很重要很多同学写dfs的时候之所以容易死循环栈溢出等等这些问题都是因为终止条件没有想清楚。
```
if (终止条件) {
存放结果;
return;
}
```
终止添加不仅是结束本层递归,同时也是我们收获结果的时候。
3. 处理目前搜索节点出发的路径
一般这里就是一个for循环的操作去遍历 目前搜索节点 所能到的所有节点。
```
for (选择:本节点所连接的其他节点) {
处理节点;
dfs(图,选择的节点); // 递归
回溯,撤销处理结果
}
```
不少录友疑惑的地方,都是 dfs代码框架中for循环里分明已经处理节点了那么 dfs函数下面 为什么还要撤销的呢。
如图七所示, 路径2 已经走到了 目的地节点6那么 路径2 是如何撤销,然后改为 路径3呢 其实这就是 回溯的过程撤销路径2走换下一个方向。
![图七](https://code-thinking-1253855093.file.myqcloud.com/pics/20220708093544.png)
## 总结
我们讲解了dfs 和 bfs的大体区别bfs详细过程下篇来讲dfs的搜索过程以及代码框架。
最后还有 深搜三部曲来解读这份代码框架。
以上如果大家都能理解了,其实搜索的代码就很好写,具体题目套用具体场景就可以了。
## 797. 所有可能的路径
### 思路
1. 确认递归函数,参数
首先我们dfs函数一定要存一个图用来遍历的还要存一个目前我们遍历的节点定义为x
至于 单一路径,和路径集合可以放在全局变量,那么代码是这样的:
```c++
vector<vector<int>> result; // 收集符合条件的路径
vector<int> path; // 0节点到终点的路径
// x目前遍历的节点
// graph存当前的图
void dfs (vector<vector<int>>& graph, int x)
```
2. 确认终止条件
什么时候我们就找到一条路径了?
当目前遍历的节点 为 最后一个节点的时候,就找到了一条,从 出发点到终止点的路径。
当前遍历的节点我们定义为x最后一点节点就是 graph.size() - 1。
所以 但 x 等于 graph.size() - 1 的时候就找到一条有效路径。 代码如下:
```c++
// 要求从节点 0 到节点 n-1 的路径并输出,所以是 graph.size() - 1
if (x == graph.size() - 1) { // 找到符合条件的一条路径
result.push_back(path); // 收集有效路径
return;
}
```
3. 处理目前搜索节点出发的路径
接下来是走 当前遍历节点x的下一个节点。
首先是要找到 x节点链接了哪些节点呢 遍历方式是这样的:
```c++
for (int i = 0; i < graph[x].size(); i++) { // 遍历节点n链接的所有节点
```
接下来就是将 选中的x所连接的节点加入到 单一路劲来。
```C++
path.push_back(graph[x][i]); // 遍历到的节点加入到路径中来
```
当前遍历的节点就是 `graph[x][i]` 了,所以进入下一层递归
```C++
dfs(graph, graph[x][i]); // 进入下一层递归
```
最后就是回溯的过程,撤销本次添加节点的操作。 该过程整体代码:
```C++
for (int i = 0; i < graph[x].size(); i++) { // 遍历节点n链接的所有节点
path.push_back(graph[x][i]); // 遍历到的节点加入到路径中来
dfs(graph, graph[x][i]); // 进入下一层递归
path.pop_back(); // 回溯,撤销本节点
}
```
### 本题代码
```c++
class Solution {
private:
vector<vector<int>> result; // 收集符合条件的路径
vector<int> path; // 0节点到终点的路径
// x目前遍历的节点
// graph存当前的图
void dfs (vector<vector<int>>& graph, int x) {
// 要求从节点 0 到节点 n-1 的路径并输出,所以是 graph.size() - 1
if (x == graph.size() - 1) { // 找到符合条件的一条路径
result.push_back(path);
return;
}
for (int i = 0; i < graph[x].size(); i++) { // 遍历节点n链接的所有节点
path.push_back(graph[x][i]); // 遍历到的节点加入到路径中来
dfs(graph, graph[x][i]); // 进入下一层递归
path.pop_back(); // 回溯,撤销本节点
}
}
public:
vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
path.push_back(0); // 无论什么路径已经是从0节点出发
dfs(graph, 0); // 开始遍历
return result;
}
};
```
## 其他语言版本
### Java
### Python
### Go

View File

@@ -31,21 +31,182 @@
* 解释:我们不能进入 2 号房间。
## 思
## 思
其实这道题的本质就是判断各个房间所连成的有向图,说明不用访问所有的房间。
本题其实给我们是一个有向图, 意识到这是有向图很重要!
如图所示:
图中给我的两个示例: `[[1],[2],[3],[]]` `[[1,3],[3,0,1],[2],[0]]`,画成对应的图如下:
<img src='https://code-thinking.cdn.bcebos.com/pics/841.钥匙和房间.png' width=600> </img></div>
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220714101414.png)
示例1就可以访问所有的房间因为通过房间里的key将房间连在了一起
我们可以看出图1的所有节点都是链接的而图二中节点2 是孤立的
示例2中就不能访问所有房间从图中就可以看出房间2是一个孤岛我们从0出发无论怎么遍历都访问不到房间2。
这就很容易让我们想起岛屿问题,只要发现独立的岛,就是不能进入所有房间。
认清本质问题之后,**使用 广度优先搜索(BFS) 还是 深度优先搜索(DFS) 都是可以的。**
此时也容易想到用并查集的方式去解决。
BFS C++代码代码如下:
**但本题是有向图**在有向图中即使所有节点都是链接的但依然不可能从0出发遍历所有边。
给大家举一个例子:
图3[[5], [], [1, 3], [5]] ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220714102201.png)
在图3中大家可以发现节点0只能到节点5然后就哪也去不了了。
所以本题是一个有向图搜索全路径的问题。 只能用深搜BFS或者广搜DFS来搜。
关于DFS的理论如果大家有困惑可以先看我这篇题解 [DFS理论基础](https://leetcode.cn/problems/all-paths-from-source-to-target/solution/by-carlsun-2-66pf)
**以下dfs分析 大家一定要仔细看本题有两种dfs的解法很多题解没有讲清楚**。 看完之后 相信你对dfs会有更深的理解。
深搜三部曲:
1. 确认递归函数,参数
需要传入二维数组rooms来遍历地图需要知道当前我们拿到的key以至于去下一个房间。
同时还需要一个数组,用来记录我们都走过了哪些房间,这样好知道最后有没有把所有房间都遍历的,可以定义一个一维数组。
所以 递归函数参数如下:
```C++
// key 当前得到的可以
// visited 记录访问过的房间
void dfs(const vector<vector<int>>& rooms, int key, vector<bool>& visited) {
```
2. 确认终止条件
遍历的时候,什么时候终止呢?
这里有一个很重要的逻辑,就是在递归中,**我们是处理当前访问的节点,还是处理下一个要访问的节点**。
这决定 终止条件怎么写。
首先明确,本题中什么叫做处理,就是 visited数组来记录访问过的节点那么把该节点默认 数组里元素都是false把元素标记为true就是处理 本节点了。
如果我们是处理当前访问的节点,当前访问的节点如果是 true 说明是访问过的节点那就终止本层递归如果不是true我们就把它赋值为true因为我们处理本层递归的节点。
代码就是这样:
```C++
// 写法一:处理当前访问的节点
void dfs(const vector<vector<int>>& rooms, int key, vector<bool>& visited) {
if (visited[key]) { // 本层递归是true说明访问过立刻返回
return;
}
visited[key] = true; // 给当前遍历的节点赋值true
vector<int> keys = rooms[key];
for (int key : keys) {
// 深度优先搜索遍历
dfs(rooms, key, visited);
}
}
```
如果我们是处理下一层访问的节点,而不是当前层。那么就要在 深搜三部曲中第三步:处理目前搜索节点出发的路径 的时候对 节点进行处理。
这样的话,就不需要终止条件,而是在 搜索下一个节点的时候,直接判断 下一个节点是否是我们要搜的节点。
代码就是这样的:
```C++
// 写法二:处理下一个要访问的节点
void dfs(const vector<vector<int>>& rooms, int key, vector<bool>& visited) {
// 这里 没有终止条件,而是在 处理下一层节点的时候来判断
vector<int> keys = rooms[key];
for (int key : keys) {
if (visited[key] == false) { // 处理下一层节点,判断是否要进行递归
visited[key] = true;
dfs(rooms, key, visited);
}
}
}
```
可以看出,如果看待 我们要访问的节点,直接决定了两种不一样的写法,很多同学对这一块很模糊,其实做过这道题,也没有思考到这个维度上。
3. 处理目前搜索节点出发的路径
其实在上面,深搜三部曲 第二部,就已经讲了,因为终止条件的两种写法, 直接决定了两种不一样的递归写法。
这里还有细节:
看上面两个版本的写法中, 好像没有发现回溯的逻辑。
我们都知道,有递归就有回溯,回溯就在递归函数的下面, 那么之前我们做的dfs题目都需要回溯操作例如[797.所有可能的路径](https://leetcode.cn/problems/all-paths-from-source-to-target/solution/by-carlsun-2-66pf/) **为什么本题就没有回溯呢?**
代码中可以看到dfs函数下面并没有回溯的操作。
此时就要在思考本题的要求了,本题是需要判断 0节点是否能到所有节点那么我们就没有必要回溯去撤销操作了只要遍历过的节点一律都标记上。
**那什么时候需要回溯操作呢?**
当我们需要搜索一条可行路径的时候,就需要回溯操作了,因为没有回溯,就没法“调头”, 如果不理解的话,去看我写的 [797.所有可能的路径](https://leetcode.cn/problems/all-paths-from-source-to-target/solution/by-carlsun-2-66pf/) 的题解。
以上分析完毕DFS整体实现C++代码如下:
```CPP
// 写法一:处理当前访问的节点
class Solution {
private:
void dfs(const vector<vector<int>>& rooms, int key, vector<bool>& visited) {
if (visited[key]) {
return;
}
visited[key] = true;
vector<int> keys = rooms[key];
for (int key : keys) {
// 深度优先搜索遍历
dfs(rooms, key, visited);
}
}
public:
bool canVisitAllRooms(vector<vector<int>>& rooms) {
vector<bool> visited(rooms.size(), false);
dfs(rooms, 0, visited);
//检查是否都访问到了
for (int i : visited) {
if (i == false) return false;
}
return true;
}
};
```
```c++
写法二:处理下一个要访问的节点
class Solution {
private:
void dfs(const vector<vector<int>>& rooms, int key, vector<bool>& visited) {
vector<int> keys = rooms[key];
for (int key : keys) {
if (visited[key] == false) {
visited[key] = true;
dfs(rooms, key, visited);
}
}
}
public:
bool canVisitAllRooms(vector<vector<int>>& rooms) {
vector<bool> visited(rooms.size(), false);
visited[0] = true; // 0 节点是出发节点,一定被访问过
dfs(rooms, 0, visited);
//检查是否都访问到了
for (int i : visited) {
if (i == false) return false;
}
return true;
}
};
```
本题我也给出 BFS C++代码至于BFS我后面会有单独文章来讲代码如下
```CPP
class Solution {
@@ -80,39 +241,11 @@ public:
};
```
DFS C++代码如下:
```CPP
class Solution {
private:
void dfs(int key, const vector<vector<int>>& rooms, vector<int>& visited) {
if (visited[key]) {
return;
}
visited[key] = 1;
vector<int> keys = rooms[key];
for (int key : keys) {
// 深度优先搜索遍历
dfs(key, rooms, visited);
}
}
public:
bool canVisitAllRooms(vector<vector<int>>& rooms) {
vector<int> visited(rooms.size(), 0);
dfs(0, rooms, visited);
//检查是否都访问到了
for (int i : visited) {
if (i == 0) return false;
}
return true;
}
};
```
# 其他语言版本
## 其他语言版本
Java
### Java
```java
class Solution {
@@ -120,24 +253,19 @@ class Solution {
if (visited.get(key)) {
return;
}
visited.set(key, true);
for (int k : rooms.get(key)) {
// 深度优先搜索遍历
dfs(k, rooms, visited);
}
}
public boolean canVisitAllRooms(List<List<Integer>> rooms) {
List<Boolean> visited = new ArrayList<Boolean>(){{
for(int i = 0 ; i < rooms.size(); i++){
add(false);
}
}};
dfs(0, rooms, visited);
//检查是否都访问到了
for (boolean flag : visited) {
if (!flag) {
@@ -149,20 +277,14 @@ class Solution {
}
```
python3
### python3
```python
class Solution:
def dfs(self, key: int, rooms: List[List[int]] , visited : List[bool] ) :
if visited[key] :
return
visited[key] = True
keys = rooms[key]
for i in range(len(keys)) :
@@ -183,7 +305,7 @@ class Solution:
```
Go
### Go
```go
@@ -201,11 +323,8 @@ func dfs(key int, rooms [][]int, visited []bool ) {
}
func canVisitAllRooms(rooms [][]int) bool {
visited := make([]bool, len(rooms));
dfs(0, rooms, visited);
//检查是否都访问到了
for i := 0; i < len(visited); i++ {
if !visited[i] {
@@ -216,7 +335,7 @@ func canVisitAllRooms(rooms [][]int) bool {
}
```
JavaScript
### JavaScript
```javascript
//DFS
var canVisitAllRooms = function(rooms) {

View File

@@ -399,6 +399,71 @@ var backspaceCompare = function(s, t) {
```
### TypeScript
> 双栈法:
```typescript
function backspaceCompare(s: string, t: string): boolean {
const stack1: string[] = [],
stack2: string[] = [];
for (let c of s) {
if (c === '#') {
stack1.pop();
} else {
stack1.push(c);
}
}
for (let c of t) {
if (c === '#') {
stack2.pop();
} else {
stack2.push(c);
}
}
if (stack1.length !== stack2.length) return false;
for (let i = 0, length = stack1.length; i < length; i++) {
if (stack1[i] !== stack2[i]) return false;
}
return true;
};
```
> 双指针法:
```typescript
function backspaceCompare(s: string, t: string): boolean {
let sIndex: number = s.length - 1,
tIndex: number = t.length - 1;
while (true) {
sIndex = getIndexAfterDel(s, sIndex);
tIndex = getIndexAfterDel(t, tIndex);
if (sIndex < 0 || tIndex < 0) break;
if (s[sIndex] !== t[tIndex]) return false;
sIndex--;
tIndex--;
}
return sIndex === -1 && tIndex === -1;
};
function getIndexAfterDel(s: string, startIndex: number): number {
let backspaceNum: number = 0;
while (startIndex >= 0) {
// 不可消除
if (s[startIndex] !== '#' && backspaceNum === 0) break;
// 可消除
if (s[startIndex] === '#') {
backspaceNum++;
} else {
backspaceNum--;
}
startIndex--;
}
return startIndex;
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@@ -328,6 +328,37 @@ function lemonadeChange(bills: number[]): boolean {
```
### Scala
```scala
object Solution {
def lemonadeChange(bills: Array[Int]): Boolean = {
var fiveNum = 0
var tenNum = 0
for (i <- bills) {
if (i == 5) fiveNum += 1
if (i == 10) {
if (fiveNum <= 0) return false
tenNum += 1
fiveNum -= 1
}
if (i == 20) {
if (fiveNum > 0 && tenNum > 0) {
tenNum -= 1
fiveNum -= 1
} else if (fiveNum >= 3) {
fiveNum -= 3
} else {
return false
}
}
}
true
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

Some files were not shown because too many files have changed in this diff Show More