Merge branch 'youngyangyang04:master' into remote
This commit is contained in:
commit
3feae289d9
15
README.md
15
README.md
|
|
@ -1,14 +1,14 @@
|
|||
|
||||
|
||||
👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定)
|
||||
👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master)
|
||||
|
||||
> 1. **介绍**:本项目是一套完整的刷题计划,旨在帮助大家少走弯路,循序渐进学算法,[关注作者](#关于作者)
|
||||
> 2. **PDF版本** : [「代码随想录」算法精讲 PDF 版本](https://programmercarl.com/other/algo_pdf.html) 。
|
||||
> 3. **刷题顺序** : README已经将刷题顺序排好了,按照顺序一道一道刷就可以。
|
||||
> 4. **学习社区** : 一起学习打卡/面试技巧/如何选择offer/大厂内推/职场规则/简历修改/技术分享/程序人生。欢迎加入[「代码随想录」知识星球](https://programmercarl.com/other/kstar.html) 。
|
||||
> 5. **提交代码**:本项目统一使用C++语言进行讲解,但已经有Java、Python、Go、JavaScript等等多语言版本,感谢[这里的每一位贡献者](https://github.com/youngyangyang04/leetcode-master/graphs/contributors),如果你也想贡献代码点亮你的头像,[点击这里](https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A)了解提交代码的方式。
|
||||
> 6. **转载须知** :以下所有文章皆为我([程序员Carl](https://github.com/youngyangyang04))的原创。引用本项目文章请注明出处,发现恶意抄袭或搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!
|
||||
> 3. **最强八股文:**:[代码随想录知识星球精华PDF](https://www.programmercarl.com/other/kstar_baguwen.html)
|
||||
> 4. **刷题顺序** : README已经将刷题顺序排好了,按照顺序一道一道刷就可以。
|
||||
> 5. **学习社区** : 一起学习打卡/面试技巧/如何选择offer/大厂内推/职场规则/简历修改/技术分享/程序人生。欢迎加入[「代码随想录」知识星球](https://programmercarl.com/other/kstar.html) 。
|
||||
> 6. **提交代码**:本项目统一使用C++语言进行讲解,但已经有Java、Python、Go、JavaScript等等多语言版本,感谢[这里的每一位贡献者](https://github.com/youngyangyang04/leetcode-master/graphs/contributors),如果你也想贡献代码点亮你的头像,[点击这里](https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A)了解提交代码的方式。
|
||||
> 7. **转载须知** :以下所有文章皆为我([程序员Carl](https://github.com/youngyangyang04))的原创。引用本项目文章请注明出处,发现恶意抄袭或搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!
|
||||
|
||||
<p align="center">
|
||||
<a href="programmercarl.com" target="_blank">
|
||||
|
|
@ -88,8 +88,7 @@
|
|||
|
||||
## 前序
|
||||
|
||||
* [「代码随想录」后序安排](https://mp.weixin.qq.com/s/4eeGJREy6E-v6D7cR_5A4g)
|
||||
* [「代码随想录」学习社区](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
* [「代码随想录」学习社区](https://programmercarl.com/other/kstar.html)
|
||||
|
||||
|
||||
* 编程语言
|
||||
|
|
@ -123,7 +122,7 @@
|
|||
|
||||
* 算法性能分析
|
||||
* [关于时间复杂度,你不知道的都在这里!](./problems/前序/关于时间复杂度,你不知道的都在这里!.md)
|
||||
* [$O(n)$的算法居然超时了,此时的n究竟是多大?](./problems/前序/On的算法居然超时了,此时的n究竟是多大?.md)
|
||||
* [O(n)的算法居然超时了,此时的n究竟是多大?](./problems/前序/On的算法居然超时了,此时的n究竟是多大?.md)
|
||||
* [通过一道面试题目,讲一讲递归算法的时间复杂度!](./problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md)
|
||||
* [本周小结!(算法性能分析系列一)](./problems/周总结/20201210复杂度分析周末总结.md)
|
||||
* [关于空间复杂度,可能有几个疑问?](./problems/前序/关于空间复杂度,可能有几个疑问?.md)
|
||||
|
|
|
|||
|
|
@ -118,6 +118,18 @@ class Solution:
|
|||
return [records[target - val], idx] # 如果存在就返回字典记录索引和当前索引
|
||||
```
|
||||
|
||||
Python (v2):
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def twoSum(self, nums: List[int], target: int) -> List[int]:
|
||||
rec = {}
|
||||
for i in range(len(nums)):
|
||||
rest = target - nums[i]
|
||||
# Use get to get the index of the data, making use of one of the dictionary properties.
|
||||
if rec.get(rest, None) is not None: return [rec[rest], i]
|
||||
rec[nums[i]] = i
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
|
@ -186,6 +198,24 @@ var twoSum = function (nums, target) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function twoSum(nums: number[], target: number): number[] {
|
||||
let helperMap: Map<number, number> = new Map();
|
||||
let index: number | undefined;
|
||||
let resArr: number[] = [];
|
||||
for (let i = 0, length = nums.length; i < length; i++) {
|
||||
index = helperMap.get(target - nums[i]);
|
||||
if (index !== undefined) {
|
||||
resArr = [i, index];
|
||||
}
|
||||
helperMap.set(nums[i], i);
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
php
|
||||
|
||||
```php
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
两层for循环,遍历区间起始位置和终止位置,然后判断这个区间是不是回文。
|
||||
|
||||
时间复杂度:$O(n^3)$
|
||||
时间复杂度:O(n^3)
|
||||
|
||||
## 动态规划
|
||||
|
||||
|
|
@ -205,8 +205,8 @@ public:
|
|||
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n^2)$
|
||||
* 空间复杂度:$O(n^2)$
|
||||
* 时间复杂度:O(n^2)
|
||||
* 空间复杂度:O(n^2)
|
||||
|
||||
## 双指针
|
||||
|
||||
|
|
@ -253,14 +253,33 @@ public:
|
|||
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n^2)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n^2)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
|
||||
|
||||
# 其他语言版本
|
||||
|
||||
## Java
|
||||
Java:
|
||||
|
||||
```java
|
||||
public int[] twoSum(int[] nums, int target) {
|
||||
int[] res = new int[2];
|
||||
if(nums == null || nums.length == 0){
|
||||
return res;
|
||||
}
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
for(int i = 0; i < nums.length; i++){
|
||||
int temp = target - nums[i];
|
||||
if(map.containsKey(temp)){
|
||||
res[1] = i;
|
||||
res[0] = map.get(temp);
|
||||
}
|
||||
map.put(nums[i], i);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 双指针 中心扩散法
|
||||
|
|
@ -291,7 +310,7 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
|
|
@ -312,7 +331,8 @@ class Solution:
|
|||
return s[left:right + 1]
|
||||
|
||||
```
|
||||
> 双指针法:
|
||||
双指针:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def longestPalindrome(self, s: str) -> str:
|
||||
|
|
@ -340,13 +360,13 @@ class Solution:
|
|||
return s[start:end]
|
||||
|
||||
```
|
||||
## Go
|
||||
Go:
|
||||
|
||||
```go
|
||||
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
JavaScript:
|
||||
|
||||
```js
|
||||
//动态规划解法
|
||||
|
|
@ -462,7 +482,93 @@ var longestPalindrome = function(s) {
|
|||
};
|
||||
```
|
||||
|
||||
C:
|
||||
|
||||
动态规划:
|
||||
```c
|
||||
//初始化dp数组,全部初始为false
|
||||
bool **initDP(int strLen) {
|
||||
bool **dp = (bool **)malloc(sizeof(bool *) * strLen);
|
||||
int i, j;
|
||||
for(i = 0; i < strLen; ++i) {
|
||||
dp[i] = (bool *)malloc(sizeof(bool) * strLen);
|
||||
for(j = 0; j < strLen; ++j)
|
||||
dp[i][j] = false;
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
char * longestPalindrome(char * s){
|
||||
//求出字符串长度
|
||||
int strLen = strlen(s);
|
||||
//初始化dp数组,元素初始化为false
|
||||
bool **dp = initDP(strLen);
|
||||
int maxLength = 0, left = 0, right = 0;
|
||||
|
||||
//从下到上,从左到右遍历
|
||||
int i, j;
|
||||
for(i = strLen - 1; i >= 0; --i) {
|
||||
for(j = i; j < strLen; ++j) {
|
||||
//若当前i与j所指字符一样
|
||||
if(s[i] == s[j]) {
|
||||
//若i、j指向相邻字符或同一字符,则为回文字符串
|
||||
if(j - i <= 1)
|
||||
dp[i][j] = true;
|
||||
//若i+1与j-1所指字符串为回文字符串,则i、j所指字符串为回文字符串
|
||||
else if(dp[i + 1][j - 1])
|
||||
dp[i][j] = true;
|
||||
}
|
||||
//若新的字符串的长度大于之前的最大长度,进行更新
|
||||
if(dp[i][j] && j - i + 1 > maxLength) {
|
||||
maxLength = j - i + 1;
|
||||
left = i;
|
||||
right = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
//复制回文字符串,并返回
|
||||
char *ret = (char*)malloc(sizeof(char) * (maxLength + 1));
|
||||
memcpy(ret, s + left, maxLength);
|
||||
ret[maxLength] = 0;
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
双指针:
|
||||
```c
|
||||
int left, maxLength;
|
||||
void extend(char *str, int i, int j, int size) {
|
||||
while(i >= 0 && j < size && str[i] == str[j]) {
|
||||
//若当前子字符串长度大于最长的字符串长度,进行更新
|
||||
if(j - i + 1 > maxLength) {
|
||||
maxLength = j - i + 1;
|
||||
left = i;
|
||||
}
|
||||
//左指针左移,右指针右移。扩大搜索范围
|
||||
++j, --i;
|
||||
}
|
||||
}
|
||||
|
||||
char * longestPalindrome(char * s){
|
||||
left = right = maxLength = 0;
|
||||
int size = strlen(s);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < size; ++i) {
|
||||
//长度为单数的子字符串
|
||||
extend(s, i, i, size);
|
||||
//长度为双数的子字符串
|
||||
extend(s, i, i + 1, size);
|
||||
}
|
||||
|
||||
//复制子字符串
|
||||
char *subStr = (char *)malloc(sizeof(char) * (maxLength + 1));
|
||||
memcpy(subStr, s + left, maxLength);
|
||||
subStr[maxLength] = 0;
|
||||
|
||||
return subStr;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -138,8 +138,12 @@ public:
|
|||
*/
|
||||
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 {
|
||||
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
|
||||
// 去重逻辑应该放在找到一个三元组之后
|
||||
|
|
@ -243,7 +247,34 @@ class Solution:
|
|||
right -= 1
|
||||
return ans
|
||||
```
|
||||
Python (v2):
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def threeSum(self, nums: List[int]) -> List[List[int]]:
|
||||
if len(nums) < 3: return []
|
||||
nums, res = sorted(nums), []
|
||||
for i in range(len(nums) - 2):
|
||||
cur, l, r = nums[i], i + 1, len(nums) - 1
|
||||
if res != [] and res[-1][0] == cur: continue # Drop duplicates for the first time.
|
||||
|
||||
while l < r:
|
||||
if cur + nums[l] + nums[r] == 0:
|
||||
res.append([cur, nums[l], nums[r]])
|
||||
# Drop duplicates for the second time in interation of l & r. Only used when target situation occurs, because that is the reason for dropping duplicates.
|
||||
while l < r - 1 and nums[l] == nums[l + 1]:
|
||||
l += 1
|
||||
while r > l + 1 and nums[r] == nums[r - 1]:
|
||||
r -= 1
|
||||
if cur + nums[l] + nums[r] > 0:
|
||||
r -= 1
|
||||
else:
|
||||
l += 1
|
||||
return res
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```Go
|
||||
func threeSum(nums []int)[][]int{
|
||||
sort.Ints(nums)
|
||||
|
|
@ -332,7 +363,43 @@ var threeSum = function(nums) {
|
|||
return res;
|
||||
};
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function threeSum(nums: number[]): number[][] {
|
||||
nums.sort((a, b) => a - b);
|
||||
let length = nums.length;
|
||||
let left: number = 0,
|
||||
right: number = length - 1;
|
||||
let resArr: number[][] = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (i > 0 && nums[i] === nums[i - 1]) {
|
||||
continue;
|
||||
}
|
||||
left = i + 1;
|
||||
right = length - 1;
|
||||
while (left < right) {
|
||||
let total: number = nums[i] + nums[left] + nums[right];
|
||||
if (total === 0) {
|
||||
resArr.push([nums[i], nums[left], nums[right]]);
|
||||
left++;
|
||||
right--;
|
||||
while (nums[right] === nums[right + 1]) {
|
||||
right--;
|
||||
}
|
||||
while (nums[left] === nums[left - 1]) {
|
||||
left++;
|
||||
}
|
||||
} else if (total < 0) {
|
||||
left++;
|
||||
} else {
|
||||
right--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
ruby:
|
||||
```ruby
|
||||
|
|
@ -509,5 +576,64 @@ int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes
|
|||
}
|
||||
```
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public class Solution
|
||||
{
|
||||
public IList<IList<int>> ThreeSum(int[] nums)
|
||||
{
|
||||
var result = new List<IList<int>>();
|
||||
|
||||
Array.Sort(nums);
|
||||
|
||||
for (int i = 0; i < nums.Length - 2; i++)
|
||||
{
|
||||
int n1 = nums[i];
|
||||
|
||||
if (n1 > 0)
|
||||
break;
|
||||
|
||||
if (i > 0 && n1 == nums[i - 1])
|
||||
continue;
|
||||
|
||||
int left = i + 1;
|
||||
int right = nums.Length - 1;
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
int n2 = nums[left];
|
||||
int n3 = nums[right];
|
||||
int sum = n1 + n2 + n3;
|
||||
|
||||
if (sum > 0)
|
||||
{
|
||||
right--;
|
||||
}
|
||||
else if (sum < 0)
|
||||
{
|
||||
left++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(new List<int> { n1, n2, n3 });
|
||||
|
||||
while (left < right && nums[left] == n2)
|
||||
{
|
||||
left++;
|
||||
}
|
||||
|
||||
while (left < right && nums[right] == n3)
|
||||
{
|
||||
right--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -420,6 +420,40 @@ var letterCombinations = function(digits) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function letterCombinations(digits: string): string[] {
|
||||
if (digits === '') return [];
|
||||
const strMap: { [index: string]: string[] } = {
|
||||
1: [],
|
||||
2: ['a', 'b', 'c'],
|
||||
3: ['d', 'e', 'f'],
|
||||
4: ['g', 'h', 'i'],
|
||||
5: ['j', 'k', 'l'],
|
||||
6: ['m', 'n', 'o'],
|
||||
7: ['p', 'q', 'r', 's'],
|
||||
8: ['t', 'u', 'v'],
|
||||
9: ['w', 'x', 'y', 'z'],
|
||||
}
|
||||
const resArr: string[] = [];
|
||||
function backTracking(digits: string, curIndex: number, route: string[]): void {
|
||||
if (curIndex === digits.length) {
|
||||
resArr.push(route.join(''));
|
||||
return;
|
||||
}
|
||||
let tempArr: string[] = strMap[digits[curIndex]];
|
||||
for (let i = 0, length = tempArr.length; i < length; i++) {
|
||||
route.push(tempArr[i]);
|
||||
backTracking(digits, curIndex + 1, route);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
backTracking(digits, 0, []);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
```c
|
||||
|
|
|
|||
|
|
@ -91,9 +91,13 @@ public:
|
|||
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
|
||||
if (nums[k] + nums[i] > target - (nums[left] + nums[right])) {
|
||||
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])) {
|
||||
left++;
|
||||
// 不合适,去重
|
||||
while (left < right && nums[left] == nums[left - 1]) left++;
|
||||
} else {
|
||||
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
|
||||
// 去重逻辑应该放在找到一个四元组之后
|
||||
|
|
@ -212,6 +216,7 @@ class Solution(object):
|
|||
|
||||
# good thing about using python is you can use set to drop duplicates.
|
||||
ans = set()
|
||||
# ans = [] # save results by list()
|
||||
for i in range(len(nums)):
|
||||
for j in range(i + 1, len(nums)):
|
||||
for k in range(j + 1, len(nums)):
|
||||
|
|
@ -220,10 +225,16 @@ class Solution(object):
|
|||
# make sure no duplicates.
|
||||
count = (nums[i] == val) + (nums[j] == val) + (nums[k] == val)
|
||||
if hashmap[val] > count:
|
||||
ans.add(tuple(sorted([nums[i], nums[j], nums[k], val])))
|
||||
ans_tmp = tuple(sorted([nums[i], nums[j], nums[k], val]))
|
||||
ans.add(ans_tmp)
|
||||
# Avoiding duplication in list manner but it cause time complexity increases
|
||||
# if ans_tmp not in ans:
|
||||
# ans.append(ans_tmp)
|
||||
else:
|
||||
continue
|
||||
return ans
|
||||
return list(ans)
|
||||
# if used list() to save results, just
|
||||
# return ans
|
||||
|
||||
```
|
||||
|
||||
|
|
@ -311,7 +322,49 @@ var fourSum = function(nums, target) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function fourSum(nums: number[], target: number): number[][] {
|
||||
nums.sort((a, b) => a - b);
|
||||
let first: number = 0,
|
||||
second: number,
|
||||
third: number,
|
||||
fourth: number;
|
||||
let length: number = nums.length;
|
||||
let resArr: number[][] = [];
|
||||
for (; first < length; first++) {
|
||||
if (first > 0 && nums[first] === nums[first - 1]) {
|
||||
continue;
|
||||
}
|
||||
for (second = first + 1; second < length; second++) {
|
||||
if ((second - first) > 1 && nums[second] === nums[second - 1]) {
|
||||
continue;
|
||||
}
|
||||
third = second + 1;
|
||||
fourth = length - 1;
|
||||
while (third < fourth) {
|
||||
let total: number = nums[first] + nums[second] + nums[third] + nums[fourth];
|
||||
if (total === target) {
|
||||
resArr.push([nums[first], nums[second], nums[third], nums[fourth]]);
|
||||
third++;
|
||||
fourth--;
|
||||
while (nums[third] === nums[third - 1]) third++;
|
||||
while (nums[fourth] === nums[fourth + 1]) fourth--;
|
||||
} else if (total < target) {
|
||||
third++;
|
||||
} else {
|
||||
fourth--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
PHP:
|
||||
|
||||
```php
|
||||
class Solution {
|
||||
/**
|
||||
|
|
@ -403,5 +456,67 @@ func fourSum(_ nums: [Int], _ target: Int) -> [[Int]] {
|
|||
}
|
||||
```
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public class Solution
|
||||
{
|
||||
public IList<IList<int>> FourSum(int[] nums, int target)
|
||||
{
|
||||
var result = new List<IList<int>>();
|
||||
|
||||
Array.Sort(nums);
|
||||
|
||||
for (int i = 0; i < nums.Length - 3; i++)
|
||||
{
|
||||
int n1 = nums[i];
|
||||
if (i > 0 && n1 == nums[i - 1])
|
||||
continue;
|
||||
|
||||
for (int j = i + 1; j < nums.Length - 2; j++)
|
||||
{
|
||||
int n2 = nums[j];
|
||||
if (j > i + 1 && n2 == nums[j - 1])
|
||||
continue;
|
||||
|
||||
int left = j + 1;
|
||||
int right = nums.Length - 1;
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
int n3 = nums[left];
|
||||
int n4 = nums[right];
|
||||
int sum = n1 + n2 + n3 + n4;
|
||||
|
||||
if (sum > target)
|
||||
{
|
||||
right--;
|
||||
}
|
||||
else if (sum < target)
|
||||
{
|
||||
left++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(new List<int> { n1, n2, n3, n4 });
|
||||
|
||||
while (left < right && nums[left] == n3)
|
||||
{
|
||||
left++;
|
||||
}
|
||||
|
||||
while (left < right && nums[right] == n4)
|
||||
{
|
||||
right--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -181,7 +181,71 @@ var removeNthFromEnd = function(head, n) {
|
|||
return ret.next;
|
||||
};
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
版本一(快慢指针法):
|
||||
|
||||
```typescript
|
||||
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
|
||||
let newHead: ListNode | null = new ListNode(0, head);
|
||||
let slowNode: ListNode | null = newHead,
|
||||
fastNode: ListNode | null = newHead;
|
||||
for (let i = 0; i < n; i++) {
|
||||
fastNode = fastNode.next;
|
||||
}
|
||||
while (fastNode.next) {
|
||||
fastNode = fastNode.next;
|
||||
slowNode = slowNode.next;
|
||||
}
|
||||
slowNode.next = slowNode.next.next;
|
||||
return newHead.next;
|
||||
};
|
||||
```
|
||||
|
||||
版本二(计算节点总数法):
|
||||
|
||||
```typescript
|
||||
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
|
||||
let curNode: ListNode | null = head;
|
||||
let listSize: number = 0;
|
||||
while (curNode) {
|
||||
curNode = curNode.next;
|
||||
listSize++;
|
||||
}
|
||||
if (listSize === n) {
|
||||
head = head.next;
|
||||
} else {
|
||||
curNode = head;
|
||||
for (let i = 0; i < listSize - n - 1; i++) {
|
||||
curNode = curNode.next;
|
||||
}
|
||||
curNode.next = curNode.next.next;
|
||||
}
|
||||
return head;
|
||||
};
|
||||
```
|
||||
|
||||
版本三(递归倒退n法):
|
||||
|
||||
```typescript
|
||||
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
|
||||
let newHead: ListNode | null = new ListNode(0, head);
|
||||
let cnt = 0;
|
||||
function recur(node) {
|
||||
if (node === null) return;
|
||||
recur(node.next);
|
||||
cnt++;
|
||||
if (cnt === n + 1) {
|
||||
node.next = node.next.next;
|
||||
}
|
||||
}
|
||||
recur(newHead);
|
||||
return newHead.next;
|
||||
};
|
||||
```
|
||||
|
||||
Kotlin:
|
||||
|
||||
```Kotlin
|
||||
fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? {
|
||||
val pre = ListNode(0).apply {
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ public:
|
|||
}
|
||||
};
|
||||
```
|
||||
技巧性的东西没有固定的学习方法,还是要多看多练,自己总灵活运用了。
|
||||
技巧性的东西没有固定的学习方法,还是要多看多练,自己灵活运用了。
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
|
|
@ -159,7 +159,7 @@ class Solution {
|
|||
```
|
||||
|
||||
Python:
|
||||
```python3
|
||||
```python
|
||||
# 方法一,仅使用栈,更省空间
|
||||
class Solution:
|
||||
def isValid(self, s: str) -> bool:
|
||||
|
|
@ -180,7 +180,7 @@ class Solution:
|
|||
return True if not stack else False
|
||||
```
|
||||
|
||||
```python3
|
||||
```python
|
||||
# 方法二,使用字典
|
||||
class Solution:
|
||||
def isValid(self, s: str) -> bool:
|
||||
|
|
@ -283,8 +283,60 @@ var isValid = function(s) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
版本一:普通版
|
||||
|
||||
```typescript
|
||||
function isValid(s: string): boolean {
|
||||
let helperStack: string[] = [];
|
||||
for (let i = 0, length = s.length; i < length; i++) {
|
||||
let x: string = s[i];
|
||||
switch (x) {
|
||||
case '(':
|
||||
helperStack.push(')');
|
||||
break;
|
||||
case '[':
|
||||
helperStack.push(']');
|
||||
break;
|
||||
case '{':
|
||||
helperStack.push('}');
|
||||
break;
|
||||
default:
|
||||
if (helperStack.pop() !== x) return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return helperStack.length === 0;
|
||||
};
|
||||
```
|
||||
|
||||
版本二:优化版
|
||||
|
||||
```typescript
|
||||
function isValid(s: string): boolean {
|
||||
type BracketMap = {
|
||||
[index: string]: string;
|
||||
}
|
||||
let helperStack: string[] = [];
|
||||
let bracketMap: BracketMap = {
|
||||
'(': ')',
|
||||
'[': ']',
|
||||
'{': '}'
|
||||
}
|
||||
for (let i of s) {
|
||||
if (bracketMap.hasOwnProperty(i)) {
|
||||
helperStack.push(bracketMap[i]);
|
||||
} else if (i !== helperStack.pop()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return helperStack.length === 0;
|
||||
};
|
||||
```
|
||||
|
||||
Swift
|
||||
|
||||
```swift
|
||||
func isValid(_ s: String) -> Bool {
|
||||
var stack = [String.Element]()
|
||||
|
|
|
|||
|
|
@ -250,6 +250,26 @@ var swapPairs = function (head) {
|
|||
};
|
||||
```
|
||||
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
Kotlin:
|
||||
|
||||
```kotlin
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
|
||||
|
||||
不要使用额外的数组空间,你必须仅使用 $O(1)$ 额外空间并**原地**修改输入数组。
|
||||
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并**原地**修改输入数组。
|
||||
|
||||
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
|
||||
|
||||
|
|
@ -68,8 +68,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n^2)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n^2)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
### 双指针法
|
||||
|
||||
|
|
@ -101,11 +101,42 @@ public:
|
|||
```
|
||||
注意这些实现方法并没有改变元素的相对位置!
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
旧文链接:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)
|
||||
|
||||
```CPP
|
||||
/**
|
||||
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
*/
|
||||
class Solution {
|
||||
public:
|
||||
int removeElement(vector<int>& nums, int val) {
|
||||
int leftIndex = 0;
|
||||
int rightIndex = nums.size() - 1;
|
||||
while (leftIndex <= rightIndex) {
|
||||
// 找左边等于val的元素
|
||||
while (leftIndex <= rightIndex && nums[leftIndex] != val){
|
||||
++leftIndex;
|
||||
}
|
||||
// 找右边不等于val的元素
|
||||
while (leftIndex <= rightIndex && nums[rightIndex] == val) {
|
||||
-- rightIndex;
|
||||
}
|
||||
// 将右边不等于val的元素覆盖左边等于val的元素
|
||||
if (leftIndex < rightIndex) {
|
||||
nums[leftIndex++] = nums[rightIndex--];
|
||||
}
|
||||
}
|
||||
return leftIndex; // leftIndex一定指向了最终数组末尾的下一个元素
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## 相关题目推荐
|
||||
|
||||
* 26.删除排序数组中的重复项
|
||||
|
|
@ -142,7 +173,7 @@ class Solution {
|
|||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
"""双指针法
|
||||
时间复杂度:O(n)
|
||||
|
|
|
|||
|
|
@ -229,9 +229,9 @@ next数组就可以是前缀表,但是很多实现都是把前缀表统一减
|
|||
|
||||
# 时间复杂度分析
|
||||
|
||||
其中n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是$O(n)$,之前还要单独生成next数组,时间复杂度是$O(m)$。所以整个KMP算法的时间复杂度是$O(n+m)$的。
|
||||
其中n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是O(n),之前还要单独生成next数组,时间复杂度是O(m)。所以整个KMP算法的时间复杂度是O(n+m)的。
|
||||
|
||||
暴力的解法显而易见是$O(n × m)$,所以**KMP在字符串匹配中极大的提高的搜索的效率。**
|
||||
暴力的解法显而易见是O(n × m),所以**KMP在字符串匹配中极大的提高的搜索的效率。**
|
||||
|
||||
为了和力扣题目28.实现strStr保持一致,方便大家理解,以下文章统称haystack为文本串, needle为模式串。
|
||||
|
||||
|
|
@ -255,11 +255,11 @@ void getNext(int* next, const string& s)
|
|||
|
||||
1. 初始化:
|
||||
|
||||
定义两个指针i和j,j指向前缀起始位置,i指向后缀起始位置。
|
||||
定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。
|
||||
|
||||
然后还要对next数组进行初始化赋值,如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
int j = -1;
|
||||
next[0] = j;
|
||||
```
|
||||
|
|
@ -278,7 +278,7 @@ next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是
|
|||
|
||||
所以遍历模式串s的循环下标i 要从 1开始,代码如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
for (int i = 1; i < s.size(); i++) {
|
||||
```
|
||||
|
||||
|
|
@ -292,7 +292,7 @@ next[j]就是记录着j(包括j)之前的子串的相同前后缀的长度
|
|||
|
||||
所以,处理前后缀不相同的情况代码如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
|
||||
j = next[j]; // 向前回退
|
||||
}
|
||||
|
|
@ -346,7 +346,7 @@ void getNext(int* next, const string& s){
|
|||
|
||||
i就从0开始,遍历文本串,代码如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
for (int i = 0; i < s.size(); i++)
|
||||
```
|
||||
|
||||
|
|
@ -356,7 +356,7 @@ for (int i = 0; i < s.size(); i++)
|
|||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
while(j >= 0 && s[i] != t[j + 1]) {
|
||||
j = next[j];
|
||||
}
|
||||
|
|
@ -364,7 +364,7 @@ while(j >= 0 && s[i] != t[j + 1]) {
|
|||
|
||||
如果 s[i] 与 t[j + 1] 相同,那么i 和 j 同时向后移动, 代码如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
if (s[i] == t[j + 1]) {
|
||||
j++; // i的增加在for循环里
|
||||
}
|
||||
|
|
@ -376,7 +376,7 @@ if (s[i] == t[j + 1]) {
|
|||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
if (j == (t.size() - 1) ) {
|
||||
return (i - t.size() + 1);
|
||||
}
|
||||
|
|
@ -929,6 +929,83 @@ var strStr = function (haystack, needle) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript版本:
|
||||
|
||||
> 前缀表统一减一
|
||||
|
||||
```typescript
|
||||
function strStr(haystack: string, needle: string): number {
|
||||
function getNext(str: string): number[] {
|
||||
let next: number[] = [];
|
||||
let j: number = -1;
|
||||
next[0] = j;
|
||||
for (let i = 1, length = str.length; i < length; i++) {
|
||||
while (j >= 0 && str[i] !== str[j + 1]) {
|
||||
j = next[j];
|
||||
}
|
||||
if (str[i] === str[j + 1]) {
|
||||
j++;
|
||||
}
|
||||
next[i] = j;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
if (needle.length === 0) return 0;
|
||||
let next: number[] = getNext(needle);
|
||||
let j: number = -1;
|
||||
for (let i = 0, length = haystack.length; i < length; i++) {
|
||||
while (j >= 0 && haystack[i] !== needle[j + 1]) {
|
||||
j = next[j];
|
||||
}
|
||||
if (haystack[i] === needle[j + 1]) {
|
||||
if (j === needle.length - 2) {
|
||||
return i - j - 1;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
```
|
||||
|
||||
> 前缀表不减一
|
||||
|
||||
```typescript
|
||||
// 不减一版本
|
||||
function strStr(haystack: string, needle: string): number {
|
||||
function getNext(str: string): number[] {
|
||||
let next: number[] = [];
|
||||
let j: number = 0;
|
||||
next[0] = j;
|
||||
for (let i = 1, length = str.length; i < length; i++) {
|
||||
while (j > 0 && str[i] !== str[j]) {
|
||||
j = next[j - 1];
|
||||
}
|
||||
if (str[i] === str[j]) {
|
||||
j++;
|
||||
}
|
||||
next[i] = j;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
if (needle.length === 0) return 0;
|
||||
let next: number[] = getNext(needle);
|
||||
let j: number = 0;
|
||||
for (let i = 0, length = haystack.length; i < length; i++) {
|
||||
while (j > 0 && haystack[i] !== needle[j]) {
|
||||
j = next[j - 1];
|
||||
}
|
||||
if (haystack[i] === needle[j]) {
|
||||
if (j === needle.length - 1) {
|
||||
return i - j;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
Swift 版本
|
||||
|
||||
> 前缀表统一减一
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ public:
|
|||
for (int j = nums.size() - 1; j > i; j--) {
|
||||
if (nums[j] > nums[i]) {
|
||||
swap(nums[j], nums[i]);
|
||||
sort(nums.begin() + i + 1, nums.end());
|
||||
reverse(nums.begin() + i + 1, nums.end());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -389,6 +389,51 @@ class Solution:
|
|||
### Go
|
||||
|
||||
```go
|
||||
func searchRange(nums []int, target int) []int {
|
||||
leftBorder := getLeft(nums, target)
|
||||
rightBorder := getRight(nums, target)
|
||||
// 情况一
|
||||
if leftBorder == -2 || rightBorder == -2 {
|
||||
return []int{-1, -1}
|
||||
}
|
||||
// 情况三
|
||||
if rightBorder - leftBorder > 1 {
|
||||
return []int{leftBorder + 1, rightBorder - 1}
|
||||
}
|
||||
// 情况二
|
||||
return []int{-1, -1}
|
||||
}
|
||||
|
||||
func getLeft(nums []int, target int) int {
|
||||
left, right := 0, len(nums)-1
|
||||
border := -2 // 记录border没有被赋值的情况;这里不能赋值-1,target = num[0]时,会无法区分情况一和情况二
|
||||
for left <= right { // []闭区间
|
||||
mid := left + ((right - left) >> 1)
|
||||
if nums[mid] >= target { // 找到第一个等于target的位置
|
||||
right = mid - 1
|
||||
border = right
|
||||
} else {
|
||||
left = mid + 1
|
||||
}
|
||||
}
|
||||
return border
|
||||
}
|
||||
|
||||
func getRight(nums []int, target int) int {
|
||||
left, right := 0, len(nums) - 1
|
||||
border := -2
|
||||
for left <= right {
|
||||
mid := left + ((right - left) >> 1)
|
||||
if nums[mid] > target {
|
||||
right = mid - 1
|
||||
} else { // 找到第一个大于target的位置
|
||||
left = mid + 1
|
||||
border = left
|
||||
}
|
||||
}
|
||||
return border
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ func searchInsert(nums []int, target int) int {
|
|||
```
|
||||
|
||||
### Python
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def searchInsert(self, nums: List[int], target: int) -> int:
|
||||
left, right = 0, len(nums) - 1
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ class Solution {
|
|||
|
||||
## Python
|
||||
**回溯**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.path = []
|
||||
|
|
@ -296,7 +296,7 @@ class Solution:
|
|||
self.path.pop() # 回溯
|
||||
```
|
||||
**剪枝回溯**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.path = []
|
||||
|
|
@ -392,7 +392,34 @@ var combinationSum = function(candidates, target) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function combinationSum(candidates: number[], target: number): number[][] {
|
||||
const resArr: number[][] = [];
|
||||
function backTracking(
|
||||
candidates: number[], target: number,
|
||||
startIndex: number, route: number[], curSum: number
|
||||
): void {
|
||||
if (curSum > target) return;
|
||||
if (curSum === target) {
|
||||
resArr.push(route.slice());
|
||||
return
|
||||
}
|
||||
for (let i = startIndex, length = candidates.length; i < length; i++) {
|
||||
let tempVal: number = candidates[i];
|
||||
route.push(tempVal);
|
||||
backTracking(candidates, target, i, route, curSum + tempVal);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
backTracking(candidates, target, 0, [], 0);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
```c
|
||||
int* path;
|
||||
int pathTop;
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ class Solution {
|
|||
|
||||
## Python
|
||||
**回溯+巧妙去重(省去使用used**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.paths = []
|
||||
|
|
@ -374,7 +374,7 @@ class Solution:
|
|||
sum_ -= candidates[i] # 回溯,为了下一轮for loop
|
||||
```
|
||||
**回溯+去重(使用used)**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.paths = []
|
||||
|
|
@ -532,6 +532,7 @@ var combinationSum2 = function(candidates, target) {
|
|||
};
|
||||
```
|
||||
**使用used去重**
|
||||
|
||||
```js
|
||||
var combinationSum2 = function(candidates, target) {
|
||||
let res = [];
|
||||
|
|
@ -562,6 +563,37 @@ var combinationSum2 = function(candidates, target) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function combinationSum2(candidates: number[], target: number): number[][] {
|
||||
candidates.sort((a, b) => a - b);
|
||||
const resArr: number[][] = [];
|
||||
function backTracking(
|
||||
candidates: number[], target: number,
|
||||
curSum: number, startIndex: number, route: number[]
|
||||
) {
|
||||
if (curSum > target) return;
|
||||
if (curSum === target) {
|
||||
resArr.push(route.slice());
|
||||
return;
|
||||
}
|
||||
for (let i = startIndex, length = candidates.length; i < length; i++) {
|
||||
if (i > startIndex && candidates[i] === candidates[i - 1]) {
|
||||
continue;
|
||||
}
|
||||
let tempVal: number = candidates[i];
|
||||
route.push(tempVal);
|
||||
backTracking(candidates, target, curSum + tempVal, i + 1, route);
|
||||
route.pop();
|
||||
|
||||
}
|
||||
}
|
||||
backTracking(candidates, target, 0, 0, []);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
```c
|
||||
|
|
|
|||
|
|
@ -129,8 +129,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为$O(n^2)$。
|
||||
空间复杂度为$O(1)$。
|
||||
因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2)。
|
||||
空间复杂度为O(1)。
|
||||
|
||||
|
||||
|
||||
|
|
@ -365,7 +365,7 @@ public:
|
|||
|
||||
## 其他语言版本
|
||||
|
||||
Java:
|
||||
### Java:
|
||||
|
||||
双指针法
|
||||
```java
|
||||
|
|
@ -468,7 +468,7 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
### Python:
|
||||
|
||||
双指针法
|
||||
```python3
|
||||
|
|
@ -491,7 +491,7 @@ class Solution:
|
|||
return res
|
||||
```
|
||||
动态规划
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def trap(self, height: List[int]) -> int:
|
||||
leftheight, rightheight = [0]*len(height), [0]*len(height)
|
||||
|
|
@ -575,7 +575,7 @@ class Solution:
|
|||
|
||||
```
|
||||
|
||||
Go:
|
||||
### Go
|
||||
|
||||
```go
|
||||
func trap(height []int) int {
|
||||
|
|
@ -642,7 +642,7 @@ func min(a,b int)int{
|
|||
|
||||
|
||||
|
||||
JavaScript:
|
||||
### JavaScript:
|
||||
|
||||
```javascript
|
||||
//双指针
|
||||
|
|
@ -744,7 +744,7 @@ var trap = function(height) {
|
|||
};
|
||||
```
|
||||
|
||||
C:
|
||||
### C:
|
||||
|
||||
一种更简便的双指针方法:
|
||||
|
||||
|
|
@ -779,8 +779,8 @@ int trap(int* height, int heightSize) {
|
|||
}
|
||||
```
|
||||
|
||||
* 时间复杂度 $O(n)$
|
||||
* 空间复杂度 $O(1)$
|
||||
* 时间复杂度 O(n)
|
||||
* 空间复杂度 O(1)
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ public:
|
|||
|
||||
### Java
|
||||
```Java
|
||||
// 版本一
|
||||
class Solution {
|
||||
public int jump(int[] nums) {
|
||||
if (nums == null || nums.length == 0 || nums.length == 1) {
|
||||
|
|
@ -172,7 +173,30 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 版本二
|
||||
class Solution {
|
||||
public int jump(int[] nums) {
|
||||
int result = 0;
|
||||
// 当前覆盖的最远距离下标
|
||||
int end = 0;
|
||||
// 下一步覆盖的最远距离下标
|
||||
int temp = 0;
|
||||
for (int i = 0; i <= end && end < nums.length - 1; ++i) {
|
||||
temp = Math.max(temp, i + nums[i]);
|
||||
// 可达位置的改变次数就是跳跃次数
|
||||
if (i == end) {
|
||||
end = temp;
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def jump(self, nums: List[int]) -> int:
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ class Solution:
|
|||
usage_list[i] = False
|
||||
```
|
||||
**回溯+丢掉usage_list**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.path = []
|
||||
|
|
@ -331,6 +331,34 @@ var permute = function(nums) {
|
|||
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function permute(nums: number[]): number[][] {
|
||||
const resArr: number[][] = [];
|
||||
const helperSet: Set<number> = new Set();
|
||||
backTracking(nums, []);
|
||||
return resArr;
|
||||
function backTracking(nums: number[], route: number[]): void {
|
||||
if (route.length === nums.length) {
|
||||
resArr.push(route.slice());
|
||||
return;
|
||||
}
|
||||
let tempVal: number;
|
||||
for (let i = 0, length = nums.length; i < length; i++) {
|
||||
tempVal = nums[i];
|
||||
if (!helperSet.has(tempVal)) {
|
||||
route.push(tempVal);
|
||||
helperSet.add(tempVal);
|
||||
backTracking(nums, route);
|
||||
route.pop();
|
||||
helperSet.delete(tempVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
```c
|
||||
|
|
|
|||
|
|
@ -292,6 +292,34 @@ var permuteUnique = function (nums) {
|
|||
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function permuteUnique(nums: number[]): number[][] {
|
||||
nums.sort((a, b) => a - b);
|
||||
const resArr: number[][] = [];
|
||||
const usedArr: boolean[] = new Array(nums.length).fill(false);
|
||||
backTracking(nums, []);
|
||||
return resArr;
|
||||
function backTracking(nums: number[], route: number[]): void {
|
||||
if (route.length === nums.length) {
|
||||
resArr.push(route.slice());
|
||||
return;
|
||||
}
|
||||
for (let i = 0, length = nums.length; i < length; i++) {
|
||||
if (i > 0 && nums[i] === nums[i - 1] && usedArr[i - 1] === false) continue;
|
||||
if (usedArr[i] === false) {
|
||||
route.push(nums[i]);
|
||||
usedArr[i] = true;
|
||||
backTracking(nums, route);
|
||||
usedArr[i] = false;
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Swift
|
||||
|
||||
```swift
|
||||
|
|
|
|||
|
|
@ -346,70 +346,57 @@ class Solution {
|
|||
|
||||
### Go
|
||||
```Go
|
||||
import "strings"
|
||||
func solveNQueens(n int) [][]string {
|
||||
var res [][]string
|
||||
|
||||
func isValid(board [][]string, row, col int) (res bool){
|
||||
n := len(board)
|
||||
for i:=0; i < row; i++ {
|
||||
if board[i][col] == "Q" {
|
||||
return false
|
||||
}
|
||||
chessboard := make([][]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
chessboard[i] = make([]string, n)
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if board[row][i] == "Q" {
|
||||
return false
|
||||
for j := 0; j < n; j++ {
|
||||
chessboard[i][j] = "."
|
||||
}
|
||||
}
|
||||
|
||||
for i ,j := row, col; i >= 0 && j >=0 ; i, j = i - 1, j- 1{
|
||||
if board[i][j] == "Q"{
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i, j := row, col; i >=0 && j < n; i,j = i-1, j+1 {
|
||||
if board[i][j] == "Q" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func backtrack(board [][]string, row int) {
|
||||
size := len(board)
|
||||
if row == size{
|
||||
temp := make([]string, size)
|
||||
for i := 0; i<size;i++{
|
||||
temp[i] = strings.Join(board[i],"")
|
||||
var backtrack func(int)
|
||||
backtrack = func(row int) {
|
||||
if row == n {
|
||||
temp := make([]string, n)
|
||||
for i, rowStr := range chessboard {
|
||||
temp[i] = strings.Join(rowStr, "")
|
||||
}
|
||||
res = append(res, temp)
|
||||
return
|
||||
}
|
||||
for col := 0; col < size; col++ {
|
||||
if !isValid(board, row, col){
|
||||
continue
|
||||
}
|
||||
board[row][col] = "Q"
|
||||
backtrack(board, row+1)
|
||||
board[row][col] = "."
|
||||
}
|
||||
}
|
||||
|
||||
func solveNQueens(n int) [][]string {
|
||||
res = [][]string{}
|
||||
board := make([][]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
board[i] = make([]string, n)
|
||||
}
|
||||
for i := 0; i < n; i++{
|
||||
for j := 0; j<n;j++{
|
||||
board[i][j] = "."
|
||||
if isValid(n, row, i, chessboard) {
|
||||
chessboard[row][i] = "Q"
|
||||
backtrack(row + 1)
|
||||
chessboard[row][i] = "."
|
||||
}
|
||||
}
|
||||
backtrack(board, 0)
|
||||
|
||||
}
|
||||
backtrack(0)
|
||||
return res
|
||||
}
|
||||
|
||||
func isValid(n, row, col int, chessboard [][]string) bool {
|
||||
for i := 0; i < row; i++ {
|
||||
if chessboard[i][col] == "Q" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i, j := row-1, col-1; i >= 0 && j >= 0; i, j = i-1, j-1 {
|
||||
if chessboard[i][j] == "Q" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i, j := row-1, col+1; i >= 0 && j < n; i, j = i-1, j+1 {
|
||||
if chessboard[i][j] == "Q" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
```
|
||||
### Javascript
|
||||
```Javascript
|
||||
|
|
@ -470,6 +457,58 @@ var solveNQueens = function(n) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function solveNQueens(n: number): string[][] {
|
||||
const board: string[][] = new Array(n).fill(0).map(_ => new Array(n).fill('.'));
|
||||
const resArr: string[][] = [];
|
||||
backTracking(n, 0, board);
|
||||
return resArr;
|
||||
function backTracking(n: number, rowNum: number, board: string[][]): void {
|
||||
if (rowNum === n) {
|
||||
resArr.push(transformBoard(board));
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < n; i++) {
|
||||
if (isValid(i, rowNum, board) === true) {
|
||||
board[rowNum][i] = 'Q';
|
||||
backTracking(n, rowNum + 1, board);
|
||||
board[rowNum][i] = '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
function isValid(col: number, row: number, board: string[][]): boolean {
|
||||
const n: number = board.length;
|
||||
if (col < 0 || col >= n || row < 0 || row >= n) return false;
|
||||
// 检查列
|
||||
for (let row of board) {
|
||||
if (row[col] === 'Q') return false;
|
||||
}
|
||||
// 检查45度方向
|
||||
let x: number = col,
|
||||
y: number = row;
|
||||
while (y >= 0 && x < n) {
|
||||
if (board[y--][x++] === 'Q') return false;
|
||||
}
|
||||
// 检查135度方向
|
||||
x = col;
|
||||
y = row;
|
||||
while (x >= 0 && y >= 0) {
|
||||
if (board[y--][x--] === 'Q') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function transformBoard(board: string[][]): string[] {
|
||||
const resArr = [];
|
||||
for (let row of board) {
|
||||
resArr.push(row.join(''));
|
||||
}
|
||||
return resArr;
|
||||
}
|
||||
```
|
||||
|
||||
### Swift
|
||||
|
||||
```swift
|
||||
|
|
|
|||
|
|
@ -143,5 +143,65 @@ var totalNQueens = function(n) {
|
|||
return count;
|
||||
};
|
||||
```
|
||||
|
||||
C
|
||||
```c
|
||||
//path[i]为在i行,path[i]列上存在皇后
|
||||
int *path;
|
||||
int pathTop;
|
||||
int answer;
|
||||
//检查当前level行index列放置皇后是否合法
|
||||
int isValid(int index, int level) {
|
||||
int i;
|
||||
//updater为若斜角存在皇后,其所应在的列
|
||||
//用来检查左上45度是否存在皇后
|
||||
int lCornerUpdater = index - level;
|
||||
//用来检查右上135度是否存在皇后
|
||||
int rCornerUpdater = index + level;
|
||||
for(i = 0; i < pathTop; ++i) {
|
||||
//path[i] == index检查index列是否存在皇后
|
||||
//检查斜角皇后:只要path[i] == updater,就说明当前位置不可放置皇后。
|
||||
//path[i] == lCornerUpdater检查左上角45度是否有皇后
|
||||
//path[i] == rCornerUpdater检查右上角135度是否有皇后
|
||||
if(path[i] == index || path[i] == lCornerUpdater || path[i] == rCornerUpdater)
|
||||
return 0;
|
||||
//更新updater指向下一行对应的位置
|
||||
++lCornerUpdater;
|
||||
--rCornerUpdater;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//回溯算法:level为当前皇后行数
|
||||
void backTracking(int n, int level) {
|
||||
//若path中元素个数已经为n,则证明有一种解法。answer+1
|
||||
if(pathTop == n) {
|
||||
++answer;
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
for(i = 0; i < n; ++i) {
|
||||
//若当前level行,i列是合法的放置位置。就将i放入path中
|
||||
if(isValid(i, level)) {
|
||||
path[pathTop++] = i;
|
||||
backTracking(n, level + 1);
|
||||
//回溯
|
||||
--pathTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int totalNQueens(int n){
|
||||
answer = 0;
|
||||
pathTop = 0;
|
||||
path = (int *)malloc(sizeof(int) * n);
|
||||
|
||||
backTracking(n, 0);
|
||||
|
||||
return answer;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
暴力解法的思路,第一层for 就是设置起始位置,第二层for循环遍历数组寻找最大值
|
||||
|
||||
* 时间复杂度:$O(n^2)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n^2)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
```CPP
|
||||
class Solution {
|
||||
|
|
@ -98,8 +98,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。
|
||||
|
||||
|
|
@ -128,8 +128,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
## 总结
|
||||
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
|
||||
## 总结
|
||||
|
|
@ -174,6 +174,7 @@ const maxSubArray = nums => {
|
|||
// 数组长度,dp初始化
|
||||
const len = nums.length;
|
||||
let dp = new Array(len).fill(0);
|
||||
dp[0] = nums[0];
|
||||
// 最大值初始化为dp[0]
|
||||
let max = dp[0];
|
||||
for (let i = 1; i < len; i++) {
|
||||
|
|
|
|||
|
|
@ -131,7 +131,46 @@ public:
|
|||
* [59.螺旋矩阵II](https://leetcode-cn.com/problems/spiral-matrix-ii/)
|
||||
* [剑指Offer 29.顺时针打印矩阵](https://leetcode-cn.com/problems/shun-shi-zhen-da-yin-ju-zhen-lcof/)
|
||||
|
||||
## 其他语言版本
|
||||
Python:
|
||||
```python
|
||||
class Solution:
|
||||
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
|
||||
m, n = len(matrix), len(matrix[0])
|
||||
left, right, up, down = 0, n - 1, 0, m - 1 # 定位四个方向的边界,闭区间
|
||||
res = []
|
||||
|
||||
while True:
|
||||
for i in range(left, right + 1): # 上边,从左到右
|
||||
res.append(matrix[up][i])
|
||||
up += 1 # 上边界下移
|
||||
|
||||
if len(res) >= m * n: # 判断是否已经遍历完
|
||||
break
|
||||
|
||||
for i in range(up, down + 1): # 右边,从上到下
|
||||
res.append(matrix[i][right])
|
||||
right -= 1 # 右边界左移
|
||||
|
||||
if len(res) >= m * n:
|
||||
break
|
||||
|
||||
for i in range(right, left - 1, -1): # 下边,从右到左
|
||||
res.append(matrix[down][i])
|
||||
down -= 1 # 下边界上移
|
||||
|
||||
if len(res) >= m * n:
|
||||
break
|
||||
|
||||
for i in range(down, up - 1, -1): # 左边,从下到上
|
||||
res.append(matrix[i][left])
|
||||
left += 1 # 左边界右移
|
||||
|
||||
if len(res) >= m * n:
|
||||
break
|
||||
|
||||
return res
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/spiral-matrix-ii/)
|
||||
|
||||
给定一个正整数 n,生成一个包含 1 到 $n^2$ 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
|
||||
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
|
||||
|
||||
示例:
|
||||
|
||||
|
|
@ -188,51 +188,35 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
python:
|
||||
python3:
|
||||
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
|
||||
def generateMatrix(self, n: int) -> List[List[int]]:
|
||||
# 初始化要填充的正方形
|
||||
matrix = [[0] * n for _ in range(n)]
|
||||
nums = [[0] * n for _ in range(n)]
|
||||
startx, starty = 0, 0 # 起始点
|
||||
loop, mid = n // 2, n // 2 # 迭代次数、n为奇数时,矩阵的中心点
|
||||
count = 1 # 计数
|
||||
|
||||
left, right, up, down = 0, n - 1, 0, n - 1
|
||||
number = 1 # 要填充的数字
|
||||
for offset in range(1, loop + 1) : # 每循环一层偏移量加1,偏移量从1开始
|
||||
for i in range(starty, n - offset) : # 从左至右,左闭右开
|
||||
nums[startx][i] = count
|
||||
count += 1
|
||||
for i in range(startx, n - offset) : # 从上至下
|
||||
nums[i][n - offset] = count
|
||||
count += 1
|
||||
for i in range(n - offset, starty, -1) : # 从右至左
|
||||
nums[n - offset][i] = count
|
||||
count += 1
|
||||
for i in range(n - offset, startx, -1) : # 从下至上
|
||||
nums[i][starty] = count
|
||||
count += 1
|
||||
startx += 1 # 更新起始点
|
||||
starty += 1
|
||||
|
||||
while left < right and up < down:
|
||||
|
||||
# 从左到右填充上边
|
||||
for x in range(left, right):
|
||||
matrix[up][x] = number
|
||||
number += 1
|
||||
|
||||
# 从上到下填充右边
|
||||
for y in range(up, down):
|
||||
matrix[y][right] = number
|
||||
number += 1
|
||||
|
||||
# 从右到左填充下边
|
||||
for x in range(right, left, -1):
|
||||
matrix[down][x] = number
|
||||
number += 1
|
||||
|
||||
# 从下到上填充左边
|
||||
for y in range(down, up, -1):
|
||||
matrix[y][left] = number
|
||||
number += 1
|
||||
|
||||
# 缩小要填充的范围
|
||||
left += 1
|
||||
right -= 1
|
||||
up += 1
|
||||
down -= 1
|
||||
|
||||
# 如果阶数为奇数,额外填充一次中心
|
||||
if n % 2:
|
||||
matrix[n // 2][n // 2] = number
|
||||
|
||||
return matrix
|
||||
if n % 2 != 0 : # n为奇数时,填充中心点
|
||||
nums[mid][mid] = count
|
||||
return nums
|
||||
```
|
||||
|
||||
javaScript
|
||||
|
|
@ -287,6 +271,51 @@ var generateMatrix = function(n) {
|
|||
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function generateMatrix(n: number): number[][] {
|
||||
let loopNum: number = Math.floor(n / 2);
|
||||
const resArr: number[][] = new Array(n).fill(1).map(i => new Array(n));
|
||||
let chunkNum: number = n - 1;
|
||||
let startX: number = 0;
|
||||
let startY: number = 0;
|
||||
let value: number = 1;
|
||||
let x: number, y: number;
|
||||
while (loopNum--) {
|
||||
x = startX;
|
||||
y = startY;
|
||||
while (x < startX + chunkNum) {
|
||||
resArr[y][x] = value;
|
||||
x++;
|
||||
value++;
|
||||
}
|
||||
while (y < startY + chunkNum) {
|
||||
resArr[y][x] = value;
|
||||
y++;
|
||||
value++;
|
||||
}
|
||||
while (x > startX) {
|
||||
resArr[y][x] = value;
|
||||
x--;
|
||||
value++;
|
||||
}
|
||||
while (y > startY) {
|
||||
resArr[y][x] = value;
|
||||
y--;
|
||||
value++;
|
||||
}
|
||||
startX++;
|
||||
startY++;
|
||||
chunkNum -= 2;
|
||||
}
|
||||
if (n % 2 === 1) {
|
||||
resArr[startX][startY] = value;
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public:
|
|||
|
||||
那二叉树的节点个数就是 2^(m + n - 1) - 1。可以理解深搜的算法就是遍历了整个满二叉树(其实没有遍历整个满二叉树,只是近似而已)
|
||||
|
||||
所以上面深搜代码的时间复杂度为$O(2^{m + n - 1} - 1)$,可以看出,这是指数级别的时间复杂度,是非常大的。
|
||||
所以上面深搜代码的时间复杂度为O(2^(m + n - 1) - 1),可以看出,这是指数级别的时间复杂度,是非常大的。
|
||||
|
||||
### 动态规划
|
||||
|
||||
|
|
@ -143,8 +143,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(m × n)$
|
||||
* 空间复杂度:$O(m × n)$
|
||||
* 时间复杂度:O(m × n)
|
||||
* 空间复杂度:O(m × n)
|
||||
|
||||
其实用一个一维数组(也可以理解是滚动数组)就可以了,但是不利于理解,可以优化点空间,建议先理解了二维,在理解一维,C++代码如下:
|
||||
|
||||
|
|
@ -164,8 +164,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(m × n)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(m × n)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
### 数论方法
|
||||
|
||||
|
|
@ -224,8 +224,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(m)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(m)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
**计算组合问题的代码还是有难度的,特别是处理溢出的情况!**
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
</a>
|
||||
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
## 63. 不同路径 II
|
||||
# 63. 不同路径 II
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/unique-paths-ii/)
|
||||
|
||||
|
|
@ -22,11 +22,11 @@
|
|||
|
||||

|
||||
|
||||
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
|
||||
输出:2
|
||||
* 输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
|
||||
* 输出:2
|
||||
解释:
|
||||
3x3 网格的正中间有一个障碍物。
|
||||
从左上角到右下角一共有 2 条不同的路径:
|
||||
* 3x3 网格的正中间有一个障碍物。
|
||||
* 从左上角到右下角一共有 2 条不同的路径:
|
||||
1. 向右 -> 向右 -> 向下 -> 向下
|
||||
2. 向下 -> 向下 -> 向右 -> 向右
|
||||
|
||||
|
|
@ -34,11 +34,10 @@
|
|||
|
||||

|
||||
|
||||
输入:obstacleGrid = [[0,1],[0,0]]
|
||||
输出:1
|
||||
* 输入:obstacleGrid = [[0,1],[0,0]]
|
||||
* 输出:1
|
||||
|
||||
提示:
|
||||
|
||||
* m == obstacleGrid.length
|
||||
* n == obstacleGrid[i].length
|
||||
* 1 <= m, n <= 100
|
||||
|
|
@ -153,8 +152,41 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n × m)$,n、m 分别为obstacleGrid 长度和宽度
|
||||
* 空间复杂度:$O(n × m)$
|
||||
* 时间复杂度:O(n × m),n、m 分别为obstacleGrid 长度和宽度
|
||||
* 空间复杂度:O(n × m)
|
||||
|
||||
|
||||
同样我们给出空间优化版本:
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
|
||||
if (obstacleGrid[0][0] == 1)
|
||||
return 0;
|
||||
vector<int> dp(obstacleGrid[0].size());
|
||||
for (int j = 0; j < dp.size(); ++j)
|
||||
if (obstacleGrid[0][j] == 1)
|
||||
dp[j] = 0;
|
||||
else if (j == 0)
|
||||
dp[j] = 1;
|
||||
else
|
||||
dp[j] = dp[j-1];
|
||||
|
||||
for (int i = 1; i < obstacleGrid.size(); ++i)
|
||||
for (int j = 0; j < dp.size(); ++j){
|
||||
if (obstacleGrid[i][j] == 1)
|
||||
dp[j] = 0;
|
||||
else if (j != 0)
|
||||
dp[j] = dp[j] + dp[j-1];
|
||||
}
|
||||
return dp.back();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:O(n × m),n、m 分别为obstacleGrid 长度和宽度
|
||||
* 空间复杂度:O(m)
|
||||
|
||||
|
||||
## 总结
|
||||
|
||||
|
|
@ -171,7 +203,7 @@ public:
|
|||
|
||||
## 其他语言版本
|
||||
|
||||
Java:
|
||||
### Java
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
|
|
@ -199,7 +231,7 @@ class Solution {
|
|||
```
|
||||
|
||||
|
||||
Python:
|
||||
### Python
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
|
|
@ -262,7 +294,7 @@ class Solution:
|
|||
```
|
||||
|
||||
|
||||
Go:
|
||||
### Go
|
||||
|
||||
```go
|
||||
func uniquePathsWithObstacles(obstacleGrid [][]int) int {
|
||||
|
|
@ -272,43 +304,30 @@ func uniquePathsWithObstacles(obstacleGrid [][]int) int {
|
|||
for i, _ := range dp {
|
||||
dp[i] = make([]int, n)
|
||||
}
|
||||
// 初始化
|
||||
for i:=0;i<m;i++ {
|
||||
// 如果是障碍物, 后面的就都是0, 不用循环了
|
||||
if obstacleGrid[i][0] == 1 {
|
||||
break
|
||||
}
|
||||
// 初始化, 如果是障碍物, 后面的就都是0, 不用循环了
|
||||
for i := 0; i < m && obstacleGrid[i][0] == 0; i++ {
|
||||
dp[i][0] = 1
|
||||
}
|
||||
for i:=0;i<n;i++ {
|
||||
if obstacleGrid[0][i] == 1 {
|
||||
break
|
||||
}
|
||||
for i := 0; i < n && obstacleGrid[0][i] == 0; i++ {
|
||||
dp[0][i] = 1
|
||||
}
|
||||
// dp数组推导过程
|
||||
for i := 1; i < m; i++ {
|
||||
for j := 1; j < n; j++ {
|
||||
// 如果obstacleGrid[i][j]这个点是障碍物, 那么我们的dp[i][j]保持为0
|
||||
// 如果obstacleGrid[i][j]这个点是障碍物, 那么dp[i][j]保持为0
|
||||
if obstacleGrid[i][j] != 1 {
|
||||
// 否则我们需要计算当前点可以到达的路径数
|
||||
dp[i][j] = dp[i-1][j] + dp[i][j-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
// debug遍历dp
|
||||
//for i,_ := range dp {
|
||||
// for j,_ := range dp[i] {
|
||||
// fmt.Printf("%.2v,",dp[i][j])
|
||||
// }
|
||||
// fmt.Println()
|
||||
//}
|
||||
return dp[m-1][n-1]
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
Javascript
|
||||
### Javascript
|
||||
```Javascript
|
||||
var uniquePathsWithObstacles = function(obstacleGrid) {
|
||||
const m = obstacleGrid.length
|
||||
|
|
|
|||
|
|
@ -256,16 +256,28 @@ public int climbStairs(int n) {
|
|||
### Python
|
||||
|
||||
```python
|
||||
# 空间复杂度为O(n)版本
|
||||
class Solution:
|
||||
def climbStairs(self, n: int) -> int:
|
||||
# dp[i]表示爬到第i级楼梯的种数, (1, 2) (2, 1)是两种不同的类型
|
||||
# dp[i] 为第 i 阶楼梯有多少种方法爬到楼顶
|
||||
dp=[0]*(n+1)
|
||||
dp[0]=1
|
||||
for i in range(n+1):
|
||||
for j in range(1, 3):
|
||||
if i>=j:
|
||||
dp[i] += dp[i-j]
|
||||
return dp[-1]
|
||||
dp[1]=1
|
||||
for i in range(2,n+1):
|
||||
dp[i]=dp[i-1]+dp[i-2]
|
||||
return dp[n]
|
||||
|
||||
# 空间复杂度为O(1)版本
|
||||
class Solution:
|
||||
def climbStairs(self, n: int) -> int:
|
||||
dp=[0]*(n+1)
|
||||
dp[0]=1
|
||||
dp[1]=1
|
||||
for i in range(2,n+1):
|
||||
tmp=dp[0]+dp[1]
|
||||
dp[0]=dp[1]
|
||||
dp[1]=tmp
|
||||
return dp[1]
|
||||
```
|
||||
|
||||
### Go
|
||||
|
|
|
|||
|
|
@ -143,10 +143,10 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
Python3:
|
||||
|
||||
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def climbStairs(self, n: int) -> int:
|
||||
dp = [0]*(n + 1)
|
||||
|
|
|
|||
|
|
@ -456,6 +456,27 @@ const combineHelper = (n, k, startIndex) => {
|
|||
}
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function combine(n: number, k: number): number[][] {
|
||||
let resArr: number[][] = [];
|
||||
function backTracking(n: number, k: number, startIndex: number, tempArr: number[]): void {
|
||||
if (tempArr.length === k) {
|
||||
resArr.push(tempArr.slice());
|
||||
return;
|
||||
}
|
||||
for (let i = startIndex; i <= n - k + 1 + tempArr.length; i++) {
|
||||
tempArr.push(i);
|
||||
backTracking(n, k, i + 1, tempArr);
|
||||
tempArr.pop();
|
||||
}
|
||||
}
|
||||
backTracking(n, k, 1, []);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Go
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ class Solution {
|
|||
```
|
||||
|
||||
Python:
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def combine(self, n: int, k: int) -> List[List[int]]:
|
||||
res=[] #存放符合条件结果的集合
|
||||
|
|
@ -240,7 +240,29 @@ var combine = function(n, k) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function combine(n: number, k: number): number[][] {
|
||||
let resArr: number[][] = [];
|
||||
function backTracking(n: number, k: number, startIndex: number, tempArr: number[]): void {
|
||||
if (tempArr.length === k) {
|
||||
resArr.push(tempArr.slice());
|
||||
return;
|
||||
}
|
||||
for (let i = startIndex; i <= n - k + 1 + tempArr.length; i++) {
|
||||
tempArr.push(i);
|
||||
backTracking(n, k, i + 1, tempArr);
|
||||
tempArr.pop();
|
||||
}
|
||||
}
|
||||
backTracking(n, k, 1, []);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
C:
|
||||
|
||||
```c
|
||||
int* path;
|
||||
int pathTop;
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ class Solution {
|
|||
```
|
||||
|
||||
## Python
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.path: List[int] = []
|
||||
|
|
@ -272,7 +272,28 @@ var subsets = function(nums) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function subsets(nums: number[]): number[][] {
|
||||
const resArr: number[][] = [];
|
||||
backTracking(nums, 0, []);
|
||||
return resArr;
|
||||
function backTracking(nums: number[], startIndex: number, route: number[]): void {
|
||||
resArr.push(route.slice());
|
||||
let length = nums.length;
|
||||
if (startIndex === length) return;
|
||||
for (let i = startIndex; i < length; i++) {
|
||||
route.push(nums[i]);
|
||||
backTracking(nums, i + 1, route);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
```c
|
||||
int* path;
|
||||
int pathTop;
|
||||
|
|
|
|||
|
|
@ -277,9 +277,9 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
Python3:
|
||||
|
||||
```python3
|
||||
```python
|
||||
|
||||
# 双指针;暴力解法(leetcode超时)
|
||||
class Solution:
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class Solution {
|
|||
private:
|
||||
vector<vector<int>> result;
|
||||
vector<int> path;
|
||||
void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
|
||||
void backtracking(vector<int>& nums, int startIndex) {
|
||||
result.push_back(path);
|
||||
unordered_set<int> uset;
|
||||
for (int i = startIndex; i < nums.size(); i++) {
|
||||
|
|
@ -96,7 +96,7 @@ private:
|
|||
}
|
||||
uset.insert(nums[i]);
|
||||
path.push_back(nums[i]);
|
||||
backtracking(nums, i + 1, used);
|
||||
backtracking(nums, i + 1);
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
|
|
@ -105,9 +105,8 @@ public:
|
|||
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
|
||||
result.clear();
|
||||
path.clear();
|
||||
vector<bool> used(nums.size(), false);
|
||||
sort(nums.begin(), nums.end()); // 去重需要排序
|
||||
backtracking(nums, 0, used);
|
||||
backtracking(nums, 0);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
@ -319,6 +318,28 @@ var subsetsWithDup = function(nums) {
|
|||
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function subsetsWithDup(nums: number[]): number[][] {
|
||||
nums.sort((a, b) => a - b);
|
||||
const resArr: number[][] = [];
|
||||
backTraking(nums, 0, []);
|
||||
return resArr;
|
||||
function backTraking(nums: number[], startIndex: number, route: number[]): void {
|
||||
resArr.push(route.slice());
|
||||
let length: number = nums.length;
|
||||
if (startIndex === length) return;
|
||||
for (let i = startIndex; i < length; i++) {
|
||||
if (i > startIndex && nums[i] === nums[i - 1]) continue;
|
||||
route.push(nums[i]);
|
||||
backTraking(nums, i + 1, route);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
```c
|
||||
|
|
@ -388,7 +409,7 @@ int** subsetsWithDup(int* nums, int numsSize, int* returnSize, int** returnColum
|
|||
}
|
||||
```
|
||||
|
||||
## Swift
|
||||
### Swift
|
||||
|
||||
```swift
|
||||
func subsetsWithDup(_ nums: [Int]) -> [[Int]] {
|
||||
|
|
|
|||
|
|
@ -304,6 +304,48 @@ class Solution {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//方法二:比上面的方法时间复杂度低,更好地剪枝,优化时间复杂度
|
||||
class Solution {
|
||||
List<String> result = new ArrayList<String>();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
public List<String> restoreIpAddresses(String s) {
|
||||
restoreIpAddressesHandler(s, 0, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
// number表示stringbuilder中ip段的数量
|
||||
public void restoreIpAddressesHandler(String s, int start, int number) {
|
||||
// 如果start等于s的长度并且ip段的数量是4,则加入结果集,并返回
|
||||
if (start == s.length() && number == 4) {
|
||||
result.add(stringBuilder.toString());
|
||||
return;
|
||||
}
|
||||
// 如果start等于s的长度但是ip段的数量不为4,或者ip段的数量为4但是start小于s的长度,则直接返回
|
||||
if (start == s.length() || number == 4) {
|
||||
return;
|
||||
}
|
||||
// 剪枝:ip段的长度最大是3,并且ip段处于[0,255]
|
||||
for (int i = start; i < s.length() && i - start < 3 && Integer.parseInt(s.substring(start, i + 1)) >= 0
|
||||
&& Integer.parseInt(s.substring(start, i + 1)) <= 255; i++) {
|
||||
// 如果ip段的长度大于1,并且第一位为0的话,continue
|
||||
if (i + 1 - start > 1 && s.charAt(start) - '0' == 0) {
|
||||
continue;
|
||||
}
|
||||
stringBuilder.append(s.substring(start, i + 1));
|
||||
// 当stringBuilder里的网段数量小于3时,才会加点;如果等于3,说明已经有3段了,最后一段不需要再加点
|
||||
if (number < 3) {
|
||||
stringBuilder.append(".");
|
||||
}
|
||||
number++;
|
||||
restoreIpAddressesHandler(s, i + 1, number);
|
||||
number--;
|
||||
// 删除当前stringBuilder最后一个网段,注意考虑点的数量的问题
|
||||
stringBuilder.delete(start + number, i + number + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## python
|
||||
|
|
@ -339,7 +381,7 @@ class Solution(object):
|
|||
```
|
||||
|
||||
python3:
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.result = []
|
||||
|
|
@ -413,6 +455,45 @@ var restoreIpAddresses = function(s) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function isValidIpSegment(str: string): boolean {
|
||||
let resBool: boolean = true;
|
||||
let tempVal: number = Number(str);
|
||||
if (
|
||||
str.length === 0 || isNaN(tempVal) ||
|
||||
tempVal > 255 || tempVal < 0 ||
|
||||
(str.length > 1 && str[0] === '0')
|
||||
) {
|
||||
resBool = false;
|
||||
}
|
||||
return resBool;
|
||||
}
|
||||
function restoreIpAddresses(s: string): string[] {
|
||||
const resArr: string[] = [];
|
||||
backTracking(s, 0, []);
|
||||
return resArr;
|
||||
function backTracking(s: string, startIndex: number, route: string[]): void {
|
||||
let length: number = s.length;
|
||||
if (route.length === 4 && startIndex >= length) {
|
||||
resArr.push(route.join('.'));
|
||||
return;
|
||||
}
|
||||
if (route.length === 4 || startIndex >= length) return;
|
||||
let tempStr: string = '';
|
||||
for (let i = startIndex + 1; i <= Math.min(length, startIndex + 3); i++) {
|
||||
tempStr = s.slice(startIndex, i);
|
||||
if (isValidIpSegment(tempStr)) {
|
||||
route.push(s.slice(startIndex, i));
|
||||
backTracking(s, i, route);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Go
|
||||
|
||||
回溯(对于前导 0的IP(特别注意s[startIndex]=='0'的判断,不应该写成s[startIndex]==0,因为s截取出来不是数字))
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
</a>
|
||||
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
## 96.不同的二叉搜索树
|
||||
# 96.不同的二叉搜索树
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/unique-binary-search-trees/)
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ public:
|
|||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
### Java
|
||||
```Java
|
||||
class Solution {
|
||||
public int numTrees(int n) {
|
||||
|
|
@ -184,7 +184,7 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
### Python
|
||||
```python
|
||||
class Solution:
|
||||
def numTrees(self, n: int) -> int:
|
||||
|
|
@ -196,7 +196,7 @@ class Solution:
|
|||
return dp[-1]
|
||||
```
|
||||
|
||||
Go:
|
||||
### Go
|
||||
```Go
|
||||
func numTrees(n int)int{
|
||||
dp:=make([]int,n+1)
|
||||
|
|
@ -210,7 +210,7 @@ func numTrees(n int)int{
|
|||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
### Javascript
|
||||
```Javascript
|
||||
const numTrees =(n) => {
|
||||
let dp = new Array(n+1).fill(0);
|
||||
|
|
@ -227,7 +227,34 @@ const numTrees =(n) => {
|
|||
};
|
||||
```
|
||||
|
||||
C:
|
||||
```c
|
||||
//开辟dp数组
|
||||
int *initDP(int n) {
|
||||
int *dp = (int *)malloc(sizeof(int) * (n + 1));
|
||||
int i;
|
||||
for(i = 0; i <= n; ++i)
|
||||
dp[i] = 0;
|
||||
return dp;
|
||||
}
|
||||
|
||||
int numTrees(int n){
|
||||
//开辟dp数组
|
||||
int *dp = initDP(n);
|
||||
//将dp[0]设为1
|
||||
dp[0] = 1;
|
||||
|
||||
int i, j;
|
||||
for(i = 1; i <= n; ++i) {
|
||||
for(j = 1; j <= i; ++j) {
|
||||
//递推公式:dp[i] = dp[i] + 根为j时左子树种类个数 * 根为j时右子树种类个数
|
||||
dp[i] += dp[j - 1] * dp[i - j];
|
||||
}
|
||||
}
|
||||
|
||||
return dp[n];
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -408,6 +408,27 @@ class Solution:
|
|||
return True
|
||||
|
||||
```
|
||||
```python
|
||||
# 遵循Carl的写法,只添加了节点判断的部分
|
||||
class Solution:
|
||||
def isValidBST(self, root: TreeNode) -> bool:
|
||||
# method 2
|
||||
que, pre = [], None
|
||||
while root or que:
|
||||
while root:
|
||||
que.append(root)
|
||||
root = root.left
|
||||
root = que.pop()
|
||||
# 对第一个节点只做记录,对后面的节点进行比较
|
||||
if pre is None:
|
||||
pre = root.val
|
||||
else:
|
||||
if pre >= root.val: return False
|
||||
pre = root.val
|
||||
root = root.right
|
||||
return True
|
||||
```
|
||||
|
||||
## Go
|
||||
|
||||
```Go
|
||||
|
|
@ -526,6 +547,48 @@ var isValidBST = function (root) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
> 辅助数组解决:
|
||||
|
||||
```typescript
|
||||
function isValidBST(root: TreeNode | null): boolean {
|
||||
const traversalArr: number[] = [];
|
||||
function inorderTraverse(root: TreeNode | null): void {
|
||||
if (root === null) return;
|
||||
inorderTraverse(root.left);
|
||||
traversalArr.push(root.val);
|
||||
inorderTraverse(root.right);
|
||||
}
|
||||
inorderTraverse(root);
|
||||
for (let i = 0, length = traversalArr.length; i < length - 1; i++) {
|
||||
if (traversalArr[i] >= traversalArr[i + 1]) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
> 递归中解决:
|
||||
|
||||
```typescript
|
||||
function isValidBST(root: TreeNode | null): boolean {
|
||||
let maxVal = -Infinity;
|
||||
function inorderTraverse(root: TreeNode | null): boolean {
|
||||
if (root === null) return true;
|
||||
let leftValid: boolean = inorderTraverse(root.left);
|
||||
if (!leftValid) return false;
|
||||
if (maxVal < root.val) {
|
||||
maxVal = root.val
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
let rightValid: boolean = inorderTraverse(root.right);
|
||||
return leftValid && rightValid;
|
||||
}
|
||||
return inorderTraverse(root);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -574,7 +574,156 @@ var isSymmetric = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript:
|
||||
|
||||
> 递归法
|
||||
|
||||
```typescript
|
||||
function isSymmetric(root: TreeNode | null): boolean {
|
||||
function recur(node1: TreeNode | null, node2: TreeNode | null): boolean {
|
||||
if (node1 === null && node2 === null) return true;
|
||||
if (node1 === null || node2 === null) return false;
|
||||
if (node1.val !== node2.val) return false
|
||||
let isSym1: boolean = recur(node1.left, node2.right);
|
||||
let isSym2: boolean = recur(node1.right, node2.left);
|
||||
return isSym1 && isSym2
|
||||
}
|
||||
if (root === null) return true;
|
||||
return recur(root.left, root.right);
|
||||
};
|
||||
```
|
||||
|
||||
> 迭代法
|
||||
|
||||
```typescript
|
||||
// 迭代法(队列)
|
||||
function isSymmetric(root: TreeNode | null): boolean {
|
||||
let helperQueue: (TreeNode | null)[] = [];
|
||||
let tempNode1: TreeNode | null,
|
||||
tempNode2: TreeNode | null;
|
||||
if (root !== null) {
|
||||
helperQueue.push(root.left);
|
||||
helperQueue.push(root.right);
|
||||
}
|
||||
while (helperQueue.length > 0) {
|
||||
tempNode1 = helperQueue.shift()!;
|
||||
tempNode2 = helperQueue.shift()!;
|
||||
if (tempNode1 === null && tempNode2 === null) continue;
|
||||
if (tempNode1 === null || tempNode2 === null) return false;
|
||||
if (tempNode1.val !== tempNode2.val) return false;
|
||||
helperQueue.push(tempNode1.left);
|
||||
helperQueue.push(tempNode2.right);
|
||||
helperQueue.push(tempNode1.right);
|
||||
helperQueue.push(tempNode2.left);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 迭代法(栈)
|
||||
function isSymmetric(root: TreeNode | null): boolean {
|
||||
let helperStack: (TreeNode | null)[] = [];
|
||||
let tempNode1: TreeNode | null,
|
||||
tempNode2: TreeNode | null;
|
||||
if (root !== null) {
|
||||
helperStack.push(root.left);
|
||||
helperStack.push(root.right);
|
||||
}
|
||||
while (helperStack.length > 0) {
|
||||
tempNode1 = helperStack.pop()!;
|
||||
tempNode2 = helperStack.pop()!;
|
||||
if (tempNode1 === null && tempNode2 === null) continue;
|
||||
if (tempNode1 === null || tempNode2 === null) return false;
|
||||
if (tempNode1.val !== tempNode2.val) return false;
|
||||
helperStack.push(tempNode1.left);
|
||||
helperStack.push(tempNode2.right);
|
||||
helperStack.push(tempNode1.right);
|
||||
helperStack.push(tempNode2.left);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
## Swift:
|
||||
|
||||
> 递归
|
||||
```swift
|
||||
func isSymmetric(_ root: TreeNode?) -> Bool {
|
||||
return _isSymmetric(root?.left, right: root?.right)
|
||||
}
|
||||
func _isSymmetric(_ left: TreeNode?, right: TreeNode?) -> Bool {
|
||||
// 首先排除空节点情况
|
||||
if left == nil && right == nil {
|
||||
return true
|
||||
} else if left == nil && right != nil {
|
||||
return false
|
||||
} else if left != nil && right == nil {
|
||||
return false
|
||||
} else if left!.val != right!.val {
|
||||
// 进而排除数值不相等的情况
|
||||
return false
|
||||
}
|
||||
|
||||
// left 和 right 都不为空, 且数值也相等就递归
|
||||
let inSide = _isSymmetric(left!.right, right: right!.left)
|
||||
let outSide = _isSymmetric(left!.left, right: right!.right)
|
||||
return inSide && outSide
|
||||
}
|
||||
```
|
||||
|
||||
> 迭代 - 使用队列
|
||||
```swift
|
||||
func isSymmetric2(_ root: TreeNode?) -> Bool {
|
||||
guard let root = root else {
|
||||
return true
|
||||
}
|
||||
var queue = [TreeNode?]()
|
||||
queue.append(root.left)
|
||||
queue.append(root.right)
|
||||
while !queue.isEmpty {
|
||||
let left = queue.removeFirst()
|
||||
let right = queue.removeFirst()
|
||||
if left == nil && right == nil {
|
||||
continue
|
||||
}
|
||||
if left == nil || right == nil || left?.val != right?.val {
|
||||
return false
|
||||
}
|
||||
queue.append(left!.left)
|
||||
queue.append(right!.right)
|
||||
queue.append(left!.right)
|
||||
queue.append(right!.left)
|
||||
}
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
> 迭代 - 使用栈
|
||||
```swift
|
||||
func isSymmetric3(_ root: TreeNode?) -> Bool {
|
||||
guard let root = root else {
|
||||
return true
|
||||
}
|
||||
var stack = [TreeNode?]()
|
||||
stack.append(root.left)
|
||||
stack.append(root.right)
|
||||
while !stack.isEmpty {
|
||||
let left = stack.removeLast()
|
||||
let right = stack.removeLast()
|
||||
|
||||
if left == nil && right == nil {
|
||||
continue
|
||||
}
|
||||
if left == nil || right == nil || left?.val != right?.val {
|
||||
return false
|
||||
}
|
||||
stack.append(left!.left)
|
||||
stack.append(right!.right)
|
||||
stack.append(left!.right)
|
||||
stack.append(right!.left)
|
||||
}
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -83,10 +83,10 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
python代码:
|
||||
python3代码:
|
||||
|
||||
|
||||
```python3
|
||||
```python
|
||||
|
||||
class Solution:
|
||||
"""二叉树层序遍历迭代解法"""
|
||||
|
|
@ -246,31 +246,56 @@ var levelOrder = function(root) {
|
|||
|
||||
```
|
||||
|
||||
Swift:
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function levelOrder(root: TreeNode | null): number[][] {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let res: number[][] = [];
|
||||
let tempArr: number[] = [];
|
||||
if (root !== null) helperQueue.push(root);
|
||||
let curNode: TreeNode;
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
curNode = helperQueue.shift()!;
|
||||
tempArr.push(curNode.val);
|
||||
if (curNode.left !== null) {
|
||||
helperQueue.push(curNode.left);
|
||||
}
|
||||
if (curNode.right !== null) {
|
||||
helperQueue.push(curNode.right);
|
||||
}
|
||||
}
|
||||
res.push(tempArr);
|
||||
tempArr = [];
|
||||
}
|
||||
return res;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func levelOrder(_ root: TreeNode?) -> [[Int]] {
|
||||
var res = [[Int]]()
|
||||
guard let root = root else {
|
||||
return res
|
||||
}
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
var result = [[Int]]()
|
||||
guard let root = root else { return result }
|
||||
// 表示一层
|
||||
var queue = [root]
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
var sub = [Int]()
|
||||
for _ in 0 ..< size {
|
||||
let count = queue.count
|
||||
var subarray = [Int]()
|
||||
for _ in 0 ..< count {
|
||||
// 当前层
|
||||
let node = queue.removeFirst()
|
||||
sub.append(node.val)
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
subarray.append(node.val)
|
||||
// 下一层
|
||||
if let node = node.left { queue.append(node) }
|
||||
if let node = node.right { queue.append(node) }
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
result.append(subarray)
|
||||
}
|
||||
}
|
||||
res.append(sub)
|
||||
}
|
||||
return res
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -454,29 +479,52 @@ var levelOrderBottom = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function levelOrderBottom(root: TreeNode | null): number[][] {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resArr: number[][] = [];
|
||||
let tempArr: number[] = [];
|
||||
let tempNode: TreeNode;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
tempArr.push(tempNode.val);
|
||||
if (tempNode.left !== null) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right !== null) helperQueue.push(tempNode.right);
|
||||
}
|
||||
resArr.push(tempArr);
|
||||
tempArr = [];
|
||||
}
|
||||
return resArr.reverse();
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func levelOrderBottom(_ root: TreeNode?) -> [[Int]] {
|
||||
var res = [[Int]]()
|
||||
guard let root = root else {
|
||||
return res
|
||||
}
|
||||
var queue: [TreeNode] = [root]
|
||||
// 表示一层
|
||||
var queue = [TreeNode]()
|
||||
if let node = root { queue.append(node) }
|
||||
var result = [[Int]]()
|
||||
while !queue.isEmpty {
|
||||
var sub = [Int]()
|
||||
for _ in 0 ..< queue.count {
|
||||
let count = queue.count
|
||||
var subarray = [Int]()
|
||||
for _ in 0 ..< count {
|
||||
// 当前层
|
||||
let node = queue.removeFirst()
|
||||
sub.append(node.val)
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
subarray.append(node.val)
|
||||
// 下一层
|
||||
if let node = node.left { queue.append(node) }
|
||||
if let node = node.right { queue.append(node)}
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
result.append(subarray)
|
||||
}
|
||||
}
|
||||
res.insert(sub, at: 0)
|
||||
}
|
||||
return res
|
||||
|
||||
return result.reversed()
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -657,35 +705,50 @@ var rightSideView = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
```swift
|
||||
func rightSideView(_ root: TreeNode?) -> [Int] {
|
||||
var res = [Int]()
|
||||
guard let root = root else {
|
||||
return res
|
||||
}
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
for i in 0 ..< size {
|
||||
let node = queue.removeFirst()
|
||||
if i == size - 1 {
|
||||
// 保存 每层最后一个元素
|
||||
res.append(node.val)
|
||||
}
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function rightSideView(root: TreeNode | null): number[] {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resArr: number[] = [];
|
||||
let tempNode: TreeNode;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
if (i === length - 1) resArr.push(tempNode.val);
|
||||
if (tempNode.left !== null) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right !== null) helperQueue.push(tempNode.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func rightSideView(_ root: TreeNode?) -> [Int] {
|
||||
// 表示一层
|
||||
var queue = [TreeNode]()
|
||||
if let node = root { queue.append(node) }
|
||||
var result = [Int]()
|
||||
while !queue.isEmpty {
|
||||
let count = queue.count
|
||||
for i in 0 ..< count {
|
||||
// 当前层
|
||||
let node = queue.removeFirst()
|
||||
if i == count - 1 { result.append(node.val) }
|
||||
|
||||
// 下一层
|
||||
if let node = node.left { queue.append(node) }
|
||||
if let node = node.right { queue.append(node) }
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
# 637.二叉树的层平均值
|
||||
|
||||
|
|
@ -868,31 +931,54 @@ var averageOfLevels = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function averageOfLevels(root: TreeNode | null): number[] {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resArr: number[] = [];
|
||||
let total: number = 0;
|
||||
let tempNode: TreeNode;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
let length = helperQueue.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
total += tempNode.val;
|
||||
if (tempNode.left) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right) helperQueue.push(tempNode.right);
|
||||
}
|
||||
resArr.push(total / length);
|
||||
total = 0;
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func averageOfLevels(_ root: TreeNode?) -> [Double] {
|
||||
var res = [Double]()
|
||||
guard let root = root else {
|
||||
return res
|
||||
}
|
||||
// 表示一层
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
if let node = root { queue.append(node) }
|
||||
var result = [Double]()
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
let count = queue.count
|
||||
var sum = 0
|
||||
for _ in 0 ..< size {
|
||||
for _ in 0 ..< count {
|
||||
// 当前层
|
||||
let node = queue.removeFirst()
|
||||
sum += node.val
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
|
||||
// 下一层
|
||||
if let node = node.left { queue.append(node) }
|
||||
if let node = node.right { queue.append(node) }
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
result.append(Double(sum) / Double(count))
|
||||
}
|
||||
}
|
||||
res.append(Double(sum) / Double(size))
|
||||
}
|
||||
return res
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1092,28 +1178,50 @@ var levelOrder = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function levelOrder(root: Node | null): number[][] {
|
||||
let helperQueue: Node[] = [];
|
||||
let resArr: number[][] = [];
|
||||
let tempArr: number[] = [];
|
||||
if (root !== null) helperQueue.push(root);
|
||||
let curNode: Node;
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
curNode = helperQueue.shift()!;
|
||||
tempArr.push(curNode.val);
|
||||
helperQueue.push(...curNode.children);
|
||||
}
|
||||
resArr.push(tempArr);
|
||||
tempArr = [];
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func levelOrder(_ root: Node?) -> [[Int]] {
|
||||
var res = [[Int]]()
|
||||
guard let root = root else {
|
||||
return res
|
||||
}
|
||||
// 表示一层
|
||||
var queue = [Node]()
|
||||
queue.append(root)
|
||||
if let node = root { queue.append(node) }
|
||||
var result = [[Int]]()
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
var sub = [Int]()
|
||||
for _ in 0 ..< size {
|
||||
let count = queue.count
|
||||
var subarray = [Int]()
|
||||
for _ in 0 ..< count {
|
||||
// 当前层
|
||||
let node = queue.removeFirst()
|
||||
sub.append(node.val)
|
||||
for childNode in node.children {
|
||||
queue.append(childNode)
|
||||
subarray.append(node.val)
|
||||
// 下一层
|
||||
for node in node.children { queue.append(node) }
|
||||
}
|
||||
result.append(subarray)
|
||||
}
|
||||
res.append(sub)
|
||||
}
|
||||
return res
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1179,23 +1287,23 @@ java代码:
|
|||
```java
|
||||
class Solution {
|
||||
public List<Integer> largestValues(TreeNode root) {
|
||||
List<Integer> retVal = new ArrayList<Integer>();
|
||||
Queue<TreeNode> tmpQueue = new LinkedList<TreeNode>();
|
||||
if (root != null) tmpQueue.add(root);
|
||||
|
||||
while (tmpQueue.size() != 0){
|
||||
int size = tmpQueue.size();
|
||||
List<Integer> lvlVals = new ArrayList<Integer>();
|
||||
for (int index = 0; index < size; index++){
|
||||
TreeNode node = tmpQueue.poll();
|
||||
lvlVals.add(node.val);
|
||||
if (node.left != null) tmpQueue.add(node.left);
|
||||
if (node.right != null) tmpQueue.add(node.right);
|
||||
if(root == null){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
retVal.add(Collections.max(lvlVals));
|
||||
List<Integer> result = new ArrayList();
|
||||
Queue<TreeNode> queue = new LinkedList();
|
||||
queue.offer(root);
|
||||
while(!queue.isEmpty()){
|
||||
int max = Integer.MIN_VALUE;
|
||||
for(int i = queue.size(); i > 0; i--){
|
||||
TreeNode node = queue.poll();
|
||||
max = Math.max(max, node.val);
|
||||
if(node.left != null) queue.offer(node.left);
|
||||
if(node.right != null) queue.offer(node.right);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
result.add(max);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1272,33 +1380,56 @@ var largestValues = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function largestValues(root: TreeNode | null): number[] {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resArr: number[] = [];
|
||||
let tempNode: TreeNode;
|
||||
let max: number = 0;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
if (i === 0) {
|
||||
max = tempNode.val;
|
||||
} else {
|
||||
max = max > tempNode.val ? max : tempNode.val;
|
||||
}
|
||||
if (tempNode.left) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right) helperQueue.push(tempNode.right);
|
||||
}
|
||||
resArr.push(max);
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func largestValues(_ root: TreeNode?) -> [Int] {
|
||||
var res = [Int]()
|
||||
guard let root = root else {
|
||||
return res
|
||||
}
|
||||
// 表示一层
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
if let node = root { queue.append(node) }
|
||||
var result = [Int]()
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
var max: Int = Int.min
|
||||
for _ in 0 ..< size {
|
||||
let count = queue.count
|
||||
var max = queue[0].val
|
||||
for _ in 0 ..< count {
|
||||
// 当前层
|
||||
let node = queue.removeFirst()
|
||||
if node.val > max {
|
||||
max = node.val
|
||||
if node.val > max { max = node.val }
|
||||
|
||||
// 下一层
|
||||
if let node = node.left { queue.append(node) }
|
||||
if let node = node.right { queue.append(node) }
|
||||
}
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
result.append(max)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
}
|
||||
}
|
||||
res.append(max)
|
||||
}
|
||||
return res
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1308,7 +1439,7 @@ func largestValues(_ root: TreeNode?) -> [Int] {
|
|||
|
||||
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
struct Node {
|
||||
int val;
|
||||
Node *left;
|
||||
|
|
@ -1463,6 +1594,31 @@ var connect = function(root) {
|
|||
};
|
||||
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function connect(root: Node | null): Node | null {
|
||||
let helperQueue: Node[] = [];
|
||||
let preNode: Node, curNode: Node;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
if (i === 0) {
|
||||
preNode = helperQueue.shift()!;
|
||||
} else {
|
||||
curNode = helperQueue.shift()!;
|
||||
preNode.next = curNode;
|
||||
preNode = curNode;
|
||||
}
|
||||
if (preNode.left) helperQueue.push(preNode.left);
|
||||
if (preNode.right) helperQueue.push(preNode.right);
|
||||
}
|
||||
preNode.next = null;
|
||||
}
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
go:
|
||||
|
||||
```GO
|
||||
|
|
@ -1504,33 +1660,34 @@ func connect(root *Node) *Node {
|
|||
}
|
||||
```
|
||||
|
||||
Swift:
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func connect(_ root: Node?) -> Node? {
|
||||
guard let root = root else {
|
||||
return nil
|
||||
}
|
||||
// 表示一层
|
||||
var queue = [Node]()
|
||||
queue.append(root)
|
||||
if let node = root { queue.append(node) }
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
var preNode: Node?
|
||||
for i in 0 ..< size {
|
||||
let node = queue.removeFirst()
|
||||
let count = queue.count
|
||||
var current, previous: Node!
|
||||
for i in 0 ..< count {
|
||||
// 当前层
|
||||
if i == 0 {
|
||||
preNode = node
|
||||
previous = queue.removeFirst()
|
||||
current = previous
|
||||
} else {
|
||||
preNode?.next = node
|
||||
preNode = node
|
||||
}
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
current = queue.removeFirst()
|
||||
previous.next = current
|
||||
previous = current
|
||||
}
|
||||
|
||||
// 下一层
|
||||
if let node = current.left { queue.append(node) }
|
||||
if let node = current.right { queue.append(node) }
|
||||
}
|
||||
previous.next = nil
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
|
@ -1689,6 +1846,31 @@ var connect = function(root) {
|
|||
return root;
|
||||
};
|
||||
```
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function connect(root: Node | null): Node | null {
|
||||
let helperQueue: Node[] = [];
|
||||
let preNode: Node, curNode: Node;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
if (i === 0) {
|
||||
preNode = helperQueue.shift()!;
|
||||
} else {
|
||||
curNode = helperQueue.shift()!;
|
||||
preNode.next = curNode;
|
||||
preNode = curNode;
|
||||
}
|
||||
if (preNode.left) helperQueue.push(preNode.left);
|
||||
if (preNode.right) helperQueue.push(preNode.right);
|
||||
}
|
||||
preNode.next = null;
|
||||
}
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
go:
|
||||
|
||||
```GO
|
||||
|
|
@ -1729,34 +1911,34 @@ func connect(root *Node) *Node {
|
|||
return root
|
||||
}
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func connect(_ root: Node?) -> Node? {
|
||||
guard let root = root else {
|
||||
return nil
|
||||
}
|
||||
// 表示一层
|
||||
var queue = [Node]()
|
||||
queue.append(root)
|
||||
if let node = root { queue.append(node) }
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
var preNode: Node?
|
||||
for i in 0 ..< size {
|
||||
let node = queue.removeFirst()
|
||||
let count = queue.count
|
||||
var current, previous: Node!
|
||||
for i in 0 ..< count {
|
||||
// 当前层
|
||||
if i == 0 {
|
||||
preNode = node
|
||||
previous = queue.removeFirst()
|
||||
current = previous
|
||||
} else {
|
||||
preNode?.next = node
|
||||
preNode = node
|
||||
}
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
current = queue.removeFirst()
|
||||
previous.next = current
|
||||
previous = current
|
||||
}
|
||||
|
||||
// 下一层
|
||||
if let node = current.left { queue.append(node) }
|
||||
if let node = current.right { queue.append(node) }
|
||||
}
|
||||
previous.next = nil
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
|
@ -1933,28 +2115,48 @@ var maxDepth = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function maxDepth(root: TreeNode | null): number {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resDepth: number = 0;
|
||||
let tempNode: TreeNode;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
resDepth++;
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
if (tempNode.left) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right) helperQueue.push(tempNode.right);
|
||||
}
|
||||
}
|
||||
return resDepth;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func maxDepth(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
guard root != nil else { return 0 }
|
||||
var depth = 0
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
var res: Int = 0
|
||||
queue.append(root!)
|
||||
while !queue.isEmpty {
|
||||
for _ in 0 ..< queue.count {
|
||||
let count = queue.count
|
||||
depth += 1
|
||||
for _ in 0 ..< count {
|
||||
// 当前层
|
||||
let node = queue.removeFirst()
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
|
||||
// 下一层
|
||||
if let node = node.left { queue.append(node) }
|
||||
if let node = node.right { queue.append(node) }
|
||||
}
|
||||
}
|
||||
res += 1
|
||||
}
|
||||
return res
|
||||
|
||||
return depth
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -2130,31 +2332,50 @@ var minDepth = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function minDepth(root: TreeNode | null): number {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resMin: number = 0;
|
||||
let tempNode: TreeNode;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
resMin++;
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
if (tempNode.left === null && tempNode.right === null) return resMin;
|
||||
if (tempNode.left !== null) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right !== null) helperQueue.push(tempNode.right);
|
||||
}
|
||||
}
|
||||
return resMin;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func minDepth(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
var res = 0
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
guard root != nil else { return 0 }
|
||||
var depth = 0
|
||||
var queue = [root!]
|
||||
while !queue.isEmpty {
|
||||
res += 1
|
||||
for _ in 0 ..< queue.count {
|
||||
let count = queue.count
|
||||
depth += 1
|
||||
for _ in 0 ..< count {
|
||||
// 当前层
|
||||
let node = queue.removeFirst()
|
||||
if node.left == nil && node.right == nil {
|
||||
return res
|
||||
if node.left == nil, node.right == nil { // 遇到叶子结点则返回
|
||||
return depth
|
||||
}
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
|
||||
// 下一层
|
||||
if let node = node.left { queue.append(node) }
|
||||
if let node = node.right { queue.append(node) }
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
return depth
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -523,8 +523,8 @@ func maxdepth(root *treenode) int {
|
|||
|
||||
```javascript
|
||||
var maxdepth = function(root) {
|
||||
if (!root) return root
|
||||
return 1 + math.max(maxdepth(root.left), maxdepth(root.right))
|
||||
if (root === null) return 0;
|
||||
return 1 + Math.max(maxdepth(root.left), maxdepth(root.right))
|
||||
};
|
||||
```
|
||||
|
||||
|
|
@ -541,7 +541,7 @@ var maxdepth = function(root) {
|
|||
//3. 确定单层逻辑
|
||||
let leftdepth=getdepth(node.left);
|
||||
let rightdepth=getdepth(node.right);
|
||||
let depth=1+math.max(leftdepth,rightdepth);
|
||||
let depth=1+Math.max(leftdepth,rightdepth);
|
||||
return depth;
|
||||
}
|
||||
return getdepth(root);
|
||||
|
|
@ -591,14 +591,90 @@ var maxDepth = function(root) {
|
|||
count++
|
||||
while(size--) {
|
||||
let node = queue.shift()
|
||||
node && (queue = [...queue, ...node.children])
|
||||
for (let item of node.children) {
|
||||
item && queue.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
};
|
||||
```
|
||||
|
||||
## TypeScript:
|
||||
|
||||
> 二叉树的最大深度:
|
||||
|
||||
```typescript
|
||||
// 后续遍历(自下而上)
|
||||
function maxDepth(root: TreeNode | null): number {
|
||||
if (root === null) return 0;
|
||||
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
|
||||
};
|
||||
|
||||
// 前序遍历(自上而下)
|
||||
function maxDepth(root: TreeNode | null): number {
|
||||
function recur(node: TreeNode | null, count: number) {
|
||||
if (node === null) {
|
||||
resMax = resMax > count ? resMax : count;
|
||||
return;
|
||||
}
|
||||
recur(node.left, count + 1);
|
||||
recur(node.right, count + 1);
|
||||
}
|
||||
let resMax: number = 0;
|
||||
let count: number = 0;
|
||||
recur(root, count);
|
||||
return resMax;
|
||||
};
|
||||
|
||||
// 层序遍历(迭代法)
|
||||
function maxDepth(root: TreeNode | null): number {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resDepth: number = 0;
|
||||
let tempNode: TreeNode;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
resDepth++;
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
if (tempNode.left) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right) helperQueue.push(tempNode.right);
|
||||
}
|
||||
}
|
||||
return resDepth;
|
||||
};
|
||||
```
|
||||
|
||||
> N叉树的最大深度
|
||||
|
||||
```typescript
|
||||
// 后续遍历(自下而上)
|
||||
function maxDepth(root: TreeNode | null): number {
|
||||
if (root === null) return 0;
|
||||
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
|
||||
};
|
||||
|
||||
// 前序遍历(自上而下)
|
||||
function maxDepth(root: TreeNode | null): number {
|
||||
function recur(node: TreeNode | null, count: number) {
|
||||
if (node === null) {
|
||||
resMax = resMax > count ? resMax : count;
|
||||
return;
|
||||
}
|
||||
recur(node.left, count + 1);
|
||||
recur(node.right, count + 1);
|
||||
}
|
||||
let resMax: number = 0;
|
||||
let count: number = 0;
|
||||
recur(root, count);
|
||||
return resMax;
|
||||
};
|
||||
|
||||
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
二叉树最大深度递归
|
||||
```c
|
||||
int maxDepth(struct TreeNode* root){
|
||||
|
|
@ -653,5 +729,82 @@ int maxDepth(struct TreeNode* root){
|
|||
}
|
||||
```
|
||||
|
||||
## Swift
|
||||
|
||||
>二叉树最大深度
|
||||
```swift
|
||||
// 递归 - 后序
|
||||
func maxDepth1(_ root: TreeNode?) -> Int {
|
||||
return _maxDepth1(root)
|
||||
}
|
||||
func _maxDepth1(_ root: TreeNode?) -> Int {
|
||||
if root == nil {
|
||||
return 0
|
||||
}
|
||||
let leftDepth = _maxDepth1(root!.left)
|
||||
let rightDepth = _maxDepth1(root!.right)
|
||||
return 1 + max(leftDepth, rightDepth)
|
||||
}
|
||||
|
||||
// 层序
|
||||
func maxDepth(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
var res: Int = 0
|
||||
while !queue.isEmpty {
|
||||
res += 1
|
||||
for _ in 0 ..< queue.count {
|
||||
let node = queue.removeFirst()
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
>N叉树最大深度
|
||||
```swift
|
||||
// 递归
|
||||
func maxDepth(_ root: Node?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
var depth = 0
|
||||
for node in root.children {
|
||||
depth = max(depth, maxDepth(node))
|
||||
}
|
||||
return depth + 1
|
||||
}
|
||||
|
||||
// 迭代-层序遍历
|
||||
func maxDepth1(_ root: Node?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
var depth = 0
|
||||
var queue = [Node]()
|
||||
queue.append(root)
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
depth += 1
|
||||
for _ in 0 ..< size {
|
||||
let node = queue.removeFirst()
|
||||
for child in node.children {
|
||||
queue.append(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
return depth
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
|
|||
|
||||
**难点大家应该发现了,就是如何切割,以及边界值找不好很容易乱套。**
|
||||
|
||||
此时应该注意确定切割的标准,是左闭右开,还有左开又闭,还是左闭又闭,这个就是不变量,要在递归中保持这个不变量。
|
||||
此时应该注意确定切割的标准,是左闭右开,还有左开右闭,还是左闭右闭,这个就是不变量,要在递归中保持这个不变量。
|
||||
|
||||
**在切割的过程中会产生四个区间,把握不好不变量的话,一会左闭右开,一会左闭又闭,必然乱套!**
|
||||
**在切割的过程中会产生四个区间,把握不好不变量的话,一会左闭右开,一会左闭右闭,必然乱套!**
|
||||
|
||||
我在[数组:每次遇到二分法,都是一看就会,一写就废](https://programmercarl.com/0035.搜索插入位置.html)和[数组:这个循环可以转懵很多人!](https://programmercarl.com/0059.螺旋矩阵II.html)中都强调过循环不变量的重要性,在二分查找以及螺旋矩阵的求解中,坚持循环不变量非常重要,本题也是。
|
||||
|
||||
|
|
@ -790,7 +790,7 @@ func findRootIndex(target int,inorder []int) int{
|
|||
|
||||
```javascript
|
||||
var buildTree = function(inorder, postorder) {
|
||||
if (!preorder.length) return null;
|
||||
if (!inorder.length) return null;
|
||||
const rootVal = postorder.pop(); // 从后序遍历的数组中获取中间节点的值, 即数组最后一个值
|
||||
let rootIndex = inorder.indexOf(rootVal); // 获取中间节点在中序遍历中的下标
|
||||
const root = new TreeNode(rootVal); // 创建中间节点
|
||||
|
|
@ -814,7 +814,114 @@ var buildTree = function(preorder, inorder) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
> 106.从中序与后序遍历序列构造二叉树
|
||||
|
||||
**创建新数组:**
|
||||
|
||||
```typescript
|
||||
function buildTree(inorder: number[], postorder: number[]): TreeNode | null {
|
||||
if (postorder.length === 0) return null;
|
||||
const rootVal: number = postorder.pop()!;
|
||||
const rootValIndex: number = inorder.indexOf(rootVal);
|
||||
const rootNode: TreeNode = new TreeNode(rootVal);
|
||||
rootNode.left = buildTree(inorder.slice(0, rootValIndex), postorder.slice(0, rootValIndex));
|
||||
rootNode.right = buildTree(inorder.slice(rootValIndex + 1), postorder.slice(rootValIndex));
|
||||
return rootNode;
|
||||
};
|
||||
```
|
||||
|
||||
**使用数组索引:**
|
||||
|
||||
```typescript
|
||||
function buildTree(inorder: number[], postorder: number[]): TreeNode | null {
|
||||
function recur(
|
||||
inorder: number[], postorder: number[],
|
||||
inBegin: number, inEnd: number,
|
||||
postBegin: number, postEnd: number
|
||||
): TreeNode | null {
|
||||
if (postBegin === postEnd) return null;
|
||||
const rootVal: number = postorder[postEnd - 1]!;
|
||||
const rootValIndex: number = inorder.indexOf(rootVal, inBegin);
|
||||
const rootNode: TreeNode = new TreeNode(rootVal);
|
||||
|
||||
const leftInorderBegin: number = inBegin;
|
||||
const leftInorderEnd: number = rootValIndex;
|
||||
const rightInorderBegin: number = rootValIndex + 1;
|
||||
const rightInorderEnd: number = inEnd;
|
||||
|
||||
const leftPostorderBegin: number = postBegin;
|
||||
const leftPostorderEnd: number = postBegin + rootValIndex - inBegin;
|
||||
const rightPostorderBegin: number = leftPostorderEnd;
|
||||
const rightPostorderEnd: number = postEnd - 1;
|
||||
|
||||
rootNode.left = recur(
|
||||
inorder, postorder,
|
||||
leftInorderBegin, leftInorderEnd,
|
||||
leftPostorderBegin, leftPostorderEnd
|
||||
);
|
||||
rootNode.right = recur(
|
||||
inorder, postorder,
|
||||
rightInorderBegin, rightInorderEnd,
|
||||
rightPostorderBegin, rightPostorderEnd
|
||||
);
|
||||
return rootNode;
|
||||
}
|
||||
return recur(inorder, postorder, 0, inorder.length, 0, inorder.length);
|
||||
};
|
||||
```
|
||||
|
||||
> 105.从前序与中序遍历序列构造二叉树
|
||||
|
||||
**新建数组:**
|
||||
|
||||
```typescript
|
||||
function buildTree(preorder: number[], inorder: number[]): TreeNode | null {
|
||||
if (preorder.length === 0) return null;
|
||||
const rootVal: number = preorder[0];
|
||||
const rootNode: TreeNode = new TreeNode(rootVal);
|
||||
const rootValIndex: number = inorder.indexOf(rootVal);
|
||||
rootNode.left = buildTree(preorder.slice(1, rootValIndex + 1), inorder.slice(0, rootValIndex));
|
||||
rootNode.right = buildTree(preorder.slice(rootValIndex + 1), inorder.slice(rootValIndex + 1));
|
||||
return rootNode;
|
||||
};
|
||||
```
|
||||
|
||||
**使用数组索引:**
|
||||
|
||||
```typescript
|
||||
function buildTree(preorder: number[], inorder: number[]): TreeNode | null {
|
||||
function recur(
|
||||
preorder: number[], inorder: number[],
|
||||
preBegin: number, preEnd: number,
|
||||
inBegin: number, inEnd: number
|
||||
): TreeNode | null {
|
||||
if (preBegin === preEnd) return null;
|
||||
const rootVal: number = preorder[preBegin];
|
||||
const rootNode: TreeNode = new TreeNode(rootVal);
|
||||
const rootValIndex: number = inorder.indexOf(rootVal, inBegin);
|
||||
|
||||
const leftPreBegin: number = preBegin + 1;
|
||||
const leftPreEnd: number = preBegin + rootValIndex - inBegin + 1;
|
||||
const leftInBegin: number = inBegin;
|
||||
const leftInEnd: number = rootValIndex;
|
||||
|
||||
const rightPreBegin: number = leftPreEnd;
|
||||
const rightPreEnd: number = preEnd;
|
||||
const rightInBegin: number = rootValIndex + 1;
|
||||
const rightInEnd: number = inEnd;
|
||||
|
||||
rootNode.left = recur(preorder, inorder, leftPreBegin, leftPreEnd, leftInBegin, leftInEnd);
|
||||
rootNode.right = recur(preorder, inorder, rightPreBegin, rightPreEnd, rightInBegin, rightInEnd);
|
||||
return rootNode;
|
||||
};
|
||||
return recur(preorder, inorder, 0, preorder.length, 0, inorder.length);
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
106 从中序与后序遍历序列构造二叉树
|
||||
|
||||
```c
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ class Solution {
|
|||
## Python
|
||||
**递归**
|
||||
|
||||
```python3
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
|
|
@ -355,6 +355,7 @@ func sortedArrayToBST(nums []int) *TreeNode {
|
|||
```
|
||||
|
||||
## JavaScript
|
||||
递归
|
||||
|
||||
```javascript
|
||||
var sortedArrayToBST = function (nums) {
|
||||
|
|
@ -372,8 +373,62 @@ var sortedArrayToBST = function (nums) {
|
|||
return buildTree(nums, 0, nums.length - 1);
|
||||
};
|
||||
```
|
||||
迭代
|
||||
```JavaScript
|
||||
var sortedArrayToBST = function(nums) {
|
||||
if(nums.length===0){
|
||||
return null;
|
||||
}
|
||||
let root=new TreeNode(0); //初始根节点
|
||||
let nodeQue=[root]; //放遍历的节点,并初始化
|
||||
let leftQue=[0]; //放左区间的下标,初始化
|
||||
let rightQue=[nums.length-1]; // 放右区间的下标
|
||||
|
||||
while(nodeQue.length){
|
||||
let curNode=nodeQue.pop();
|
||||
let left=leftQue.pop();
|
||||
let right=rightQue.pop();
|
||||
let mid=left+Math.floor((right-left)/2);
|
||||
|
||||
curNode.val=nums[mid]; //将下标为mid的元素给中间节点
|
||||
|
||||
// 处理左区间
|
||||
if(left<=mid-1){
|
||||
curNode.left=new TreeNode(0);
|
||||
nodeQue.push(curNode.left);
|
||||
leftQue.push(left);
|
||||
rightQue.push(mid-1);
|
||||
}
|
||||
|
||||
// 处理右区间
|
||||
if(right>=mid+1){
|
||||
curNode.right=new TreeNode(0);
|
||||
nodeQue.push(curNode.right);
|
||||
leftQue.push(mid+1);
|
||||
rightQue.push(right);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
};
|
||||
```
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function sortedArrayToBST(nums: number[]): TreeNode | null {
|
||||
function recur(nums: number[], left: number, right: number): TreeNode | null {
|
||||
if (left > right) return null;
|
||||
let mid: number = Math.floor((left + right) / 2);
|
||||
const root: TreeNode = new TreeNode(nums[mid]);
|
||||
root.left = recur(nums, left, mid - 1);
|
||||
root.right = recur(nums, mid + 1, right);
|
||||
return root;
|
||||
}
|
||||
return recur(nums, 0, nums.length - 1);
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
递归
|
||||
```c
|
||||
struct TreeNode* traversal(int* nums, int left, int right) {
|
||||
|
|
|
|||
|
|
@ -497,7 +497,7 @@ class Solution {
|
|||
## Python
|
||||
|
||||
递归法:
|
||||
```python3
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
|
|
@ -605,6 +605,7 @@ func abs(a int)int{
|
|||
```
|
||||
|
||||
## JavaScript
|
||||
递归法:
|
||||
```javascript
|
||||
var isBalanced = function(root) {
|
||||
//还是用递归三部曲 + 后序遍历 左右中 当前左子树右子树高度相差大于1就返回-1
|
||||
|
|
@ -614,8 +615,10 @@ var isBalanced = function(root) {
|
|||
if(node === null) return 0;
|
||||
// 3. 确定单层递归逻辑
|
||||
let leftDepth = getDepth(node.left); //左子树高度
|
||||
let rightDepth = getDepth(node.right); //右子树高度
|
||||
// 当判定左子树不为平衡二叉树时,即可直接返回-1
|
||||
if(leftDepth === -1) return -1;
|
||||
let rightDepth = getDepth(node.right); //右子树高度
|
||||
// 当判定右子树不为平衡二叉树时,即可直接返回-1
|
||||
if(rightDepth === -1) return -1;
|
||||
if(Math.abs(leftDepth - rightDepth) > 1) {
|
||||
return -1;
|
||||
|
|
@ -627,7 +630,68 @@ var isBalanced = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
迭代法:
|
||||
```javascript
|
||||
// 获取当前节点的高度
|
||||
var getHeight = function (curNode) {
|
||||
let queue = [];
|
||||
if (curNode !== null) queue.push(curNode); // 压入当前元素
|
||||
let depth = 0, res = 0;
|
||||
while (queue.length) {
|
||||
let node = queue[queue.length - 1]; // 取出栈顶
|
||||
if (node !== null) {
|
||||
queue.pop();
|
||||
queue.push(node); // 中
|
||||
queue.push(null);
|
||||
depth++;
|
||||
node.right && queue.push(node.right); // 右
|
||||
node.left && queue.push(node.left); // 左
|
||||
} else {
|
||||
queue.pop();
|
||||
node = queue[queue.length - 1];
|
||||
queue.pop();
|
||||
depth--;
|
||||
}
|
||||
res = res > depth ? res : depth;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
var isBalanced = function (root) {
|
||||
if (root === null) return true;
|
||||
let queue = [root];
|
||||
while (queue.length) {
|
||||
let node = queue[queue.length - 1]; // 取出栈顶
|
||||
queue.pop();
|
||||
if (Math.abs(getHeight(node.left) - getHeight(node.right)) > 1) {
|
||||
return false;
|
||||
}
|
||||
node.right && queue.push(node.right);
|
||||
node.left && queue.push(node.left);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
// 递归法
|
||||
function isBalanced(root: TreeNode | null): boolean {
|
||||
function getDepth(root: TreeNode | null): number {
|
||||
if (root === null) return 0;
|
||||
let leftDepth: number = getDepth(root.left);
|
||||
if (leftDepth === -1) return -1;
|
||||
let rightDepth: number = getDepth(root.right);
|
||||
if (rightDepth === -1) return -1;
|
||||
if (Math.abs(leftDepth - rightDepth) > 1) return -1;
|
||||
return 1 + Math.max(leftDepth, rightDepth);
|
||||
}
|
||||
return getDepth(root) !== -1;
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
递归法:
|
||||
```c
|
||||
int getDepth(struct TreeNode* node) {
|
||||
|
|
@ -730,5 +794,33 @@ bool isBalanced(struct TreeNode* root){
|
|||
}
|
||||
```
|
||||
|
||||
## Swift:
|
||||
|
||||
>递归
|
||||
```swift
|
||||
func isBalanced(_ root: TreeNode?) -> Bool {
|
||||
// -1 已经不是平衡二叉树
|
||||
return getHeight(root) == -1 ? false : true
|
||||
}
|
||||
func getHeight(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
let leftHeight = getHeight(root.left)
|
||||
if leftHeight == -1 {
|
||||
return -1
|
||||
}
|
||||
let rightHeight = getHeight(root.right)
|
||||
if rightHeight == -1 {
|
||||
return -1
|
||||
}
|
||||
if abs(leftHeight - rightHeight) > 1 {
|
||||
return -1
|
||||
} else {
|
||||
return 1 + max(leftHeight, rightHeight)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -404,8 +404,89 @@ var minDepth = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
> 递归法
|
||||
|
||||
```typescript
|
||||
function minDepth(root: TreeNode | null): number {
|
||||
if (root === null) return 0;
|
||||
if (root.left !== null && root.right === null) {
|
||||
return 1 + minDepth(root.left);
|
||||
}
|
||||
if (root.left === null && root.right !== null) {
|
||||
return 1 + minDepth(root.right);
|
||||
}
|
||||
return 1 + Math.min(minDepth(root.left), minDepth(root.right));
|
||||
}
|
||||
```
|
||||
|
||||
> 迭代法
|
||||
|
||||
```typescript
|
||||
function minDepth(root: TreeNode | null): number {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resMin: number = 0;
|
||||
let tempNode: TreeNode;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
resMin++;
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
if (tempNode.left === null && tempNode.right === null) return resMin;
|
||||
if (tempNode.left !== null) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right !== null) helperQueue.push(tempNode.right);
|
||||
}
|
||||
}
|
||||
return resMin;
|
||||
};
|
||||
```
|
||||
|
||||
## Swift
|
||||
|
||||
> 递归
|
||||
```Swift
|
||||
func minDepth(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
if root.left == nil && root.right != nil {
|
||||
return 1 + minDepth(root.right)
|
||||
}
|
||||
if root.left != nil && root.right == nil {
|
||||
return 1 + minDepth(root.left)
|
||||
}
|
||||
return 1 + min(minDepth(root.left), minDepth(root.right))
|
||||
}
|
||||
```
|
||||
|
||||
> 迭代
|
||||
```Swift
|
||||
func minDepth(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
var res = 0
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
while !queue.isEmpty {
|
||||
res += 1
|
||||
for _ in 0 ..< queue.count {
|
||||
let node = queue.removeFirst()
|
||||
if node.left == nil && node.right == nil {
|
||||
return res
|
||||
}
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
如果大家完全理解了本地的递归方法之后,就可以顺便把leetcode上113. 路径总和ii做了。
|
||||
如果大家完全理解了本题的递归方法之后,就可以顺便把leetcode上113. 路径总和ii做了。
|
||||
|
||||
# 113. 路径总和ii
|
||||
|
||||
|
|
@ -496,25 +496,20 @@ class solution:
|
|||
def pathsum(self, root: treenode, targetsum: int) -> list[list[int]]:
|
||||
|
||||
def traversal(cur_node, remain):
|
||||
if not cur_node.left and not cur_node.right and remain == 0:
|
||||
if not cur_node.left and not cur_node.right:
|
||||
if remain == 0:
|
||||
result.append(path[:])
|
||||
return
|
||||
|
||||
if not cur_node.left and not cur_node.right: return
|
||||
|
||||
if cur_node.left:
|
||||
path.append(cur_node.left.val)
|
||||
remain -= cur_node.left.val
|
||||
traversal(cur_node.left, remain)
|
||||
traversal(cur_node.left, remain-cur_node.left.val)
|
||||
path.pop()
|
||||
remain += cur_node.left.val
|
||||
|
||||
if cur_node.right:
|
||||
path.append(cur_node.right.val)
|
||||
remain -= cur_node.right.val
|
||||
traversal(cur_node.right, remain)
|
||||
traversal(cur_node.right, remain-cur_node.left.val)
|
||||
path.pop()
|
||||
remain += cur_node.right.val
|
||||
|
||||
result, path = [], []
|
||||
if not root:
|
||||
|
|
@ -524,6 +519,30 @@ class solution:
|
|||
return result
|
||||
```
|
||||
|
||||
**迭代法,用第二个队列保存目前的总和与路径**
|
||||
```python
|
||||
class Solution:
|
||||
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
|
||||
if not root:
|
||||
return []
|
||||
que, temp = deque([root]), deque([(root.val, [root.val])])
|
||||
result = []
|
||||
while que:
|
||||
for _ in range(len(que)):
|
||||
node = que.popleft()
|
||||
value, path = temp.popleft()
|
||||
if (not node.left) and (not node.right):
|
||||
if value == targetSum:
|
||||
result.append(path)
|
||||
if node.left:
|
||||
que.append(node.left)
|
||||
temp.append((node.left.val+value, path+[node.left.val]))
|
||||
if node.right:
|
||||
que.append(node.right)
|
||||
temp.append((node.right.val+value, path+[node.right.val]))
|
||||
return result
|
||||
```
|
||||
|
||||
## go
|
||||
|
||||
112. 路径总和
|
||||
|
|
@ -531,82 +550,63 @@ class solution:
|
|||
```go
|
||||
//递归法
|
||||
/**
|
||||
* definition for a binary tree node.
|
||||
* type treenode struct {
|
||||
* val int
|
||||
* left *treenode
|
||||
* right *treenode
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
func haspathsum(root *treenode, targetsum int) bool {
|
||||
var flage bool //找没找到的标志
|
||||
func hasPathSum(root *TreeNode, targetSum int) bool {
|
||||
if root == nil {
|
||||
return flage
|
||||
return false
|
||||
}
|
||||
pathsum(root,0,targetsum,&flage)
|
||||
return flage
|
||||
}
|
||||
func pathsum(root *treenode, sum int,targetsum int,flage *bool){
|
||||
sum+=root.val
|
||||
if root.left==nil&&root.right==nil&&sum==targetsum{
|
||||
*flage=true
|
||||
return
|
||||
}
|
||||
if root.left!=nil&&!(*flage){//左节点不为空且还没找到
|
||||
pathsum(root.left,sum,targetsum,flage)
|
||||
}
|
||||
if root.right!=nil&&!(*flage){//右节点不为空且没找到
|
||||
pathsum(root.right,sum,targetsum,flage)
|
||||
|
||||
targetSum -= root.Val // 将targetSum在遍历每层的时候都减去本层节点的值
|
||||
if root.Left == nil && root.Right == nil && targetSum == 0 { // 如果剩余的targetSum为0, 则正好就是符合的结果
|
||||
return true
|
||||
}
|
||||
return hasPathSum(root.Left, targetSum) || hasPathSum(root.Right, targetSum) // 否则递归找
|
||||
}
|
||||
```
|
||||
|
||||
113 递归法
|
||||
113. 路径总和 II
|
||||
|
||||
```go
|
||||
/**
|
||||
* definition for a binary tree node.
|
||||
* type treenode struct {
|
||||
* val int
|
||||
* left *treenode
|
||||
* right *treenode
|
||||
* Definition for a binary tree node.
|
||||
* type TreeNode struct {
|
||||
* Val int
|
||||
* Left *TreeNode
|
||||
* Right *TreeNode
|
||||
* }
|
||||
*/
|
||||
func pathsum(root *treenode, targetsum int) [][]int {
|
||||
var result [][]int//最终结果
|
||||
if root==nil{
|
||||
func pathSum(root *TreeNode, targetSum int) [][]int {
|
||||
result := make([][]int, 0)
|
||||
traverse(root, &result, new([]int), targetSum)
|
||||
return result
|
||||
}
|
||||
var sumnodes []int//经过路径的节点集合
|
||||
haspathsum(root,&sumnodes,targetsum,&result)
|
||||
return result
|
||||
|
||||
func traverse(node *TreeNode, result *[][]int, currPath *[]int, targetSum int) {
|
||||
if node == nil { // 这个判空也可以挪到递归遍历左右子树时去判断
|
||||
return
|
||||
}
|
||||
func haspathsum(root *treenode,sumnodes *[]int,targetsum int,result *[][]int){
|
||||
*sumnodes=append(*sumnodes,root.val)
|
||||
if root.left==nil&&root.right==nil{//叶子节点
|
||||
fmt.println(*sumnodes)
|
||||
var sum int
|
||||
var number int
|
||||
for k,v:=range *sumnodes{//求该路径节点的和
|
||||
sum+=v
|
||||
number=k
|
||||
|
||||
targetSum -= node.Val // 将targetSum在遍历每层的时候都减去本层节点的值
|
||||
*currPath = append(*currPath, node.Val) // 把当前节点放到路径记录里
|
||||
|
||||
if node.Left == nil && node.Right == nil && targetSum == 0 { // 如果剩余的targetSum为0, 则正好就是符合的结果
|
||||
// 不能直接将currPath放到result里面, 因为currPath是共享的, 每次遍历子树时都会被修改
|
||||
pathCopy := make([]int, len(*currPath))
|
||||
for i, element := range *currPath {
|
||||
pathCopy[i] = element
|
||||
}
|
||||
tempnodes:=make([]int,number+1)//新的nodes接受指针里的值,防止最终指针里的值发生变动,导致最后的结果都是最后一个sumnodes的值
|
||||
for k,v:=range *sumnodes{
|
||||
tempnodes[k]=v
|
||||
}
|
||||
if sum==targetsum{
|
||||
*result=append(*result,tempnodes)
|
||||
}
|
||||
}
|
||||
if root.left!=nil{
|
||||
haspathsum(root.left,sumnodes,targetsum,result)
|
||||
*sumnodes=(*sumnodes)[:len(*sumnodes)-1]//回溯
|
||||
}
|
||||
if root.right!=nil{
|
||||
haspathsum(root.right,sumnodes,targetsum,result)
|
||||
*sumnodes=(*sumnodes)[:len(*sumnodes)-1]//回溯
|
||||
*result = append(*result, pathCopy) // 将副本放到结果集里
|
||||
}
|
||||
|
||||
traverse(node.Left, result, currPath, targetSum)
|
||||
traverse(node.Right, result, currPath, targetSum)
|
||||
*currPath = (*currPath)[:len(*currPath)-1] // 当前节点遍历完成, 从路径记录里删除掉
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -766,7 +766,245 @@ let pathSum = function(root, targetSum) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
> 0112.路径总和
|
||||
|
||||
**递归法:**
|
||||
|
||||
```typescript
|
||||
function hasPathSum(root: TreeNode | null, targetSum: number): boolean {
|
||||
function recur(node: TreeNode, sum: number): boolean {
|
||||
console.log(sum);
|
||||
if (
|
||||
node.left === null &&
|
||||
node.right === null &&
|
||||
sum === 0
|
||||
) return true;
|
||||
if (node.left !== null) {
|
||||
sum -= node.left.val;
|
||||
if (recur(node.left, sum) === true) return true;
|
||||
sum += node.left.val;
|
||||
}
|
||||
if (node.right !== null) {
|
||||
sum -= node.right.val;
|
||||
if (recur(node.right, sum) === true) return true;
|
||||
sum += node.right.val;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (root === null) return false;
|
||||
return recur(root, targetSum - root.val);
|
||||
};
|
||||
```
|
||||
|
||||
**递归法(精简版):**
|
||||
|
||||
```typescript
|
||||
function hasPathSum(root: TreeNode | null, targetSum: number): boolean {
|
||||
if (root === null) return false;
|
||||
targetSum -= root.val;
|
||||
if (
|
||||
root.left === null &&
|
||||
root.right === null &&
|
||||
targetSum === 0
|
||||
) return true;
|
||||
return hasPathSum(root.left, targetSum) ||
|
||||
hasPathSum(root.right, targetSum);
|
||||
};
|
||||
```
|
||||
|
||||
**迭代法:**
|
||||
|
||||
```typescript
|
||||
function hasPathSum(root: TreeNode | null, targetSum: number): boolean {
|
||||
type Pair = {
|
||||
node: TreeNode, // 当前节点
|
||||
sum: number // 根节点到当前节点的路径数值总和
|
||||
}
|
||||
|
||||
const helperStack: Pair[] = [];
|
||||
if (root !== null) helperStack.push({ node: root, sum: root.val });
|
||||
let tempPair: Pair;
|
||||
while (helperStack.length > 0) {
|
||||
tempPair = helperStack.pop()!;
|
||||
if (
|
||||
tempPair.node.left === null &&
|
||||
tempPair.node.right === null &&
|
||||
tempPair.sum === targetSum
|
||||
) return true;
|
||||
if (tempPair.node.right !== null) {
|
||||
helperStack.push({
|
||||
node: tempPair.node.right,
|
||||
sum: tempPair.sum + tempPair.node.right.val
|
||||
});
|
||||
}
|
||||
if (tempPair.node.left !== null) {
|
||||
helperStack.push({
|
||||
node: tempPair.node.left,
|
||||
sum: tempPair.sum + tempPair.node.left.val
|
||||
});
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
```
|
||||
|
||||
> 0112.路径总和 ii
|
||||
|
||||
**递归法:**
|
||||
|
||||
```typescript
|
||||
function pathSum(root: TreeNode | null, targetSum: number): number[][] {
|
||||
function recur(node: TreeNode, sumGap: number, routeArr: number[]): void {
|
||||
if (
|
||||
node.left === null &&
|
||||
node.right === null &&
|
||||
sumGap === 0
|
||||
) resArr.push([...routeArr]);
|
||||
if (node.left !== null) {
|
||||
sumGap -= node.left.val;
|
||||
routeArr.push(node.left.val);
|
||||
recur(node.left, sumGap, routeArr);
|
||||
sumGap += node.left.val;
|
||||
routeArr.pop();
|
||||
}
|
||||
if (node.right !== null) {
|
||||
sumGap -= node.right.val;
|
||||
routeArr.push(node.right.val);
|
||||
recur(node.right, sumGap, routeArr);
|
||||
sumGap += node.right.val;
|
||||
routeArr.pop();
|
||||
}
|
||||
}
|
||||
const resArr: number[][] = [];
|
||||
if (root === null) return resArr;
|
||||
const routeArr: number[] = [];
|
||||
routeArr.push(root.val);
|
||||
recur(root, targetSum - root.val, routeArr);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
## Swift
|
||||
|
||||
0112.路径总和
|
||||
|
||||
**递归**
|
||||
|
||||
```swift
|
||||
func hasPathSum(_ root: TreeNode?, _ targetSum: Int) -> Bool {
|
||||
guard let root = root else {
|
||||
return false
|
||||
}
|
||||
|
||||
return traversal(root, targetSum - root.val)
|
||||
}
|
||||
|
||||
func traversal(_ cur: TreeNode?, _ count: Int) -> Bool {
|
||||
if cur?.left == nil && cur?.right == nil && count == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if cur?.left == nil && cur?.right == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if let leftNode = cur?.left {
|
||||
if traversal(leftNode, count - leftNode.val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if let rightNode = cur?.right {
|
||||
if traversal(rightNode, count - rightNode.val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
```
|
||||
**迭代**
|
||||
```swift
|
||||
func hasPathSum(_ root: TreeNode?, _ targetSum: Int) -> Bool {
|
||||
guard let root = root else {
|
||||
return false
|
||||
}
|
||||
|
||||
var stack = Array<(TreeNode, Int)>()
|
||||
stack.append((root, root.val))
|
||||
|
||||
while !stack.isEmpty {
|
||||
let node = stack.removeLast()
|
||||
|
||||
if node.0.left == nil && node.0.right == nil && targetSum == node.1 {
|
||||
return true
|
||||
}
|
||||
|
||||
if let rightNode = node.0.right {
|
||||
stack.append((rightNode, node.1 + rightNode.val))
|
||||
}
|
||||
|
||||
if let leftNode = node.0.left {
|
||||
stack.append((leftNode, node.1 + leftNode.val))
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
```
|
||||
|
||||
0113.路径总和 II
|
||||
|
||||
**递归**
|
||||
|
||||
```swift
|
||||
var result = [[Int]]()
|
||||
var path = [Int]()
|
||||
func pathSum(_ root: TreeNode?, _ targetSum: Int) -> [[Int]] {
|
||||
result.removeAll()
|
||||
path.removeAll()
|
||||
guard let root = root else {
|
||||
return result
|
||||
}
|
||||
path.append(root.val)
|
||||
traversal(root, count: targetSum - root.val)
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
func traversal(_ cur: TreeNode?, count: Int) {
|
||||
var count = count
|
||||
// 遇到了叶子节点且找到了和为targetSum的路径
|
||||
if cur?.left == nil && cur?.right == nil && count == 0 {
|
||||
result.append(path)
|
||||
return
|
||||
}
|
||||
|
||||
// 遇到叶子节点而没有找到合适的边,直接返回
|
||||
if cur?.left == nil && cur?.right == nil{
|
||||
return
|
||||
}
|
||||
|
||||
if let leftNode = cur?.left {
|
||||
path.append(leftNode.val)
|
||||
count -= leftNode.val
|
||||
traversal(leftNode, count: count)// 递归
|
||||
count += leftNode.val// 回溯
|
||||
path.removeLast()// 回溯
|
||||
}
|
||||
|
||||
if let rightNode = cur?.right {
|
||||
path.append(rightNode.val)
|
||||
count -= rightNode.val
|
||||
traversal(rightNode, count: count)// 递归
|
||||
count += rightNode.val// 回溯
|
||||
path.removeLast()// 回溯
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -211,9 +211,52 @@ class Solution:
|
|||
return root
|
||||
```
|
||||
## Go
|
||||
|
||||
```go
|
||||
// 迭代法
|
||||
func connect(root *Node) *Node {
|
||||
if root == nil {
|
||||
return root
|
||||
}
|
||||
stack := make([]*Node, 0)
|
||||
stack = append(stack, root)
|
||||
for len(stack) > 0 {
|
||||
n := len(stack) // 记录当前层节点个数
|
||||
for i := 0; i < n; i++ {
|
||||
node := stack[0] // 依次弹出节点
|
||||
stack = stack[1:]
|
||||
if i == n - 1 { // 如果是这层最右的节点,next指向nil
|
||||
node.Next = nil
|
||||
} else {
|
||||
node.Next = stack[0] // 如果不是最右的节点,next指向右边的节点
|
||||
}
|
||||
if node.Left != nil { // 如果存在左子节点,放入栈中
|
||||
stack = append(stack, node.Left)
|
||||
}
|
||||
if node.Right != nil { // 如果存在右子节点,放入栈中
|
||||
stack = append(stack, node.Right)
|
||||
}
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
```go
|
||||
// 常量级额外空间,使用next
|
||||
func connect(root *Node) *Node {
|
||||
if root == nil {
|
||||
return root
|
||||
}
|
||||
for cur := root; cur.Left != nil; cur = cur.Left { // 遍历每层最左边的节点
|
||||
for node := cur; node != nil; node = node.Next { // 当前层从左到右遍历
|
||||
node.Left.Next = node.Right // 左子节点next指向右子节点
|
||||
if node.Next != nil { //如果node next有值,右子节点指向next节点的左子节点
|
||||
node.Right.Next = node.Next.Left
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
大家可以本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的代码几乎一样,唯一的区别在:
|
||||
|
||||
|
|
@ -121,8 +121,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -148,8 +148,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(n × 5)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(n × 5)
|
||||
|
||||
当然,大家可以看到力扣官方题解里的一种优化空间写法,我这里给出对应的C++版本:
|
||||
|
||||
|
|
@ -173,8 +173,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
大家会发现dp[2]利用的是当天的dp[1]。 但结果也是对的。
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/word-ladder/)
|
||||
|
||||
|
||||
字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:
|
||||
* 序列中第一个单词是 beginWord 。
|
||||
* 序列中最后一个单词是 endWord 。
|
||||
|
|
@ -135,7 +134,29 @@ public int ladderLength(String beginWord, String endWord, List<String> wordList)
|
|||
```
|
||||
|
||||
## Python
|
||||
|
||||
```
|
||||
class Solution:
|
||||
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
|
||||
wordSet = set(wordList)
|
||||
if len(wordSet)== 0 or endWord not in wordSet:
|
||||
return 0
|
||||
mapping = {beginWord:1}
|
||||
queue = deque([beginWord])
|
||||
while queue:
|
||||
word = queue.popleft()
|
||||
path = mapping[word]
|
||||
for i in range(len(word)):
|
||||
word_list = list(word)
|
||||
for j in range(26):
|
||||
word_list[i] = chr(ord('a')+j)
|
||||
newWord = "".join(word_list)
|
||||
if newWord == endWord:
|
||||
return path+1
|
||||
if newWord in wordSet and newWord not in mapping:
|
||||
mapping[newWord] = path+1
|
||||
queue.append(newWord)
|
||||
return 0
|
||||
```
|
||||
## Go
|
||||
|
||||
## JavaScript
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ class Solution {
|
|||
```
|
||||
|
||||
Python:
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def sumNumbers(self, root: TreeNode) -> int:
|
||||
res = 0
|
||||
|
|
@ -289,7 +289,33 @@ var sumNumbers = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
C:
|
||||
```c
|
||||
//sum记录总和
|
||||
int sum;
|
||||
void traverse(struct TreeNode *node, int val) {
|
||||
//更新val为根节点到当前节点的和
|
||||
val = val * 10 + node->val;
|
||||
//若当前节点为叶子节点,记录val
|
||||
if(!node->left && !node->right) {
|
||||
sum+=val;
|
||||
return;
|
||||
}
|
||||
//若有左/右节点,遍历左/右节点
|
||||
if(node->left)
|
||||
traverse(node->left, val);
|
||||
if(node->right)
|
||||
traverse(node->right, val);
|
||||
}
|
||||
|
||||
int sumNumbers(struct TreeNode* root){
|
||||
sum = 0;
|
||||
|
||||
traverse(root, 0);
|
||||
|
||||
return sum;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ class Solution {
|
|||
|
||||
## Python
|
||||
**回溯+正反序判断回文串**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.paths = []
|
||||
|
|
@ -326,7 +326,7 @@ class Solution:
|
|||
continue
|
||||
```
|
||||
**回溯+函数判断回文串**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.paths = []
|
||||
|
|
@ -450,6 +450,38 @@ var partition = function(s) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function partition(s: string): string[][] {
|
||||
function isPalindromeStr(s: string, left: number, right: number): boolean {
|
||||
while (left < right) {
|
||||
if (s[left++] !== s[right--]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function backTracking(s: string, startIndex: number, route: string[]): void {
|
||||
let length: number = s.length;
|
||||
if (length === startIndex) {
|
||||
resArr.push(route.slice());
|
||||
return;
|
||||
}
|
||||
for (let i = startIndex; i < length; i++) {
|
||||
if (isPalindromeStr(s, startIndex, i)) {
|
||||
route.push(s.slice(startIndex, i + 1));
|
||||
backTracking(s, i + 1, route);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
const resArr: string[][] = [];
|
||||
backTracking(s, 0, []);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
```c
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public:
|
|||
```
|
||||
|
||||
* 时间复杂度:$O(n^2)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(1)$
|
||||
|
||||
C++暴力解法在leetcode上提交也可以过。
|
||||
|
||||
|
|
@ -239,6 +239,30 @@ class Solution {
|
|||
|
||||
### Python
|
||||
```python
|
||||
# 解法1
|
||||
class Solution:
|
||||
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
|
||||
n = len(gas)
|
||||
cur_sum = 0
|
||||
min_sum = float('inf')
|
||||
|
||||
for i in range(n):
|
||||
cur_sum += gas[i] - cost[i]
|
||||
min_sum = min(min_sum, cur_sum)
|
||||
|
||||
if cur_sum < 0: return -1
|
||||
if min_sum >= 0: return 0
|
||||
|
||||
for j in range(n - 1, 0, -1):
|
||||
min_sum += gas[j] - cost[j]
|
||||
if min_sum >= 0:
|
||||
return j
|
||||
|
||||
return -1
|
||||
```
|
||||
|
||||
```python
|
||||
# 解法2
|
||||
class Solution:
|
||||
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
|
||||
start = 0
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(2^n)$,因为每一个单词都有两个状态,切割和不切割
|
||||
* 空间复杂度:$O(n)$,算法递归系统调用栈的空间
|
||||
* 时间复杂度:O(2^n),因为每一个单词都有两个状态,切割和不切割
|
||||
* 空间复杂度:O(n),算法递归系统调用栈的空间
|
||||
|
||||
那么以上代码很明显要超时了,超时的数据如下:
|
||||
|
||||
|
|
@ -89,33 +89,32 @@ class Solution {
|
|||
private:
|
||||
bool backtracking (const string& s,
|
||||
const unordered_set<string>& wordSet,
|
||||
vector<int>& memory,
|
||||
vector<bool>& memory,
|
||||
int startIndex) {
|
||||
if (startIndex >= s.size()) {
|
||||
return true;
|
||||
}
|
||||
// 如果memory[startIndex]不是初始值了,直接使用memory[startIndex]的结果
|
||||
if (memory[startIndex] != -1) return memory[startIndex];
|
||||
if (!memory[startIndex]) return memory[startIndex];
|
||||
for (int i = startIndex; i < s.size(); i++) {
|
||||
string word = s.substr(startIndex, i - startIndex + 1);
|
||||
if (wordSet.find(word) != wordSet.end() && backtracking(s, wordSet, memory, i + 1)) {
|
||||
memory[startIndex] = 1; // 记录以startIndex开始的子串是可以被拆分的
|
||||
return true;
|
||||
}
|
||||
}
|
||||
memory[startIndex] = 0; // 记录以startIndex开始的子串是不可以被拆分的
|
||||
memory[startIndex] = false; // 记录以startIndex开始的子串是不可以被拆分的
|
||||
return false;
|
||||
}
|
||||
public:
|
||||
bool wordBreak(string s, vector<string>& wordDict) {
|
||||
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
|
||||
vector<int> memory(s.size(), -1); // -1 表示初始化状态
|
||||
vector<bool> memory(s.size(), 1); // -1 表示初始化状态
|
||||
return backtracking(s, wordSet, memory, 0);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
这个时间复杂度其实也是:$O(2^n)$。只不过对于上面那个超时测试用例优化效果特别明显。
|
||||
这个时间复杂度其实也是:O(2^n)。只不过对于上面那个超时测试用例优化效果特别明显。
|
||||
|
||||
**这个代码就可以AC了,当然回溯算法不是本题的主菜,背包才是!**
|
||||
|
||||
|
|
@ -208,8 +207,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n^3)$,因为substr返回子串的副本是$O(n)$的复杂度(这里的n是substring的长度)
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(n^3),因为substr返回子串的副本是O(n)的复杂度(这里的n是substring的长度)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
|
||||
## 总结
|
||||
|
|
@ -248,6 +247,40 @@ class Solution {
|
|||
return valid[s.length()];
|
||||
}
|
||||
}
|
||||
|
||||
// 回溯法+记忆化
|
||||
class Solution {
|
||||
private Set<String> set;
|
||||
private int[] memo;
|
||||
public boolean wordBreak(String s, List<String> wordDict) {
|
||||
memo = new int[s.length()];
|
||||
set = new HashSet<>(wordDict);
|
||||
return backtracking(s, 0);
|
||||
}
|
||||
|
||||
public boolean backtracking(String s, int startIndex) {
|
||||
// System.out.println(startIndex);
|
||||
if (startIndex == s.length()) {
|
||||
return true;
|
||||
}
|
||||
if (memo[startIndex] == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = startIndex; i < s.length(); i++) {
|
||||
String sub = s.substring(startIndex, i + 1);
|
||||
// 拆分出来的单词无法匹配
|
||||
if (!set.contains(sub)) {
|
||||
continue;
|
||||
}
|
||||
boolean res = backtracking(s, i + 1);
|
||||
if (res) return true;
|
||||
}
|
||||
// 这里是关键,找遍了startIndex~s.length()也没能完全匹配,标记从startIndex开始不能找到
|
||||
memo[startIndex] = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
|
|
|||
|
|
@ -106,6 +106,21 @@ class Solution:
|
|||
## Go
|
||||
|
||||
```go
|
||||
func hasCycle(head *ListNode) bool {
|
||||
if head==nil{
|
||||
return false
|
||||
} //空链表一定不会有环
|
||||
fast:=head
|
||||
slow:=head //快慢指针
|
||||
for fast.Next!=nil&&fast.Next.Next!=nil{
|
||||
fast=fast.Next.Next
|
||||
slow=slow.Next
|
||||
if fast==slow{
|
||||
return true //快慢指针相遇则有环
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
|
|
|||
|
|
@ -294,7 +294,30 @@ var detectCycle = function(head) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
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;
|
||||
fastNode = fastNode.next.next;
|
||||
if (slowNode === fastNode) {
|
||||
slowNode = head;
|
||||
while (slowNode !== fastNode) {
|
||||
slowNode = (slowNode as ListNode).next;
|
||||
fastNode = (fastNode as ListNode).next;
|
||||
}
|
||||
return slowNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
class Solution {
|
||||
func detectCycle(_ head: ListNode?) -> ListNode? {
|
||||
|
|
|
|||
|
|
@ -439,7 +439,75 @@ var reorderList = function(head, s = [], tmp) {
|
|||
}
|
||||
```
|
||||
|
||||
### C
|
||||
方法三:反转链表
|
||||
```c
|
||||
//翻转链表
|
||||
struct ListNode *reverseList(struct ListNode *head) {
|
||||
if(!head)
|
||||
return NULL;
|
||||
struct ListNode *preNode = NULL, *curNode = head;
|
||||
while(curNode) {
|
||||
//创建tempNode记录curNode->next(即将被更新)
|
||||
struct ListNode* tempNode = curNode->next;
|
||||
//将curNode->next指向preNode
|
||||
curNode->next = preNode;
|
||||
//更新preNode为curNode
|
||||
preNode = curNode;
|
||||
//curNode更新为原链表中下一个元素
|
||||
curNode = tempNode;
|
||||
}
|
||||
return preNode;
|
||||
}
|
||||
|
||||
void reorderList(struct ListNode* head){
|
||||
//slow用来截取到链表的中间节点(第一个链表的最后节点),每次循环跳一个节点。fast用来辅助,每次循环跳两个节点
|
||||
struct ListNode *fast = head, *slow = head;
|
||||
while(fast && fast->next && fast->next->next) {
|
||||
//fast每次跳两个节点
|
||||
fast = fast->next->next;
|
||||
//slow每次跳一个节点
|
||||
slow = slow->next;
|
||||
}
|
||||
//将slow->next后的节点翻转
|
||||
struct ListNode *sndLst = reverseList(slow->next);
|
||||
//将第一个链表与第二个链表断开
|
||||
slow->next = NULL;
|
||||
//因为插入从curNode->next开始,curNode刚开始已经head。所以fstList要从head->next开始
|
||||
struct ListNode *fstLst = head->next;
|
||||
struct ListNode *curNode = head;
|
||||
|
||||
int count = 0;
|
||||
//当第一个链表和第二个链表中都有节点时循环
|
||||
while(sndLst && fstLst) {
|
||||
//count为奇数,插入fstLst中的节点
|
||||
if(count % 2) {
|
||||
curNode->next = fstLst;
|
||||
fstLst = fstLst->next;
|
||||
}
|
||||
//count为偶数,插入sndList的节点
|
||||
else {
|
||||
curNode->next = sndLst;
|
||||
sndLst = sndLst->next;
|
||||
}
|
||||
//设置下一个节点
|
||||
curNode = curNode->next;
|
||||
//更新count
|
||||
++count;
|
||||
}
|
||||
|
||||
//若两个链表fstList和sndLst中还有节点,将其放入链表
|
||||
if(fstLst) {
|
||||
curNode->next = fstLst;
|
||||
}
|
||||
if(sndLst) {
|
||||
curNode->next = sndLst;
|
||||
}
|
||||
|
||||
//返回链表
|
||||
return head;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -210,6 +210,71 @@ var evalRPN = function(tokens) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
普通版:
|
||||
|
||||
```typescript
|
||||
function evalRPN(tokens: string[]): number {
|
||||
let helperStack: number[] = [];
|
||||
let temp: number;
|
||||
let i: number = 0;
|
||||
while (i < tokens.length) {
|
||||
let t: string = tokens[i];
|
||||
switch (t) {
|
||||
case '+':
|
||||
temp = helperStack.pop()! + helperStack.pop()!;
|
||||
helperStack.push(temp);
|
||||
break;
|
||||
case '-':
|
||||
temp = helperStack.pop()!;
|
||||
temp = helperStack.pop()! - temp;
|
||||
helperStack.push(temp);
|
||||
break;
|
||||
case '*':
|
||||
temp = helperStack.pop()! * helperStack.pop()!;
|
||||
helperStack.push(temp);
|
||||
break;
|
||||
case '/':
|
||||
temp = helperStack.pop()!;
|
||||
temp = Math.trunc(helperStack.pop()! / temp);
|
||||
helperStack.push(temp);
|
||||
break;
|
||||
default:
|
||||
helperStack.push(Number(t));
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return helperStack.pop()!;
|
||||
};
|
||||
```
|
||||
|
||||
优化版:
|
||||
|
||||
```typescript
|
||||
function evalRPN(tokens: string[]): number {
|
||||
const helperStack: number[] = [];
|
||||
const operatorMap: Map<string, (a: number, b: number) => number> = new Map([
|
||||
['+', (a, b) => a + b],
|
||||
['-', (a, b) => a - b],
|
||||
['/', (a, b) => Math.trunc(a / b)],
|
||||
['*', (a, b) => a * b],
|
||||
]);
|
||||
let a: number, b: number;
|
||||
for (let t of tokens) {
|
||||
if (operatorMap.has(t)) {
|
||||
b = helperStack.pop()!;
|
||||
a = helperStack.pop()!;
|
||||
helperStack.push(operatorMap.get(t)!(a, b));
|
||||
} else {
|
||||
helperStack.push(Number(t));
|
||||
}
|
||||
}
|
||||
return helperStack.pop()!;
|
||||
};
|
||||
```
|
||||
|
||||
python3
|
||||
|
||||
```python
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
一些同学会使用split库函数,分隔单词,然后定义一个新的string字符串,最后再把单词倒序相加,那么这道题题目就是一道水题了,失去了它的意义。
|
||||
|
||||
所以这里我还是提高一下本题的难度:**不要使用辅助空间,空间复杂度要求为$O(1)$。**
|
||||
所以这里我还是提高一下本题的难度:**不要使用辅助空间,空间复杂度要求为O(1)。**
|
||||
|
||||
不能使用辅助空间之后,那么只能在原字符串上下功夫了。
|
||||
|
||||
|
|
@ -79,13 +79,13 @@ void removeExtraSpaces(string& s) {
|
|||
|
||||
逻辑很简单,从前向后遍历,遇到空格了就erase。
|
||||
|
||||
如果不仔细琢磨一下erase的时间复杂读,还以为以上的代码是$O(n)$的时间复杂度呢。
|
||||
如果不仔细琢磨一下erase的时间复杂读,还以为以上的代码是O(n)的时间复杂度呢。
|
||||
|
||||
想一下真正的时间复杂度是多少,一个erase本来就是$O(n)$的操作,erase实现原理题目:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html),最优的算法来移除元素也要$O(n)$。
|
||||
想一下真正的时间复杂度是多少,一个erase本来就是O(n)的操作,erase实现原理题目:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html),最优的算法来移除元素也要O(n)。
|
||||
|
||||
erase操作上面还套了一个for循环,那么以上代码移除冗余空格的代码时间复杂度为$O(n^2)$。
|
||||
erase操作上面还套了一个for循环,那么以上代码移除冗余空格的代码时间复杂度为O(n^2)。
|
||||
|
||||
那么使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到$O(n)$的时间复杂度。
|
||||
那么使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到O(n)的时间复杂度。
|
||||
|
||||
如果对这个操作比较生疏了,可以再看一下这篇文章:[数组:就移除个元素很难么?](https://programmercarl.com/0027.移除元素.html)是如何移除元素的。
|
||||
|
||||
|
|
@ -222,7 +222,44 @@ public:
|
|||
效率:
|
||||
<img src='https://code-thinking.cdn.bcebos.com/pics/151_翻转字符串里的单词.png' width=600> </img></div>
|
||||
|
||||
```CPP
|
||||
//版本二:
|
||||
//原理同版本1,更简洁实现。
|
||||
class Solution {
|
||||
public:
|
||||
void reverse(string& s, int start, int end){ //翻转,区间写法:闭区间 []
|
||||
for (int i = start, j = end; i < j; i++, j--) {
|
||||
swap(s[i], s[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
|
||||
int slow = 0; //整体思想参考Leetcode: 27. 移除元素:https://leetcode-cn.com/problems/remove-element/
|
||||
for (int i = 0; i < s.size(); ++i) { //
|
||||
if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
|
||||
if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
|
||||
while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
|
||||
s[slow++] = s[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
s.resize(slow); //slow的大小即为去除多余空格后的大小。
|
||||
}
|
||||
|
||||
string reverseWords(string s) {
|
||||
removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。
|
||||
reverse(s, 0, s.size() - 1);
|
||||
int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。
|
||||
for (int i = 0; i <= s.size(); ++i) {
|
||||
if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。
|
||||
reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。
|
||||
start = i + 1; //更新下一个单词的开始下标start
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
|
@ -438,6 +475,38 @@ class Solution:
|
|||
|
||||
```
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def reverseWords(self, s: str) -> str:
|
||||
# method 1 - Rude but work & efficient method.
|
||||
s_list = [i for i in s.split(" ") if len(i) > 0]
|
||||
return " ".join(s_list[::-1])
|
||||
|
||||
# method 2 - Carlo's idea
|
||||
def trim_head_tail_space(ss: str):
|
||||
p = 0
|
||||
while p < len(ss) and ss[p] == " ":
|
||||
p += 1
|
||||
return ss[p:]
|
||||
|
||||
# Trim the head and tail space
|
||||
s = trim_head_tail_space(s)
|
||||
s = trim_head_tail_space(s[::-1])[::-1]
|
||||
|
||||
pf, ps, s = 0, 0, s[::-1] # Reverse the string.
|
||||
while pf < len(s):
|
||||
if s[pf] == " ":
|
||||
# Will not excede. Because we have clean the tail space.
|
||||
if s[pf] == s[pf + 1]:
|
||||
s = s[:pf] + s[pf + 1:]
|
||||
continue
|
||||
else:
|
||||
s = s[:ps] + s[ps: pf][::-1] + s[pf:]
|
||||
ps, pf = pf + 1, pf + 2
|
||||
else:
|
||||
pf += 1
|
||||
return s[:ps] + s[ps:][::-1] # Must do the last step, because the last word is omit though the pointers are on the correct positions,
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
|
@ -553,6 +622,65 @@ function reverse(strArr, start, end) {
|
|||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function reverseWords(s: string): string {
|
||||
/** Utils **/
|
||||
// 删除多余空格, 如' hello world ' => 'hello world'
|
||||
function delExtraSpace(arr: string[]): void {
|
||||
let left: number = 0,
|
||||
right: number = 0,
|
||||
length: number = arr.length;
|
||||
while (right < length && arr[right] === ' ') {
|
||||
right++;
|
||||
}
|
||||
while (right < length) {
|
||||
if (arr[right] === ' ' && arr[right - 1] === ' ') {
|
||||
right++;
|
||||
continue;
|
||||
}
|
||||
arr[left++] = arr[right++];
|
||||
}
|
||||
if (arr[left - 1] === ' ') {
|
||||
arr.length = left - 1;
|
||||
} else {
|
||||
arr.length = left;
|
||||
}
|
||||
}
|
||||
// 翻转字符串,如:'hello' => 'olleh'
|
||||
function reverseWords(strArr: string[], start: number, end: number) {
|
||||
let temp: string;
|
||||
while (start < end) {
|
||||
temp = strArr[start];
|
||||
strArr[start] = strArr[end];
|
||||
strArr[end] = temp;
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
}
|
||||
|
||||
/** Main code **/
|
||||
let strArr: string[] = s.split('');
|
||||
delExtraSpace(strArr);
|
||||
let length: number = strArr.length;
|
||||
// 翻转整个字符串
|
||||
reverseWords(strArr, 0, length - 1);
|
||||
let start: number = 0,
|
||||
end: number = 0;
|
||||
while (start < length) {
|
||||
end = start;
|
||||
while (strArr[end] !== ' ' && end < length) {
|
||||
end++;
|
||||
}
|
||||
// 翻转单个单词
|
||||
reverseWords(strArr, start, end - 1);
|
||||
start = end + 1;
|
||||
}
|
||||
return strArr.join('');
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ class Solution:
|
|||
return dp[-1][2*k]
|
||||
```
|
||||
版本二
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def maxProfit(self, k: int, prices: List[int]) -> int:
|
||||
if len(prices) == 0: return 0
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
进阶:
|
||||
|
||||
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
|
||||
你可以使用空间复杂度为 $O(1)$ 的 原地 算法解决这个问题吗?
|
||||
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
|
||||
|
||||
示例 1:
|
||||
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
本题其实和[字符串:剑指Offer58-II.左旋转字符串](https://programmercarl.com/剑指Offer58-II.左旋转字符串.html)就非常像了,剑指offer上左旋转,本题是右旋转。
|
||||
|
||||
注意题目要求是**要求使用空间复杂度为 $O(1)$ 的 原地 算法**
|
||||
注意题目要求是**要求使用空间复杂度为 O(1) 的 原地 算法**
|
||||
|
||||
那么我来提供一种旋转的方式哈。
|
||||
|
||||
|
|
@ -124,6 +124,19 @@ class Solution:
|
|||
## Go
|
||||
|
||||
```go
|
||||
func rotate(nums []int, k int) {
|
||||
l:=len(nums)
|
||||
index:=l-k%l
|
||||
reverse(nums)
|
||||
reverse(nums[:l-index])
|
||||
reverse(nums[l-index:])
|
||||
}
|
||||
func reverse(nums []int){
|
||||
l:=len(nums)
|
||||
for i:=0;i<l/2;i++{
|
||||
nums[i],nums[l-1-i]=nums[l-1-i],nums[i]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
|
|
|
|||
|
|
@ -232,7 +232,27 @@ var isHappy = function(n) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function isHappy(n: number): boolean {
|
||||
// Utils
|
||||
// 计算val各位的平方和
|
||||
function calcSum(val: number): number {
|
||||
return String(val).split("").reduce((pre, cur) => (pre + Number(cur) * Number(cur)), 0);
|
||||
}
|
||||
|
||||
let storeSet: Set<number> = new Set();
|
||||
while (n !== 1 && !storeSet.has(n)) {
|
||||
storeSet.add(n);
|
||||
n = calcSum(n);
|
||||
}
|
||||
return n === 1;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
// number 每个位置上的数字的平方和
|
||||
func getSum(_ number: Int) -> Int {
|
||||
|
|
@ -295,5 +315,75 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
C:
|
||||
```C
|
||||
typedef struct HashNodeTag {
|
||||
int key; /* num */
|
||||
struct HashNodeTag *next;
|
||||
}HashNode;
|
||||
|
||||
/* Calcualte the hash key */
|
||||
static inline int hash(int key, int size) {
|
||||
int index = key % size;
|
||||
return (index > 0) ? (index) : (-index);
|
||||
}
|
||||
|
||||
/* Calculate the sum of the squares of its digits*/
|
||||
static inline int calcSquareSum(int num) {
|
||||
unsigned int sum = 0;
|
||||
while(num > 0) {
|
||||
sum += (num % 10) * (num % 10);
|
||||
num = num/10;
|
||||
}
|
||||
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;
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ function removeElements(head: ListNode | null, val: number): ListNode | null {
|
|||
head = head.next;
|
||||
}
|
||||
if (head === null) return head;
|
||||
let pre: ListNode = head, cur: ListNode = head.next;
|
||||
let pre: ListNode = head, cur: ListNode | null = head.next;
|
||||
// 删除非头部节点
|
||||
while (cur) {
|
||||
if (cur.val === val) {
|
||||
|
|
@ -342,14 +342,14 @@ function removeElements(head: ListNode | null, val: number): ListNode | null {
|
|||
|
||||
```typescript
|
||||
function removeElements(head: ListNode | null, val: number): ListNode | null {
|
||||
head = new ListNode(0, head);
|
||||
let pre: ListNode = head, cur: ListNode = head.next;
|
||||
let dummyHead = new ListNode(0, head);
|
||||
let pre: ListNode = dummyHead, cur: ListNode | null = dummyHead.next;
|
||||
// 删除非头部节点
|
||||
while (cur) {
|
||||
if (cur.val === val) {
|
||||
pre.next = cur.next;
|
||||
} else {
|
||||
pre = pre.next;
|
||||
pre = cur;
|
||||
}
|
||||
cur = cur.next;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -314,6 +314,54 @@ var reverseList = function(head) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
// 双指针法
|
||||
function reverseList(head: ListNode | null): ListNode | null {
|
||||
let preNode: ListNode | null = null,
|
||||
curNode: ListNode | null = head,
|
||||
tempNode: ListNode | null;
|
||||
while (curNode) {
|
||||
tempNode = curNode.next;
|
||||
curNode.next = preNode;
|
||||
preNode = curNode;
|
||||
curNode = tempNode;
|
||||
}
|
||||
return preNode;
|
||||
};
|
||||
|
||||
// 递归(从前往后翻转)
|
||||
function reverseList(head: ListNode | null): ListNode | null {
|
||||
function recur(preNode: ListNode | null, curNode: ListNode | null): ListNode | null {
|
||||
if (curNode === null) return preNode;
|
||||
let tempNode: ListNode | null = curNode.next;
|
||||
curNode.next = preNode;
|
||||
preNode = curNode;
|
||||
curNode = tempNode;
|
||||
return recur(preNode, curNode);
|
||||
}
|
||||
return recur(null, head);
|
||||
};
|
||||
|
||||
// 递归(从后往前翻转)
|
||||
function reverseList(head: ListNode | null): ListNode | null {
|
||||
if (head === null) return null;
|
||||
let newHead: ListNode | null;
|
||||
function recur(node: ListNode | null, preNode: ListNode | null): void {
|
||||
if (node.next === null) {
|
||||
newHead = node;
|
||||
newHead.next = preNode;
|
||||
} else {
|
||||
recur(node.next, node);
|
||||
node.next = preNode;
|
||||
}
|
||||
}
|
||||
recur(head, null);
|
||||
return newHead;
|
||||
};
|
||||
```
|
||||
|
||||
Ruby:
|
||||
|
||||
```ruby
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
## 暴力解法
|
||||
|
||||
这道题目暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是$O(n^2)$。
|
||||
这道题目暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是O(n^2)。
|
||||
|
||||
代码如下:
|
||||
|
||||
|
|
@ -47,8 +47,8 @@ public:
|
|||
}
|
||||
};
|
||||
```
|
||||
时间复杂度:$O(n^2)$
|
||||
空间复杂度:$O(1)$
|
||||
时间复杂度:O(n^2)
|
||||
空间复杂度:O(1)
|
||||
|
||||
## 滑动窗口
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ public:
|
|||
|
||||

|
||||
|
||||
可以发现**滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将$O(n^2)$的暴力解法降为$O(n)$。**
|
||||
可以发现**滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。**
|
||||
|
||||
C++代码如下:
|
||||
|
||||
|
|
@ -107,12 +107,12 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
时间复杂度:$O(n)$
|
||||
空间复杂度:$O(1)$
|
||||
时间复杂度:O(n)
|
||||
空间复杂度:O(1)
|
||||
|
||||
**一些录友会疑惑为什么时间复杂度是$O(n)$**。
|
||||
**一些录友会疑惑为什么时间复杂度是O(n)**。
|
||||
|
||||
不要以为for里放一个while就以为是$O(n^2)$啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被被操作两次,所以时间复杂度是 2 × n 也就是$O(n)$。
|
||||
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
|
||||
|
||||
## 相关题目推荐
|
||||
|
||||
|
|
@ -330,5 +330,55 @@ def min_sub_array_len(target, nums)
|
|||
end
|
||||
```
|
||||
|
||||
C:
|
||||
暴力解法:
|
||||
```c
|
||||
int minSubArrayLen(int target, int* nums, int numsSize){
|
||||
//初始化最小长度为INT_MAX
|
||||
int minLength = INT_MAX;
|
||||
int sum;
|
||||
|
||||
int left, right;
|
||||
for(left = 0; left < numsSize; ++left) {
|
||||
//每次遍历都清零sum,计算当前位置后和>=target的子数组的长度
|
||||
sum = 0;
|
||||
//从left开始,sum中添加元素
|
||||
for(right = left; right < numsSize; ++right) {
|
||||
sum += nums[right];
|
||||
//若加入当前元素后,和大于target,则更新minLength
|
||||
if(sum >= target) {
|
||||
int subLength = right - left + 1;
|
||||
minLength = minLength < subLength ? minLength : subLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
//若minLength不为INT_MAX,则返回minLnegth
|
||||
return minLength == INT_MAX ? 0 : minLength;
|
||||
}
|
||||
```
|
||||
|
||||
滑动窗口:
|
||||
```c
|
||||
int minSubArrayLen(int target, int* nums, int numsSize){
|
||||
//初始化最小长度为INT_MAX
|
||||
int minLength = INT_MAX;
|
||||
int sum = 0;
|
||||
|
||||
int left = 0, right = 0;
|
||||
//右边界向右扩展
|
||||
for(; right < numsSize; ++right) {
|
||||
sum += nums[right];
|
||||
//当sum的值大于等于target时,保存长度,并且收缩左边界
|
||||
while(sum >= target) {
|
||||
int subLength = right - left + 1;
|
||||
minLength = minLength < subLength ? minLength : subLength;
|
||||
sum -= nums[left++];
|
||||
}
|
||||
}
|
||||
//若minLength不为INT_MAX,则返回minLnegth
|
||||
return minLength == INT_MAX ? 0 : minLength;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -123,22 +123,24 @@ Python:
|
|||
```Python
|
||||
class Solution:
|
||||
def rob(self, nums: List[int]) -> int:
|
||||
if (n := len(nums)) == 0:
|
||||
return 0
|
||||
if n == 1:
|
||||
#在198入门级的打家劫舍问题上分两种情况考虑
|
||||
#一是不偷第一间房,二是不偷最后一间房
|
||||
if len(nums)==1:#题目中提示nums.length>=1,所以不需要考虑len(nums)==0的情况
|
||||
return nums[0]
|
||||
result1 = self.robRange(nums, 0, n - 2)
|
||||
result2 = self.robRange(nums, 1, n - 1)
|
||||
return max(result1 , result2)
|
||||
val1=self.roblist(nums[1:])#不偷第一间房
|
||||
val2=self.roblist(nums[:-1])#不偷最后一间房
|
||||
return max(val1,val2)
|
||||
|
||||
def robRange(self, nums: List[int], start: int, end: int) -> int:
|
||||
if end == start: return nums[start]
|
||||
dp = [0] * len(nums)
|
||||
dp[start] = nums[start]
|
||||
dp[start + 1] = max(nums[start], nums[start + 1])
|
||||
for i in range(start + 2, end + 1):
|
||||
dp[i] = max(dp[i -2] + nums[i], dp[i - 1])
|
||||
return dp[end]
|
||||
def robRange(self,nums):
|
||||
l=len(nums)
|
||||
dp=[0]*l
|
||||
dp[0]=nums[0]
|
||||
for i in range(1,l):
|
||||
if i==1:
|
||||
dp[i]=max(dp[i-1],nums[i])
|
||||
else:
|
||||
dp[i]=max(dp[i-1],dp[i-2]+nums[i])
|
||||
return dp[-1]
|
||||
```
|
||||
|
||||
javascipt:
|
||||
|
|
|
|||
|
|
@ -396,6 +396,30 @@ var combinationSum3 = function(k, n) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function combinationSum3(k: number, n: number): number[][] {
|
||||
const resArr: number[][] = [];
|
||||
function backTracking(k: number, n: number, sum: number, startIndex: number, tempArr: number[]): void {
|
||||
if (sum > n) return;
|
||||
if (tempArr.length === k) {
|
||||
if (sum === n) {
|
||||
resArr.push(tempArr.slice());
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (let i = startIndex; i <= 9 - (k - tempArr.length) + 1; i++) {
|
||||
tempArr.push(i);
|
||||
backTracking(k, n, sum + i, i + 1, tempArr);
|
||||
tempArr.pop();
|
||||
}
|
||||
}
|
||||
backTracking(k, n, 0, 1, []);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
## C
|
||||
|
||||
```c
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(\log n)$,算上了递归系统栈占用的空间
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(log n),算上了递归系统栈占用的空间
|
||||
|
||||
**网上基本都是这个精简的代码版本,其实不建议大家照着这个来写,代码确实精简,但隐藏了一些内容,连遍历的顺序都看不出来,所以初学者建议学习版本一的代码,稳稳的打基础**。
|
||||
|
||||
|
|
@ -138,8 +138,8 @@ public:
|
|||
}
|
||||
};
|
||||
```
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
## 完全二叉树
|
||||
|
||||
|
|
@ -185,8 +185,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(\log n × \log n)$
|
||||
* 空间复杂度:$O(\log n)$
|
||||
* 时间复杂度:O(log n × log n)
|
||||
* 空间复杂度:O(log n)
|
||||
|
||||
# 其他语言版本
|
||||
|
||||
|
|
@ -447,7 +447,63 @@ var countNodes = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScrpt:
|
||||
|
||||
> 递归法
|
||||
|
||||
```typescript
|
||||
function countNodes(root: TreeNode | null): number {
|
||||
if (root === null) return 0;
|
||||
return 1 + countNodes(root.left) + countNodes(root.right);
|
||||
};
|
||||
```
|
||||
|
||||
> 迭代法
|
||||
|
||||
```typescript
|
||||
function countNodes(root: TreeNode | null): number {
|
||||
let helperQueue: TreeNode[] = [];
|
||||
let resCount: number = 0;
|
||||
let tempNode: TreeNode;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
tempNode = helperQueue.shift()!;
|
||||
resCount++;
|
||||
if (tempNode.left) helperQueue.push(tempNode.left);
|
||||
if (tempNode.right) helperQueue.push(tempNode.right);
|
||||
}
|
||||
}
|
||||
return resCount;
|
||||
};
|
||||
```
|
||||
|
||||
> 利用完全二叉树性质
|
||||
|
||||
```typescript
|
||||
function countNodes(root: TreeNode | null): number {
|
||||
if (root === null) return 0;
|
||||
let left: number = 0,
|
||||
right: number = 0;
|
||||
let curNode: TreeNode | null= root;
|
||||
while (curNode !== null) {
|
||||
left++;
|
||||
curNode = curNode.left;
|
||||
}
|
||||
curNode = root;
|
||||
while (curNode !== null) {
|
||||
right++;
|
||||
curNode = curNode.right;
|
||||
}
|
||||
if (left === right) {
|
||||
return 2 ** left - 1;
|
||||
}
|
||||
return 1 + countNodes(root.left) + countNodes(root.right);
|
||||
};
|
||||
```
|
||||
|
||||
## C:
|
||||
|
||||
递归法
|
||||
```c
|
||||
int countNodes(struct TreeNode* root) {
|
||||
|
|
@ -522,5 +578,73 @@ int countNodes(struct TreeNode* root){
|
|||
}
|
||||
```
|
||||
|
||||
## Swift:
|
||||
|
||||
> 递归
|
||||
```swift
|
||||
func countNodes(_ root: TreeNode?) -> Int {
|
||||
return _countNodes(root)
|
||||
}
|
||||
func _countNodes(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
let leftCount = _countNodes(root.left)
|
||||
let rightCount = _countNodes(root.right)
|
||||
return 1 + leftCount + rightCount
|
||||
}
|
||||
```
|
||||
|
||||
> 层序遍历
|
||||
```Swift
|
||||
func countNodes(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
var res = 0
|
||||
var queue = [TreeNode]()
|
||||
queue.append(root)
|
||||
while !queue.isEmpty {
|
||||
let size = queue.count
|
||||
for _ in 0 ..< size {
|
||||
let node = queue.removeFirst()
|
||||
res += 1
|
||||
if let left = node.left {
|
||||
queue.append(left)
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
> 利用完全二叉树性质
|
||||
```Swift
|
||||
func countNodes(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
var leftNode = root.left
|
||||
var rightNode = root.right
|
||||
var leftDepth = 0
|
||||
var rightDepth = 0
|
||||
while leftNode != nil {
|
||||
leftNode = leftNode!.left
|
||||
leftDepth += 1
|
||||
}
|
||||
while rightNode != nil {
|
||||
rightNode = rightNode!.right
|
||||
rightDepth += 1
|
||||
}
|
||||
if leftDepth == rightDepth {
|
||||
return (2 << leftDepth) - 1
|
||||
}
|
||||
return countNodes(root.left) + countNodes(root.right) + 1
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
模拟的队列执行语句如下:
|
||||
|
||||
```
|
||||
```cpp
|
||||
queue.push(1);
|
||||
queue.push(2);
|
||||
queue.pop(); // 注意弹出的操作
|
||||
|
|
@ -354,6 +354,32 @@ class MyStack:
|
|||
return len(self.queue_in) == 0
|
||||
|
||||
```
|
||||
优化,使用一个队列实现
|
||||
```python
|
||||
class MyStack:
|
||||
|
||||
def __init__(self):
|
||||
self.que = deque()
|
||||
|
||||
def push(self, x: int) -> None:
|
||||
self.que.append(x)
|
||||
|
||||
def pop(self) -> int:
|
||||
if self.empty():
|
||||
return None
|
||||
for i in range(len(self.que)-1):
|
||||
self.que.append(self.que.popleft())
|
||||
return self.que.popleft()
|
||||
|
||||
def top(self) -> int:
|
||||
if self.empty():
|
||||
return None
|
||||
return self.que[-1]
|
||||
|
||||
def empty(self) -> bool:
|
||||
return not self.que
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
|
|
@ -598,7 +624,80 @@ MyStack.prototype.empty = function() {
|
|||
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
版本一:使用两个队列模拟栈
|
||||
|
||||
```typescript
|
||||
class MyStack {
|
||||
private queue: number[];
|
||||
private tempQueue: number[];
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
this.tempQueue = [];
|
||||
}
|
||||
|
||||
push(x: number): void {
|
||||
this.queue.push(x);
|
||||
}
|
||||
|
||||
pop(): number {
|
||||
for (let i = 0, length = this.queue.length - 1; i < length; i++) {
|
||||
this.tempQueue.push(this.queue.shift()!);
|
||||
}
|
||||
let res: number = this.queue.pop()!;
|
||||
let temp: number[] = this.queue;
|
||||
this.queue = this.tempQueue;
|
||||
this.tempQueue = temp;
|
||||
return res;
|
||||
}
|
||||
|
||||
top(): number {
|
||||
let res: number = this.pop();
|
||||
this.push(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
empty(): boolean {
|
||||
return this.queue.length === 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
版本二:使用一个队列模拟栈
|
||||
|
||||
```typescript
|
||||
class MyStack {
|
||||
private queue: number[];
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
push(x: number): void {
|
||||
this.queue.push(x);
|
||||
}
|
||||
|
||||
pop(): number {
|
||||
for (let i = 0, length = this.queue.length - 1; i < length; i++) {
|
||||
this.queue.push(this.queue.shift()!);
|
||||
}
|
||||
return this.queue.shift()!;
|
||||
}
|
||||
|
||||
top(): number {
|
||||
let res: number = this.pop();
|
||||
this.push(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
empty(): boolean {
|
||||
return this.queue.length === 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Swift
|
||||
|
||||
```Swift
|
||||
// 定义一个队列数据结构
|
||||
class Queue {
|
||||
|
|
|
|||
|
|
@ -47,8 +47,6 @@
|
|||
|
||||
## 递归法
|
||||
|
||||
|
||||
|
||||
对于二叉树的递归法的前中后序遍历,已经在[二叉树:前中后序递归遍历](https://programmercarl.com/二叉树的递归遍历.html)详细讲解了。
|
||||
|
||||
我们下文以前序遍历为例,通过动画来看一下翻转的过程:
|
||||
|
|
@ -63,7 +61,7 @@
|
|||
|
||||
返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为`TreeNode*`。
|
||||
|
||||
```
|
||||
```cpp
|
||||
TreeNode* invertTree(TreeNode* root)
|
||||
```
|
||||
|
||||
|
|
@ -71,7 +69,7 @@ TreeNode* invertTree(TreeNode* root)
|
|||
|
||||
当前节点为空的时候,就返回
|
||||
|
||||
```
|
||||
```cpp
|
||||
if (root == NULL) return root;
|
||||
```
|
||||
|
||||
|
|
@ -79,7 +77,7 @@ if (root == NULL) return root;
|
|||
|
||||
因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。
|
||||
|
||||
```
|
||||
```cpp
|
||||
swap(root->left, root->right);
|
||||
invertTree(root->left);
|
||||
invertTree(root->right);
|
||||
|
|
@ -257,7 +255,7 @@ public:
|
|||
## 其他语言版本
|
||||
|
||||
|
||||
### Java:
|
||||
### Java
|
||||
|
||||
```Java
|
||||
//DFS递归
|
||||
|
|
@ -469,8 +467,6 @@ func invertTree(root *TreeNode) *TreeNode {
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### JavaScript
|
||||
|
||||
使用递归版本的前序遍历
|
||||
|
|
@ -563,7 +559,135 @@ var invertTree = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
### C:
|
||||
### TypeScript:
|
||||
|
||||
递归法:
|
||||
|
||||
```typescript
|
||||
// 递归法(前序遍历)
|
||||
function invertTree(root: TreeNode | null): TreeNode | null {
|
||||
if (root === null) return root;
|
||||
let tempNode: TreeNode | null = root.left;
|
||||
root.left = root.right;
|
||||
root.right = tempNode;
|
||||
invertTree(root.left);
|
||||
invertTree(root.right);
|
||||
return root;
|
||||
};
|
||||
|
||||
// 递归法(后序遍历)
|
||||
function invertTree(root: TreeNode | null): TreeNode | null {
|
||||
if (root === null) return root;
|
||||
invertTree(root.left);
|
||||
invertTree(root.right);
|
||||
let tempNode: TreeNode | null = root.left;
|
||||
root.left = root.right;
|
||||
root.right = tempNode;
|
||||
return root;
|
||||
};
|
||||
|
||||
// 递归法(中序遍历)
|
||||
function invertTree(root: TreeNode | null): TreeNode | null {
|
||||
if (root === null) return root;
|
||||
invertTree(root.left);
|
||||
let tempNode: TreeNode | null = root.left;
|
||||
root.left = root.right;
|
||||
root.right = tempNode;
|
||||
// 因为左右节点已经进行交换,此时的root.left 是原先的root.right
|
||||
invertTree(root.left);
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
迭代法:
|
||||
|
||||
```typescript
|
||||
// 迭代法(栈模拟前序遍历)
|
||||
function invertTree(root: TreeNode | null): TreeNode | null {
|
||||
let helperStack: TreeNode[] = [];
|
||||
let curNode: TreeNode,
|
||||
tempNode: TreeNode | null;
|
||||
if (root !== null) helperStack.push(root);
|
||||
while (helperStack.length > 0) {
|
||||
curNode = helperStack.pop()!;
|
||||
// 入栈操作最好在交换节点之前进行,便于理解
|
||||
if (curNode.right) helperStack.push(curNode.right);
|
||||
if (curNode.left) helperStack.push(curNode.left);
|
||||
tempNode = curNode.left;
|
||||
curNode.left = curNode.right;
|
||||
curNode.right = tempNode;
|
||||
}
|
||||
return root;
|
||||
};
|
||||
|
||||
// 迭代法(栈模拟中序遍历-统一写法形式)
|
||||
function invertTree(root: TreeNode | null): TreeNode | null {
|
||||
let helperStack: (TreeNode | null)[] = [];
|
||||
let curNode: TreeNode | null,
|
||||
tempNode: TreeNode | null;
|
||||
if (root !== null) helperStack.push(root);
|
||||
while (helperStack.length > 0) {
|
||||
curNode = helperStack.pop();
|
||||
if (curNode !== null) {
|
||||
if (curNode.right !== null) helperStack.push(curNode.right);
|
||||
helperStack.push(curNode);
|
||||
helperStack.push(null);
|
||||
if (curNode.left !== null) helperStack.push(curNode.left);
|
||||
} else {
|
||||
curNode = helperStack.pop()!;
|
||||
tempNode = curNode.left;
|
||||
curNode.left = curNode.right;
|
||||
curNode.right = tempNode;
|
||||
}
|
||||
}
|
||||
return root;
|
||||
};
|
||||
|
||||
// 迭代法(栈模拟后序遍历-统一写法形式)
|
||||
function invertTree(root: TreeNode | null): TreeNode | null {
|
||||
let helperStack: (TreeNode | null)[] = [];
|
||||
let curNode: TreeNode | null,
|
||||
tempNode: TreeNode | null;
|
||||
if (root !== null) helperStack.push(root);
|
||||
while (helperStack.length > 0) {
|
||||
curNode = helperStack.pop();
|
||||
if (curNode !== null) {
|
||||
helperStack.push(curNode);
|
||||
helperStack.push(null);
|
||||
if (curNode.right !== null) helperStack.push(curNode.right);
|
||||
if (curNode.left !== null) helperStack.push(curNode.left);
|
||||
} else {
|
||||
curNode = helperStack.pop()!;
|
||||
tempNode = curNode.left;
|
||||
curNode.left = curNode.right;
|
||||
curNode.right = tempNode;
|
||||
}
|
||||
}
|
||||
return root;
|
||||
};
|
||||
|
||||
// 迭代法(队列模拟层序遍历)
|
||||
function invertTree(root: TreeNode | null): TreeNode | null {
|
||||
const helperQueue: TreeNode[] = [];
|
||||
let curNode: TreeNode,
|
||||
tempNode: TreeNode | null;
|
||||
if (root !== null) helperQueue.push(root);
|
||||
while (helperQueue.length > 0) {
|
||||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||||
curNode = helperQueue.shift()!;
|
||||
tempNode = curNode.left;
|
||||
curNode.left = curNode.right;
|
||||
curNode.right = tempNode;
|
||||
if (curNode.left !== null) helperQueue.push(curNode.left);
|
||||
if (curNode.right !== null) helperQueue.push(curNode.right);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
### C
|
||||
|
||||
递归法
|
||||
```c
|
||||
struct TreeNode* invertTree(struct TreeNode* root){
|
||||
|
|
@ -647,5 +771,54 @@ func invertTree1(_ root: TreeNode?) -> TreeNode? {
|
|||
}
|
||||
```
|
||||
|
||||
### Swift
|
||||
|
||||
深度优先递归。
|
||||
|
||||
```swift
|
||||
func invertTree(_ root: TreeNode?) -> TreeNode? {
|
||||
guard let node = root else { return root }
|
||||
swap(&node.left, &node.right)
|
||||
_ = invertTree(node.left)
|
||||
_ = invertTree(node.right)
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
深度优先迭代,子结点顺序不重要,从根结点出发深度遍历即可。
|
||||
|
||||
```swift
|
||||
func invertTree(_ root: TreeNode?) -> TreeNode? {
|
||||
guard let node = root else { return root }
|
||||
var stack = [node]
|
||||
while !stack.isEmpty {
|
||||
guard let node = stack.popLast() else { break }
|
||||
swap(&node.left, &node.right)
|
||||
if let node = node.left { stack.append(node) }
|
||||
if let node = node.right { stack.append(node) }
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
广度优先迭代。
|
||||
|
||||
```swift
|
||||
func invertTree(_ root: TreeNode?) -> TreeNode? {
|
||||
guard let node = root else { return root }
|
||||
var queue = [node]
|
||||
while !queue.isEmpty {
|
||||
let count = queue.count
|
||||
for _ in 0 ..< count {
|
||||
let node = queue.removeFirst()
|
||||
swap(&node.left, &node.right)
|
||||
if let node = node.left { queue.append(node) }
|
||||
if let node = node.right { queue.append(node) }
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ empty() -- 返回队列是否为空。
|
|||
|
||||
示例:
|
||||
|
||||
```
|
||||
```cpp
|
||||
MyQueue queue = new MyQueue();
|
||||
queue.push(1);
|
||||
queue.push(2);
|
||||
|
|
@ -348,7 +348,44 @@ MyQueue.prototype.empty = function() {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
class MyQueue {
|
||||
private stackIn: number[]
|
||||
private stackOut: number[]
|
||||
constructor() {
|
||||
this.stackIn = [];
|
||||
this.stackOut = [];
|
||||
}
|
||||
|
||||
push(x: number): void {
|
||||
this.stackIn.push(x);
|
||||
}
|
||||
|
||||
pop(): number {
|
||||
if (this.stackOut.length === 0) {
|
||||
while (this.stackIn.length > 0) {
|
||||
this.stackOut.push(this.stackIn.pop()!);
|
||||
}
|
||||
}
|
||||
return this.stackOut.pop()!;
|
||||
}
|
||||
|
||||
peek(): number {
|
||||
let temp: number = this.pop();
|
||||
this.stackOut.push(temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
empty(): boolean {
|
||||
return this.stackIn.length === 0 && this.stackOut.length === 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
class MyQueue {
|
||||
|
||||
|
|
|
|||
|
|
@ -350,6 +350,39 @@ var lowestCommonAncestor = function(root, p, q) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
> 递归法:
|
||||
|
||||
```typescript
|
||||
function lowestCommonAncestor(root: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null {
|
||||
if (root.val > p.val && root.val > q.val)
|
||||
return lowestCommonAncestor(root.left, p, q);
|
||||
if (root.val < p.val && root.val < q.val)
|
||||
return lowestCommonAncestor(root.right, p, q);
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
|
||||
```typescript
|
||||
function lowestCommonAncestor(root: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null {
|
||||
while (root !== null) {
|
||||
if (root.val > p.val && root.val > q.val) {
|
||||
root = root.left;
|
||||
} else if (root.val < p.val && root.val < q.val) {
|
||||
root = root.right;
|
||||
} else {
|
||||
return root;
|
||||
};
|
||||
};
|
||||
return null;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -45,9 +45,13 @@
|
|||
|
||||
接下来就看如何判断一个节点是节点q和节点p的公共公共祖先呢。
|
||||
|
||||
**如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。**
|
||||
**首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。**
|
||||
|
||||
使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现如何这个条件的节点,就是最近公共节点了。
|
||||
**但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。**
|
||||
|
||||
使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现满足第一种情况的节点,就是最近公共节点了。
|
||||
|
||||
**但是如果p或者q本身就是最近公共祖先呢?其实只需要找到一个节点是p或者q的时候,直接返回当前节点,无需继续递归子树。如果接下来的遍历中找到了后继节点满足第一种情况则修改返回值为后继节点,否则,继续返回已找到的节点即可。为什么满足第一种情况的节点一定是p或q的后继节点呢?大家可以仔细思考一下。**
|
||||
|
||||
递归三部曲:
|
||||
|
||||
|
|
@ -325,6 +329,20 @@ var lowestCommonAncestor = function(root, p, q) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
function lowestCommonAncestor(root: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null {
|
||||
if (root === null || root === p || root === q) return root;
|
||||
const left = lowestCommonAncestor(root.left, p, q);
|
||||
const right = lowestCommonAncestor(root.right, p, q);
|
||||
if (left !== null && right !== null) return root;
|
||||
if (left !== null) return left;
|
||||
if (right !== null) return right;
|
||||
return null;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
这个队列应该长这个样子:
|
||||
|
||||
```
|
||||
```cpp
|
||||
class MyQueue {
|
||||
public:
|
||||
void pop(int value) {
|
||||
|
|
@ -193,7 +193,7 @@ public:
|
|||
|
||||
# 扩展
|
||||
|
||||
大家貌似对单调队列 都有一些疑惑,首先要明确的是,题解中单调队列里的pop和push接口,仅适用于本题哈。单调队列不是一成不变的,而是不同场景不同写法,总之要保证队列里单调递减或递增的原则,所以叫做单调队列。 不要以为本地中的单调队列实现就是固定的写法哈。
|
||||
大家貌似对单调队列 都有一些疑惑,首先要明确的是,题解中单调队列里的pop和push接口,仅适用于本题哈。单调队列不是一成不变的,而是不同场景不同写法,总之要保证队列里单调递减或递增的原则,所以叫做单调队列。 不要以为本题中的单调队列实现就是固定的写法哈。
|
||||
|
||||
大家貌似对deque也有一些疑惑,C++中deque是stack和queue默认的底层实现容器(这个我们之前已经讲过啦),deque是可以两边扩展的,而且deque里元素并不是严格的连续分布的。
|
||||
|
||||
|
|
@ -395,30 +395,102 @@ func maxSlidingWindow(nums []int, k int) []int {
|
|||
|
||||
Javascript:
|
||||
```javascript
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @param {number} k
|
||||
* @return {number[]}
|
||||
*/
|
||||
var maxSlidingWindow = function (nums, k) {
|
||||
// 队列数组(存放的是元素下标,为了取值方便)
|
||||
const q = [];
|
||||
// 结果数组
|
||||
const ans = [];
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
// 若队列不为空,且当前元素大于等于队尾所存下标的元素,则弹出队尾
|
||||
while (q.length && nums[i] >= nums[q[q.length - 1]]) {
|
||||
q.pop();
|
||||
class MonoQueue {
|
||||
queue;
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
}
|
||||
// 入队当前元素下标
|
||||
q.push(i);
|
||||
// 判断当前最大值(即队首元素)是否在窗口中,若不在便将其出队
|
||||
if (q[0] <= i - k) {
|
||||
q.shift();
|
||||
enqueue(value) {
|
||||
let back = this.queue[this.queue.length - 1];
|
||||
while (back !== undefined && back < value) {
|
||||
this.queue.pop();
|
||||
back = this.queue[this.queue.length - 1];
|
||||
}
|
||||
// 当达到窗口大小时便开始向结果中添加数据
|
||||
if (i >= k - 1) ans.push(nums[q[0]]);
|
||||
this.queue.push(value);
|
||||
}
|
||||
return ans;
|
||||
dequeue(value) {
|
||||
let front = this.front();
|
||||
if (front === value) {
|
||||
this.queue.shift();
|
||||
}
|
||||
}
|
||||
front() {
|
||||
return this.queue[0];
|
||||
}
|
||||
}
|
||||
let helperQueue = new MonoQueue();
|
||||
let i = 0, j = 0;
|
||||
let resArr = [];
|
||||
while (j < k) {
|
||||
helperQueue.enqueue(nums[j++]);
|
||||
}
|
||||
resArr.push(helperQueue.front());
|
||||
while (j < nums.length) {
|
||||
helperQueue.enqueue(nums[j]);
|
||||
helperQueue.dequeue(nums[i]);
|
||||
resArr.push(helperQueue.front());
|
||||
i++, j++;
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function maxSlidingWindow(nums: number[], k: number): number[] {
|
||||
/** 单调递减队列 */
|
||||
class MonoQueue {
|
||||
private queue: number[];
|
||||
constructor() {
|
||||
this.queue = [];
|
||||
};
|
||||
/** 入队:value如果大于队尾元素,则将队尾元素删除,直至队尾元素大于value,或者队列为空 */
|
||||
public enqueue(value: number): void {
|
||||
let back: number | undefined = this.queue[this.queue.length - 1];
|
||||
while (back !== undefined && back < value) {
|
||||
this.queue.pop();
|
||||
back = this.queue[this.queue.length - 1];
|
||||
}
|
||||
this.queue.push(value);
|
||||
};
|
||||
/** 出队:只有当队头元素等于value,才出队 */
|
||||
public dequeue(value: number): void {
|
||||
let top: number | undefined = this.top();
|
||||
if (top !== undefined && top === value) {
|
||||
this.queue.shift();
|
||||
}
|
||||
}
|
||||
public top(): number | undefined {
|
||||
return this.queue[0];
|
||||
}
|
||||
}
|
||||
const helperQueue: MonoQueue = new MonoQueue();
|
||||
let i: number = 0,
|
||||
j: number = 0;
|
||||
let resArr: number[] = [];
|
||||
while (j < k) {
|
||||
helperQueue.enqueue(nums[j++]);
|
||||
}
|
||||
resArr.push(helperQueue.top()!);
|
||||
while (j < nums.length) {
|
||||
helperQueue.enqueue(nums[j]);
|
||||
helperQueue.dequeue(nums[i]);
|
||||
resArr.push(helperQueue.top()!);
|
||||
j++, i++;
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```Swift
|
||||
/// 双向链表
|
||||
class DoublyListNode {
|
||||
|
|
@ -525,5 +597,39 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Swift解法二:
|
||||
|
||||
```swift
|
||||
func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
|
||||
var result = [Int]()
|
||||
var window = [Int]()
|
||||
var right = 0, left = right - k + 1
|
||||
|
||||
while right < nums.count {
|
||||
let value = nums[right]
|
||||
|
||||
// 因为窗口移动丢弃的左边数
|
||||
if left > 0, left - 1 == window.first {
|
||||
window.removeFirst()
|
||||
}
|
||||
|
||||
// 保证末尾的是最大的
|
||||
while !window.isEmpty, value > nums[window.last!] {
|
||||
window.removeLast()
|
||||
}
|
||||
window.append(right)
|
||||
|
||||
if left >= 0 { // 窗口形成
|
||||
result.append(nums[window.first!])
|
||||
}
|
||||
|
||||
right += 1
|
||||
left += 1
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
## 思路
|
||||
|
||||
先看暴力的解法,两层for循环,同时还要记录字符是否重复出现,很明显时间复杂度是 $O(n^2)$。
|
||||
先看暴力的解法,两层for循环,同时还要记录字符是否重复出现,很明显时间复杂度是 O(n^2)。
|
||||
|
||||
暴力的方法这里就不做介绍了,直接看一下有没有更优的方式。
|
||||
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
|
||||
最后如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。
|
||||
|
||||
时间复杂度为$O(n)$,空间上因为定义是的一个常量大小的辅助数组,所以空间复杂度为$O(1)$。
|
||||
时间复杂度为O(n),空间上因为定义是的一个常量大小的辅助数组,所以空间复杂度为O(1)。
|
||||
|
||||
|
||||
C++ 代码如下:
|
||||
|
|
@ -214,7 +214,23 @@ var isAnagram = function(s, t) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function isAnagram(s: string, t: string): boolean {
|
||||
if (s.length !== t.length) return false;
|
||||
let helperArr: number[] = new Array(26).fill(0);
|
||||
let pivot: number = 'a'.charCodeAt(0);
|
||||
for (let i = 0, length = s.length; i < length; i++) {
|
||||
helperArr[s.charCodeAt(i) - pivot]++;
|
||||
helperArr[t.charCodeAt(i) - pivot]--;
|
||||
}
|
||||
return helperArr.every(i => i === 0);
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```Swift
|
||||
func isAnagram(_ s: String, _ t: String) -> Bool {
|
||||
if s.count != t.count {
|
||||
|
|
|
|||
|
|
@ -581,7 +581,126 @@ var binaryTreePaths = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 递归法
|
||||
|
||||
```typescript
|
||||
function binaryTreePaths(root: TreeNode | null): string[] {
|
||||
function recur(node: TreeNode, route: string, resArr: string[]): void {
|
||||
route += String(node.val);
|
||||
if (node.left === null && node.right === null) {
|
||||
resArr.push(route);
|
||||
return;
|
||||
}
|
||||
if (node.left !== null) recur(node.left, route + '->', resArr);
|
||||
if (node.right !== null) recur(node.right, route + '->', resArr);
|
||||
}
|
||||
const resArr: string[] = [];
|
||||
if (root === null) return resArr;
|
||||
recur(root, '', resArr);
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
> 迭代法
|
||||
|
||||
```typescript
|
||||
// 迭代法2
|
||||
function binaryTreePaths(root: TreeNode | null): string[] {
|
||||
let helperStack: TreeNode[] = [];
|
||||
let tempNode: TreeNode;
|
||||
let routeArr: string[] = [];
|
||||
let resArr: string[] = [];
|
||||
if (root !== null) {
|
||||
helperStack.push(root);
|
||||
routeArr.push(String(root.val));
|
||||
};
|
||||
while (helperStack.length > 0) {
|
||||
tempNode = helperStack.pop()!;
|
||||
let route: string = routeArr.pop()!; // tempNode 对应的路径
|
||||
if (tempNode.left === null && tempNode.right === null) {
|
||||
resArr.push(route);
|
||||
}
|
||||
if (tempNode.right !== null) {
|
||||
helperStack.push(tempNode.right);
|
||||
routeArr.push(route + '->' + tempNode.right.val); // tempNode.right 对应的路径
|
||||
}
|
||||
if (tempNode.left !== null) {
|
||||
helperStack.push(tempNode.left);
|
||||
routeArr.push(route + '->' + tempNode.left.val); // tempNode.left 对应的路径
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
> 递归/回溯
|
||||
```swift
|
||||
func binaryTreePaths(_ root: TreeNode?) -> [String] {
|
||||
var res = [String]()
|
||||
guard let root = root else {
|
||||
return res
|
||||
}
|
||||
var path = [Int]()
|
||||
_binaryTreePaths(root, path: &path, res: &res)
|
||||
return res
|
||||
}
|
||||
func _binaryTreePaths(_ root: TreeNode, path: inout [Int], res: inout [String]) {
|
||||
path.append(root.val)
|
||||
if root.left == nil && root.right == nil {
|
||||
var str = ""
|
||||
for i in 0 ..< path.count - 1 {
|
||||
str.append("\(path[i])->")
|
||||
}
|
||||
str.append("\(path.last!)")
|
||||
res.append(str)
|
||||
return
|
||||
}
|
||||
if let left = root.left {
|
||||
_binaryTreePaths(left, path: &path, res: &res)
|
||||
path.removeLast()
|
||||
}
|
||||
if let right = root.right {
|
||||
_binaryTreePaths(right, path: &path, res: &res)
|
||||
path.removeLast()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 迭代
|
||||
```swift
|
||||
func binaryTreePaths(_ root: TreeNode?) -> [String] {
|
||||
var res = [String]()
|
||||
guard let root = root else {
|
||||
return res
|
||||
}
|
||||
var stackNode = [TreeNode]()
|
||||
stackNode.append(root)
|
||||
|
||||
var stackStr = [String]()
|
||||
stackStr.append("\(root.val)")
|
||||
|
||||
while !stackNode.isEmpty {
|
||||
let node = stackNode.popLast()!
|
||||
let str = stackStr.popLast()!
|
||||
if node.left == nil && node.right == nil {
|
||||
res.append(str)
|
||||
}
|
||||
if let left = node.left {
|
||||
stackNode.append(left)
|
||||
stackStr.append("\(str)->\(left.val)")
|
||||
}
|
||||
if let right = node.right {
|
||||
stackNode.append(right)
|
||||
stackStr.append("\(str)->\(right.val)")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ class Solution {
|
|||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def numSquares(self, n: int) -> int:
|
||||
'''版本一,先遍历背包, 再遍历物品'''
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
好了,我们说一说双指针法,大家如果对双指针还不熟悉,可以看我的这篇总结[双指针法:总结篇!](https://programmercarl.com/双指针总结.html)。
|
||||
|
||||
双指针法在数组移除元素中,可以达到$O(n)$的时间复杂度,在[27.移除元素](https://programmercarl.com/0027.移除元素.html)里已经详细讲解了,那么本题和移除元素其实是一个套路。
|
||||
双指针法在数组移除元素中,可以达到O(n)的时间复杂度,在[27.移除元素](https://programmercarl.com/0027.移除元素.html)里已经详细讲解了,那么本题和移除元素其实是一个套路。
|
||||
|
||||
**相当于对整个数组移除元素0,然后slowIndex之后都是移除元素0的冗余元素,把这些元素都赋值为0就可以了**。
|
||||
|
||||
|
|
@ -89,9 +89,33 @@ Python:
|
|||
for i in range(slow, len(nums)):
|
||||
nums[i] = 0
|
||||
```
|
||||
交换前后变量,避免补零
|
||||
```python
|
||||
def moveZeroes(self, nums: List[int]) -> None:
|
||||
slow, fast = 0, 0
|
||||
while fast < len(nums):
|
||||
if nums[fast] != 0:
|
||||
nums[slow], nums[fast] = nums[fast], nums[slow]
|
||||
slow += 1 # 保持[0, slow)区间是没有0的
|
||||
fast += 1
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func moveZeroes(nums []int) {
|
||||
slow := 0
|
||||
for fast := 0; fast < len(nums); fast ++ {
|
||||
if nums[fast] != 0 {
|
||||
temp := nums[slow]
|
||||
nums[slow] = nums[fast]
|
||||
nums[fast] = temp
|
||||
slow++
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
```javascript
|
||||
var moveZeroes = function(nums) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
1. dp[i]的定义
|
||||
|
||||
**dp[i]表示i之前包括i的最长上升子序列的长度**。
|
||||
**dp[i]表示i之前包括i的以nums[i]结尾最长上升子序列的长度**
|
||||
|
||||
2. 状态转移方程
|
||||
|
||||
|
|
|
|||
|
|
@ -205,6 +205,29 @@ class Solution {
|
|||
}
|
||||
}
|
||||
```
|
||||
```java
|
||||
//另一种解题思路
|
||||
class Solution {
|
||||
public int maxProfit(int[] prices) {
|
||||
int[][] dp = new int[prices.length + 1][2];
|
||||
dp[1][0] = -prices[0];
|
||||
|
||||
for (int i = 2; i <= prices.length; i++) {
|
||||
/*
|
||||
dp[i][0] 第i天未持有股票收益;
|
||||
dp[i][1] 第i天持有股票收益;
|
||||
情况一:第i天是冷静期,不能以dp[i-1][1]购买股票,所以以dp[i - 2][1]买股票,没问题
|
||||
情况二:第i天不是冷静期,理论上应该以dp[i-1][1]购买股票,但是第i天不是冷静期说明,第i-1天没有卖出股票,
|
||||
则dp[i-1][1]=dp[i-2][1],所以可以用dp[i-2][1]买股票,没问题
|
||||
*/
|
||||
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 2][1] - prices[i - 1]);
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i - 1]);
|
||||
}
|
||||
|
||||
return dp[prices.length][1];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
|
@ -284,6 +307,24 @@ const maxProfit = (prices) => {
|
|||
};
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 一维数组空间优化
|
||||
const maxProfit = (prices) => {
|
||||
const n = prices.length
|
||||
const dp = new Array(4).fill(0)
|
||||
dp[0] = -prices[0]
|
||||
for (let i = 1; i < n; i ++) {
|
||||
const temp = dp[0] // 缓存上一次的状态
|
||||
const temp1 = dp[2]
|
||||
dp[0] = Math.max(dp[0], Math.max(dp[3] - prices[i], dp[1] - prices[i])) // 持有状态
|
||||
dp[1] = Math.max(dp[1], dp[3]) // 今天不操作且不持有股票
|
||||
dp[2] = temp + prices[i] // 今天卖出股票
|
||||
dp[3] = temp1 // 冷冻期
|
||||
}
|
||||
return Math.max(...dp)
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ class Solution {
|
|||
|
||||
Python:
|
||||
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def coinChange(self, coins: List[int], amount: int) -> int:
|
||||
'''版本一'''
|
||||
|
|
|
|||
|
|
@ -342,6 +342,64 @@ class Solution:
|
|||
return path
|
||||
```
|
||||
|
||||
### Go
|
||||
```go
|
||||
type pair struct {
|
||||
target string
|
||||
visited bool
|
||||
}
|
||||
type pairs []*pair
|
||||
|
||||
func (p pairs) Len() int {
|
||||
return len(p)
|
||||
}
|
||||
func (p pairs) Swap(i, j int) {
|
||||
p[i], p[j] = p[j], p[i]
|
||||
}
|
||||
func (p pairs) Less(i, j int) bool {
|
||||
return p[i].target < p[j].target
|
||||
}
|
||||
|
||||
func findItinerary(tickets [][]string) []string {
|
||||
result := []string{}
|
||||
// map[出发机场] pair{目的地,是否被访问过}
|
||||
targets := make(map[string]pairs)
|
||||
for _, ticket := range tickets {
|
||||
if targets[ticket[0]] == nil {
|
||||
targets[ticket[0]] = make(pairs, 0)
|
||||
}
|
||||
targets[ticket[0]] = append(targets[ticket[0]], &pair{target: ticket[1], visited: false})
|
||||
}
|
||||
for k, _ := range targets {
|
||||
sort.Sort(targets[k])
|
||||
}
|
||||
result = append(result, "JFK")
|
||||
var backtracking func() bool
|
||||
backtracking = func() bool {
|
||||
if len(tickets)+1 == len(result) {
|
||||
return true
|
||||
}
|
||||
// 取出起飞航班对应的目的地
|
||||
for _, pair := range targets[result[len(result)-1]] {
|
||||
if pair.visited == false {
|
||||
result = append(result, pair.target)
|
||||
pair.visited = true
|
||||
if backtracking() {
|
||||
return true
|
||||
}
|
||||
result = result[:len(result)-1]
|
||||
pair.visited = false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
backtracking()
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
### C语言
|
||||
|
||||
```C
|
||||
|
|
@ -448,6 +506,50 @@ var findItinerary = function(tickets) {
|
|||
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function findItinerary(tickets: string[][]): string[] {
|
||||
/**
|
||||
TicketsMap 实例:
|
||||
{ NRT: Map(1) { 'JFK' => 1 }, JFK: Map(2) { 'KUL' => 1, 'NRT' => 1 } }
|
||||
这里选择Map数据结构的原因是:与Object类型的一个主要差异是,Map实例会维护键值对的插入顺序。
|
||||
*/
|
||||
type TicketsMap = {
|
||||
[index: string]: Map<string, number>
|
||||
};
|
||||
tickets.sort((a, b) => {
|
||||
return a[1] < b[1] ? -1 : 1;
|
||||
});
|
||||
const ticketMap: TicketsMap = {};
|
||||
for (const [from, to] of tickets) {
|
||||
if (ticketMap[from] === undefined) {
|
||||
ticketMap[from] = new Map();
|
||||
}
|
||||
ticketMap[from].set(to, (ticketMap[from].get(to) || 0) + 1);
|
||||
}
|
||||
const resRoute = ['JFK'];
|
||||
backTracking(tickets.length, ticketMap, resRoute);
|
||||
return resRoute;
|
||||
function backTracking(ticketNum: number, ticketMap: TicketsMap, route: string[]): boolean {
|
||||
if (route.length === ticketNum + 1) return true;
|
||||
const targetMap = ticketMap[route[route.length - 1]];
|
||||
if (targetMap !== undefined) {
|
||||
for (const [to, count] of targetMap.entries()) {
|
||||
if (count > 0) {
|
||||
route.push(to);
|
||||
targetMap.set(to, count - 1);
|
||||
if (backTracking(ticketNum, ticketMap, route) === true) return true;
|
||||
targetMap.set(to, count);
|
||||
route.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Swift
|
||||
|
||||
直接迭代tickets数组:
|
||||
|
|
@ -568,5 +670,77 @@ for line in tickets {
|
|||
}
|
||||
```
|
||||
|
||||
### Go
|
||||
```Go
|
||||
|
||||
// 先排序,然后找到第一条路径即可返回
|
||||
func findItinerary(tickets [][]string) []string {
|
||||
var path []string // 用来保存搜索的路径
|
||||
data := make(map[string]ticketSlice) // 用来保存tickets排序后的结果
|
||||
|
||||
var search func(airport string) bool
|
||||
search = func(airport string) bool {
|
||||
if len(path) == len(tickets) {
|
||||
path = append(path, airport)
|
||||
return true
|
||||
}
|
||||
to := data[airport]
|
||||
for _, item := range to {
|
||||
if item.Count == 0 {
|
||||
// 已用完
|
||||
continue
|
||||
}
|
||||
|
||||
path = append(path, airport)
|
||||
item.Count--
|
||||
if search(item.To) { return true }
|
||||
item.Count++
|
||||
path = path[:len(path) - 1]
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 排序
|
||||
// 感觉这段代码有点啰嗦,不知道能不能简化一下
|
||||
tmp := make(map[string]map[string]int)
|
||||
for _, ticket := range tickets {
|
||||
if to, ok := tmp[ticket[0]]; ok {
|
||||
if _, ok2 := to[ticket[1]]; ok2 {
|
||||
to[ticket[1]]++
|
||||
} else {
|
||||
to[ticket[1]] = 1
|
||||
}
|
||||
} else {
|
||||
tmp[ticket[0]] = map[string]int{
|
||||
ticket[1]: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
for from, to := range tmp {
|
||||
var tmp ticketSlice
|
||||
for to, num := range to {
|
||||
tmp = append(tmp, &ticketStat{To: to, Count: num})
|
||||
}
|
||||
sort.Sort(tmp)
|
||||
data[from] = tmp
|
||||
}
|
||||
|
||||
search("JFK")
|
||||
return path
|
||||
}
|
||||
|
||||
type ticketStat struct {
|
||||
To string
|
||||
Count int
|
||||
}
|
||||
type ticketSlice []*ticketStat
|
||||
|
||||
func (p ticketSlice) Len() int { return len(p) }
|
||||
func (p ticketSlice) Less(i, j int) bool { return strings.Compare(p[i].To, p[j].To) == -1 }
|
||||
func (p ticketSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ public:
|
|||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
### Java
|
||||
```Java
|
||||
class Solution {
|
||||
// 1.递归去偷,超时
|
||||
|
|
@ -284,11 +284,11 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
### Python
|
||||
|
||||
> 暴力递归
|
||||
|
||||
```python3
|
||||
```python
|
||||
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
|
|
@ -315,7 +315,7 @@ class Solution:
|
|||
|
||||
> 记忆化递归
|
||||
|
||||
```python3
|
||||
```python
|
||||
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
|
|
@ -345,7 +345,7 @@ class Solution:
|
|||
```
|
||||
|
||||
> 动态规划
|
||||
```python3
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
|
|
@ -367,7 +367,7 @@ class Solution:
|
|||
return (val1, val2)
|
||||
```
|
||||
|
||||
Go:
|
||||
### Go
|
||||
|
||||
动态规划
|
||||
|
||||
|
|
@ -402,7 +402,7 @@ func robTree(cur *TreeNode) []int {
|
|||
}
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
### JavaScript
|
||||
|
||||
> 动态规划
|
||||
|
||||
|
|
@ -429,7 +429,7 @@ const rob = root => {
|
|||
};
|
||||
```
|
||||
|
||||
Go:
|
||||
### Go
|
||||
```go
|
||||
// 打家劫舍Ⅲ 动态规划
|
||||
// 时间复杂度O(n) 空间复杂度O(logn)
|
||||
|
|
|
|||
|
|
@ -4,23 +4,22 @@
|
|||
</a>
|
||||
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
## 343. 整数拆分
|
||||
# 343. 整数拆分
|
||||
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/integer-break/)
|
||||
|
||||
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
|
||||
|
||||
示例 1:
|
||||
输入: 2
|
||||
输出: 1
|
||||
|
||||
\解释: 2 = 1 + 1, 1 × 1 = 1。
|
||||
* 输入: 2
|
||||
* 输出: 1
|
||||
* 解释: 2 = 1 + 1, 1 × 1 = 1。
|
||||
|
||||
示例 2:
|
||||
输入: 10
|
||||
输出: 36
|
||||
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
|
||||
说明: 你可以假设 n 不小于 2 且不大于 58。
|
||||
* 输入: 10
|
||||
* 输出: 36
|
||||
* 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
|
||||
* 说明: 你可以假设 n 不小于 2 且不大于 58。
|
||||
|
||||
## 思路
|
||||
|
||||
|
|
@ -120,8 +119,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n^2)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(n^2)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
### 贪心
|
||||
|
||||
|
|
@ -149,8 +148,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n)$
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
## 总结
|
||||
|
||||
|
|
@ -193,18 +192,21 @@ public:
|
|||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
### Java
|
||||
```Java
|
||||
class Solution {
|
||||
public int integerBreak(int n) {
|
||||
//dp[i]为正整数i拆分结果的最大乘积
|
||||
//dp[i] 为正整数 i 拆分后的结果的最大乘积
|
||||
int[]dp=new int[n+1];
|
||||
dp[2]=1;
|
||||
for (int i = 3; i <= n; ++i) {
|
||||
for (int j = 1; j < i - 1; ++j) {
|
||||
//j*(i-j)代表把i拆分为j和i-j两个数相乘
|
||||
//j*dp[i-j]代表把i拆分成j和继续把(i-j)这个数拆分,取(i-j)拆分结果中的最大乘积与j相乘
|
||||
for(int i=3;i<=n;i++){
|
||||
for(int j=1;j<=i-j;j++){
|
||||
// 这里的 j 其实最大值为 i-j,再大只不过是重复而已,
|
||||
//并且,在本题中,我们分析 dp[0], dp[1]都是无意义的,
|
||||
//j 最大到 i-j,就不会用到 dp[0]与dp[1]
|
||||
dp[i]=Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
|
||||
// j * (i - j) 是单纯的把整数 i 拆分为两个数 也就是 i,i-j ,再相乘
|
||||
//而j * dp[i - j]是将 i 拆分成两个以及两个以上的个数,再相乘。
|
||||
}
|
||||
}
|
||||
return dp[n];
|
||||
|
|
@ -212,7 +214,7 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
### Python
|
||||
```python
|
||||
class Solution:
|
||||
def integerBreak(self, n: int) -> int:
|
||||
|
|
@ -226,7 +228,8 @@ class Solution:
|
|||
dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))
|
||||
return dp[n]
|
||||
```
|
||||
Go:
|
||||
|
||||
### Go
|
||||
```golang
|
||||
func integerBreak(n int) int {
|
||||
/**
|
||||
|
|
@ -256,7 +259,7 @@ func max(a,b int) int{
|
|||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
### Javascript
|
||||
```Javascript
|
||||
var integerBreak = function(n) {
|
||||
let dp = new Array(n + 1).fill(0)
|
||||
|
|
@ -271,5 +274,40 @@ var integerBreak = function(n) {
|
|||
};
|
||||
```
|
||||
|
||||
C:
|
||||
```c
|
||||
//初始化DP数组
|
||||
int *initDP(int num) {
|
||||
int* dp = (int*)malloc(sizeof(int) * (num + 1));
|
||||
int i;
|
||||
for(i = 0; i < num + 1; ++i) {
|
||||
dp[i] = 0;
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
//取三数最大值
|
||||
int max(int num1, int num2, int num3) {
|
||||
int tempMax = num1 > num2 ? num1 : num2;
|
||||
return tempMax > num3 ? tempMax : num3;
|
||||
}
|
||||
|
||||
int integerBreak(int n){
|
||||
int *dp = initDP(n);
|
||||
//初始化dp[2]为1
|
||||
dp[2] = 1;
|
||||
|
||||
int i;
|
||||
for(i = 3; i <= n; ++i) {
|
||||
int j;
|
||||
for(j = 1; j < i - 1; ++j) {
|
||||
//取得上次循环:dp[i],原数相乘,或j*dp[]i-j] 三数中的最大值
|
||||
dp[i] = max(dp[i], j * (i - j), j * dp[i - j]);
|
||||
}
|
||||
}
|
||||
return dp[n];
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -200,6 +200,27 @@ var reverseString = function(s) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
Do not return anything, modify s in-place instead.
|
||||
*/
|
||||
function reverseString(s: string[]): void {
|
||||
let length: number = s.length;
|
||||
let left: number = 0,
|
||||
right: number = length - 1;
|
||||
let tempStr: string;
|
||||
while (left < right) {
|
||||
tempStr = s[left];
|
||||
s[left] = s[right];
|
||||
s[right] = tempStr;
|
||||
left++;
|
||||
right--;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
|
|
@ -232,6 +253,19 @@ void reverseString(char* s, int sSize){
|
|||
}
|
||||
```
|
||||
|
||||
C#:
|
||||
```csharp
|
||||
public class Solution
|
||||
{
|
||||
public void ReverseString(char[] s)
|
||||
{
|
||||
for (int i = 0, j = s.Length - 1; i < j; i++, j--)
|
||||
{
|
||||
(s[i], s[j]) = (s[j], s[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ class Solution {
|
|||
|
||||
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
|
||||
// 根据map的value值正序排,相当于一个小顶堆
|
||||
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o2.getValue() - o1.getValue());
|
||||
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
|
||||
for (Map.Entry<Integer, Integer> entry : entries) {
|
||||
queue.offer(entry);
|
||||
if (queue.size() > k) {
|
||||
|
|
@ -358,6 +358,22 @@ PriorityQueue.prototype.compare = function(index1, index2) {
|
|||
}
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function topKFrequent(nums: number[], k: number): number[] {
|
||||
const countMap: Map<number, number> = new Map();
|
||||
for (let num of nums) {
|
||||
countMap.set(num, (countMap.get(num) || 0) + 1);
|
||||
}
|
||||
// tS没有最小堆的数据结构,所以直接对整个数组进行排序,取前k个元素
|
||||
return [...countMap.entries()]
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, k)
|
||||
.map(i => i[0]);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
注意题目特意说明:**输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序**
|
||||
|
||||
这道题用暴力的解法时间复杂度是$O(n^2)$,那来看看使用哈希法进一步优化。
|
||||
这道题用暴力的解法时间复杂度是O(n^2),那来看看使用哈希法进一步优化。
|
||||
|
||||
那么用数组来做哈希表也是不错的选择,例如[242. 有效的字母异位词](https://programmercarl.com/0242.有效的字母异位词.html)
|
||||
|
||||
|
|
@ -190,7 +190,33 @@ var intersection = function(nums1, nums2) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
版本一(正常解法):
|
||||
|
||||
```typescript
|
||||
function intersection(nums1: number[], nums2: number[]): number[] {
|
||||
let resSet: Set<number> = new Set(),
|
||||
nums1Set: Set<number> = new Set(nums1);
|
||||
for (let i of nums2) {
|
||||
if (nums1Set.has(i)) {
|
||||
resSet.add(i);
|
||||
}
|
||||
}
|
||||
return Array.from(resSet);
|
||||
};
|
||||
```
|
||||
|
||||
版本二(秀操作):
|
||||
|
||||
```typescript
|
||||
function intersection(nums1: number[], nums2: number[]): number[] {
|
||||
return Array.from(new Set(nums1.filter(i => nums2.includes(i))))
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func intersection(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
|
||||
var set1 = Set<Int>()
|
||||
|
|
@ -255,6 +281,38 @@ impl Solution {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
C:
|
||||
```C
|
||||
int* intersection1(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
|
||||
|
||||
int nums1Cnt[1000] = {0};
|
||||
int lessSize = nums1Size < nums2Size ? nums1Size : nums2Size;
|
||||
int * result = (int *) calloc(lessSize, sizeof(int));
|
||||
int resultIndex = 0;
|
||||
int* tempNums;
|
||||
|
||||
int i;
|
||||
|
||||
/* Calculate the number's counts for nums1 array */
|
||||
for(i = 0; i < nums1Size; i ++) {
|
||||
nums1Cnt[nums1[i]]++;
|
||||
}
|
||||
|
||||
/* Check if the value in nums2 is existing in nums1 count array */
|
||||
for(i = 0; i < nums2Size; i ++) {
|
||||
if(nums1Cnt[nums2[i]] > 0) {
|
||||
result[resultIndex] = nums2[i];
|
||||
resultIndex ++;
|
||||
/* Clear this count to avoid duplicated value */
|
||||
nums1Cnt[nums2[i]] = 0;
|
||||
}
|
||||
}
|
||||
* returnSize = resultIndex;
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
## 相关题目
|
||||
|
||||
* 350.两个数组的交集 II
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ public:
|
|||
```Java
|
||||
class Solution {
|
||||
public int wiggleMaxLength(int[] nums) {
|
||||
if (nums == null || nums.length <= 1) {
|
||||
if (nums.length <= 1) {
|
||||
return nums.length;
|
||||
}
|
||||
//当前差值
|
||||
|
|
@ -228,7 +228,7 @@ class Solution {
|
|||
|
||||
### Python
|
||||
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def wiggleMaxLength(self, nums: List[int]) -> int:
|
||||
preC,curC,res = 0,0,1 #题目里nums长度大于等于1,当长度为1时,其实到不了for循环里去,所以不用考虑nums长度
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public:
|
|||
|
||||
```
|
||||
|
||||
C++测试用例有超过两个树相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
|
||||
C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
|
||||
|
||||
但java就不用考虑这个限制,java里的int也是四个字节吧,也有可能leetcode后台对不同语言的测试数据不一样。
|
||||
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ class Solution(object):
|
|||
|
||||
Python写法四:
|
||||
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
|
||||
c1 = collections.Counter(ransomNote)
|
||||
|
|
@ -264,6 +264,27 @@ var canConstruct = function(ransomNote, magazine) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function canConstruct(ransomNote: string, magazine: string): boolean {
|
||||
let helperArr: number[] = new Array(26).fill(0);
|
||||
let base: number = 'a'.charCodeAt(0);
|
||||
let index: number;
|
||||
for (let i = 0, length = magazine.length; i < length; i++) {
|
||||
helperArr[magazine[i].charCodeAt(0) - base]++;
|
||||
}
|
||||
for (let i = 0, length = ransomNote.length; i < length; i++) {
|
||||
index = ransomNote[i].charCodeAt(0) - base;
|
||||
helperArr[index]--;
|
||||
if (helperArr[index] < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
PHP:
|
||||
```php
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ if (s[i - 1] != t[j - 1]),此时相当于t要删除元素,t如果把当前
|
|||
|
||||
如果要是定义的dp[i][j]是以下标i为结尾的字符串s和以下标j为结尾的字符串t,初始化就比较麻烦了。
|
||||
|
||||
这里dp[i][0]和dp[0][j]是没有含义的,仅仅是为了给递推公式做前期铺垫,所以初始化为0。
|
||||
dp[i][0] 表示以下标i-1为结尾的字符串,与空字符串的相同子序列长度,所以为0. dp[0][j]同理。
|
||||
|
||||
**其实这里只初始化dp[i][0]就够了,但一起初始化也方便,所以就一起操作了**,代码如下:
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
**首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。**
|
||||
|
||||
因为题目中其实没有说清楚左叶子究竟是什么节点,那么我来给出左叶子的明确定义:**如果左节点不为空,且左节点没有左右孩子,那么这个节点就是左叶子**
|
||||
因为题目中其实没有说清楚左叶子究竟是什么节点,那么我来给出左叶子的明确定义:**如果左节点不为空,且左节点没有左右孩子,那么这个节点的左节点就是左叶子**
|
||||
|
||||
大家思考一下如下图中二叉树,左叶子之和究竟是多少?
|
||||
|
||||
|
|
@ -229,7 +229,7 @@ class Solution {
|
|||
## Python
|
||||
|
||||
**递归后序遍历**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def sumOfLeftLeaves(self, root: TreeNode) -> int:
|
||||
if not root:
|
||||
|
|
@ -246,7 +246,7 @@ class Solution:
|
|||
```
|
||||
|
||||
**迭代**
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
def sumOfLeftLeaves(self, root: TreeNode) -> int:
|
||||
"""
|
||||
|
|
@ -372,7 +372,99 @@ var sumOfLeftLeaves = function(root) {
|
|||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
> 递归法
|
||||
|
||||
```typescript
|
||||
function sumOfLeftLeaves(root: TreeNode | null): number {
|
||||
if (root === null) return 0;
|
||||
let midVal: number = 0;
|
||||
if (
|
||||
root.left !== null &&
|
||||
root.left.left === null &&
|
||||
root.left.right === null
|
||||
) {
|
||||
midVal = root.left.val;
|
||||
}
|
||||
let leftVal: number = sumOfLeftLeaves(root.left);
|
||||
let rightVal: number = sumOfLeftLeaves(root.right);
|
||||
return midVal + leftVal + rightVal;
|
||||
};
|
||||
```
|
||||
|
||||
> 迭代法
|
||||
|
||||
```typescript
|
||||
function sumOfLeftLeaves(root: TreeNode | null): number {
|
||||
let helperStack: TreeNode[] = [];
|
||||
let tempNode: TreeNode;
|
||||
let sum: number = 0;
|
||||
if (root !== null) helperStack.push(root);
|
||||
while (helperStack.length > 0) {
|
||||
tempNode = helperStack.pop()!;
|
||||
if (
|
||||
tempNode.left !== null &&
|
||||
tempNode.left.left === null &&
|
||||
tempNode.left.right === null
|
||||
) {
|
||||
sum += tempNode.left.val;
|
||||
}
|
||||
if (tempNode.right !== null) helperStack.push(tempNode.right);
|
||||
if (tempNode.left !== null) helperStack.push(tempNode.left);
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
```
|
||||
|
||||
## Swift
|
||||
|
||||
**递归法**
|
||||
```swift
|
||||
func sumOfLeftLeaves(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
|
||||
let leftValue = sumOfLeftLeaves(root.left)
|
||||
let rightValue = sumOfLeftLeaves(root.right)
|
||||
|
||||
var midValue: Int = 0
|
||||
if root.left != nil && root.left?.left == nil && root.left?.right == nil {
|
||||
midValue = root.left!.val
|
||||
}
|
||||
|
||||
let sum = midValue + leftValue + rightValue
|
||||
return sum
|
||||
}
|
||||
```
|
||||
**迭代法**
|
||||
```swift
|
||||
func sumOfLeftLeaves(_ root: TreeNode?) -> Int {
|
||||
guard let root = root else {
|
||||
return 0
|
||||
}
|
||||
|
||||
var stack = Array<TreeNode>()
|
||||
stack.append(root)
|
||||
var sum = 0
|
||||
|
||||
while !stack.isEmpty {
|
||||
let lastNode = stack.removeLast()
|
||||
|
||||
if lastNode.left != nil && lastNode.left?.left == nil && lastNode.left?.right == nil {
|
||||
sum += lastNode.left!.val
|
||||
}
|
||||
if let right = lastNode.right {
|
||||
stack.append(right)
|
||||
}
|
||||
if let left = lastNode.left {
|
||||
stack.append(left)
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -116,12 +116,12 @@ public:
|
|||
}
|
||||
};
|
||||
```
|
||||
* 时间复杂度:$O(n\log n + n^2)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(nlog n + n^2)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
但使用vector是非常费时的,C++中vector(可以理解是一个动态数组,底层是普通数组实现的)如果插入元素大于预先普通数组大小,vector底部会有一个扩容的操作,即申请两倍于原先普通数组的大小,然后把数据拷贝到另一个更大的数组上。
|
||||
|
||||
所以使用vector(动态数组)来insert,是费时的,插入再拷贝的话,单纯一个插入的操作就是$O(n^2)$了,甚至可能拷贝好几次,就不止$O(n^2)$了。
|
||||
所以使用vector(动态数组)来insert,是费时的,插入再拷贝的话,单纯一个插入的操作就是O(n^2)了,甚至可能拷贝好几次,就不止O(n^2)了。
|
||||
|
||||
改成链表之后,C++代码如下:
|
||||
|
||||
|
|
@ -150,8 +150,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n\log n + n^2)$
|
||||
* 空间复杂度:$O(n)$
|
||||
* 时间复杂度:O(nlog n + n^2)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
大家可以把两个版本的代码提交一下试试,就可以发现其差别了!
|
||||
|
||||
|
|
|
|||
|
|
@ -127,9 +127,9 @@ for(int i = 0; i < nums.size(); i++) {
|
|||
|
||||
5. 举例推导dp数组
|
||||
|
||||
dp[i]的数值一定是小于等于i的。
|
||||
dp[j]的数值一定是小于等于j的。
|
||||
|
||||
**如果dp[i] == i 说明,集合中的子集总和正好可以凑成总和i,理解这一点很重要。**
|
||||
**如果dp[j] == j 说明,集合中的子集总和正好可以凑成总和j,理解这一点很重要。**
|
||||
|
||||
用例1,输入[1,5,11,5] 为例,如图:
|
||||
|
||||
|
|
@ -168,8 +168,8 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n^2)$
|
||||
* 空间复杂度:$O(n)$,虽然dp数组大小为一个常数,但是大常数
|
||||
* 时间复杂度:O(n^2)
|
||||
* 空间复杂度:O(n),虽然dp数组大小为一个常数,但是大常数
|
||||
|
||||
## 总结
|
||||
|
||||
|
|
@ -180,8 +180,6 @@ public:
|
|||
看代码的话,就可以发现,基本就是按照01背包的写法来的。
|
||||
|
||||
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
|
|
@ -210,18 +208,126 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public class Solution {
|
||||
public static void main(String[] args) {
|
||||
int num[] = {1,5,11,5};
|
||||
canPartition(num);
|
||||
|
||||
}
|
||||
public static boolean canPartition(int[] nums) {
|
||||
int len = nums.length;
|
||||
// 题目已经说非空数组,可以不做非空判断
|
||||
int sum = 0;
|
||||
for (int num : nums) {
|
||||
sum += num;
|
||||
}
|
||||
// 特判:如果是奇数,就不符合要求
|
||||
if ((sum %2 ) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int target = sum / 2; //目标背包容量
|
||||
// 创建二维状态数组,行:物品索引,列:容量(包括 0)
|
||||
/*
|
||||
dp[i][j]表示从数组的 [0, i] 这个子区间内挑选一些正整数
|
||||
每个数只能用一次,使得这些数的和恰好等于 j。
|
||||
*/
|
||||
boolean[][] dp = new boolean[len][target + 1];
|
||||
|
||||
// 先填表格第 0 行,第 1 个数只能让容积为它自己的背包恰好装满 (这里的dp[][]数组的含义就是“恰好”,所以就算容积比它大的也不要)
|
||||
if (nums[0] <= target) {
|
||||
dp[0][nums[0]] = true;
|
||||
}
|
||||
// 再填表格后面几行
|
||||
//外层遍历物品
|
||||
for (int i = 1; i < len; i++) {
|
||||
//内层遍历背包
|
||||
for (int j = 0; j <= target; j++) {
|
||||
// 直接从上一行先把结果抄下来,然后再修正
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
|
||||
//如果某个物品单独的重量恰好就等于背包的重量,那么也是满足dp数组的定义的
|
||||
if (nums[i] == j) {
|
||||
dp[i][j] = true;
|
||||
continue;
|
||||
}
|
||||
//如果某个物品的重量小于j,那就可以看该物品是否放入背包
|
||||
//dp[i - 1][j]表示该物品不放入背包,如果在 [0, i - 1] 这个子区间内已经有一部分元素,使得它们的和为 j ,那么 dp[i][j] = true;
|
||||
//dp[i - 1][j - nums[i]]表示该物品放入背包。如果在 [0, i - 1] 这个子区间内就得找到一部分元素,使得它们的和为 j - nums[i]。
|
||||
if (nums[i] < j) {
|
||||
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
for (int j = 0; j <= target; j++) {
|
||||
System.out.print(dp[i][j]+" ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
return dp[len - 1][target];
|
||||
}
|
||||
}
|
||||
//dp数组的打印结果
|
||||
false true false false false false false false false false false false
|
||||
false true false false false true true false false false false false
|
||||
false true false false false true true false false false false true
|
||||
false true false false false true true false false false true true
|
||||
```
|
||||
|
||||
|
||||
二维数组版本(易于理解):
|
||||
```Java
|
||||
class Solution {
|
||||
public boolean canPartition(int[] nums) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
sum += nums[i];
|
||||
}
|
||||
|
||||
if (sum % 2 == 1)
|
||||
return false;
|
||||
int target = sum / 2;
|
||||
|
||||
//dp[i][j]代表可装物品为0-i,背包容量为j的情况下,背包内容量的最大价值
|
||||
int[][] dp = new int[nums.length][target + 1];
|
||||
|
||||
//初始化,dp[0][j]的最大价值nums[0](if j > weight[i])
|
||||
//dp[i][0]均为0,不用初始化
|
||||
for (int j = nums[0]; j <= target; j++) {
|
||||
dp[0][j] = nums[0];
|
||||
}
|
||||
|
||||
//遍历物品,遍历背包
|
||||
//递推公式:
|
||||
for (int i = 1; i < nums.length; i++) {
|
||||
for (int j = 0; j <= target; j++) {
|
||||
//背包容量可以容纳nums[i]
|
||||
if (j >= nums[i]) {
|
||||
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);
|
||||
} else {
|
||||
dp[i][j] = dp[i - 1][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dp[nums.length - 1][target] == target;
|
||||
}
|
||||
}
|
||||
```
|
||||
Python:
|
||||
```python
|
||||
class Solution:
|
||||
def canPartition(self, nums: List[int]) -> bool:
|
||||
taraget = sum(nums)
|
||||
if taraget % 2 == 1: return False
|
||||
taraget //= 2
|
||||
target = sum(nums)
|
||||
if target % 2 == 1: return False
|
||||
target //= 2
|
||||
dp = [0] * 10001
|
||||
for i in range(len(nums)):
|
||||
for j in range(taraget, nums[i] - 1, -1):
|
||||
for j in range(target, nums[i] - 1, -1):
|
||||
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
|
||||
return taraget == dp[taraget]
|
||||
return target == dp[target]
|
||||
```
|
||||
Go:
|
||||
```go
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ public:
|
|||
}
|
||||
};
|
||||
```
|
||||
* 时间复杂度:$O(n\log n)$ ,有一个快排
|
||||
* 空间复杂度:$O(1)$
|
||||
* 时间复杂度:O(nlog n) ,有一个快排
|
||||
* 空间复杂度:O(1)
|
||||
|
||||
大家此时会发现如此复杂的一个问题,代码实现却这么简单!
|
||||
|
||||
|
|
@ -183,28 +183,22 @@ public:
|
|||
```java
|
||||
class Solution {
|
||||
public int eraseOverlapIntervals(int[][] intervals) {
|
||||
if (intervals.length < 2) return 0;
|
||||
|
||||
Arrays.sort(intervals, new Comparator<int[]>() {
|
||||
@Override
|
||||
public int compare(int[] o1, int[] o2) {
|
||||
if (o1[1] != o2[1]) {
|
||||
return Integer.compare(o1[1],o2[1]);
|
||||
} else {
|
||||
return Integer.compare(o1[0],o2[0]);
|
||||
}
|
||||
}
|
||||
Arrays.sort(intervals, (a, b) -> {
|
||||
if (a[0] == a[0]) return a[1] - b[1];
|
||||
return a[0] - b[0];
|
||||
});
|
||||
|
||||
int count = 1;
|
||||
int edge = intervals[0][1];
|
||||
for (int i = 1; i < intervals.length; i++) {
|
||||
int count = 0;
|
||||
int edge = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < intervals.length; i++) {
|
||||
if (edge <= intervals[i][0]) {
|
||||
count ++; //non overlap + 1
|
||||
edge = intervals[i][1];
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return intervals.length - count;
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ if (root == nullptr) return root;
|
|||
|
||||
* 确定单层递归的逻辑
|
||||
|
||||
这里就把平衡二叉树中删除节点遇到的情况都搞清楚。
|
||||
这里就把二叉搜索树中删除节点遇到的情况都搞清楚。
|
||||
|
||||
有以下五种情况:
|
||||
|
||||
|
|
@ -518,6 +518,70 @@ var deleteNode = function (root, key) {
|
|||
}
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
> 递归法:
|
||||
|
||||
```typescript
|
||||
function deleteNode(root: TreeNode | null, key: number): TreeNode | null {
|
||||
if (root === null) return null;
|
||||
if (root.val === key) {
|
||||
if (root.left === null && root.right === null) return null;
|
||||
if (root.left === null) return root.right;
|
||||
if (root.right === null) return root.left;
|
||||
let curNode: TreeNode = root.right;
|
||||
while (curNode.left !== null) {
|
||||
curNode = curNode.left;
|
||||
}
|
||||
curNode.left = root.left;
|
||||
return root.right;
|
||||
}
|
||||
if (root.val > key) root.left = deleteNode(root.left, key);
|
||||
if (root.val < key) root.right = deleteNode(root.right, key);
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
|
||||
```typescript
|
||||
function deleteNode(root: TreeNode | null, key: number): TreeNode | null {
|
||||
function removeTargetNode(root: TreeNode): TreeNode | null {
|
||||
if (root.left === null && root.right === null) return null;
|
||||
if (root.right === null) return root.left;
|
||||
if (root.left === null) return root.right;
|
||||
let curNode: TreeNode | null = root.right;
|
||||
while (curNode.left !== null) {
|
||||
curNode = curNode.left;
|
||||
}
|
||||
curNode.left = root.left;
|
||||
return root.right;
|
||||
}
|
||||
let preNode: TreeNode | null = null,
|
||||
curNode: TreeNode | null = root;
|
||||
while (curNode !== null) {
|
||||
if (curNode.val === key) break;
|
||||
preNode = curNode;
|
||||
if (curNode.val > key) {
|
||||
curNode = curNode.left;
|
||||
} else {
|
||||
curNode = curNode.right;
|
||||
}
|
||||
}
|
||||
if (curNode === null) return root;
|
||||
if (preNode === null) {
|
||||
// 删除头节点
|
||||
return removeTargetNode(curNode);
|
||||
}
|
||||
if (preNode.val > key) {
|
||||
preNode.left = removeTargetNode(curNode);
|
||||
} else {
|
||||
preNode.right = removeTargetNode(curNode);
|
||||
}
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -239,5 +239,30 @@ int findMinArrowShots(int** points, int pointsSize, int* pointsColSize){
|
|||
}
|
||||
```
|
||||
|
||||
### Rust
|
||||
```Rust
|
||||
use std::cmp;
|
||||
impl Solution {
|
||||
pub fn find_min_arrow_shots(mut points: Vec<Vec<i32>>) -> i32 {
|
||||
if points.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
points.sort_by_key(|point| point[0]);
|
||||
|
||||
let size = points.len();
|
||||
let mut count = 1;
|
||||
|
||||
for i in 1..size {
|
||||
if points[i][0] > points[i-1][1] {
|
||||
count += 1;
|
||||
} else {
|
||||
points[i][1] = cmp::min(points[i][1], points[i-1][1]);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
```
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
|
|
|||
|
|
@ -141,7 +141,24 @@ class Solution(object):
|
|||
|
||||
```
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def fourSumCount(self, nums1: list, nums2: list, nums3: list, nums4: list) -> int:
|
||||
from collections import defaultdict # You may use normal dict instead.
|
||||
rec, cnt = defaultdict(lambda : 0), 0
|
||||
# To store the summary of all the possible combinations of nums1 & nums2, together with their frequencies.
|
||||
for i in nums1:
|
||||
for j in nums2:
|
||||
rec[i+j] += 1
|
||||
# To add up the frequencies if the corresponding value occurs in the dictionary
|
||||
for i in nums3:
|
||||
for j in nums4:
|
||||
cnt += rec.get(-(i+j), 0) # No matched key, return 0.
|
||||
return cnt
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
|
||||
m := make(map[int]int)
|
||||
|
|
@ -192,8 +209,33 @@ var fourSumCount = function(nums1, nums2, nums3, nums4) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
```typescript
|
||||
function fourSumCount(nums1: number[], nums2: number[], nums3: number[], nums4: number[]): number {
|
||||
let helperMap: Map<number, number> = new Map();
|
||||
let resNum: number = 0;
|
||||
let tempVal: number | undefined;
|
||||
for (let i of nums1) {
|
||||
for (let j of nums2) {
|
||||
tempVal = helperMap.get(i + j);
|
||||
helperMap.set(i + j, tempVal ? tempVal + 1 : 1);
|
||||
}
|
||||
}
|
||||
for (let k of nums3) {
|
||||
for (let l of nums4) {
|
||||
tempVal = helperMap.get(0 - (k + l));
|
||||
if (tempVal) {
|
||||
resNum += tempVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
return resNum;
|
||||
};
|
||||
```
|
||||
|
||||
PHP:
|
||||
|
||||
```php
|
||||
class Solution {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
## 思路
|
||||
|
||||
为了了满足更多的小孩,就不要造成饼干尺寸的浪费。
|
||||
为了满足更多的小孩,就不要造成饼干尺寸的浪费。
|
||||
|
||||
大尺寸的饼干既可以满足胃口大的孩子也可以满足胃口小的孩子,那么就应该优先满足胃口大的。
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ class Solution {
|
|||
```
|
||||
|
||||
### Python
|
||||
```python3
|
||||
```python
|
||||
class Solution:
|
||||
# 思路1:优先考虑胃饼干
|
||||
def findContentChildren(self, g: List[int], s: List[int]) -> int:
|
||||
|
|
|
|||
|
|
@ -361,7 +361,65 @@ var repeatedSubstringPattern = function (s) {
|
|||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 前缀表统一减一
|
||||
|
||||
```typescript
|
||||
function repeatedSubstringPattern(s: string): boolean {
|
||||
function getNext(str: string): number[] {
|
||||
let next: number[] = [];
|
||||
let j: number = -1;
|
||||
next[0] = j;
|
||||
for (let i = 1, length = str.length; i < length; i++) {
|
||||
while (j >= 0 && str[i] !== str[j + 1]) {
|
||||
j = next[j];
|
||||
}
|
||||
if (str[i] === str[j + 1]) {
|
||||
j++;
|
||||
}
|
||||
next[i] = j;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
let next: number[] = getNext(s);
|
||||
let sLength: number = s.length;
|
||||
let nextLength: number = next.length;
|
||||
let suffixLength: number = next[nextLength - 1] + 1;
|
||||
if (suffixLength > 0 && sLength % (sLength - suffixLength) === 0) return true;
|
||||
return false;
|
||||
};
|
||||
```
|
||||
|
||||
> 前缀表不减一
|
||||
|
||||
```typescript
|
||||
function repeatedSubstringPattern(s: string): boolean {
|
||||
function getNext(str: string): number[] {
|
||||
let next: number[] = [];
|
||||
let j: number = 0;
|
||||
next[0] = j;
|
||||
for (let i = 1, length = str.length; i < length; i++) {
|
||||
while (j > 0 && str[i] !== str[j]) {
|
||||
j = next[j - 1];
|
||||
}
|
||||
if (str[i] === str[j]) {
|
||||
j++;
|
||||
}
|
||||
next[i] = j;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
let next: number[] = getNext(s);
|
||||
let sLength: number = s.length;
|
||||
let nextLength: number = next.length;
|
||||
let suffixLength: number = next[nextLength - 1];
|
||||
if (suffixLength > 0 && sLength % (sLength - suffixLength) === 0) return true;
|
||||
return false;
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<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
Loading…
Reference in New Issue