mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-15 07:20:40 +08:00
Merge branch 'master' of https://github.com/youngyangyang04/leetcode-master into youngyangyang04-master
This commit is contained in:
@ -9,7 +9,7 @@
|
||||
|
||||
## 1. 两数之和
|
||||
|
||||
https://leetcode-cn.com/problems/two-sum/
|
||||
[力扣题目链接](https://leetcode-cn.com/problems/two-sum/)
|
||||
|
||||
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
|
||||
|
||||
@ -29,10 +29,10 @@ https://leetcode-cn.com/problems/two-sum/
|
||||
很明显暴力的解法是两层for循环查找,时间复杂度是O(n^2)。
|
||||
|
||||
建议大家做这道题目之前,先做一下这两道
|
||||
* [242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)
|
||||
* [349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)
|
||||
* [242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html)
|
||||
* [349. 两个数组的交集](https://www.programmercarl.com/0349.两个数组的交集.html)
|
||||
|
||||
[242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://mp.weixin.qq.com/s/aMSA5zrp3jJcLjuSB0Es2Q)这道题目是通过set作为哈希表来解决哈希问题。
|
||||
[242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://www.programmercarl.com/0349.两个数组的交集.html)这道题目是通过set作为哈希表来解决哈希问题。
|
||||
|
||||
本题呢,则要使用map,那么来看一下使用数组和set来做哈希法的局限。
|
||||
|
||||
@ -51,7 +51,7 @@ C++中map,有三种类型:
|
||||
|
||||
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。
|
||||
|
||||
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)。
|
||||
同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://www.programmercarl.com/哈希表理论基础.html)。
|
||||
|
||||
**这道题目中并不需要key有序,选择std::unordered_map 效率更高!**
|
||||
|
||||
@ -62,7 +62,7 @@ std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底
|
||||
|
||||
C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<int> twoSum(vector<int>& nums, int target) {
|
||||
@ -110,13 +110,14 @@ Python:
|
||||
```python
|
||||
class Solution:
|
||||
def twoSum(self, nums: List[int], target: int) -> List[int]:
|
||||
hashmap={}
|
||||
for ind,num in enumerate(nums):
|
||||
hashmap[num] = ind
|
||||
for i,num in enumerate(nums):
|
||||
j = hashmap.get(target - num)
|
||||
if j is not None and i!=j:
|
||||
return [i,j]
|
||||
records = dict()
|
||||
|
||||
# 用枚举更方便,就不需要通过索引再去取当前位置的值
|
||||
for idx, val in enumerate(nums):
|
||||
if target - val not in records:
|
||||
records[val] = idx
|
||||
else:
|
||||
return [records[target - val], idx] # 如果存在就返回字典记录索引和当前索引
|
||||
```
|
||||
|
||||
|
||||
@ -210,4 +211,4 @@ function twoSum(array $nums, int $target): array
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -67,7 +67,7 @@
|
||||
|
||||
以上三种情况分析完了,那么递归公式如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (s[i] == s[j]) {
|
||||
if (j - i <= 1) { // 情况一 和 情况二
|
||||
dp[i][j] = true;
|
||||
@ -81,7 +81,7 @@ if (s[i] == s[j]) {
|
||||
|
||||
在得到[i,j]区间是否是回文子串的时候,直接保存最长回文子串的左边界和右边界,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (s[i] == s[j]) {
|
||||
if (j - i <= 1) { // 情况一 和 情况二
|
||||
dp[i][j] = true;
|
||||
@ -120,7 +120,7 @@ dp[i + 1][j - 1] 在 dp[i][j]的左下角,如图:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序
|
||||
for (int j = i; j < s.size(); j++) {
|
||||
if (s[i] == s[j]) {
|
||||
@ -150,7 +150,7 @@ for (int i = s.size() - 1; i >= 0; i--) { // 注意遍历顺序
|
||||
|
||||
以上分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
string longestPalindrome(string s) {
|
||||
@ -181,7 +181,7 @@ public:
|
||||
```
|
||||
以上代码是为了凸显情况一二三,当然是可以简洁一下的,如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
string longestPalindrome(string s) {
|
||||
@ -226,7 +226,7 @@ public:
|
||||
|
||||
**这两种情况可以放在一起计算,但分别计算思路更清晰,我倾向于分别计算**,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int left = 0;
|
||||
@ -270,6 +270,23 @@ public:
|
||||
## Python
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def longestPalindrome(self, s: str) -> str:
|
||||
dp = [[False] * len(s) for _ in range(len(s))]
|
||||
maxlenth = 0
|
||||
left = 0
|
||||
right = 0
|
||||
for i in range(len(s) - 1, -1, -1):
|
||||
for j in range(i, len(s)):
|
||||
if s[j] == s[i]:
|
||||
if j - i <= 1 or dp[i + 1][j - 1]:
|
||||
dp[i][j] = True
|
||||
if dp[i][j] and j - i + 1 > maxlenth:
|
||||
maxlenth = j - i + 1
|
||||
left = i
|
||||
right = j
|
||||
return s[left:right + 1]
|
||||
|
||||
```
|
||||
|
||||
## Go
|
||||
@ -286,6 +303,6 @@ public:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -46,7 +46,7 @@ https://leetcode-cn.com/problems/3sum/
|
||||
大家可以尝试使用哈希法写一写,就知道其困难的程度了。
|
||||
|
||||
哈希法C++代码:
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> threeSum(vector<int>& nums) {
|
||||
@ -107,7 +107,7 @@ public:
|
||||
|
||||
C++代码代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> threeSum(vector<int>& nums) {
|
||||
@ -399,4 +399,4 @@ function threeSum(array $nums): array
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -416,4 +416,4 @@ var letterCombinations = function(digits) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -67,7 +67,7 @@ https://leetcode-cn.com/problems/4sum/
|
||||
|
||||
C++代码
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> fourSum(vector<int>& nums, int target) {
|
||||
@ -201,6 +201,54 @@ class Solution(object):
|
||||
```
|
||||
|
||||
Go:
|
||||
```go
|
||||
func fourSum(nums []int, target int) [][]int {
|
||||
if len(nums) < 4 {
|
||||
return nil
|
||||
}
|
||||
sort.Ints(nums)
|
||||
var res [][]int
|
||||
for i := 0; i < len(nums)-3; i++ {
|
||||
n1 := nums[i]
|
||||
// if n1 > target { // 不能这样写,因为可能是负数
|
||||
// break
|
||||
// }
|
||||
if i > 0 && n1 == nums[i-1] {
|
||||
continue
|
||||
}
|
||||
for j := i + 1; j < len(nums)-2; j++ {
|
||||
n2 := nums[j]
|
||||
if j > i+1 && n2 == nums[j-1] {
|
||||
continue
|
||||
}
|
||||
l := j + 1
|
||||
r := len(nums) - 1
|
||||
for l < r {
|
||||
n3 := nums[l]
|
||||
n4 := nums[r]
|
||||
sum := n1 + n2 + n3 + n4
|
||||
if sum < target {
|
||||
l++
|
||||
} else if sum > target {
|
||||
r--
|
||||
} else {
|
||||
res = append(res, []int{n1, n2, n3, n4})
|
||||
for l < r && n3 == nums[l+1] { // 去重
|
||||
l++
|
||||
}
|
||||
for l < r && n4 == nums[r-1] { // 去重
|
||||
r--
|
||||
}
|
||||
// 找到答案时,双指针同时靠近
|
||||
r--
|
||||
l++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
@ -241,4 +289,4 @@ var fourSum = function(nums, target) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -58,7 +58,7 @@
|
||||
|
||||
此时不难写出如下C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
ListNode* removeNthFromEnd(ListNode* head, int n) {
|
||||
@ -184,9 +184,28 @@ var removeNthFromEnd = function(head, n) {
|
||||
return ret.next;
|
||||
};
|
||||
```
|
||||
Kotlin:
|
||||
```Kotlin
|
||||
fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? {
|
||||
val pre = ListNode(0).apply {
|
||||
this.next = head
|
||||
}
|
||||
var fastNode: ListNode? = pre
|
||||
var slowNode: ListNode? = pre
|
||||
for (i in 0..n) {
|
||||
fastNode = fastNode?.next
|
||||
}
|
||||
while (fastNode != null) {
|
||||
slowNode = slowNode?.next
|
||||
fastNode = fastNode.next
|
||||
}
|
||||
slowNode?.next = slowNode?.next?.next
|
||||
return pre.next
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -108,7 +108,7 @@ cd a/b/c/../../
|
||||
实现C++代码如下:
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isValid(string s) {
|
||||
@ -264,4 +264,4 @@ var isValid = function(s) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -43,7 +43,7 @@ https://leetcode-cn.com/problems/swap-nodes-in-pairs/
|
||||
|
||||
对应的C++代码实现如下: (注释中详细和如上图中的三步做对应)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
ListNode* swapPairs(ListNode* head) {
|
||||
@ -86,6 +86,34 @@ public:
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
C:
|
||||
```
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* struct ListNode {
|
||||
* int val;
|
||||
* struct ListNode *next;
|
||||
* };
|
||||
*/
|
||||
|
||||
|
||||
struct ListNode* swapPairs(struct ListNode* head){
|
||||
//使用双指针避免使用中间变量
|
||||
typedef struct ListNode ListNode;
|
||||
ListNode *fakehead = (ListNode *)malloc(sizeof(ListNode));
|
||||
fakehead->next = head;
|
||||
ListNode* right = fakehead->next;
|
||||
ListNode* left = fakehead;
|
||||
while(left && right && right->next ){
|
||||
left->next = right->next;
|
||||
right->next = left->next->next;
|
||||
left->next->next = right;
|
||||
left = right;
|
||||
right = left->next;
|
||||
}
|
||||
return fakehead->next;
|
||||
}
|
||||
```
|
||||
|
||||
Java:
|
||||
|
||||
@ -200,9 +228,30 @@ var swapPairs = function (head) {
|
||||
};
|
||||
```
|
||||
|
||||
Kotlin:
|
||||
|
||||
```kotlin
|
||||
fun swapPairs(head: ListNode?): ListNode? {
|
||||
val dummyNode = ListNode(0).apply {
|
||||
this.next = head
|
||||
}
|
||||
var cur: ListNode? = dummyNode
|
||||
while (cur?.next != null && cur.next?.next != null) {
|
||||
val temp = cur.next
|
||||
val temp2 = cur.next?.next?.next
|
||||
cur.next = cur.next?.next
|
||||
cur.next?.next = temp
|
||||
cur.next?.next?.next = temp2
|
||||
cur = cur.next?.next
|
||||
}
|
||||
return dummyNode.next
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 时间复杂度:O(n^2)
|
||||
// 空间复杂度:O(1)
|
||||
class Solution {
|
||||
@ -85,7 +85,7 @@ public:
|
||||
|
||||
后序都会一一介绍到,本题代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 时间复杂度:O(n)
|
||||
// 空间复杂度:O(1)
|
||||
class Solution {
|
||||
@ -216,8 +216,27 @@ fn main() {
|
||||
println!("{:?}",remove_element(&mut nums, 5));
|
||||
}
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func removeElement(_ nums: inout [Int], _ val: Int) -> Int {
|
||||
var slowIndex = 0
|
||||
|
||||
for fastIndex in 0..<nums.count {
|
||||
if val != nums[fastIndex] {
|
||||
if slowIndex != fastIndex {
|
||||
nums[slowIndex] = nums[fastIndex]
|
||||
}
|
||||
slowIndex += 1
|
||||
}
|
||||
}
|
||||
return slowIndex
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -315,7 +315,7 @@ next[i] = j;
|
||||
|
||||
最后整体构建next数组的函数代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
void getNext(int* next, const string& s){
|
||||
int j = -1;
|
||||
next[0] = j;
|
||||
@ -386,7 +386,7 @@ if (j == (t.size() - 1) ) {
|
||||
|
||||
那么使用next数组,用模式串匹配文本串的整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
int j = -1; // 因为next数组里记录的起始位置为-1
|
||||
for (int i = 0; i < s.size(); i++) { // 注意i就从0开始
|
||||
while(j >= 0 && s[i] != t[j + 1]) { // 不匹配
|
||||
@ -405,7 +405,7 @@ for (int i = 0; i < s.size(); i++) { // 注意i就从0开始
|
||||
|
||||
# 前缀表统一减一 C++代码实现
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
void getNext(int* next, const string& s) {
|
||||
@ -457,7 +457,7 @@ public:
|
||||
|
||||
我给出的getNext的实现为:(前缀表统一减一)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
void getNext(int* next, const string& s) {
|
||||
int j = -1;
|
||||
next[0] = j;
|
||||
@ -479,7 +479,7 @@ void getNext(int* next, const string& s) {
|
||||
|
||||
那么前缀表不减一来构建next数组,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
void getNext(int* next, const string& s) {
|
||||
int j = 0;
|
||||
next[0] = 0;
|
||||
@ -502,7 +502,7 @@ void getNext(int* next, const string& s) {
|
||||
|
||||
实现代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
void getNext(int* next, const string& s) {
|
||||
@ -808,7 +808,93 @@ func strStr(haystack string, needle string) int {
|
||||
}
|
||||
```
|
||||
|
||||
JavaScript版本
|
||||
|
||||
> 前缀表统一减一
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* @param {string} haystack
|
||||
* @param {string} needle
|
||||
* @return {number}
|
||||
*/
|
||||
var strStr = function (haystack, needle) {
|
||||
if (needle.length === 0)
|
||||
return 0;
|
||||
|
||||
const getNext = (needle) => {
|
||||
let next = [];
|
||||
let j = -1;
|
||||
next.push(j);
|
||||
|
||||
for (let i = 1; i < needle.length; ++i) {
|
||||
while (j >= 0 && needle[i] !== needle[j + 1])
|
||||
j = next[j];
|
||||
if (needle[i] === needle[j + 1])
|
||||
j++;
|
||||
next.push(j);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
let next = getNext(needle);
|
||||
let j = -1;
|
||||
for (let i = 0; i < haystack.length; ++i) {
|
||||
while (j >= 0 && haystack[i] !== needle[j + 1])
|
||||
j = next[j];
|
||||
if (haystack[i] === needle[j + 1])
|
||||
j++;
|
||||
if (j === needle.length - 1)
|
||||
return (i - needle.length + 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
```
|
||||
|
||||
> 前缀表统一不减一
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* @param {string} haystack
|
||||
* @param {string} needle
|
||||
* @return {number}
|
||||
*/
|
||||
var strStr = function (haystack, needle) {
|
||||
if (needle.length === 0)
|
||||
return 0;
|
||||
|
||||
const getNext = (needle) => {
|
||||
let next = [];
|
||||
let j = 0;
|
||||
next.push(j);
|
||||
|
||||
for (let i = 1; i < needle.length; ++i) {
|
||||
while (j > 0 && needle[i] !== needle[j])
|
||||
j = next[j - 1];
|
||||
if (needle[i] === needle[j])
|
||||
j++;
|
||||
next.push(j);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
let next = getNext(needle);
|
||||
let j = 0;
|
||||
for (let i = 0; i < haystack.length; ++i) {
|
||||
while (j > 0 && haystack[i] !== needle[j])
|
||||
j = next[j - 1];
|
||||
if (haystack[i] === needle[j])
|
||||
j++;
|
||||
if (j === needle.length)
|
||||
return (i - needle.length + 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -816,4 +902,4 @@ func strStr(haystack string, needle string) int {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -75,7 +75,7 @@
|
||||
|
||||
对应的C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
void nextPermutation(vector<int>& nums) {
|
||||
@ -99,6 +99,24 @@ public:
|
||||
## Java
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public void nextPermutation(int[] nums) {
|
||||
for (int i = nums.length - 1; i >= 0; i--) {
|
||||
for (int j = nums.length - 1; j > i; j--) {
|
||||
if (nums[j] > nums[i]) {
|
||||
// 交换
|
||||
int temp = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = temp;
|
||||
// [i + 1, nums.length) 内元素升序排序
|
||||
Arrays.sort(nums, i + 1, nums.length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Arrays.sort(nums); // 不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
@ -120,5 +138,5 @@ public:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -14,7 +14,7 @@
|
||||
如果数组中不存在目标值 target,返回 [-1, -1]。
|
||||
|
||||
进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
|
||||
|
||||
|
||||
|
||||
示例 1:
|
||||
* 输入:nums = [5,7,7,8,8,10], target = 8
|
||||
@ -50,21 +50,21 @@
|
||||
|
||||
接下来,在去寻找左边界,和右边界了。
|
||||
|
||||
采用二分法来取寻找左右边界,为了让代码清晰,我分别写两个二分来寻找左边界和右边界。
|
||||
采用二分法来去寻找左右边界,为了让代码清晰,我分别写两个二分来寻找左边界和右边界。
|
||||
|
||||
**刚刚接触二分搜索的同学不建议上来就像如果用一个二分来查找左右边界,很容易把自己绕进去,建议扎扎实实的写两个二分分别找左边界和右边界**
|
||||
|
||||
## 寻找右边界
|
||||
|
||||
先来寻找右边界,至于二分查找,如果看过[为什么每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)就会知道,二分查找中什么时候用while (left <= right),有什么时候用while (left < right),其实只要清楚**循环不变量**,很容易区分两种写法。
|
||||
先来寻找右边界,至于二分查找,如果看过[704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)就会知道,二分查找中什么时候用while (left <= right),有什么时候用while (left < right),其实只要清楚**循环不变量**,很容易区分两种写法。
|
||||
|
||||
那么这里我采用while (left <= right)的写法,区间定义为[left, right],即左闭又闭的区间(如果这里有点看不懂了,强烈建议把[为什么每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)这篇文章先看了,在把「leetcode:35.搜索插入位置」做了之后在做这道题目就好很多了)
|
||||
那么这里我采用while (left <= right)的写法,区间定义为[left, right],即左闭又闭的区间(如果这里有点看不懂了,强烈建议把[704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)这篇文章先看了,704题目做了之后再做这道题目就好很多了)
|
||||
|
||||
确定好:计算出来的右边界是不包好target的右边界,左边界同理。
|
||||
|
||||
可以写出如下代码
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 二分查找,寻找target的右边界(不包括target)
|
||||
// 如果rightBorder为没有被赋值(即target在数组范围的左边,例如数组[3,3],target为2),为了处理情况一
|
||||
int getRightBorder(vector<int>& nums, int target) {
|
||||
@ -86,7 +86,7 @@ int getRightBorder(vector<int>& nums, int target) {
|
||||
|
||||
## 寻找左边界
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 二分查找,寻找target的左边界leftBorder(不包括target)
|
||||
// 如果leftBorder没有被赋值(即target在数组范围的右边,例如数组[3,3],target为4),为了处理情况一
|
||||
int getLeftBorder(vector<int>& nums, int target) {
|
||||
@ -110,7 +110,7 @@ int getLeftBorder(vector<int>& nums, int target) {
|
||||
|
||||
左右边界计算完之后,看一下主体代码,这里把上面讨论的三种情况,都覆盖了
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<int> searchRange(vector<int>& nums, int target) {
|
||||
@ -173,11 +173,219 @@ private:
|
||||
## Java
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
int[] searchRange(int[] nums, int target) {
|
||||
int leftBorder = getLeftBorder(nums, target);
|
||||
int rightBorder = getRightBorder(nums, target);
|
||||
// 情况一
|
||||
if (leftBorder == -2 || rightBorder == -2) return new int[]{-1, -1};
|
||||
// 情况三
|
||||
if (rightBorder - leftBorder > 1) return new int[]{leftBorder + 1, rightBorder - 1};
|
||||
// 情况二
|
||||
return new int[]{-1, -1};
|
||||
}
|
||||
|
||||
int getRightBorder(int[] nums, int target) {
|
||||
int left = 0;
|
||||
int right = nums.length - 1;
|
||||
int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
|
||||
while (left <= right) {
|
||||
int middle = left + ((right - left) / 2);
|
||||
if (nums[middle] > target) {
|
||||
right = middle - 1;
|
||||
} else { // 寻找右边界,nums[middle] == target的时候更新left
|
||||
left = middle + 1;
|
||||
rightBorder = left;
|
||||
}
|
||||
}
|
||||
return rightBorder;
|
||||
}
|
||||
|
||||
int getLeftBorder(int[] nums, int target) {
|
||||
int left = 0;
|
||||
int right = nums.length - 1;
|
||||
int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
|
||||
while (left <= right) {
|
||||
int middle = left + ((right - left) / 2);
|
||||
if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
|
||||
right = middle - 1;
|
||||
leftBorder = right;
|
||||
} else {
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
return leftBorder;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 解法2
|
||||
// 1、首先,在 nums 数组中二分查找 target;
|
||||
// 2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1};
|
||||
// 3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间
|
||||
|
||||
class Solution {
|
||||
public int[] searchRange(int[] nums, int target) {
|
||||
int index = binarySearch(nums, target); // 二分查找
|
||||
|
||||
if (index == -1) { // nums 中不存在 target,直接返回 {-1, -1}
|
||||
return new int[] {-1, -1}; // 匿名数组
|
||||
}
|
||||
// nums 中存在 targe,则左右滑动指针,来找到符合题意的区间
|
||||
int left = index;
|
||||
int right = index;
|
||||
// 向左滑动,找左边界
|
||||
while (left - 1 >= 0 && nums[left - 1] == nums[index]) { // 防止数组越界。逻辑短路,两个条件顺序不能换
|
||||
left--;
|
||||
}
|
||||
// 向左滑动,找右边界
|
||||
while (right + 1 < nums.length && nums[right + 1] == nums[index]) { // 防止数组越界。
|
||||
right++;
|
||||
}
|
||||
return new int[] {left, right};
|
||||
}
|
||||
|
||||
/**
|
||||
* 二分查找
|
||||
* @param nums
|
||||
* @param target
|
||||
*/
|
||||
public int binarySearch(int[] nums, int target) {
|
||||
int left = 0;
|
||||
int right = nums.length - 1; // 不变量:左闭右闭区间
|
||||
|
||||
while (left <= right) { // 不变量:左闭右闭区间
|
||||
int mid = left + (right - left) / 2;
|
||||
if (nums[mid] == target) {
|
||||
return mid;
|
||||
} else if (nums[mid] < target) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid - 1; // 不变量:左闭右闭区间
|
||||
}
|
||||
}
|
||||
return -1; // 不存在
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def searchRange(self, nums: List[int], target: int) -> List[int]:
|
||||
def getRightBorder(nums:List[int], target:int) -> int:
|
||||
left, right = 0, len(nums)-1
|
||||
rightBoder = -2 # 记录一下rightBorder没有被赋值的情况
|
||||
while left <= right:
|
||||
middle = left + (right-left) // 2
|
||||
if nums[middle] > target:
|
||||
right = middle - 1
|
||||
else: # 寻找右边界,nums[middle] == target的时候更新left
|
||||
left = middle + 1
|
||||
rightBoder = left
|
||||
|
||||
return rightBoder
|
||||
|
||||
def getLeftBorder(nums:List[int], target:int) -> int:
|
||||
left, right = 0, len(nums)-1
|
||||
leftBoder = -2 # 记录一下leftBorder没有被赋值的情况
|
||||
while left <= right:
|
||||
middle = left + (right-left) // 2
|
||||
if nums[middle] >= target: # 寻找左边界,nums[middle] == target的时候更新right
|
||||
right = middle - 1;
|
||||
leftBoder = right;
|
||||
else:
|
||||
left = middle + 1
|
||||
return leftBoder
|
||||
leftBoder = getLeftBorder(nums, target)
|
||||
rightBoder = getRightBorder(nums, target)
|
||||
# 情况一
|
||||
if leftBoder == -2 or rightBoder == -2: return [-1, -1]
|
||||
# 情况三
|
||||
if rightBoder -leftBoder >1: return [leftBoder + 1, rightBoder - 1]
|
||||
# 情况二
|
||||
return [-1, -1]
|
||||
```
|
||||
```python
|
||||
# 解法2
|
||||
# 1、首先,在 nums 数组中二分查找 target;
|
||||
# 2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1};
|
||||
# 3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间
|
||||
class Solution:
|
||||
def searchRange(self, nums: List[int], target: int) -> List[int]:
|
||||
def binarySearch(nums:List[int], target:int) -> int:
|
||||
left, right = 0, len(nums)-1
|
||||
while left<=right: # 不变量:左闭右闭区间
|
||||
middle = left + (right-left) // 2
|
||||
if nums[middle] > target:
|
||||
right = middle - 1
|
||||
elif nums[middle] < target:
|
||||
left = middle + 1
|
||||
else:
|
||||
return middle
|
||||
return -1
|
||||
index = binarySearch(nums, target)
|
||||
if index == -1:return [-1, -1] # nums 中不存在 target,直接返回 {-1, -1}
|
||||
# nums 中存在 targe,则左右滑动指针,来找到符合题意的区间
|
||||
left, right = index, index
|
||||
# 向左滑动,找左边界
|
||||
while left -1 >=0 and nums[left - 1] == target: left -=1
|
||||
# 向右滑动,找右边界
|
||||
while right+1 < len(nums) and nums[right + 1] == target: right +=1
|
||||
return [left, right]
|
||||
```
|
||||
```python
|
||||
# 解法3
|
||||
# 1、首先,在 nums 数组中二分查找得到第一个大于等于 target的下标(左边界)与第一个大于target的下标(右边界);
|
||||
# 2、如果左边界<= 右边界,则返回 [左边界, 右边界]。否则返回[-1, -1]
|
||||
class Solution:
|
||||
def searchRange(self, nums: List[int], target: int) -> List[int]:
|
||||
def binarySearch(nums:List[int], target:int, lower:bool) -> int:
|
||||
left, right = 0, len(nums)-1
|
||||
ans = len(nums)
|
||||
while left<=right: # 不变量:左闭右闭区间
|
||||
middle = left + (right-left) //2
|
||||
# lower为True,执行前半部分,找到第一个大于等于 target的下标 ,否则找到第一个大于target的下标
|
||||
if nums[middle] > target or (lower and nums[middle] >= target):
|
||||
right = middle - 1
|
||||
ans = middle
|
||||
else:
|
||||
left = middle + 1
|
||||
return ans
|
||||
|
||||
leftBorder = binarySearch(nums, target, True) # 搜索左边界
|
||||
rightBorder = binarySearch(nums, target, False) -1 # 搜索右边界
|
||||
if leftBorder<= rightBorder and rightBorder< len(nums) and nums[leftBorder] == target and nums[rightBorder] == target:
|
||||
return [leftBorder, rightBorder]
|
||||
return [-1, -1]
|
||||
```
|
||||
|
||||
```python
|
||||
# 解法4
|
||||
# 1、首先,在 nums 数组中二分查找得到第一个大于等于 target的下标leftBorder;
|
||||
# 2、在 nums 数组中二分查找得到第一个大于等于 target+1的下标, 减1则得到rightBorder;
|
||||
# 3、如果开始位置在数组的右边或者不存在target,则返回[-1, -1] 。否则返回[leftBorder, rightBorder]
|
||||
class Solution:
|
||||
def searchRange(self, nums: List[int], target: int) -> List[int]:
|
||||
def binarySearch(nums:List[int], target:int) -> int:
|
||||
left, right = 0, len(nums)-1
|
||||
while left<=right: # 不变量:左闭右闭区间
|
||||
middle = left + (right-left) //2
|
||||
if nums[middle] >= target:
|
||||
right = middle - 1
|
||||
else:
|
||||
left = middle + 1
|
||||
return left # 若存在target,则返回第一个等于target的值
|
||||
|
||||
leftBorder = binarySearch(nums, target) # 搜索左边界
|
||||
rightBorder = binarySearch(nums, target+1) -1 # 搜索右边界
|
||||
if leftBorder == len(nums) or nums[leftBorder]!= target: # 情况一和情况二
|
||||
return [-1, -1]
|
||||
return [leftBorder, rightBorder]
|
||||
```
|
||||
|
||||
## Go
|
||||
@ -194,6 +402,5 @@ private:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -116,7 +116,7 @@ public:
|
||||
|
||||
**大家要仔细看注释,思考为什么要写while(left <= right), 为什么要写right = middle - 1**。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int searchInsert(vector<int>& nums, int target) {
|
||||
@ -158,7 +158,7 @@ public:
|
||||
|
||||
**大家要仔细看注释,思考为什么要写while (left < right), 为什么要写right = middle**。
|
||||
|
||||
```cpp
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int searchInsert(vector<int>& nums, int target) {
|
||||
@ -234,8 +234,6 @@ class Solution {
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
Python:
|
||||
```python3
|
||||
class Solution:
|
||||
@ -254,9 +252,6 @@ class Solution:
|
||||
return right + 1
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
```js
|
||||
var searchInsert = function (nums, target) {
|
||||
@ -277,9 +272,45 @@ var searchInsert = function (nums, target) {
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
// 暴力法
|
||||
func searchInsert(_ nums: [Int], _ target: Int) -> Int {
|
||||
for i in 0..<nums.count {
|
||||
if nums[i] >= target {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return nums.count
|
||||
}
|
||||
|
||||
// 二分法
|
||||
func searchInsert(_ nums: [Int], _ target: Int) -> Int {
|
||||
var left = 0
|
||||
var right = nums.count - 1
|
||||
|
||||
while left <= right {
|
||||
let middle = left + ((right - left) >> 1)
|
||||
|
||||
if nums[middle] > target {
|
||||
right = middle - 1
|
||||
}else if nums[middle] < target {
|
||||
left = middle + 1
|
||||
}else if nums[middle] == target {
|
||||
return middle
|
||||
}
|
||||
}
|
||||
|
||||
return right + 1
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -90,7 +90,7 @@ bool backtracking(vector<vector<char>>& board)
|
||||
|
||||
代码如下:(**详细看注释**)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
bool backtracking(vector<vector<char>>& board) {
|
||||
for (int i = 0; i < board.size(); i++) { // 遍历行
|
||||
for (int j = 0; j < board[0].size(); j++) { // 遍历列
|
||||
@ -125,7 +125,7 @@ bool backtracking(vector<vector<char>>& board) {
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
bool isValid(int row, int col, char val, vector<vector<char>>& board) {
|
||||
for (int i = 0; i < 9; i++) { // 判断行里是否重复
|
||||
if (board[row][i] == val) {
|
||||
@ -154,7 +154,7 @@ bool isValid(int row, int col, char val, vector<vector<char>>& board) {
|
||||
|
||||
## C++代码
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
bool backtracking(vector<vector<char>>& board) {
|
||||
@ -437,4 +437,4 @@ var solveSudoku = function(board) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -73,7 +73,7 @@ candidates 中的数字可以无限制重复被选取。
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<vector<int>> result;
|
||||
vector<int> path;
|
||||
void backtracking(vector<int>& candidates, int target, int sum, int startIndex)
|
||||
@ -89,7 +89,7 @@ void backtracking(vector<int>& candidates, int target, int sum, int startIndex)
|
||||
|
||||
sum等于target的时候,需要收集结果,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (sum > target) {
|
||||
return;
|
||||
}
|
||||
@ -107,7 +107,7 @@ if (sum == target) {
|
||||
|
||||
如何重复选取呢,看代码,注释部分:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = startIndex; i < candidates.size(); i++) {
|
||||
sum += candidates[i];
|
||||
path.push_back(candidates[i]);
|
||||
@ -119,7 +119,7 @@ for (int i = startIndex; i < candidates.size(); i++) {
|
||||
|
||||
按照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中给出的模板,不难写出如下C++完整代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
private:
|
||||
@ -179,7 +179,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
|
||||
|
||||
整体代码如下:(注意注释的部分)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<int>> result;
|
||||
@ -351,4 +351,4 @@ var combinationSum = function(candidates, target) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -90,7 +90,7 @@ candidates 中的每个数字在每个组合中只能使用一次。
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<vector<int>> result; // 存放组合集合
|
||||
vector<int> path; // 符合条件的组合
|
||||
void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
|
||||
@ -102,7 +102,7 @@ void backtracking(vector<int>& candidates, int target, int sum, int startIndex,
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (sum > target) { // 这个条件其实可以省略
|
||||
return;
|
||||
}
|
||||
@ -137,7 +137,7 @@ if (sum == target) {
|
||||
|
||||
那么单层搜索的逻辑代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
|
||||
// used[i - 1] == true,说明同一树支candidates[i - 1]使用过
|
||||
// used[i - 1] == false,说明同一树层candidates[i - 1]使用过
|
||||
@ -161,7 +161,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
|
||||
|
||||
回溯三部曲分析完了,整体C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<int>> result;
|
||||
@ -206,7 +206,7 @@ public:
|
||||
|
||||
这里直接用startIndex来去重也是可以的, 就不用used数组了。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<int>> result;
|
||||
@ -398,4 +398,4 @@ var combinationSum2 = function(candidates, target) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -77,7 +77,7 @@
|
||||
一样的方法,只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。
|
||||
|
||||
首先从头遍历所有的列,并且**要注意第一个柱子和最后一个柱子不接雨水**,代码如下:
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 0; i < height.size(); i++) {
|
||||
// 第一个柱子和最后一个柱子不接雨水
|
||||
if (i == 0 || i == height.size() - 1) continue;
|
||||
@ -86,7 +86,7 @@ for (int i = 0; i < height.size(); i++) {
|
||||
|
||||
在for循环中求左右两边最高柱子,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
int rHeight = height[i]; // 记录右边柱子的最高高度
|
||||
int lHeight = height[i]; // 记录左边柱子的最高高度
|
||||
for (int r = i + 1; r < height.size(); r++) {
|
||||
@ -99,14 +99,14 @@ for (int l = i - 1; l >= 0; l--) {
|
||||
|
||||
最后,计算该列的雨水高度,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
int h = min(lHeight, rHeight) - height[i];
|
||||
if (h > 0) sum += h; // 注意只有h大于零的时候,在统计到总和中
|
||||
```
|
||||
|
||||
整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int trap(vector<int>& height) {
|
||||
@ -152,7 +152,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int trap(vector<int>& height) {
|
||||
@ -287,7 +287,7 @@ if (height[i] == height[st.top()]) { // 例如 5 5 1 7 这种情况
|
||||
|
||||
求当前凹槽雨水的体积代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while,持续跟新栈顶元素
|
||||
int mid = st.top();
|
||||
st.pop();
|
||||
@ -301,7 +301,7 @@ while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while,
|
||||
|
||||
关键部分讲完了,整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int trap(vector<int>& height) {
|
||||
@ -335,7 +335,7 @@ public:
|
||||
|
||||
以上代码冗余了一些,但是思路是清晰的,下面我将代码精简一下,如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int trap(vector<int>& height) {
|
||||
@ -388,6 +388,44 @@ class Solution:
|
||||
res += res1
|
||||
return res
|
||||
```
|
||||
动态规划
|
||||
```python3
|
||||
class Solution:
|
||||
def trap(self, height: List[int]) -> int:
|
||||
leftheight, rightheight = [0]*len(height), [0]*len(height)
|
||||
|
||||
leftheight[0]=height[0]
|
||||
for i in range(1,len(height)):
|
||||
leftheight[i]=max(leftheight[i-1],height[i])
|
||||
rightheight[-1]=height[-1]
|
||||
for i in range(len(height)-2,-1,-1):
|
||||
rightheight[i]=max(rightheight[i+1],height[i])
|
||||
|
||||
result = 0
|
||||
for i in range(0,len(height)):
|
||||
summ = min(leftheight[i],rightheight[i])-height[i]
|
||||
result += summ
|
||||
return result
|
||||
```
|
||||
单调栈
|
||||
```python3
|
||||
class Solution:
|
||||
def trap(self, height: List[int]) -> int:
|
||||
st =[0]
|
||||
result = 0
|
||||
for i in range(1,len(height)):
|
||||
while st!=[] and height[i]>height[st[-1]]:
|
||||
midh = height[st[-1]]
|
||||
st.pop()
|
||||
if st!=[]:
|
||||
hright = height[i]
|
||||
hleft = height[st[-1]]
|
||||
h = min(hright,hleft)-midh
|
||||
w = i-st[-1]-1
|
||||
result+=h*w
|
||||
st.append(i)
|
||||
return result
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
@ -398,4 +436,4 @@ JavaScript:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -63,7 +63,7 @@
|
||||
|
||||
C++代码如下:(详细注释)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
@ -106,7 +106,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
@ -236,4 +236,4 @@ var jump = function(nums) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -106,7 +106,7 @@ for (int i = 0; i < nums.size(); i++) {
|
||||
整体C++代码如下:
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> result;
|
||||
@ -227,24 +227,27 @@ class Solution:
|
||||
|
||||
Go:
|
||||
```Go
|
||||
var result [][]int
|
||||
func backtrack(nums,pathNums []int,used []bool){
|
||||
if len(nums)==len(pathNums){
|
||||
tmp:=make([]int,len(nums))
|
||||
copy(tmp,pathNums)
|
||||
result=append(result,tmp)
|
||||
//result=append(result,pathNums)
|
||||
return
|
||||
}
|
||||
for i:=0;i<len(nums);i++{
|
||||
if !used[i]{
|
||||
used[i]=true
|
||||
pathNums=append(pathNums,nums[i])
|
||||
backtrack(nums,pathNums,used)
|
||||
pathNums=pathNums[:len(pathNums)-1]
|
||||
used[i]=false
|
||||
}
|
||||
}
|
||||
var res [][]int
|
||||
func permute(nums []int) [][]int {
|
||||
res = [][]int{}
|
||||
backTrack(nums,len(nums),[]int{})
|
||||
return res
|
||||
}
|
||||
func backTrack(nums []int,numsLen int,path []int) {
|
||||
if len(nums)==0{
|
||||
p:=make([]int,len(path))
|
||||
copy(p,path)
|
||||
res = append(res,p)
|
||||
}
|
||||
for i:=0;i<numsLen;i++{
|
||||
cur:=nums[i]
|
||||
path = append(path,cur)
|
||||
nums = append(nums[:i],nums[i+1:]...)//直接使用切片
|
||||
backTrack(nums,len(nums),path)
|
||||
nums = append(nums[:i],append([]int{cur},nums[i:]...)...)//回溯的时候切片也要复原,元素位置不能变
|
||||
path = path[:len(path)-1]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@ -286,4 +289,4 @@ var permute = function(nums) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -228,35 +228,31 @@ Go:
|
||||
```go
|
||||
var res [][]int
|
||||
func permute(nums []int) [][]int {
|
||||
res = [][]int{}
|
||||
sort.Ints(nums)
|
||||
dfs(nums, make([]int, 0), make([]bool, len(nums)))
|
||||
return res
|
||||
res = [][]int{}
|
||||
backTrack(nums,len(nums),[]int{})
|
||||
return res
|
||||
}
|
||||
func backTrack(nums []int,numsLen int,path []int) {
|
||||
if len(nums)==0{
|
||||
p:=make([]int,len(path))
|
||||
copy(p,path)
|
||||
res = append(res,p)
|
||||
}
|
||||
used := [21]int{}//跟前一题唯一的区别,同一层不使用重复的数。关于used的思想carl在递增子序列那一题中提到过
|
||||
for i:=0;i<numsLen;i++{
|
||||
if used[nums[i]+10]==1{
|
||||
continue
|
||||
}
|
||||
cur:=nums[i]
|
||||
path = append(path,cur)
|
||||
used[nums[i]+10]=1
|
||||
nums = append(nums[:i],nums[i+1:]...)
|
||||
backTrack(nums,len(nums),path)
|
||||
nums = append(nums[:i],append([]int{cur},nums[i:]...)...)
|
||||
path = path[:len(path)-1]
|
||||
|
||||
func dfs(nums, path []int, used []bool) {
|
||||
if len(path) == len(nums) {
|
||||
res = append(res, append([]int{}, path...))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m := make(map[int]bool)
|
||||
for i := 0; i < len(nums); i++ {
|
||||
// used 从剩余 nums 中选
|
||||
if used[i] {
|
||||
continue
|
||||
}
|
||||
// m 集合间去重
|
||||
if _, ok := m[nums[i]]; ok {
|
||||
continue
|
||||
}
|
||||
m[nums[i]] = true
|
||||
path = append(path, nums[i])
|
||||
used[i] = true
|
||||
dfs(nums, path, used)
|
||||
used[i] = false
|
||||
path = path[:len(path)-1]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -342,4 +338,4 @@ func backTring(nums,subRes []int,res *[][]int,used []bool){
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -171,7 +171,7 @@ bool isValid(int row, int col, vector<string>& chessboard, int n) {
|
||||
|
||||
## C++代码
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<string>> result;
|
||||
@ -490,4 +490,4 @@ var solveNQueens = function(n) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
时间复杂度:O(n^2)
|
||||
空间复杂度:O(1)
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxSubArray(vector<int>& nums) {
|
||||
@ -81,7 +81,7 @@ if (count > result) result = count;
|
||||
|
||||
那么不难写出如下C++代码(关键地方已经注释)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxSubArray(vector<int>& nums) {
|
||||
@ -109,7 +109,7 @@ public:
|
||||
|
||||
那么先给出我的dp代码如下,有时间的录友可以提前做一做:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxSubArray(vector<int>& nums) {
|
||||
@ -214,4 +214,4 @@ var maxSubArray = function(nums) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -65,7 +65,7 @@ dp[0]应该是多少呢?
|
||||
|
||||
以上动规五部曲分析完毕,完整代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxSubArray(vector<int>& nums) {
|
||||
@ -138,7 +138,52 @@ class Solution:
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
// solution
|
||||
// 1, dp
|
||||
// 2, 贪心
|
||||
|
||||
func maxSubArray(nums []int) int {
|
||||
n := len(nums)
|
||||
// 这里的dp[i] 表示,最大的连续子数组和,包含num[i] 元素
|
||||
dp := make([]int,n)
|
||||
// 初始化,由于dp 状态转移方程依赖dp[0]
|
||||
dp[0] = nums[0]
|
||||
// 初始化最大的和
|
||||
mx := nums[0]
|
||||
for i:=1;i<n;i++ {
|
||||
// 这里的状态转移方程就是:求最大和
|
||||
// 会面临2种情况,一个是带前面的和,一个是不带前面的和
|
||||
dp[i] = max(dp[i-1]+nums[i],nums[i])
|
||||
mx = max(mx,dp[i])
|
||||
}
|
||||
return mx
|
||||
}
|
||||
|
||||
func max(a,b int) int{
|
||||
if a>b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
|
||||
```javascript
|
||||
const maxSubArray = nums => {
|
||||
// 数组长度,dp初始化
|
||||
const [len, dp] = [nums.length, [nums[0]]];
|
||||
// 最大值初始化为dp[0]
|
||||
let max = dp[0];
|
||||
for (let i = 1; i < len; i++) {
|
||||
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
|
||||
// 更新最大值
|
||||
max = Math.max(max, dp[i]);
|
||||
}
|
||||
return max;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -146,4 +191,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -58,7 +58,7 @@ i每次移动只能在cover的范围内移动,每移动一个元素,cover得
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool canJump(vector<int>& nums) {
|
||||
@ -161,4 +161,4 @@ var canJump = function(nums) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -56,7 +56,7 @@
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
// 按照区间左边界从小到大排序
|
||||
@ -92,7 +92,7 @@ public:
|
||||
|
||||
当然以上代码有冗余一些,可以优化一下,如下:(思路是一样的)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> merge(vector<vector<int>>& intervals) {
|
||||
@ -176,30 +176,27 @@ class Solution:
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
```golang
|
||||
func merge(intervals [][]int) [][]int {
|
||||
sort.Slice(intervals, func(i, j int) bool {
|
||||
return intervals[i][0]<intervals[j][0]
|
||||
})
|
||||
|
||||
res:=[][]int{}
|
||||
prev:=intervals[0]
|
||||
|
||||
for i:=1;i<len(intervals);i++{
|
||||
cur :=intervals[i]
|
||||
if prev[1]<cur[0]{
|
||||
res=append(res,prev)
|
||||
prev=cur
|
||||
}else {
|
||||
prev[1]=max(prev[1],cur[1])
|
||||
}
|
||||
}
|
||||
res=append(res,prev)
|
||||
return res
|
||||
//先从小到大排序
|
||||
sort.Slice(intervals,func(i,j int)bool{
|
||||
return intervals[i][0]<intervals[j][0]
|
||||
})
|
||||
//再弄重复的
|
||||
for i:=0;i<len(intervals)-1;i++{
|
||||
if intervals[i][1]>=intervals[i+1][0]{
|
||||
intervals[i][1]=max(intervals[i][1],intervals[i+1][1])//赋值最大值
|
||||
intervals=append(intervals[:i+1],intervals[i+2:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
return intervals
|
||||
}
|
||||
func max(a, b int) int {
|
||||
if a > b { return a }
|
||||
return b
|
||||
func max(a,b int)int{
|
||||
if a>b{
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
@ -229,4 +226,4 @@ var merge = function (intervals) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -66,7 +66,7 @@
|
||||
|
||||
整体C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> generateMatrix(int n) {
|
||||
@ -302,6 +302,61 @@ func generateMatrix(n int) [][]int {
|
||||
}
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func generateMatrix(_ n: Int) -> [[Int]] {
|
||||
var result = [[Int]](repeating: [Int](repeating: 0, count: n), count: n)
|
||||
|
||||
var startRow = 0
|
||||
var startColumn = 0
|
||||
var loopCount = n / 2
|
||||
let mid = n / 2
|
||||
var count = 1
|
||||
var offset = 1
|
||||
var row: Int
|
||||
var column: Int
|
||||
|
||||
while loopCount > 0 {
|
||||
row = startRow
|
||||
column = startColumn
|
||||
|
||||
for c in column ..< startColumn + n - offset {
|
||||
result[startRow][c] = count
|
||||
count += 1
|
||||
column += 1
|
||||
}
|
||||
|
||||
for r in row ..< startRow + n - offset {
|
||||
result[r][column] = count
|
||||
count += 1
|
||||
row += 1
|
||||
}
|
||||
|
||||
for _ in startColumn ..< column {
|
||||
result[row][column] = count
|
||||
count += 1
|
||||
column -= 1
|
||||
}
|
||||
|
||||
for _ in startRow ..< row {
|
||||
result[row][column] = count
|
||||
count += 1
|
||||
row -= 1
|
||||
}
|
||||
|
||||
startRow += 1
|
||||
startColumn += 1
|
||||
offset += 2
|
||||
loopCount -= 1
|
||||
}
|
||||
|
||||
if (n % 2) != 0 {
|
||||
result[mid][mid] = count
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -309,4 +364,4 @@ func generateMatrix(n int) [][]int {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
此时问题就可以转化为求二叉树叶子节点的个数,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
int dfs(int i, int j, int m, int n) {
|
||||
@ -128,7 +128,7 @@ for (int j = 0; j < n; j++) dp[0][j] = 1;
|
||||
|
||||
以上动规五部曲分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int uniquePaths(int m, int n) {
|
||||
@ -149,7 +149,7 @@ public:
|
||||
|
||||
其实用一个一维数组(也可以理解是滚动数组)就可以了,但是不利于理解,可以优化点空间,建议先理解了二维,在理解一维,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int uniquePaths(int m, int n) {
|
||||
@ -187,7 +187,7 @@ public:
|
||||
|
||||
例如如下代码是不行的。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int uniquePaths(int m, int n) {
|
||||
@ -204,7 +204,7 @@ public:
|
||||
|
||||
需要在计算分子的时候,不断除以分母,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int uniquePaths(int m, int n) {
|
||||
@ -333,4 +333,4 @@ var uniquePaths = function(m, n) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -97,7 +97,7 @@ for (int j = 0; j < n; j++) dp[0][j] = 1;
|
||||
|
||||
所以本题初始化代码为:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<vector<int>> dp(m, vector<int>(n, 0));
|
||||
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
|
||||
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
|
||||
@ -111,7 +111,7 @@ for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 1; i < m; i++) {
|
||||
for (int j = 1; j < n; j++) {
|
||||
if (obstacleGrid[i][j] == 1) continue;
|
||||
@ -135,7 +135,7 @@ for (int i = 1; i < m; i++) {
|
||||
|
||||
动规五部分分析完毕,对应C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
|
||||
@ -341,4 +341,4 @@ var uniquePathsWithObstacles = function(obstacleGrid) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -109,7 +109,7 @@ dp[i]: 爬到第i层楼梯,有dp[i]种方法
|
||||
|
||||
以上五部分析完之后,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
@ -130,7 +130,7 @@ public:
|
||||
|
||||
当然依然也可以,优化一下空间复杂度,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
@ -163,7 +163,7 @@ public:
|
||||
|
||||
这里我先给出我的实现代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int climbStairs(int n) {
|
||||
@ -301,4 +301,4 @@ var climbStairs = function(n) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -193,4 +193,4 @@ func climbStairs(n int) int {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -91,18 +91,18 @@ if (word1[i - 1] != word2[j - 1])
|
||||
|
||||
`if (word1[i - 1] != word2[j - 1])`,此时就需要编辑了,如何编辑呢?
|
||||
|
||||
操作一:word1增加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 加上一个增加元素的操作。
|
||||
* 操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 再加上一个操作。
|
||||
|
||||
即 `dp[i][j] = dp[i - 1][j] + 1;`
|
||||
|
||||
|
||||
操作二:word2添加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个增加元素的操作。
|
||||
* 操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 再加上一个操作。
|
||||
|
||||
即 `dp[i][j] = dp[i][j - 1] + 1;`
|
||||
|
||||
这里有同学发现了,怎么都是添加元素,删除元素去哪了。
|
||||
这里有同学发现了,怎么都是删除元素,添加元素去哪了。
|
||||
|
||||
**word2添加一个元素,相当于word1删除一个元素**,例如 `word1 = "ad" ,word2 = "a"`,`word1`删除元素`'d'`,`word2`添加一个元素`'d'`,变成`word1="a", word2="ad"`, 最终的操作数是一样! dp数组如下图所示意的:
|
||||
**word2添加一个元素,相当于word1删除一个元素**,例如 `word1 = "ad" ,word2 = "a"`,`word1`删除元素`'d'` 和 `word2`添加一个元素`'d'`,变成`word1="a", word2="ad"`, 最终的操作数是一样! dp数组如下图所示意的:
|
||||
|
||||
```
|
||||
a a d
|
||||
@ -123,7 +123,7 @@ if (word1[i - 1] != word2[j - 1])
|
||||
|
||||
递归公式代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (word1[i - 1] == word2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
}
|
||||
@ -151,7 +151,7 @@ dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最
|
||||
|
||||
所以C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
|
||||
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
|
||||
```
|
||||
@ -175,7 +175,7 @@ for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 1; i <= word1.size(); i++) {
|
||||
for (int j = 1; j <= word2.size(); j++) {
|
||||
if (word1[i - 1] == word2[j - 1]) {
|
||||
@ -198,7 +198,7 @@ for (int i = 1; i <= word1.size(); i++) {
|
||||
|
||||
以上动规五部分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int minDistance(string word1, string word2) {
|
||||
@ -338,4 +338,4 @@ const minDistance = (word1, word2) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -173,7 +173,7 @@ for循环每次从startIndex开始遍历,然后用path保存取到的节点i
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = startIndex; i <= n; i++) { // 控制树的横向遍历
|
||||
path.push_back(i); // 处理节点
|
||||
backtracking(n, k, i + 1); // 递归:控制树的纵向遍历,注意下一层搜索要从i+1开始
|
||||
@ -188,7 +188,7 @@ backtracking的下面部分就是回溯的操作了,撤销本次处理的结
|
||||
关键地方都讲完了,组合问题C++完整代码如下:
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<int>> result; // 存放符合条件结果的集合
|
||||
@ -440,4 +440,4 @@ func backtrack(n,k,start int,track []int){
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -249,4 +249,4 @@ var combine = function(n, k) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -120,7 +120,7 @@ void backtracking(参数) {
|
||||
|
||||
可以写出如下回溯算法C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<int>> result;
|
||||
@ -268,4 +268,4 @@ var subsets = function(nums) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
247
problems/0084.柱状图中最大的矩形.md
Normal file
247
problems/0084.柱状图中最大的矩形.md
Normal file
@ -0,0 +1,247 @@
|
||||
|
||||
|
||||
# 84.柱状图中最大的矩形
|
||||
|
||||
链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram/
|
||||
|
||||
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
|
||||
|
||||
求在该柱状图中,能够勾勒出来的矩形的最大面积。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
# 思路
|
||||
|
||||
本题和[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw),是遥相呼应的两道题目,建议都要仔细做一做,原理上有很多相同的地方,但细节上又有差异,更可以加深对单调栈的理解!
|
||||
|
||||
其实这两道题目先做那一道都可以,但我先写的42.接雨水的题解,所以如果没做过接雨水的话,建议先做一做接雨水,可以参考我的题解:[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)
|
||||
|
||||
我们先来看一下双指针的解法:
|
||||
|
||||
## 双指针解法
|
||||
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int largestRectangleArea(vector<int>& heights) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < heights.size(); i++) {
|
||||
int left = i;
|
||||
int right = i;
|
||||
for (; left >= 0; left--) {
|
||||
if (heights[left] < heights[i]) break;
|
||||
}
|
||||
for (; right < heights.size(); right++) {
|
||||
if (heights[right] < heights[i]) break;
|
||||
}
|
||||
int w = right - left - 1;
|
||||
int h = heights[i];
|
||||
sum = max(sum, w * h);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
如上代码并不能通过leetcode,超时了,因为时间复杂度是O(n^2)。
|
||||
|
||||
## 动态规划
|
||||
|
||||
本题动态规划的写法整体思路和[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)是一致的,但要比[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)难一些。
|
||||
|
||||
难就难在本题要记录记录每个柱子 左边第一个小于该柱子的下标,而不是左边第一个小于该柱子的高度。
|
||||
|
||||
所以需要循环查找,也就是下面在寻找的过程中使用了while,详细请看下面注释,整理思路在题解:[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)中已经介绍了。
|
||||
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int largestRectangleArea(vector<int>& heights) {
|
||||
vector<int> minLeftIndex(heights.size());
|
||||
vector<int> minRightIndex(heights.size());
|
||||
int size = heights.size();
|
||||
|
||||
// 记录每个柱子 左边第一个小于该柱子的下标
|
||||
minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环
|
||||
for (int i = 1; i < size; i++) {
|
||||
int t = i - 1;
|
||||
// 这里不是用if,而是不断向左寻找的过程
|
||||
while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
|
||||
minLeftIndex[i] = t;
|
||||
}
|
||||
// 记录每个柱子 右边第一个小于该柱子的下标
|
||||
minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环
|
||||
for (int i = size - 2; i >= 0; i--) {
|
||||
int t = i + 1;
|
||||
// 这里不是用if,而是不断向右寻找的过程
|
||||
while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
|
||||
minRightIndex[i] = t;
|
||||
}
|
||||
// 求和
|
||||
int result = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
|
||||
result = max(sum, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 单调栈
|
||||
|
||||
本地单调栈的解法和接雨水的题目是遥相呼应的。
|
||||
|
||||
为什么这么说呢,[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)是找每个柱子左右两边第一个大于该柱子高度的柱子,而本题是找每个柱子左右两边第一个小于该柱子的柱子。
|
||||
|
||||
**这里就涉及到了单调栈很重要的性质,就是单调栈里的顺序,是从小到大还是从大到小**。
|
||||
|
||||
在题解[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)中我讲解了接雨水的单调栈从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。
|
||||
|
||||
那么因为本题是要找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出)到栈底的顺序应该是从大到小的顺序!
|
||||
|
||||
我来举一个例子,如图:
|
||||
|
||||

|
||||
|
||||
只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。
|
||||
|
||||
所以本题单调栈的顺序正好与接雨水反过来。
|
||||
|
||||
此时大家应该可以发现其实就是**栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度**
|
||||
|
||||
理解这一点,对单调栈就掌握的比较到位了。
|
||||
|
||||
除了栈内元素顺序和接雨水不同,剩下的逻辑就都差不多了,在题解[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)我已经对单调栈的各个方面做了详细讲解,这里就不赘述了。
|
||||
|
||||
剩下就是分析清楚如下三种情况:
|
||||
|
||||
* 情况一:当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况
|
||||
* 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况
|
||||
* 情况三:当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
int largestRectangleArea(vector<int>& heights) {
|
||||
stack<int> st;
|
||||
heights.insert(heights.begin(), 0); // 数组头部加入元素0
|
||||
heights.push_back(0); // 数组尾部加入元素0
|
||||
st.push(0);
|
||||
int result = 0;
|
||||
// 第一个元素已经入栈,从下表1开始
|
||||
for (int i = 1; i < heights.size(); i++) {
|
||||
// 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下表
|
||||
if (heights[i] > heights[st.top()]) {
|
||||
st.push(i);
|
||||
} else if (heights[i] == heights[st.top()]) {
|
||||
st.pop(); // 这个可以加,可以不加,效果一样,思路不同
|
||||
st.push(i);
|
||||
} else {
|
||||
while (heights[i] < heights[st.top()]) { // 注意是while
|
||||
int mid = st.top();
|
||||
st.pop();
|
||||
int left = st.top();
|
||||
int right = i;
|
||||
int w = right - left - 1;
|
||||
int h = heights[mid];
|
||||
result = max(result, w * h);
|
||||
}
|
||||
st.push(i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
代码精简之后:
|
||||
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
int largestRectangleArea(vector<int>& heights) {
|
||||
stack<int> st;
|
||||
heights.insert(heights.begin(), 0); // 数组头部加入元素0
|
||||
heights.push_back(0); // 数组尾部加入元素0
|
||||
st.push(0);
|
||||
int result = 0;
|
||||
for (int i = 1; i < heights.size(); i++) {
|
||||
while (heights[i] < heights[st.top()]) {
|
||||
int mid = st.top();
|
||||
st.pop();
|
||||
int w = i - st.top() - 1;
|
||||
int h = heights[mid];
|
||||
result = max(result, w * h);
|
||||
}
|
||||
st.push(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
这里我依然建议大家按部就班把版本一写出来,把情况一二三分析清楚,然后在精简代码到版本二。 直接看版本二容易忽略细节!
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
Java:
|
||||
|
||||
Python:
|
||||
|
||||
动态规划
|
||||
```python3
|
||||
class Solution:
|
||||
def largestRectangleArea(self, heights: List[int]) -> int:
|
||||
result = 0
|
||||
minleftindex, minrightindex = [0]*len(heights), [0]*len(heights)
|
||||
|
||||
minleftindex[0]=-1
|
||||
for i in range(1,len(heights)):
|
||||
t = i-1
|
||||
while t>=0 and heights[t]>=heights[i]: t=minleftindex[t]
|
||||
minleftindex[i]=t
|
||||
|
||||
minrightindex[-1]=len(heights)
|
||||
for i in range(len(heights)-2,-1,-1):
|
||||
t=i+1
|
||||
while t<len(heights) and heights[t]>=heights[i]: t=minrightindex[t]
|
||||
minrightindex[i]=t
|
||||
|
||||
for i in range(0,len(heights)):
|
||||
left = minleftindex[i]
|
||||
right = minrightindex[i]
|
||||
summ = (right-left-1)*heights[i]
|
||||
result = max(result,summ)
|
||||
return result
|
||||
```
|
||||
单调栈 版本二
|
||||
```python3
|
||||
class Solution:
|
||||
def largestRectangleArea(self, heights: List[int]) -> int:
|
||||
heights.insert(0,0) # 数组头部加入元素0
|
||||
heights.append(0) # 数组尾部加入元素0
|
||||
st = [0]
|
||||
result = 0
|
||||
for i in range(1,len(heights)):
|
||||
while st!=[] and heights[i]<heights[st[-1]]:
|
||||
midh = heights[st[-1]]
|
||||
st.pop()
|
||||
if st!=[]:
|
||||
minrightindex = i
|
||||
minleftindex = st[-1]
|
||||
summ = (minrightindex-minleftindex-1)*midh
|
||||
result = max(summ,result)
|
||||
st.append(i)
|
||||
return result
|
||||
```
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
@ -124,7 +124,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<int>> result;
|
||||
@ -288,4 +288,4 @@ var subsetsWithDup = function(nums) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -183,7 +183,7 @@ void backtracking(参数) {
|
||||
|
||||
可以写出如下回溯算法C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<string> result;// 记录结果
|
||||
@ -453,4 +453,4 @@ func isNormalIp(s string,startIndex,end int)bool{
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -103,7 +103,7 @@ j相当于是头结点的元素,从1遍历到i为止。
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= i; j++) {
|
||||
dp[i] += dp[j - 1] * dp[i - j];
|
||||
@ -123,7 +123,7 @@ n为5时候的dp数组状态如图:
|
||||
|
||||
综上分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int numTrees(int n) {
|
||||
@ -234,4 +234,4 @@ const numTrees =(n) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
可以递归中序遍历将二叉搜索树转变成一个数组,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<int> vec;
|
||||
void traversal(TreeNode* root) {
|
||||
if (root == NULL) return;
|
||||
@ -44,7 +44,7 @@ void traversal(TreeNode* root) {
|
||||
|
||||
然后只要比较一下,这个数组是否是有序的,**注意二叉搜索树中不能有重复元素**。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
traversal(root);
|
||||
for (int i = 1; i < vec.size(); i++) {
|
||||
// 注意要小于等于,搜索树里不能有相同元素
|
||||
@ -55,7 +55,7 @@ return true;
|
||||
|
||||
整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<int> vec;
|
||||
@ -103,7 +103,7 @@ if (root->val > root->left->val && root->val < root->right->val) {
|
||||
|
||||

|
||||
|
||||
节点10小于左节点5,大于右节点15,但右子树里出现了一个6 这就不符合了!
|
||||
节点10大于左节点5,小于右节点15,但右子树里出现了一个6 这就不符合了!
|
||||
|
||||
* 陷阱2
|
||||
|
||||
@ -163,7 +163,7 @@ return left && right;
|
||||
|
||||
整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值
|
||||
@ -189,7 +189,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* pre = NULL; // 用来记录前一个节点
|
||||
@ -214,7 +214,7 @@ public:
|
||||
|
||||
迭代法中序遍历稍加改动就可以了,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isValidBST(TreeNode* root) {
|
||||
@ -337,6 +337,8 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
**递归** - 利用BST中序遍历特性,把树"压缩"成数组
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
@ -344,29 +346,56 @@ Python:
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
# 递归法
|
||||
class Solution:
|
||||
def isValidBST(self, root: TreeNode) -> bool:
|
||||
res = [] //把二叉搜索树按中序遍历写成list
|
||||
def buildalist(root):
|
||||
if not root: return
|
||||
buildalist(root.left) //左
|
||||
res.append(root.val) //中
|
||||
buildalist(root.right) //右
|
||||
return res
|
||||
buildalist(root)
|
||||
return res == sorted(res) and len(set(res)) == len(res) //检查list里的数有没有重复元素,以及是否按从小到大排列
|
||||
# 思路: 利用BST中序遍历的特性.
|
||||
# 中序遍历输出的二叉搜索树节点的数值是有序序列
|
||||
candidate_list = []
|
||||
|
||||
def __traverse(root: TreeNode) -> None:
|
||||
nonlocal candidate_list
|
||||
if not root:
|
||||
return
|
||||
__traverse(root.left)
|
||||
candidate_list.append(root.val)
|
||||
__traverse(root.right)
|
||||
|
||||
def __is_sorted(nums: list) -> bool:
|
||||
for i in range(1, len(nums)):
|
||||
if nums[i] <= nums[i - 1]: # ⚠️ 注意: Leetcode定义二叉搜索树中不能有重复元素
|
||||
return False
|
||||
return True
|
||||
|
||||
__traverse(root)
|
||||
res = __is_sorted(candidate_list)
|
||||
|
||||
return res
|
||||
```
|
||||
|
||||
# 简单递归法
|
||||
**递归** - 标准做法
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def isValidBST(self, root: TreeNode) -> bool:
|
||||
def isBST(root, min_val, max_val):
|
||||
if not root: return True
|
||||
if root.val >= max_val or root.val <= min_val:
|
||||
# 规律: BST的中序遍历节点数值是从小到大.
|
||||
cur_max = -float("INF")
|
||||
def __isValidBST(root: TreeNode) -> bool:
|
||||
nonlocal cur_max
|
||||
|
||||
if not root:
|
||||
return True
|
||||
|
||||
is_left_valid = __isValidBST(root.left)
|
||||
if cur_max < root.val:
|
||||
cur_max = root.val
|
||||
else:
|
||||
return False
|
||||
return isBST(root.left, min_val, root.val) and isBST(root.right, root.val, max_val)
|
||||
return isBST(root, float("-inf"), float("inf"))
|
||||
|
||||
is_right_valid = __isValidBST(root.right)
|
||||
|
||||
return is_left_valid and is_right_valid
|
||||
return __isValidBST(root)
|
||||
```
|
||||
```
|
||||
# 迭代-中序遍历
|
||||
class Solution:
|
||||
def isValidBST(self, root: TreeNode) -> bool:
|
||||
@ -504,4 +533,4 @@ var isValidBST = function (root) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -61,7 +61,7 @@ bool compare(TreeNode* tree1, TreeNode* tree2)
|
||||
此时tree1、tree2节点不为空,且数值也不相同的情况我们也处理了。
|
||||
|
||||
代码如下:
|
||||
```C++
|
||||
```CPP
|
||||
if (tree1 == NULL && tree2 != NULL) return false;
|
||||
else if (tree1 != NULL && tree2 == NULL) return false;
|
||||
else if (tree1 == NULL && tree2 == NULL) return true;
|
||||
@ -77,7 +77,7 @@ else if (tree1->val != tree2->val) return false; // 注意这里我没有
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
bool left = compare(tree1->left, tree2->left); // 左子树:左、 右子树:左
|
||||
bool right = compare(tree1->right, tree2->right); // 左子树:右、 右子树:右
|
||||
bool isSame = left && right; // 左子树:中、 右子树:中(逻辑处理)
|
||||
@ -85,7 +85,7 @@ return isSame;
|
||||
```
|
||||
最后递归的C++整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool compare(TreeNode* tree1, TreeNode* tree2) {
|
||||
@ -119,7 +119,7 @@ public:
|
||||
|
||||
## 递归
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool compare(TreeNode* left, TreeNode* right) {
|
||||
@ -138,30 +138,30 @@ public:
|
||||
|
||||
## 迭代法
|
||||
|
||||
```C++
|
||||
lass Solution {
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
|
||||
bool isSameTree(TreeNode* p, TreeNode* q) {
|
||||
if (p == NULL && q == NULL) return true;
|
||||
if (p == NULL || q == NULL) return false;
|
||||
queue<TreeNode*> que;
|
||||
que.push(p); //
|
||||
que.push(q); //
|
||||
que.push(p); // 添加根节点p
|
||||
que.push(q); // 添加根节点q
|
||||
while (!que.empty()) { //
|
||||
TreeNode* leftNode = que.front(); que.pop();
|
||||
TreeNode* rightNode = que.front(); que.pop();
|
||||
if (!leftNode && !rightNode) { //
|
||||
if (!leftNode && !rightNode) { // 若p的节点与q的节点都为空
|
||||
continue;
|
||||
}
|
||||
//
|
||||
// 若p的节点与q的节点有一个为空或p的节点的值与q节点不同
|
||||
if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {
|
||||
return false;
|
||||
}
|
||||
que.push(leftNode->left); //
|
||||
que.push(rightNode->left); //
|
||||
que.push(leftNode->right); //
|
||||
que.push(rightNode->right); //
|
||||
que.push(leftNode->left); // 添加p节点的左子树节点
|
||||
que.push(rightNode->left); // 添加q节点的左子树节点
|
||||
que.push(leftNode->right); // 添加p节点的右子树节点
|
||||
que.push(rightNode->right); // 添加q节点的右子树节点
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -172,8 +172,72 @@ public:
|
||||
|
||||
Java:
|
||||
|
||||
Python:
|
||||
```java
|
||||
// 递归法
|
||||
class Solution {
|
||||
public boolean isSameTree(TreeNode p, TreeNode q) {
|
||||
if (p == null && q == null) return true;
|
||||
else if (q == null || p == null) return false;
|
||||
else if (q.val != p.val) return false;
|
||||
return isSameTree(q.left, p.left) && isSameTree(q.right, p.right);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 迭代法
|
||||
class Solution {
|
||||
public boolean isSameTree(TreeNode p, TreeNode q) {
|
||||
if(p == null && q == null) return true;
|
||||
if(p == null || q == null) return false;
|
||||
Queue<TreeNode> que= new LinkedList<TreeNode>();
|
||||
que.offer(p);
|
||||
que.offer(q);
|
||||
while(!que.isEmpty()){
|
||||
TreeNode leftNode = que.poll();
|
||||
TreeNode rightNode = que.poll();
|
||||
if(leftNode == null && rightNode == null) continue;
|
||||
if(leftNode == null || rightNode== null || leftNode.val != rightNode.val) return false;
|
||||
que.offer(leftNode.left);
|
||||
que.offer(rightNode.left);
|
||||
que.offer(leftNode.right);
|
||||
que.offer(rightNode.right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
Python:
|
||||
```python
|
||||
# 递归法
|
||||
class Solution:
|
||||
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
|
||||
if not p and not q: return True
|
||||
elif not p or not q: return False
|
||||
elif p.val != q.val: return False
|
||||
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
|
||||
```
|
||||
|
||||
```python
|
||||
# 迭代法
|
||||
class Solution:
|
||||
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
|
||||
if not p and not q: return True
|
||||
if not p or not q: return False
|
||||
que = collections.deque()
|
||||
que.append(p)
|
||||
que.append(q)
|
||||
while que:
|
||||
leftNode = que.popleft()
|
||||
rightNode = que.popleft()
|
||||
if not leftNode and not rightNode: continue
|
||||
if not leftNode or not rightNode or leftNode.val != rightNode.val: return False
|
||||
que.append(leftNode.left)
|
||||
que.append(rightNode.left)
|
||||
que.append(leftNode.right)
|
||||
que.append(rightNode.right)
|
||||
return True
|
||||
```
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
@ -182,5 +246,5 @@ JavaScript:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -73,7 +73,7 @@ bool compare(TreeNode* left, TreeNode* right)
|
||||
此时左右节点不为空,且数值也不相同的情况我们也处理了。
|
||||
|
||||
代码如下:
|
||||
```C++
|
||||
```CPP
|
||||
if (left == NULL && right != NULL) return false;
|
||||
else if (left != NULL && right == NULL) return false;
|
||||
else if (left == NULL && right == NULL) return true;
|
||||
@ -93,7 +93,7 @@ else if (left->val != right->val) return false; // 注意这里我没有
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
|
||||
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
|
||||
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
|
||||
@ -104,7 +104,7 @@ return isSame;
|
||||
|
||||
最后递归的C++整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool compare(TreeNode* left, TreeNode* right) {
|
||||
@ -137,7 +137,7 @@ public:
|
||||
**盲目的照着抄,结果就是:发现这是一道“简单题”,稀里糊涂的就过了,但是真正的每一步判断逻辑未必想到清楚。**
|
||||
|
||||
当然我可以把如上代码整理如下:
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool compare(TreeNode* left, TreeNode* right) {
|
||||
@ -177,7 +177,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isSymmetric(TreeNode* root) {
|
||||
@ -212,7 +212,7 @@ public:
|
||||
|
||||
只要把队列原封不动的改成栈就可以了,我下面也给出了代码。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isSymmetric(TreeNode* root) {
|
||||
@ -251,6 +251,8 @@ public:
|
||||
|
||||
# 相关题目推荐
|
||||
|
||||
这两道题目基本和本题是一样的,只要稍加修改就可以AC。
|
||||
|
||||
* 100.相同的树
|
||||
* 572.另一个树的子树
|
||||
|
||||
@ -579,4 +581,4 @@ var isSymmetric = function(root) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -6,11 +6,8 @@
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
# 二叉树的层序遍历
|
||||
|
||||
看完这篇文章虽然不能打十个,但是可以迅速打八个!而且够快!
|
||||
|
||||
学会二叉树的层序遍历,可以一口气撸完leetcode上八道题目:
|
||||
学会二叉树的层序遍历,可以一口气打完以下十题:
|
||||
|
||||
* 102.二叉树的层序遍历
|
||||
* 107.二叉树的层次遍历II
|
||||
@ -20,9 +17,16 @@
|
||||
* 515.在每个树行中找最大值
|
||||
* 116.填充每个节点的下一个右侧节点指针
|
||||
* 117.填充每个节点的下一个右侧节点指针II
|
||||
* 104.二叉树的最大深度
|
||||
* 111.二叉树的最小深度
|
||||
|
||||
在之前写过这篇文章 [二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog),可惜当时只打了5个,还不够,再给我一次机会,我打十个!
|
||||
|
||||

|
||||
|
||||
|
||||
## 102.二叉树的层序遍历
|
||||
|
||||
# 102.二叉树的层序遍历
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
|
||||
|
||||
@ -38,7 +42,6 @@
|
||||
* [二叉树:前中后序迭代法](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)
|
||||
* [二叉树:前中后序迭代方式统一写法](https://mp.weixin.qq.com/s/ATQMPCpBlaAgrqdLDMVPZA)
|
||||
|
||||
|
||||
接下来我们再来介绍二叉树的另一种遍历方式:层序遍历。
|
||||
|
||||
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。
|
||||
@ -53,11 +56,11 @@
|
||||
|
||||
这样就实现了层序从左到右遍历二叉树。
|
||||
|
||||
代码如下:**这份代码也可以作为二叉树层序遍历的模板,以后再打七个就靠它了**。
|
||||
代码如下:**这份代码也可以作为二叉树层序遍历的模板,打十个就靠它了**。
|
||||
|
||||
C++代码:
|
||||
|
||||
```
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> levelOrder(TreeNode* root) {
|
||||
@ -225,9 +228,10 @@ var levelOrder = function(root) {
|
||||
|
||||
```
|
||||
|
||||
**此时我们就掌握了二叉树的层序遍历了,那么如下五道leetcode上的题目,只需要修改模板的一两行代码(不能再多了),便可打倒!**
|
||||
**此时我们就掌握了二叉树的层序遍历了,那么如下九道力扣上的题目,只需要修改模板的两三行代码(不能再多了),便可打倒!**
|
||||
|
||||
## 107.二叉树的层次遍历 II
|
||||
|
||||
# 107.二叉树的层次遍历 II
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/
|
||||
|
||||
@ -241,7 +245,7 @@ var levelOrder = function(root) {
|
||||
|
||||
C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> levelOrderBottom(TreeNode* root) {
|
||||
@ -404,7 +408,7 @@ var levelOrderBottom = function(root) {
|
||||
```
|
||||
|
||||
|
||||
## 199.二叉树的右视图
|
||||
# 199.二叉树的右视图
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/binary-tree-right-side-view/
|
||||
|
||||
@ -418,7 +422,7 @@ var levelOrderBottom = function(root) {
|
||||
|
||||
C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<int> rightSideView(TreeNode* root) {
|
||||
@ -581,7 +585,7 @@ var rightSideView = function(root) {
|
||||
};
|
||||
```
|
||||
|
||||
## 637.二叉树的层平均值
|
||||
# 637.二叉树的层平均值
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/
|
||||
|
||||
@ -595,7 +599,7 @@ var rightSideView = function(root) {
|
||||
|
||||
C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<double> averageOfLevels(TreeNode* root) {
|
||||
@ -765,7 +769,7 @@ var averageOfLevels = function(root) {
|
||||
};
|
||||
```
|
||||
|
||||
## 429.N叉树的层序遍历
|
||||
# 429.N叉树的层序遍历
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/
|
||||
|
||||
@ -790,7 +794,7 @@ var averageOfLevels = function(root) {
|
||||
|
||||
C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<vector<int>> levelOrder(Node* root) {
|
||||
@ -985,7 +989,7 @@ var levelOrder = function(root) {
|
||||
};
|
||||
```
|
||||
|
||||
## 515.在每个树行中找最大值
|
||||
# 515.在每个树行中找最大值
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/
|
||||
|
||||
@ -999,7 +1003,7 @@ var levelOrder = function(root) {
|
||||
|
||||
C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<int> largestValues(TreeNode* root) {
|
||||
@ -1042,6 +1046,31 @@ class Solution:
|
||||
out_list.append(max(in_list))
|
||||
return out_list
|
||||
```
|
||||
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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
go:
|
||||
|
||||
@ -1115,7 +1144,7 @@ var largestValues = function(root) {
|
||||
};
|
||||
```
|
||||
|
||||
## 116.填充每个节点的下一个右侧节点指针
|
||||
# 116.填充每个节点的下一个右侧节点指针
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
|
||||
|
||||
@ -1143,7 +1172,7 @@ struct Node {
|
||||
|
||||
C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
Node* connect(Node* root) {
|
||||
@ -1176,6 +1205,35 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
java代码:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public Node connect(Node root) {
|
||||
Queue<Node> tmpQueue = new LinkedList<Node>();
|
||||
if (root != null) tmpQueue.add(root);
|
||||
|
||||
while (tmpQueue.size() != 0){
|
||||
int size = tmpQueue.size();
|
||||
|
||||
Node cur = tmpQueue.poll();
|
||||
if (cur.left != null) tmpQueue.add(cur.left);
|
||||
if (cur.right != null) tmpQueue.add(cur.right);
|
||||
|
||||
for (int index = 1; index < size; index++){
|
||||
Node next = tmpQueue.poll();
|
||||
if (next.left != null) tmpQueue.add(next.left);
|
||||
if (next.right != null) tmpQueue.add(next.right);
|
||||
|
||||
cur.next = next;
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
python代码:
|
||||
|
||||
@ -1254,7 +1312,8 @@ func connect(root *Node) *Node {
|
||||
}
|
||||
```
|
||||
|
||||
## 117.填充每个节点的下一个右侧节点指针II
|
||||
# 117.填充每个节点的下一个右侧节点指针II
|
||||
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/
|
||||
|
||||
@ -1264,7 +1323,7 @@ func connect(root *Node) *Node {
|
||||
|
||||
C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
Node* connect(Node* root) {
|
||||
@ -1295,6 +1354,44 @@ public:
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Java 代码:
|
||||
|
||||
```java
|
||||
// 二叉树之层次遍历
|
||||
class Solution {
|
||||
public Node connect(Node root) {
|
||||
Queue<Node> queue = new LinkedList<>();
|
||||
if (root != null) {
|
||||
queue.add(root);
|
||||
}
|
||||
while (!queue.isEmpty()) {
|
||||
int size = queue.size();
|
||||
Node node = null;
|
||||
Node nodePre = null;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i == 0) {
|
||||
nodePre = queue.poll(); // 取出本层头一个节点
|
||||
node = nodePre;
|
||||
} else {
|
||||
node = queue.poll();
|
||||
nodePre.next = node; // 本层前一个节点 next 指向当前节点
|
||||
nodePre = nodePre.next;
|
||||
}
|
||||
if (node.left != null) {
|
||||
queue.add(node.left);
|
||||
}
|
||||
if (node.right != null) {
|
||||
queue.add(node.right);
|
||||
}
|
||||
}
|
||||
nodePre.next = null; // 本层最后一个节点 next 指向 null
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
```
|
||||
python代码:
|
||||
|
||||
```python
|
||||
@ -1378,12 +1475,119 @@ func connect(root *Node) *Node {
|
||||
return root
|
||||
}
|
||||
```
|
||||
# 104.二叉树的最大深度
|
||||
|
||||
## 总结
|
||||
题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
|
||||
|
||||
二叉树的层序遍历,就是图论中的广度优先搜索在二叉树中的应用,需要借助队列来实现(此时是不是又发现队列的应用了)。
|
||||
给定一个二叉树,找出其最大深度。
|
||||
|
||||
虽然不能一口气打十个,打八个也还行。
|
||||
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
|
||||
|
||||
说明: 叶子节点是指没有子节点的节点。
|
||||
|
||||
示例:
|
||||
|
||||
给定二叉树 [3,9,20,null,null,15,7],
|
||||
|
||||

|
||||
|
||||
返回它的最大深度 3 。
|
||||
|
||||
思路:
|
||||
|
||||
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
|
||||
|
||||
在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示:
|
||||
|
||||

|
||||
|
||||
所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxDepth(TreeNode* root) {
|
||||
if (root == NULL) return 0;
|
||||
int depth = 0;
|
||||
queue<TreeNode*> que;
|
||||
que.push(root);
|
||||
while(!que.empty()) {
|
||||
int size = que.size();
|
||||
depth++; // 记录深度
|
||||
for (int i = 0; i < size; i++) {
|
||||
TreeNode* node = que.front();
|
||||
que.pop();
|
||||
if (node->left) que.push(node->left);
|
||||
if (node->right) que.push(node->right);
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Java:
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
|
||||
# 111.二叉树的最小深度
|
||||
|
||||
相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
|
||||
|
||||
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
|
||||
|
||||
代码如下:(详细注释)
|
||||
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int minDepth(TreeNode* root) {
|
||||
if (root == NULL) return 0;
|
||||
int depth = 0;
|
||||
queue<TreeNode*> que;
|
||||
que.push(root);
|
||||
while(!que.empty()) {
|
||||
int size = que.size();
|
||||
depth++; // 记录最小深度
|
||||
for (int i = 0; i < size; i++) {
|
||||
TreeNode* node = que.front();
|
||||
que.pop();
|
||||
if (node->left) que.push(node->left);
|
||||
if (node->right) que.push(node->right);
|
||||
if (!node->left && !node->right) { // 当左右孩子都为空的时候,说明是最低点的一层了,退出
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Java:
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
|
||||
|
||||
|
||||
|
||||
# 总结
|
||||
|
||||
二叉树的层序遍历,**就是图论中的广度优先搜索在二叉树中的应用**,需要借助队列来实现(此时又发现队列的一个应用了)。
|
||||
|
||||
来吧,一口气打十个:
|
||||
|
||||
* 102.二叉树的层序遍历
|
||||
* 107.二叉树的层次遍历II
|
||||
@ -1393,289 +1597,15 @@ func connect(root *Node) *Node {
|
||||
* 515.在每个树行中找最大值
|
||||
* 116.填充每个节点的下一个右侧节点指针
|
||||
* 117.填充每个节点的下一个右侧节点指针II
|
||||
* 104.二叉树的最大深度
|
||||
* 111.二叉树的最小深度
|
||||
|
||||
如果非要打十个,还得找叶师傅!
|
||||
|
||||

|
||||
**致敬叶师傅!**
|
||||
|
||||
|
||||
|
||||
|
||||
# 其他语言版本
|
||||
|
||||
|
||||
> 二叉树的层序遍历(Javascript语言完全版) (迭代 + 递归)
|
||||
|
||||
```js
|
||||
/**
|
||||
* 102. 二叉树的层序遍历
|
||||
* @param {TreeNode} root
|
||||
* @return {number[][]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
|
||||
var levelOrder = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
const val = [];
|
||||
while(len--) {
|
||||
const node = queue.shift();
|
||||
val.push(node.val);
|
||||
node.left && queue.push(node.left);
|
||||
node.right && queue.push(node.right);
|
||||
}
|
||||
res.push(val);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var levelOrder = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(!res[i]) res[i] = [];
|
||||
res[i].push(root.val)
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 107. 二叉树的层序遍历 II
|
||||
* @param {TreeNode} root
|
||||
* @return {number[][]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
|
||||
var levelOrderBottom = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
const val = [];
|
||||
while(len--) {
|
||||
const node = queue.shift();
|
||||
val.push(node.val);
|
||||
node.left && queue.push(node.left);
|
||||
node.right && queue.push(node.right);
|
||||
}
|
||||
res.push(val);
|
||||
}
|
||||
return res.reverse()
|
||||
};
|
||||
|
||||
// 递归
|
||||
|
||||
var levelOrderBottom = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(!res[i]) res[i] = [];
|
||||
res[i].push(root.val);
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res.reverse();
|
||||
};
|
||||
|
||||
/**
|
||||
* 199. 二叉树的右视图
|
||||
* @param {TreeNode} root
|
||||
* @return {number[]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
|
||||
var rightSideView = function(root) {
|
||||
const res = [], queue = [];
|
||||
root && queue.push(root);
|
||||
while(l = queue.length) {
|
||||
while (l--) {
|
||||
const {val, left, right} = queue.shift();
|
||||
!l && res.push(val);
|
||||
left && queue.push(left);
|
||||
right && queue.push(right);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var rightSideView = function(root) {
|
||||
const res = [];
|
||||
function defs(root, i) {
|
||||
if(!root) return;
|
||||
res[i] = root.val;
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* 637. 二叉树的层平均值
|
||||
* @param {TreeNode} root
|
||||
* @return {number[]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
var averageOfLevels = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
let sum = 0, l = len;
|
||||
while(l--) {
|
||||
const {val, left, right} = queue.shift();
|
||||
sum += val;
|
||||
left && queue.push(left);
|
||||
right && queue.push(right);
|
||||
}
|
||||
res.push(sum/len);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var averageOfLevels = function(root) {
|
||||
const resCount = [], res = [];
|
||||
function defs(root, i) {
|
||||
if(!root) return;
|
||||
if(isNaN(res[i])) resCount[i] = res[i] = 0;
|
||||
res[i] += root.val;
|
||||
resCount[i]++;
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res.map((val, i) => val / resCount[i]);
|
||||
};
|
||||
|
||||
/**
|
||||
* 515. 在每个树行中找最大值
|
||||
* @param {TreeNode} root
|
||||
* @return {number[]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
const MIN_G = Number.MIN_SAFE_INTEGER;
|
||||
var largestValues = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
let max = MIN_G;
|
||||
while(len--) {
|
||||
const {val, left, right} = queue.shift();
|
||||
max = max > val ? max : val;
|
||||
left && queue.push(left);
|
||||
right && queue.push(right);
|
||||
}
|
||||
res.push(max);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var largestValues = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(isNaN(res[i])) res[i] = root.val;
|
||||
res[i] = res[i] > root.val ? res[i] : root.val;
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* 429. N 叉树的层序遍历
|
||||
* @param {Node|null} root
|
||||
* @return {number[][]}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
var levelOrder = function(root) {
|
||||
const queue = [], res = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
const vals = [];
|
||||
while(len--) {
|
||||
const {val, children} = queue.shift();
|
||||
vals.push(val);
|
||||
for(const e of children) {
|
||||
queue.push(e);
|
||||
}
|
||||
}
|
||||
res.push(vals);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
// 递归
|
||||
|
||||
var levelOrder = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(!res[i]) res[i] = [];
|
||||
res[i].push(root.val);
|
||||
for(const e of root.children) {
|
||||
defs(e, i + 1);
|
||||
}
|
||||
}
|
||||
defs(root, 0);
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* 116. 填充每个节点的下一个右侧节点指针
|
||||
* 117. 填充每个节点的下一个右侧节点指针 II
|
||||
* @param {Node} root
|
||||
* @return {Node}
|
||||
*/
|
||||
|
||||
// 迭代
|
||||
var connect = function(root) {
|
||||
const queue = [];
|
||||
root && queue.push(root);
|
||||
while(len = queue.length) {
|
||||
while(len--) {
|
||||
const node1 = queue.shift(),
|
||||
node2 = len ? queue[0] : null;
|
||||
node1.next = node2;
|
||||
node1.left && queue.push(node1.left);
|
||||
node1.right && queue.push(node1.right);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
};
|
||||
|
||||
// 递归
|
||||
var connect = function(root) {
|
||||
const res = [];
|
||||
function defs (root, i) {
|
||||
if(!root) return;
|
||||
if(res[i]) res[i].next = root;
|
||||
res[i] = root;
|
||||
root.left && defs(root.left, i + 1);
|
||||
root.right && defs(root.right, i + 1);
|
||||
}
|
||||
defs(root, 0);
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -8,10 +8,11 @@
|
||||
|
||||
|
||||
看完本篇可以一起做了如下两道题目:
|
||||
* 104.二叉树的最大深度
|
||||
* 559.N叉树的最大深度
|
||||
|
||||
## 104.二叉树的最大深度
|
||||
* 104.二叉树的最大深度
|
||||
* 559.n叉树的最大深度
|
||||
|
||||
# 104.二叉树的最大深度
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
|
||||
|
||||
@ -28,7 +29,7 @@
|
||||
|
||||
返回它的最大深度 3 。
|
||||
|
||||
### 递归法
|
||||
## 递归法
|
||||
|
||||
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
|
||||
|
||||
@ -41,53 +42,53 @@
|
||||
1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
|
||||
|
||||
代码如下:
|
||||
```
|
||||
int getDepth(TreeNode* node)
|
||||
```c++
|
||||
int getdepth(treenode* node)
|
||||
```
|
||||
|
||||
2. 确定终止条件:如果为空节点的话,就返回0,表示高度为0。
|
||||
|
||||
代码如下:
|
||||
```
|
||||
if (node == NULL) return 0;
|
||||
```c++
|
||||
if (node == null) return 0;
|
||||
```
|
||||
|
||||
3. 确定单层递归的逻辑:先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
|
||||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
int leftDepth = getDepth(node->left); // 左
|
||||
int rightDepth = getDepth(node->right); // 右
|
||||
int depth = 1 + max(leftDepth, rightDepth); // 中
|
||||
```c++
|
||||
int leftdepth = getdepth(node->left); // 左
|
||||
int rightdepth = getdepth(node->right); // 右
|
||||
int depth = 1 + max(leftdepth, rightdepth); // 中
|
||||
return depth;
|
||||
```
|
||||
|
||||
所以整体C++代码如下:
|
||||
所以整体c++代码如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```c++
|
||||
class solution {
|
||||
public:
|
||||
int getDepth(TreeNode* node) {
|
||||
if (node == NULL) return 0;
|
||||
int leftDepth = getDepth(node->left); // 左
|
||||
int rightDepth = getDepth(node->right); // 右
|
||||
int depth = 1 + max(leftDepth, rightDepth); // 中
|
||||
int getdepth(treenode* node) {
|
||||
if (node == null) return 0;
|
||||
int leftdepth = getdepth(node->left); // 左
|
||||
int rightdepth = getdepth(node->right); // 右
|
||||
int depth = 1 + max(leftdepth, rightdepth); // 中
|
||||
return depth;
|
||||
}
|
||||
int maxDepth(TreeNode* root) {
|
||||
return getDepth(root);
|
||||
int maxdepth(treenode* root) {
|
||||
return getdepth(root);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
代码精简之后C++代码如下:
|
||||
```C++
|
||||
class Solution {
|
||||
代码精简之后c++代码如下:
|
||||
```c++
|
||||
class solution {
|
||||
public:
|
||||
int maxDepth(TreeNode* root) {
|
||||
if (root == NULL) return 0;
|
||||
return 1 + max(maxDepth(root->left), maxDepth(root->right));
|
||||
int maxdepth(treenode* root) {
|
||||
if (root == null) return 0;
|
||||
return 1 + max(maxdepth(root->left), maxdepth(root->right));
|
||||
}
|
||||
};
|
||||
|
||||
@ -98,31 +99,31 @@ public:
|
||||
|
||||
本题当然也可以使用前序,代码如下:(**充分表现出求深度回溯的过程**)
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```c++
|
||||
class solution {
|
||||
public:
|
||||
int result;
|
||||
void getDepth(TreeNode* node, int depth) {
|
||||
void getdepth(treenode* node, int depth) {
|
||||
result = depth > result ? depth : result; // 中
|
||||
|
||||
if (node->left == NULL && node->right == NULL) return ;
|
||||
if (node->left == null && node->right == null) return ;
|
||||
|
||||
if (node->left) { // 左
|
||||
depth++; // 深度+1
|
||||
getDepth(node->left, depth);
|
||||
getdepth(node->left, depth);
|
||||
depth--; // 回溯,深度-1
|
||||
}
|
||||
if (node->right) { // 右
|
||||
depth++; // 深度+1
|
||||
getDepth(node->right, depth);
|
||||
getdepth(node->right, depth);
|
||||
depth--; // 回溯,深度-1
|
||||
}
|
||||
return ;
|
||||
}
|
||||
int maxDepth(TreeNode* root) {
|
||||
int maxdepth(treenode* root) {
|
||||
result = 0;
|
||||
if (root == 0) return result;
|
||||
getDepth(root, 1);
|
||||
getdepth(root, 1);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@ -132,31 +133,31 @@ public:
|
||||
|
||||
注意以上代码是为了把细节体现出来,简化一下代码如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```c++
|
||||
class solution {
|
||||
public:
|
||||
int result;
|
||||
void getDepth(TreeNode* node, int depth) {
|
||||
void getdepth(treenode* node, int depth) {
|
||||
result = depth > result ? depth : result; // 中
|
||||
if (node->left == NULL && node->right == NULL) return ;
|
||||
if (node->left == null && node->right == null) return ;
|
||||
if (node->left) { // 左
|
||||
getDepth(node->left, depth + 1);
|
||||
getdepth(node->left, depth + 1);
|
||||
}
|
||||
if (node->right) { // 右
|
||||
getDepth(node->right, depth + 1);
|
||||
getdepth(node->right, depth + 1);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
int maxDepth(TreeNode* root) {
|
||||
int maxdepth(treenode* root) {
|
||||
result = 0;
|
||||
if (root == 0) return result;
|
||||
getDepth(root, 1);
|
||||
getdepth(root, 1);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 迭代法
|
||||
## 迭代法
|
||||
|
||||
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
|
||||
|
||||
@ -166,23 +167,23 @@ public:
|
||||
|
||||
所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。
|
||||
|
||||
如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)
|
||||
如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)
|
||||
|
||||
C++代码如下:
|
||||
c++代码如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```c++
|
||||
class solution {
|
||||
public:
|
||||
int maxDepth(TreeNode* root) {
|
||||
if (root == NULL) return 0;
|
||||
int maxdepth(treenode* root) {
|
||||
if (root == null) return 0;
|
||||
int depth = 0;
|
||||
queue<TreeNode*> que;
|
||||
queue<treenode*> que;
|
||||
que.push(root);
|
||||
while(!que.empty()) {
|
||||
int size = que.size();
|
||||
depth++; // 记录深度
|
||||
for (int i = 0; i < size; i++) {
|
||||
TreeNode* node = que.front();
|
||||
treenode* node = que.front();
|
||||
que.pop();
|
||||
if (node->left) que.push(node->left);
|
||||
if (node->right) que.push(node->right);
|
||||
@ -193,19 +194,19 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
那么我们可以顺便解决一下N叉树的最大深度问题
|
||||
那么我们可以顺便解决一下n叉树的最大深度问题
|
||||
|
||||
## 559.N叉树的最大深度
|
||||
# 559.n叉树的最大深度
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/maximum-depth-of-n-ary-tree/
|
||||
|
||||
给定一个 N 叉树,找到其最大深度。
|
||||
给定一个 n 叉树,找到其最大深度。
|
||||
|
||||
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
|
||||
|
||||
例如,给定一个 3叉树 :
|
||||
|
||||

|
||||

|
||||
|
||||
我们应返回其最大深度,3。
|
||||
|
||||
@ -213,39 +214,39 @@ public:
|
||||
|
||||
依然可以提供递归法和迭代法,来解决这个问题,思路是和二叉树思路一样的,直接给出代码如下:
|
||||
|
||||
### 递归法
|
||||
## 递归法
|
||||
|
||||
C++代码:
|
||||
c++代码:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```c++
|
||||
class solution {
|
||||
public:
|
||||
int maxDepth(Node* root) {
|
||||
int maxdepth(node* root) {
|
||||
if (root == 0) return 0;
|
||||
int depth = 0;
|
||||
for (int i = 0; i < root->children.size(); i++) {
|
||||
depth = max (depth, maxDepth(root->children[i]));
|
||||
depth = max (depth, maxdepth(root->children[i]));
|
||||
}
|
||||
return depth + 1;
|
||||
}
|
||||
};
|
||||
```
|
||||
### 迭代法
|
||||
## 迭代法
|
||||
|
||||
依然是层序遍历,代码如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```c++
|
||||
class solution {
|
||||
public:
|
||||
int maxDepth(Node* root) {
|
||||
queue<Node*> que;
|
||||
if (root != NULL) que.push(root);
|
||||
int maxdepth(node* root) {
|
||||
queue<node*> que;
|
||||
if (root != null) que.push(root);
|
||||
int depth = 0;
|
||||
while (!que.empty()) {
|
||||
int size = que.size();
|
||||
depth++; // 记录深度
|
||||
for (int i = 0; i < size; i++) {
|
||||
Node* node = que.front();
|
||||
node* node = que.front();
|
||||
que.pop();
|
||||
for (int j = 0; j < node->children.size(); j++) {
|
||||
if (node->children[j]) que.push(node->children[j]);
|
||||
@ -257,45 +258,46 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
## 其他语言版本
|
||||
# 其他语言版本
|
||||
|
||||
## java
|
||||
|
||||
Java:
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
```Java
|
||||
class Solution {
|
||||
```java
|
||||
class solution {
|
||||
/**
|
||||
* 递归法
|
||||
*/
|
||||
public int maxDepth(TreeNode root) {
|
||||
public int maxdepth(treenode root) {
|
||||
if (root == null) {
|
||||
return 0;
|
||||
}
|
||||
int leftDepth = maxDepth(root.left);
|
||||
int rightDepth = maxDepth(root.right);
|
||||
return Math.max(leftDepth, rightDepth) + 1;
|
||||
int leftdepth = maxdepth(root.left);
|
||||
int rightdepth = maxdepth(root.right);
|
||||
return math.max(leftdepth, rightdepth) + 1;
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```Java
|
||||
class Solution {
|
||||
```java
|
||||
class solution {
|
||||
/**
|
||||
* 迭代法,使用层序遍历
|
||||
*/
|
||||
public int maxDepth(TreeNode root) {
|
||||
public int maxdepth(treenode root) {
|
||||
if(root == null) {
|
||||
return 0;
|
||||
}
|
||||
Deque<TreeNode> deque = new LinkedList<>();
|
||||
deque<treenode> deque = new linkedlist<>();
|
||||
deque.offer(root);
|
||||
int depth = 0;
|
||||
while (!deque.isEmpty()) {
|
||||
while (!deque.isempty()) {
|
||||
int size = deque.size();
|
||||
depth++;
|
||||
for (int i = 0; i < size; i++) {
|
||||
TreeNode poll = deque.poll();
|
||||
treenode poll = deque.poll();
|
||||
if (poll.left != null) {
|
||||
deque.offer(poll.left);
|
||||
}
|
||||
@ -309,37 +311,39 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
## python
|
||||
|
||||
104.二叉树的最大深度
|
||||
> 递归法:
|
||||
### 104.二叉树的最大深度
|
||||
|
||||
递归法:
|
||||
```python
|
||||
class Solution:
|
||||
def maxDepth(self, root: TreeNode) -> int:
|
||||
return self.getDepth(root)
|
||||
class solution:
|
||||
def maxdepth(self, root: treenode) -> int:
|
||||
return self.getdepth(root)
|
||||
|
||||
def getDepth(self, node):
|
||||
def getdepth(self, node):
|
||||
if not node:
|
||||
return 0
|
||||
leftDepth = self.getDepth(node.left) #左
|
||||
rightDepth = self.getDepth(node.right) #右
|
||||
depth = 1 + max(leftDepth, rightDepth) #中
|
||||
leftdepth = self.getdepth(node.left) #左
|
||||
rightdepth = self.getdepth(node.right) #右
|
||||
depth = 1 + max(leftdepth, rightdepth) #中
|
||||
return depth
|
||||
```
|
||||
> 递归法;精简代码
|
||||
|
||||
递归法:精简代码
|
||||
```python
|
||||
class Solution:
|
||||
def maxDepth(self, root: TreeNode) -> int:
|
||||
class solution:
|
||||
def maxdepth(self, root: treenode) -> int:
|
||||
if not root:
|
||||
return 0
|
||||
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
|
||||
return 1 + max(self.maxdepth(root.left), self.maxdepth(root.right))
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
迭代法:
|
||||
```python
|
||||
import collections
|
||||
class Solution:
|
||||
def maxDepth(self, root: TreeNode) -> int:
|
||||
class solution:
|
||||
def maxdepth(self, root: treenode) -> int:
|
||||
if not root:
|
||||
return 0
|
||||
depth = 0 #记录深度
|
||||
@ -357,24 +361,25 @@ class Solution:
|
||||
return depth
|
||||
```
|
||||
|
||||
559.N叉树的最大深度
|
||||
> 递归法:
|
||||
### 559.n叉树的最大深度
|
||||
|
||||
递归法:
|
||||
```python
|
||||
class Solution:
|
||||
def maxDepth(self, root: 'Node') -> int:
|
||||
class solution:
|
||||
def maxdepth(self, root: 'node') -> int:
|
||||
if not root:
|
||||
return 0
|
||||
depth = 0
|
||||
for i in range(len(root.children)):
|
||||
depth = max(depth, self.maxDepth(root.children[i]))
|
||||
depth = max(depth, self.maxdepth(root.children[i]))
|
||||
return depth + 1
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
迭代法:
|
||||
```python
|
||||
import collections
|
||||
class Solution:
|
||||
def maxDepth(self, root: 'Node') -> int:
|
||||
class solution:
|
||||
def maxdepth(self, root: 'node') -> int:
|
||||
queue = collections.deque()
|
||||
if root:
|
||||
queue.append(root)
|
||||
@ -390,10 +395,10 @@ class Solution:
|
||||
return depth
|
||||
```
|
||||
|
||||
> 使用栈来模拟后序遍历依然可以
|
||||
使用栈来模拟后序遍历依然可以
|
||||
```python
|
||||
class Solution:
|
||||
def maxDepth(self, root: 'Node') -> int:
|
||||
class solution:
|
||||
def maxdepth(self, root: 'node') -> int:
|
||||
st = []
|
||||
if root:
|
||||
st.append(root)
|
||||
@ -401,9 +406,9 @@ class Solution:
|
||||
result = 0
|
||||
while st:
|
||||
node = st.pop()
|
||||
if node != None:
|
||||
if node != none:
|
||||
st.append(node) #中
|
||||
st.append(None)
|
||||
st.append(none)
|
||||
depth += 1
|
||||
for i in range(len(node.children)): #处理孩子
|
||||
if node.children[i]:
|
||||
@ -417,15 +422,15 @@ class Solution:
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
## go
|
||||
|
||||
```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 max (a, b int) int {
|
||||
@ -435,28 +440,28 @@ func max (a, b int) int {
|
||||
return b;
|
||||
}
|
||||
// 递归
|
||||
func maxDepth(root *TreeNode) int {
|
||||
func maxdepth(root *treenode) int {
|
||||
if root == nil {
|
||||
return 0;
|
||||
}
|
||||
return max(maxDepth(root.Left), maxDepth(root.Right)) + 1;
|
||||
return max(maxdepth(root.left), maxdepth(root.right)) + 1;
|
||||
}
|
||||
|
||||
// 遍历
|
||||
func maxDepth(root *TreeNode) int {
|
||||
func maxdepth(root *treenode) int {
|
||||
levl := 0;
|
||||
queue := make([]*TreeNode, 0);
|
||||
queue := make([]*treenode, 0);
|
||||
if root != nil {
|
||||
queue = append(queue, root);
|
||||
}
|
||||
for l := len(queue); l > 0; {
|
||||
for ;l > 0;l-- {
|
||||
node := queue[0];
|
||||
if node.Left != nil {
|
||||
queue = append(queue, node.Left);
|
||||
if node.left != nil {
|
||||
queue = append(queue, node.left);
|
||||
}
|
||||
if node.Right != nil {
|
||||
queue = append(queue, node.Right);
|
||||
if node.right != nil {
|
||||
queue = append(queue, node.right);
|
||||
}
|
||||
queue = queue[1:];
|
||||
}
|
||||
@ -469,46 +474,49 @@ func maxDepth(root *TreeNode) int {
|
||||
```
|
||||
|
||||
|
||||
JavaScript
|
||||
## javascript
|
||||
|
||||
```javascript
|
||||
var maxDepth = function(root) {
|
||||
var maxdepth = function(root) {
|
||||
if (!root) return root
|
||||
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right))
|
||||
return 1 + math.max(maxdepth(root.left), maxdepth(root.right))
|
||||
};
|
||||
```
|
||||
```
|
||||
|
||||
二叉树最大深度递归遍历
|
||||
```javascript
|
||||
var maxDepth = function(root) {
|
||||
var maxdepth = function(root) {
|
||||
//使用递归的方法 递归三部曲
|
||||
//1. 确定递归函数的参数和返回值
|
||||
const getDepth=function(node){
|
||||
const getdepth=function(node){
|
||||
//2. 确定终止条件
|
||||
if(node===null){
|
||||
return 0;
|
||||
}
|
||||
//3. 确定单层逻辑
|
||||
let leftDepth=getDepth(node.left);
|
||||
let rightDepth=getDepth(node.right);
|
||||
let depth=1+Math.max(leftDepth,rightDepth);
|
||||
let leftdepth=getdepth(node.left);
|
||||
let rightdepth=getdepth(node.right);
|
||||
let depth=1+math.max(leftdepth,rightdepth);
|
||||
return depth;
|
||||
}
|
||||
return getDepth(root);
|
||||
return getdepth(root);
|
||||
};
|
||||
```
|
||||
|
||||
二叉树最大深度层级遍历
|
||||
```javascript
|
||||
var maxDepth = function(root) {
|
||||
var maxdepth = function(root) {
|
||||
//使用递归的方法 递归三部曲
|
||||
//1. 确定递归函数的参数和返回值
|
||||
const getDepth=function(node){
|
||||
const getdepth=function(node){
|
||||
//2. 确定终止条件
|
||||
if(node===null){
|
||||
return 0;
|
||||
}
|
||||
//3. 确定单层逻辑
|
||||
let leftDepth=getDepth(node.left);
|
||||
let rightDepth=getDepth(node.right);
|
||||
let depth=1+Math.max(leftDepth,rightDepth);
|
||||
let leftdepth=getdepth(node.left);
|
||||
let rightdepth=getdepth(node.right);
|
||||
let depth=1+math.max(leftdepth,rightdepth);
|
||||
return depth;
|
||||
}
|
||||
return getDepth(root);
|
||||
@ -519,4 +527,4 @@ var maxDepth = function(root) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
不难写出如下代码:(先把框架写出来)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
|
||||
|
||||
// 第一步
|
||||
@ -155,7 +155,7 @@ root->right = traversal(rightInorder, rightPostorder);
|
||||
|
||||
### C++完整代码
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
|
||||
@ -209,7 +209,7 @@ public:
|
||||
加了日志的代码如下:(加了日志的代码不要在leetcode上提交,容易超时)
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
|
||||
@ -277,7 +277,7 @@ public:
|
||||
下面给出用下表索引写出的代码版本:(思路是一样的,只不过不用重复定义vector了,每次用下表索引来分割)
|
||||
|
||||
### C++优化版本
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
// 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
|
||||
@ -325,7 +325,7 @@ public:
|
||||
|
||||
那么这个版本写出来依然要打日志进行调试,打日志的版本如下:(**该版本不要在leetcode上提交,容易超时**)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
|
||||
@ -419,7 +419,7 @@ public:
|
||||
|
||||
带日志的版本C++代码如下: (**带日志的版本仅用于调试,不要在leetcode上提交,会超时**)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
|
||||
@ -493,7 +493,7 @@ public:
|
||||
|
||||
105.从前序与中序遍历序列构造二叉树,最后版本,C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
|
||||
@ -654,43 +654,68 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
105.从前序与中序遍历序列构造二叉树
|
||||
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
//递归法
|
||||
class Solution:
|
||||
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
|
||||
if not preorder: return None //特殊情况
|
||||
root = TreeNode(preorder[0]) //新建父节点
|
||||
p=inorder.index(preorder[0]) //找到父节点在中序遍历的位置(因为没有重复的元素,才可以这样找)
|
||||
root.left = self.buildTree(preorder[1:p+1],inorder[:p]) //注意左节点时分割中序数组和前续数组的开闭环
|
||||
root.right = self.buildTree(preorder[p+1:],inorder[p+1:]) //分割中序数组和前续数组
|
||||
return root
|
||||
# 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件
|
||||
if not preorder:
|
||||
return None
|
||||
|
||||
# 第二步: 前序遍历的第一个就是当前的中间节点.
|
||||
root_val = preorder[0]
|
||||
root = TreeNode(root_val)
|
||||
|
||||
# 第三步: 找切割点.
|
||||
separator_idx = inorder.index(root_val)
|
||||
|
||||
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
|
||||
inorder_left = inorder[:separator_idx]
|
||||
inorder_right = inorder[separator_idx + 1:]
|
||||
|
||||
# 第五步: 切割preorder数组. 得到preorder数组的左,右半边.
|
||||
# ⭐️ 重点1: 中序数组大小一定跟前序数组大小是相同的.
|
||||
preorder_left = preorder[1:1 + len(inorder_left)]
|
||||
preorder_right = preorder[1 + len(inorder_left):]
|
||||
|
||||
# 第六步: 递归
|
||||
root.left = self.buildTree(preorder_left, inorder_left)
|
||||
root.right = self.buildTree(preorder_right, inorder_right)
|
||||
|
||||
return root
|
||||
```
|
||||
106.从中序与后序遍历序列构造二叉树
|
||||
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
//递归法
|
||||
class Solution:
|
||||
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
|
||||
if not postorder: return None //特殊情况
|
||||
root = TreeNode(postorder[-1]) //新建父节点
|
||||
p=inorder.index(postorder[-1]) //找到父节点在中序遍历的位置*因为没有重复的元素,才可以这样找
|
||||
root.left = self.buildTree(inorder[:p],postorder[:p]) //分割中序数组和后续数组
|
||||
root.right = self.buildTree(inorder[p+1:],postorder[p:-1]) //注意右节点时分割中序数组和后续数组的开闭环
|
||||
return root
|
||||
# 第一步: 特殊情况讨论: 树为空. (递归终止条件)
|
||||
if not postorder:
|
||||
return None
|
||||
|
||||
# 第二步: 后序遍历的最后一个就是当前的中间节点.
|
||||
root_val = postorder[-1]
|
||||
root = TreeNode(root_val)
|
||||
|
||||
# 第三步: 找切割点.
|
||||
separator_idx = inorder.index(root_val)
|
||||
|
||||
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
|
||||
inorder_left = inorder[:separator_idx]
|
||||
inorder_right = inorder[separator_idx + 1:]
|
||||
|
||||
# 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
|
||||
# ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的.
|
||||
postorder_left = postorder[:len(inorder_left)]
|
||||
postorder_right = postorder[len(inorder_left): len(postorder) - 1]
|
||||
|
||||
# 第六步: 递归
|
||||
root.left = self.buildTree(inorder_left, postorder_left)
|
||||
root.right = self.buildTree(inorder_right, postorder_right)
|
||||
|
||||
return root
|
||||
```
|
||||
Go:
|
||||
> 106 从中序与后序遍历序列构造二叉树
|
||||
@ -793,4 +818,4 @@ var buildTree = function(preorder, inorder) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -122,7 +122,7 @@ return root;
|
||||
|
||||
* 递归整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
TreeNode* traversal(vector<int>& nums, int left, int right) {
|
||||
@ -150,7 +150,7 @@ public:
|
||||
|
||||
模拟的就是不断分割的过程,C++代码如下:(我已经详细注释)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* sortedArrayToBST(vector<int>& nums) {
|
||||
@ -388,4 +388,4 @@ var sortedArrayToBST = function (nums) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
> 求高度还是求深度,你搞懂了不?
|
||||
|
||||
## 110.平衡二叉树
|
||||
# 110.平衡二叉树
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/balanced-binary-tree/
|
||||
|
||||
@ -33,9 +33,10 @@
|
||||
|
||||
返回 false 。
|
||||
|
||||
## 题外话
|
||||
# 题外话
|
||||
|
||||
咋眼一看这道题目和[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)很像,其实有很大区别。
|
||||
|
||||
咋眼一看这道题目和[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)很像,其实有很大区别。
|
||||
|
||||
这里强调一波概念:
|
||||
|
||||
@ -50,13 +51,13 @@
|
||||
|
||||
因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
|
||||
|
||||
有的同学一定疑惑,为什么[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中求的是二叉树的最大深度,也用的是后序遍历。
|
||||
有的同学一定疑惑,为什么[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)中求的是二叉树的最大深度,也用的是后序遍历。
|
||||
|
||||
**那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这颗树的最大深度,所以才可以使用后序遍历。**
|
||||
|
||||
在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中,如果真正求取二叉树的最大深度,代码应该写成如下:(前序遍历)
|
||||
在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)中,如果真正求取二叉树的最大深度,代码应该写成如下:(前序遍历)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int result;
|
||||
@ -90,7 +91,7 @@ public:
|
||||
|
||||
注意以上代码是为了把细节体现出来,简化一下代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int result;
|
||||
@ -114,9 +115,9 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
## 本题思路
|
||||
# 本题思路
|
||||
|
||||
### 递归
|
||||
## 递归
|
||||
|
||||
此时大家应该明白了既然要求比较高度,必然是要后序遍历。
|
||||
|
||||
@ -160,7 +161,7 @@ if (node == NULL) {
|
||||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
```CPP
|
||||
int leftDepth = depth(node->left); // 左
|
||||
if (leftDepth == -1) return -1;
|
||||
int rightDepth = depth(node->right); // 右
|
||||
@ -178,7 +179,7 @@ return result;
|
||||
|
||||
代码精简之后如下:
|
||||
|
||||
```
|
||||
```CPP
|
||||
int leftDepth = getDepth(node->left);
|
||||
if (leftDepth == -1) return -1;
|
||||
int rightDepth = getDepth(node->right);
|
||||
@ -190,7 +191,7 @@ return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
|
||||
|
||||
getDepth整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
int getDepth(TreeNode* node) {
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
@ -205,7 +206,7 @@ int getDepth(TreeNode* node) {
|
||||
|
||||
最后本题整体递归代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
// 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1
|
||||
@ -225,9 +226,9 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
### 迭代
|
||||
## 迭代
|
||||
|
||||
在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
|
||||
在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
|
||||
|
||||
本题的迭代方式可以先定义一个函数,专门用来求高度。
|
||||
|
||||
@ -235,7 +236,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// cur节点的最大深度,就是cur的高度
|
||||
int getDepth(TreeNode* cur) {
|
||||
stack<TreeNode*> st;
|
||||
@ -266,7 +267,7 @@ int getDepth(TreeNode* cur) {
|
||||
|
||||
然后再用栈来模拟前序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:
|
||||
|
||||
```
|
||||
```CPP
|
||||
bool isBalanced(TreeNode* root) {
|
||||
stack<TreeNode*> st;
|
||||
if (root == NULL) return true;
|
||||
@ -286,7 +287,7 @@ bool isBalanced(TreeNode* root) {
|
||||
|
||||
整体代码如下:
|
||||
|
||||
```
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
int getDepth(TreeNode* cur) {
|
||||
@ -342,7 +343,7 @@ public:
|
||||
|
||||
因为对于回溯算法已经是非常复杂的递归了,如果在用迭代的话,就是自己给自己找麻烦,效率也并不一定高。
|
||||
|
||||
## 总结
|
||||
# 总结
|
||||
|
||||
通过本题可以了解求二叉树深度 和 二叉树高度的差异,求深度适合用前序遍历,而求高度适合用后序遍历。
|
||||
|
||||
@ -351,9 +352,9 @@ public:
|
||||
但是递归方式是一定要掌握的!
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
# 其他语言版本
|
||||
|
||||
Java:
|
||||
## Java
|
||||
|
||||
```Java
|
||||
class Solution {
|
||||
@ -494,9 +495,9 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
## Python
|
||||
|
||||
> 递归法:
|
||||
递归法:
|
||||
```python
|
||||
class Solution:
|
||||
def isBalanced(self, root: TreeNode) -> bool:
|
||||
@ -513,7 +514,7 @@ class Solution:
|
||||
return -1 if abs(leftDepth - rightDepth)>1 else 1 + max(leftDepth, rightDepth)
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
迭代法:
|
||||
```python
|
||||
class Solution:
|
||||
def isBalanced(self, root: TreeNode) -> bool:
|
||||
@ -553,7 +554,7 @@ class Solution:
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
## Go
|
||||
```Go
|
||||
func isBalanced(root *TreeNode) bool {
|
||||
if root==nil{
|
||||
@ -589,7 +590,7 @@ func abs(a int)int{
|
||||
}
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
## JavaScript
|
||||
```javascript
|
||||
var isBalanced = function(root) {
|
||||
//还是用递归三部曲 + 后序遍历 左右中 当前左子树右子树高度相差大于1就返回-1
|
||||
@ -623,4 +624,4 @@ var isBalanced = function(root) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
> 和求最大深度一个套路?
|
||||
|
||||
## 111.二叉树的最小深度
|
||||
# 111.二叉树的最小深度
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
|
||||
|
||||
@ -27,9 +27,9 @@
|
||||
|
||||
返回它的最小深度 2.
|
||||
|
||||
## 思路
|
||||
# 思路
|
||||
|
||||
看完了这篇[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),再来看看如何求最小深度。
|
||||
看完了这篇[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw),再来看看如何求最小深度。
|
||||
|
||||
直觉上好像和求最大深度差不多,其实还是差不少的。
|
||||
|
||||
@ -87,7 +87,7 @@ return result;
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
int leftDepth = getDepth(node->left); // 左
|
||||
int rightDepth = getDepth(node->right); // 右
|
||||
// 中
|
||||
@ -106,7 +106,7 @@ return result;
|
||||
遍历的顺序为后序(左右中),可以看出:**求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。**
|
||||
|
||||
整体递归代码如下:
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int getDepth(TreeNode* node) {
|
||||
@ -134,7 +134,7 @@ public:
|
||||
|
||||
精简之后代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int minDepth(TreeNode* root) {
|
||||
@ -154,15 +154,15 @@ public:
|
||||
|
||||
## 迭代法
|
||||
|
||||
相对于[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg),本题还可以使用层序遍历的方式来解决,思路是一样的。
|
||||
相对于[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw),本题还可以使用层序遍历的方式来解决,思路是一样的。
|
||||
|
||||
如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)
|
||||
如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)
|
||||
|
||||
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
|
||||
|
||||
代码如下:(详细注释)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
|
||||
@ -190,10 +190,10 @@ public:
|
||||
```
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
# 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
## Java
|
||||
|
||||
```Java
|
||||
class Solution {
|
||||
@ -253,7 +253,7 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
## Python
|
||||
|
||||
递归法:
|
||||
|
||||
@ -299,7 +299,7 @@ class Solution:
|
||||
```
|
||||
|
||||
|
||||
Go:
|
||||
## Go
|
||||
|
||||
```go
|
||||
/**
|
||||
@ -360,7 +360,7 @@ func minDepth(root *TreeNode) int {
|
||||
```
|
||||
|
||||
|
||||
JavaScript:
|
||||
## JavaScript
|
||||
|
||||
递归法:
|
||||
|
||||
@ -413,4 +413,4 @@ var minDepth = function(root) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -9,14 +9,14 @@
|
||||
|
||||
> 递归函数什么时候需要返回值
|
||||
|
||||
相信很多同学都会疑惑,递归函数什么时候要有返回值,什么时候没有返回值,特别是有的时候递归函数返回类型为bool类型。那么
|
||||
相信很多同学都会疑惑,递归函数什么时候要有返回值,什么时候没有返回值,特别是有的时候递归函数返回类型为bool类型。
|
||||
|
||||
接下来我通过详细讲解如下两道题,来回答这个问题:
|
||||
那么接下来我通过详细讲解如下两道题,来回答这个问题:
|
||||
|
||||
* 112.路径总和
|
||||
* 113.路径总和II
|
||||
* 113.路径总和ii
|
||||
|
||||
## 112. 路径总和
|
||||
# 112. 路径总和
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/path-sum/
|
||||
|
||||
@ -31,11 +31,11 @@
|
||||
|
||||
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
|
||||
|
||||
### 思路
|
||||
# 思路
|
||||
|
||||
这道题我们要遍历从根节点到叶子节点的的路径看看总和是不是目标和。
|
||||
|
||||
### 递归
|
||||
## 递归
|
||||
|
||||
可以使用深度优先遍历的方式(本题前中后序都可以,无所谓,因为中节点也没有处理逻辑)来遍历二叉树
|
||||
|
||||
@ -43,13 +43,11 @@
|
||||
|
||||
参数:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。
|
||||
|
||||
**再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?**
|
||||
再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
|
||||
|
||||
在文章[二叉树:我的左下角的值是多少?](https://mp.weixin.qq.com/s/MH2gbLvzQ91jHPKqiub0Nw)中,我给出了一个结论:
|
||||
|
||||
**如果需要搜索整颗二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径了就要及时返回。**
|
||||
|
||||
在[二叉树:我的左下角的值是多少?](https://mp.weixin.qq.com/s/MH2gbLvzQ91jHPKqiub0Nw)中,因为要遍历树的所有路径,找出深度最深的叶子节点,所以递归函数不要返回值。
|
||||
* 如果需要搜索整颗二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
|
||||
* 如果需要搜索整颗二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在[236. 二叉树的最近公共祖先](https://mp.weixin.qq.com/s/n6Rk3nc_X3TSkhXHrVmBTQ)中介绍)
|
||||
* 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
|
||||
|
||||
而本题我们要找一条符合条件的路径,所以递归函数需要返回值,及时返回,那么返回类型是什么呢?
|
||||
|
||||
@ -62,7 +60,7 @@
|
||||
所以代码如下:
|
||||
|
||||
```
|
||||
bool traversal(TreeNode* cur, int count) // 注意函数的返回类型
|
||||
bool traversal(treenode* cur, int count) // 注意函数的返回类型
|
||||
```
|
||||
|
||||
|
||||
@ -91,7 +89,7 @@ if (!cur->left && !cur->right) return false; // 遇到叶子节点而没有找
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
if (cur->left) { // 左 (空节点不遍历)
|
||||
// 遇到叶子节点返回true,则直接返回true
|
||||
if (traversal(cur->left, count - cur->left->val)) return true; // 注意这里有回溯的逻辑
|
||||
@ -109,7 +107,7 @@ return false;
|
||||
|
||||
为了把回溯的过程体现出来,可以改为如下代码:
|
||||
|
||||
```C++
|
||||
```cpp
|
||||
if (cur->left) { // 左
|
||||
count -= cur->left->val; // 递归,处理节点;
|
||||
if (traversal(cur->left, count)) return true;
|
||||
@ -126,10 +124,10 @@ return false;
|
||||
|
||||
整体代码如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```cpp
|
||||
class solution {
|
||||
private:
|
||||
bool traversal(TreeNode* cur, int count) {
|
||||
bool traversal(treenode* cur, int count) {
|
||||
if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
|
||||
if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回
|
||||
|
||||
@ -147,8 +145,8 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
bool hasPathSum(TreeNode* root, int sum) {
|
||||
if (root == NULL) return false;
|
||||
bool haspathsum(treenode* root, int sum) {
|
||||
if (root == null) return false;
|
||||
return traversal(root, sum - root->val);
|
||||
}
|
||||
};
|
||||
@ -156,15 +154,15 @@ public:
|
||||
|
||||
以上代码精简之后如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```cpp
|
||||
class solution {
|
||||
public:
|
||||
bool hasPathSum(TreeNode* root, int sum) {
|
||||
if (root == NULL) return false;
|
||||
bool haspathsum(treenode* root, int sum) {
|
||||
if (root == null) return false;
|
||||
if (!root->left && !root->right && sum == root->val) {
|
||||
return true;
|
||||
}
|
||||
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
|
||||
return haspathsum(root->left, sum - root->val) || haspathsum(root->right, sum - root->val);
|
||||
}
|
||||
};
|
||||
```
|
||||
@ -172,43 +170,43 @@ public:
|
||||
**是不是发现精简之后的代码,已经完全看不出分析的过程了,所以我们要把题目分析清楚之后,在追求代码精简。** 这一点我已经强调很多次了!
|
||||
|
||||
|
||||
### 迭代
|
||||
## 迭代
|
||||
|
||||
如果使用栈模拟递归的话,那么如果做回溯呢?
|
||||
|
||||
**此时栈里一个元素不仅要记录该节点指针,还要记录从头结点到该节点的路径数值总和。**
|
||||
|
||||
C++就我们用pair结构来存放这个栈里的元素。
|
||||
c++就我们用pair结构来存放这个栈里的元素。
|
||||
|
||||
定义为:`pair<TreeNode*, int>` pair<节点指针,路径数值>
|
||||
定义为:`pair<treenode*, int>` pair<节点指针,路径数值>
|
||||
|
||||
这个为栈里的一个元素。
|
||||
|
||||
如下代码是使用栈模拟的前序遍历,如下:(详细注释)
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```cpp
|
||||
class solution {
|
||||
|
||||
public:
|
||||
bool hasPathSum(TreeNode* root, int sum) {
|
||||
if (root == NULL) return false;
|
||||
bool haspathsum(treenode* root, int sum) {
|
||||
if (root == null) return false;
|
||||
// 此时栈里要放的是pair<节点指针,路径数值>
|
||||
stack<pair<TreeNode*, int>> st;
|
||||
st.push(pair<TreeNode*, int>(root, root->val));
|
||||
stack<pair<treenode*, int>> st;
|
||||
st.push(pair<treenode*, int>(root, root->val));
|
||||
while (!st.empty()) {
|
||||
pair<TreeNode*, int> node = st.top();
|
||||
pair<treenode*, int> node = st.top();
|
||||
st.pop();
|
||||
// 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
|
||||
if (!node.first->left && !node.first->right && sum == node.second) return true;
|
||||
|
||||
// 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
|
||||
if (node.first->right) {
|
||||
st.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));
|
||||
st.push(pair<treenode*, int>(node.first->right, node.second + node.first->right->val));
|
||||
}
|
||||
|
||||
// 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
|
||||
if (node.first->left) {
|
||||
st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));
|
||||
st.push(pair<treenode*, int>(node.first->left, node.second + node.first->left->val));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -216,9 +214,9 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
如果大家完全理解了本地的递归方法之后,就可以顺便把leetcode上113. 路径总和II做了。
|
||||
如果大家完全理解了本地的递归方法之后,就可以顺便把leetcode上113. 路径总和ii做了。
|
||||
|
||||
## 113. 路径总和II
|
||||
# 113. 路径总和ii
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/path-sum-ii/
|
||||
|
||||
@ -230,26 +228,27 @@ public:
|
||||
给定如下二叉树,以及目标和 sum = 22,
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
### 思路
|
||||
## 思路
|
||||
|
||||
113.路径总和II要遍历整个树,找到所有路径,**所以递归函数不要返回值!**
|
||||
|
||||
113.路径总和ii要遍历整个树,找到所有路径,**所以递归函数不要返回值!**
|
||||
|
||||
如图:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
为了尽可能的把细节体现出来,我写出如下代码(**这份代码并不简洁,但是逻辑非常清晰**)
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
```cpp
|
||||
class solution {
|
||||
private:
|
||||
vector<vector<int>> result;
|
||||
vector<int> path;
|
||||
// 递归函数不需要返回值,因为我们要遍历整个树
|
||||
void traversal(TreeNode* cur, int count) {
|
||||
void traversal(treenode* cur, int count) {
|
||||
if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径
|
||||
result.push_back(path);
|
||||
return;
|
||||
@ -275,10 +274,10 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
vector<vector<int>> pathSum(TreeNode* root, int sum) {
|
||||
vector<vector<int>> pathsum(treenode* root, int sum) {
|
||||
result.clear();
|
||||
path.clear();
|
||||
if (root == NULL) return result;
|
||||
if (root == null) return result;
|
||||
path.push_back(root->val); // 把根节点放进路径
|
||||
traversal(root, sum - root->val);
|
||||
return result;
|
||||
@ -286,11 +285,11 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
至于113. 路径总和II 的迭代法我并没有写,用迭代方式记录所有路径比较麻烦,也没有必要,如果大家感兴趣的话,可以再深入研究研究。
|
||||
至于113. 路径总和ii 的迭代法我并没有写,用迭代方式记录所有路径比较麻烦,也没有必要,如果大家感兴趣的话,可以再深入研究研究。
|
||||
|
||||
## 总结
|
||||
# 总结
|
||||
|
||||
本篇通过leetcode上112. 路径总和 和 113. 路径总和II 详细的讲解了 递归函数什么时候需要返回值,什么不需要返回值。
|
||||
本篇通过leetcode上112. 路径总和 和 113. 路径总和ii 详细的讲解了 递归函数什么时候需要返回值,什么不需要返回值。
|
||||
|
||||
这两道题目是掌握这一知识点非常好的题目,大家看完本篇文章再去做题,就会感受到搜索整棵树和搜索某一路径的差别。
|
||||
|
||||
@ -299,31 +298,30 @@ public:
|
||||
|
||||
|
||||
|
||||
# 其他语言版本
|
||||
|
||||
## java
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
```Java
|
||||
class Solution {
|
||||
public boolean hasPathSum(TreeNode root, int targetSum) {
|
||||
lc112
|
||||
```java
|
||||
class solution {
|
||||
public boolean haspathsum(treenode root, int targetsum) {
|
||||
if (root == null) {
|
||||
return false;
|
||||
}
|
||||
targetSum -= root.val;
|
||||
targetsum -= root.val;
|
||||
// 叶子结点
|
||||
if (root.left == null && root.right == null) {
|
||||
return targetSum == 0;
|
||||
return targetsum == 0;
|
||||
}
|
||||
if (root.left != null) {
|
||||
boolean left = hasPathSum(root.left, targetSum);
|
||||
boolean left = haspathsum(root.left, targetsum);
|
||||
if (left) {// 已经找到
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (root.right != null) {
|
||||
boolean right = hasPathSum(root.right, targetSum);
|
||||
boolean right = haspathsum(root.right, targetsum);
|
||||
if (right) {// 已经找到
|
||||
return true;
|
||||
}
|
||||
@ -332,34 +330,34 @@ class Solution {
|
||||
}
|
||||
}
|
||||
|
||||
// LC112 简洁方法
|
||||
class Solution {
|
||||
public boolean hasPathSum(TreeNode root, int targetSum) {
|
||||
// lc112 简洁方法
|
||||
class solution {
|
||||
public boolean haspathsum(treenode root, int targetsum) {
|
||||
|
||||
if (root == null) return false; // 为空退出
|
||||
|
||||
// 叶子节点判断是否符合
|
||||
if (root.left == null && root.right == null) return root.val == targetSum;
|
||||
if (root.left == null && root.right == null) return root.val == targetsum;
|
||||
|
||||
// 求两侧分支的路径和
|
||||
return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
|
||||
return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val);
|
||||
}
|
||||
}
|
||||
```
|
||||
迭代
|
||||
```java
|
||||
class Solution {
|
||||
public boolean hasPathSum(TreeNode root, int targetSum) {
|
||||
class solution {
|
||||
public boolean haspathsum(treenode root, int targetsum) {
|
||||
if(root==null)return false;
|
||||
Stack<TreeNode> stack1 = new Stack<>();
|
||||
Stack<Integer> stack2 = new Stack<>();
|
||||
stack<treenode> stack1 = new stack<>();
|
||||
stack<integer> stack2 = new stack<>();
|
||||
stack1.push(root);stack2.push(root.val);
|
||||
while(!stack1.isEmpty()){
|
||||
while(!stack1.isempty()){
|
||||
int size = stack1.size();
|
||||
for(int i=0;i<size;i++){
|
||||
TreeNode node = stack1.pop();int sum=stack2.pop();
|
||||
treenode node = stack1.pop();int sum=stack2.pop();
|
||||
// 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
|
||||
if(node.left==null && node.right==null && sum==targetSum)return true;
|
||||
if(node.left==null && node.right==null && sum==targetsum)return true;
|
||||
// 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
|
||||
if(node.right!=null){
|
||||
stack1.push(node.right);stack2.push(sum+node.right.val);
|
||||
@ -380,209 +378,226 @@ class Solution {
|
||||
0113.路径总和-ii
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
|
||||
List<List<Integer>> res = new ArrayList<>();
|
||||
class solution {
|
||||
public list<list<integer>> pathsum(treenode root, int targetsum) {
|
||||
list<list<integer>> res = new arraylist<>();
|
||||
if (root == null) return res; // 非空判断
|
||||
|
||||
List<Integer> path = new LinkedList<>();
|
||||
preorderDFS(root, targetSum, res, path);
|
||||
list<integer> path = new linkedlist<>();
|
||||
preorderdfs(root, targetsum, res, path);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void preorderDFS(TreeNode root, int targetSum, List<List<Integer>> res, List<Integer> path) {
|
||||
public void preorderdfs(treenode root, int targetsum, list<list<integer>> res, list<integer> path) {
|
||||
path.add(root.val);
|
||||
// 遇到了叶子节点
|
||||
if (root.left == null && root.right == null) {
|
||||
// 找到了和为 targetSum 的路径
|
||||
if (targetSum - root.val == 0) {
|
||||
res.add(new ArrayList<>(path));
|
||||
// 找到了和为 targetsum 的路径
|
||||
if (targetsum - root.val == 0) {
|
||||
res.add(new arraylist<>(path));
|
||||
}
|
||||
return; // 如果和不为 targetSum,返回
|
||||
return; // 如果和不为 targetsum,返回
|
||||
}
|
||||
|
||||
if (root.left != null) {
|
||||
preorderDFS(root.left, targetSum - root.val, res, path);
|
||||
preorderdfs(root.left, targetsum - root.val, res, path);
|
||||
path.remove(path.size() - 1); // 回溯
|
||||
}
|
||||
if (root.right != null) {
|
||||
preorderDFS(root.right, targetSum - root.val, res, path);
|
||||
preorderdfs(root.right, targetsum - root.val, res, path);
|
||||
path.remove(path.size() - 1); // 回溯
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
## python
|
||||
|
||||
0112.路径总和
|
||||
|
||||
**递归**
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
|
||||
// 递归法
|
||||
|
||||
class Solution:
|
||||
def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
|
||||
def isornot(root,targetSum)->bool:
|
||||
if (not root.left) and (not root.right) and targetSum == 0:return True // 遇到叶子节点,并且计数为0
|
||||
if (not root.left) and (not root.right):return False //遇到叶子节点,计数不为0
|
||||
class solution:
|
||||
def haspathsum(self, root: treenode, targetsum: int) -> bool:
|
||||
def isornot(root, targetsum) -> bool:
|
||||
if (not root.left) and (not root.right) and targetsum == 0:
|
||||
return true # 遇到叶子节点,并且计数为0
|
||||
if (not root.left) and (not root.right):
|
||||
return false # 遇到叶子节点,计数不为0
|
||||
if root.left:
|
||||
targetSum -= root.left.val //左节点
|
||||
if isornot(root.left,targetSum):return True //递归,处理左节点
|
||||
targetSum += root.left.val //回溯
|
||||
targetsum -= root.left.val # 左节点
|
||||
if isornot(root.left, targetsum): return true # 递归,处理左节点
|
||||
targetsum += root.left.val # 回溯
|
||||
if root.right:
|
||||
targetSum -= root.right.val //右节点
|
||||
if isornot(root.right,targetSum):return True //递归,处理右节点
|
||||
targetSum += root.right.val //回溯
|
||||
return False
|
||||
|
||||
if root == None:return False //别忘记处理空TreeNode
|
||||
else:return isornot(root,targetSum-root.val)
|
||||
targetsum -= root.right.val # 右节点
|
||||
if isornot(root.right, targetsum): return true # 递归,处理右节点
|
||||
targetsum += root.right.val # 回溯
|
||||
return false
|
||||
|
||||
if root == none:
|
||||
return false # 别忘记处理空treenode
|
||||
else:
|
||||
return isornot(root, targetsum - root.val)
|
||||
```
|
||||
|
||||
**迭代 - 层序遍历**
|
||||
```python
|
||||
class solution:
|
||||
def haspathsum(self, root: treenode, targetsum: int) -> bool:
|
||||
if not root:
|
||||
return false
|
||||
|
||||
stack = [] # [(当前节点,路径数值), ...]
|
||||
stack.append((root, root.val))
|
||||
|
||||
while stack:
|
||||
cur_node, path_sum = stack.pop()
|
||||
|
||||
if not cur_node.left and not cur_node.right and path_sum == targetsum:
|
||||
return true
|
||||
|
||||
if cur_node.right:
|
||||
stack.append((cur_node.right, path_sum + cur_node.right.val))
|
||||
|
||||
if cur_node.left:
|
||||
stack.append((cur_node.left, path_sum + cur_node.left.val))
|
||||
|
||||
return false
|
||||
```
|
||||
|
||||
0113.路径总和-ii
|
||||
|
||||
**递归**
|
||||
```python
|
||||
# Definition for a binary tree node.
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
//递归法
|
||||
class Solution:
|
||||
def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
|
||||
path=[]
|
||||
res=[]
|
||||
def pathes(root,targetSum):
|
||||
if (not root.left) and (not root.right) and targetSum == 0: // 遇到叶子节点,并且计数为0
|
||||
res.append(path[:]) //找到一种路径,记录到res中,注意必须是path[:]而不是path
|
||||
return
|
||||
if (not root.left) and (not root.right):return // 遇到叶子节点直接返回
|
||||
if root.left: //左
|
||||
targetSum -= root.left.val
|
||||
path.append(root.left.val) //递归前记录节点
|
||||
pathes(root.left,targetSum) //递归
|
||||
targetSum += root.left.val //回溯
|
||||
path.pop() //回溯
|
||||
if root.right: //右
|
||||
targetSum -= root.right.val
|
||||
path.append(root.right.val) //递归前记录节点
|
||||
pathes(root.right,targetSum) //递归
|
||||
targetSum += root.right.val //回溯
|
||||
path.pop() //回溯
|
||||
return
|
||||
|
||||
if root == None:return [] //处理空TreeNode
|
||||
else:
|
||||
path.append(root.val) //首先处理根节点
|
||||
pathes(root,targetSum-root.val)
|
||||
return res
|
||||
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 cur_node.left:
|
||||
path.append(cur_node.left.val)
|
||||
remain -= cur_node.left.val
|
||||
traversal(cur_node.left, remain)
|
||||
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)
|
||||
path.pop()
|
||||
remain += cur_node.right.val
|
||||
|
||||
result, path = [], []
|
||||
if not root:
|
||||
return []
|
||||
path.append(root.val)
|
||||
traversal(root, targetsum - root.val)
|
||||
return result
|
||||
```
|
||||
|
||||
Go:
|
||||
## go
|
||||
|
||||
> 112. 路径总和
|
||||
112. 路径总和
|
||||
|
||||
```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 {
|
||||
func haspathsum(root *treenode, targetsum int) bool {
|
||||
var flage bool //找没找到的标志
|
||||
if root==nil{
|
||||
return flage
|
||||
}
|
||||
pathSum(root,0,targetSum,&flage)
|
||||
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{
|
||||
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.left!=nil&&!(*flage){//左节点不为空且还没找到
|
||||
pathsum(root.left,sum,targetsum,flage)
|
||||
}
|
||||
if root.Right!=nil&&!(*flage){//右节点不为空且没找到
|
||||
pathSum(root.Right,sum,targetSum,flage)
|
||||
if root.right!=nil&&!(*flage){//右节点不为空且没找到
|
||||
pathsum(root.right,sum,targetsum,flage)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
> 113 递归法
|
||||
113 递归法
|
||||
|
||||
```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 {
|
||||
func pathsum(root *treenode, targetsum int) [][]int {
|
||||
var result [][]int//最终结果
|
||||
if root==nil{
|
||||
return result
|
||||
}
|
||||
var sumNodes []int//经过路径的节点集合
|
||||
hasPathSum(root,&sumNodes,targetSum,&result)
|
||||
var sumnodes []int//经过路径的节点集合
|
||||
haspathsum(root,&sumnodes,targetsum,&result)
|
||||
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)
|
||||
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{//求该路径节点的和
|
||||
for k,v:=range *sumnodes{//求该路径节点的和
|
||||
sum+=v
|
||||
number=k
|
||||
}
|
||||
tempNodes:=make([]int,number+1)//新的nodes接受指针里的值,防止最终指针里的值发生变动,导致最后的结果都是最后一个sumNodes的值
|
||||
for k,v:=range *sumNodes{
|
||||
tempNodes[k]=v
|
||||
tempnodes:=make([]int,number+1)//新的nodes接受指针里的值,防止最终指针里的值发生变动,导致最后的结果都是最后一个sumnodes的值
|
||||
for k,v:=range *sumnodes{
|
||||
tempnodes[k]=v
|
||||
}
|
||||
if sum==targetSum{
|
||||
*result=append(*result,tempNodes)
|
||||
if sum==targetsum{
|
||||
*result=append(*result,tempnodes)
|
||||
}
|
||||
}
|
||||
if root.Left!=nil{
|
||||
hasPathSum(root.Left,sumNodes,targetSum,result)
|
||||
*sumNodes=(*sumNodes)[:len(*sumNodes)-1]//回溯
|
||||
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]//回溯
|
||||
if root.right!=nil{
|
||||
haspathsum(root.right,sumnodes,targetsum,result)
|
||||
*sumnodes=(*sumnodes)[:len(*sumnodes)-1]//回溯
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
## javascript
|
||||
|
||||
0112.路径总和
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* @param {TreeNode} root
|
||||
* @param {number} targetSum
|
||||
* @param {treenode} root
|
||||
* @param {number} targetsum
|
||||
* @return {boolean}
|
||||
*/
|
||||
let hasPathSum = function (root, targetSum) {
|
||||
let haspathsum = function (root, targetsum) {
|
||||
// 递归法
|
||||
const traversal = (node, cnt) => {
|
||||
// 遇到叶子节点,并且计数为0
|
||||
@ -597,19 +612,19 @@ let hasPathSum = function (root, targetSum) {
|
||||
return false;
|
||||
};
|
||||
if (!root) return false;
|
||||
return traversal(root, targetSum - root.val);
|
||||
return traversal(root, targetsum - root.val);
|
||||
|
||||
// 精简代码:
|
||||
// if (!root) return false;
|
||||
// if (!root.left && !root.right && targetSum === root.val) return true;
|
||||
// return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
|
||||
// if (!root.left && !root.right && targetsum === root.val) return true;
|
||||
// return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val);
|
||||
};
|
||||
```
|
||||
|
||||
0113.路径总和-ii
|
||||
|
||||
```javascript
|
||||
let pathSum = function (root, targetSum) {
|
||||
let pathsum = function (root, targetsum) {
|
||||
// 递归法
|
||||
// 要遍历整个树找到所有路径,所以递归函数不需要返回值, 与112不同
|
||||
const res = [];
|
||||
@ -635,62 +650,29 @@ let pathSum = function (root, targetSum) {
|
||||
return;
|
||||
};
|
||||
if (!root) return res;
|
||||
travelsal(root, targetSum - root.val, [root.val]); // 把根节点放进路径
|
||||
travelsal(root, targetsum - root.val, [root.val]); // 把根节点放进路径
|
||||
return res;
|
||||
};
|
||||
```
|
||||
|
||||
0112 路径总和
|
||||
113 路径总和 精简版
|
||||
```javascript
|
||||
var hasPathSum = function(root, targetSum) {
|
||||
var pathsum = function(root, targetsum) {
|
||||
//递归方法
|
||||
// 1. 确定函数参数
|
||||
const traversal = function(node,count){
|
||||
// 2. 确定终止条件
|
||||
if(node.left===null&&node.right===null&&count===0){
|
||||
return true;
|
||||
}
|
||||
if(node.left===null&&node.right===null){
|
||||
return false;
|
||||
}
|
||||
//3. 单层递归逻辑
|
||||
if(node.left){
|
||||
if(traversal(node.left,count-node.left.val)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(node.right){
|
||||
if(traversal(node.right,count-node.right.val)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if(root===null){
|
||||
return false;
|
||||
}
|
||||
return traversal(root,targetSum-root.val);
|
||||
};
|
||||
```
|
||||
113 路径总和
|
||||
```javascript
|
||||
var pathSum = function(root, targetSum) {
|
||||
//递归方法
|
||||
let resPath = [],curPath = [];
|
||||
let respath = [],curpath = [];
|
||||
// 1. 确定递归函数参数
|
||||
const travelTree = function(node,count){
|
||||
curPath.push(node.val);
|
||||
const traveltree = function(node,count){
|
||||
curpath.push(node.val);
|
||||
count-=node.val;
|
||||
if(node.left===null&&node.right===null&&count===0){
|
||||
resPath.push([...curPath]);
|
||||
respath.push([...curpath]);
|
||||
}
|
||||
node.left&&travelTree(node.left,count);
|
||||
node.right&&travelTree(node.right,count);
|
||||
let cur = curPath.pop();
|
||||
node.left&&traveltree(node.left,count);
|
||||
node.right&&traveltree(node.right,count);
|
||||
let cur = curpath.pop();
|
||||
count-=cur;
|
||||
}
|
||||
if(root===null){
|
||||
return resPath;
|
||||
return respath;
|
||||
}
|
||||
travelTree(root,targetSum);
|
||||
return resPath;
|
||||
@ -699,8 +681,9 @@ var pathSum = function(root, targetSum) {
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -82,7 +82,7 @@ dp[0][0]应该是1,空字符串s,可以删除0个元素,变成空字符串
|
||||
|
||||
初始化分析完毕,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<vector<long long>> dp(s.size() + 1, vector<long long>(t.size() + 1));
|
||||
for (int i = 0; i <= s.size(); i++) dp[i][0] = 1;
|
||||
for (int j = 1; j <= t.size(); j++) dp[0][j] = 0; // 其实这行代码可以和dp数组初始化的时候放在一起,但我为了凸显初始化的逻辑,所以还是加上了。
|
||||
@ -97,7 +97,7 @@ for (int j = 1; j <= t.size(); j++) dp[0][j] = 0; // 其实这行代码可以和
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 1; i <= s.size(); i++) {
|
||||
for (int j = 1; j <= t.size(); j++) {
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
@ -120,7 +120,7 @@ for (int i = 1; i <= s.size(); i++) {
|
||||
|
||||
动规五部曲分析完毕,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int numDistinct(string s, string t) {
|
||||
@ -250,4 +250,4 @@ const numDistinct = (s, t) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -52,7 +52,7 @@ struct Node {
|
||||
|
||||
图中cur节点为元素4,那么搭线的逻辑代码:(**注意注释中操作1和操作2和图中的对应关系**)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (cur->left) cur->left->next = cur->right; // 操作1
|
||||
if (cur->right) {
|
||||
if (cur->next) cur->right->next = cur->next->left; // 操作2
|
||||
@ -63,7 +63,7 @@ if (cur->right) {
|
||||
理解到这里,使用前序遍历,那么不难写出如下代码:
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
void traversal(Node* cur) {
|
||||
@ -93,7 +93,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
Node* connect(Node* root) {
|
||||
@ -130,26 +130,134 @@ public:
|
||||
## Java
|
||||
|
||||
```java
|
||||
// 递归法
|
||||
class Solution {
|
||||
public void traversal(Node cur) {
|
||||
if (cur == null) return;
|
||||
if (cur.left != null) cur.left.next = cur.right; // 操作1
|
||||
if (cur.right != null) {
|
||||
if(cur.next != null) cur.right.next = cur.next.left; //操作2
|
||||
else cur.right.next = null;
|
||||
}
|
||||
traversal(cur.left); // 左
|
||||
traversal(cur.right); //右
|
||||
}
|
||||
public Node connect(Node root) {
|
||||
traversal(root);
|
||||
return root;
|
||||
}
|
||||
}
|
||||
```
|
||||
```java
|
||||
// 迭代法
|
||||
class Solution {
|
||||
public Node connect(Node root) {
|
||||
if (root == null) return root;
|
||||
Queue<Node> que = new LinkedList<Node>();
|
||||
que.offer(root);
|
||||
Node nodePre = null;
|
||||
Node node = null;
|
||||
while (!que.isEmpty()) {
|
||||
int size = que.size();
|
||||
for (int i=0; i<size; i++) { // 开始每一层的遍历
|
||||
if (i == 0) {
|
||||
nodePre = que.peek(); // 记录一层的头结点
|
||||
que.poll();
|
||||
node = nodePre;
|
||||
} else {
|
||||
node = que.peek();
|
||||
que.poll();
|
||||
nodePre.next = node; // 本层前一个节点next指向本节点
|
||||
nodePre = nodePre.next;
|
||||
}
|
||||
if (node.left != null) que.offer(node.left);
|
||||
if (node.right != null) que.offer(node.right);
|
||||
}
|
||||
nodePre.next = null; // 本层最后一个节点指向null
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
# 递归法
|
||||
class Solution:
|
||||
def connect(self, root: 'Node') -> 'Node':
|
||||
def traversal(cur: 'Node') -> 'Node':
|
||||
if not cur: return []
|
||||
if cur.left: cur.left.next = cur.right # 操作1
|
||||
if cur.right:
|
||||
if cur.next:
|
||||
cur.right.next = cur.next.left # 操作2
|
||||
else:
|
||||
cur.right.next = None
|
||||
traversal(cur.left) # 左
|
||||
traversal(cur.right) # 右
|
||||
traversal(root)
|
||||
return root
|
||||
```
|
||||
```python
|
||||
# 迭代法
|
||||
class Solution:
|
||||
def connect(self, root: 'Node') -> 'Node':
|
||||
if not root: return
|
||||
res = []
|
||||
queue = [root]
|
||||
while queue:
|
||||
size = len(queue)
|
||||
for i in range(size): # 开始每一层的遍历
|
||||
if i==0:
|
||||
nodePre = queue.pop(0) # 记录一层的头结点
|
||||
node = nodePre
|
||||
else:
|
||||
node = queue.pop(0)
|
||||
nodePre.next = node # 本层前一个节点next指向本节点
|
||||
nodePre = nodePre.next
|
||||
if node.left: queue.append(node.left)
|
||||
if node.right: queue.append(node.right)
|
||||
nodePre.next = None # 本层最后一个节点指向None
|
||||
return root
|
||||
```
|
||||
|
||||
## Go
|
||||
|
||||
```go
|
||||
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
|
||||
```js
|
||||
const connect = root => {
|
||||
if (!root) return root;
|
||||
// 根节点入队
|
||||
const Q = [root];
|
||||
while (Q.length) {
|
||||
const len = Q.length;
|
||||
// 遍历这一层的所有节点
|
||||
for (let i = 0; i < len; i++) {
|
||||
// 队头出队
|
||||
const node = Q.shift();
|
||||
// 连接
|
||||
if (i < len - 1) {
|
||||
// 新的队头是node的右边元素
|
||||
node.next = Q[0];
|
||||
}
|
||||
// 队头左节点有值,放入队列
|
||||
node.left && Q.push(node.left);
|
||||
// 队头右节点有值,放入队列
|
||||
node.right && Q.push(node.right);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
这道题目最直观的想法,就是暴力,找最优间距了。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxProfit(vector<int>& prices) {
|
||||
@ -59,7 +59,7 @@ public:
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxProfit(vector<int>& prices) {
|
||||
@ -139,7 +139,7 @@ dp[5][1]就是最终结果。
|
||||
|
||||
以上分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
@ -169,7 +169,7 @@ dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
|
||||
|
||||
那么我们只需要记录 当前天的dp状态和前一天的dp状态就可以了,可以使用滚动数组来节省空间,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
@ -313,6 +313,26 @@ func max(a,b int)int {
|
||||
}
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
|
||||
```javascript
|
||||
const maxProfit = prices => {
|
||||
const len = prices.length;
|
||||
// 创建dp数组
|
||||
const dp = new Array(len).fill([0, 0]);
|
||||
// dp数组初始化
|
||||
dp[0] = [-prices[0], 0];
|
||||
for (let i = 1; i < len; i++) {
|
||||
// 更新dp[i]
|
||||
dp[i] = [
|
||||
Math.max(dp[i - 1][0], -prices[i]),
|
||||
Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0]),
|
||||
];
|
||||
}
|
||||
return dp[len - 1][1];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@ -320,4 +340,4 @@ func max(a,b int)int {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -80,7 +80,7 @@
|
||||
|
||||
对应C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxProfit(vector<int>& prices) {
|
||||
@ -99,7 +99,7 @@ public:
|
||||
|
||||
动态规划将在下一个系列详细讲解,本题解先给出我的C++代码(带详细注释),感兴趣的同学可以自己先学习一下。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxProfit(vector<int>& prices) {
|
||||
@ -239,4 +239,4 @@ var maxProfit = function(prices) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -74,7 +74,7 @@
|
||||
|
||||
代码如下:(注意代码中的注释,标记了和121.买卖股票的最佳时机唯一不同的地方)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxProfit(vector<int>& prices) {
|
||||
@ -106,7 +106,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
|
||||
|
||||
这里我依然给出滚动数组的版本,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
@ -231,4 +231,4 @@ const maxProfit = (prices) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -127,7 +127,7 @@ dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
|
||||
|
||||
以上五部都分析完了,不难写出如下代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
@ -153,7 +153,7 @@ public:
|
||||
|
||||
当然,大家可以看到力扣官方题解里的一种优化空间写法,我这里给出对应的C++版本:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
@ -278,7 +278,44 @@ class Solution:
|
||||
return dp[4]
|
||||
```
|
||||
|
||||
Go:
|
||||
JavaScript:
|
||||
|
||||
> 版本一:
|
||||
|
||||
```javascript
|
||||
const maxProfit = prices => {
|
||||
const len = prices.length;
|
||||
const dp = new Array(len).fill(0).map(x => new Array(5).fill(0));
|
||||
dp[0][1] = -prices[0];
|
||||
dp[0][3] = -prices[0];
|
||||
for (let i = 1; i < len; i++) {
|
||||
dp[i][0] = dp[i - 1][0];
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
|
||||
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
|
||||
dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
|
||||
dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
|
||||
}
|
||||
return dp[len - 1][4];
|
||||
};
|
||||
```
|
||||
|
||||
> 版本二:
|
||||
|
||||
```javascript
|
||||
const maxProfit = prices => {
|
||||
const len = prices.length;
|
||||
const dp = new Array(5).fill(0);
|
||||
dp[1] = -prices[0];
|
||||
dp[3] = -prices[0];
|
||||
for (let i = 1; i < len; i++) {
|
||||
dp[1] = Math.max(dp[1], dp[0] - prices[i]);
|
||||
dp[2] = Math.max(dp[2], dp[1] + prices[i]);
|
||||
dp[3] = Math.max(dp[3], dp[2] - prices[i]);
|
||||
dp[4] = Math.max(dp[4], dp[3] + prices[i]);
|
||||
}
|
||||
return dp[4];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -287,4 +324,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -52,7 +52,7 @@ if (!cur->left && !cur->right) { // 遇到了叶子节点
|
||||
|
||||
这里vectorToInt函数就是把数组转成int,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
int vectorToInt(const vector<int>& vec) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < vec.size(); i++) {
|
||||
@ -78,7 +78,7 @@ int vectorToInt(const vector<int>& vec) {
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 中
|
||||
if (cur->left) { // 左 (空节点不遍历)
|
||||
path.push_back(cur->left->val);
|
||||
@ -94,7 +94,7 @@ if (cur->right) { // 右 (空节点不遍历)
|
||||
|
||||
这里要注意回溯和递归要永远在一起,一个递归,对应一个回溯,是一对一的关系,有的同学写成如下代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (cur->left) { // 左 (空节点不遍历)
|
||||
path.push_back(cur->left->val);
|
||||
traversal(cur->left); // 递归
|
||||
@ -111,7 +111,7 @@ path.pop_back(); // 回溯
|
||||
|
||||
关键逻辑分析完了,整体C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
int result;
|
||||
@ -165,7 +165,32 @@ public:
|
||||
Java:
|
||||
|
||||
Python:
|
||||
```python3
|
||||
class Solution:
|
||||
def sumNumbers(self, root: TreeNode) -> int:
|
||||
res = 0
|
||||
path = []
|
||||
def backtrace(root):
|
||||
nonlocal res
|
||||
if not root: return # 节点空则返回
|
||||
path.append(root.val)
|
||||
if not root.left and not root.right: # 遇到了叶子节点
|
||||
res += get_sum(path)
|
||||
if root.left: # 左子树不空
|
||||
backtrace(root.left)
|
||||
if root.right: # 右子树不空
|
||||
backtrace(root.right)
|
||||
path.pop()
|
||||
|
||||
def get_sum(arr):
|
||||
s = 0
|
||||
for i in range(len(arr)):
|
||||
s = s * 10 + arr[i]
|
||||
return s
|
||||
|
||||
backtrace(root)
|
||||
return res
|
||||
```
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
@ -176,4 +201,4 @@ JavaScript:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -70,7 +70,7 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<vector<string>> result;
|
||||
vector<string> path; // 放已经回文的子串
|
||||
void backtracking (const string& s, int startIndex) {
|
||||
@ -88,7 +88,7 @@ void backtracking (const string& s, int startIndex) {
|
||||
|
||||
所以终止条件代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
void backtracking (const string& s, int startIndex) {
|
||||
// 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
|
||||
if (startIndex >= s.size()) {
|
||||
@ -108,7 +108,7 @@ void backtracking (const string& s, int startIndex) {
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = startIndex; i < s.size(); i++) {
|
||||
if (isPalindrome(s, startIndex, i)) { // 是回文子串
|
||||
// 获取[startIndex,i]在s中的子串
|
||||
@ -132,7 +132,7 @@ for (int i = startIndex; i < s.size(); i++) {
|
||||
|
||||
那么判断回文的C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
bool isPalindrome(const string& s, int start, int end) {
|
||||
for (int i = start, j = end; i < j; i++, j--) {
|
||||
if (s[i] != s[j]) {
|
||||
@ -151,7 +151,7 @@ for (int i = startIndex; i < s.size(); i++) {
|
||||
|
||||
根据Carl给出的回溯算法模板:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
void backtracking(参数) {
|
||||
if (终止条件) {
|
||||
存放结果;
|
||||
@ -169,7 +169,7 @@ void backtracking(参数) {
|
||||
|
||||
不难写出如下代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<string>> result;
|
||||
@ -395,4 +395,4 @@ var partition = function(s) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -101,7 +101,7 @@ dp[i]: 范围是[0, i]的回文子串,最少分割次数是dp[i]。
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<int> dp(s.size(), INT_MAX);
|
||||
dp[0] = 0;
|
||||
```
|
||||
@ -109,7 +109,7 @@ dp[0] = 0;
|
||||
其实也可以这样初始化,更具dp[i]的定义,dp[i]的最大值其实就是i,也就是把每个字符分割出来。
|
||||
|
||||
所以初始化代码也可以为:
|
||||
```C++
|
||||
```CPP
|
||||
vector<int> dp(s.size());
|
||||
for (int i = 0; i < s.size(); i++) dp[i] = i;
|
||||
```
|
||||
@ -122,7 +122,7 @@ j是在[0,i]之间,所以遍历i的for循环一定在外层,这里遍历j
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 1; i < s.size(); i++) {
|
||||
if (isPalindromic[0][i]) { // 判断是不是回文子串
|
||||
dp[i] = 0;
|
||||
@ -149,7 +149,7 @@ for (int i = 1; i < s.size(); i++) {
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<vector<bool>> isPalindromic(s.size(), vector<bool>(s.size(), false));
|
||||
for (int i = s.size() - 1; i >= 0; i--) {
|
||||
for (int j = i; j < s.size(); j++) {
|
||||
@ -168,7 +168,7 @@ for (int i = s.size() - 1; i >= 0; i--) {
|
||||
|
||||
以上分析完毕,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int minCut(string s) {
|
||||
@ -252,5 +252,5 @@ class Solution:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -65,7 +65,7 @@ cost = [3,4,3]
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
|
||||
@ -99,7 +99,7 @@ C++暴力解法在leetcode上提交也可以过。
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
|
||||
@ -160,7 +160,7 @@ i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
|
||||
@ -288,4 +288,4 @@ var canCompleteCircuit = function(gas, cost) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -47,7 +47,7 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 从前向后
|
||||
for (int i = 1; i < ratings.size(); i++) {
|
||||
if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;
|
||||
@ -80,7 +80,7 @@ for (int i = 1; i < ratings.size(); i++) {
|
||||
|
||||
所以该过程代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 从后向前
|
||||
for (int i = ratings.size() - 2; i >= 0; i--) {
|
||||
if (ratings[i] > ratings[i + 1] ) {
|
||||
@ -90,7 +90,7 @@ for (int i = ratings.size() - 2; i >= 0; i--) {
|
||||
```
|
||||
|
||||
整体代码如下:
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int candy(vector<int>& ratings) {
|
||||
@ -242,4 +242,4 @@ var candy = function(ratings) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
那么这里我也给出回溯法C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
bool backtracking (const string& s, const unordered_set<string>& wordSet, int startIndex) {
|
||||
@ -86,7 +86,7 @@ public:
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
bool backtracking (const string& s,
|
||||
@ -190,7 +190,7 @@ dp[s.size()]就是最终结果。
|
||||
|
||||
动规五部曲分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool wordBreak(string s, vector<string>& wordDict) {
|
||||
@ -319,4 +319,4 @@ const wordBreak = (s, wordDict) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -14,7 +14,7 @@
|
||||
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
|
||||
|
||||
如果链表中存在环,则返回 true 。 否则,返回 false 。
|
||||
|
||||
|
||||

|
||||
|
||||
# 思路
|
||||
@ -45,7 +45,7 @@ fast和slow各自再走一步, fast和slow就相遇了
|
||||
|
||||
C++代码如下
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool hasCycle(ListNode *head) {
|
||||
@ -74,6 +74,21 @@ public:
|
||||
## Java
|
||||
|
||||
```java
|
||||
public class Solution {
|
||||
public boolean hasCycle(ListNode head) {
|
||||
ListNode fast = head;
|
||||
ListNode slow = head;
|
||||
// 空链表、单节点链表一定不会有环
|
||||
while (fast != null && fast.next != null) {
|
||||
fast = fast.next.next; // 快指针,一次移动两步
|
||||
slow = slow.next; // 慢指针,一次移动一步
|
||||
if (fast == slow) { // 快慢指针相遇,表明有环
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; // 正常走到链表末尾,表明没有环
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
@ -105,5 +120,5 @@ class Solution:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -109,7 +109,7 @@ fast指针走过的节点数:` x + y + n (y + z)`,n为fast指针在环内走
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* struct ListNode {
|
||||
@ -301,4 +301,4 @@ var detectCycle = function(head) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
@ -25,7 +24,7 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
void reorderList(ListNode* head) {
|
||||
@ -63,7 +62,8 @@ public:
|
||||
## 方法二
|
||||
|
||||
把链表放进双向队列,然后通过双向队列一前一后弹出数据,来构造新的链表。这种方法比操作数组容易一些,不用双指针模拟一前一后了
|
||||
```C++
|
||||
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
void reorderList(ListNode* head) {
|
||||
@ -108,7 +108,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
// 反转链表
|
||||
@ -176,15 +176,116 @@ public:
|
||||
|
||||
Java:
|
||||
|
||||
Python:
|
||||
```java
|
||||
public class ReorderList {
|
||||
public void reorderList(ListNode head) {
|
||||
ListNode fast = head, slow = head;
|
||||
//求出中点
|
||||
while (fast.next != null && fast.next.next != null) {
|
||||
slow = slow.next;
|
||||
fast = fast.next.next;
|
||||
}
|
||||
//right就是右半部分 12345 就是45 1234 就是34
|
||||
ListNode right = slow.next;
|
||||
//断开左部分和右部分
|
||||
slow.next = null;
|
||||
//反转右部分 right就是反转后右部分的起点
|
||||
right = reverseList(right);
|
||||
//左部分的起点
|
||||
ListNode left = head;
|
||||
//进行左右部分来回连接
|
||||
//这里左部分的节点个数一定大于等于右部分的节点个数 因此只判断right即可
|
||||
while (right != null) {
|
||||
ListNode curLeft = left.next;
|
||||
left.next = right;
|
||||
left = curLeft;
|
||||
|
||||
ListNode curRight = right.next;
|
||||
right.next = left;
|
||||
right = curRight;
|
||||
}
|
||||
}
|
||||
|
||||
public ListNode reverseList(ListNode head) {
|
||||
ListNode headNode = new ListNode(0);
|
||||
ListNode cur = head;
|
||||
ListNode next = null;
|
||||
while (cur != null) {
|
||||
next = cur.next;
|
||||
cur.next = headNode.next;
|
||||
headNode.next = cur;
|
||||
cur = next;
|
||||
}
|
||||
return headNode.next;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
```python3
|
||||
# 方法二 双向队列
|
||||
class Solution:
|
||||
def reorderList(self, head: ListNode) -> None:
|
||||
"""
|
||||
Do not return anything, modify head in-place instead.
|
||||
"""
|
||||
d = collections.deque()
|
||||
tmp = head
|
||||
while tmp.next: # 链表除了首元素全部加入双向队列
|
||||
d.append(tmp.next)
|
||||
tmp = tmp.next
|
||||
tmp = head
|
||||
while len(d): # 一后一前加入链表
|
||||
tmp.next = d.pop()
|
||||
tmp = tmp.next
|
||||
if len(d):
|
||||
tmp.next = d.popleft()
|
||||
tmp = tmp.next
|
||||
tmp.next = None # 尾部置空
|
||||
|
||||
# 方法三 反转链表
|
||||
class Solution:
|
||||
def reorderList(self, head: ListNode) -> None:
|
||||
if head == None or head.next == None:
|
||||
return True
|
||||
slow, fast = head, head
|
||||
while fast and fast.next:
|
||||
slow = slow.next
|
||||
fast = fast.next.next
|
||||
right = slow.next # 分割右半边
|
||||
slow.next = None # 切断
|
||||
right = self.reverseList(right) #反转右半边
|
||||
left = head
|
||||
# 左半边一定比右半边长, 因此判断右半边即可
|
||||
while right:
|
||||
curLeft = left.next
|
||||
left.next = right
|
||||
left = curLeft
|
||||
|
||||
curRight = right.next
|
||||
right.next = left
|
||||
right = curRight
|
||||
|
||||
|
||||
def reverseList(self, head: ListNode) -> ListNode:
|
||||
cur = head
|
||||
pre = None
|
||||
while(cur!=None):
|
||||
temp = cur.next # 保存一下cur的下一个节点
|
||||
cur.next = pre # 反转
|
||||
pre = cur
|
||||
cur = temp
|
||||
return pre
|
||||
```
|
||||
Go:
|
||||
|
||||
JavaScript:
|
||||
|
||||
-----------------------
|
||||
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -80,7 +80,7 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
|
||||
C++代码如下:
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int evalRPN(vector<string>& tokens) {
|
||||
@ -241,4 +241,4 @@ def evalRPN(tokens) -> int:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -61,7 +61,7 @@ https://leetcode-cn.com/problems/reverse-words-in-a-string/
|
||||
|
||||
思路很明确了,我们说一说代码的实现细节,就拿移除多余空格来说,一些同学会上来写如下代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
void removeExtraSpaces(string& s) {
|
||||
for (int i = s.size() - 1; i > 0; i--) {
|
||||
if (s[i] == s[i - 1] && s[i] == ' ') {
|
||||
@ -93,7 +93,7 @@ erase操作上面还套了一个for循环,那么以上代码移除冗余空格
|
||||
|
||||
那么使用双指针来移除冗余空格代码如下: fastIndex走的快,slowIndex走的慢,最后slowIndex就标记着移除多余空格后新字符串的长度。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
void removeExtraSpaces(string& s) {
|
||||
int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
|
||||
// 去掉字符串前面的空格
|
||||
@ -141,7 +141,7 @@ void reverse(string& s, int start, int end) {
|
||||
本题C++整体代码
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
@ -474,4 +474,4 @@ function reverse(strArr, start, end) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -1,2 +1,3 @@
|
||||
|
||||
同:[链表:链表相交](./面试题02.07.链表相交.md)
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -84,7 +84,7 @@ vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
|
||||
|
||||
同理可以类比剩下的状态,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int j = 0; j < 2 * k - 1; j += 2) {
|
||||
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
|
||||
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
|
||||
@ -117,7 +117,7 @@ for (int j = 0; j < 2 * k - 1; j += 2) {
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int j = 1; j < 2 * k; j += 2) {
|
||||
dp[0][j] = -prices[0];
|
||||
}
|
||||
@ -139,7 +139,7 @@ for (int j = 1; j < 2 * k; j += 2) {
|
||||
|
||||
以上分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxProfit(int k, vector<int>& prices) {
|
||||
@ -287,4 +287,4 @@ const maxProfit = (k,prices) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -69,7 +69,7 @@
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
void rotate(vector<int>& nums, int k) {
|
||||
@ -137,6 +137,6 @@ class Solution:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -25,7 +25,7 @@
|
||||
输出:12
|
||||
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
|
||||
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
|
||||
|
||||
|
||||
|
||||
提示:
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<int> dp(nums.size());
|
||||
dp[0] = nums[0];
|
||||
dp[1] = max(nums[0], nums[1]);
|
||||
@ -70,7 +70,7 @@ dp[1] = max(nums[0], nums[1]);
|
||||
dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么一定是从前到后遍历!
|
||||
|
||||
代码如下:
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 2; i < nums.size(); i++) {
|
||||
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
|
||||
}
|
||||
@ -86,7 +86,7 @@ for (int i = 2; i < nums.size(); i++) {
|
||||
|
||||
以上分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int rob(vector<int>& nums) {
|
||||
@ -175,6 +175,22 @@ func max(a, b int) int {
|
||||
}
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
|
||||
```javascript
|
||||
const rob = nums => {
|
||||
// 数组长度
|
||||
const len = nums.length;
|
||||
// dp数组初始化
|
||||
const dp = [nums[0], Math.max(nums[0], nums[1])];
|
||||
// 从下标2开始遍历
|
||||
for (let i = 2; i < len; i++) {
|
||||
dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
|
||||
}
|
||||
return dp[len - 1];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@ -182,4 +198,4 @@ func max(a, b int) int {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -46,7 +46,7 @@ https://leetcode-cn.com/problems/happy-number/
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
// 取数值各个位上的单数之和
|
||||
@ -111,25 +111,29 @@ Python:
|
||||
```python
|
||||
class Solution:
|
||||
def isHappy(self, n: int) -> bool:
|
||||
set_ = set()
|
||||
while 1:
|
||||
sum_ = self.getSum(n)
|
||||
if sum_ == 1:
|
||||
def calculate_happy(num):
|
||||
sum_ = 0
|
||||
|
||||
# 从个位开始依次取,平方求和
|
||||
while num:
|
||||
sum_ += (num % 10) ** 2
|
||||
num = num // 10
|
||||
return sum_
|
||||
|
||||
# 记录中间结果
|
||||
record = set()
|
||||
|
||||
while True:
|
||||
n = calculate_happy(n)
|
||||
if n == 1:
|
||||
return True
|
||||
#如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
|
||||
if sum_ in set_:
|
||||
|
||||
# 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
|
||||
if n in record:
|
||||
return False
|
||||
else:
|
||||
set_.add(sum_)
|
||||
n = sum_
|
||||
|
||||
#取数值各个位上的单数之和
|
||||
def getSum(self, n):
|
||||
sum_ = 0
|
||||
while n > 0:
|
||||
sum_ += (n%10) * (n%10)
|
||||
n //= 10
|
||||
return sum_
|
||||
record.add(n)
|
||||
|
||||
```
|
||||
|
||||
Go:
|
||||
@ -193,4 +197,4 @@ var isHappy = function(n) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -89,7 +89,7 @@ https://leetcode-cn.com/problems/remove-linked-list-elements/
|
||||
|
||||
**直接使用原来的链表来进行移除节点操作:**
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
ListNode* removeElements(ListNode* head, int val) {
|
||||
@ -118,7 +118,7 @@ public:
|
||||
|
||||
**设置一个虚拟头结点在进行移除节点操作:**
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
ListNode* removeElements(ListNode* head, int val) {
|
||||
@ -146,8 +146,39 @@ public:
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
C:
|
||||
```c
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* struct ListNode {
|
||||
* int val;
|
||||
* struct ListNode *next;
|
||||
* };
|
||||
*/
|
||||
|
||||
|
||||
struct ListNode* removeElements(struct ListNode* head, int val){
|
||||
typedef struct ListNode ListNode;
|
||||
ListNode *shead;
|
||||
shead = (ListNode *)malloc(sizeof(ListNode));
|
||||
shead->next = head;
|
||||
ListNode *cur = shead;
|
||||
while(cur->next != NULL){
|
||||
if (cur->next->val == val){
|
||||
ListNode *tmp = cur->next;
|
||||
cur->next = cur->next->next;
|
||||
free(tmp);
|
||||
}
|
||||
else{
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
head = shead->next;
|
||||
free(shead);
|
||||
return head;
|
||||
}
|
||||
```
|
||||
|
||||
Java:
|
||||
```java
|
||||
/**
|
||||
@ -273,6 +304,34 @@ var removeElements = function(head, val) {
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* public class ListNode {
|
||||
* public var val: Int
|
||||
* public var next: ListNode?
|
||||
* public init() { self.val = 0; self.next = nil; }
|
||||
* public init(_ val: Int) { self.val = val; self.next = nil; }
|
||||
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
|
||||
* }
|
||||
*/
|
||||
func removeElements(_ head: ListNode?, _ val: Int) -> ListNode? {
|
||||
let dummyNode = ListNode()
|
||||
dummyNode.next = head
|
||||
var currentNode = dummyNode
|
||||
while let curNext = currentNode.next {
|
||||
if curNext.val == val {
|
||||
currentNode.next = curNext.next
|
||||
} else {
|
||||
currentNode = curNext
|
||||
}
|
||||
}
|
||||
return dummyNode.next
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@ -280,4 +339,4 @@ var removeElements = function(head, val) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
C++代码 如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isIsomorphic(string s, string t) {
|
||||
@ -68,6 +68,25 @@ public:
|
||||
## Java
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public boolean isIsomorphic(String s, String t) {
|
||||
Map<Character, Character> map1 = new HashMap<>();
|
||||
Map<Character, Character> map2 = new HashMap<>();
|
||||
for (int i = 0, j = 0; i < s.length(); i++, j++) {
|
||||
if (!map1.containsKey(s.charAt(i))) {
|
||||
map1.put(s.charAt(i), t.charAt(j)); // map1保存 s[i] 到 t[j]的映射
|
||||
}
|
||||
if (!map2.containsKey(t.charAt(j))) {
|
||||
map2.put(t.charAt(j), s.charAt(i)); // map2保存 t[j] 到 s[i]的映射
|
||||
}
|
||||
// 无法映射,返回 false
|
||||
if (map1.get(s.charAt(i)) != t.charAt(j) || map2.get(t.charAt(j)) != s.charAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
@ -89,5 +108,5 @@ public:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -48,7 +48,7 @@ https://leetcode-cn.com/problems/reverse-linked-list/
|
||||
# C++代码
|
||||
|
||||
## 双指针法
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
ListNode* reverseList(ListNode* head) {
|
||||
@ -74,7 +74,7 @@ public:
|
||||
关键是初始化的地方,可能有的同学会不理解, 可以看到双指针法中初始化 cur = head,pre = NULL,在递归法中可以从如下代码看出初始化的逻辑也是一样的,只不过写法变了。
|
||||
|
||||
具体可以看代码(已经详细注释),**双指针法写出来之后,理解如下递归写法就不难了,代码逻辑都是一样的。**
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
ListNode* reverse(ListNode* pre,ListNode* cur){
|
||||
@ -275,11 +275,67 @@ var reverseList = function(head) {
|
||||
};
|
||||
```
|
||||
|
||||
Ruby:
|
||||
|
||||
```ruby
|
||||
# 双指针
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode
|
||||
# attr_accessor :val, :next
|
||||
# def initialize(val = 0, _next = nil)
|
||||
# @val = val
|
||||
# @next = _next
|
||||
# end
|
||||
# end
|
||||
def reverse_list(head)
|
||||
# return nil if head.nil? # 循环判断条件亦能起到相同作用因此不必单独判断
|
||||
cur, per = head, nil
|
||||
until cur.nil?
|
||||
tem = cur.next
|
||||
cur.next = per
|
||||
per = cur
|
||||
cur = tem
|
||||
end
|
||||
per
|
||||
end
|
||||
|
||||
# 递归
|
||||
# Definition for singly-linked list.
|
||||
# class ListNode
|
||||
# attr_accessor :val, :next
|
||||
# def initialize(val = 0, _next = nil)
|
||||
# @val = val
|
||||
# @next = _next
|
||||
# end
|
||||
# end
|
||||
def reverse_list(head)
|
||||
reverse(nil, head)
|
||||
end
|
||||
|
||||
def reverse(pre, cur)
|
||||
return pre if cur.nil?
|
||||
tem = cur.next
|
||||
cur.next = pre
|
||||
reverse(cur, tem) # 通过递归实现双指针法中的更新操作
|
||||
end
|
||||
```
|
||||
Kotlin:
|
||||
```Kotlin
|
||||
fun reverseList(head: ListNode?): ListNode? {
|
||||
var pre: ListNode? = null
|
||||
var cur = head
|
||||
while (cur != null) {
|
||||
val temp = cur.next
|
||||
cur.next = pre
|
||||
pre = cur
|
||||
cur = temp
|
||||
}
|
||||
return pre
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int minSubArrayLen(int s, vector<int>& nums) {
|
||||
@ -86,7 +86,7 @@ public:
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int minSubArrayLen(int s, vector<int>& nums) {
|
||||
@ -216,8 +216,31 @@ var minSubArrayLen = function(target, nums) {
|
||||
};
|
||||
```
|
||||
|
||||
Swift:
|
||||
|
||||
```swift
|
||||
func minSubArrayLen(_ target: Int, _ nums: [Int]) -> Int {
|
||||
var result = Int.max
|
||||
var sum = 0
|
||||
var starIndex = 0
|
||||
for endIndex in 0..<nums.count {
|
||||
sum += nums[endIndex]
|
||||
|
||||
while sum >= target {
|
||||
result = min(result, endIndex - starIndex + 1)
|
||||
sum -= nums[starIndex]
|
||||
starIndex += 1
|
||||
}
|
||||
}
|
||||
|
||||
return result == Int.max ? 0 : result
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 注意注释中的情况二情况三,以及把198.打家劫舍的代码抽离出来了
|
||||
class Solution {
|
||||
public:
|
||||
@ -174,4 +174,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -94,7 +94,7 @@ void backtracking(int targetSum, int k, int sum, int startIndex)
|
||||
|
||||
所以 终止代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (path.size() == k) {
|
||||
if (sum == targetSum) result.push_back(path);
|
||||
return; // 如果path.size() == k 但sum != targetSum 直接返回
|
||||
@ -112,7 +112,7 @@ if (path.size() == k) {
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = startIndex; i <= 9; i++) {
|
||||
sum += i;
|
||||
path.push_back(i);
|
||||
@ -126,7 +126,7 @@ for (int i = startIndex; i <= 9; i++) {
|
||||
|
||||
参照[关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)中的模板,不难写出如下C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
vector<vector<int>> result; // 存放结果集
|
||||
@ -398,4 +398,4 @@ var combinationSum3 = function(k, n) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -7,24 +7,23 @@
|
||||
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
|
||||
## 222.完全二叉树的节点个数
|
||||
# 222.完全二叉树的节点个数
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/count-complete-tree-nodes/
|
||||
|
||||
给出一个完全二叉树,求出该树的节点个数。
|
||||
|
||||
示例:
|
||||
示例 1:
|
||||
输入:root = [1,2,3,4,5,6]
|
||||
输出:6
|
||||
* 输入:root = [1,2,3,4,5,6]
|
||||
* 输出:6
|
||||
|
||||
示例 2:
|
||||
输入:root = []
|
||||
输出:0
|
||||
* 输入:root = []
|
||||
* 输出:0
|
||||
|
||||
示例 3:
|
||||
输入:root = [1]
|
||||
输出:1
|
||||
* 输入:root = [1]
|
||||
* 输出:1
|
||||
|
||||
提示:
|
||||
|
||||
@ -33,21 +32,22 @@
|
||||
* 题目数据保证输入的树是 完全二叉树
|
||||
|
||||
|
||||
## 思路
|
||||
# 思路
|
||||
|
||||
本篇给出按照普通二叉树的求法以及利用完全二叉树性质的求法。
|
||||
|
||||
|
||||
## 普通二叉树
|
||||
|
||||
首先按照普通二叉树的逻辑来求。
|
||||
|
||||
这道题目的递归法和求二叉树的深度写法类似, 而迭代法,[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。
|
||||
这道题目的递归法和求二叉树的深度写法类似, 而迭代法,[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。
|
||||
|
||||
递归遍历的顺序依然是后序(左右中)。
|
||||
|
||||
### 递归
|
||||
|
||||
如果对求二叉树深度还不熟悉的话,看这篇:[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)。
|
||||
如果对求二叉树深度还不熟悉的话,看这篇:[二叉树:看看这些树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)。
|
||||
|
||||
1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回以该节点为根节点二叉树的节点数量,所以返回值为int类型。
|
||||
|
||||
@ -77,7 +77,7 @@ return treeNum;
|
||||
|
||||
所以整体C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
private:
|
||||
@ -96,7 +96,7 @@ public:
|
||||
```
|
||||
|
||||
代码精简之后C++代码如下:
|
||||
```C++
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
@ -107,19 +107,19 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
时间复杂度:O(n)
|
||||
空间复杂度:O(logn),算上了递归系统栈占用的空间
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(logn),算上了递归系统栈占用的空间
|
||||
|
||||
**网上基本都是这个精简的代码版本,其实不建议大家照着这个来写,代码确实精简,但隐藏了一些内容,连遍历的顺序都看不出来,所以初学者建议学习版本一的代码,稳稳的打基础**。
|
||||
|
||||
|
||||
### 迭代法
|
||||
|
||||
如果对求二叉树层序遍历还不熟悉的话,看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/Gb3BjakIKGNpup2jYtTzog)。
|
||||
如果对求二叉树层序遍历还不熟悉的话,看这篇:[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)。
|
||||
|
||||
那么只要模板少做改动,加一个变量result,统计节点数量就可以了
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int countNodes(TreeNode* root) {
|
||||
@ -140,12 +140,12 @@ public:
|
||||
}
|
||||
};
|
||||
```
|
||||
时间复杂度:O(n)
|
||||
空间复杂度:O(n)
|
||||
* 时间复杂度:O(n)
|
||||
* 空间复杂度:O(n)
|
||||
|
||||
## 完全二叉树
|
||||
|
||||
以上方法都是按照普通二叉树来做的,对于完全二叉树特性不了解的同学可以看这篇 [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/_ymfWYvTNd2GvWvC5HOE4A),这篇详细介绍了各种二叉树的特性。
|
||||
以上方法都是按照普通二叉树来做的,对于完全二叉树特性不了解的同学可以看这篇 [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/q_eKfL8vmSbSFcptZ3aeRA),这篇详细介绍了各种二叉树的特性。
|
||||
|
||||
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
|
||||
|
||||
@ -163,7 +163,7 @@ public:
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int countNodes(TreeNode* root) {
|
||||
@ -187,13 +187,12 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
时间复杂度:O(logn * logn)
|
||||
空间复杂度:O(logn)
|
||||
* 时间复杂度:O(logn * logn)
|
||||
* 空间复杂度:O(logn)
|
||||
|
||||
## 其他语言版本
|
||||
# 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
## Java
|
||||
```java
|
||||
class Solution {
|
||||
// 通用递归解法
|
||||
@ -238,9 +237,9 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
## Python
|
||||
|
||||
> 递归法:
|
||||
递归法:
|
||||
```python
|
||||
class Solution:
|
||||
def countNodes(self, root: TreeNode) -> int:
|
||||
@ -255,7 +254,7 @@ class Solution:
|
||||
return treeNum
|
||||
```
|
||||
|
||||
> 递归法:精简版
|
||||
递归法:精简版
|
||||
```python
|
||||
class Solution:
|
||||
def countNodes(self, root: TreeNode) -> int:
|
||||
@ -264,7 +263,7 @@ class Solution:
|
||||
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
|
||||
```
|
||||
|
||||
> 迭代法:
|
||||
迭代法:
|
||||
```python
|
||||
import collections
|
||||
class Solution:
|
||||
@ -285,7 +284,7 @@ class Solution:
|
||||
return result
|
||||
```
|
||||
|
||||
> 完全二叉树
|
||||
完全二叉树
|
||||
```python
|
||||
class Solution:
|
||||
def countNodes(self, root: TreeNode) -> int:
|
||||
@ -306,7 +305,7 @@ class Solution:
|
||||
return self.countNodes(root.left) + self.countNodes(root.right) + 1
|
||||
```
|
||||
|
||||
Go:
|
||||
## Go
|
||||
|
||||
递归版本
|
||||
|
||||
@ -361,7 +360,7 @@ func countNodes(root *TreeNode) int {
|
||||
|
||||
|
||||
|
||||
JavaScript:
|
||||
## JavaScript:
|
||||
|
||||
递归版本
|
||||
```javascript
|
||||
@ -436,4 +435,4 @@ var countNodes = function(root) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -65,7 +65,7 @@ queue.empty();
|
||||
详细如代码注释所示:
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class MyStack {
|
||||
public:
|
||||
queue<int> que1;
|
||||
@ -118,7 +118,7 @@ public:
|
||||
|
||||
C++优化代码
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class MyStack {
|
||||
public:
|
||||
queue<int> que;
|
||||
@ -460,4 +460,4 @@ MyStack.prototype.empty = function() {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -89,7 +89,7 @@ invertTree(root->right);
|
||||
|
||||
基于这递归三步法,代码基本写完,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* invertTree(TreeNode* root) {
|
||||
@ -111,7 +111,7 @@ public:
|
||||
|
||||
C++代码迭代法(前序遍历)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* invertTree(TreeNode* root) {
|
||||
@ -136,7 +136,7 @@ public:
|
||||
|
||||
C++代码如下迭代法(前序遍历)
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* invertTree(TreeNode* root) {
|
||||
@ -168,7 +168,7 @@ public:
|
||||
|
||||
也就是层序遍历,层数遍历也是可以翻转这棵树的,因为层序遍历也可以把每个节点的左右孩子都翻转一遍,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* invertTree(TreeNode* root) {
|
||||
@ -196,7 +196,7 @@ public:
|
||||
|
||||
如果非要使用递归中序的方式写,也可以,如下代码就可以避免节点左右孩子翻转两次的情况:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* invertTree(TreeNode* root) {
|
||||
@ -215,7 +215,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* invertTree(TreeNode* root) {
|
||||
@ -478,4 +478,4 @@ var invertTree = function(root) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -67,7 +67,7 @@ queue.empty();
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class MyQueue {
|
||||
public:
|
||||
stack<int> stIn;
|
||||
@ -384,7 +384,7 @@ func (this *MyQueue) Peek() int {
|
||||
func (this *MyQueue) Empty() bool {
|
||||
return len(this.stack) == 0 && len(this.back) == 0
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
javaScript:
|
||||
|
||||
@ -442,10 +442,8 @@ MyQueue.prototype.empty = function() {
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
代码也比较简单。如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isPalindrome(ListNode* head) {
|
||||
@ -51,7 +51,7 @@ public:
|
||||
|
||||
上面代码可以在优化,就是先求出链表长度,然后给定vector的初始长度,这样避免vector每次添加节点重新开辟空间
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isPalindrome(ListNode* head) {
|
||||
@ -95,7 +95,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isPalindrome(ListNode* head) {
|
||||
@ -148,7 +148,62 @@ public:
|
||||
|
||||
## Python
|
||||
|
||||
```python
|
||||
```python3
|
||||
#数组模拟
|
||||
class Solution:
|
||||
def isPalindrome(self, head: ListNode) -> bool:
|
||||
length = 0
|
||||
tmp = head
|
||||
while tmp: #求链表长度
|
||||
length += 1
|
||||
tmp = tmp.next
|
||||
|
||||
result = [0] * length
|
||||
tmp = head
|
||||
index = 0
|
||||
while tmp: #链表元素加入数组
|
||||
result[index] = tmp.val
|
||||
index += 1
|
||||
tmp = tmp.next
|
||||
|
||||
i, j = 0, length - 1
|
||||
while i < j: # 判断回文
|
||||
if result[i] != result[j]:
|
||||
return False
|
||||
i += 1
|
||||
j -= 1
|
||||
return True
|
||||
|
||||
#反转后半部分链表
|
||||
class Solution:
|
||||
def isPalindrome(self, head: ListNode) -> bool:
|
||||
if head == None or head.next == None:
|
||||
return True
|
||||
slow, fast = head, head
|
||||
while fast and fast.next:
|
||||
pre = slow
|
||||
slow = slow.next
|
||||
fast = fast.next.next
|
||||
|
||||
pre.next = None # 分割链表
|
||||
cur1 = head # 前半部分
|
||||
cur2 = self.reverseList(slow) # 反转后半部分,总链表长度如果是奇数,cur2比cur1多一个节点
|
||||
while cur1:
|
||||
if cur1.val != cur2.val:
|
||||
return False
|
||||
cur1 = cur1.next
|
||||
cur2 = cur2.next
|
||||
return True
|
||||
|
||||
def reverseList(self, head: ListNode) -> ListNode:
|
||||
cur = head
|
||||
pre = None
|
||||
while(cur!=None):
|
||||
temp = cur.next # 保存一下cur的下一个节点
|
||||
cur.next = pre # 反转
|
||||
pre = cur
|
||||
cur = temp
|
||||
return pre
|
||||
```
|
||||
|
||||
## Go
|
||||
@ -166,5 +221,5 @@ public:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -93,7 +93,7 @@ if (cur == NULL) return cur;
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (cur->val > p->val && cur->val > q->val) {
|
||||
TreeNode* left = traversal(cur->left, p, q);
|
||||
if (left != NULL) {
|
||||
@ -147,7 +147,7 @@ return cur;
|
||||
|
||||
那么整体递归代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {
|
||||
@ -177,7 +177,7 @@ public:
|
||||
|
||||
精简后代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
|
||||
@ -198,7 +198,7 @@ public:
|
||||
|
||||
迭代代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
|
||||
@ -360,4 +360,4 @@ var lowestCommonAncestor = function(root, p, q) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -150,7 +150,7 @@ TreeNode* right = lowestCommonAncestor(root->right, p, q);
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (left == NULL && right != NULL) return right;
|
||||
else if (left != NULL && right == NULL) return left;
|
||||
else { // (left == NULL && right == NULL)
|
||||
@ -167,7 +167,7 @@ else { // (left == NULL && right == NULL)
|
||||
|
||||
整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
|
||||
@ -188,7 +188,7 @@ public:
|
||||
|
||||
稍加精简,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
|
||||
@ -249,7 +249,6 @@ class Solution {
|
||||
```java
|
||||
// 代码精简版
|
||||
class Solution {
|
||||
TreeNode pre;
|
||||
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
|
||||
if (root == null || root.val == p.val ||root.val == q.val) return root;
|
||||
TreeNode left = lowestCommonAncestor(root.left,p,q);
|
||||
@ -343,4 +342,4 @@ var lowestCommonAncestor = function(root, p, q) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -108,7 +108,7 @@ public:
|
||||
|
||||
基于刚刚说过的单调队列pop和push的规则,代码不难实现,如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class MyQueue { //单调队列(从大到小)
|
||||
public:
|
||||
deque<int> que; // 使用deque来实现单调队列
|
||||
@ -140,7 +140,7 @@ public:
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
class MyQueue { //单调队列(从大到小)
|
||||
@ -425,4 +425,4 @@ var maxSlidingWindow = function (nums, k) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -61,7 +61,7 @@ https://leetcode-cn.com/problems/valid-anagram/
|
||||
|
||||
|
||||
C++ 代码如下:
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
bool isAnagram(string s, string t) {
|
||||
@ -209,4 +209,4 @@ var isAnagram = function(s, t) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
> 以为只用了递归,其实还用了回溯
|
||||
|
||||
## 257. 二叉树的所有路径
|
||||
# 257. 二叉树的所有路径
|
||||
|
||||
题目地址:https://leetcode-cn.com/problems/binary-tree-paths/
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
示例:
|
||||

|
||||
|
||||
## 思路
|
||||
# 思路
|
||||
|
||||
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
|
||||
|
||||
@ -77,7 +77,7 @@ if (cur->left == NULL && cur->right == NULL) {
|
||||
|
||||
这里我们先使用vector<int>结构的path容器来记录路径,那么终止处理逻辑如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (cur->left == NULL && cur->right == NULL) { // 遇到叶子节点
|
||||
string sPath;
|
||||
for (int i = 0; i < path.size() - 1; i++) { // 将path里记录的路径转为string格式
|
||||
@ -113,7 +113,7 @@ if (cur->right) {
|
||||
|
||||
那么回溯要怎么回溯呢,一些同学会这么写,如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (cur->left) {
|
||||
traversal(cur->left, path, result);
|
||||
}
|
||||
@ -129,7 +129,7 @@ path.pop_back();
|
||||
|
||||
那么代码应该这么写:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
if (cur->left) {
|
||||
traversal(cur->left, path, result);
|
||||
path.pop_back(); // 回溯
|
||||
@ -142,7 +142,7 @@ if (cur->right) {
|
||||
|
||||
那么本题整体代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
|
||||
@ -183,7 +183,7 @@ public:
|
||||
|
||||
那么如上代码可以精简成如下代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
|
||||
@ -215,8 +215,52 @@ public:
|
||||
|
||||
那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。** 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。
|
||||
|
||||
**如果这里还不理解的话,可以看这篇[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA),我这这篇中详细的解释了递归中如何隐藏着回溯。 **
|
||||
为了把这份精简代码的回溯过程展现出来,大家可以试一试把:
|
||||
|
||||
```CPP
|
||||
if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里
|
||||
```
|
||||
|
||||
改成如下代码:
|
||||
|
||||
```CPP
|
||||
path += "->";
|
||||
traversal(cur->left, path, result); // 左
|
||||
```
|
||||
|
||||
即:
|
||||
|
||||
```CPP
|
||||
if (cur->left) {
|
||||
path += "->";
|
||||
traversal(cur->left, path, result); // 左
|
||||
}
|
||||
if (cur->right) {
|
||||
path += "->";
|
||||
traversal(cur->right, path, result); // 右
|
||||
}
|
||||
```
|
||||
|
||||
此时就没有回溯了,这个代码就是通过不了的了。
|
||||
|
||||
如果想把回溯加上,就要 在上面代码的基础上,加上回溯,就可以AC了。
|
||||
|
||||
```CPP
|
||||
if (cur->left) {
|
||||
path += "->";
|
||||
traversal(cur->left, path, result); // 左
|
||||
path.pop_back(); // 回溯
|
||||
path.pop_back();
|
||||
}
|
||||
if (cur->right) {
|
||||
path += "->";
|
||||
traversal(cur->right, path, result); // 右
|
||||
path.pop_back(); // 回溯
|
||||
path.pop_back();
|
||||
}
|
||||
```
|
||||
|
||||
**大家应该可以感受出来,如果把 `path + "->"`作为函数参数就是可以的,因为并有没有改变path的数值,执行完递归函数之后,path依然是之前的数值(相当于回溯了)**
|
||||
|
||||
|
||||
**综合以上,第二种递归的代码虽然精简但把很多重要的点隐藏在了代码细节里,第一种递归写法虽然代码多一些,但是把每一个逻辑处理都完整的展现了出来了。**
|
||||
@ -225,13 +269,14 @@ public:
|
||||
|
||||
## 迭代法
|
||||
|
||||
至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,对该迭代方式不了解的同学,可以看文章[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/c_zCrGHIVlBjUH_hJtghCg)和[二叉树:前中后序迭代方式的写法就不能统一一下么?](https://mp.weixin.qq.com/s/WKg0Ty1_3SZkztpHubZPRg)。
|
||||
|
||||
至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,对该迭代方式不了解的同学,可以看文章[二叉树:听说递归能做的,栈也能做!](https://mp.weixin.qq.com/s/OH7aCVJ5-Gi32PkNCoZk4A)和[二叉树:前中后序迭代方式统一写法](https://mp.weixin.qq.com/s/ATQMPCpBlaAgrqdLDMVPZA)。
|
||||
|
||||
这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<string> binaryTreePaths(TreeNode* root) {
|
||||
@ -262,7 +307,7 @@ public:
|
||||
```
|
||||
当然,使用java的同学,可以直接定义一个成员变量为object的栈`Stack<Object> stack = new Stack<>();`,这样就不用定义两个栈了,都放到一个栈里就可以了。
|
||||
|
||||
## 总结
|
||||
# 总结
|
||||
|
||||
**本文我们开始初步涉及到了回溯,很多同学过了这道题目,可能都不知道自己其实使用了回溯,回溯和递归都是相伴相生的。**
|
||||
|
||||
@ -278,7 +323,7 @@ public:
|
||||
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
# 其他语言版本
|
||||
|
||||
Java:
|
||||
|
||||
@ -321,62 +366,10 @@ class Solution {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//解法二(常规前序遍历,不用回溯),更容易理解
|
||||
class Solution {
|
||||
public List<String> binaryTreePaths(TreeNode root) {
|
||||
List<String> res = new ArrayList<>();
|
||||
helper(root, new StringBuilder(), res);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void helper(TreeNode root, StringBuilder sb, List<String> res) {
|
||||
if (root == null) {return;}
|
||||
// 遇到叶子结点就放入当前路径到res集合中
|
||||
if (root.left == null && root.right ==null) {
|
||||
sb.append(root.val);
|
||||
res.add(sb.toString());
|
||||
// 记得结束当前方法
|
||||
return;
|
||||
}
|
||||
helper(root.left,new StringBuilder(sb).append(root.val + "->"),res);
|
||||
helper(root.right,new StringBuilder(sb).append(root.val + "->"),res);
|
||||
}
|
||||
}
|
||||
|
||||
//针对解法二优化,思路本质是一样的
|
||||
class Solution {
|
||||
public List<String> binaryTreePaths(TreeNode root) {
|
||||
List<String> res = new ArrayList<>();
|
||||
helper(root, "", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void helper(TreeNode root, String path, List<String> res) {
|
||||
if (root == null) {return;}
|
||||
// 由原始解法二可以知道,root的值肯定会下面某一个条件加入到path中,那么干脆直接在这一步加入即可
|
||||
StringBuilder sb = new StringBuilder(path);
|
||||
sb.append(root.val);
|
||||
if (root.left == null && root.right ==null) {
|
||||
res.add(sb.toString());
|
||||
}else{
|
||||
// 如果是非叶子结点则还需要跟上一个 “->”
|
||||
sb.append("->");
|
||||
helper(root.left,sb.toString(),res);
|
||||
helper(root.right,sb.toString(),res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Python:
|
||||
```Python
|
||||
# class TreeNode:
|
||||
# def __init__(self, val=0, left=None, right=None):
|
||||
# self.val = val
|
||||
# self.left = left
|
||||
# self.right = right
|
||||
class Solution:
|
||||
def binaryTreePaths(self, root: TreeNode) -> List[str]:
|
||||
path=[]
|
||||
@ -396,8 +389,8 @@ class Solution:
|
||||
return ["->".join(list(map(str,i))) for i in res]
|
||||
|
||||
```
|
||||
Go:
|
||||
|
||||
Go:
|
||||
```go
|
||||
func binaryTreePaths(root *TreeNode) []string {
|
||||
res := make([]string, 0)
|
||||
@ -422,7 +415,9 @@ func binaryTreePaths(root *TreeNode) []string {
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
|
||||
1.递归版本
|
||||
|
||||
```javascript
|
||||
var binaryTreePaths = function(root) {
|
||||
//递归遍历+递归三部曲
|
||||
@ -452,4 +447,4 @@ var binaryTreePaths = function(root) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -76,7 +76,7 @@ dp[0]表示 和为0的完全平方数的最小数量,那么dp[0]一定是0。
|
||||
|
||||
我这里先给出外层遍历背包,里层遍历物品的代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<int> dp(n + 1, INT_MAX);
|
||||
dp[0] = 0;
|
||||
for (int i = 0; i <= n; i++) { // 遍历背包
|
||||
@ -106,7 +106,7 @@ dp[5] = min(dp[4] + 1, dp[1] + 1) = 2
|
||||
|
||||
以上动规五部曲分析完毕C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
@ -125,7 +125,7 @@ public:
|
||||
|
||||
同样我在给出先遍历物品,在遍历背包的代码,一样的可以AC的。
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
@ -286,10 +286,38 @@ func min(a, b int) int {
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
```Javascript
|
||||
// 先遍历物品,再遍历背包
|
||||
var numSquares1 = function(n) {
|
||||
let dp = new Array(n + 1).fill(Infinity)
|
||||
dp[0] = 0
|
||||
|
||||
for(let i = 0; i <= n; i++) {
|
||||
let val = i * i
|
||||
for(let j = val; j <= n; j++) {
|
||||
dp[j] = Math.min(dp[j], dp[j - val] + 1)
|
||||
}
|
||||
}
|
||||
return dp[n]
|
||||
};
|
||||
// 先遍历背包,再遍历物品
|
||||
var numSquares2 = function(n) {
|
||||
let dp = new Array(n + 1).fill(Infinity)
|
||||
dp[0] = 0
|
||||
|
||||
for(let i = 1; i <= n; i++) {
|
||||
for(let j = 1; j * j <= i; j++) {
|
||||
dp[i] = Math.min(dp[i - j * j] + 1, dp[i])
|
||||
}
|
||||
}
|
||||
|
||||
return dp[n]
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -42,7 +42,7 @@
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
void moveZeroes(vector<int>& nums) {
|
||||
@ -64,6 +64,21 @@ public:
|
||||
|
||||
Java:
|
||||
|
||||
```java
|
||||
public void moveZeroes(int[] nums) {
|
||||
int slow = 0;
|
||||
for (int fast = 0; fast < nums.length; fast++) {
|
||||
if (nums[fast] != 0) {
|
||||
nums[slow++] = nums[fast];
|
||||
}
|
||||
}
|
||||
// 后面的元素全变成 0
|
||||
for (int j = slow; j < nums.length; j++) {
|
||||
nums[j] = 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
@ -85,5 +100,5 @@ JavaScript:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -60,7 +60,7 @@ dp[i] 是有0到i-1各个位置的最长升序子序列 推导而来,那么遍
|
||||
|
||||
j其实就是0到i-1,遍历i的循环里外层,遍历j则在内层,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (int i = 1; i < nums.size(); i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
|
||||
@ -80,7 +80,7 @@ for (int i = 1; i < nums.size(); i++) {
|
||||
|
||||
以上五部分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int lengthOfLIS(vector<int>& nums) {
|
||||
@ -198,4 +198,4 @@ const lengthOfLIS = (nums) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -95,7 +95,7 @@ p[i][3] = dp[i - 1][2];
|
||||
|
||||
综上分析,递推代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i];
|
||||
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
|
||||
dp[i][2] = dp[i - 1][0] + prices[i];
|
||||
@ -129,7 +129,7 @@ dp[i][3] = dp[i - 1][2];
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int maxProfit(vector<int>& prices) {
|
||||
@ -236,4 +236,4 @@ const maxProfit = (prices) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -112,7 +112,7 @@ dp[amount]为最终结果。
|
||||
## C++代码
|
||||
以上分析完毕,C++ 代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
@ -134,7 +134,7 @@ public:
|
||||
|
||||
对于遍历方式遍历背包放在外循环,遍历物品放在内循环也是可以的,我就直接给出代码了
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 版本二
|
||||
class Solution {
|
||||
public:
|
||||
@ -330,4 +330,4 @@ const coinChange = (coins, amount) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -178,7 +178,7 @@ if (result.size() == ticketNum + 1) {
|
||||
|
||||
遍历过程如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
|
||||
if (target.second > 0 ) { // 记录到达机场是否飞过了
|
||||
result.push_back(target.first);
|
||||
@ -194,7 +194,7 @@ for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
|
||||
|
||||
分析完毕,此时完整C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
private:
|
||||
// unordered_map<出发机场, map<到达机场, 航班次数>> targets
|
||||
@ -450,4 +450,4 @@ var findItinerary = function(tickets) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int rob(TreeNode* root) {
|
||||
@ -65,7 +65,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
unordered_map<TreeNode* , int> umap; // 记录计算过的结果
|
||||
@ -103,7 +103,7 @@ public:
|
||||
|
||||
参数为当前节点,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<int> robTree(TreeNode* cur) {
|
||||
```
|
||||
|
||||
@ -138,7 +138,7 @@ if (cur == NULL) return vector<int>{0, 0};
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 下标0:不偷,下标1:偷
|
||||
vector<int> left = robTree(cur->left); // 左
|
||||
vector<int> right = robTree(cur->right); // 右
|
||||
@ -156,7 +156,7 @@ vector<int> right = robTree(cur->right); // 右
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
vector<int> left = robTree(cur->left); // 左
|
||||
vector<int> right = robTree(cur->right); // 右
|
||||
|
||||
@ -179,7 +179,7 @@ return {val2, val1};
|
||||
|
||||
递归三部曲与动规五部曲分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int rob(TreeNode* root) {
|
||||
@ -368,7 +368,32 @@ class Solution:
|
||||
return (val1, val2)
|
||||
```
|
||||
|
||||
Go:
|
||||
JavaScript:
|
||||
|
||||
> 动态规划
|
||||
|
||||
```javascript
|
||||
const rob = root => {
|
||||
// 后序遍历函数
|
||||
const postOrder = node => {
|
||||
// 递归出口
|
||||
if (!node) return [0, 0];
|
||||
// 遍历左子树
|
||||
const left = postOrder(node.left);
|
||||
// 遍历右子树
|
||||
const right = postOrder(node.right);
|
||||
// 不偷当前节点,左右子节点都可以偷或不偷,取最大值
|
||||
const DoNot = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
|
||||
// 偷当前节点,左右子节点只能不偷
|
||||
const Do = node.val + left[0] + right[0];
|
||||
// [不偷,偷]
|
||||
return [DoNot, Do];
|
||||
};
|
||||
const res = postOrder(root);
|
||||
// 返回最大值
|
||||
return Math.max(...res);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -377,4 +402,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -59,6 +59,10 @@ j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算
|
||||
|
||||
所以递推公式:dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});
|
||||
|
||||
那么在取最大值的时候,为什么还要比较dp[i]呢?
|
||||
|
||||
因为在递推公式推导的过程中,每次计算dp[i],取最大的而已。
|
||||
|
||||
|
||||
3. dp的初始化
|
||||
|
||||
@ -101,7 +105,7 @@ for (int i = 3; i <= n ; i++) {
|
||||
|
||||
以上动规五部曲分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int integerBreak(int n) {
|
||||
@ -128,7 +132,7 @@ public:
|
||||
|
||||
给出我的C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int integerBreak(int n) {
|
||||
@ -154,7 +158,7 @@ public:
|
||||
|
||||
其实这道题目的递推公式并不好想,而且初始化的地方也很有讲究,我在写本题的时候一开始写的代码是这样的:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int integerBreak(int n) {
|
||||
@ -244,4 +248,4 @@ var integerBreak = function(n) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -74,7 +74,7 @@ https://leetcode-cn.com/problems/reverse-string/
|
||||
|
||||
不难写出如下C++代码:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
void reverseString(vector<char>& s) {
|
||||
for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {
|
||||
swap(s[i],s[j]);
|
||||
@ -90,7 +90,7 @@ swap可以有两种实现。
|
||||
|
||||
一种就是常见的交换数值:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
int tmp = s[i];
|
||||
s[i] = s[j];
|
||||
s[j] = tmp;
|
||||
@ -99,7 +99,7 @@ s[j] = tmp;
|
||||
|
||||
一种就是通过位运算:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
s[i] ^= s[j];
|
||||
s[j] ^= s[i];
|
||||
s[i] ^= s[j];
|
||||
@ -120,7 +120,7 @@ s[i] ^= s[j];
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
void reverseString(vector<char>& s) {
|
||||
@ -219,4 +219,4 @@ var reverseString = function(s) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -76,7 +76,7 @@ https://leetcode-cn.com/problems/top-k-frequent-elements/
|
||||
我们来看一下C++代码:
|
||||
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 时间复杂度:O(nlogk)
|
||||
// 空间复杂度:O(n)
|
||||
class Solution {
|
||||
@ -190,10 +190,105 @@ class Solution:
|
||||
Go:
|
||||
|
||||
|
||||
javaScript:
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @param {number} k
|
||||
* @return {number[]}
|
||||
*/
|
||||
var topKFrequent = function(nums, k) {
|
||||
const map = new Map();
|
||||
|
||||
for(const num of nums) {
|
||||
map.set(num, (map.get(num) || 0) + 1);
|
||||
}
|
||||
|
||||
// 创建小顶堆
|
||||
const priorityQueue = new PriorityQueue((a, b) => a[1] - b[1]);
|
||||
|
||||
// entry 是一个长度为2的数组,0位置存储key,1位置存储value
|
||||
for (const entry of map.entries()) {
|
||||
priorityQueue.push(entry);
|
||||
if (priorityQueue.size() > k) {
|
||||
priorityQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
const ret = [];
|
||||
|
||||
for(let i = priorityQueue.size() - 1; i >= 0; i--) {
|
||||
ret[i] = priorityQueue.pop()[0];
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
function PriorityQueue(compareFn) {
|
||||
this.compareFn = compareFn;
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
// 添加
|
||||
PriorityQueue.prototype.push = function(item) {
|
||||
this.queue.push(item);
|
||||
let index = this.queue.length - 1;
|
||||
let parent = Math.floor((index - 1) / 2);
|
||||
// 上浮
|
||||
while(parent >= 0 && this.compare(parent, index) > 0) {
|
||||
// 交换
|
||||
[this.queue[index], this.queue[parent]] = [this.queue[parent], this.queue[index]];
|
||||
index = parent;
|
||||
parent = Math.floor((index - 1) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取堆顶元素并移除
|
||||
PriorityQueue.prototype.pop = function() {
|
||||
const ret = this.queue[0];
|
||||
|
||||
// 把最后一个节点移到堆顶
|
||||
this.queue[0] = this.queue.pop();
|
||||
|
||||
let index = 0;
|
||||
// 左子节点下标,left + 1 就是右子节点下标
|
||||
let left = 1;
|
||||
let selectedChild = this.compare(left, left + 1) > 0 ? left + 1 : left;
|
||||
|
||||
// 下沉
|
||||
while(selectedChild !== undefined && this.compare(index, selectedChild) > 0) {
|
||||
// 交换
|
||||
[this.queue[index], this.queue[selectedChild]] = [this.queue[selectedChild], this.queue[index]];
|
||||
index = selectedChild;
|
||||
left = 2 * index + 1;
|
||||
selectedChild = this.compare(left, left + 1) > 0 ? left + 1 : left;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PriorityQueue.prototype.size = function() {
|
||||
return this.queue.length;
|
||||
}
|
||||
|
||||
// 使用传入的 compareFn 比较两个位置的元素
|
||||
PriorityQueue.prototype.compare = function(index1, index2) {
|
||||
if (this.queue[index1] === undefined) {
|
||||
return 1;
|
||||
}
|
||||
if (this.queue[index2] === undefined) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.compareFn(this.queue[index1], this.queue[index2]);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -54,7 +54,7 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
|
||||
@ -121,13 +121,7 @@ Python:
|
||||
```python
|
||||
class Solution:
|
||||
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
|
||||
result_set = set()
|
||||
|
||||
set1 = set(nums1)
|
||||
for num in nums2:
|
||||
if num in set1:
|
||||
result_set.add(num) # set1里出现的nums2元素 存放到结果
|
||||
return result_set
|
||||
return list(set(nums1) & set(nums2)) # 两个数组先变成集合,求交集后还原为数组
|
||||
```
|
||||
|
||||
|
||||
@ -188,4 +182,4 @@ var intersection = function(nums1, nums2) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -70,7 +70,7 @@
|
||||
|
||||
C++代码如下(和上图是对应的逻辑):
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int wiggleMaxLength(vector<int>& nums) {
|
||||
@ -192,4 +192,4 @@ var wiggleMaxLength = function(nums) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -107,7 +107,7 @@ dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导
|
||||
|
||||
以上分析完毕,C++代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
class Solution {
|
||||
public:
|
||||
int combinationSum4(vector<int>& nums, int target) {
|
||||
@ -226,4 +226,4 @@ const combinationSum4 = (nums, target) => {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
@ -40,7 +40,7 @@ canConstruct("aa", "aab") -> true
|
||||
|
||||
那么第一个思路其实就是暴力枚举了,两层for循环,不断去寻找,代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 时间复杂度: O(n^2)
|
||||
// 空间复杂度:O(1)
|
||||
class Solution {
|
||||
@ -79,7 +79,7 @@ public:
|
||||
|
||||
代码如下:
|
||||
|
||||
```C++
|
||||
```CPP
|
||||
// 时间复杂度: O(n)
|
||||
// 空间复杂度:O(1)
|
||||
class Solution {
|
||||
@ -209,6 +209,22 @@ class Solution(object):
|
||||
return True
|
||||
```
|
||||
|
||||
Python写法四:
|
||||
|
||||
```python3
|
||||
class Solution:
|
||||
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
|
||||
c1 = collections.Counter(ransomNote)
|
||||
c2 = collections.Counter(magazine)
|
||||
x = c1 - c2
|
||||
#x只保留值大于0的符号,当c1里面的符号个数小于c2时,不会被保留
|
||||
#所以x只保留下了,magazine不能表达的
|
||||
if(len(x)==0):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
```go
|
||||
@ -256,4 +272,4 @@ var canConstruct = function(ransomNote, magazine) {
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user