Merge branch 'master' of github.com:Hanmengnan/leetcode-master

This commit is contained in:
HanMengnan 2022-05-16 10:47:34 +08:00
commit 34b4ffa24c
243 changed files with 9125 additions and 4473 deletions

View File

@ -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)
* 编程语言
@ -103,6 +102,7 @@
* [看了这么多代码,谈一谈代码风格!](./problems/前序/代码风格.md)
* [力扣上的代码想在本地编译运行?](./problems/前序/力扣上的代码想在本地编译运行?.md)
* [什么是核心代码模式什么又是ACM模式](./problems/前序/什么是核心代码模式什么又是ACM模式.md)
* [刷题要不要用库函数](./problems/前序/刷力扣用不用库函数.md)
* [ACM模式如何构造二叉树](./problems/前序/ACM模式如何构建二叉树.md)
* [解密互联网大厂研发流程](./problems/前序/互联网大厂研发流程.md)
@ -123,52 +123,13 @@
* 算法性能分析
* [关于时间复杂度,你不知道的都在这里!](./problems/前序/关于时间复杂度,你不知道的都在这里!.md)
* [$O(n)$的算法居然超时了此时的n究竟是多大](./problems/前序/On的算法居然超时了此时的n究竟是多大.md)
* [O(n)的算法居然超时了此时的n究竟是多大](./problems/前序/On的算法居然超时了此时的n究竟是多大.md)
* [通过一道面试题目,讲一讲递归算法的时间复杂度!](./problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md)
* [本周小结!(算法性能分析系列一)](./problems/周总结/20201210复杂度分析周末总结.md)
* [关于空间复杂度,可能有几个疑问?](./problems/前序/关于空间复杂度,可能有几个疑问?.md)
* [递归算法的时间与空间复杂度分析!](./problems/前序/递归算法的时间与空间复杂度分析.md)
* [刷了这么多题,你了解自己代码的内存消耗么?](./problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md)
## 知识星球精选
* [秋招面试,心态很重要!](./problems/知识星球精选/秋招总结3.md)
* [秋招倒霉透顶,触底反弹!](./problems/知识星球精选/秋招总结2.md)
* [无竞赛,无实习,如何秋招?](./problems/知识星球精选/秋招总结1.md)
* [offer总决赛何去何从](./problems/知识星球精选/offer总决赛何去何从.md)
* [入职后担心代码能力跟不上!](./problems/知识星球精选/入职后担心代码能力跟不上.md)
* [秋招进入offer决赛圈](./problems/知识星球精选/offer对比-决赛圈.md)
* [非科班的困扰](./problems/知识星球精选/非科班的困扰.md)
* [offer的选择-开奖](./problems/知识星球精选/秋招开奖.md)
* [看到代码就抵触!怎么办?](./problems/知识星球精选/不喜欢写代码怎么办.md)
* [遭遇逼签,怎么办?](./problems/知识星球精选/逼签.md)
* [HR特意刁难非科班](./problems/知识星球精选/HR特意刁难非科班.md)
* [offer的选择](./problems/知识星球精选/offer的选择.md)
* [天下乌鸦一般黑哪家没有PUA](./problems/知识星球精选/天下乌鸦一般黑.md)
* [初入大三考研VS工作](./problems/知识星球精选/初入大三选择考研VS工作.md)
* [非科班2021秋招总结](./problems/知识星球精选/非科班2021秋招总结.md)
* [秋招下半场依然没offer怎么办](./problems/知识星球精选/秋招下半场依然没offer.md)
* [合适自己的就是最好的](./problems/知识星球精选/合适自己的就是最好的.md)
* [为什么都说客户端会消失](./problems/知识星球精选/客三消.md)
* [博士转计算机如何找工作](./problems/知识星球精选/博士转行计算机.md)
* [不一样的七夕](./problems/知识星球精选/不一样的七夕.md)
* [HR面注意事项](./problems/知识星球精选/HR面注意事项.md)
* [刷题攻略要刷两遍!](./problems/知识星球精选/刷题攻略要刷两遍.md)
* [秋招进行中的迷茫与焦虑......](./problems/知识星球精选/秋招进行中的迷茫与焦虑.md)
* [大厂新人培养体系应该是什么样的?](./problems/知识星球精选/大厂新人培养体系.md)
* [你的简历里「专业技能」写的够专业么?](./problems/知识星球精选/专业技能可以这么写.md)
* [Carl看了上百份简历总结了这些](./problems/知识星球精选/写简历的一些问题.md)
* [备战2022届秋招](./problems/知识星球精选/备战2022届秋招.md)
* [技术不太好,如果选择方向](./problems/知识星球精选/技术不好如何选择技术方向.md)
* [刷题要不要使用库函数](./problems/知识星球精选/刷力扣用不用库函数.md)
* [关于实习的几点问题](./problems/知识星球精选/关于实习大家的疑问.md)
* [面试中遇到了发散性问题,怎么办?](./problems/知识星球精选/面试中发散性问题.md)
* [英语到底重不重要!](./problems/知识星球精选/英语到底重不重要.md)
* [计算机专业要不要读研!](./problems/知识星球精选/要不要考研.md)
* [关于提前批的一些建议](./problems/知识星球精选/关于提前批的一些建议.md)
* [已经在实习的录友要如何准备秋招](./problems/知识星球精选/如何权衡实习与秋招复习.md)
* [华为提前批已经开始了](./problems/知识星球精选/提前批已经开始了.md)
## 杂谈
* [「代码随想录」刷题网站上线](https://mp.weixin.qq.com/s/-6rd_g7LrVD1fuKBYk2tXQ)。
@ -570,7 +531,8 @@
如果是已工作,备注:姓名-城市-岗位-组队刷题。如果学生,备注:姓名-学校-年级-组队刷题。**备注没有自我介绍不通过哦**
<div align="center"><img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20220102204804.png" data-img="1" width="200" height="200"></img></div>
<div align="center"><img src="https://code-thinking-1253855093.file.myqcloud.com/pics/第二企业刷题活码.png" data-img="1" width="200" height="200"></img></div>
@ -582,6 +544,7 @@
**来看看就知道了,你会发现相见恨晚!**
<a name="公众号"></a>
<div align="center"><img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20211026122841.png" data-img="1" width="650" height="500"></img></div>

BIN
pics/.DS_Store vendored

Binary file not shown.

View File

@ -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

View File

@ -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>

View File

@ -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)
@ -282,57 +313,75 @@ func threeSum(nums []int)[][]int{
javaScript:
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
// 循环内不考虑去重
var threeSum = function(nums) {
const len = nums.length;
if(len < 3) return [];
nums.sort((a, b) => a - b);
const resSet = new Set();
for(let i = 0; i < len - 2; i++) {
if(nums[i] > 0) break;
let l = i + 1, r = len - 1;
const res = [], len = nums.length
// 将数组排序
nums.sort((a, b) => a - b)
for (let i = 0; i < len; i++) {
let l = i + 1, r = len - 1, iNum = nums[i]
// 数组排过序如果第一个数大于0直接返回res
if (iNum > 0) return res
// 去重
if (iNum == nums[i - 1]) continue
while(l < r) {
const sum = nums[i] + nums[l] + nums[r];
if(sum < 0) { l++; continue };
if(sum > 0) { r--; continue };
resSet.add(`${nums[i]},${nums[l]},${nums[r]}`);
l++;
r--;
let lNum = nums[l], rNum = nums[r], threeSum = iNum + lNum + rNum
// 三数之和小于0则左指针向右移动
if (threeSum < 0) l++
else if (threeSum > 0) r--
else {
res.push([iNum, lNum, rNum])
// 去重
while(l < r && nums[l] == nums[l + 1]){
l++
}
while(l < r && nums[r] == nums[r - 1]) {
r--
}
l++
r--
}
}
}
return Array.from(resSet).map(i => i.split(","));
};
// 去重优化
var threeSum = function(nums) {
const len = nums.length;
if(len < 3) return [];
nums.sort((a, b) => a - b);
const res = [];
for(let i = 0; i < len - 2; i++) {
if(nums[i] > 0) break;
// a去重
if(i > 0 && nums[i] === nums[i - 1]) continue;
let l = i + 1, r = len - 1;
while(l < r) {
const sum = nums[i] + nums[l] + nums[r];
if(sum < 0) { l++; continue };
if(sum > 0) { r--; continue };
res.push([nums[i], nums[l], nums[r]])
// b c 去重
while(l < r && nums[l] === nums[++l]);
while(l < r && nums[r] === nums[--r]);
}
}
return res;
return res
};
```
TypeScript:
```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 +558,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>

View File

@ -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

View File

@ -31,7 +31,7 @@
四数之和,和[15.三数之和](https://programmercarl.com/0015.三数之和.html)是一个思路,都是使用双指针法, 基本解法就是在[15.三数之和](https://programmercarl.com/0015.三数之和.html) 的基础上再套一层for循环。
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来)
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。比如:数组是`[-4, -3, -2, -1]``target`是`-10`,不能因为`-4 > -10`而跳过。但是我们依旧可以去做剪枝,逻辑变成`nums[i] > target && (nums[i] >=0 || target >= 0)`就可以了。
[15.三数之和](https://programmercarl.com/0015.三数之和.html)的双指针解法是一层for循环num[i]为确定值然后循环内有left和right下标作为双指针找到nums[i] + nums[left] + nums[right] == 0。
@ -72,15 +72,20 @@ public:
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
// 这种剪枝是错误的这道题目target 是任意值
// if (nums[k] > target) {
// return result;
// }
// 剪枝处理
if (nums[k] > target && (nums[k] >= 0 || target >= 0)) {
break; // 这里使用break统一通过最后的return返回
}
// 去重
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k + 1; i < nums.size(); i++) {
// 2级剪枝处理
if (nums[k] + nums[i] > target && (nums[k] + nums[i] >= 0 || target >= 0)) {
break;
}
// 正确去重方法
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
@ -91,9 +96,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 +221,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 +230,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])))
else:
continue
return ans
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 list(ans)
# if used list() to save results, just
# return ans
```
@ -311,7 +327,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 +461,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>

View File

@ -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]()

View File

@ -254,32 +254,20 @@ TypeScript
```typescript
function swapPairs(head: ListNode | null): ListNode | null {
/**
* 初始状态:
* curNode -> node1 -> node2 -> tmepNode
* 转换过程:
* curNode -> node2
* curNode -> node2 -> node1
* curNode -> node2 -> node1 -> tempNode
* curNode = node1
*/
let retNode: ListNode | null = new ListNode(0, head),
curNode: ListNode | null = retNode,
node1: ListNode | null = null,
node2: ListNode | null = null,
tempNode: ListNode | null = 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;
while (curNode && curNode.next && curNode.next.next) {
node1 = curNode.next;
node2 = curNode.next.next;
tempNode = node2.next;
curNode.next = node2;
node2.next = node1;
node1.next = tempNode;
curNode = node1;
}
return retNode.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:

View File

@ -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)
@ -250,10 +281,8 @@ func removeElement(_ nums: inout [Int], _ val: Int) -> Int {
for fastIndex in 0..<nums.count {
if val != nums[fastIndex] {
if slowIndex != fastIndex {
nums[slowIndex] = nums[fastIndex]
}
slowIndex += 1
slowIndex += 1
}
}
return slowIndex

View File

@ -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和jj指向前缀起始位置i指向后缀起始位置。
定义两个指针i和jj指向前缀末尾位置i指向后缀末尾位置。
然后还要对next数组进行初始化赋值如下
```
```cpp
int j = -1;
next[0] = j;
```
@ -278,8 +278,8 @@ next[i] 表示 i包括i之前最长相等的前后缀长度其实就是
所以遍历模式串s的循环下标i 要从 1开始代码如下
```
for(int i = 1; i < s.size(); i++) {
```cpp
for (int i = 1; i < s.size(); i++) {
```
如果 s[i] 与 s[j+1]不相同,也就是遇到 前后缀末尾不相同的情况,就要向前回退。
@ -292,7 +292,7 @@ next[j]就是记录着j包括j之前的子串的相同前后缀的长度
所以,处理前后缀不相同的情况代码如下:
```
```cpp
while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
    j = next[j]; // 向前回退
}
@ -300,7 +300,7 @@ while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
3. 处理前后缀相同的情况
如果s[i] 与 s[j + 1] 相同那么就同时向后移动i 和j 说明找到了相同的前后缀同时还要将j前缀的长度赋给next[i], 因为next[i]要记录相同前后缀的长度。
如果 s[i] 与 s[j + 1] 相同那么就同时向后移动i 和j 说明找到了相同的前后缀同时还要将j前缀的长度赋给next[i], 因为next[i]要记录相同前后缀的长度。
代码如下:
@ -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 版本
> 前缀表统一减一
@ -982,5 +1059,112 @@ func getNext(_ next: inout [Int], needle: [Character]) {
```
> 前缀表右移
```swift
func strStr(_ haystack: String, _ needle: String) -> Int {
let s = Array(haystack), p = Array(needle)
guard p.count != 0 else { return 0 }
var j = 0
var next = [Int].init(repeating: 0, count: p.count)
getNext(&next, p)
for i in 0 ..< s.count {
while j > 0 && s[i] != p[j] {
j = next[j]
}
if s[i] == p[j] {
j += 1
}
if j == p.count {
return i - p.count + 1
}
}
return -1
}
// 前缀表后移一位,首位用 -1 填充
func getNext(_ next: inout [Int], _ needle: [Character]) {
guard needle.count > 1 else { return }
var j = 0
next[0] = j
for i in 1 ..< needle.count-1 {
while j > 0 && needle[i] != needle[j] {
j = next[j-1]
}
if needle[i] == needle[j] {
j += 1
}
next[i] = j
}
next.removeLast()
next.insert(-1, at: 0)
}
```
> 前缀表统一不减一
```swift
func strStr(_ haystack: String, _ needle: String) -> Int {
let s = Array(haystack), p = Array(needle)
guard p.count != 0 else { return 0 }
var j = 0
var next = [Int](repeating: 0, count: needle.count)
// KMP
getNext(&next, needle: p)
for i in 0 ..< s.count {
while j > 0 && s[i] != p[j] {
j = next[j-1]
}
if s[i] == p[j] {
j += 1
}
if j == p.count {
return i - p.count + 1
}
}
return -1
}
//前缀表
func getNext(_ next: inout [Int], needle: [Character]) {
var j = 0
next[0] = j
for i in 1 ..< needle.count {
while j>0 && needle[i] != needle[j] {
j = next[j-1]
}
if needle[i] == needle[j] {
j += 1
}
next[i] = j
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -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;
}
}

View File

@ -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
@ -318,6 +318,31 @@ func searchInsert(_ nums: [Int], _ target: Int) -> Int {
```
### PHP
```php
// 二分法(1)[左闭右闭]
function searchInsert($nums, $target)
{
$n = count($nums);
$l = 0;
$r = $n - 1;
while ($l <= $r) {
$mid = floor(($l + $r) / 2);
if ($nums[$mid] > $target) {
// 下次搜索在左区间:[$l,$mid-1]
$r = $mid - 1;
} else if ($nums[$mid] < $target) {
// 下次搜索在右区间:[$mid+1,$r]
$l = $mid + 1;
} else {
// 命中返回
return $mid;
}
}
return $r + 1;
}
```
-----------------------

View File

@ -439,6 +439,55 @@ var solveSudoku = function(board) {
};
```
### TypeScript
```typescript
/**
Do not return anything, modify board in-place instead.
*/
function isValid(col: number, row: number, val: string, board: string[][]): boolean {
let n: number = board.length;
// 列向检查
for (let rowIndex = 0; rowIndex < n; rowIndex++) {
if (board[rowIndex][col] === val) return false;
}
// 横向检查
for (let colIndex = 0; colIndex < n; colIndex++) {
if (board[row][colIndex] === val) return false;
}
// 九宫格检查
const startX = Math.floor(col / 3) * 3;
const startY = Math.floor(row / 3) * 3;
for (let rowIndex = startY; rowIndex < startY + 3; rowIndex++) {
for (let colIndex = startX; colIndex < startX + 3; colIndex++) {
if (board[rowIndex][colIndex] === val) return false;
}
}
return true;
}
function solveSudoku(board: string[][]): void {
let n: number = 9;
backTracking(n, board);
function backTracking(n: number, board: string[][]): boolean {
for (let row = 0; row < n; row++) {
for (let col = 0; col < n; col++) {
if (board[row][col] === '.') {
for (let i = 1; i <= n; i++) {
if (isValid(col, row, String(i), board)) {
board[row][col] = String(i);
if (backTracking(n, board) === true) return true;
board[row][col] = '.';
}
}
return false;
}
}
}
return true;
}
};
```
### C
```C

View File

@ -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 = []
@ -370,18 +370,17 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int)
```js
var combinationSum = function(candidates, target) {
const res = [], path = [];
candidates.sort(); // 排序
candidates.sort((a,b)=>a-b); // 排序
backtracking(0, 0);
return res;
function backtracking(j, sum) {
if (sum > target) return;
if (sum === target) {
res.push(Array.from(path));
return;
}
for(let i = j; i < candidates.length; i++ ) {
const n = candidates[i];
if(n > target - sum) continue;
if(n > target - sum) break;
path.push(n);
sum += n;
backtracking(i, sum);
@ -392,7 +391,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;

View File

@ -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 = []
@ -508,22 +508,27 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int)
*/
var combinationSum2 = function(candidates, target) {
const res = []; path = [], len = candidates.length;
candidates.sort();
candidates.sort((a,b)=>a-b);
backtracking(0, 0);
return res;
function backtracking(sum, i) {
if (sum > target) return;
if (sum === target) {
res.push(Array.from(path));
return;
}
let f = -1;
for(let j = i; j < len; j++) {
const n = candidates[j];
if(n > target - sum || n === f) continue;
if(j > i && candidates[j] === candidates[j-1]){
//若当前元素和前一个元素相等
//则本次循环结束,防止出现重复组合
continue;
}
//如果当前元素值大于目标值-总和的值
//由于数组已排序,那么该元素之后的元素必定不满足条件
//直接终止当前层的递归
if(n > target - sum) break;
path.push(n);
sum += n;
f = n;
backtracking(sum, j + 1);
path.pop();
sum -= n;
@ -532,6 +537,7 @@ var combinationSum2 = function(candidates, target) {
};
```
**使用used去重**
```js
var combinationSum2 = function(candidates, target) {
let res = [];
@ -562,6 +568,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

View File

@ -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)
-----------------------

View File

@ -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:
@ -226,6 +250,27 @@ var jump = function(nums) {
};
```
### TypeScript
```typescript
function jump(nums: number[]): number {
const length: number = nums.length;
let curFarthestIndex: number = 0,
nextFarthestIndex: number = 0;
let curIndex: number = 0;
let stepNum: number = 0;
while (curIndex < length - 1) {
nextFarthestIndex = Math.max(nextFarthestIndex, curIndex + nums[curIndex]);
if (curIndex === curFarthestIndex) {
curFarthestIndex = nextFarthestIndex;
stepNum++;
}
curIndex++;
}
return stepNum;
};
```

View File

@ -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

View File

@ -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

View File

@ -129,7 +129,6 @@ for (int col = 0; col < n; col++) {
```CPP
bool isValid(int row, int col, vector<string>& chessboard, int n) {
int count = 0;
// 检查列
for (int i = 0; i < row; i++) { // 这是一个剪枝
if (chessboard[i][col] == 'Q') {
@ -178,7 +177,6 @@ void backtracking(int n, int row, vector<string>& chessboard) {
}
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
int count = 0;
// 检查列
for (int i = 0; i < row; i++) { // 这是一个剪枝
if (chessboard[i][col] == 'Q') {
@ -346,69 +344,56 @@ class Solution {
### Go
```Go
import "strings"
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
}
}
for i := 0; i < n; i++{
if board[row][i] == "Q" {
return false
}
}
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],"")
}
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] = "."
}
}
backtrack(board, 0)
var res [][]string
chessboard := make([][]string, n)
for i := 0; i < n; i++ {
chessboard[i] = make([]string, n)
}
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
chessboard[i][j] = "."
}
}
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 i := 0; i < n; i++ {
if isValid(n, row, i, chessboard) {
chessboard[row][i] = "Q"
backtrack(row + 1)
chessboard[row][i] = "."
}
}
}
backtrack(0)
return res
}
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
@ -470,6 +455,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

View File

@ -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>

View File

@ -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)
## 总结
@ -231,6 +231,96 @@ var maxSubArray = function(nums) {
```
### C:
贪心:
```c
int maxSubArray(int* nums, int numsSize){
int maxVal = INT_MIN;
int subArrSum = 0;
int i;
for(i = 0; i < numsSize; ++i) {
subArrSum += nums[i];
// 若当前局部和大于之前的最大结果,对结果进行更新
maxVal = subArrSum > maxVal ? subArrSum : maxVal;
// 若当前局部和为负对结果无益。则从nums[i+1]开始应重新计算。
subArrSum = subArrSum < 0 ? 0 : subArrSum;
}
return maxVal;
}
```
动态规划:
```c
/**
* 解题思路:动态规划:
* 1. dp数组dp[i]表示从0到i的子序列中最大序列和的值
* 2. 递推公式dp[i] = max(dp[i-1] + nums[i], nums[i])
若dp[i-1]<0对最后结果无益dp[i]则为nums[i]
* 3. dp数组初始化dp[0]的最大子数组和为nums[0]
* 4. 推导顺序:从前往后遍历
*/
#define max(a, b) (((a) > (b)) ? (a) : (b))
int maxSubArray(int* nums, int numsSize){
int dp[numsSize];
// dp[0]最大子数组和为nums[0]
dp[0] = nums[0];
// 若numsSize为1应直接返回nums[0]
int subArrSum = nums[0];
int i;
for(i = 1; i < numsSize; ++i) {
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
// 若dp[i]大于之前记录的最大值,进行更新
if(dp[i] > subArrSum)
subArrSum = dp[i];
}
return subArrSum;
}
```
### TypeScript
**贪心**
```typescript
function maxSubArray(nums: number[]): number {
let curSum: number = 0;
let resMax: number = -Infinity;
for (let i = 0, length = nums.length; i < length; i++) {
curSum += nums[i];
resMax = Math.max(curSum, resMax);
if (curSum < 0) curSum = 0;
}
return resMax;
};
```
**动态规划**
```typescript
// 动态规划
function maxSubArray(nums: number[]): number {
const length = nums.length;
if (length === 0) return 0;
const dp: number[] = [];
dp[0] = nums[0];
let resMax: number = nums[0];
for (let i = 1; i < length; i++) {
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
resMax = Math.max(resMax, dp[i]);
}
return resMax;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -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++) {

View File

@ -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>

View File

@ -155,5 +155,46 @@ var canJump = function(nums) {
```
### C
```c
#define max(a, b) (((a) > (b)) ? (a) : (b))
bool canJump(int* nums, int numsSize){
int cover = 0;
int i;
// 只可能获取cover范围中的步数所以i<=cover
for(i = 0; i <= cover; ++i) {
// 更新cover为从i出发能到达的最大值/cover的值中较大值
cover = max(i + nums[i], cover);
// 若更新后cover可以到达最后的元素返回true
if(cover >= numsSize - 1)
return true;
}
return false;
}
```
### TypeScript
```typescript
function canJump(nums: number[]): boolean {
let farthestIndex: number = 0;
let cur: number = 0;
while (cur <= farthestIndex) {
farthestIndex = Math.max(farthestIndex, cur + nums[cur]);
if (farthestIndex >= nums.length - 1) return true;
cur++;
}
return false;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -266,6 +266,26 @@ var merge = function(intervals) {
};
```
### TypeScript
```typescript
function merge(intervals: number[][]): number[][] {
const resArr: number[][] = [];
intervals.sort((a, b) => a[0] - b[0]);
resArr[0] = [...intervals[0]]; // 避免修改原intervals
for (let i = 1, length = intervals.length; i < length; i++) {
let interval: number[] = intervals[i];
let last: number[] = resArr[resArr.length - 1];
if (interval[0] <= last[1]) {
last[1] = Math.max(interval[1], last[1]);
} else {
resArr.push([...intervals[i]]);
}
}
return resArr;
};
```
-----------------------

View File

@ -10,7 +10,7 @@
[力扣题目链接](https://leetcode-cn.com/problems/spiral-matrix-ii/)
给定一个正整数 n生成一个包含 1 到 $n^2$ 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
给定一个正整数 n生成一个包含 1 到 n^2 所有元素且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
@ -30,7 +30,7 @@
相信很多同学刚开始做这种题目的时候,上来就是一波判断猛如虎。
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里里有问题,改了那里这里又跑不起来了。
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里里有问题,改了那里这里又跑不起来了。
大家还记得我们在这篇文章[数组:每次遇到二分法,都是一看就会,一写就废](https://programmercarl.com/0704.二分查找.html)中讲解了二分法,提到如果要写出正确的二分法一定要坚持**循环不变量原则**。
@ -47,7 +47,7 @@
可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是**一进循环深似海从此offer是路人**。
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开闭的原则,这样这一圈才能按照统一的规则画下来。
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开闭的原则,这样这一圈才能按照统一的规则画下来。
那么我按照左闭右开的原则,来画一圈,大家看一下:
@ -59,7 +59,7 @@
一些同学做这道题目之所以一直写不好,代码越写越乱。
就是因为在画每一条边的时候,一会左开闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
就是因为在画每一条边的时候,一会左开闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
代码如下已经详细注释了每一步的目的可以看出while循环里判断的情况是很多的代码里处理的原则也是统一的左闭右开。
@ -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
@ -262,11 +246,11 @@ var generateMatrix = function(n) {
res[row][col] = count++;
}
// 下行从右到左(左闭右开)
for (; col > startX; col--) {
for (; col > startY; col--) {
res[row][col] = count++;
}
// 左列做下到上(左闭右开)
for (; row > startY; row--) {
for (; row > startX; row--) {
res[row][col] = count++;
}

View File

@ -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)
**计算组合问题的代码还是有难度的,特别是处理溢出的情况!**

View File

@ -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,23 +22,22 @@
![](https://img-blog.csdnimg.cn/20210111204939971.png)
输入obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出2
* 输入obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
* 输出2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
* 3x3 网格的正中间有一个障碍物。
* 从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
示例 2
![](https://img-blog.csdnimg.cn/20210111205857918.png)
输入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,54 +294,41 @@ class Solution:
```
Go
### Go
```go
func uniquePathsWithObstacles(obstacleGrid [][]int) int {
m,n:= len(obstacleGrid),len(obstacleGrid[0])
m, n := len(obstacleGrid), len(obstacleGrid[0])
// 定义一个dp数组
dp := make([][]int,m)
for i,_ := range dp {
dp[i] = make([]int,n)
dp := make([][]int, m)
for i, _ := range dp {
dp[i] = make([]int, n)
}
// 初始化
for i:=0;i<m;i++ {
// 如果是障碍物, 后面的就都是0, 不用循环了
if obstacleGrid[i][0] == 1 {
break
}
dp[i][0]=1
// 初始化, 如果是障碍物, 后面的就都是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
}
dp[0][i]=1
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
if obstacleGrid[i][j] != 1 {
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
// 如果obstacleGrid[i][j]这个点是障碍物, 那么dp[i][j]保持为0
if obstacleGrid[i][j] != 1 {
// 否则我们需要计算当前点可以到达的路径数
dp[i][j] = dp[i-1][j]+dp[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
```Javascript
var uniquePathsWithObstacles = function(obstacleGrid) {
const m = obstacleGrid.length
const n = obstacleGrid[0].length

View File

@ -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 = [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[i] 为第 i 阶楼梯有多少种方法爬到楼顶
dp=[0]*(n+1)
dp[0]=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

View File

@ -143,10 +143,10 @@ class Solution {
}
```
Python
Python3
```python3
```python
class Solution:
def climbStairs(self, n: int) -> int:
dp = [0]*(n + 1)

View File

@ -423,7 +423,7 @@ class Solution:
if len(path) == k:
res.append(path[:])
return
for i in range(startIndex,n - (k - len(path)) + 2): #优化的地方
for i in range(startIndex,n-(k-len(path))+2): #优化的地方
path.append(i) #处理节点
backtrack(n,k,i+1) #递归
path.pop() #回溯,撤销处理的节点
@ -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

View File

@ -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;

View File

@ -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;

View File

@ -277,9 +277,9 @@ class Solution {
}
```
Python:
Python3:
```python3
```python
# 双指针暴力解法leetcode超时
class Solution:

View File

@ -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]] {

View File

@ -227,7 +227,7 @@ private:
public:
vector<string> restoreIpAddresses(string s) {
result.clear();
if (s.size() > 12) return result; // 算是剪枝了
if (s.size() < 4 || s.size() > 12) return result; // 算是剪枝了
backtracking(s, 0, 0);
return result;
}
@ -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截取出来不是数字

View File

@ -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>

View File

@ -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);
};
```
-----------------------

View File

@ -574,6 +574,75 @@ 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:
> 递归

View File

@ -83,10 +83,10 @@ public:
};
```
python代码
python3代码:
```python3
```python
class Solution:
"""二叉树层序遍历迭代解法"""
@ -246,31 +246,56 @@ var levelOrder = function(root) {
```
Swift:
```swift
func levelOrder(_ 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
var sub = [Int]()
for _ in 0 ..< size {
let node = queue.removeFirst()
sub.append(node.val)
if let left = node.left {
queue.append(left)
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 let right = node.right {
queue.append(right)
if (curNode.right !== null) {
helperQueue.push(curNode.right);
}
}
res.append(sub)
res.push(tempArr);
tempArr = [];
}
return res
return res;
};
```
Swift
```swift
func levelOrder(_ root: TreeNode?) -> [[Int]] {
var result = [[Int]]()
guard let root = root else { return result }
// 表示一层
var queue = [root]
while !queue.isEmpty {
let count = queue.count
var subarray = [Int]()
for _ in 0 ..< count {
// 当前层
let node = queue.removeFirst()
subarray.append(node.val)
// 下一层
if let node = node.left { queue.append(node) }
if let node = node.right { queue.append(node) }
}
result.append(subarray)
}
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)
}
if let right = node.right {
queue.append(right)
}
subarray.append(node.val)
// 下一层
if let node = node.left { queue.append(node) }
if let node = node.right { queue.append(node)}
}
res.insert(sub, at: 0)
result.append(subarray)
}
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 right = node.right {
queue.append(right)
}
// 下一层
if let node = node.left { queue.append(node) }
if let node = node.right { queue.append(node) }
}
res.append(Double(sum) / Double(size))
result.append(Double(sum) / Double(count))
}
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) }
}
res.append(sub)
result.append(subarray)
}
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);
}
retVal.add(Collections.max(lvlVals));
}
return retVal;
if(root == null){
return Collections.emptyList();
}
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);
}
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 let left = node.left {
queue.append(left)
}
if let right = node.right {
queue.append(right)
}
if node.val > max { max = node.val }
// 下一层
if let node = node.left { queue.append(node) }
if let node = node.right { queue.append(node) }
}
res.append(max)
result.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) {
};
```
Swift
```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)
}
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 res
return resMin;
};
```
Swift
```swift
func minDepth(_ root: TreeNode?) -> Int {
guard root != nil else { return 0 }
var depth = 0
var queue = [root!]
while !queue.isEmpty {
let count = queue.count
depth += 1
for _ in 0 ..< count {
// 当前层
let node = queue.removeFirst()
if node.left == nil, node.right == nil { // 遇到叶子结点则返回
return depth
}
// 下一层
if let node = node.left { queue.append(node) }
if let node = node.right { queue.append(node) }
}
}
return depth
}
```

View File

@ -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){

View File

@ -89,9 +89,9 @@ TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
**难点大家应该发现了,就是如何切割,以及边界值找不好很容易乱套。**
此时应该注意确定切割的标准,是左闭右开,还有左开又闭,还是左闭又闭,这个就是不变量,要在递归中保持这个不变量。
此时应该注意确定切割的标准,是左闭右开,还有左开右闭,还是左闭右闭,这个就是不变量,要在递归中保持这个不变量。
**在切割的过程中会产生四个区间,把握不好不变量的话,一会左闭右开,一会左闭闭,必然乱套!**
**在切割的过程中会产生四个区间,把握不好不变量的话,一会左闭右开,一会左闭闭,必然乱套!**
我在[数组:每次遇到二分法,都是一看就会,一写就废](https://programmercarl.com/0035.搜索插入位置.html)和[数组:这个循环可以转懵很多人!](https://programmercarl.com/0059.螺旋矩阵II.html)中都强调过循环不变量的重要性,在二分查找以及螺旋矩阵的求解中,坚持循环不变量非常重要,本题也是。
@ -103,7 +103,7 @@ TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割,如下代码中我坚持左闭右开的原则:
```C++
```CPP
// 找到中序遍历的切割点
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
@ -130,7 +130,7 @@ vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );
代码如下:
```
```CPP
// postorder 舍弃末尾元素,因为这个元素就是中间节点,已经用过了
postorder.resize(postorder.size() - 1);
@ -144,7 +144,7 @@ vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end
接下来可以递归了,代码如下:
```
```CPP
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
```
@ -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

View File

@ -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) {

View File

@ -264,7 +264,7 @@ int getDepth(TreeNode* cur) {
}
```
然后再用栈来模拟序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:
然后再用栈来模拟序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:
```CPP
bool isBalanced(TreeNode* root) {
@ -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>

View File

@ -404,6 +404,44 @@ 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
> 递归

View File

@ -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:
result.append(path[:])
return
if not cur_node.left and not cur_node.right: return
if not cur_node.left and not cur_node.right:
if remain == 0:
result.append(path[:])
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 //找没找到的标志
if root==nil{
return flage
func hasPathSum(root *TreeNode, targetSum int) bool {
if root == nil {
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{
return result
}
var sumnodes []int//经过路径的节点集合
haspathsum(root,&sumnodes,targetsum,&result)
func pathSum(root *TreeNode, targetSum int) [][]int {
result := make([][]int, 0)
traverse(root, &result, new([]int), targetSum)
return result
}
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
}
tempnodes:=make([]int,number+1)//新的nodes接受指针里的值防止最终指针里的值发生变动导致最后的结果都是最后一个sumnodes的值
for k,v:=range *sumnodes{
tempnodes[k]=v
}
if sum==targetsum{
*result=append(*result,tempnodes)
}
func traverse(node *TreeNode, result *[][]int, currPath *[]int, targetSum int) {
if node == nil { // 这个判空也可以挪到递归遍历左右子树时去判断
return
}
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]//回溯
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
}
*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
}
```

View File

@ -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

View File

@ -311,7 +311,36 @@ class Solution:
```
Go:
> 贪心法:
```Go
func maxProfit(prices []int) int {
low := math.MaxInt32
rlt := 0
for i := range prices{
low = min(low, prices[i])
rlt = max(rlt, prices[i]-low)
}
return rlt
}
func min(a, b int) int {
if a < b{
return a
}
return b
}
func max(a, b int) int {
if a > b{
return a
}
return b
}
```
> 动态规划:版本一
```Go
func maxProfit(prices []int) int {
length:=len(prices)
@ -338,6 +367,29 @@ func max(a,b int)int {
}
```
> 动态规划:版本二
```Go
func maxProfit(prices []int) int {
dp := [2][2]int{}
dp[0][0] = -prices[0]
dp[0][1] = 0
for i := 1; i < len(prices); i++{
dp[i%2][0] = max(dp[(i-1)%2][0], -prices[i])
dp[i%2][1] = max(dp[(i-1)%2][1], dp[(i-1)%2][0]+prices[i])
}
return dp[(len(prices)-1)%2][1]
}
func max(a, b int) int {
if a > b{
return a
}
return b
}
```
JavaScript:
> 动态规划

View File

@ -40,7 +40,7 @@
本题首先要清楚两点:
* 只有一只股票!
* 当前只有买股票或者股票的操作
* 当前只有买股票或者股票的操作
想获得利润至少要两天为一个交易单元。
@ -264,12 +264,24 @@ const maxProfit = (prices) => {
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]);
}
return dp[prices.length -1][0];
return dp[prices.length -1][1];
};
```
TypeScript
```typescript
function maxProfit(prices: number[]): number {
let resProfit: number = 0;
for (let i = 1, length = prices.length; i < length; i++) {
resProfit += Math.max(prices[i] - prices[i - 1], 0);
}
return resProfit;
};
```
C:
贪心:
```c
int maxProfit(int* prices, int pricesSize){
int result = 0;
@ -284,5 +296,27 @@ int maxProfit(int* prices, int pricesSize){
}
```
动态规划:
```c
#define max(a, b) (((a) > (b)) ? (a) : (b))
int maxProfit(int* prices, int pricesSize){
int dp[pricesSize][2];
dp[0][0] = 0 - prices[0];
dp[0][1] = 0;
int i;
for(i = 1; i < pricesSize; ++i) {
// dp[i][0]为i-1天持股的钱数/在第i天用i-1天的钱买入的最大值。
// 若i-1天持股且第i天买入股票比i-1天持股时更亏说明应在i-1天时持股
dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]);
//dp[i][1]为i-1天不持股钱数/在第i天卖出所持股票dp[i-1][0] + prices[i]的最大值
dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]);
}
// 返回在最后一天不持股时的钱数(将股票卖出后钱最大化)
return dp[pricesSize - 1][1];
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -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)
@ -276,7 +276,7 @@ const maxProfit = (prices) => {
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i]);
}
return dp[prices.length -1][0];
return dp[prices.length -1][1];
};
// 方法二:动态规划(滚动数组)

View File

@ -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]。 但结果也是对的。

View File

@ -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

View File

@ -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>

View File

@ -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,43 @@ var partition = function(s) {
};
```
## TypeScript
```typescript
function partition(s: string): string[][] {
const res: string[][] = []
const path: string[] = []
const isHuiwen = (
str: string,
startIndex: number,
endIndex: number
): boolean => {
for (; startIndex < endIndex; startIndex++, endIndex--) {
if (str[startIndex] !== str[endIndex]) {
return false
}
}
return true
}
const rec = (str: string, index: number): void => {
if (index >= str.length) {
res.push([...path])
return
}
for (let i = index; i < str.length; i++) {
if (!isHuiwen(str, index, i)) {
continue
}
path.push(str.substring(index, i + 1))
rec(str, i + 1)
path.pop()
}
}
rec(s, 0)
return res
};
```
## C
```c

View File

@ -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
@ -340,7 +364,53 @@ var canCompleteCircuit = function(gas, cost) {
};
```
### TypeScript
**暴力法:**
```typescript
function canCompleteCircuit(gas: number[], cost: number[]): number {
for (let i = 0, length = gas.length; i < length; i++) {
let curSum: number = 0;
let index: number = i;
while (curSum >= 0 && index < i + length) {
let tempIndex: number = index % length;
curSum += gas[tempIndex] - cost[tempIndex];
index++;
}
if (index === i + length && curSum >= 0) return i;
}
return -1;
};
```
**解法二:**
```typescript
function canCompleteCircuit(gas: number[], cost: number[]): number {
let total: number = 0;
let curGas: number = 0;
let tempDiff: number = 0;
let resIndex: number = 0;
for (let i = 0, length = gas.length; i < length; i++) {
tempDiff = gas[i] - cost[i];
total += tempDiff;
curGas += tempDiff;
if (curGas < 0) {
resIndex = i + 1;
curGas = 0;
}
}
if (total < 0) return -1;
return resIndex;
};
```
### C
贪心算法:方法一
```c
int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
int curSum = 0;
@ -370,5 +440,36 @@ int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
}
```
贪心算法:方法二
```c
int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
int curSum = 0;
int totalSum = 0;
int start = 0;
int i;
for(i = 0; i < gasSize; ++i) {
// 当前i站中加油量与耗油量的差
int diff = gas[i] - cost[i];
curSum += diff;
totalSum += diff;
// 若0到i的加油量都为负则开始位置应为i+1
if(curSum < 0) {
curSum = 0;
// 当i + 1 == gasSize时totalSum < 0此时i为gasSize - 1)油车不可能返回原点
start = i + 1;
}
}
// 若总和小于0加油车无论如何都无法返回原点。返回-1
if(totalSum < 0)
return -1;
return start;
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -239,5 +239,73 @@ var candy = function(ratings) {
```
### C
```c
#define max(a, b) (((a) > (b)) ? (a) : (b))
int *initCandyArr(int size) {
int *candyArr = (int*)malloc(sizeof(int) * size);
int i;
for(i = 0; i < size; ++i)
candyArr[i] = 1;
return candyArr;
}
int candy(int* ratings, int ratingsSize){
// 初始化数组,每个小孩开始至少有一颗糖
int *candyArr = initCandyArr(ratingsSize);
int i;
// 先判断右边是否比左边评分高。若是,右边孩子的糖果为左边孩子+1candyArr[i] = candyArr[i - 1] + 1)
for(i = 1; i < ratingsSize; ++i) {
if(ratings[i] > ratings[i - 1])
candyArr[i] = candyArr[i - 1] + 1;
}
// 再判断左边评分是否比右边高。
// 若是,左边孩子糖果为右边孩子糖果+1/自己所持糖果最大值。(若糖果已经比右孩子+1多则不需要更多糖果
// 举例ratings为[1, 2, 3, 1]。此时评分为3的孩子在判断右边比左边大后为3虽然它比最末尾的1(ratings[3])大但是candyArr[3]为1。所以不必更新candyArr[2]
for(i = ratingsSize - 2; i >= 0; --i) {
if(ratings[i] > ratings[i + 1])
candyArr[i] = max(candyArr[i], candyArr[i + 1] + 1);
}
// 求出糖果之和
int result = 0;
for(i = 0; i < ratingsSize; ++i) {
result += candyArr[i];
}
return result;
}
```
### TypeScript
```typescript
function candy(ratings: number[]): number {
const candies: number[] = [];
candies[0] = 1;
// 保证右边高分孩子一定比左边低分孩子发更多的糖果
for (let i = 1, length = ratings.length; i < length; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
} else {
candies[i] = 1;
}
}
// 保证左边高分孩子一定比右边低分孩子发更多的糖果
for (let i = ratings.length - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] = Math.max(candies[i], candies[i + 1] + 1);
}
}
return candies.reduce((pre, cur) => pre + cur);
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -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

View File

@ -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

View File

@ -336,7 +336,33 @@ class Solution:
return pre
```
### Go
```go
# 方法三 分割链表
func reorderList(head *ListNode) {
var slow=head
var fast=head
for fast!=nil&&fast.Next!=nil{
slow=slow.Next
fast=fast.Next.Next
} //双指针将链表分为左右两部分
var right =new(ListNode)
for slow!=nil{
temp:=slow.Next
slow.Next=right.Next
right.Next=slow
slow=temp
} //翻转链表右半部分
right=right.Next //right为反转后得右半部分
h:=head
for right.Next!=nil{
temp:=right.Next
right.Next=h.Next
h.Next=right
h=h.Next.Next
right=temp
} //将左右两部分重新组合
}
```
### JavaScript
```javascript
@ -439,7 +465,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;
}
```
-----------------------

View File

@ -109,7 +109,7 @@ public:
};
```
# 题外话
## 题外话
我们习惯看到的表达式都是中缀表达式,因为符合我们的习惯,但是中缀表达式对于计算机来说就不是很友好了。
@ -128,7 +128,7 @@ public:
# 其他语言版本
## 其他语言版本
java:
@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -145,6 +145,38 @@ public:
## 其他语言版本
C:
用原来的链表操作:
```c
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* temp;
// 当头结点存在并且头结点的值等于val时
while(head && head->val == val) {
temp = head;
// 将新的头结点设置为head->next并删除原来的头结点
head = head->next;
free(temp);
}
struct ListNode *cur = head;
// 当cur存在并且cur->next存在时
// 此解法需要判断cur存在因为cur指向head。若head本身为NULL或者原链表中元素都为val的话cur也会为NULL
while(cur && (temp = cur->next)) {
// 若cur->next的值等于val
if(temp->val == val) {
// 将cur->next设置为cur->next->next并删除cur->next
cur->next = temp->next;
free(temp);
}
// 若cur->next不等于val则将cur后移一位
else
cur = cur->next;
}
// 返回头结点
return head;
}
```
设置一个虚拟头结点:
```c
/**
* Definition for singly-linked list.
@ -324,7 +356,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 +374,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;
}

View File

@ -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:
![leetcode_209](https://img-blog.csdnimg.cn/20210312160441942.png)
可以发现**滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将$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,76 @@ 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;
}
```
Kotlin:
```kotlin
class Solution {
fun minSubArrayLen(target: Int, nums: IntArray): Int {
var start = 0
var end = 0
var ret = Int.MAX_VALUE
var count = 0
while (end < nums.size) {
count += nums[end]
while (count >= target) {
ret = if (ret > (end - start + 1)) end - start + 1 else ret
count -= nums[start++]
}
end++
}
return if (ret == Int.MAX_VALUE) 0 else ret
}
}
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -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:
return nums[0]
result1 = self.robRange(nums, 0, n - 2)
result2 = self.robRange(nums, 1, n - 1)
return max(result1 , result2)
#在198入门级的打家劫舍问题上分两种情况考虑
#一是不偷第一间房,二是不偷最后一间房
if len(nums)==1:#题目中提示nums.length>=1,所以不需要考虑len(nums)==0的情况
return nums[0]
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:

View File

@ -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

View File

@ -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) {

View File

@ -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 {

View File

@ -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>

View File

@ -21,7 +21,7 @@ empty() -- 返回队列是否为空。
示例:
```
```cpp
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
@ -275,15 +275,11 @@ func (this *MyQueue) Pop() int {
/** Get the front element. */
func (this *MyQueue) Peek() int {
for len(this.stack) != 0 {
val := this.stack[len(this.stack)-1]
this.stack = this.stack[:len(this.stack)-1]
this.back = append(this.back, val)
}
if len(this.back) == 0 {
val := this.Pop()
if val == 0 {
return 0
}
val := this.back[len(this.back)-1]
this.back = append(this.back, val)
return val
}
@ -348,7 +344,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 {

View File

@ -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>

View File

@ -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;
};
```
-----------------------

View File

@ -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 = [];
}
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];
}
this.queue.push(value);
}
dequeue(value) {
let front = this.front();
if (front === value) {
this.queue.shift();
}
}
front() {
return this.queue[0];
}
}
// 入队当前元素下标
q.push(i);
// 判断当前最大值(即队首元素)是否在窗口中,若不在便将其出队
if (q[0] <= i - k) {
q.shift();
let helperQueue = new MonoQueue();
let i = 0, j = 0;
let resArr = [];
while (j < k) {
helperQueue.enqueue(nums[j++]);
}
// 当达到窗口大小时便开始向结果中添加数据
if (i >= k - 1) ans.push(nums[q[0]]);
}
return ans;
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>

View File

@ -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++ 代码如下:

View File

@ -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>

View File

@ -207,7 +207,7 @@ class Solution {
Python
```python3
```python
class Solution:
def numSquares(self, n: int) -> int:
'''版本一,先遍历背包, 再遍历物品'''

View File

@ -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就可以了**。

View File

@ -37,7 +37,7 @@
1. dp[i]的定义
**dp[i]表示i之前包括i的最长上升子序列的长度**
**dp[i]表示i之前包括i的以nums[i]结尾最长上升子序列的长度**
2. 状态转移方程
@ -168,6 +168,39 @@ func lengthOfLIS(nums []int ) int {
}
```
```go
// 动态规划求解
func lengthOfLIS(nums []int) int {
// dp数组的定义 dp[i]表示取第i个元素的时候表示子序列的长度其中包括 nums[i] 这个元素
dp := make([]int, len(nums))
// 初始化所有的元素都应该初始化为1
for i := range dp {
dp[i] = 1
}
ans := dp[0]
for i := 1; i < len(nums); i++ {
for j := 0; j < i; j++ {
if nums[i] > nums[j] {
dp[i] = max(dp[i], dp[j] + 1)
}
}
if dp[i] > ans {
ans = dp[i]
}
}
return ans
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
```
Javascript
```javascript
const lengthOfLIS = (nums) => {

View File

@ -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>

View File

@ -207,7 +207,7 @@ class Solution {
Python
```python3
```python
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
'''版本一'''

View File

@ -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>

View File

@ -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)

View File

@ -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拆分结果的最大乘积
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相乘
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
//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-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>

View File

@ -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>

View File

@ -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]);
};
```
-----------------------

View File

@ -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

View File

@ -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长度
@ -298,5 +298,84 @@ var wiggleMaxLength = function(nums) {
};
```
### C
**贪心**
```c
int wiggleMaxLength(int* nums, int numsSize){
if(numsSize <= 1)
return numsSize;
int length = 1;
int preDiff , curDiff;
preDiff = curDiff = 0;
for(int i = 0; i < numsSize - 1; ++i) {
// 计算当前i元素与i+1元素差值
curDiff = nums[i+1] - nums[i];
// 若preDiff与curDiff符号不符则子序列长度+1。更新preDiff的符号
// 若preDiff与curDiff符号一致当前i元素为连续升序/连续降序子序列的中间元素。不被记录入长度
// 注当preDiff为0时curDiff为正或为负都属于符号不同
if((curDiff > 0 && preDiff <= 0) || (preDiff >= 0 && curDiff < 0)) {
preDiff = curDiff;
length++;
}
}
return length;
}
```
### TypeScript
**贪心**
```typescript
function wiggleMaxLength(nums: number[]): number {
let length: number = nums.length;
if (length <= 1) return length;
let preDiff: number = 0;
let curDiff: number = 0;
let count: number = 1;
for (let i = 1; i < length; i++) {
curDiff = nums[i] - nums[i - 1];
if (
(preDiff <= 0 && curDiff > 0) ||
(preDiff >= 0 && curDiff < 0)
) {
preDiff = curDiff;
count++;
}
}
return count;
};
```
**动态规划**
```typescript
function wiggleMaxLength(nums: number[]): number {
const length: number = nums.length;
if (length <= 1) return length;
const dp: number[][] = new Array(length).fill(0).map(_ => []);
dp[0][0] = 1; // 第一个数作为波峰
dp[0][1] = 1; // 第一个数作为波谷
for (let i = 1; i < length; i++) {
dp[i][0] = 1;
dp[i][1] = 1;
for (let j = 0; j < i; j++) {
if (nums[j] < nums[i]) dp[i][0] = Math.max(dp[i][0], dp[j][1] + 1);
}
for (let j = 0; j < i; j++) {
if (nums[j] > nums[i]) dp[i][1] = Math.max(dp[i][1], dp[j][0] + 1);
}
}
return Math.max(dp[length - 1][0], dp[length - 1][1]);
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -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后台对不同语言的测试数据不一样。

View File

@ -84,6 +84,10 @@ class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0};
//add
if (ransomNote.size() > magazine.size()) {
return false;
}
for (int i = 0; i < magazine.length(); i++) {
// 通过recode数据记录 magazine里各个字符出现次数
record[magazine[i]-'a'] ++;
@ -209,7 +213,7 @@ class Solution(object):
Python写法四
```python3
```python
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
c1 = collections.Counter(ransomNote)
@ -264,6 +268,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

View File

@ -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]就够了,但一起初始化也方便,所以就一起操作了**,代码如下:

View File

@ -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,8 +372,149 @@ 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
}
```
## C
递归法:
```c
int sumOfLeftLeaves(struct TreeNode* root){
// 递归结束条件若当前结点为空返回0
if(!root)
return 0;
// 递归取左子树的左结点和和右子树的左结点和
int leftValue = sumOfLeftLeaves(root->left);
int rightValue = sumOfLeftLeaves(root->right);
// 若当前结点的左结点存在,且其为叶子结点。取它的值
int midValue = 0;
if(root->left && (!root->left->left && !root->left->right))
midValue = root->left->val;
return leftValue + rightValue + midValue;
}
```
迭代法:
```c
int sumOfLeftLeaves(struct TreeNode* root){
struct TreeNode* stack[1000];
int stackTop = 0;
// 若传入root结点不为空将其入栈
if(root)
stack[stackTop++] = root;
int sum = 0;
//若栈不为空,进行循环
while(stackTop) {
// 出栈栈顶元素
struct TreeNode *topNode = stack[--stackTop];
// 若栈顶元素的左孩子为左叶子结点将其值加入sum中
if(topNode->left && (!topNode->left->left && !topNode->left->right))
sum += topNode->left->val;
// 若当前栈顶结点有左右孩子。将他们加入栈中进行遍历
if(topNode->right)
stack[stackTop++] = topNode->right;
if(topNode->left)
stack[stackTop++] = topNode->left;
}
return sum;
}
```
-----------------------

View File

@ -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)
大家可以把两个版本的代码提交一下试试,就可以发现其差别了!
@ -291,5 +291,72 @@ var reconstructQueue = function(people) {
```
### C
```c
int cmp(const void *p1, const void *p2) {
int *pp1 = *(int**)p1;
int *pp2 = *(int**)p2;
// 若身高相同则按照k从小到大排列
// 若身高不同,按身高从大到小排列
return pp1[0] == pp2[0] ? pp1[1] - pp2[1] : pp2[0] - pp1[0];
}
// 将start与end中间的元素都后移一位
// start为将要新插入元素的位置
void moveBack(int **people, int peopleSize, int start, int end) {
int i;
for(i = end; i > start; i--) {
people[i] = people[i-1];
}
}
int** reconstructQueue(int** people, int peopleSize, int* peopleColSize, int* returnSize, int** returnColumnSizes){
int i;
// 将people按身高从大到小排列若身高相同按k从小到大排列
qsort(people, peopleSize, sizeof(int*), cmp);
for(i = 0; i < peopleSize; ++i) {
// people[i]要插入的位置
int position = people[i][1];
int *temp = people[i];
// 将position到i中间的元素后移一位
// 注因为已经排好序position不会比i大。(举例排序后people最后一位元素最小其可能的k最大值为peopleSize-2小于此时的i)
moveBack(people, peopleSize, position, i);
// 将temp放置到position处
people[position] = temp;
}
// 设置返回二维数组的大小以及里面每个一维数组的长度
*returnSize = peopleSize;
*returnColumnSizes = (int*)malloc(sizeof(int) * peopleSize);
for(i = 0; i < peopleSize; ++i) {
(*returnColumnSizes)[i] = 2;
}
return people;
}
```
### TypeScript
```typescript
function reconstructQueue(people: number[][]): number[][] {
people.sort((a, b) => {
if (a[0] === b[0]) return a[1] - b[1];
return b[0] - a[0];
});
const resArr: number[][] = [];
for (let i = 0, length = people.length; i < length; i++) {
resArr.splice(people[i][1], 0, people[i]);
}
return resArr;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -50,7 +50,7 @@
## 01背包问题
背包问题大家都知道有N件物品和一个最多能重量为W 的背包。第i件物品的重量是weight[i]得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
背包问题大家都知道有N件物品和一个最多能重量为W 的背包。第i件物品的重量是weight[i]得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
**背包问题有多种背包方式常见的有01背包、完全背包、多重背包、分组背包和混合背包等等。**
@ -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

View File

@ -92,8 +92,8 @@ public:
}
};
```
* 时间复杂度:$O(n\log n)$ ,有一个快排
* 空间复杂度:$O(1)$
* 时间复杂度O(nlog n) ,有一个快排
* 空间复杂度O(1)
大家此时会发现如此复杂的一个问题,代码实现却这么简单!
@ -183,28 +183,23 @@ 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) -> {
// 按照区间右边界升序排序
return a[1] - b[1];
});
int count = 1;
int edge = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (edge <= intervals[i][0]){
count ++; //non overlap + 1
int count = 0;
int edge = Integer.MIN_VALUE;
for (int i = 0; i < intervals.length; i++) {
// 若上一个区间的右边界小于当前区间的左边界,说明无交集
if (edge <= intervals[i][0]) {
edge = intervals[i][1];
} else {
count++;
}
}
return intervals.length - count;
return count;
}
}
```
@ -312,6 +307,55 @@ var eraseOverlapIntervals = function(intervals) {
}
```
### TypeScript
> 按右边界排序,从左往右遍历
```typescript
function eraseOverlapIntervals(intervals: number[][]): number {
const length = intervals.length;
if (length === 0) return 0;
intervals.sort((a, b) => a[1] - b[1]);
let right: number = intervals[0][1];
let count: number = 1;
for (let i = 1; i < length; i++) {
if (intervals[i][0] >= right) {
count++;
right = intervals[i][1];
}
}
return length - count;
};
```
> 按左边界排序,从左往右遍历
```typescript
function eraseOverlapIntervals(intervals: number[][]): number {
if (intervals.length === 0) return 0;
intervals.sort((a, b) => a[0] - b[0]);
let right: number = intervals[0][1];
let tempInterval: number[];
let resCount: number = 0;
for (let i = 1, length = intervals.length; i < length; i++) {
tempInterval = intervals[i];
if (tempInterval[0] >= right) {
// 未重叠
right = tempInterval[1];
} else {
// 有重叠移除当前interval和前一个interval中右边界更大的那个
right = Math.min(right, tempInterval[1]);
resCount++;
}
}
return resCount;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

View File

@ -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;
};
```
-----------------------

View File

@ -214,7 +214,31 @@ var findMinArrowShots = function(points) {
};
```
### TypeScript
```typescript
function findMinArrowShots(points: number[][]): number {
const length: number = points.length;
if (length === 0) return 0;
points.sort((a, b) => a[0] - b[0]);
let resCount: number = 1;
let right: number = points[0][1]; // 右边界
let tempPoint: number[];
for (let i = 1; i < length; i++) {
tempPoint = points[i];
if (tempPoint[0] > right) {
resCount++;
right = tempPoint[1];
} else {
right = Math.min(right, tempPoint[1]);
}
}
return resCount;
};
```
### C
```c
int cmp(const void *a,const void *b)
{

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