From 80dc27742439b6d1529c1aaefaad052dcdeab7b4 Mon Sep 17 00:00:00 2001 From: youngyangyang04 <826123027@qq.com> Date: Tue, 24 Aug 2021 15:56:18 +0800 Subject: [PATCH] 20210824 --- README.md | 6 + .../0052.N\347\232\207\345\220\216II.md" | 97 ++++++++++ ...47\347\232\204\347\237\251\345\275\242.md" | 33 ++++ ...11\346\220\234\347\264\242\346\240\221.md" | 40 ++-- ...02\345\272\217\351\201\215\345\216\206.md" | 29 +++ ...40\344\272\214\345\217\211\346\240\221.md" | 39 ++-- ...54\344\272\214\345\217\211\346\240\221.md" | 2 +- ...36\347\216\260\351\230\237\345\210\227.md" | 47 ----- ...53\345\206\267\345\206\273\346\234\237.md" | 4 +- ...\345\255\227\347\254\246\344\270\262II.md" | 2 +- ...66\344\272\214\345\217\211\346\240\221.md" | 23 +-- ...47\344\272\214\345\217\211\346\240\221.md" | 41 ++-- ...55\347\232\204\346\220\234\347\264\242.md" | 51 ++--- ...60\347\233\256\346\216\222\345\272\217.md" | 177 +++++++----------- ...16\346\234\254\351\241\271\347\233\256.md" | 7 + 15 files changed, 331 insertions(+), 267 deletions(-) create mode 100644 "problems/0052.N\347\232\207\345\220\216II.md" create mode 100644 "problems/\345\205\266\344\273\226/\345\217\202\344\270\216\346\234\254\351\241\271\347\233\256.md" diff --git a/README.md b/README.md index 022a45e846..267d794e26 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ 👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定) +👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master) > 1. **介绍**:本项目是一套完整的刷题计划,旨在帮助大家少走弯路,循序渐进学算法,[关注作者](#关于作者) > 2. **PDF版本** : [「代码随想录」算法精讲 PDF 版本](https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ) 。 @@ -477,6 +478,11 @@ * [100.相同的树](./problems/0100.相同的树.md) 同101.对称二叉树 一个思路 * [116.填充每个节点的下一个右侧节点指针](./problems/0116.填充每个节点的下一个右侧节点指针.md) +## 回溯算法 + +* [52.N皇后II](./problems/0052.N皇后II.md) + + ## 贪心 * [649.Dota2参议院](./problems/0649.Dota2参议院.md) 有难度 diff --git "a/problems/0052.N\347\232\207\345\220\216II.md" "b/problems/0052.N\347\232\207\345\220\216II.md" new file mode 100644 index 0000000000..81f5c7ea71 --- /dev/null +++ "b/problems/0052.N\347\232\207\345\220\216II.md" @@ -0,0 +1,97 @@ + +

+ + + + +

+

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ + +# 52. N皇后II + +题目链接:https://leetcode-cn.com/problems/n-queens-ii/ + +n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 + +上图为 8 皇后问题的一种解法。 +![51n皇后](https://img-blog.csdnimg.cn/20200821152118456.png) + +给定一个整数 n,返回 n 皇后不同的解决方案的数量。 + +示例: + +输入: 4 +输出: 2 +解释: 4 皇后问题存在如下两个不同的解法。 +[ + [".Q..",  // 解法 1 +  "...Q", +  "Q...", +  "..Q."], + + ["..Q.",  // 解法 2 +  "Q...", +  "...Q", +  ".Q.."] +] + +# 思路 + + +想看:[51.N皇后](https://mp.weixin.qq.com/s/lU_QwCMj6g60nh8m98GAWg) ,基本没有区别 + +# C++代码 + +```CPP +class Solution { +private: +int count = 0; +void backtracking(int n, int row, vector& chessboard) { + if (row == n) { + count++; + return; + } + for (int col = 0; col < n; col++) { + if (isValid(row, col, chessboard, n)) { + chessboard[row][col] = 'Q'; // 放置皇后 + backtracking(n, row + 1, chessboard); + chessboard[row][col] = '.'; // 回溯 + } + } +} +bool isValid(int row, int col, vector& chessboard, int n) { + int count = 0; + // 检查列 + for (int i = 0; i < row; i++) { // 这是一个剪枝 + if (chessboard[i][col] == 'Q') { + return false; + } + } + // 检查 45度角是否有皇后 + for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) { + if (chessboard[i][j] == 'Q') { + return false; + } + } + // 检查 135度角是否有皇后 + for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { + if (chessboard[i][j] == 'Q') { + return false; + } + } + return true; +} + +public: + int totalNQueens(int n) { + std::vector chessboard(n, std::string(n, '.')); + backtracking(n, 0, chessboard); + return count; + + } +}; +``` + +# 其他语言补充 + diff --git "a/problems/0084.\346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242.md" "b/problems/0084.\346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242.md" index 941888dba3..61910abf5d 100644 --- "a/problems/0084.\346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242.md" +++ "b/problems/0084.\346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242.md" @@ -195,6 +195,39 @@ public: Java: +动态规划 +```java +class Solution { + public int largestRectangleArea(int[] heights) { + int length = heights.length; + int[] minLeftIndex = new int [length]; + int[] maxRigthIndex = new int [length]; + // 记录左边第一个小于该柱子的下标 + minLeftIndex[0] = -1 ; + for (int i = 1; i < length; i++) { + int t = i - 1; + // 这里不是用if,而是不断向右寻找的过程 + while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t]; + minLeftIndex[i] = t; + } + // 记录每个柱子 右边第一个小于该柱子的下标 + maxRigthIndex[length - 1] = length; + for (int i = length - 2; i >= 0; i--) { + int t = i + 1; + while(t < length && heights[t] >= heights[i]) t = maxRigthIndex[t]; + maxRigthIndex[i] = t; + } + // 求和 + int result = 0; + for (int i = 0; i < length; i++) { + int sum = heights[i] * (maxRigthIndex[i] - minLeftIndex[i] - 1); + result = Math.max(sum, result); + } + return result; + } +} +``` + Python: 动态规划 diff --git "a/problems/0098.\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.md" "b/problems/0098.\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.md" index 6f7e5c1465..9e31bfd6bb 100644 --- "a/problems/0098.\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.md" +++ "b/problems/0098.\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.md" @@ -7,7 +7,7 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 98.验证二叉搜索树 +# 98.验证二叉搜索树 题目地址:https://leetcode-cn.com/problems/validate-binary-search-tree/ @@ -22,7 +22,7 @@ ![98.验证二叉搜索树](https://img-blog.csdnimg.cn/20210203144334501.png) -## 思路 +# 思路 要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。 @@ -121,7 +121,7 @@ if (root->val > root->left->val && root->val < root->right->val) { 要定义一个longlong的全局变量,用来比较遍历的节点是否有序,因为后台测试数据中有int最小值,所以定义为longlong的类型,初始化为longlong最小值。 -注意递归函数要有bool类型的返回值, 我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg) 中讲了,只有寻找某一条边(或者一个节点)的时候,递归函数会有bool类型的返回值。 +注意递归函数要有bool类型的返回值, 我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://mp.weixin.qq.com/s/EJr_nZ31TnvZmptBjkDGqA) 中讲了,只有寻找某一条边(或者一个节点)的时候,递归函数会有bool类型的返回值。 其实本题是同样的道理,我们在寻找一个不符合条件的节点,如果没有找到这个节点就遍历了整个树,如果找到不符合的节点了,立刻返回。 @@ -210,7 +210,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) 迭代法中序遍历稍加改动就可以了,代码如下: @@ -240,9 +241,10 @@ public: }; ``` -在[二叉树:二叉搜索树登场!](https://mp.weixin.qq.com/s/vsKrWRlETxCVsiRr8v_hHg)中我们分明写出了痛哭流涕的简洁迭代法,怎么在这里不行了呢,因为本题是要验证二叉搜索树啊。 -## 总结 +在[二叉树:二叉搜索树登场!](https://mp.weixin.qq.com/s/muRjJulujBqZXW4apLkGpg)中我们分明写出了痛哭流涕的简洁迭代法,怎么在这里不行了呢,因为本题是要验证二叉搜索树啊。 + +# 总结 这道题目是一个简单题,但对于没接触过的同学还是有难度的。 @@ -251,10 +253,10 @@ public: 只要把基本类型的题目都做过,总结过之后,思路自然就开阔了。 -## 其他语言版本 +# 其他语言版本 -Java: +## Java ```Java class Solution { @@ -336,16 +338,10 @@ class Solution { } ``` -Python: +## Python **递归** - 利用BST中序遍历特性,把树"压缩"成数组 ```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 isValidBST(self, root: TreeNode) -> bool: # 思路: 利用BST中序遍历的特性. @@ -395,8 +391,9 @@ class Solution: return is_left_valid and is_right_valid return __isValidBST(root) ``` -``` -# 迭代-中序遍历 + +```python +迭代-中序遍历 class Solution: def isValidBST(self, root: TreeNode) -> bool: stack = [] @@ -415,7 +412,8 @@ class Solution: return True ``` -Go: +## Go + ```Go import "math" @@ -458,9 +456,9 @@ func isValidBST(root *TreeNode) bool { } ``` -JavaScript版本 +## JavaScript -> 辅助数组解决 +辅助数组解决 ```javascript /** @@ -493,7 +491,7 @@ var isValidBST = function (root) { }; ``` -> 递归中解决 +递归中解决 ```javascript /** diff --git "a/problems/0102.\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.md" "b/problems/0102.\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.md" index 0f9b2df659..954a19cd26 100644 --- "a/problems/0102.\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.md" +++ "b/problems/0102.\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.md" @@ -1573,6 +1573,35 @@ public: Java: +```java +class Solution { + public int minDepth(TreeNode root){ + if (root == null) { + return 0; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + int depth = 0; + while (!queue.isEmpty()){ + int size = queue.size(); + depth++; + TreeNode cur = null; + for (int i = 0; i < size; i++) { + cur = queue.poll(); + //如果当前节点的左右孩子都为空,直接返回最小深度 + if (cur.left == null && cur.right == null){ + return depth; + } + if (cur.left != null) queue.offer(cur.left); + if (cur.right != null) queue.offer(cur.right); + } + } + return depth; + } +} +``` + + Python: diff --git "a/problems/0106.\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.md" "b/problems/0106.\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.md" index 4cfe28582a..b225807608 100644 --- "a/problems/0106.\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.md" +++ "b/problems/0106.\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.md" @@ -12,7 +12,7 @@ * 106.从中序与后序遍历序列构造二叉树 * 105.从前序与中序遍历序列构造二叉树 -## 106.从中序与后序遍历序列构造二叉树 +# 106.从中序与后序遍历序列构造二叉树 题目地址:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/ @@ -29,7 +29,7 @@ ![106. 从中序与后序遍历序列构造二叉树1](https://img-blog.csdnimg.cn/20210203154316774.png) -### 思路 +## 思路 首先回忆一下如何根据两个顺序构造一个唯一的二叉树,相信理论知识大家应该都清楚,就是以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来在切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。 @@ -95,7 +95,8 @@ TreeNode* traversal (vector& inorder, vector& postorder) { **在切割的过程中会产生四个区间,把握不好不变量的话,一会左闭右开,一会左闭又闭,必然乱套!** -我在[数组:每次遇到二分法,都是一看就会,一写就废](https://mp.weixin.qq.com/s/fCf5QbPDtE6SSlZ1yh_q8Q)和[数组:这个循环可以转懵很多人!](https://mp.weixin.qq.com/s/KTPhaeqxbMK9CxHUUgFDmg)中都强调过循环不变量的重要性,在二分查找以及螺旋矩阵的求解中,坚持循环不变量非常重要,本题也是。 + +我在[704.二分查找](https://mp.weixin.qq.com/s/4X-8VRgnYRGd5LYGZ33m4w)和[59.螺旋矩阵II](https://mp.weixin.qq.com/s/Hn6-mlCPvKAdWbiFfQyaaw)中都强调过循环不变量的重要性,在二分查找以及螺旋矩阵的求解中,坚持循环不变量非常重要,本题也是。 首先要切割中序数组,为什么先切割中序数组呢? @@ -105,7 +106,7 @@ TreeNode* traversal (vector& inorder, vector& postorder) { 中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割,如下代码中我坚持左闭右开的原则: -``` +```C++ // 找到中序遍历的切割点 int delimiterIndex; for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) { @@ -394,7 +395,7 @@ public: }; ``` -## 105.从前序与中序遍历序列构造二叉树 +# 105.从前序与中序遍历序列构造二叉树 题目地址:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ @@ -411,7 +412,7 @@ public: ![105. 从前序与中序遍历序列构造二叉树](https://img-blog.csdnimg.cn/20210203154626672.png) -### 思路 +## 思路 本题和106是一样的道理。 @@ -540,7 +541,7 @@ public: }; ``` -## 思考题 +# 思考题 前序和中序可以唯一确定一颗二叉树。 @@ -562,7 +563,7 @@ tree2 的前序遍历是[1 2 3], 后序遍历是[3 2 1]。 所以前序和后序不能唯一确定一颗二叉树! -## 总结 +# 总结 之前我们讲的二叉树题目都是各种遍历二叉树,这次开始构造二叉树了,思路其实比较简单,但是真正代码实现出来并不容易。 @@ -578,9 +579,9 @@ tree2 的前序遍历是[1 2 3], 后序遍历是[3 2 1]。 -## 其他语言版本 +# 其他语言版本 -Java: +## Java 106.从中序与后序遍历序列构造二叉树 @@ -653,7 +654,7 @@ class Solution { } ``` -Python: +## Python 105.从前序与中序遍历序列构造二叉树 @@ -685,7 +686,8 @@ class Solution: root.right = self.buildTree(preorder_right, inorder_right) return root -``` +``` + 106.从中序与后序遍历序列构造二叉树 ```python @@ -716,9 +718,11 @@ class Solution: root.right = self.buildTree(inorder_right, postorder_right) return root -``` -Go: -> 106 从中序与后序遍历序列构造二叉树 +``` + +## Go + +106 从中序与后序遍历序列构造二叉树 ```go /** @@ -751,7 +755,7 @@ func findRootIndex(inorder []int,target int) (index int){ } ``` -> 105 从前序与中序遍历序列构造二叉树 +105 从前序与中序遍历序列构造二叉树 ```go /** @@ -784,7 +788,8 @@ func findRootIndex(target int,inorder []int) int{ -JavaScript +## JavaScript + ```javascript var buildTree = function(inorder, postorder) { if (!postorder.length) return null diff --git "a/problems/0226.\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.md" "b/problems/0226.\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.md" index 6eb6f301f8..5bae8e3a94 100644 --- "a/problems/0226.\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.md" +++ "b/problems/0226.\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.md" @@ -43,7 +43,7 @@ **注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果** -**这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不行,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了** +**这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了** 那么层序遍历可以不可以呢?**依然可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!** diff --git "a/problems/0232.\347\224\250\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" "b/problems/0232.\347\224\250\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" index 0df82d35b5..0da600fd54 100644 --- "a/problems/0232.\347\224\250\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" +++ "b/problems/0232.\347\224\250\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" @@ -176,53 +176,6 @@ class MyQueue { } ``` -个人习惯写法,使用Deque通用api: -```java -class MyQueue { - // java中的 Stack 有设计上的缺陷,官方推荐使用 Deque(双端队列) 代替 Stack - // Deque 中的 addFirst、removeFirst、peekFirst 等方法等效于 Stack(堆栈) 中的 push、pop、peek - Deque stIn; - Deque stOut; - /** Initialize your data structure here. */ - public MyQueue() { - stIn = new ArrayDeque<>(); - stOut = new ArrayDeque<>(); - } - - /** Push element x to the back of queue. */ - public void push(int x) { - stIn.addLast(x); - } - - /** Removes the element from in front of queue and returns that element. */ - public int pop() { - // 只要 stOut 为空,那么就应该将 stIn 中所有的元素倒腾到 stOut 中 - if (stOut.isEmpty()) { - while (!stIn.isEmpty()) { - stOut.addLast(stIn.pollLast()); - } - } - // 再返回 stOut 中的元素 - return stOut.pollLast(); - } - - /** Get the front element. */ - public int peek() { - // 直接使用已有的pop函数 - int res = this.pop(); - // 因为pop函数弹出了元素res,所以再添加回去 - stOut.addLast(res); - return res; - } - - /** Returns whether the queue is empty. */ - public boolean empty() { - // 当 stIn 栈为空时,说明没有元素可以倒腾到 stOut 栈了 - // 并且 stOut 栈也为空时,说明没有以前从 stIn 中倒腾到的元素了 - return stIn.isEmpty() && stOut.isEmpty(); - } -} -``` ```java class MyQueue { diff --git "a/problems/0309.\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.md" "b/problems/0309.\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.md" index 3b1b65003c..60f6ed8e57 100644 --- "a/problems/0309.\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.md" +++ "b/problems/0309.\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.md" @@ -10,7 +10,9 @@ 题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ -给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​ +[https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/) + +给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): diff --git "a/problems/0541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262II.md" "b/problems/0541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262II.md" index ab1ef16afa..386c2ed2c4 100644 --- "a/problems/0541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262II.md" +++ "b/problems/0541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262II.md" @@ -65,7 +65,7 @@ public: }; ``` -那么我们也可以实现自己的reverse函数,其实和题目[344. 反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w)道理是一样的。 +那么我们也可以实现自己的reverse函数,其实和题目[344. 反转字符串](https://programmercarl.com/0541.反转字符串II.html)道理是一样的。 下面我实现的reverse函数区间是左闭右闭区间,代码如下: diff --git "a/problems/0617.\345\220\210\345\271\266\344\272\214\345\217\211\346\240\221.md" "b/problems/0617.\345\220\210\345\271\266\344\272\214\345\217\211\346\240\221.md" index b24a934b66..4714288def 100644 --- "a/problems/0617.\345\220\210\345\271\266\344\272\214\345\217\211\346\240\221.md" +++ "b/problems/0617.\345\220\210\345\271\266\344\272\214\345\217\211\346\240\221.md" @@ -7,7 +7,7 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 617.合并二叉树 +# 617.合并二叉树 题目地址:https://leetcode-cn.com/problems/merge-two-binary-trees/ @@ -21,7 +21,7 @@ 注意: 合并必须从两个树的根节点开始。 -## 思路 +# 思路 相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢? @@ -165,7 +165,8 @@ public: 使用迭代法,如何同时处理两棵树呢? -思路我们在[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)中的迭代法已经讲过一次了,求二叉树对称的时候就是把两个树的节点同时加入队列进行比较。 + +思路我们在[二叉树:我对称么?](https://mp.weixin.qq.com/s/5voTWHFuB9szmXGcJUzOPQ)中的迭代法已经讲过一次了,求二叉树对称的时候就是把两个树的节点同时加入队列进行比较。 本题我们也使用队列,模拟的层序遍历,代码如下: @@ -209,7 +210,7 @@ public: }; ``` -## 拓展 +# 拓展 当然也可以秀一波指针的操作,这是我写的野路子,大家就随便看看就行了,以防带跑遍了。 @@ -241,21 +242,21 @@ public: }; ``` -## 总结 +# 总结 合并二叉树,也是二叉树操作的经典题目,如果没有接触过的话,其实并不简单,因为我们习惯了操作一个二叉树,一起操作两个二叉树,还会有点懵懵的。 -这不是我们第一次操作两颗二叉树了,在[二叉树:我对称么?](https://mp.weixin.qq.com/s/Kgf0gjvlDlNDfKIH2b1Oxg)中也一起操作了两棵二叉树。 +这不是我们第一次操作两颗二叉树了,在[二叉树:我对称么?](https://mp.weixin.qq.com/s/5voTWHFuB9szmXGcJUzOPQ)中也一起操作了两棵二叉树。 迭代法中,一般一起操作两个树都是使用队列模拟类似层序遍历,同时处理两个树的节点,这种方式最好理解,如果用模拟递归的思路的话,要复杂一些。 最后拓展中,我给了一个操作指针的野路子,大家随便看看就行了,如果学习C++的话,可以在去研究研究。 -## 其他语言版本 +# 其他语言版本 -Java: +## Java ```Java class Solution { @@ -311,7 +312,7 @@ class Solution { } ``` -Python: +## Python **递归法 - 前序遍历** ```python @@ -374,7 +375,7 @@ class Solution: return root1 ``` -Go: +## Go ```go /** @@ -468,7 +469,7 @@ func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode { } ``` -JavaScript: +## JavaScript ```javascript /** diff --git "a/problems/0654.\346\234\200\345\244\247\344\272\214\345\217\211\346\240\221.md" "b/problems/0654.\346\234\200\345\244\247\344\272\214\345\217\211\346\240\221.md" index 8c310a21f3..8c89064e9d 100644 --- "a/problems/0654.\346\234\200\345\244\247\344\272\214\345\217\211\346\240\221.md" +++ "b/problems/0654.\346\234\200\345\244\247\344\272\214\345\217\211\346\240\221.md" @@ -7,7 +7,7 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 654.最大二叉树 +# 654.最大二叉树 题目地址:https://leetcode-cn.com/problems/maximum-binary-tree/ @@ -27,7 +27,7 @@ 给定的数组的大小在 [1, 1000] 之间。 -## 思路 +# 思路 最大二叉树的构建过程如下: @@ -41,7 +41,7 @@ 代码如下: -``` +```CPP TreeNode* constructMaximumBinaryTree(vector& nums) ``` @@ -53,7 +53,7 @@ TreeNode* constructMaximumBinaryTree(vector& nums) 代码如下: -``` +```CPP TreeNode* node = new TreeNode(0); if (nums.size() == 1) {     node->val = nums[0]; @@ -68,7 +68,7 @@ if (nums.size() == 1) { 1. 先要找到数组中最大的值和对应的下表, 最大的值构造根节点,下表用来下一步分割数组。 代码如下: -``` +```CPP int maxValue = 0; int maxValueIndex = 0; for (int i = 0; i < nums.size(); i++) { @@ -86,7 +86,7 @@ node->val = maxValue; 这里要判断maxValueIndex > 0,因为要保证左区间至少有一个数值。 代码如下: -``` +```CPP if (maxValueIndex > 0) {     vector newVec(nums.begin(), nums.begin() + maxValueIndex);     node->left = constructMaximumBinaryTree(newVec); @@ -99,7 +99,7 @@ if (maxValueIndex > 0) { 代码如下: -``` +```CPP if (maxValueIndex < (nums.size() - 1)) {     vector newVec(nums.begin() + maxValueIndex + 1, nums.end());     node->right = constructMaximumBinaryTree(newVec); @@ -143,7 +143,7 @@ public: 以上代码比较冗余,效率也不高,每次还要切割的时候每次都要定义新的vector(也就是数组),但逻辑比较清晰。 -和文章[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg)中一样的优化思路,就是每次分隔不用定义新的数组,而是通过下表索引直接在原数组上操作。 +和文章[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/Dza-fqjTyGrsRw4PWNKdxA)中一样的优化思路,就是每次分隔不用定义新的数组,而是通过下表索引直接在原数组上操作。 优化后代码如下: @@ -177,7 +177,7 @@ public: }; ``` -## 拓展 +# 拓展 可以发现上面的代码看上去简洁一些,**主要是因为第二版其实是允许空节点进入递归,所以不用在递归的时候加判断节点是否为空** @@ -209,10 +209,9 @@ root->right = traversal(nums, maxValueIndex + 1, right); 第二版相应的终止条件,是遇到空节点,也就是数组区间为0,就终止了。 +# 总结 -## 总结 - -这道题目其实和 [二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg) 是一个思路,比[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg) 还简单一些。 +这道题目其实和 [二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/Dza-fqjTyGrsRw4PWNKdxA) 是一个思路,比[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/Dza-fqjTyGrsRw4PWNKdxA) 还简单一些。 **注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下表索引直接在原数组上操作,这样可以节约时间和空间上的开销。** @@ -220,10 +219,10 @@ root->right = traversal(nums, maxValueIndex + 1, right); 其实就是不同代码风格的实现,**一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。** -## 其他语言版本 +# 其他语言版本 -Java: +## Java ```Java class Solution { @@ -255,14 +254,9 @@ class Solution { } ``` -Python: +## Python + ```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 constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: @@ -276,9 +270,8 @@ class Solution: return root ``` -Go: +## Go -> 654. 最大二叉树 ```go /** @@ -311,7 +304,7 @@ func findMax(nums []int) (index int){ } ``` -JavaScript版本 +## JavaScript ```javascript /** diff --git "a/problems/0700.\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\220\234\347\264\242.md" "b/problems/0700.\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\220\234\347\264\242.md" index 68d30e3f85..ec050fefc7 100644 --- "a/problems/0700.\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\220\234\347\264\242.md" +++ "b/problems/0700.\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\220\234\347\264\242.md" @@ -7,7 +7,7 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 700.二叉搜索树中的搜索 +# 700.二叉搜索树中的搜索 题目地址:https://leetcode-cn.com/problems/search-in-a-binary-search-tree/ @@ -19,11 +19,12 @@ 在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。 -## 思路 +# 思路 之前我们讲了都是普通二叉树,那么接下来看看二叉搜索树。 -在[关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/_ymfWYvTNd2GvWvC5HOE4A)中,我们已经讲过了二叉搜索树。 + +在[关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/q_eKfL8vmSbSFcptZ3aeRA)中,我们已经讲过了二叉搜索树。 二叉搜索树是一个有序树: @@ -73,7 +74,7 @@ return NULL; 这里可能会疑惑,在递归遍历的时候,什么时候直接return 递归函数的返回值,什么时候不用加这个 return呢。 -我们在[二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?](https://mp.weixin.qq.com/s/6TWAVjxQ34kVqROWgcRFOg)中讲了,如果要搜索一条边,递归函数就要加返回值,这里也是一样的道理。 +我们在[二叉树:路径总和](https://mp.weixin.qq.com/s/EJr_nZ31TnvZmptBjkDGqA)中讲了,如果要搜索一条边,递归函数就要加返回值,这里也是一样的道理。 **因为搜索到目标节点了,就要立即return了,这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。** @@ -125,7 +126,7 @@ public: 第一次看到了如此简单的迭代法,是不是感动的痛哭流涕,哭一会~ -## 总结 +# 总结 本篇我们介绍了二叉搜索树的遍历方式,因为二叉搜索树的有序性,遍历的时候要比普通二叉树简单很多。 @@ -138,9 +139,9 @@ public: -## 其他语言版本 +# 其他语言版本 -Java: +## Java ```Java class Solution { @@ -207,17 +208,11 @@ class Solution { } ``` -Python: +## Python 递归法: ```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 searchBST(self, root: TreeNode, val: int) -> TreeNode: # 为什么要有返回值: @@ -248,19 +243,11 @@ class Solution: ``` -Go: +## Go -> 递归法 +递归法: ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ //递归法 func searchBST(root *TreeNode, val int) *TreeNode { if root==nil||root.Val==val{ @@ -273,17 +260,9 @@ func searchBST(root *TreeNode, val int) *TreeNode { } ``` -> 迭代法 +迭代法: ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ //迭代法 func searchBST(root *TreeNode, val int) *TreeNode { for root!=nil{ @@ -299,9 +278,9 @@ func searchBST(root *TreeNode, val int) *TreeNode { } ``` -JavaScript版本 +## JavaScript -> 递归 +递归: ```javascript /** @@ -329,7 +308,7 @@ var searchBST = function (root, val) { }; ``` -> 迭代 +迭代: ```javascript /** diff --git "a/problems/1356.\346\240\271\346\215\256\346\225\260\345\255\227\344\272\214\350\277\233\345\210\266\344\270\2131\347\232\204\346\225\260\347\233\256\346\216\222\345\272\217.md" "b/problems/1356.\346\240\271\346\215\256\346\225\260\345\255\227\344\272\214\350\277\233\345\210\266\344\270\2131\347\232\204\346\225\260\347\233\256\346\216\222\345\272\217.md" index 300aa0e256..e94a32d3f9 100644 --- "a/problems/1356.\346\240\271\346\215\256\346\225\260\345\255\227\344\272\214\350\277\233\345\210\266\344\270\2131\347\232\204\346\225\260\347\233\256\346\216\222\345\272\217.md" +++ "b/problems/1356.\346\240\271\346\215\256\346\225\260\345\255\227\344\272\214\350\277\233\345\210\266\344\270\2131\347\232\204\346\225\260\347\233\256\346\216\222\345\272\217.md" @@ -7,112 +7,113 @@

欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-# 1365.有多少小于当前数字的数字 -题目链接:https://leetcode-cn.com/problems/sort-integers-by-the-number-of-1-bits/ +# 1356. 根据数字二进制下 1 的数目排序 -给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。 +题目链接:https://leetcode-cn.com/problems/sort-integers-by-the-number-of-1-bits/ -换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。 +给你一个整数数组 arr 。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。 -以数组形式返回答案。 +如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。 +请你返回排序后的数组。 示例 1: -输入:nums = [8,1,2,2,3] -输出:[4,0,1,1,3] -解释: -对于 nums[0]=8 存在四个比它小的数字:(1,2,2 和 3)。 -对于 nums[1]=1 不存在比它小的数字。 -对于 nums[2]=2 存在一个比它小的数字:(1)。 -对于 nums[3]=2 存在一个比它小的数字:(1)。 -对于 nums[4]=3 存在三个比它小的数字:(1,2 和 2)。 +* 输入:arr = [0,1,2,3,4,5,6,7,8] +* 输出:[0,1,2,4,8,3,5,6,7] +* 解释:[0] 是唯一一个有 0 个 1 的数。 +[1,2,4,8] 都有 1 个 1 。 +[3,5,6] 有 2 个 1 。 +[7] 有 3 个 1 。按照 1 的个数排序得到的结果数组为 [0,1,2,4,8,3,5,6,7] + 示例 2: -输入:nums = [6,5,4,8] -输出:[2,1,0,3] +* 输入:arr = [1024,512,256,128,64,32,16,8,4,2,1] +* 输出:[1,2,4,8,16,32,64,128,256,512,1024] +* 解释:数组中所有整数二进制下都只有 1 个 1 ,所以你需要按照数值大小将它们排序。 示例 3: -输入:nums = [7,7,7,7] -输出:[0,0,0,0] - -提示: -* 2 <= nums.length <= 500 -* 0 <= nums[i] <= 100 - -# 思路 - -两层for循环暴力查找,时间复杂度明显为O(n^2)。 - -那么我们来看一下如何优化。 +* 输入:arr = [10000,10000] +* 输出:[10000,10000] -首先要找小于当前数字的数字,那么从小到大排序之后,该数字之前的数字就都是比它小的了。 +示例 4: +* 输入:arr = [2,3,5,7,11,13,17,19] +* 输出:[2,3,5,17,7,11,13,19] -所以可以定义一个新数组,将数组排个序。 +示例 5: +* 输入:arr = [10,100,1000,10000] +* 输出:[10,100,10000,1000] -**排序之后,其实每一个数值的下标就代表这前面有几个比它小的了**。 -代码如下: -``` -vector vec = nums; -sort(vec.begin(), vec.end()); // 从小到大排序之后,元素下标就是小于当前数字的数字 -``` +# 思路 -此时用一个哈希表hash(本题可以就用一个数组)来做数值和下标的映射。这样就可以通过数值快速知道下标(也就是前面有几个比它小的)。 +这道题其实是考察如何计算一个数的二进制中1的数量。 -此时有一个情况,就是数值相同怎么办? +我提供两种方法: -例如,数组:1 2 3 4 4 4 ,第一个数值4的下标是3,第二个数值4的下标是4了。 +* 方法一: -这里就需要一个技巧了,**在构造数组hash的时候,从后向前遍历,这样hash里存放的就是相同元素最左面的数值和下标了**。 -代码如下: +朴实无华挨个计算1的数量,最多就是循环n的二进制位数,32位。 -```CPP -int hash[101]; -for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对应的下标 - hash[vec[i]] = i; +```C++ +int bitCount(int n) { + int count = 0; // 计数器 + while (n > 0) { + if((n & 1) == 1) count++; // 当前位是1,count++ + n >>= 1 ; // n向右移位 + } + return count; } ``` -最后在遍历原数组nums,用hash快速找到每一个数值 对应的 小于这个数值的个数。存放在将结果存放在另一个数组中。 +* 方法二 -代码如下: +这种方法,只循环n的二进制中1的个数次,比方法一高效的多 -```CPP -// 此时hash里保存的每一个元素数值 对应的 小于这个数值的个数 -for (int i = 0; i < nums.size(); i++) { - vec[i] = hash[nums[i]]; +```C++ +int bitCount(int n) { + int count = 0; + while (n) { + n &= (n - 1); // 清除最低位的1 + count++; + } + return count; } ``` +以计算12的二进制1的数量为例,如图所示: -流程如图: + - +下面我就使用方法二,来做这道题目: -关键地方讲完了,整体C++代码如下: +## C++代码 -```CPP +```C++ class Solution { -public: - vector smallerNumbersThanCurrent(vector& nums) { - vector vec = nums; - sort(vec.begin(), vec.end()); // 从小到大排序之后,元素下标就是小于当前数字的数字 - int hash[101]; - for (int i = vec.size() - 1; i >= 0; i--) { // 从后向前,记录 vec[i] 对应的下标 - hash[vec[i]] = i; - } - // 此时hash里保存的每一个元素数值 对应的 小于这个数值的个数 - for (int i = 0; i < nums.size(); i++) { - vec[i] = hash[nums[i]]; +private: + static int bitCount(int n) { // 计算n的二进制中1的数量 + int count = 0; + while(n) { + n &= (n -1); // 清除最低位的1 + count++; } - return vec; + return count; + } + static bool cmp(int a, int b) { + int bitA = bitCount(a); + int bitB = bitCount(b); + if (bitA == bitB) return a < b; // 如果bit中1数量相同,比较数值大小 + return bitA < bitB; // 否则比较bit中1数量大小 + } +public: + vector sortByBits(vector& arr) { + sort(arr.begin(), arr.end(), cmp); + return arr; } }; ``` -可以排序之后加哈希,时间复杂度为O(nlogn) - # 其他语言版本 @@ -120,48 +121,8 @@ public: ## Java ```java -/** -* 解法一:暴力 -* 时间复杂度:O(n^2) -* 空间复杂度:O(n) -*/ -class Solution { - public int[] smallerNumbersThanCurrent(int[] nums) { - int[] res = new int[nums.length]; - for (int i = 0; i < nums.length; i++) { - for (int j = 0; j < nums.length; j++) { - if (nums[j] < nums[i] && j != i) { // 注意 j 不能和 i 重合 - res[i]++; - } - } - } - return res; - } -} ``` -```java -/** -* 优化:排序 + 哈希表 -* 时间复杂度:O(nlogn) -* 空间复杂度:O(n) -*/ -class Solution { - public int[] smallerNumbersThanCurrent(int[] nums) { - int[] res = Arrays.copyOf(nums, nums.length); - Arrays.sort(res); // 是对 res 排序,nums 中顺序还要保持 - int[] hash = new int[101]; // 使用哈希表,记录比当前元素小的元素个数 - for (int i = res.length - 1; i >= 0; i--) { // 注意:从后向前 - hash[res[i]] = i; // 排序后,当前下标即表示比当前元素小的元素个数 - } - // 此时 hash中保存的每一个元素数值 便是 小于这个数值的个数 - for (int i = 0; i < res.length; i++) { - res[i] = hash[nums[i]]; - } - return res; - } -} -``` diff --git "a/problems/\345\205\266\344\273\226/\345\217\202\344\270\216\346\234\254\351\241\271\347\233\256.md" "b/problems/\345\205\266\344\273\226/\345\217\202\344\270\216\346\234\254\351\241\271\347\233\256.md" new file mode 100644 index 0000000000..69cb855532 --- /dev/null +++ "b/problems/\345\205\266\344\273\226/\345\217\202\344\270\216\346\234\254\351\241\271\347\233\256.md" @@ -0,0 +1,7 @@ + +优化已有代码 +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210821161813.png) + +**push代码之前 一定要 先pull最新代码**,否则提交的pr可能会有删除其他录友代码的操作。 + +一个pr 不要修改过多文件,因为一旦有一个 文件修改有问题,就不能合入,影响其他文件的合入了。