Merge branch 'youngyangyang04:master' into master

This commit is contained in:
飞鸟
2021-08-09 10:10:37 +08:00
committed by GitHub
43 changed files with 1462 additions and 696 deletions

View File

@ -184,6 +184,25 @@ 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)

View File

@ -228,6 +228,27 @@ 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)

View File

@ -216,6 +216,25 @@ 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)

View File

@ -56,9 +56,9 @@
## 寻找右边界
先来寻找右边界,至于二分查找,如果看过[为什么每次遇到二分法,都是一看就会,一写就废](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)这篇文章先看了在把leetcode35.搜索插入位置做了之后做这道题目就好很多了
那么这里我采用while (left <= right)的写法区间定义为[left, right]即左闭又闭的区间如果这里有点看不懂了强烈建议把[704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)这篇文章先看了704题目做了之后做这道题目就好很多了
确定好计算出来的右边界是不包好target的右边界左边界同理

View File

@ -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,6 +272,42 @@ 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)

View File

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

View File

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

View File

@ -91,12 +91,12 @@ 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增加一个元素使其word1[i - 1]与word2[j - 1]相同那么就是以下标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添加一个元素使其word1[i - 1]与word2[j - 1]相同那么就是以下标i-1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个增加元素的操作。
`dp[i][j] = dp[i][j - 1] + 1;`

View File

@ -0,0 +1,193 @@
# 84.柱状图中最大的矩形
链接https://leetcode-cn.com/problems/largest-rectangle-in-histogram/
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210803220437.png)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210803220506.png)
# 思路
本题和[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw),是遥相呼应的两道题目,建议都要仔细做一做,原理上有很多相同的地方,但细节上又有差异,更可以加深对单调栈的理解!
其实这两道题目先做那一道都可以但我先写的42.接雨水的题解,所以如果没做过接雨水的话,建议先做一做接雨水,可以参考我的题解:[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)
我们先来看一下双指针的解法:
## 双指针解法
```C++
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)中已经介绍了。
```C++
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)中我讲解了接雨水的单调栈从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。
那么因为本题是要找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出)到栈底的顺序应该是从大到小的顺序!
我来举一个例子,如图:
![84.柱状图中最大的矩形](https://img-blog.csdnimg.cn/20210223155303971.jpg)
只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。
所以本题单调栈的顺序正好与接雨水反过来。
此时大家应该可以发现其实就是**栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度**
理解这一点,对单调栈就掌握的比较到位了。
除了栈内元素顺序和接雨水不同,剩下的逻辑就都差不多了,在题解[42. 接雨水](https://mp.weixin.qq.com/s/QogENxhotboct9wn7GgYUw)我已经对单调栈的各个方面做了详细讲解,这里就不赘述了。
剩下就是分析清楚如下三种情况:
* 情况一当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况
* 情况二当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况
* 情况三当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况
C++代码如下:
```C++
// 版本一
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;
}
};
```
代码精简之后:
```C++
// 版本二
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;
}
};
```
这里我依然建议大家按部就班把版本一写出来,把情况一二三分析清楚,然后在精简代码到版本二。 直接看版本二容易忽略细节!

View File

@ -139,29 +139,29 @@ public:
## 迭代法
```C++
lass Solution {
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;
}
@ -173,23 +173,71 @@ public:
Java
```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;
} else {
return isSameTree(q.left, p.left) && isSameTree(q.right, p.right);
}
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

View File

@ -251,6 +251,8 @@ public:
# 相关题目推荐
这两道题目基本和本题是一样的只要稍加修改就可以AC。
* 100.相同的树
* 572.另一个树的子树

View File

@ -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个还不够再给我一次机会我打十个
![我要打十个](https://tva1.sinaimg.cn/large/008eGmZEly1gnadnltbpjg309603w4qp.gif)
## 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++代码:
```
```C++
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/
@ -404,7 +408,7 @@ var levelOrderBottom = function(root) {
```
## 199.二叉树的右视图
# 199.二叉树的右视图
题目链接https://leetcode-cn.com/problems/binary-tree-right-side-view/
@ -581,7 +585,7 @@ var rightSideView = function(root) {
};
```
## 637.二叉树的层平均值
# 637.二叉树的层平均值
题目链接https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/
@ -765,7 +769,7 @@ var averageOfLevels = function(root) {
};
```
## 429.N叉树的层序遍历
# 429.N叉树的层序遍历
题目链接https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/
@ -985,7 +989,7 @@ var levelOrder = function(root) {
};
```
## 515.在每个树行中找最大值
# 515.在每个树行中找最大值
题目链接https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/
@ -1115,7 +1119,7 @@ var largestValues = function(root) {
};
```
## 116.填充每个节点的下一个右侧节点指针
# 116.填充每个节点的下一个右侧节点指针
题目链接https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/
@ -1254,45 +1258,8 @@ func connect(root *Node) *Node {
}
```
Java 代码:
# 117.填充每个节点的下一个右侧节点指针II
```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;
}
}
```
## 117.填充每个节点的下一个右侧节点指针II
题目地址https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/
@ -1333,6 +1300,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
@ -1416,12 +1421,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]
![104. 二叉树的最大深度](https://img-blog.csdnimg.cn/20210203153031914.png)
返回它的最大深度 3 。
思路:
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示:
![层序遍历](https://img-blog.csdnimg.cn/20200810193056585.png)
所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。
C++代码如下:
```C++
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.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
代码如下:(详细注释)
```C++
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
@ -1431,287 +1543,13 @@ func connect(root *Node) *Node {
* 515.在每个树行中找最大值
* 116.填充每个节点的下一个右侧节点指针
* 117.填充每个节点的下一个右侧节点指针II
* 104.二叉树的最大深度
* 111.二叉树的最小深度
如果非要打十个,还得找叶师傅!
![我要打十个](https://tva1.sinaimg.cn/large/008eGmZEly1gnadnltbpjg309603w4qp.gif)
**致敬叶师傅!**
# 其他语言版本
> 二叉树的层序遍历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)

View File

@ -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叉树 :
![559.N叉树的最大深度](https://img-blog.csdnimg.cn/2021020315313214.png)
![559.n叉树的最大深度](https://img-blog.csdnimg.cn/2021020315313214.png)
我们应返回其最大深度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);

View File

@ -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,11 +51,11 @@
因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
有的同学一定疑惑,为什么[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++
class Solution {
@ -114,9 +115,9 @@ public:
};
```
## 本题思路
# 本题思路
### 递归
## 递归
此时大家应该明白了既然要求比较高度,必然是要后序遍历。
@ -160,7 +161,7 @@ if (node == NULL) {
代码如下:
```
```C++
int leftDepth = depth(node->left); // 左
if (leftDepth == -1) return -1;
int rightDepth = depth(node->right); // 右
@ -178,7 +179,7 @@ return result;
代码精简之后如下:
```
```C++
int leftDepth = getDepth(node->left);
if (leftDepth == -1) return -1;
int rightDepth = getDepth(node->right);
@ -225,9 +226,9 @@ public:
};
```
### 迭代
## 迭代
在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/guKwV-gSNbA1CcbvkMtHBg)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
在[104.二叉树的最大深度](https://mp.weixin.qq.com/s/jRaRcRerhEHepQbt-aKstw)中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。
本题的迭代方式可以先定义一个函数,专门用来求高度。
@ -266,7 +267,7 @@ int getDepth(TreeNode* cur) {
然后再用栈来模拟前序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:
```
```C++
bool isBalanced(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL) return true;
@ -286,7 +287,7 @@ bool isBalanced(TreeNode* root) {
整体代码如下:
```
```C++
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

View File

@ -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),再来看看如何求最小深度。
直觉上好像和求最大深度差不多,其实还是差不少的。
@ -154,9 +154,9 @@ 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)
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点**
@ -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
递归法:

View File

@ -319,7 +319,20 @@ def reverse(pre, cur)
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)

View File

@ -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类型。
@ -107,15 +107,15 @@ 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统计节点数量就可以了
@ -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),这篇详细介绍了各种二叉树的特性。
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
@ -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

View File

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

View File

@ -9,7 +9,7 @@
> 以为只用了递归,其实还用了回溯
## 257. 二叉树的所有路径
# 257. 二叉树的所有路径
题目地址https://leetcode-cn.com/problems/binary-tree-paths/
@ -20,7 +20,7 @@
示例:
![257.二叉树的所有路径1](https://img-blog.csdnimg.cn/2021020415161576.png)
## 思路
# 思路
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
@ -215,8 +215,52 @@ public:
那么在如上代码中,**貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在`traversal(cur->left, path + "->", result);`中的 `path + "->"`。** 每次函数调用完path依然是没有加上"->" 的,这就是回溯了。
**如果这里还不理解的话,可以看这篇[二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA),我这这篇中详细的解释了递归中如何隐藏着回溯。 **
为了把这份精简代码的回溯过程展现出来,大家可以试一试把:
```C++
if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里
```
改成如下代码:
```C++
path += "->";
traversal(cur->left, path, result); // 左
```
即:
```C++
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左
}
if (cur->right) {
path += "->";
traversal(cur->right, path, result); // 右
}
```
此时就没有回溯了,这个代码就是通过不了的了。
如果想把回溯加上,就要 在上面代码的基础上加上回溯就可以AC了。
```C++
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,7 +269,8 @@ 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)。
这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。
@ -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) {
//递归遍历+递归三部曲

View File

@ -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的初始化

View File

@ -190,6 +190,101 @@ 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位置存储key1位置存储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]);
}
```
-----------------------

View File

@ -127,7 +127,7 @@ class Solution:
for num in nums2:
if num in set1:
result_set.add(num) # set1里出现的nums2元素 存放到结果
return result_set
return list(result_set)
```

View File

@ -217,7 +217,25 @@ class Solution:
```
Go
```golang
func reconstructQueue(people [][]int) [][]int {
//先将身高从大到小排序,确定最大个子的相对位置
sort.Slice(people,func(i,j int)bool{
if people[i][0]==people[j][0]{
return people[i][1]<people[j][1]//这个才是当身高相同时将K按照从小到大排序
}
return people[i][0]>people[j][0]//这个只是确保身高按照由大到小的顺序来排并不确定K是按照从小到大排序的
})
//再按照K进行插入排序优先插入K小的
result := make([][]int, 0)
for _, info := range people {
result = append(result, info)
copy(result[info[1] +1:], result[info[1]:])//将插入位置之后的元素后移动一位(意思是腾出空间)
result[info[1]] = info//将插入元素位置插入元素
}
return result
}
```
Javascript:
```Javascript
var reconstructQueue = function(people) {

View File

@ -250,7 +250,29 @@ class Solution:
```
Go
```golang
func eraseOverlapIntervals(intervals [][]int) int {
var flag int
//先排序
sort.Slice(intervals,func(i,j int)bool{
return intervals[i][0]<intervals[j][0]
})
fmt.Println(intervals)
for i:=1;i<len(intervals);i++{
if intervals[i-1][1]>intervals[i][0]{
flag++
intervals[i][1]=min(intervals[i-1][1],intervals[i][1])//由于是先排序的,所以,第一位是递增顺序,故只需要将临近两个元素的第二个值最小值更新到该元素的第二个值即可作之后的判断
}
}
return flag
}
func min(a,b int)int{
if a>b{
return b
}
return a
}
```
Javascript:
```Javascript
var eraseOverlapIntervals = function(intervals) {

View File

@ -320,6 +320,7 @@ class Solution:
Go
```Go
// 递归版本
func deleteNode(root *TreeNode, key int) *TreeNode {
if root==nil{
return nil
@ -356,6 +357,51 @@ func deleteNode1(root *TreeNode)*TreeNode{
root.Left=deleteNode1(root.Left)
return root
}
// 迭代版本
func deleteOneNode(target *TreeNode) *TreeNode {
if target == nil {
return target
}
if target.Right == nil {
return target.Left
}
cur := target.Right
for cur.Left != nil {
cur = cur.Left
}
cur.Left = target.Left
return target.Right
}
func deleteNode(root *TreeNode, key int) *TreeNode {
// 特殊情况处理
if root == nil {
return root
}
cur := root
var pre *TreeNode
for cur != nil {
if cur.Val == key {
break
}
pre = cur
if cur.Val > key {
cur = cur.Left
} else {
cur = cur.Right
}
}
if pre == nil {
return deleteOneNode(cur)
}
// pre 要知道是删除左孩子还有右孩子
if pre.Left != nil && pre.Left.Val == key {
pre.Left = deleteOneNode(cur)
}
if pre.Right != nil && pre.Right.Val == key {
pre.Right = deleteOneNode(cur)
}
return root
}
```
JavaScript版本

View File

@ -175,6 +175,30 @@ class Solution:
Go
```golang
func findMinArrowShots(points [][]int) int {
var res int =1//弓箭数
//先按照第一位排序
sort.Slice(points,func (i,j int) bool{
return points[i][0]<points[j][0]
})
for i:=1;i<len(points);i++{
if points[i-1][1]<points[i][0]{//如果前一位的右边界小于后一位的左边界,则一定不重合
res++
}else{
points[i][1] = min(points[i - 1][1], points[i][1]); // 更新重叠气球最小右边界,覆盖该位置的值,留到下一步使用
}
}
return res
}
func min(a,b int) int{
if a>b{
return b
}
return a
}
```
Javascript:
```Javascript
var findMinArrowShots = function(points) {

View File

@ -9,6 +9,8 @@
## 513.找树左下角的值
题目地址:[https://leetcode-cn.com/problems/find-bottom-left-tree-value/](https://leetcode-cn.com/problems/find-bottom-left-tree-value/v)
给定一个二叉树,在树的最后一行找到最左边的值。
示例 1:

View File

@ -426,6 +426,46 @@ func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
root1.Right = mergeTrees(root1.Right, root2.Right)
return root1
}
// 迭代版本
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
queue := make([]*TreeNode,0)
if root1 == nil{
return root2
}
if root2 == nil{
return root1
}
queue = append(queue,root1)
queue = append(queue,root2)
for size:=len(queue);size>0;size=len(queue){
node1 := queue[0]
queue = queue[1:]
node2 := queue[0]
queue = queue[1:]
node1.Val += node2.Val
// 左子树都不为空
if node1.Left != nil && node2.Left != nil{
queue = append(queue,node1.Left)
queue = append(queue,node2.Left)
}
// 右子树都不为空
if node1.Right !=nil && node2.Right !=nil{
queue = append(queue,node1.Right)
queue = append(queue,node2.Right)
}
// 树 1 的左子树为 nil直接接上树 2 的左子树
if node1.Left == nil{
node1.Left = node2.Left
}
// 树 1 的右子树为 nil直接接上树 2 的右子树
if node1.Right == nil{
node1.Right = node2.Right
}
}
return root1
}
```
JavaScript:

View File

@ -255,7 +255,7 @@ class Solution {
Python
递归法
**递归法** - 有返回值
```python
class Solution:
@ -268,7 +268,63 @@ class Solution:
root.left = self.insertIntoBST(root.left, val) # 递归创建左子树
return root
```
**递归法** - 无返回值
```python
class Solution:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if not root:
return TreeNode(val)
parent = None
def __traverse(cur: TreeNode, val: int) -> None:
# 在函数运行的同时把新节点插入到该被插入的地方.
nonlocal parent
if not cur:
new_node = TreeNode(val)
if parent.val < val:
parent.right = new_node
else:
parent.left = new_node
return
parent = cur # 重点: parent的作用只有运行到上面if not cur:才会发挥出来.
if cur.val < val:
__traverse(cur.right, val)
else:
__traverse(cur.left, val)
return
__traverse(root, val)
return root
```
**迭代法**
与无返回值的递归函数的思路大体一致
```python
class Solution:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if not root:
return TreeNode(val)
parent = None
cur = root
# 用while循环不断地找新节点的parent
while cur:
if cur.val < val:
parent = cur
cur = cur.right
elif cur.val > val:
parent = cur
cur = cur.left
# 运行到这意味着已经跳出上面的while循环,
# 同时意味着新节点的parent已经被找到.
# parent已被找到, 新节点已经ready. 把两个节点黏在一起就好了.
if parent.val > val:
parent.left = TreeNode(val)
else:
parent.right = TreeNode(val)
return root
```
Go

View File

@ -352,6 +352,60 @@ def search(nums, target)
end
```
**Swift:**
```swift
// (版本一)左闭右闭区间
func search(nums: [Int], target: Int) -> Int {
// 1. 先定义区间。这里的区间是[left, right]
var left = 0
var right = nums.count - 1
while left <= right {// 因为taeget是在[left, right]中包括两个边界值所以这里的left == right是有意义的
// 2. 计算区间中间的下标如果left、right都比较大的情况下left + right就有可能会溢出
// let middle = (left + right) / 2
// 防溢出:
let middle = left + (right - left) / 2
// 3. 判断
if target < nums[middle] {
// 当目标在区间左侧,就需要更新右边的边界值,新区间为[left, middle - 1]
right = middle - 1
} else if target > nums[middle] {
// 当目标在区间右侧,就需要更新左边的边界值,新区间为[middle + 1, right]
left = middle + 1
} else {
// 当目标就是在中间,则返回中间值的下标
return middle
}
}
// 如果找不到目标,则返回-1
return -1
}
// (版本二)左闭右开区间
func search(nums: [Int], target: Int) -> Int {
var left = 0
var right = nums.count
while left < right {
let middle = left + ((right - left) >> 1)
if target < nums[middle] {
right = middle
} else if target > nums[middle] {
left = middle + 1
} else {
return middle
}
}
return -1
}
```
-----------------------

View File

@ -880,7 +880,73 @@ MyLinkedList.prototype.deleteAtIndex = function(index) {
* obj.deleteAtIndex(index)
*/
```
Kotlin:
```kotlin
class MyLinkedList {
var next: ListNode? = null
var size: Int = 0
fun get(index: Int): Int {
if (index + 1 > size) return -1
var cur = this.next
for (i in 0 until index) {
cur = cur?.next
}
return cur?.`val` ?: -1
}
fun addAtHead(`val`: Int) {
val head = ListNode(`val`)
head.next = this.next
this.next = head
size++
}
fun addAtTail(`val`: Int) {
val pre = ListNode(0)
pre.next = this.next
var cur: ListNode? = pre
while (cur?.next != null) {
cur = cur.next
}
cur?.next = ListNode(`val`)
this.next = pre.next
size++
}
fun addAtIndex(index: Int, `val`: Int) {
if (index > size) return
val pre = ListNode(0)
pre.next = this.next
var cur:ListNode? = pre
for (i in 0 until index) {
cur = cur?.next
}
val temp = cur?.next
cur?.next = ListNode(`val`)
cur?.next?.next = temp
this.next = pre.next
size++
}
fun deleteAtIndex(index: Int) {
if (index + 1 > size) return
val pre = ListNode(0)
pre.next = this.next
var cur: ListNode? = pre
for (i in 0 until index) {
cur = cur?.next
}
val temp = cur?.next?.next
cur?.next?.next = null
cur?.next = temp
this.next = pre.next
size--
}
}
```

View File

@ -1,9 +1,41 @@
## 题目链接
<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>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
https://leetcode-cn.com/problems/valid-mountain-array/
# 941.有效的山脉数组
## 思路
题目链接https://leetcode-cn.com/problems/valid-mountain-array/
给定一个整数数组 arr如果它是有效的山脉数组就返回 true否则返回 false。
让我们回顾一下,如果 A 满足下述条件,那么它是一个山脉数组:
* arr.length >= 3
* 0 < i < arr.length - 1 条件下存在 i 使得
* arr[0] < arr[1] < ... arr[i-1] < arr[i]
* arr[i] > arr[i+1] > ... > arr[arr.length - 1]
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210729103604.png)
示例 1
* 输入arr = [2,1]
* 输出false
示例 2
* 输入arr = [3,5,5]
* 输出false
示例 3
* 输入arr = [0,3,2,1]
* 输出true
# 思路
判断是山峰,主要就是要严格的保存左边到中间,和右边到中间是递增的。
@ -38,7 +70,12 @@ public:
}
};
```
Java 版本如下:
如果想系统学一学双指针的话, 可以看一下这篇[双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA)
# 其他语言版本
## Java
```java
class Solution {
@ -66,5 +103,26 @@ class Solution {
}
```
如果想系统学一学双指针的话, 可以看一下这篇[双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA)
## Python
```python
```
## Go
```go
```
## JavaScript
```js
```
-----------------------
* 作者微信:[程序员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>

View File

@ -224,6 +224,36 @@ const sortedSquares = function (nums) {
}
```
Swift:
```swift
func sortedSquares(_ nums: [Int]) -> [Int] {
// 指向新数组最后一个元素
var k = nums.count - 1
// 指向原数组第一个元素
var i = 0
// 指向原数组最后一个元素
var j = nums.count - 1
// 初始化新数组(用-1填充)
var result = Array<Int>(repeating: -1, count: nums.count)
for _ in 0..<nums.count {
if nums[i] * nums[i] < nums[j] * nums[j] {
result[k] = nums[j] * nums[j]
j -= 1
} else {
result[k] = nums[i] * nums[i]
i += 1
}
k -= 1
}
return result
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)

View File

@ -169,6 +169,29 @@ class Solution {
}
}
```
```python
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
if not words: return []
result = []
hash = [0] * 26 # 用来统计所有字符串里字符出现的最小频率
for i, c in enumerate(words[0]): # 用第一个字符串给hash初始化
hash[ord(c) - ord('a')] += 1
# 统计除第一个字符串外字符的出现频率
for i in range(1, len(words)):
hashOtherStr = [0] * 26
for j in range(len(words[0])):
hashOtherStr[ord(words[i][j]) - ord('a')] += 1
# 更新hash保证hash里统计26个字符在所有字符串里出现的最小次数
for k in range(26):
hash[k] = min(hash[k], hashOtherStr[k])
# 将hash统计的字符次数转成输出形式
for i in range(26):
while hash[i] != 0: # 注意这里是while多个重复的字符
result.extend(chr(i + ord('a')))
hash[i] -= 1
return result
```
javaScript
```js
var commonChars = function (words) {

View File

@ -100,7 +100,21 @@ class Solution {
```
Python
```python
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
count = [0] * 2002
for i in range(len(arr)):
count[arr[i] + 1000] += 1 # 防止负数作为下标
freq = [False] * 1002 # 标记相同频率是否重复出现
for i in range(2001):
if count[i] > 0:
if freq[count[i]] == False:
freq[count[i]] = True
else:
return False
return True
```
Go
JavaScript

View File

@ -120,13 +120,13 @@ public:
为了把这份精简代码的回溯过程展现出来,大家可以试一试把:
```
```C++
if (cur->left) traversal(cur->left, path + "->", result); // 左 回溯就隐藏在这里
```
改成如下代码:
```
```C++
path += "->";
traversal(cur->left, path, result); // 左
```
@ -149,7 +149,7 @@ if (cur->right) {
如果想把回溯加上,就要 在上面代码的基础上加上回溯就可以AC了。
```
```C++
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左

View File

@ -44,7 +44,7 @@ int function1(int x, int n) {
那么就可以写出了如下这样的一个递归的算法,使用递归解决了这个问题。
```
```C++
int function2(int x, int n) {
if (n == 0) {
return 1; // return 1 同样是因为0次方是等于1的
@ -62,7 +62,7 @@ int function2(int x, int n) {
这个时间复杂度就没有达到面试官的预期。于是又写出了如下的递归算法的代码:
```
```C++
int function3(int x, int n) {
if (n == 0) {
return 1;
@ -101,7 +101,7 @@ int function3(int x, int n) {
于是又写出如下递归算法的代码:
```
```C++
int function4(int x, int n) {
if (n == 0) {
return 1;
@ -132,7 +132,7 @@ int function4(int x, int n) {
对于function3 这样的递归实现很容易让人感觉这是O(logn)的时间复杂度其实这是O(n)的算法!
```
```C++
int function3(int x, int n) {
if (n == 0) {
return 1;
@ -145,14 +145,12 @@ int function3(int x, int n) {
```
可以看出这道题目非常简单,但是又很考究算法的功底,特别是对递归的理解,这也是我面试别人的时候用过的一道题,所以整个情景我才写的如此逼真,哈哈。
大厂面试的时候最喜欢用“简单题”来考察候选人的算法功底,注意这里的“简单题”可并不一定真的简单哦!
大厂面试的时候最喜欢用“简单题”来考察候选人的算法功底,注意这里的“简单题”可并不一定真的简单哦!
如果认真读完本篇,相信大家对递归算法的有一个新的认识的,同一道题目,同样是递归,效率可是不一样的!
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)

View File

@ -223,12 +223,12 @@ public:
文中我明确的说了:**回溯就隐藏在traversal(cur->left, path + "->", result);中的 path + "->"。 每次函数调用完path依然是没有加上"->" 的,这就是回溯了。**
如果还不理解的话,可以把
```
```C++
traversal(cur->left, path + "->", result);
```
改成
```
```C++
string tmp = path + "->";
traversal(cur->left, tmp, result);
```

View File

@ -78,7 +78,7 @@ int main() {
注意地址为16进制可以看出二维数组地址是连续一条线的。
一些录友可能看不懂内存地址,我就简单介绍一下, 0x7ffee4065820 与 0x7ffee4065824 差了一个4就是4个字节因为这是一个int型的数组所以两个相数组元素地址差4个字节。
一些录友可能看不懂内存地址,我就简单介绍一下, 0x7ffee4065820 与 0x7ffee4065824 差了一个4就是4个字节因为这是一个int型的数组所以两个相数组元素地址差4个字节。
0x7ffee4065828 与 0x7ffee406582c 也是差了4个字节在16进制里8 + 4 = cc就是12。

View File

@ -82,8 +82,8 @@ leetcode上没有纯01背包的问题都是01背包应用方面的题目
那么可以有两个方向推出来dp[i][j]
* 由dp[i - 1][j]推出即背包容量为j里面不放物品i的最大价值此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时物品i无法放进背包中所以被背包内的价值依然和前面相同。)
* 由dp[i - 1][j - weight[i]]推出dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值那么dp[i - 1][j - weight[i]] + value[i] 物品i的价值就是背包放物品i得到的最大价值
* **不放物品i**由dp[i - 1][j]推出即背包容量为j里面不放物品i的最大价值此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时物品i无法放进背包中所以被背包内的价值依然和前面相同。)
* **放物品i**由dp[i - 1][j - weight[i]]推出dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值那么dp[i - 1][j - weight[i]] + value[i] 物品i的价值就是背包放物品i得到的最大价值
所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

View File

@ -60,7 +60,7 @@ dp[j]可以通过dp[j - weight[i]]推导出来dp[j - weight[i]]表示容量
dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。也就是容量为j的背包放入物品i了之后的价值即dp[j]
此时dp[j]有两个选择一个是取自己dp[j]一个是取dp[j - weight[i]] + value[i],指定是取最大的,毕竟是求最大价值,
此时dp[j]有两个选择一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j]即不放物品i一个是取dp[j - weight[i]] + value[i]即放物品i指定是取最大的,毕竟是求最大价值,
所以递归公式为:

View File

@ -92,7 +92,7 @@ dp状态图如下
遍历背包容量在外层循环,遍历物品在内层循环,状态如图:
![动态规划-完全背包2](https://img-blog.csdnimg.cn/20210126104741304.jpg)
![动态规划-完全背包2](https://code-thinking-1253855093.file.myqcloud.com/pics/20210729234011.png)
看了这两个图大家就会理解完全背包中两个for循环的先后循序都不影响计算dp[j]所需要的值这个值就是下标j之前所对应的dp[j])。
@ -311,7 +311,38 @@ func main() {
fmt.Println(test_CompletePack2(weight, price, 4))
}
```
Javascript:
```Javascript
// 先遍历物品,再遍历背包容量
function test_completePack1() {
let weight = [1, 3, 5]
let value = [15, 20, 30]
let bagWeight = 4
let dp = new Array(bagWeight + 1).fill(0)
for(let i = 0; i <= weight.length; i++) {
for(let j = weight[i]; j <= bagWeight; j++) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
}
}
console.log(dp)
}
// 先遍历背包容量,再遍历物品
function test_completePack2() {
let weight = [1, 3, 5]
let value = [15, 20, 30]
let bagWeight = 4
let dp = new Array(bagWeight + 1).fill(0)
for(let j = 0; j <= bagWeight; j++) {
for(let i = 0; i < weight.length; i++) {
if (j >= weight[i]) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
}
}
}
console.log(2, dp);
}
```
-----------------------