Merge branch 'master' into master
This commit is contained in:
@@ -523,6 +523,10 @@
|
||||
|
||||
[点此这里](https://github.com/youngyangyang04/leetcode-master/graphs/contributors)查看LeetCode-Master的所有贡献者。感谢他们补充了LeetCode-Master的其他语言版本,让更多的读者收益于此项目。
|
||||
|
||||
# Star 趋势
|
||||
|
||||
[](https://star-history.com/#youngyangyang04/leetcode-master&Date)
|
||||
|
||||
# 关于作者
|
||||
|
||||
大家好,我是程序员Carl,哈工大师兄,《代码随想录》作者,先后在腾讯和百度从事后端技术研发,CSDN博客专家。对算法和C++后端技术有一定的见解,利用工作之余重新刷leetcode。
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
|
||||
## 思路
|
||||
|
||||
很明显暴力的解法是两层for循环查找,时间复杂度是$O(n^2)$。
|
||||
建议看一下我录的这期视频:[梦开始的地方,Leetcode:1.两数之和](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 效率更高!** 使用其他语言的录友注意了解一下自己所用语言的数据结构就行。
|
||||
|
||||
解题思路动画如下:
|
||||
接下来需要明确两点:
|
||||
|
||||

|
||||
* **map用来做什么**
|
||||
* **map中key和value分别表示什么**
|
||||
|
||||
map目的用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下表,这样才能找到与当前元素相匹配的(也就是相加等于target)
|
||||
|
||||
接下来是map中key和value分别表示什么。
|
||||
|
||||
这道题 我们需要 给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标。
|
||||
|
||||
那么判断元素是否出现,这个元素就要作为key,所以数组中的元素作为key,有key对应的就是value,value用来存下标。
|
||||
|
||||
所以 map中的存储结构为 {key:数据元素,value:数组元素对应的下表}。
|
||||
|
||||
在遍历数组的时候,只需要向map去查询是否有和目前遍历元素比配的数值,如果有,就找到的匹配对,如果没有,就把目前遍历的元素放进map中,因为map存放的就是我们访问过的元素。
|
||||
|
||||
过程如下:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
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>
|
||||
|
||||
@@ -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函数
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
## 思路
|
||||
|
||||
针对本题重点难点,我录制了B站讲解视频,[帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点](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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -142,7 +142,7 @@ public:
|
||||
```
|
||||
|
||||
* 时间复杂度:O(log n)
|
||||
* 时间复杂度:O(1)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
效率如下:
|
||||

|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() # 回溯
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 // 如果上面没有返回就是跳不到
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 // 求和
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
# 143.重排链表
|
||||
|
||||
[力扣题目链接](https://leetcode.cn/problems/reorder-list/submissions/)
|
||||
|
||||

|
||||
|
||||
## 思路
|
||||
@@ -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
|
||||
//翻转链表
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
# 思路
|
||||
|
||||
本题我录制了B站视频,[帮你拿下反转链表 | LeetCode:206.反转链表](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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
## 思路
|
||||
|
||||
本题B站视频讲解版:[学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词](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
|
||||
```
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ dp[i][j],第i天状态为j,所剩的最多现金为dp[i][j]。
|
||||
* 状态三:今天卖出了股票
|
||||
* 状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!
|
||||
|
||||

|
||||
|
||||
j的状态为:
|
||||
|
||||
* 0:状态一
|
||||
@@ -57,7 +59,7 @@ j的状态为:
|
||||
|
||||
**注意这里的每一个状态,例如状态一,是买入股票状态并不是说今天已经就买入股票,而是说保存买入股票的状态即:可能是前几天买入的,之后一直没操作,所以保持买入股票的状态**。
|
||||
|
||||
2. 确定递推公式
|
||||
1. 确定递推公式
|
||||
|
||||
|
||||
达到买入股票状态(状态一)即:dp[i][0],有两个具体操作:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
-----------------------
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
## 思路
|
||||
|
||||
关于本题,我录制了讲解视频:[学透哈希表,set使用有技巧!Leetcode:349. 两个数组的交集](https://www.bilibili.com/video/BV1ba411S7wu),看视频配合题解,事半功倍。
|
||||
|
||||
这道题目,主要要学会使用一种哈希数据结构:unordered_set,这个数据结构可以解决很多类似的问题。
|
||||
|
||||
注意题目特意说明:**输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序**
|
||||
@@ -48,7 +50,8 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底
|
||||
|
||||
思路如图所示:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
## 总结
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
```
|
||||
|
||||
迭代
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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] = 7,next[len - 1] + 1 = 8,8就是此时字符串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>
|
||||
|
||||
@@ -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的个数小于m,1的个数小于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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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)
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:**
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
# 思路
|
||||
|
||||
为了方便大家理解,我特意录制了视频:[帮你把链表操作学个通透!LeetCode:707.设计链表](https://www.bilibili.com/video/BV1FU4y1X7WD),结合视频在看本题解,事半功倍。
|
||||
|
||||
如果对链表的基础知识还不太懂,可以看这篇文章:[关于链表,你该了解这些!](https://programmercarl.com/链表理论基础.html)
|
||||
|
||||
如果对链表的虚拟头结点不清楚,可以看这篇文章:[链表:听说用虚拟头节点会方便很多?](https://programmercarl.com/0203.移除链表元素.html)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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数组吧。
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
||||
296
problems/0797.所有可能的路径.md
Normal file
296
problems/0797.所有可能的路径.md
Normal file
@@ -0,0 +1,296 @@
|
||||
|
||||
看一下 算法4,深搜是怎么讲的
|
||||
|
||||
# 797.所有可能的路径
|
||||
|
||||
本题是一道 原汁原味的 深度优先搜索(dfs)模板题,那么用这道题目 来讲解 深搜最合适不过了。
|
||||
|
||||
接下来给大家详细讲解dfs:
|
||||
|
||||
## dfs 与 bfs 区别
|
||||
|
||||
先来了解dfs的过程,很多录友可能对dfs(深度优先搜索),bfs(广度优先搜索)分不清。
|
||||
|
||||
先给大家说一下两者大概的区别:
|
||||
|
||||
* dfs是可一个方向去搜,不到黄河不回头,直到遇到绝境了,搜不下去了,在换方向(换方向的过程就涉及到了回溯)。
|
||||
* bfs是先把本节点所连接的所有节点遍历一遍,走到下一个节点的时候,再把连接节点的所有节点遍历一遍,搜索方向更像是广度,四面八方的搜索过程。
|
||||
|
||||
当然以上讲的是,大体可以这么理解,接下来 我们详细讲解dfs,(bfs在用单独一篇文章详细讲解)
|
||||
|
||||
## dfs 搜索过程
|
||||
|
||||
上面说道dfs是可一个方向搜,不到黄河不回头。 那么我们来举一个例子。
|
||||
|
||||
如图一,是一个无向图,我们要搜索从节点1到节点6的所有路径。
|
||||
|
||||

|
||||
|
||||
那么dfs搜索的第一条路径是这样的: (假设第一次延默认方向,就找到了节点6),图二
|
||||
|
||||

|
||||
|
||||
此时我们找到了节点6,(遇到黄河了,是不是应该回头了),那么应该再去搜索其他方向了。 如图三:
|
||||
|
||||

|
||||
|
||||
路径2撤销了,改变了方向,走路径3(红色线), 接着也找到终点6。 那么撤销路径2,改为路径3,在dfs中其实就是回溯的过程(这一点很重要,很多录友,都不理解dfs代码中回溯是用来干什么的)
|
||||
|
||||
又找到了一条从节点1到节点6的路径,又到黄河了,此时再回头,下图图四中,路径4撤销(回溯的过程),改为路径5。
|
||||
|
||||

|
||||
|
||||
又找到了一条从节点1到节点6的路径,又到黄河了,此时再回头,下图图五,路径6撤销(回溯的过程),改为路径7,路径8 和 路径7,路径9, 结果发现死路一条,都走到了自己走过的节点。
|
||||
|
||||

|
||||
|
||||
那么节点2所连接路径和节点3所链接的路径 都走过了,撤销路径只能向上回退,去选择撤销当初节点4的选择,也就是撤销路径5,改为路径10 。 如图图六:
|
||||
|
||||

|
||||
|
||||
|
||||
上图演示中,其实我并没有把 所有的 从节点1 到节点6的dfs(深度优先搜索)的过程都画出来,那样太冗余了,但 已经把dfs 关键的地方都涉及到了,关键就两点:
|
||||
|
||||
* 搜索方向,是认准一个方向搜,直到碰壁之后在换方向
|
||||
* 换方向是撤销原路径,改为节点链接的下一个路径,回溯的过程。
|
||||
|
||||
|
||||
## 代码框架
|
||||
|
||||
正式因为dfs搜索可一个方向,并需要回溯,所以用递归的方式来实现是最方便的。
|
||||
|
||||
很多录友对回溯很陌生,建议先看看码随想录,[回溯算法章节](https://programmercarl.com/回溯算法理论基础.html)。
|
||||
|
||||
有递归的地方就有回溯,那么回溯在哪里呢?
|
||||
|
||||
就地递归函数的下面,例如如下代码:
|
||||
```
|
||||
void dfs(参数) {
|
||||
处理节点
|
||||
dfs(图,选择的节点); // 递归
|
||||
回溯,撤销处理结果
|
||||
}
|
||||
```
|
||||
|
||||
可以看到回溯操作就在递归函数的下面,递归和回溯是相辅相成的。
|
||||
|
||||
在讲解[二叉树章节](https://programmercarl.com/二叉树理论基础.html)的时候,二叉树的递归法其实就是dfs,而二叉树的迭代法,就是bfs(广度优先搜索)
|
||||
|
||||
所以**dfs,bfs其实是基础搜索算法,也广泛应用与其他数据结构与算法中**。
|
||||
|
||||
我们在回顾一下[回溯法](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,走换下一个方向。
|
||||
|
||||

|
||||
|
||||
|
||||
## 总结
|
||||
|
||||
我们讲解了,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
|
||||
@@ -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>
|
||||

|
||||
|
||||
示例1就可以访问所有的房间,因为通过房间里的key将房间连在了一起。
|
||||
我们可以看出图1的所有节点都是链接的,而图二中,节点2 是孤立的。
|
||||
|
||||
示例2中,就不能访问所有房间,从图中就可以看出,房间2是一个孤岛,我们从0出发,无论怎么遍历,都访问不到房间2。
|
||||
这就很容易让我们想起岛屿问题,只要发现独立的岛,就是不能进入所有房间。
|
||||
|
||||
认清本质问题之后,**使用 广度优先搜索(BFS) 还是 深度优先搜索(DFS) 都是可以的。**
|
||||
此时也容易想到用并查集的方式去解决。
|
||||
|
||||
BFS C++代码代码如下:
|
||||
**但本题是有向图**,在有向图中,即使所有节点都是链接的,但依然不可能从0出发遍历所有边。
|
||||
给大家举一个例子:
|
||||
|
||||
图3:[[5], [], [1, 3], [5]] ,如图:
|
||||
|
||||

|
||||
|
||||
在图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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user