queue = new LinkedList<>();
+ queue.offer(root1);
+ queue.offer(root2);
+ while (!queue.isEmpty()) {
+ TreeNode node1 = queue.poll();
+ TreeNode node2 = queue.poll();
+ // 此时两个节点一定不为空,val相加
+ node1.val = node1.val + node2.val;
+ // 如果两棵树左节点都不为空,加入队列
+ if (node1.left != null && node2.left != null) {
+ queue.offer(node1.left);
+ queue.offer(node2.left);
+ }
+ // 如果两棵树右节点都不为空,加入队列
+ if (node1.right != null && node2.right != null) {
+ queue.offer(node1.right);
+ queue.offer(node2.right);
+ }
+ // 若node1的左节点为空,直接赋值
+ if (node1.left == null && node2.left != null) {
+ node1.left = node2.left;
+ }
+ // 若node2的左节点为空,直接赋值
+ if (node1.right == null && node2.right != null) {
+ node1.right = node2.right;
+ }
+ }
+ return root1;
+ }
+}
+```
## Python
@@ -470,6 +507,8 @@ func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
## JavaScript
+> 递归法:
+
```javascript
/**
* Definition for a binary tree node.
@@ -498,10 +537,57 @@ var mergeTrees = function (root1, root2) {
return preOrder(root1, root2);
};
```
+> 迭代法:
+
+```javascript
+
+/**
+ * Definition for a binary tree node.
+ * function TreeNode(val, left, right) {
+ * this.val = (val===undefined ? 0 : val)
+ * this.left = (left===undefined ? null : left)
+ * this.right = (right===undefined ? null : right)
+ * }
+ */
+/**
+ * @param {TreeNode} root1
+ * @param {TreeNode} root2
+ * @return {TreeNode}
+ */
+var mergeTrees = function(root1, root2) {
+ if (root1 === null) return root2;
+ if (root2 === null) return root1;
+
+ let queue = [];
+ queue.push(root1);
+ queue.push(root2);
+ while (queue.length) {
+ let node1 = queue.shift();
+ let node2 = queue.shift();;
+ node1.val += node2.val;
+ if (node1.left !== null && node2.left !== null) {
+ queue.push(node1.left);
+ queue.push(node2.left);
+ }
+ if (node1.right !== null && node2.right !== null) {
+ queue.push(node1.right);
+ queue.push(node2.right);
+ }
+ if (node1.left === null && node2.left !== null) {
+ node1.left = node2.left;
+ }
+ if (node1.right === null && node2.right !== null) {
+ node1.right = node2.right;
+ }
+ }
+ return root1;
+};
+
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0647.回文子串.md b/problems/0647.回文子串.md
index 7b90ca0c..cdc6b2e3 100644
--- a/problems/0647.回文子串.md
+++ b/problems/0647.回文子串.md
@@ -412,4 +412,4 @@ const countSubstrings = (s) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0649.Dota2参议院.md b/problems/0649.Dota2参议院.md
index e2900824..41b2b361 100644
--- a/problems/0649.Dota2参议院.md
+++ b/problems/0649.Dota2参议院.md
@@ -1,4 +1,3 @@
-
@@ -8,6 +7,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 649. Dota2 参议院
[力扣题目链接](https://leetcode-cn.com/problems/dota2-senate/)
@@ -219,11 +219,36 @@ func predictPartyVictory(senateStr string) string {
## JavaScript
```js
+var predictPartyVictory = function(senateStr) {
+ // R = true表示本轮循环结束后,字符串里依然有R;D同理。
+ let R = true, D = true;
+ // 当flag大于0时,R在D前出现,R可以消灭D。当flag小于0时,D在R前出现,D可以消灭R
+ let flag = 0;
+ let senate = senateStr.split('');
+ while(R && D){ // 一旦R或者D为false,就结束循环,说明本轮结束后只剩下R或者D了
+ R = false;
+ D = false;
+ for(let i = 0; i < senate.length; i++){
+ if(senate[i] === 'R'){
+ if(flag < 0) senate[i] = 0;// 消灭R,R此时为false
+ else R = true;// 如果没被消灭,本轮循环结束有R
+ flag++;
+ }
+ if(senate[i] === 'D'){
+ if(flag > 0) senate[i] = 0;
+ else D = true;
+ flag--;
+ }
+ }
+ }
+ // 循环结束之后,R和D只能有一个为true
+ return R ? "Radiant" : "Dire";
+};
```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md
index 1a6d39af..a4ae868a 100644
--- a/problems/0654.最大二叉树.md
+++ b/problems/0654.最大二叉树.md
@@ -258,16 +258,30 @@ class Solution {
## Python
```python
-//递归法
class Solution:
+ """最大二叉树 递归法"""
+
def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
- if not nums: return None //终止条件
- root = TreeNode(max(nums)) //新建节点
- p = nums.index(root.val) //找到最大值位置
- if p > 0: //保证有左子树
- root.left = self.constructMaximumBinaryTree(nums[:p]) //递归
- if p < len(nums): //保证有右子树
- root.right = self.constructMaximumBinaryTree(nums[p+1:]) //递归
+ return self.traversal(nums, 0, len(nums))
+
+ def traversal(self, nums: List[int], begin: int, end: int) -> TreeNode:
+ # 列表长度为0时返回空节点
+ if begin == end:
+ return None
+
+ # 找到最大的值和其对应的下标
+ max_index = begin
+ for i in range(begin, end):
+ if nums[i] > nums[max_index]:
+ max_index = i
+
+ # 构建当前节点
+ root = TreeNode(nums[max_index])
+
+ # 递归构建左右子树
+ root.left = self.traversal(nums, begin, max_index)
+ root.right = self.traversal(nums, max_index + 1, end)
+
return root
```
@@ -348,4 +362,4 @@ var constructMaximumBinaryTree = function (nums) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0657.机器人能否返回原点.md b/problems/0657.机器人能否返回原点.md
index ffa5d6f2..fb35c15b 100644
--- a/problems/0657.机器人能否返回原点.md
+++ b/problems/0657.机器人能否返回原点.md
@@ -1,4 +1,3 @@
-
@@ -7,6 +6,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 657. 机器人能否返回原点
[力扣题目链接](https://leetcode-cn.com/problems/robot-return-to-origin/)
@@ -151,9 +151,9 @@ var judgeCircle = function(moves) {
};
```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0669.修剪二叉搜索树.md b/problems/0669.修剪二叉搜索树.md
index fe18e5a5..09a512c4 100644
--- a/problems/0669.修剪二叉搜索树.md
+++ b/problems/0669.修剪二叉搜索树.md
@@ -10,7 +10,7 @@
> 如果不对递归有深刻的理解,本题有点难
> 单纯移除一个节点那还不够,要修剪!
-## 669. 修剪二叉搜索树
+# 669. 修剪二叉搜索树
[力扣题目链接](https://leetcode-cn.com/problems/trim-a-binary-search-tree/)
@@ -20,7 +20,7 @@

-## 思路
+# 思路
相信看到这道题目大家都感觉是一道简单题(事实上leetcode上也标明是简单)。
@@ -228,7 +228,7 @@ public:
};
```
-## 总结
+# 总结
修剪二叉搜索树其实并不难,但在递归法中大家可看出我费了很大的功夫来讲解如何删除节点的,这个思路其实是比较绕的。
@@ -238,10 +238,10 @@ public:
本题我依然给出递归法和迭代法,初学者掌握递归就可以了,如果想进一步学习,就把迭代法也写一写。
-## 其他语言版本
+# 其他语言版本
-Java:
+## Java
```Java
class Solution {
@@ -264,10 +264,9 @@ class Solution {
```
-Python:
-
+## Python
+**递归**
```python3
-
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
@@ -276,26 +275,32 @@ Python:
# self.right = right
class Solution:
def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
- if not root: return root
- if root.val < low:
- return self.trimBST(root.right,low,high) // 寻找符合区间[low, high]的节点
- if root.val > high:
- return self.trimBST(root.left,low,high) // 寻找符合区间[low, high]的节点
- root.left = self.trimBST(root.left,low,high) // root->left接入符合条件的左孩子
- root.right = self.trimBST(root.right,low,high) // root->right接入符合条件的右孩子
- return root
+ '''
+ 确认递归函数参数以及返回值:返回更新后剪枝后的当前root节点
+ '''
+ # Base Case
+ if not root: return None
+
+ # 单层递归逻辑
+ if root.val < low:
+ # 若当前root节点小于左界:只考虑其右子树,用于替代更新后的其本身,抛弃其左子树整体
+ return self.trimBST(root.right, low, high)
+
+ if high < root.val:
+ # 若当前root节点大于右界:只考虑其左子树,用于替代更新后的其本身,抛弃其右子树整体
+ return self.trimBST(root.left, low, high)
+
+ if low <= root.val <= high:
+ root.left = self.trimBST(root.left, low, high)
+ root.right = self.trimBST(root.right, low, high)
+ # 返回更新后的剪枝过的当前节点root
+ return root
```
-Go:
+
+## Go
+
```go
-/**
- * Definition for a binary tree node.
- * type TreeNode struct {
- * Val int
- * Left *TreeNode
- * Right *TreeNode
- * }
- */
// 递归
func trimBST(root *TreeNode, low int, high int) *TreeNode {
if root==nil{
@@ -313,6 +318,7 @@ func trimBST(root *TreeNode, low int, high int) *TreeNode {
root.Right=trimBST(root.Right,low,high)
return root
}
+
// 迭代
func trimBST(root *TreeNode, low int, high int) *TreeNode {
if root == nil {
@@ -347,7 +353,8 @@ func trimBST(root *TreeNode, low int, high int) *TreeNode {
```
-JavaScript版本:
+## JavaScript版本
+
迭代:
```js
var trimBST = function(root, low, high) {
@@ -405,4 +412,4 @@ var trimBST = function (root,low,high) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0673.最长递增子序列的个数.md b/problems/0673.最长递增子序列的个数.md
index b3907e0e..4be91db1 100644
--- a/problems/0673.最长递增子序列的个数.md
+++ b/problems/0673.最长递增子序列的个数.md
@@ -1,4 +1,3 @@
-
@@ -7,6 +6,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 673.最长递增子序列的个数
@@ -28,7 +28,7 @@
# 思路
-这道题可以说是 300.最长上升子序列 的进阶版本
+这道题可以说是 [300.最长上升子序列](https://programmercarl.com/0300.最长上升子序列.html) 的进阶版本
1. 确定dp数组(dp table)以及下标的含义
@@ -337,11 +337,33 @@ func findNumberOfLIS(nums []int) int {
## JavaScript
```js
+var findNumberOfLIS = function(nums) {
+ const len = nums.length;
+ if(len <= 1) return len;
+ let dp = new Array(len).fill(1); // i之前(包括i)最长递增子序列的长度为dp[i]
+ let count = new Array(len).fill(1); // 以nums[i]为结尾的字符串,最长递增子序列的个数为count[i]
+ let res = 0;
+ for(let i = 1; i < len; i++){
+ for(let j = 0; j < i; j++){
+ if(nums[i] > nums[j]){
+ if(dp[j] + 1 > dp[i]){ // 第 j 个数字为前一个数字的子序列是否更更长
+ dp[i] = dp[j] + 1; //更新 dp[i]
+ count[i] = count[j]; // 重置count[i]
+ } else if(dp[j] + 1 === dp[i]){ // 和原来一样长
+ count[i] += count[j]; //更新count[i]
+ }
+ }
+ }
+ }
+ let max = Math.max(...dp); //扩展运算符找到最大长度
+ for(let i = 0; i < len; i++) if(dp[i] === max) res += count[i]; // 累加
+ return res;
+};
```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md
index 3f3b5e6f..c81155cb 100644
--- a/problems/0674.最长连续递增序列.md
+++ b/problems/0674.最长连续递增序列.md
@@ -268,4 +268,4 @@ const findLengthOfLCIS = (nums) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0684.冗余连接.md b/problems/0684.冗余连接.md
index c9eb33c4..48ee6011 100644
--- a/problems/0684.冗余连接.md
+++ b/problems/0684.冗余连接.md
@@ -1,4 +1,3 @@
-
@@ -8,8 +7,12 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 684.冗余连接
+
+[力扣题目链接](https://leetcode-cn.com/problems/redundant-connection/)
+
树可以看成是一个连通且 无环 的 无向 图。
给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。
@@ -140,32 +143,214 @@ public:
## Java
```java
+class Solution {
+ private int n; // 节点数量3 到 1000
+ private int[] father;
+ public Solution() {
+ n = 1005;
+ father = new int[n];
+
+ // 并查集初始化
+ for (int i = 0; i < n; ++i) {
+ father[i] = i;
+ }
+ }
+
+ // 并查集里寻根的过程
+ private int find(int u) {
+ if(u == father[u]) {
+ return u;
+ }
+ father[u] = find(father[u]);
+ return father[u];
+ }
+
+ // 将v->u 这条边加入并查集
+ private void join(int u, int v) {
+ u = find(u);
+ v = find(v);
+ if (u == v) return ;
+ father[v] = u;
+ }
+
+ // 判断 u 和 v是否找到同一个根,本题用不上
+ private Boolean same(int u, int v) {
+ u = find(u);
+ v = find(v);
+ return u == v;
+ }
+
+ public int[] findRedundantConnection(int[][] edges) {
+ for (int i = 0; i < edges.length; i++) {
+ if (same(edges[i][0], edges[i][1])) {
+ return edges[i];
+ } else {
+ join(edges[i][0], edges[i][1]);
+ }
+ }
+ return null;
+ }
+}
```
## Python
```python
+
+class Solution:
+
+ def __init__(self):
+ """
+ 初始化
+ """
+ self.n = 1005
+ self.father = [i for i in range(self.n)]
+
+
+ def find(self, u):
+ """
+ 并查集里寻根的过程
+ """
+ if u == self.father[u]:
+ return u
+ self.father[u] = self.find(self.father[u])
+ return self.father[u]
+
+ def join(self, u, v):
+ """
+ 将v->u 这条边加入并查集
+ """
+ u = self.find(u)
+ v = self.find(v)
+ if u == v : return
+ self.father[v] = u
+ pass
+
+
+ def same(self, u, v ):
+ """
+ 判断 u 和 v是否找到同一个根,本题用不上
+ """
+ u = self.find(u)
+ v = self.find(v)
+ return u == v
+
+ def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
+ for i in range(len(edges)):
+ if self.same(edges[i][0], edges[i][1]) :
+ return edges[i]
+ else :
+ self.join(edges[i][0], edges[i][1])
+ return []
```
## Go
```go
+
+// 全局变量
+var (
+ n = 1005 // 节点数量3 到 1000
+ father = make([]int, 1005)
+)
+
+// 并查集初始化
+func initialize() {
+ for i := 0; i < n; i++ {
+ father[i] = i
+ }
+}
+
+// 并查集里寻根的过程
+func find(u int) int {
+ if u == father[u] {
+ return u
+ }
+ father[u] = find(father[u])
+ return father[u]
+}
+
+// 将v->u 这条边加入并查集
+func join(u, v int) {
+ u = find(u)
+ v = find(v)
+ if u == v {
+ return
+ }
+ father[v] = u
+}
+
+// 判断 u 和 v是否找到同一个根,本题用不上
+func same(u, v int) bool {
+ u = find(u)
+ v = find(v)
+ return u == v
+}
+
+func findRedundantConnection(edges [][]int) []int {
+ initialize()
+ for i := 0; i < len(edges); i++ {
+ if same(edges[i][0], edges[i][1]) {
+ return edges[i]
+ } else {
+ join(edges[i][0], edges[i][1])
+ }
+ }
+ return []int{}
+}
```
## JavaScript
```js
+const n = 1005;
+const father = new Array(n);
+// 并查集里寻根的过程
+const find = u => {
+ return u == father[u] ? u : father[u] = find(father[u]);
+};
+
+// 将v->u 这条边加入并查集
+const join = (u, v) => {
+ u = find(u);
+ v = find(v);
+ if(u == v) return;
+ father[v] = u;
+};
+
+// 判断 u 和 v是否找到同一个根,本题用不上
+const same = (u, v) => {
+ u = find(u);
+ v = find(v);
+ return u == v;
+};
+
+/**
+ * @param {number[][]} edges
+ * @return {number[]}
+ */
+var findRedundantConnection = function(edges) {
+ // 并查集初始化
+ for(let i = 0; i < n; i++){
+ father[i] = i;
+ }
+ for(let i = 0; i < edges.length; i++){
+ if(same(edges[i][0], edges[i][1])) return edges[i];
+ else join(edges[i][0], edges[i][1]);
+ }
+ return null;
+};
```
+
+
+
+
+
+
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
-
-
-
-
-
-
+
diff --git a/problems/0685.冗余连接II.md b/problems/0685.冗余连接II.md
index 68d0376e..cbd6cc2d 100644
--- a/problems/0685.冗余连接II.md
+++ b/problems/0685.冗余连接II.md
@@ -1,5 +1,3 @@
-
-
@@ -39,7 +37,7 @@
**这说明题目中的图原本是是一棵树,只不过在不增加节点的情况下多加了一条边!**
-还有**若有多个答案,返回最后出现在给定二维数组的答案。**这说明在两天边都可以删除的情况下,要删顺序靠后的!
+还有**若有多个答案,返回最后出现在给定二维数组的答案。**这说明在两条边都可以删除的情况下,要删顺序靠后的!
那么有如下三种情况,前两种情况是出现入度为2的点,如图:
@@ -58,7 +56,7 @@
首先先计算节点的入度,代码如下:
-```CPP
+```cpp
int inDegree[N] = {0}; // 记录节点入度
n = edges.size(); // 边的数量
for (int i = 0; i < n; i++) {
@@ -70,7 +68,7 @@ for (int i = 0; i < n; i++) {
代码如下:
-```CPP
+```cpp
vector vec; // 记录入度为2的边(如果有的话就两条边)
// 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案
for (int i = n - 1; i >= 0; i--) {
@@ -112,7 +110,7 @@ vector getRemoveEdge(const vector>& edges)
本题C++代码如下:(详细注释了)
-```CPP
+```cpp
class Solution {
private:
static const int N = 1010; // 如题:二维数组大小的在3到1000范围内
@@ -174,7 +172,7 @@ public:
inDegree[edges[i][1]]++; // 统计入度
}
vector vec; // 记录入度为2的边(如果有的话就两条边)
- // 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案
+ // 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
for (int i = n - 1; i >= 0; i--) {
if (inDegree[edges[i][1]] == 2) {
vec.push_back(i);
@@ -203,27 +201,409 @@ public:
## Java
```java
+
+class Solution {
+
+ private static final int N = 1010; // 如题:二维数组大小的在3到1000范围内
+ private int[] father;
+ public Solution() {
+ father = new int[N];
+
+ // 并查集初始化
+ for (int i = 0; i < N; ++i) {
+ father[i] = i;
+ }
+ }
+
+ // 并查集里寻根的过程
+ private int find(int u) {
+ if(u == father[u]) {
+ return u;
+ }
+ father[u] = find(father[u]);
+ return father[u];
+ }
+
+ // 将v->u 这条边加入并查集
+ private void join(int u, int v) {
+ u = find(u);
+ v = find(v);
+ if (u == v) return ;
+ father[v] = u;
+ }
+
+ // 判断 u 和 v是否找到同一个根,本题用不上
+ private Boolean same(int u, int v) {
+ u = find(u);
+ v = find(v);
+ return u == v;
+ }
+
+ /**
+ * 初始化并查集
+ */
+ private void initFather() {
+ // 并查集初始化
+ for (int i = 0; i < N; ++i) {
+ father[i] = i;
+ }
+ }
+
+ /**
+ * 在有向图里找到删除的那条边,使其变成树
+ * @param edges
+ * @return 要删除的边
+ */
+ private int[] getRemoveEdge(int[][] edges) {
+ initFather();
+ for(int i = 0; i < edges.length; i++) {
+ if(same(edges[i][0], edges[i][1])) { // 构成有向环了,就是要删除的边
+ return edges[i];
+ }
+ join(edges[i][0], edges[i][1]);
+ }
+ return null;
+ }
+
+ /**
+ * 删一条边之后判断是不是树
+ * @param edges
+ * @param deleteEdge 要删除的边
+ * @return true: 是树, false: 不是树
+ */
+ private Boolean isTreeAfterRemoveEdge(int[][] edges, int deleteEdge)
+ {
+ initFather();
+ for(int i = 0; i < edges.length; i++)
+ {
+ if(i == deleteEdge) continue;
+ if(same(edges[i][0], edges[i][1])) { // 构成有向环了,一定不是树
+ return false;
+ }
+ join(edges[i][0], edges[i][1]);
+ }
+ return true;
+ }
+
+ public int[] findRedundantDirectedConnection(int[][] edges) {
+ int[] inDegree = new int[N];
+ for(int i = 0; i < edges.length; i++)
+ {
+ // 入度
+ inDegree[ edges[i][1] ] += 1;
+ }
+
+ // 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
+ ArrayList twoDegree = new ArrayList();
+ for(int i = edges.length - 1; i >= 0; i--)
+ {
+ if(inDegree[edges[i][1]] == 2) {
+ twoDegree.add(i);
+ }
+ }
+
+ // 处理图中情况1 和 情况2
+ // 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
+ if(!twoDegree.isEmpty())
+ {
+ if(isTreeAfterRemoveEdge(edges, twoDegree.get(0))) {
+ return edges[ twoDegree.get(0)];
+ }
+ return edges[ twoDegree.get(1)];
+ }
+
+ // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
+ return getRemoveEdge(edges);
+ }
+}
```
## Python
```python
+
+class Solution:
+
+ def __init__(self):
+ self.n = 1010
+ self.father = [i for i in range(self.n)]
+
+
+ def find(self, u: int):
+ """
+ 并查集里寻根的过程
+ """
+ if u == self.father[u]:
+ return u
+ self.father[u] = self.find(self.father[u])
+ return self.father[u]
+
+ def join(self, u: int, v: int):
+ """
+ 将v->u 这条边加入并查集
+ """
+ u = self.find(u)
+ v = self.find(v)
+ if u == v : return
+ self.father[v] = u
+ pass
+
+
+ def same(self, u: int, v: int ):
+ """
+ 判断 u 和 v是否找到同一个根,本题用不上
+ """
+ u = self.find(u)
+ v = self.find(v)
+ return u == v
+
+ def init_father(self):
+ self.father = [i for i in range(self.n)]
+ pass
+
+ def getRemoveEdge(self, edges: List[List[int]]) -> List[int]:
+ """
+ 在有向图里找到删除的那条边,使其变成树
+ """
+
+ self.init_father()
+ for i in range(len(edges)):
+ if self.same(edges[i][0], edges[i][1]): # 构成有向环了,就是要删除的边
+ return edges[i]
+ self.join(edges[i][0], edges[i][1]);
+ return []
+
+ def isTreeAfterRemoveEdge(self, edges: List[List[int]], deleteEdge: int) -> bool:
+ """
+ 删一条边之后判断是不是树
+ """
+
+ self.init_father()
+ for i in range(len(edges)):
+ if i == deleteEdge: continue
+ if self.same(edges[i][0], edges[i][1]): # 构成有向环了,一定不是树
+ return False
+ self.join(edges[i][0], edges[i][1]);
+ return True
+
+ def findRedundantDirectedConnection(self, edges: List[List[int]]) -> List[int]:
+ inDegree = [0 for i in range(self.n)]
+
+ for i in range(len(edges)):
+ inDegree[ edges[i][1] ] += 1
+
+ # 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
+ towDegree = []
+ for i in range(len(edges))[::-1]:
+ if inDegree[edges[i][1]] == 2 :
+ towDegree.append(i)
+
+ # 处理图中情况1 和 情况2
+ # 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
+ if len(towDegree) > 0:
+ if(self.isTreeAfterRemoveEdge(edges, towDegree[0])) :
+ return edges[towDegree[0]]
+ return edges[towDegree[1]]
+
+ # 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
+ return self.getRemoveEdge(edges)
```
## Go
```go
+
+// 全局变量
+var (
+ n = 1010// 节点数量3 到 1000
+ father = make([]int, n)
+)
+
+// 并查集初始化
+func initialize() {
+ for i := 0; i < n; i++ {
+ father[i] = i
+ }
+}
+
+// 并查集里寻根的过程
+func find(u int) int {
+ if u == father[u] {
+ return u
+ }
+ father[u] = find(father[u])
+ return father[u]
+}
+
+// 将v->u 这条边加入并查集
+func join(u, v int) {
+ u = find(u)
+ v = find(v)
+ if u == v {
+ return
+ }
+ father[v] = u
+}
+
+// 判断 u 和 v是否找到同一个根,本题用不上
+func same(u, v int) bool {
+ u = find(u)
+ v = find(v)
+ return u == v
+}
+
+// getRemoveEdge 在有向图里找到删除的那条边,使其变成树
+func getRemoveEdge(edges [][]int) []int {
+ initialize()
+ for i := 0; i < len(edges); i++ { // 遍历所有的边
+ if same(edges[i][0], edges[i][1]) { // 构成有向环了,就是要删除的边
+ return edges[i]
+ }
+ join(edges[i][0], edges[i][1])
+ }
+ return []int{}
+}
+
+// isTreeAfterRemoveEdge 删一条边之后判断是不是树
+func isTreeAfterRemoveEdge(edges [][]int, deleteEdge int) bool {
+ initialize()
+ for i := 0; i < len(edges); i++ {
+ if i == deleteEdge {
+ continue
+ }
+ if same(edges[i][0], edges[i][1]) { // 构成有向环了,一定不是树
+ return false
+ }
+ join(edges[i][0], edges[i][1])
+ }
+ return true
+}
+
+func findRedundantDirectedConnection(edges [][]int) []int {
+ inDegree := make([]int, len(father))
+ for i := 0; i < len(edges); i++ {
+ // 统计入度
+ inDegree[edges[i][1]] += 1
+ }
+ // 记录入度为2的边(如果有的话就两条边)
+ // 找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
+ twoDegree := make([]int, 0)
+ for i := len(edges) - 1; i >= 0; i-- {
+ if inDegree[edges[i][1]] == 2 {
+ twoDegree = append(twoDegree, i)
+ }
+ }
+
+ // 处理图中情况1 和 情况2
+ // 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
+ if len(twoDegree) > 0 {
+ if isTreeAfterRemoveEdge(edges, twoDegree[0]) {
+ return edges[twoDegree[0]]
+ }
+ return edges[twoDegree[1]]
+ }
+
+ // 处理图中情况3
+ // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
+ return getRemoveEdge(edges)
+}
+
```
## JavaScript
```js
+const N = 1010; // 如题:二维数组大小的在3到1000范围内
+const father = new Array(N);
+let n; // 边的数量
+
+// 并查集里寻根的过程
+const find = u => {
+ return u == father[u] ? u : father[u] = find(father[u]);
+};
+
+// 将v->u 这条边加入并查集
+const join = (u, v) => {
+ u = find(u);
+ v = find(v);
+ if(u == v) return;
+ father[v] = u;
+};
+
+// 判断 u 和 v是否找到同一个根
+const same = (u, v) => {
+ u = find(u);
+ v = find(v);
+ return u == v;
+};
+
+// 在有向图里找到删除的那条边,使其变成树
+const getRemoveEdge = edges => {
+ // 初始化并查集
+ for (let i = 1; i <= n; i++) {
+ father[i] = i;
+ }
+ for (let i = 0; i < n; i++) { // 遍历所有的边
+ if (same(edges[i][0], edges[i][1])) { // 构成有向环了,就是要删除的边
+ return edges[i];
+ }
+ join(edges[i][0], edges[i][1]);
+ }
+ return [];
+}
+
+// 删一条边之后判断是不是树
+const isTreeAfterRemoveEdge = (edges, deleteEdge) => {
+ // 初始化并查集
+ for (let i = 1; i <= n; i++) {
+ father[i] = i;
+ }
+ for (let i = 0; i < n; i++) {
+ if (i == deleteEdge) continue;
+ if (same(edges[i][0], edges[i][1])) { // 构成有向环了,一定不是树
+ return false;
+ }
+ join(edges[i][0], edges[i][1]);
+ }
+ return true;
+}
+
+/**
+ * @param {number[][]} edges
+ * @return {number[]}
+ */
+var findRedundantDirectedConnection = function(edges) {
+ n = edges.length;// 边的数量
+ const inDegree = new Array(n+1).fill(0); // 记录节点入度
+ for (let i = 0; i < n; i++) {
+ inDegree[edges[i][1]]++; // 统计入度
+ }
+ let vec = [];// 记录入度为2的边(如果有的话就两条边)
+ // 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案
+ for (let i = n - 1; i >= 0; i--) {
+ if (inDegree[edges[i][1]] == 2) {
+ vec.push(i);
+ }
+ }
+ // 处理图中情况1 和 情况2
+ // 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
+ if (vec.length > 0) {
+ if (isTreeAfterRemoveEdge(edges, vec[0])) {
+ return edges[vec[0]];
+ } else {
+ return edges[vec[1]];
+ }
+ }
+ // 处理图中情况3
+ // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
+ return getRemoveEdge(edges);
+};
```
+
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
-
+
diff --git a/problems/0700.二叉搜索树中的搜索.md b/problems/0700.二叉搜索树中的搜索.md
index c25ea12f..bf67ce0e 100644
--- a/problems/0700.二叉搜索树中的搜索.md
+++ b/problems/0700.二叉搜索树中的搜索.md
@@ -342,4 +342,4 @@ var searchBST = function (root, val) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0701.二叉搜索树中的插入操作.md b/problems/0701.二叉搜索树中的插入操作.md
index c245ddcf..2dca140a 100644
--- a/problems/0701.二叉搜索树中的插入操作.md
+++ b/problems/0701.二叉搜索树中的插入操作.md
@@ -7,7 +7,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-## 701.二叉搜索树中的插入操作
+# 701.二叉搜索树中的插入操作
[力扣题目链接](https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/)
@@ -24,7 +24,7 @@
* -10^8 <= val <= 10^8
* 新值和原始二叉搜索树中的任意节点值都不同
-## 思路
+# 思路
其实这道题目其实是一道简单题目,**但是题目中的提示:有多种有效的插入方式,还可以重构二叉搜索树,一下子吓退了不少人**,瞬间感觉题目复杂了很多。
@@ -195,7 +195,7 @@ public:
};
```
-## 总结
+# 总结
首先在二叉搜索树中的插入操作,大家不用恐惧其重构搜索树,其实根本不用重构。
@@ -204,9 +204,10 @@ public:
最后依然给出了迭代的方法,迭代的方法就需要记录当前遍历节点的父节点了,这个和没有返回值的递归函数实现的代码逻辑是一样的。
-## 其他语言版本
+# 其他语言版本
+
+## Java
-Java:
```java
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
@@ -252,22 +253,40 @@ 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 insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
- if root is None:
- return TreeNode(val) # 如果当前节点为空,也就意味着val找到了合适的位置,此时创建节点直接返回。
+ # 返回更新后的以当前root为根节点的新树,方便用于更新上一层的父子节点关系链
+
+ # Base Case
+ if not root: return TreeNode(val)
+
+ # 单层递归逻辑:
+ if val < root.val:
+ # 将val插入至当前root的左子树中合适的位置
+ # 并更新当前root的左子树为包含目标val的新左子树
+ root.left = self.insertIntoBST(root.left, val)
+
if root.val < val:
- root.right = self.insertIntoBST(root.right, val) # 递归创建右子树
- if root.val > val:
- root.left = self.insertIntoBST(root.left, val) # 递归创建左子树
- return root
-```
+ # 将val插入至当前root的右子树中合适的位置
+ # 并更新当前root的右子树为包含目标val的新右子树
+ root.right = self.insertIntoBST(root.right, val)
+
+ # 返回更新后的以当前root为根节点的新树
+ return roo
+```
+
**递归法** - 无返回值
```python
class Solution:
@@ -294,7 +313,8 @@ class Solution:
return
__traverse(root, val)
return root
-```
+```
+
**迭代法**
与无返回值的递归函数的思路大体一致
```python
@@ -325,8 +345,8 @@ class Solution:
return root
```
-
-Go:
+-----
+## Go
递归法
@@ -343,8 +363,10 @@ func insertIntoBST(root *TreeNode, val int) *TreeNode {
}
return root
}
-```
-迭代法
+```
+
+迭代法
+
```go
func insertIntoBST(root *TreeNode, val int) *TreeNode {
if root == nil {
@@ -369,10 +391,10 @@ func insertIntoBST(root *TreeNode, val int) *TreeNode {
return root
}
```
+-----
+## JavaScript
-JavaScript版本
-
-> 有返回值的递归写法
+有返回值的递归写法
```javascript
/**
@@ -404,7 +426,7 @@ var insertIntoBST = function (root, val) {
};
```
-> 无返回值的递归
+无返回值的递归
```javascript
/**
@@ -444,7 +466,7 @@ var insertIntoBST = function (root, val) {
};
```
-> 迭代
+迭代
```javascript
/**
@@ -487,4 +509,4 @@ var insertIntoBST = function (root, val) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0704.二分查找.md b/problems/0704.二分查找.md
index f358d2be..1cdc5896 100644
--- a/problems/0704.二分查找.md
+++ b/problems/0704.二分查找.md
@@ -140,7 +140,7 @@ public:
## 相关题目推荐
* [35.搜索插入位置](https://programmercarl.com/0035.搜索插入位置.html)
-* 34.在排序数组中查找元素的第一个和最后一个位置
+* [34.在排序数组中查找元素的第一个和最后一个位置](https://programmercarl.com/0034.%E5%9C%A8%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E6%9F%A5%E6%89%BE%E5%85%83%E7%B4%A0%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%92%8C%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AA%E4%BD%8D%E7%BD%AE.html)
* 69.x 的平方根
* 367.有效的完全平方数
@@ -283,31 +283,47 @@ func search(nums []int, target int) int {
// (版本一)左闭右闭区间
+/**
+ * @param {number[]} nums
+ * @param {number} target
+ * @return {number}
+ */
+/**
var search = function(nums, target) {
- let l = 0, r = nums.length - 1;
- // 区间 [l, r]
- while(l <= r) {
- let mid = (l + r) >> 1;
- if(nums[mid] === target) return mid;
- let isSmall = nums[mid] < target;
- l = isSmall ? mid + 1 : l;
- r = isSmall ? r : mid - 1;
+ let left = 0, right = nums.length - 1;
+ // 使用左闭右闭区间
+ while (left <= right) {
+ let mid = left + Math.floor((right - left)/2);
+ if (nums[mid] > target) {
+ right = mid - 1; // 去左面闭区间寻找
+ } else if (nums[mid] < target) {
+ left = mid + 1; // 去右面闭区间寻找
+ } else {
+ return mid;
+ }
}
return -1;
};
// (版本二)左闭右开区间
+/**
+ * @param {number[]} nums
+ * @param {number} target
+ * @return {number}
+ */
var search = function(nums, target) {
- let l = 0, r = nums.length;
- // 区间 [l, r)
- while(l < r) {
- let mid = (l + r) >> 1;
- if(nums[mid] === target) return mid;
- let isSmall = nums[mid] < target;
- l = isSmall ? mid + 1 : l;
- // 所以 mid 不会被取到
- r = isSmall ? r : mid;
+ let left = 0, right = nums.length;
+ // 使用左闭右开区间 [left, right)
+ while (left < right) {
+ let mid = left + Math.floor((right - left)/2);
+ if (nums[mid] > target) {
+ right = mid; // 去左区间寻找
+ } else if (nums[mid] < target) {
+ left = mid + 1; // 去右区间寻找
+ } else {
+ return mid;
+ }
}
return -1;
};
@@ -514,4 +530,4 @@ class Solution {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0707.设计链表.md b/problems/0707.设计链表.md
index e05165a9..ba0e7e3b 100644
--- a/problems/0707.设计链表.md
+++ b/problems/0707.设计链表.md
@@ -282,12 +282,12 @@ Java:
```Java
//单链表
class ListNode {
-int val;
-ListNode next;
-ListNode(){}
-ListNode(int val) {
-this.val=val;
-}
+ int val;
+ ListNode next;
+ ListNode(){}
+ ListNode(int val) {
+ this.val=val;
+ }
}
class MyLinkedList {
//size存储链表元素的个数
@@ -880,6 +880,121 @@ MyLinkedList.prototype.deleteAtIndex = function(index) {
* obj.deleteAtIndex(index)
*/
```
+
+TypeScript:
+```TypeScript
+class ListNode {
+ public val: number;
+ public next: ListNode | null;
+ constructor(val?: number, next?: ListNode | null) {
+ this.val = val === undefined ? 0 : val;
+ this.next = next === undefined ? null : next;
+ }
+}
+
+class MyLinkedList {
+ // 记录链表长度
+ private size: number;
+ private head: ListNode | null;
+ private tail: ListNode | null;
+ constructor() {
+ this.size = 0;
+ this.head = null;
+ this.tail = null;
+ }
+
+ // 获取链表中第 index个节点的值
+ get(index: number): number {
+ // 索引无效的情况
+ if (index < 0 || index >= this.size) {
+ return -1;
+ }
+ let curNode = this.getNode(index);
+ // 这里在前置条件下,理论上不会出现 null的情况
+ return curNode.val;
+ }
+
+ // 在链表的第一个元素之前添加一个值为 val的节点。插入后,新节点将成为链表的第一个节点。
+ addAtHead(val: number): void {
+ let node: ListNode = new ListNode(val, this.head);
+ this.head = node;
+ if (!this.tail) {
+ this.tail = node;
+ }
+ this.size++;
+ }
+
+ // 将值为 val 的节点追加到链表的最后一个元素。
+ addAtTail(val: number): void {
+ let node: ListNode = new ListNode(val, null);
+ if (this.tail) {
+ this.tail.next = node;
+ } else {
+ // 还没有尾节点,说明一个节点都还没有
+ this.head = node;
+ }
+ this.tail = node;
+ this.size++;
+ }
+
+ // 在链表中的第 index个节点之前添加值为 val的节点。
+ // 如果 index等于链表的长度,则该节点将附加到链表的末尾。如果 index大于链表长度,则不会插入节点。如果 index小于0,则在头部插入节点。
+ addAtIndex(index: number, val: number): void {
+ if (index === this.size) {
+ this.addAtTail(val);
+ return;
+ }
+ if (index > this.size) {
+ return;
+ }
+ // <= 0 的情况都是在头部插入
+ if (index <= 0) {
+ this.addAtHead(val);
+ return;
+ }
+ // 正常情况
+ // 获取插入位置的前一个 node
+ let curNode = this.getNode(index - 1);
+ let node: ListNode = new ListNode(val, curNode.next);
+ curNode.next = node;
+ this.size++;
+ }
+
+ // 如果索引 index有效,则删除链表中的第 index个节点。
+ deleteAtIndex(index: number): void {
+ if (index < 0 || index >= this.size) {
+ return;
+ }
+ // 处理头节点
+ if (index === 0) {
+ this.head = this.head!.next;
+ this.size--;
+ return;
+ }
+ // 索引有效
+ let curNode: ListNode = this.getNode(index - 1);
+ curNode.next = curNode.next!.next;
+ // 处理尾节点
+ if (index === this.size - 1) {
+ this.tail = curNode;
+ }
+ this.size--;
+ }
+
+ // 获取指定 Node节点
+ private getNode(index: number): ListNode {
+ // 这里不存在没办法获取到节点的情况,都已经在前置方法做过判断
+ // 创建虚拟头节点
+ let curNode: ListNode = new ListNode(0, this.head);
+ for (let i = 0; i <= index; i++) {
+ // 理论上不会出现 null
+ curNode = curNode.next!;
+ }
+ return curNode;
+ }
+}
+```
+
Kotlin:
```kotlin
class MyLinkedList {
@@ -1037,4 +1152,4 @@ class MyLinkedList {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0714.买卖股票的最佳时机含手续费.md b/problems/0714.买卖股票的最佳时机含手续费.md
index 4ac4684e..576f5f85 100644
--- a/problems/0714.买卖股票的最佳时机含手续费.md
+++ b/problems/0714.买卖股票的最佳时机含手续费.md
@@ -262,6 +262,34 @@ var maxProfit = function(prices, fee) {
}
return result
};
+
+// 动态规划
+/**
+ * @param {number[]} prices
+ * @param {number} fee
+ * @return {number}
+ */
+var maxProfit = function(prices, fee) {
+ // 滚动数组
+ // have表示当天持有股票的最大收益
+ // notHave表示当天不持有股票的最大收益
+ // 把手续费算在买入价格中
+ let n = prices.length,
+ have = -prices[0]-fee, // 第0天持有股票的最大收益
+ notHave = 0; // 第0天不持有股票的最大收益
+ for (let i = 1; i < n; i++) {
+ // 第i天持有股票的最大收益由两种情况组成
+ // 1、第i-1天就已经持有股票,第i天什么也没做
+ // 2、第i-1天不持有股票,第i天刚买入
+ have = Math.max(have, notHave - prices[i] - fee);
+ // 第i天不持有股票的最大收益由两种情况组成
+ // 1、第i-1天就已经不持有股票,第i天什么也没做
+ // 2、第i-1天持有股票,第i天刚卖出
+ notHave = Math.max(notHave, have + prices[i]);
+ }
+ // 最后手中不持有股票,收益才能最大化
+ return notHave;
+};
```
@@ -269,4 +297,4 @@ var maxProfit = function(prices, fee) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md
index 7c54a2fe..8dd0894e 100644
--- a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md
+++ b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md
@@ -190,4 +190,4 @@ const maxProfit = (prices,fee) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md
index 9e3da663..54d1b07e 100644
--- a/problems/0718.最长重复子数组.md
+++ b/problems/0718.最长重复子数组.md
@@ -278,11 +278,30 @@ const findLength = (A, B) => {
return res;
};
```
-
+> 滚动数组
+```javascript
+const findLength = (nums1, nums2) => {
+ let len1 = nums1.length, len2 = nums2.length;
+ // dp[i][j]: 以nums1[i-1]、nums2[j-1]为结尾的最长公共子数组的长度
+ let dp = new Array(len2+1).fill(0);
+ let res = 0;
+ for (let i = 1; i <= len1; i++) {
+ for (let j = len2; j > 0; j--) {
+ if (nums1[i-1] === nums2[j-1]) {
+ dp[j] = dp[j-1] + 1;
+ } else {
+ dp[j] = 0;
+ }
+ res = Math.max(res, dp[j]);
+ }
+ }
+ return res;
+}
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0724.寻找数组的中心索引.md b/problems/0724.寻找数组的中心索引.md
index b4115893..13d2ab68 100644
--- a/problems/0724.寻找数组的中心索引.md
+++ b/problems/0724.寻找数组的中心索引.md
@@ -1,4 +1,3 @@
-
@@ -7,8 +6,11 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 724.寻找数组的中心下标
+[力扣题目链接](https://leetcode-cn.com/problems/find-pivot-index/)
+
给你一个整数数组 nums ,请计算数组的 中心下标 。
数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
@@ -87,15 +89,15 @@ class Solution {
}
```
-## Python
+## Python3
-```python3
+```python
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
numSum = sum(nums) #数组总和
leftSum = 0
for i in range(len(nums)):
- if numSum - leftSum -nums[i] == leftSum: #左右和相等
+ if numSum - leftSum -nums[i] == leftSum: #左右和相等
return i
leftSum += nums[i]
return -1
@@ -104,16 +106,45 @@ class Solution:
## Go
```go
+func pivotIndex(nums []int) int {
+ sum := 0
+ for _, v := range nums {
+ sum += v;
+ }
+
+ leftSum := 0 // 中心索引左半和
+ rightSum := 0 // 中心索引右半和
+ for i := 0; i < len(nums); i++ {
+ leftSum += nums[i]
+ rightSum = sum - leftSum + nums[i]
+ if leftSum == rightSum{
+ return i
+ }
+ }
+ return -1
+}
+
```
## JavaScript
```js
+var pivotIndex = function(nums) {
+ const sum = nums.reduce((a,b) => a + b);//求和
+ // 中心索引左半和 中心索引右半和
+ let leftSum = 0, rightSum = 0;
+ for(let i = 0; i < nums.length; i++){
+ leftSum += nums[i];
+ rightSum = sum - leftSum + nums[i];// leftSum 里面已经有 nums[i],多减了一次,所以加上
+ if(leftSum === rightSum) return i;
+ }
+ return -1;
+};
```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0738.单调递增的数字.md b/problems/0738.单调递增的数字.md
index f63a05b8..61175521 100644
--- a/problems/0738.单调递增的数字.md
+++ b/problems/0738.单调递增的数字.md
@@ -127,6 +127,7 @@ public:
Java:
```java
+版本1
class Solution {
public int monotoneIncreasingDigits(int N) {
String[] strings = (N + "").split("");
@@ -144,6 +145,31 @@ class Solution {
}
}
```
+java版本1中创建了String数组,多次使用Integer.parseInt了方法,这导致不管是耗时还是空间占用都非常高,用时12ms,下面提供一个版本在char数组上原地修改,用时1ms的版本
+```java
+版本2
+class Solution {
+ public int monotoneIncreasingDigits(int n) {
+ if (n==0)return 0;
+ char[] chars= Integer.toString(n).toCharArray();
+ int start=Integer.MAX_VALUE;//start初始值设为最大值,这是为了防止当数字本身是单调递增时,没有一位数字需要改成9的情况
+ for (int i=chars.length-1;i>0;i--){
+ if (chars[i]=start){
+ res.append('9');
+ }else res.append(chars[i]);
+ }
+ return Integer.parseInt(res.toString());
+ }
+}
+```
Python:
@@ -209,4 +235,4 @@ var monotoneIncreasingDigits = function(n) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0739.每日温度.md b/problems/0739.每日温度.md
index b00701ed..612b3c75 100644
--- a/problems/0739.每日温度.md
+++ b/problems/0739.每日温度.md
@@ -1,4 +1,3 @@
-
@@ -7,6 +6,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 739. 每日温度
[力扣题目链接](https://leetcode-cn.com/problems/daily-temperatures/)
@@ -276,11 +276,41 @@ func dailyTemperatures(num []int) []int {
}
```
+JavaScript:
+```javascript
+/**
+ * @param {number[]} temperatures
+ * @return {number[]}
+ */
+var dailyTemperatures = function(temperatures) {
+ let n = temperatures.length;
+ let res = new Array(n).fill(0);
+ let stack = []; // 递减栈:用于存储元素右面第一个比他大的元素下标
+ stack.push(0);
+ for (let i = 1; i < n; i++) {
+ // 栈顶元素
+ let top = stack[stack.length - 1];
+ if (temperatures[i] < temperatures[top]) {
+ stack.push(i);
+ } else if (temperatures[i] === temperatures[top]) {
+ stack.push(i);
+ } else {
+ while (stack.length && temperatures[i] > temperatures[stack[stack.length - 1]]) {
+ let top = stack.pop();
+ res[top] = i - top;
+ }
+ stack.push(i);
+ }
+ }
+ return res;
+};
+```
+
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0746.使用最小花费爬楼梯.md b/problems/0746.使用最小花费爬楼梯.md
index eb2a437a..96bfbb7c 100644
--- a/problems/0746.使用最小花费爬楼梯.md
+++ b/problems/0746.使用最小花费爬楼梯.md
@@ -272,4 +272,4 @@ var minCostClimbingStairs = function(cost) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0763.划分字母区间.md b/problems/0763.划分字母区间.md
index 43c663c2..c64ff3c8 100644
--- a/problems/0763.划分字母区间.md
+++ b/problems/0763.划分字母区间.md
@@ -88,15 +88,15 @@ Java:
class Solution {
public List partitionLabels(String S) {
List list = new LinkedList<>();
- int[] edge = new int[123];
+ int[] edge = new int[26];
char[] chars = S.toCharArray();
for (int i = 0; i < chars.length; i++) {
- edge[chars[i] - 0] = i;
+ edge[chars[i] - 'a'] = i;
}
int idx = 0;
int last = -1;
for (int i = 0; i < chars.length; i++) {
- idx = Math.max(idx,edge[chars[i] - 0]);
+ idx = Math.max(idx,edge[chars[i] - 'a']);
if (i == idx) {
list.add(i - last);
last = i;
@@ -181,4 +181,4 @@ var partitionLabels = function(s) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0841.钥匙和房间.md b/problems/0841.钥匙和房间.md
index 4a0185ec..42e6337f 100644
--- a/problems/0841.钥匙和房间.md
+++ b/problems/0841.钥匙和房间.md
@@ -1,4 +1,3 @@
-
@@ -8,6 +7,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 841.钥匙和房间
[力扣题目链接](https://leetcode-cn.com/problems/keys-and-rooms/)
@@ -25,12 +25,7 @@
示例 1:
* 输入: [[1],[2],[3],[]]
* 输出: true
-* 解释:
-我们从 0 号房间开始,拿到钥匙 1。
-之后我们去 1 号房间,拿到钥匙 2。
-然后我们去 2 号房间,拿到钥匙 3。
-最后我们去了 3 号房间。
-由于我们能够进入每个房间,我们返回 true。
+* 解释: 我们从 0 号房间开始,拿到钥匙 1。 之后我们去 1 号房间,拿到钥匙 2。 然后我们去 2 号房间,拿到钥匙 3。 最后我们去了 3 号房间。 由于我们能够进入每个房间,我们返回 true。
示例 2:
* 输入:[[1,3],[3,0,1],[2],[0]]
@@ -121,15 +116,163 @@ public:
Java:
+```java
+class Solution {
+ private void dfs(int key, List> rooms, List visited) {
+ if (visited.get(key)) {
+ return;
+ }
+
+ visited.set(key, true);
+ for (int k : rooms.get(key)) {
+ // 深度优先搜索遍历
+ dfs(k, rooms, visited);
+ }
+ }
+
+
+ public boolean canVisitAllRooms(List> rooms) {
+ List visited = new ArrayList(){{
+ for(int i = 0 ; i < rooms.size(); i++){
+ add(false);
+ }
+ }};
+
+ dfs(0, rooms, visited);
+
+ //检查是否都访问到了
+ for (boolean flag : visited) {
+ if (!flag) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+```
+
+
+
+
Python:
+python3
+
+```python
+
+class Solution:
+
+ def dfs(self, key: int, rooms: List[List[int]] , visited : List[bool] ) :
+ if visited[key] :
+ return
+
+ visited[key] = True
+ keys = rooms[key]
+ for i in range(len(keys)) :
+ # 深度优先搜索遍历
+ self.dfs(keys[i], rooms, visited)
+
+ def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
+ visited = [False for i in range(len(rooms))]
+
+ self.dfs(0, rooms, visited)
+
+ # 检查是否都访问到了
+ for i in range(len(visited)):
+ if not visited[i] :
+ return False
+ return True
+
+```
+
+
Go:
+```go
+
+func dfs(key int, rooms [][]int, visited []bool ) {
+ if visited[key] {
+ return;
+ }
+
+ visited[key] = true
+ keys := rooms[key]
+ for _ , key := range keys {
+ // 深度优先搜索遍历
+ dfs(key, rooms, visited);
+ }
+}
+
+func canVisitAllRooms(rooms [][]int) bool {
+
+ visited := make([]bool, len(rooms));
+
+ dfs(0, rooms, visited);
+
+ //检查是否都访问到了
+ for i := 0; i < len(visited); i++ {
+ if !visited[i] {
+ return false;
+ }
+ }
+ return true;
+}
+```
+
JavaScript:
+```javascript
+//DFS
+var canVisitAllRooms = function(rooms) {
+ const dfs = (key, rooms, visited) => {
+ if(visited[key]) return;
+ visited[key] = 1;
+ for(let k of rooms[key]){
+ // 深度优先搜索遍历
+ dfs(k, rooms, visited);
+ }
+ }
+ const visited = new Array(rooms.length).fill(false);
+ dfs(0, rooms, visited);
+ //检查是否都访问到了
+ for (let i of visited) {
+ if (!i) {
+ return false;
+ }
+ }
+ return true;
+};
+
+//BFS
+var canVisitAllRooms = function(rooms) {
+ const bfs = rooms => {
+ const visited = new Array(rooms.length).fill(0); // 标记房间是否被访问过
+ visited[0] = 1; // 0 号房间开始
+ const queue = []; //js数组作为队列使用
+ queue.push(0); // 0 号房间开始
+ // 广度优先搜索的过程
+ while(queue.length !== 0){
+ let key = queue[0];
+ queue.shift();
+ for(let k of rooms[key]){
+ if(!visited[k]){
+ queue.push(k);
+ visited[k] = 1;
+ }
+ }
+ }
+ // 检查房间是不是都遍历过了
+ for(let i of visited){
+ if(i === 0) return false;
+ }
+ return true;
+ }
+ return bfs(rooms);
+};
+```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0844.比较含退格的字符串.md b/problems/0844.比较含退格的字符串.md
index e6bf3493..a60fca22 100644
--- a/problems/0844.比较含退格的字符串.md
+++ b/problems/0844.比较含退格的字符串.md
@@ -1,4 +1,3 @@
-
@@ -7,6 +6,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 844.比较含退格的字符串
[力扣题目链接](https://leetcode-cn.com/problems/backspace-string-compare/)
@@ -188,13 +188,112 @@ class Solution {
Python:
+python3
+
+```python
+class Solution:
+
+ def get_string(self, s: str) -> str :
+ bz = []
+ for i in range(len(s)) :
+ c = s[i]
+ if c != '#' :
+ bz.append(c) # 模拟入栈
+ elif len(bz) > 0: # 栈非空才能弹栈
+ bz.pop() # 模拟弹栈
+ return str(bz)
+
+ def backspaceCompare(self, s: str, t: str) -> bool:
+ return self.get_string(s) == self.get_string(t)
+ pass
+```
+
+
Go:
+```go
+
+func getString(s string) string {
+ bz := []rune{}
+ for _, c := range s {
+ if c != '#' {
+ bz = append(bz, c); // 模拟入栈
+ } else if len(bz) > 0 { // 栈非空才能弹栈
+ bz = bz[:len(bz)-1] // 模拟弹栈
+ }
+ }
+ return string(bz)
+}
+
+func backspaceCompare(s string, t string) bool {
+ return getString(s) == getString(t)
+}
+
+```
+
JavaScript:
+```javascript
+// 双栈
+var backspaceCompare = function(s, t) {
+ const arrS = [], arrT = []; // 数组作为栈使用
+ for(let char of s){
+ char === '#' ? arrS.pop() : arrS.push(char);
+ }
+ for(let char of t){
+ char === '#' ? arrT.pop() : arrT.push(char);
+ }
+ return arrS.join('') === arrT.join(''); // 比较两个字符串是否相等
+};
+
+//双栈精简
+var backspaceCompare = function(s, t) {
+ const getString = s => {
+ let arrS = [];
+ for(let char of s){
+ char === '#' ? arrS.pop() : arrS.push(char);
+ }
+ return arrS.join('');
+ }
+ return getString(s) === getString(t);
+};
+
+//双指针
+var backspaceCompare = function(s, t) {
+ let sSkipNum = 0; // 记录s的#数量
+ let tSkipNum = 0; // 记录t的#数量
+ let i = s.length - 1, j = t.length - 1;
+ while(true) {
+ while(i >= 0){ // 从后向前,消除s的#
+ if(s[i] === '#') sSkipNum++;
+ else {
+ if (sSkipNum > 0) sSkipNum--;
+ else break;
+ }
+ i--;
+ }
+ while (j >= 0) { // 从后向前,消除t的#
+ if (t[j] === '#') tSkipNum++;
+ else {
+ if (tSkipNum > 0) tSkipNum--;
+ else break;
+ }
+ j--;
+ }
+ // 后半部分#消除完了,接下来比较s[i] != t[j]
+ if (i < 0 || j < 0) break; // s 或者t 遍历到头了
+ if (s[i] !== t[j]) return false;
+ i--;j--;
+ }
+ // 说明s和t同时遍历完毕
+ if (i == -1 && j == -1) return true;
+ return false;
+};
+
+```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0860.柠檬水找零.md b/problems/0860.柠檬水找零.md
index 46e05419..2e6065b4 100644
--- a/problems/0860.柠檬水找零.md
+++ b/problems/0860.柠檬水找零.md
@@ -260,4 +260,4 @@ var lemonadeChange = function(bills) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0922.按奇偶排序数组II.md b/problems/0922.按奇偶排序数组II.md
index 97d7091e..43046133 100644
--- a/problems/0922.按奇偶排序数组II.md
+++ b/problems/0922.按奇偶排序数组II.md
@@ -1,4 +1,3 @@
-
@@ -9,8 +8,11 @@
+
# 922. 按奇偶排序数组II
+[力扣题目链接](https://leetcode-cn.com/problems/sort-array-by-parity-ii/)
+
给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。
对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;当 A[i] 为偶数时, i 也是偶数。
@@ -147,9 +149,9 @@ class Solution {
}
```
-## Python
+## Python3
-```python3
+```python
#方法2
class Solution:
def sortArrayByParityII(self, nums: List[int]) -> List[int]:
@@ -180,16 +182,89 @@ class Solution:
## Go
```go
+
+// 方法一
+func sortArrayByParityII(nums []int) []int {
+ // 分别存放 nums 中的奇数、偶数
+ even, odd := []int{}, []int{}
+ for i := 0; i < len(nums); i++ {
+ if (nums[i] % 2 == 0) {
+ even = append(even, nums[i])
+ } else {
+ odd = append(odd, nums[i])
+ }
+ }
+
+ // 把奇偶数组重新存回 nums
+ result := make([]int, len(nums))
+ index := 0
+ for i := 0; i < len(even); i++ {
+ result[index] = even[i]; index++;
+ result[index] = odd[i]; index++;
+ }
+ return result;
+}
```
## JavaScript
```js
+//方法一
+var sortArrayByParityII = function(nums) {
+ const n = nums.length;
+ // 分别存放 nums 中的奇数、偶数
+ let evenIndex = 0, oddIndex = 0;
+ // 初始化就确定数组大小,节省开销
+ const even = new Array(Math.floor(n/2));
+ const odd = new Array(Math.floor(n/2));
+ // 把A数组放进偶数数组,和奇数数组
+ for(let i = 0; i < n; i++){
+ if(nums[i] % 2 === 0) even[evenIndex++] = nums[i];
+ else odd[oddIndex++] = nums[i];
+ }
+ // 把奇偶数组重新存回 nums
+ let index = 0;
+ for(let i = 0; i < even.length; i++){
+ nums[index++] = even[i];
+ nums[index++] = odd[i];
+ }
+ return nums;
+};
+
+//方法二
+var sortArrayByParityII = function(nums) {
+ const n = nums.length;
+ const result = new Array(n);
+ // 偶数下标 和 奇数下标
+ let evenIndex = 0, oddIndex = 1;
+ for(let i = 0; i < n; i++){
+ if(nums[i] % 2 === 0) {
+ result[evenIndex] = nums[i];
+ evenIndex += 2;
+ } else {
+ result[oddIndex] = nums[i];
+ oddIndex += 2;
+ }
+ }
+ return result;
+};
+
+//方法三
+var sortArrayByParityII = function(nums) {
+ let oddIndex = 1;
+ for(let i = 0; i < nums.length; i += 2){
+ if(nums[i] % 2 === 1){ // 在偶数位遇到了奇数
+ while(nums[oddIndex] % 2 !== 0) oddIndex += 2;// 在奇数位找一个偶数
+ [nums[oddIndex], nums[i]] = [nums[i], nums[oddIndex]]; // 解构赋值交换
+ }
+ }
+ return nums;
+};
```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0925.长按键入.md b/problems/0925.长按键入.md
index 3502f2fb..d40f619b 100644
--- a/problems/0925.长按键入.md
+++ b/problems/0925.长按键入.md
@@ -1,4 +1,3 @@
-
@@ -7,6 +6,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 925.长按键入
[力扣题目链接](https://leetcode-cn.com/problems/long-pressed-name/)
@@ -134,7 +134,7 @@ Python:
class Solution:
def isLongPressedName(self, name: str, typed: str) -> bool:
i, j = 0, 0
- m, n = len(name) , len(typed)
+ m, n = len(name) , len(typed)
while i< m and j < n:
if name[i] == typed[j]: # 相同时向后匹配
i += 1
@@ -155,13 +155,65 @@ class Solution:
else: return False
return True
```
+
Go:
+```go
+
+func isLongPressedName(name string, typed string) bool {
+ if(name[0] != typed[0] || len(name) > len(typed)) {
+ return false;
+ }
+
+ idx := 0 // name的索引
+ var last byte // 上个匹配字符
+ for i := 0; i < len(typed); i++ {
+ if idx < len(name) && name[idx] == typed[i] {
+ last = name[idx]
+ idx++
+ } else if last == typed[i] {
+ continue
+ } else {
+ return false
+ }
+ }
+ return idx == len(name)
+}
+```
+
JavaScript:
+```javascript
+var isLongPressedName = function(name, typed) {
+ let i = 0, j = 0;
+ const m = name.length, n = typed.length;
+ while(i < m && j < n){
+ if(name[i] === typed[j]){ // 相同则同时向后匹配
+ i++; j++;
+ } else {
+ if(j === 0) return false; // 如果是第一位就不相同直接返回false
+ // 判断边界为n-1,若为n会越界,例如name:"kikcxmvzi" typed:"kiikcxxmmvvzzz"
+ while(j < n - 1 && typed[j] === typed[j-1]) j++;
+ if(name[i] === typed[j]){ // j跨越重复项之后再次和name[i]匹配,相同则同时向后匹配
+ i++; j++;
+ } else {
+ return false;
+ }
+ }
+ }
+ // 说明name没有匹配完 例如 name:"pyplrzzzzdsfa" type:"ppyypllr"
+ if(i < m) return false;
+ // 说明type没有匹配完 例如 name:"alex" type:"alexxrrrrssda"
+ while(j < n) {
+ if(typed[j] === typed[j-1]) j++;
+ else return false;
+ }
+ return true;
+};
+```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/0941.有效的山脉数组.md b/problems/0941.有效的山脉数组.md
index ef5739e3..9416b309 100644
--- a/problems/0941.有效的山脉数组.md
+++ b/problems/0941.有效的山脉数组.md
@@ -1,4 +1,3 @@
-
@@ -7,7 +6,8 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-# 941.有效的山脉数组
+
+# 941.有效的山脉数组
[力扣题目链接](https://leetcode-cn.com/problems/valid-mountain-array/)
@@ -50,7 +50,7 @@
C++代码如下:
-```c++
+```CPP
class Solution {
public:
bool validMountainArray(vector& A) {
@@ -103,26 +103,74 @@ class Solution {
}
```
-## Python
+## Python3
```python
+class Solution:
+ def validMountainArray(self, arr: List[int]) -> bool:
+ if len(arr) < 3 :
+ return False
+
+ i = 1
+ flagIncrease = False # 上升
+ flagDecrease = False # 下降
+
+ while i < len(arr) and arr[i-1] < arr[i]:
+ flagIncrease = True
+ i += 1
+
+ while i < len(arr) and arr[i-1] > arr[i]:
+ flagDecrease = True
+ i += 1
+
+ return i == len(arr) and flagIncrease and flagDecrease
+
```
## Go
```go
+func validMountainArray(arr []int) bool {
+ if len(arr) < 3 {
+ return false
+ }
+
+ i := 1
+ flagIncrease := false // 上升
+ flagDecrease := false // 下降
+
+ for ; i < len(arr) && arr[i-1] < arr[i]; i++ {
+ flagIncrease = true;
+ }
+
+ for ; i < len(arr) && arr[i-1] > arr[i]; i++ {
+ flagDecrease = true;
+ }
+
+ return i == len(arr) && flagIncrease && flagDecrease;
+}
```
## JavaScript
```js
+var validMountainArray = function(arr) {
+ if(arr.length < 3) return false;// 一定不是山脉数组
+ let left = 0, right = arr.length - 1;// 双指针
+ // 注意防止越界
+ while(left < arr.length && arr[left] < arr[left+1]) left++;
+ while(right>0 && arr[right-1] > arr[right]) right--;
+ // 如果left或者right都在起始位置,说明不是山峰
+ if(left === right && left !== 0 && right !== arr.length - 1) return true;
+ return false;
+};
```
+
+
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
-
-
+
diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md
index a5fa71a7..3d698164 100644
--- a/problems/0968.监控二叉树.md
+++ b/problems/0968.监控二叉树.md
@@ -347,24 +347,57 @@ class Solution {
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 minCameraCover(self, root: TreeNode) -> int:
+ # Greedy Algo:
+ # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
+ # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
+ # 0: 该节点未覆盖
+ # 1: 该节点有摄像头
+ # 2: 该节点有覆盖
+
result = 0
- def traversal(cur):
+ # 从下往上遍历:后序(左右中)
+ def traversal(curr: TreeNode) -> int:
nonlocal result
- if not cur:
- return 2
- left = traversal(cur.left)
- right = traversal(cur.right)
- if left == 2 and right == 2:
+
+ if not curr: return 2
+ left = traversal(curr.left)
+ right = traversal(curr.right)
+
+ # Case 1:
+ # 左右节点都有覆盖
+ if left == 2 and right == 2:
return 0
- elif left == 0 or right == 0:
+
+ # Case 2:
+ # left == 0 && right == 0 左右节点无覆盖
+ # left == 1 && right == 0 左节点有摄像头,右节点无覆盖
+ # left == 0 && right == 1 左节点有无覆盖,右节点摄像头
+ # left == 0 && right == 2 左节点无覆盖,右节点覆盖
+ # left == 2 && right == 0 左节点覆盖,右节点无覆盖
+ elif left == 0 or right == 0:
result += 1
return 1
+
+ # Case 3:
+ # left == 1 && right == 2 左节点有摄像头,右节点有覆盖
+ # left == 2 && right == 1 左节点有覆盖,右节点有摄像头
+ # left == 1 && right == 1 左右节点都有摄像头
elif left == 1 or right == 1:
return 2
- else: return -1
- if traversal(root) == 0: result += 1
+
+ # 其他情况前段代码均已覆盖
+
+ if traversal(root) == 0:
+ result += 1
+
return result
```
Go:
@@ -477,4 +510,4 @@ int minCameraCover(struct TreeNode* root){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md
index a57af3c1..883b2f16 100644
--- a/problems/0977.有序数组的平方.md
+++ b/problems/0977.有序数组的平方.md
@@ -27,7 +27,7 @@
## 暴力排序
-最直观的相反,莫过于:每个数平方之后,排个序,美滋滋,代码如下:
+最直观的想法,莫过于:每个数平方之后,排个序,美滋滋,代码如下:
```CPP
class Solution {
@@ -339,4 +339,4 @@ class Solution {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1002.查找常用字符.md b/problems/1002.查找常用字符.md
index c0ca578e..44a02ceb 100644
--- a/problems/1002.查找常用字符.md
+++ b/problems/1002.查找常用字符.md
@@ -1,4 +1,3 @@
-
@@ -8,22 +7,29 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 1002. 查找常用字符
[力扣题目链接](https://leetcode-cn.com/problems/find-common-characters/)
-给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。
+给你一个字符串数组 words ,请你找出所有在 words 的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。
-你可以按任意顺序返回答案。
+示例 1:
-【示例一】
-输入:["bella","label","roller"]
+输入:words = ["bella","label","roller"]
输出:["e","l","l"]
+示例 2:
-【示例二】
-输入:["cool","lock","cook"]
+输入:words = ["cool","lock","cook"]
输出:["c","o"]
+提示:
+
+1 <= words.length <= 100
+1 <= words[i].length <= 100
+words[i] 由小写英文字母组成
+
+
# 思路
@@ -193,15 +199,32 @@ class Solution:
hash[i] -= 1
return result
```
+
+Python 3 使用collections.Counter
+```python
+class Solution:
+ def commonChars(self, words: List[str]) -> List[str]:
+ tmp = collections.Counter(words[0])
+ l = []
+ for i in range(1,len(words)):
+ # 使用 & 取交集
+ tmp = tmp & collections.Counter(words[i])
+
+ # 剩下的就是每个单词都出现的字符(键),个数(值)
+ for j in tmp:
+ v = tmp[j]
+ while(v):
+ l.append(j)
+ v -= 1
+ return l
+```
+
javaScript
```js
var commonChars = function (words) {
let res = []
let size = 26
- let firstHash = new Array(size)
- for (let i = 0; i < size; i++) { // 初始化 hash 数组
- firstHash[i] = 0
- }
+ let firstHash = new Array(size).fill(0) // 初始化 hash 数组
let a = "a".charCodeAt()
let firstWord = words[0]
@@ -209,21 +232,20 @@ var commonChars = function (words) {
let idx = firstWord[i].charCodeAt()
firstHash[idx - a] += 1
}
-
+
+ let otherHash = new Array(size).fill(0) // 初始化 hash 数组
for (let i = 1; i < words.length; i++) { // 1-n 个单词统计
- let otherHash = new Array(size)
- for (let i = 0; i < size; i++) { // 初始化 hash 数组
- otherHash[i] = 0
- }
-
for (let j = 0; j < words[i].length; j++) {
let idx = words[i][j].charCodeAt()
otherHash[idx - a] += 1
}
+
for (let i = 0; i < size; i++) {
firstHash[i] = Math.min(firstHash[i], otherHash[i])
}
+ otherHash.fill(0)
}
+
for (let i = 0; i < size; i++) {
while (firstHash[i] > 0) {
res.push(String.fromCharCode(i + a))
@@ -313,4 +335,4 @@ func commonChars(_ words: [String]) -> [String] {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md
index 020476a9..1b5386fd 100644
--- a/problems/1005.K次取反后最大化的数组和.md
+++ b/problems/1005.K次取反后最大化的数组和.md
@@ -217,4 +217,4 @@ var largestSumAfterKNegations = function(nums, k) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1035.不相交的线.md b/problems/1035.不相交的线.md
index b71a5199..f4e8f702 100644
--- a/problems/1035.不相交的线.md
+++ b/problems/1035.不相交的线.md
@@ -28,8 +28,8 @@
拿示例一A = [1,4,2], B = [1,2,4]为例,相交情况如图:
-
+
其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)
@@ -109,6 +109,41 @@ class Solution:
return dp[-1][-1]
```
+
+Golang:
+
+```go
+
+func maxUncrossedLines(A []int, B []int) int {
+ m, n := len(A), len(B)
+ dp := make([][]int, m+1)
+ for i := range dp {
+ dp[i] = make([]int, n+1)
+ }
+
+ for i := 1; i <= len(A); i++ {
+ for j := 1; j <= len(B); j++ {
+ if (A[i - 1] == B[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1
+ } else {
+ dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
+ }
+ }
+ }
+ return dp[m][n]
+
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+```
+
+
+
JavaScript:
```javascript
@@ -139,4 +174,4 @@ const maxUncrossedLines = (nums1, nums2) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1047.删除字符串中的所有相邻重复项.md b/problems/1047.删除字符串中的所有相邻重复项.md
index b88fd618..f70f39f3 100644
--- a/problems/1047.删除字符串中的所有相邻重复项.md
+++ b/problems/1047.删除字符串中的所有相邻重复项.md
@@ -275,4 +275,4 @@ var removeDuplicates = function(s) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1049.最后一块石头的重量II.md b/problems/1049.最后一块石头的重量II.md
index 25299f14..4ac80409 100644
--- a/problems/1049.最后一块石头的重量II.md
+++ b/problems/1049.最后一块石头的重量II.md
@@ -219,10 +219,31 @@ func max(a, b int) int {
}
```
+JavaScript版本
+```javascript
+/**
+ * @param {number[]} stones
+ * @return {number}
+ */
+var lastStoneWeightII = function (stones) {
+ let sum = stones.reduce((s, n) => s + n);
+
+ let dpLen = Math.floor(sum / 2);
+ let dp = new Array(dpLen + 1).fill(0);
+
+ for (let i = 0; i < stones.length; ++i) {
+ for (let j = dpLen; j >= stones[i]; --j) {
+ dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
+ }
+ }
+
+ return sum - dp[dpLen] - dp[dpLen];
+};
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1143.最长公共子序列.md b/problems/1143.最长公共子序列.md
index b3b5e6c0..55083d89 100644
--- a/problems/1143.最长公共子序列.md
+++ b/problems/1143.最长公共子序列.md
@@ -221,4 +221,4 @@ const longestCommonSubsequence = (text1, text2) => {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1207.独一无二的出现次数.md b/problems/1207.独一无二的出现次数.md
index 027c9f5a..808ff9f8 100644
--- a/problems/1207.独一无二的出现次数.md
+++ b/problems/1207.独一无二的出现次数.md
@@ -118,10 +118,42 @@ class Solution:
Go:
JavaScript:
+``` javascript
+// 方法一:使用数组记录元素出现次数
+var uniqueOccurrences = function(arr) {
+ const count = new Array(2002).fill(0);// -1000 <= arr[i] <= 1000
+ for(let i = 0; i < arr.length; i++){
+ count[arr[i] + 1000]++;// 防止负数作为下标
+ }
+ // 标记相同频率是否重复出现
+ const fre = new Array(1002).fill(false);// 1 <= arr.length <= 1000
+ for(let i = 0; i <= 2000; i++){
+ if(count[i] > 0){//有i出现过
+ if(fre[count[i]] === false) fre[count[i]] = true;//之前未出现过,标记为出现
+ else return false;//之前就出现了,重复出现
+ }
+ }
+ return true;
+};
+
+// 方法二:使用Map 和 Set
+/**
+ * @param {number[]} arr
+ * @return {boolean}
+ */
+var uniqueOccurrences = function(arr) {
+ // 记录每个元素出现次数
+ let map = new Map();
+ arr.forEach( x => {
+ map.set(x, (map.get(x) || 0) + 1);
+ })
+ // Set() 里的元素是不重复的。如果有元素出现次数相同,则最后的set的长度不等于map的长度
+ return map.size === new Set(map.values()).size
+};
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/1221.分割平衡字符串.md b/problems/1221.分割平衡字符串.md
new file mode 100644
index 00000000..bc1f1fc4
--- /dev/null
+++ b/problems/1221.分割平衡字符串.md
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
+# 1221. 分割平衡字符串
+
+[力扣题目链接](https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/)
+
+在一个 平衡字符串 中,'L' 和 'R' 字符的数量是相同的。
+
+给你一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串。
+
+注意:分割得到的每个字符串都必须是平衡字符串。
+
+返回可以通过分割得到的平衡字符串的 最大数量 。
+
+
+示例 1:
+
+* 输入:s = "RLRRLLRLRL"
+* 输出:4
+* 解释:s 可以分割为 "RL"、"RRLL"、"RL"、"RL" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
+
+示例 2:
+* 输入:s = "RLLLLRRRLR"
+* 输出:3
+* 解释:s 可以分割为 "RL"、"LLLRRR"、"LR" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
+
+示例 3:
+* 输入:s = "LLLLRRRR"
+* 输出:1
+* 解释:s 只能保持原样 "LLLLRRRR".
+
+示例 4:
+* 输入:s = "RLRRRLLRLL"
+* 输出:2
+* 解释:s 可以分割为 "RL"、"RRRLLRLL" ,每个子字符串中都包含相同数量的 'L' 和 'R' 。
+
+# 思路
+
+这道题目看起来好像很复杂,其实是非常简单的贪心,关于贪心,我在这里[关于贪心算法,你该了解这些!](https://programmercarl.com/贪心算法理论基础.html)有详细的讲解。
+
+从前向后遍历,只要遇到平衡子串,计数就+1,遍历一遍即可。
+
+局部最优:从前向后遍历,只要遇到平衡子串 就统计
+
+全局最优:统计了最多的平衡子串。
+
+局部最优可以推出全局最优,举不出反例,那么就试试贪心。
+
+
+例如,LRLR 这本身就是平衡子串 , 但要遇到LR就可以分割。
+
+C++代码如下:
+
+```CPP
+class Solution {
+public:
+ int balancedStringSplit(string s) {
+ int result = 0;
+ int count = 0;
+ for (int i = 0; i < s.size(); i++) {
+ if (s[i] == 'R') count++;
+ else count--;
+ if (count == 0) result++;
+ }
+ return result;
+ }
+};
+```
+
+# 拓展
+
+一些同学可能想,你这个推理不靠谱,都没有数学证明。怎么就能说是合理的呢,怎么就能说明 局部最优可以推出全局最优呢?
+
+一般数学证明有如下两种方法:
+
+* 数学归纳法
+* 反证法
+
+如果真的去严格数学证明其实不是在我们刷题或者 面试的考察范围内了。
+
+所以贪心题目的思考过程是: 如果发现局部最优好像可以推出全局最优,那么就 尝试一下举反例,如果举不出反例,那么就试试贪心。
+
+
+
+# 其他语言版本
+
+## Java
+
+```java
+class Solution {
+ public int balancedStringSplit(String s) {
+ int result = 0;
+ int count = 0;
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == 'R') count++;
+ else count--;
+ if (count == 0) result++;
+ }
+ return result;
+ }
+}
+```
+
+## Python
+
+```python
+```
+
+## Go
+
+```go
+```
+
+## JavaScript
+
+```js
+var balancedStringSplit = function(s) {
+ let res = 0, total = 0;//res为平衡字符串数量 total为当前"R"字符和"L"字符的数量差
+ for(let c of s){// 遍历字符串每个字符
+ //因为开始字符数量差就是0,遍历的时候要先改变数量差,否则会影响结果数量
+ total += c === 'R' ? 1:-1;//遇到"R",total++;遇到"L",total--
+ if(total === 0) res++;//只要"R""L"数量一样就可以算是一个平衡字符串
+ }
+ return res;
+};
+```
+
+
+-----------------------
+* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
+* B站视频:[代码随想录](https://space.bilibili.com/525438321)
+* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
+
diff --git a/problems/1356.根据数字二进制下1的数目排序.md b/problems/1356.根据数字二进制下1的数目排序.md
index 660434a2..a122df6f 100644
--- a/problems/1356.根据数字二进制下1的数目排序.md
+++ b/problems/1356.根据数字二进制下1的数目排序.md
@@ -1,4 +1,3 @@
-
@@ -8,6 +7,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 1356. 根据数字二进制下 1 的数目排序
[力扣题目链接](https://leetcode-cn.com/problems/sort-integers-by-the-number-of-1-bits/)
@@ -123,6 +123,31 @@ public:
## Java
```java
+class Solution {
+ private int cntInt(int val){
+ int count = 0;
+ while(val > 0) {
+ val = val & (val - 1);
+ count ++;
+ }
+
+ return count;
+ }
+
+ public int[] sortByBits(int[] arr) {
+ return Arrays.stream(arr).boxed()
+ .sorted(new Comparator(){
+ @Override
+ public int compare(Integer o1, Integer o2) {
+ int cnt1 = cntInt(o1);
+ int cnt2 = cntInt(o2);
+ return (cnt1 == cnt2) ? Integer.compare(o1, o2) : Integer.compare(cnt1, cnt2);
+ }
+ })
+ .mapToInt(Integer::intValue)
+ .toArray();
+ }
+}
```
@@ -141,11 +166,23 @@ public:
## JavaScript
```js
+var sortByBits = function(arr) {
+ const bitCount = n =>{// 计算n的二进制中1的数量
+ let count = 0;
+ while(n){
+ n &= (n - 1);// 清除最低位的1
+ count++;
+ }
+ return count;
+ }
+ // 如果有差,则按bits数排,如果无差,则按原值排
+ return arr.sort((a,b) => bitCount(a) - bitCount(b) || a - b);
+};
```
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/1365.有多少小于当前数字的数字.md b/problems/1365.有多少小于当前数字的数字.md
index 5cf6b2d8..9f282209 100644
--- a/problems/1365.有多少小于当前数字的数字.md
+++ b/problems/1365.有多少小于当前数字的数字.md
@@ -1,4 +1,3 @@
-
@@ -8,6 +7,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 1365.有多少小于当前数字的数字
[力扣题目链接](https://leetcode-cn.com/problems/how-many-numbers-are-smaller-than-the-current-number/)
@@ -155,6 +155,41 @@ class Solution:
Go:
JavaScript:
+```javascript
+// 方法一:使用哈希表记录位置
+var smallerNumbersThanCurrent = function(nums) {
+ const map = new Map();// 记录数字 nums[i] 有多少个比它小的数字
+ const res = nums.slice(0);//深拷贝nums
+ res.sort((a,b) => a - b);
+ for(let i = 0; i < res.length; i++){
+ if(!map.has(res[i])){// 遇到了相同的数字,那么不需要更新该 number 的情况
+ map.set(res[i],i);
+ }
+ }
+ // 此时map里保存的每一个元素数值 对应的 小于这个数值的个数
+ for(let i = 0; i < nums.length; i++){
+ res[i] = map.get(nums[i]);
+ }
+ return res;
+};
+
+// 方法二:不使用哈希表,只使用一个额外数组
+/**
+ * @param {number[]} nums
+ * @return {number[]}
+ */
+var smallerNumbersThanCurrent = function(nums) {
+ let array = [...nums]; // 深拷贝
+ // 升序排列,此时数组元素下标即是比他小的元素的个数
+ array = array.sort((a, b) => a-b);
+ let res = [];
+ nums.forEach( x => {
+ // 即使元素重复也不怕,indexOf 只返回找到的第一个元素的下标
+ res.push(array.indexOf(x));
+ })
+ return res;
+};
+```
@@ -162,4 +197,4 @@ JavaScript:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/1382.将二叉搜索树变平衡.md b/problems/1382.将二叉搜索树变平衡.md
index bce58c33..b7c1bec4 100644
--- a/problems/1382.将二叉搜索树变平衡.md
+++ b/problems/1382.将二叉搜索树变平衡.md
@@ -1,4 +1,3 @@
-
@@ -7,6 +6,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 1382.将二叉搜索树变平衡
[力扣题目链接](https://leetcode-cn.com/problems/balance-a-binary-search-tree/)
@@ -126,11 +126,34 @@ class Solution:
Go:
JavaScript:
+```javascript
+var balanceBST = function(root) {
+ const res = [];
+ // 中序遍历转成有序数组
+ const travesal = cur => {
+ if(!cur) return;
+ travesal(cur.left);
+ res.push(cur.val);
+ travesal(cur.right);
+ }
+ // 有序数组转成平衡二叉树
+ const getTree = (nums, left, right) => {
+ if(left > right) return null;
+ let mid = left + ((right - left) >> 1);
+ let root = new TreeNode(nums[mid]);// 中心位置作为当前节点的值
+ root.left = getTree(nums, left, mid - 1);// 递归地将区间[left,mid−1] 作为当前节点的左子树
+ root.right = getTree(nums, mid + 1, right);// 递归地将区间[mid+1,right] 作为当前节点的左子树
+ return root;
+ }
+ travesal(root);
+ return getTree(res, 0, res.length - 1);
+};
+```
+
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
-
+
diff --git a/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md b/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
index 6897c01f..f9aee37f 100644
--- a/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
+++ b/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
@@ -235,4 +235,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md b/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md
index 31a5e448..af2b66ae 100644
--- a/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md
+++ b/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md
@@ -167,7 +167,33 @@ else {
Java:
-
+```java
+class Solution {
+ public int minDistance(String word1, String word2) {
+ int m = word1.length();
+ int n = word2.length();
+ int[][] dp = new int[m+1][n+1];
+ for(int i = 1; i <= m; i++){
+ dp[i][0] = i;
+ }
+ for(int i = 1; i <= n; i++){
+ dp[0][i] = i;
+ }
+ for(int i = 1; i <= m; i++){
+ for(int j = 1; j <= n; j++){
+ int left = dp[i][j-1]+1;
+ int mid = dp[i-1][j-1];
+ int right = dp[i-1][j]+1;
+ if(word1.charAt(i-1) != word2.charAt(j-1)){
+ mid ++;
+ }
+ dp[i][j] = Math.min(left,Math.min(mid,right));
+ }
+ }
+ return dp[m][n];
+ }
+}
+```
Python:
@@ -181,4 +207,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树中递归带着回溯.md b/problems/二叉树中递归带着回溯.md
index 7b0ccad7..0a386fe1 100644
--- a/problems/二叉树中递归带着回溯.md
+++ b/problems/二叉树中递归带着回溯.md
@@ -145,22 +145,22 @@ if (cur->right) {
}
```
-此时就没有回溯了,这个代码就是通过不了的了。
+因为在递归右子树之前需要还原path,所以在左子树递归后必须为了右子树而进行回溯操作。而只右子树自己不添加回溯也可以成功AC。
-如果想把回溯加上,就要 在上面代码的基础上,加上回溯,就可以AC了。
+因此,在上面代码的基础上,再加上左右子树的回溯代码,就可以AC了。
```CPP
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左
- path.pop_back(); // 回溯
- path.pop_back();
+ path.pop_back(); // 回溯,抛掉val
+ path.pop_back(); // 回溯,抛掉->
}
if (cur->right) {
path += "->";
traversal(cur->right, path, result); // 右
- path.pop_back(); // 回溯
- path.pop_back();
+ path.pop_back(); // 回溯(非必要)
+ path.pop_back();
}
```
@@ -439,13 +439,88 @@ func traversal(root *TreeNode,result *[]string,path *[]int){
}
```
+JavaScript:
+100.相同的树
+```javascript
+/**
+ * Definition for a binary tree node.
+ * function TreeNode(val, left, right) {
+ * this.val = (val===undefined ? 0 : val)
+ * this.left = (left===undefined ? null : left)
+ * this.right = (right===undefined ? null : right)
+ * }
+ */
+/**
+ * @param {TreeNode} p
+ * @param {TreeNode} q
+ * @return {boolean}
+ */
+var isSameTree = function(p, q) {
+ if (p === null && q === null) {
+ return true;
+ } else if (p === null || q === null) {
+ return false;
+ } else if (p.val !== q.val) {
+ return false;
+ } else {
+ return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
+ }
+};
+```
+257.二叉树的不同路径
+> 回溯法:
+
+```javascript
+/**
+ * Definition for a binary tree node.
+ * function TreeNode(val, left, right) {
+ * this.val = (val===undefined ? 0 : val)
+ * this.left = (left===undefined ? null : left)
+ * this.right = (right===undefined ? null : right)
+ * }
+ */
+/**
+ * @param {TreeNode} root
+ * @return {string[]}
+ */
+var binaryTreePaths = function(root) {
+ const getPath = (root, path, result) => {
+ path.push(root.val);
+ if (root.left === null && root.right === null) {
+ let n = path.length;
+ let str = '';
+ for (let i=0; i';
+ }
+ str += path[n-1];
+ result.push(str);
+ }
+
+ if (root.left !== null) {
+ getPath(root.left, path, result);
+ path.pop(); // 回溯
+ }
+
+ if (root.right !== null) {
+ getPath(root.right, path, result);
+ path.pop();
+ }
+ }
+
+ if (root === null) return [];
+ let result = [];
+ let path = [];
+ getPath(root, path, result);
+ return result;
+};
+```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树总结篇.md b/problems/二叉树总结篇.md
index ee046366..37696fc0 100644
--- a/problems/二叉树总结篇.md
+++ b/problems/二叉树总结篇.md
@@ -175,4 +175,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树理论基础.md b/problems/二叉树理论基础.md
index 94a0d67f..b473861d 100644
--- a/problems/二叉树理论基础.md
+++ b/problems/二叉树理论基础.md
@@ -8,7 +8,9 @@
# 二叉树理论基础篇
-我们要开启新的征程了,大家跟上!
+题目分类大纲如下:
+
+
说道二叉树,大家对于二叉树其实都很熟悉了,本文呢我也不想教科书式的把二叉树的基础内容在啰嗦一遍,所以一下我讲的都是一些比较重点的内容。
@@ -179,11 +181,6 @@ struct TreeNode {
**说道二叉树,就不得不说递归,很多同学对递归都是又熟悉又陌生,递归的代码一般很简短,但每次都是一看就会,一写就废。**
-
-
-
-
-
## 其他语言版本
@@ -237,4 +234,4 @@ function TreeNode(val, left, right) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树的统一迭代法.md b/problems/二叉树的统一迭代法.md
index d8299e10..19962cba 100644
--- a/problems/二叉树的统一迭代法.md
+++ b/problems/二叉树的统一迭代法.md
@@ -530,4 +530,4 @@ var postorderTraversal = function(root, res = []) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树的迭代遍历.md b/problems/二叉树的迭代遍历.md
index 84363610..6889ecc0 100644
--- a/problems/二叉树的迭代遍历.md
+++ b/problems/二叉树的迭代遍历.md
@@ -473,4 +473,4 @@ var postorderTraversal = function(root, res = []) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/二叉树的递归遍历.md b/problems/二叉树的递归遍历.md
index 93f4000e..eee150b7 100644
--- a/problems/二叉树的递归遍历.md
+++ b/problems/二叉树的递归遍历.md
@@ -415,4 +415,4 @@ int* postorderTraversal(struct TreeNode* root, int* returnSize){
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/关于时间复杂度,你不知道的都在这里!.md b/problems/关于时间复杂度,你不知道的都在这里!.md
index 7ff9b470..105c2b24 100644
--- a/problems/关于时间复杂度,你不知道的都在这里!.md
+++ b/problems/关于时间复杂度,你不知道的都在这里!.md
@@ -177,4 +177,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/其他/参与本项目.md b/problems/其他/参与本项目.md
index cfa75439..76a2c61e 100644
--- a/problems/其他/参与本项目.md
+++ b/problems/其他/参与本项目.md
@@ -9,3 +9,7 @@
git add之前,要git diff 查看一下,本次提交所修改的代码是不是 自己修改的,是否 误删,或者误加的文件。
提交代码,不要使用git push -f 这种命令,要足够了解 -f 意味着什么。
+
+
+不用非要写出牛逼的代码才能提交PR,只要发现 文章中有任何问题,或者错别字,都欢迎提交PR,成为contributor。
+
diff --git a/problems/前序/ACM模式如何构建二叉树.md b/problems/前序/ACM模式如何构建二叉树.md
new file mode 100644
index 00000000..682e18f5
--- /dev/null
+++ b/problems/前序/ACM模式如何构建二叉树.md
@@ -0,0 +1,243 @@
+
+
+
+
+
+
+
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
+
+# 力扣上如何自己构造二叉树输入用例?
+
+经常有录友问,二叉树的题目中输入用例在ACM模式下应该怎么构造呢?
+
+力扣上的题目,输入用例就给了一个数组,怎么就能构造成二叉树呢?
+
+这次就给大家好好讲一讲!
+
+就拿最近公众号上 二叉树的打卡题目来说:
+
+[538.把二叉搜索树转换为累加树](https://mp.weixin.qq.com/s/rlJUFGCnXsIMX0Lg-fRpIw)
+
+其输入用例,就是用一个数组来表述 二叉树,如下:
+
+
+
+一直跟着公众号学算法的录友 应该知道,我在[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/Dza-fqjTyGrsRw4PWNKdxA),已经讲过,**只有 中序与后序 和 中序和前序 可以确定一颗唯一的二叉树。 前序和后序是不能确定唯一的二叉树的**。
+
+那么[538.把二叉搜索树转换为累加树](https://mp.weixin.qq.com/s/rlJUFGCnXsIMX0Lg-fRpIw)的示例中,为什么,一个序列(数组或者是字符串)就可以确定二叉树了呢?
+
+很明显,是后台直接明确了构造规则。
+
+再看一下 这个 输入序列 和 对应的二叉树。
+
+
+从二叉树 推导到 序列,大家可以发现这就是层序遍历。
+
+但从序列 推导到 二叉树,很多同学就看不懂了,这得怎么转换呢。
+
+我在 [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/q_eKfL8vmSbSFcptZ3aeRA)已经详细讲过,二叉树可以有两种存储方式,一种是 链式存储,另一种是顺序存储。
+
+链式存储,就是大家熟悉的二叉树,用指针指向左右孩子。
+
+顺序存储,就是用一个数组来存二叉树,其方式如图所示:
+
+
+
+那么此时大家是不是应该知道了,数组如何转化成 二叉树了。**如果父节点的数组下标是i,那么它的左孩子下标就是i * 2 + 1,右孩子下标就是 i * 2 + 2**。
+
+那么这里又有同学疑惑了,这些我都懂了,但我还是不知道 应该 怎么构造。
+
+来,咱上代码。 昨天晚上 速度敲了一遍实现代码。
+
+具体过程看注释:
+
+```CPP
+// 根据数组构造二叉树
+TreeNode* construct_binary_tree(const vector& vec) {
+ vector vecTree (vec.size(), NULL);
+ TreeNode* root = NULL;
+ // 把输入数值数组,先转化为二叉树节点数组
+ for (int i = 0; i < vec.size(); i++) {
+ TreeNode* node = NULL;
+ if (vec[i] != -1) node = new TreeNode(vec[i]); // 用 -1 表示null
+ vecTree[i] = node;
+ if (i == 0) root = node;
+ }
+ // 遍历一遍,根据规则左右孩子赋值就可以了
+ // 注意这里 结束规则是 i * 2 + 2 < vec.size(),避免空指针
+ for (int i = 0; i * 2 + 2 < vec.size(); i++) {
+ if (vecTree[i] != NULL) {
+ // 线性存储转连式存储关键逻辑
+ vecTree[i]->left = vecTree[i * 2 + 1];
+ vecTree[i]->right = vecTree[i * 2 + 2];
+ }
+ }
+ return root;
+}
+```
+
+这个函数最后返回的 指针就是 根节点的指针, 这就是 传入二叉树的格式了,也就是 力扣上的用例输入格式,如图:
+
+
+
+也有不少同学在做ACM模式的题目,就经常疑惑:
+
+* 让我传入数值,我会!
+* 让我传入数组,我会!
+* 让我传入链表,我也会!
+* **让我传入二叉树,我懵了,啥? 传入二叉树?二叉树怎么传?**
+
+其实传入二叉树,就是传入二叉树的根节点的指针,和传入链表都是一个逻辑。
+
+这种现象主要就是大家对ACM模式过于陌生,说实话,ACM模式才真正的考察代码能力(注意不是算法能力),而 力扣的核心代码模式 总有一种 不够彻底的感觉。
+
+所以,如果大家对ACM模式不够了解,一定要多去练习!
+
+那么以上的代码,我们根据数组构造二叉树,接来下我们在 把 这个二叉树打印出来,看看是不是 我们输入的二叉树结构,这里就用到了层序遍历,我们在[二叉树:层序遍历登场!](https://mp.weixin.qq.com/s/4-bDKi7SdwfBGRm9FYduiA)中讲过。
+
+
+完整测试代码如下:
+
+```CPP
+#include
+#include
+#include
+using namespace std;
+
+struct TreeNode {
+ int val;
+ TreeNode *left;
+ TreeNode *right;
+ TreeNode(int x) : val(x), left(NULL), right(NULL) {}
+};
+
+// 根据数组构造二叉树
+TreeNode* construct_binary_tree(const vector& vec) {
+ vector vecTree (vec.size(), NULL);
+ TreeNode* root = NULL;
+ for (int i = 0; i < vec.size(); i++) {
+ TreeNode* node = NULL;
+ if (vec[i] != -1) node = new TreeNode(vec[i]);
+ vecTree[i] = node;
+ if (i == 0) root = node;
+ }
+ for (int i = 0; i * 2 + 2 < vec.size(); i++) {
+ if (vecTree[i] != NULL) {
+ vecTree[i]->left = vecTree[i * 2 + 1];
+ vecTree[i]->right = vecTree[i * 2 + 2];
+ }
+ }
+ return root;
+}
+
+// 层序打印打印二叉树
+void print_binary_tree(TreeNode* root) {
+ queue que;
+ if (root != NULL) que.push(root);
+ vector> result;
+ while (!que.empty()) {
+ int size = que.size();
+ vector vec;
+ for (int i = 0; i < size; i++) {
+ TreeNode* node = que.front();
+ que.pop();
+ if (node != NULL) {
+ vec.push_back(node->val);
+ que.push(node->left);
+ que.push(node->right);
+ }
+ // 这里的处理逻辑是为了把null节点打印出来,用-1 表示null
+ else vec.push_back(-1);
+ }
+ result.push_back(vec);
+ }
+ for (int i = 0; i < result.size(); i++) {
+ for (int j = 0; j < result[i].size(); j++) {
+ cout << result[i][j] << " ";
+ }
+ cout << endl;
+ }
+}
+
+int main() {
+ // 注意本代码没有考虑输入异常数据的情况
+ // 用 -1 来表示null
+ vector vec = {4,1,6,0,2,5,7,-1,-1,-1,3,-1,-1,-1,8};
+ TreeNode* root = construct_binary_tree(vec);
+ print_binary_tree(root);
+}
+
+```
+
+可以看出我们传入的数组是:{4,1,6,0,2,5,7,-1,-1,-1,3,-1,-1,-1,8} , 这里是用 -1 来表示null,
+
+和 [538.把二叉搜索树转换为累加树](https://mp.weixin.qq.com/s/rlJUFGCnXsIMX0Lg-fRpIw) 中的输入是一样的
+
+
+
+这里可能又有同学疑惑,你这不一样啊,题目是null,你为啥用-1。
+
+用-1 表示null为了方便举例,如果非要和 力扣输入一样一样的,就是简单的字符串处理,把null 替换为 -1 就行了。
+
+在来看,测试代码输出的效果:
+
+
+
+可以看出和 题目中输入用例 这个图 是一样一样的。 只不过题目中图没有把 空节点 画出来而已。
+
+
+
+大家可以拿我的代码去测试一下,跑一跑。
+
+**注意:我的测试代码,并没有处理输入异常的情况(例如输入空数组之类的),处理各种输入异常,大家可以自己去练练**。
+
+
+# 总结
+
+大家可以发现,这个问题,其实涉及很多知识点,而这些知识点 其实我在文章里都讲过,而且是详细的讲过,如果大家能把这些知识点串起来,很容易解决心中的疑惑了。
+
+但为什么很多录友都没有想到这个程度呢。
+
+这也是我反复强调,**代码随想录上的 题目和理论基础,至少要详细刷两遍**。
+
+**[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)**里有的录友已经开始三刷:
+
+
+
+只做过一遍,真的就是懂了一点皮毛, 第二遍刷才有真的对各个题目有较为深入的理解,也会明白 我为什么要这样安排刷题的顺序了。
+
+**都是卡哥的良苦用心呀!**
+
+
+# 其他语言版本
+
+
+## Java
+
+```Java
+```
+
+
+## Python
+
+```Python
+```
+
+
+## Go
+
+```Go
+```
+
+## JavaScript
+
+```JavaScript
+```
+
+-----------------------
+* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
+* B站视频:[代码随想录](https://space.bilibili.com/525438321)
+* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
+
diff --git a/problems/前序/递归算法的时间与空间复杂度分析.md b/problems/前序/递归算法的时间与空间复杂度分析.md
index 27b9d7cc..63376d11 100644
--- a/problems/前序/递归算法的时间与空间复杂度分析.md
+++ b/problems/前序/递归算法的时间与空间复杂度分析.md
@@ -166,7 +166,7 @@ void time_consumption() {
system_clock::now().time_since_epoch()
);
- fibonacci_3(0, 1, n);
+ fibonacci_3(1, 1, n);
milliseconds end_time = duration_cast(
system_clock::now().time_since_epoch()
@@ -247,7 +247,7 @@ int binary_search( int arr[], int l, int r, int x) {
每次递归的空间复杂度可以看出主要就是参数里传入的这个arr数组,但需要注意的是在C/C++中函数传递数组参数,不是整个数组拷贝一份传入函数而是传入的数组首元素地址。
-**也就是说每一层递归都是公用一块数组地址空间的**,所以 每次递归的时间复杂度是常数即:O(1)。
+**也就是说每一层递归都是公用一块数组地址空间的**,所以 每次递归的空间复杂度是常数即:O(1)。
再来看递归的深度,二分查找的递归深度是logn ,递归深度就是调用栈的长度,那么这段代码的空间复杂度为 1 * logn = O(logn)。
diff --git a/problems/剑指Offer05.替换空格.md b/problems/剑指Offer05.替换空格.md
index 47907319..232d1a0a 100644
--- a/problems/剑指Offer05.替换空格.md
+++ b/problems/剑指Offer05.替换空格.md
@@ -313,4 +313,4 @@ func replaceSpace(_ s: String) -> String {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/剑指Offer58-II.左旋转字符串.md b/problems/剑指Offer58-II.左旋转字符串.md
index d8aaca67..60f7115d 100644
--- a/problems/剑指Offer58-II.左旋转字符串.md
+++ b/problems/剑指Offer58-II.左旋转字符串.md
@@ -200,17 +200,14 @@ func reverse(b []byte, left, right int){
JavaScript:
```javascript
-var reverseLeftWords = function (s, n) {
- const reverse = (str, left, right) => {
- let strArr = str.split("");
- for (; left < right; left++, right--) {
- [strArr[left], strArr[right]] = [strArr[right], strArr[left]];
- }
- return strArr.join("");
- }
- s = reverse(s, 0, n - 1);
- s = reverse(s, n, s.length - 1);
- return reverse(s, 0, s.length - 1);
+var reverseLeftWords = function(s, n) {
+ const length = s.length;
+ let i = 0;
+ while (i < length - n) {
+ s = s[length - 1] + s;
+ i++;
+ }
+ return s.slice(0, length);
};
```
@@ -249,4 +246,4 @@ func reverseString(_ s: inout [Character], startIndex: Int, endIndex: Int) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/动态规划-股票问题总结篇.md b/problems/动态规划-股票问题总结篇.md
index a3443ecb..899b9f6e 100644
--- a/problems/动态规划-股票问题总结篇.md
+++ b/problems/动态规划-股票问题总结篇.md
@@ -486,4 +486,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/动态规划总结篇.md b/problems/动态规划总结篇.md
index e7e57351..1f62b9df 100644
--- a/problems/动态规划总结篇.md
+++ b/problems/动态规划总结篇.md
@@ -1,4 +1,3 @@
-
@@ -8,6 +7,7 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
如今动态规划已经讲解了42道经典题目,共50篇文章,是时候做一篇总结了。
关于动态规划,在专题第一篇[关于动态规划,你该了解这些!](https://programmercarl.com/动态规划理论基础.html)就说了动规五部曲,**而且强调了五部对解动规题目至关重要!**
@@ -132,9 +132,9 @@
最后感谢录友们的一路支持,Carl才有继续更下去的动力[玫瑰],[撒花]
+
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
-
+
diff --git a/problems/动态规划理论基础.md b/problems/动态规划理论基础.md
index fd7977a6..728f0d7e 100644
--- a/problems/动态规划理论基础.md
+++ b/problems/动态规划理论基础.md
@@ -6,6 +6,12 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+# 动态规划理论基础
+
+动态规划刷题大纲
+
+
+
## 什么是动态规划
动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
@@ -122,22 +128,8 @@
今天我们开始新的征程了,你准备好了么?
-## 其他语言版本
-
-
-Java:
-
-
-Python:
-
-
-Go:
-
-
-
-
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/双指针总结.md b/problems/双指针总结.md
index 11d6ffa4..bea1359c 100644
--- a/problems/双指针总结.md
+++ b/problems/双指针总结.md
@@ -100,4 +100,4 @@ for (int i = 0; i < array.size(); i++) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/周总结/20200927二叉树周末总结.md b/problems/周总结/20200927二叉树周末总结.md
index 5a7e398a..ff8f67d4 100644
--- a/problems/周总结/20200927二叉树周末总结.md
+++ b/problems/周总结/20200927二叉树周末总结.md
@@ -51,8 +51,8 @@ morris遍历是二叉树遍历算法的超强进阶算法,morris遍历可以
在[二叉树:一入递归深似海,从此offer是路人](https://programmercarl.com/二叉树的递归遍历.html)中讲到了递归三要素,以及前中后序的递归写法。
文章中我给出了leetcode上三道二叉树的前中后序题目,但是看完[二叉树:一入递归深似海,从此offer是路人](https://programmercarl.com/二叉树的递归遍历.html),依然可以解决n叉树的前后序遍历,在leetcode上分别是
-* 589. N叉树的前序遍历
-* 590. N叉树的后序遍历
+* [589. N叉树的前序遍历](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/)
+* [590. N叉树的后序遍历](https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/)
大家可以再去把这两道题目做了。
diff --git a/problems/哈希表总结.md b/problems/哈希表总结.md
index 58e386bc..99a60585 100644
--- a/problems/哈希表总结.md
+++ b/problems/哈希表总结.md
@@ -131,4 +131,4 @@ std::unordered_map 底层实现为哈希,std::map 和std::multimap 的底层
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/哈希表理论基础.md b/problems/哈希表理论基础.md
index 2d3b03bd..aeb11b2e 100644
--- a/problems/哈希表理论基础.md
+++ b/problems/哈希表理论基础.md
@@ -133,4 +133,4 @@ std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/回溯总结.md b/problems/回溯总结.md
index f4578c83..421691da 100644
--- a/problems/回溯总结.md
+++ b/problems/回溯总结.md
@@ -382,7 +382,7 @@ used数组可是全局变量,每层与每层之间公用一个used数组,所
以下在计算空间复杂度的时候我都把系统栈(不是数据结构里的栈)所占空间算进去。
子集问题分析:
-* 时间复杂度:O(n * 2^n),因为每一个元素的状态无外乎取与不取,所以时间复杂度为O(2^n),构造每一组子集都需要填进数组,又有需要O(n),最终时间复杂度:O(n * 2^n)
+* 时间复杂度:O(2^n),因为每一个元素的状态无外乎取与不取,所以时间复杂度为O(2^n)
* 空间复杂度:O(n),递归深度为n,所以系统栈所用空间为O(n),每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为O(n)
排列问题分析:
@@ -390,7 +390,7 @@ used数组可是全局变量,每层与每层之间公用一个used数组,所
* 空间复杂度:O(n),和子集问题同理。
组合问题分析:
-* 时间复杂度:O(n * 2^n),组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
+* 时间复杂度:O(2^n),组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
* 空间复杂度:O(n),和子集问题同理。
N皇后问题分析:
@@ -454,4 +454,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/回溯算法去重问题的另一种写法.md b/problems/回溯算法去重问题的另一种写法.md
index d267d23c..d8125e91 100644
--- a/problems/回溯算法去重问题的另一种写法.md
+++ b/problems/回溯算法去重问题的另一种写法.md
@@ -250,9 +250,84 @@ used数组可是全局变量,每层与每层之间公用一个used数组,所
Java:
-
Python:
+**90.子集II**
+
+```python
+class Solution:
+ def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
+ res = []
+ nums.sort()
+ def backtracking(start, path):
+ res.append(path)
+ uset = set()
+ for i in range(start, len(nums)):
+ if nums[i] not in uset:
+ backtracking(i + 1, path + [nums[i]])
+ uset.add(nums[i])
+
+ backtracking(0, [])
+ return res
+```
+
+**40. 组合总和 II**
+
+```python
+class Solution:
+ def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
+
+ res = []
+ candidates.sort()
+
+ def backtracking(start, path):
+ if sum(path) == target:
+ res.append(path)
+ elif sum(path) < target:
+ used = set()
+ for i in range(start, len(candidates)):
+ if candidates[i] in used:
+ continue
+ else:
+ used.add(candidates[i])
+ backtracking(i + 1, path + [candidates[i]])
+
+ backtracking(0, [])
+
+ return res
+```
+
+**47. 全排列 II**
+
+```python
+class Solution:
+ def permuteUnique(self, nums: List[int]) -> List[List[int]]:
+ path = []
+ res = []
+ used = [False]*len(nums)
+
+ def backtracking():
+ if len(path) == len(nums):
+ res.append(path.copy())
+
+ deduplicate = set()
+ for i, num in enumerate(nums):
+ if used[i] == True:
+ continue
+ if num in deduplicate:
+ continue
+ used[i] = True
+ path.append(nums[i])
+ backtracking()
+ used[i] = False
+ path.pop()
+ deduplicate.add(num)
+
+ backtracking()
+
+ return res
+```
+
Go:
@@ -263,4 +338,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/回溯算法理论基础.md b/problems/回溯算法理论基础.md
index 29981c66..c11200a2 100644
--- a/problems/回溯算法理论基础.md
+++ b/problems/回溯算法理论基础.md
@@ -6,10 +6,15 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+# 回溯算法理论基础
-> 可以配合我的B站视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/) 一起学习!
+## 题目分类大纲如下:
-# 什么是回溯法
+
+
+可以配合我的B站视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/) 一起学习!
+
+## 什么是回溯法
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。
@@ -19,7 +24,7 @@
**所以以下讲解中,回溯函数也就是递归函数,指的都是一个函数**。
-# 回溯法的效率
+## 回溯法的效率
回溯法的性能如何呢,这里要和大家说清楚了,**虽然回溯法很难,很不好理解,但是回溯法并不是什么高效的算法**。
@@ -31,7 +36,7 @@
此时大家应该好奇了,都什么问题,这么牛逼,只能暴力搜索。
-# 回溯法解决的问题
+## 回溯法解决的问题
回溯法,一般可以解决如下几种问题:
@@ -52,7 +57,7 @@
记住组合无序,排列有序,就可以了。
-# 如何理解回溯法
+## 如何理解回溯法
**回溯法解决的问题都可以抽象为树形结构**,是的,我指的是所有回溯法的问题都可以抽象为树形结构!
@@ -63,7 +68,7 @@
这块可能初学者还不太理解,后面的回溯算法解决的所有题目中,我都会强调这一点并画图举相应的例子,现在有一个印象就行。
-# 回溯法模板
+## 回溯法模板
这里给出Carl总结的回溯算法模板。
@@ -149,7 +154,7 @@ void backtracking(参数) {
如果从来没有学过回溯算法的录友们,看到这里会有点懵,后面开始讲解具体题目的时候就会好一些了,已经做过回溯法题目的录友,看到这里应该会感同身受了。
-# 总结
+## 总结
本篇我们讲解了,什么是回溯算法,知道了回溯和递归是相辅相成的。
@@ -163,17 +168,6 @@ void backtracking(参数) {
-## 其他语言版本
-
-
-Java:
-
-
-Python:
-
-
-Go:
-
@@ -181,4 +175,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/字符串总结.md b/problems/字符串总结.md
index 57ac9a31..5ac415a9 100644
--- a/problems/字符串总结.md
+++ b/problems/字符串总结.md
@@ -130,4 +130,4 @@ KMP算法是字符串查找最重要的算法,但彻底理解KMP并不容易
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/数组总结篇.md b/problems/数组总结篇.md
index 7fafb94b..42e3323a 100644
--- a/problems/数组总结篇.md
+++ b/problems/数组总结篇.md
@@ -65,9 +65,9 @@
[数组:每次遇到二分法,都是一看就会,一写就废](https://programmercarl.com/0704.二分查找.html)
-这道题目呢,考察的数据的基本操作,思路很简单,但是在通过率在简单题里并不高,不要轻敌。
+这道题目呢,考察数组的基本操作,思路很简单,但是通过率在简单题里并不高,不要轻敌。
-可以使用暴力解法,通过这道题目,如果准求更优的算法,建议试一试用二分法,来解决这道题目
+可以使用暴力解法,通过这道题目,如果追求更优的算法,建议试一试用二分法,来解决这道题目
暴力解法时间复杂度:O(n)
二分法时间复杂度:O(logn)
@@ -86,10 +86,10 @@
暴力解法时间复杂度:O(n^2)
双指针时间复杂度:O(n)
-这道题目迷惑了不少同学,纠结于数组中的元素为什么不能删除,主要是因为一下两点:
+这道题目迷惑了不少同学,纠结于数组中的元素为什么不能删除,主要是因为以下两点:
* 数组在内存中是连续的地址空间,不能释放单一元素,如果要释放,就是全释放(程序运行结束,回收内存栈空间)。
-* C++中vector和array的区别一定要弄清楚,vector的底层实现是array,所以vector展现出友好的一些都是因为经过包装了。
+* C++中vector和array的区别一定要弄清楚,vector的底层实现是array,封装后使用更友好。
双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,都使用双指针法。
@@ -124,7 +124,7 @@
从二分法到双指针,从滑动窗口到螺旋矩阵,相信如果大家真的认真做了「代码随想录」每日推荐的题目,定会有所收获。
-推荐的题目即使大家之前做过了,再读一遍的文章,也会帮助你提炼出解题的精髓所在。
+推荐的题目即使大家之前做过了,再读一遍文章,也会帮助你提炼出解题的精髓所在。
如果感觉有所收获,希望大家多多支持,打卡转发,点赞在看 都是对我最大的鼓励!
@@ -149,4 +149,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/数组理论基础.md b/problems/数组理论基础.md
index 6d7b9f9a..15fbe9e4 100644
--- a/problems/数组理论基础.md
+++ b/problems/数组理论基础.md
@@ -88,7 +88,7 @@ int main() {
**所以可以看出在C++中二维数组在地址空间上是连续的**。
-像Java是没有指针的,同时也不对程序员暴漏其元素的地址,寻址操作完全交给虚拟机。
+像Java是没有指针的,同时也不对程序员暴露其元素的地址,寻址操作完全交给虚拟机。
所以看不到每个元素的地址情况,这里我以Java为例,也做一个实验。
@@ -124,4 +124,4 @@ public static void test_arr() {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/栈与队列总结.md b/problems/栈与队列总结.md
index ffcd38a1..b745ea19 100644
--- a/problems/栈与队列总结.md
+++ b/problems/栈与队列总结.md
@@ -181,4 +181,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/栈与队列理论基础.md b/problems/栈与队列理论基础.md
index c43ce0f5..3889b7ba 100644
--- a/problems/栈与队列理论基础.md
+++ b/problems/栈与队列理论基础.md
@@ -94,4 +94,4 @@ std::queue> third; // 定义以list为底层容器的队列
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/根据身高重建队列(vector原理讲解).md b/problems/根据身高重建队列(vector原理讲解).md
index dfc824fa..6d11fcbe 100644
--- a/problems/根据身高重建队列(vector原理讲解).md
+++ b/problems/根据身高重建队列(vector原理讲解).md
@@ -180,4 +180,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/知识星球精选/HR面注意事项.md b/problems/知识星球精选/HR面注意事项.md
new file mode 100644
index 00000000..6a0a26f1
--- /dev/null
+++ b/problems/知识星球精选/HR面注意事项.md
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+# HR面注意事项
+
+[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里已经有一些录友开始准备HR面。
+
+
+
+甚至星球里已经有录友拿到百度提前批的offer
+
+
+
+看到一些录友面试这么顺利还是非常开心的,同时我也在知识星球里分享了HR面最容易遇到的 问题,已经应该如何回答。
+
+相信公众号里不少录友也会遇到同样的问题,所以就给大家再分享一下。
+
+HR面的话,如果不犯重大问题,一般不会刷人。
+
+但有些同学也会犯重大问题,这样丢了offer,可就太可惜了。
+
+**HR的职责是选择符合公司文化价值观的员工**,那么说到文化价值观,大家可能感觉 这虚无缥缈的,怎么能证明自己符合文化价值观呢。
+
+其实HR主要从如下几个问题来考察,大家只要把这几个问题想清楚,就差不多了。
+
+
+## 为什么选择我们公司?
+
+这个大家一定要有所准备,不能被问到了之后一脸茫然,然后说 “就是想找个工作”,那基本就没戏了
+
+要从**技术氛围,职业发展,公司潜力**等等方面来说自己为什么选择这家公司。
+
+要表现自己如何如何看好这家公司,期待和这家公司一起成长。
+
+## 有没有职业规划?
+
+一般应届生都没有明确的职业规划,不过当HR问起来的时候,不要说 自己想工作几年想做项目经理,工作几年想做产品经理,甚至想当leader带团队,这样会被HR认为 职业规划不清晰,尽量从技术的角度规划自己。
+
+这个策略同样适用于社招。
+
+虽然大部分程序员的终极目标都想做leader,或者做管理,(极少数想要写一辈子代码的大牛除外,不过国内环境对这种大牛并不友好)
+
+大家都想做战略做规划,那比写代码有意思,有成就感多了。
+
+但不要说出来,一定要围绕技术这块来规划,根据你的岗位,**一年 技术达到什么程度,三年在某个技术领域有深入研究,五年成为技术专家之类的**等等。
+
+这块其实我和HR朋友还真的讨论过,我说:就大厂,百分之九十五以上的程序员都不想写代码,以后指定是想转产品或者升leader做项目管理, 但你为什么还要问这么 无聊的问题呢。
+
+HR朋友的回答是:你不说真相,我会认为你可能对技术有追求,但如果你说出真相,那么明确你对技术没有追求。
+
+所以,即使你有其他想法,在职业规划HR面的时候,**也要仅仅围绕技术,树立自己要深耕技术的形象**。
+
+## 是否接受加班?
+
+虽然大家都不喜欢加班,但是这个问题,我还是建议如果手头没有offer的话,大家尽量选择接受了吧
+
+就说:自己可以介绍 XX程度的加班。
+
+如果确实身体不适,那就直接拒绝,毕竟健康是第一位。
+
+## 坚持最长的一件事情是什么?
+
+这个问题,大家最好之前就想好,有一些同学可能印象里自己没有坚持很长的事情,也没有好好想过这个问题,在HR面的时候被问到的时候,一脸茫然,不知道该说啥。
+
+憋了半天说出一个不痛不痒的事情。这就是一个减分项了!
+
+问这个问题主要是考察大家的韧性,会不会做一个件事遇到困难就中途放弃了。
+
+星球里的录友可以说自己坚持每日打卡总结,这也是可以的,毕竟这种需要自己克制才能做到的事情。
+
+## 如果校招,直接会问:期望薪资XXX是否接受?
+
+这里大家如果感觉自己表现的很好 给面试官留下的很好的印象,**可以在这里争取 special offer**
+
+这都是可以的,但是要真的对自己信心十足。
+
+## 如果社招,则会了解前一家目前公司薪水多少 ?
+
+**这里大家切记不要虚报工资,因为入职前是要查流水的,也就是要把上一件公司的银行流水截图报上来,这个是比较严肃的问题。**
+
+
+好了,说了这么多,希望对大家有所帮助。
+
+---------------
+
+加入「代码随想录」知识星球,[点击这里](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
diff --git a/problems/知识星球精选/不一样的七夕.md b/problems/知识星球精选/不一样的七夕.md
new file mode 100644
index 00000000..a670e078
--- /dev/null
+++ b/problems/知识星球精选/不一样的七夕.md
@@ -0,0 +1,73 @@
+
+
+
+
+
+# 特殊的七夕
+
+昨天在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)发了一个状态:
+
+
+
+我还以为 过节嘛,录友们应该不会打卡了,但还依旧阻挡不辽录友打卡学习的步伐,来瞅瞅:
+
+
+
+
+
+
+
+
+
+
+
+当然了,我截图了一小部分,星球里每天的信息量都非常大。
+
+如果说一个人坚持每天总结记笔记,**其实是很容易放弃的,今天不记,明天不急,后天不整理感觉也无所谓**。
+
+这样时间就一点一点的被浪费掉了。
+
+**因为我们学过的东西都会忘,不及时整理,时间就不能沉淀下来**,这就造成了一边学,一边忘,最后感觉自己好像也没学啥的感觉!
+
+所以每天记笔记,及时整理,是非常重要的。
+
+这个习惯其实靠自己约束很容易放弃,但一群人一起坚持,就会不一样,大家相互监督,每天不总结记录点什么就会感觉少了点啥。
+
+而且,大家每天的总结,我都会看,有问题 我都及时指出来,这样也防止自己学着学着学跑偏了。
+
+昨天我也在[知识星球](https://mp.weixin.qq.com/s/X1XCH-KevURi3LnakJsCkA)回答了几位录友的问题,其中有两个问题 还是比较典型的,估计公众号里的录友也会遇到这样的疑惑。
+
+所以也给大家说一说:
+
+## 准备的太晚了,想放弃秋招,直接准备春招
+
+
+
+很多准备今年秋招的录友感觉自己还没准备好,想先找实习,或者 自己在学习学习,然后直接春招。
+
+其实不到万不得已,我还是建议要冲刺秋招。
+
+如果说,因为没准备好,提前批放弃还是可以的,但秋招不能也直接放弃了!
+
+秋招没找到好工作,一般11月份左右,一些大厂还会有补招,腾讯就经常补招,实在不行再准备春招,春招可能国企单位会多一些。
+
+**而且面试也很看缘分,永远没有真正准备好的时候,知识一直都学不完**,所以 把秋招当做最后的机会,就算秋招没找到,也可以在冲春招,而不是 直接放弃秋招。
+
+还有就心态方面来说,直接放弃秋招,等 今年 10月份,身边同学都找到工作了,天天吃喝玩乐,见面打招呼就问:你去哪了,你签哪了。那时候 自己心里压力会非常大,甚至会影响 春招找工作。
+
+
+## HR/面试官问你手上有没有其他offer,如何回答
+
+
+
+这个问题,无论是校招还是社招,大家都会遇到。
+
+如果大家手上有其他更好的offer,或者说同等水平公司的offer,可以说一说,这样凸显出自己的优势,即:你们不要我,有更好的公司要我, 这样给面试官或者HR点压力,可以争取到更高的薪酬。
+
+如果没有更好的offer,可以说没有,然后解释:只认准贵公司,从技术氛围,职业发展,公司前景,来说贵司多么多么的好,我多么渴望和贵司一起成长之类的。**总之,就是捧起来,显得自己很专一**。
+
+都是套路,哈哈哈哈。
+
+**好了,说了这么多,希望对大家有所帮助**。
+
+
diff --git a/problems/知识星球精选/不少录友想放弃秋招.md b/problems/知识星球精选/不少录友想放弃秋招.md
new file mode 100644
index 00000000..721a9313
--- /dev/null
+++ b/problems/知识星球精选/不少录友想放弃秋招.md
@@ -0,0 +1,81 @@
+
+
+
+
+
+# 不少录友想放弃秋招了.....
+
+马上就要九月份了,互联网大厂的秋招的序幕早已拉开。
+
+发现[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里有一部分录友想放弃秋招,直接准备明年的春招,估计关注公众号的录友也有不少有这种想法的。
+
+
+
+一般有这种想法的录友都是 **之前没有准备好,或者是总感觉时间赶趟赶趟,然后突然间 发现时间不赶趟了。。。**
+
+也有一些感觉自己没有实习经历,简历上也没什么好写,想下半年去找一找实习,不去秋招,然后直接准备春招。
+
+**对于这种情况,我的建议依然要冲刺秋招!**
+
+# 把秋招当做最后的机会
+
+**等到春招的时候,可以选岗位已经很少了,各个大厂几乎都招满了**。
+
+而且就算秋招没找到好工作,一般 11月份左右,一些大厂还会有补招,腾讯就经常补招。
+
+补招的情况是就是腾讯发出了 offer,有的候选人 选择违约,不来了,那么腾讯就需要补招,把人数凑齐。
+
+可能有录友想,谁居然连腾讯的offer都拒绝呢?
+
+其实挺多的,例如:有其他大厂的核心部门offer,父母给安排了 国企、央企 的核心岗位,或者有的选择 读博了之类的,导师毕业能给安排留校 或者去其他高校任教。
+
+所以几乎每年,腾讯都要补招,其他大厂也会有补招,一般是11月份,所以就算秋招没拿到大厂offer,依然有机会!
+
+话再说回来,面试其实也很看缘分,**永远没有真正准备好的时候,知识一直都学不完**。
+
+所以 **把秋招当做最后的机会,就算秋招没找到,也可以在冲春招,而不是 直接放弃秋招**。
+
+
+# 放弃秋招,对心态的影响
+
+如果直接放弃秋招,等 今年 10月份,身边同学都找到工作了,那时候的场面就是歌舞升平,大家天天吃喝玩乐。
+
+见面打会招呼就问:你去哪了,你签哪了?
+
+那时候如果自己还没有面试,还在准备面试,此时自己心里阴影面积有多大,甚至会影响春招找工作。
+
+# 面试要趁早准备
+
+每年这时候,都会有同学后悔,我怎么就没早点准备,就感觉时间不够用。
+
+所以也给明年找工作的录友们(2023届)提一个醒,现在就要系统性的准备起来了,因为明年春季实习招聘 是一个很好的进大厂的机会,剩下的时间也不是很多了。
+
+来看看[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,一位准大三的录友准备的情况
+
+
+
+再来看看一位准大二的录友准备情况
+
+
+
+**我已经预感到 这两位 等到秋招的时候就是稳稳的offer收割机**。
+
+[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)还有很多已经开始提前准备,或者看了 星球发文状态就开始着手准备的录友了。
+
+
+所以 **所谓的大牛,都是 很早就规划自己要学的东西,很早就开始向过来人请教应该如何找工作,很早就知道自己应该学哪些技术,看哪些书, 这样等到找工作的时候,才是剑锋出鞘的时候**。
+
+我们远远还没有到拼智商的程度。
+
+这里 也是给公众号里的录友们提一个醒,估计还有不少录友依然在感觉时间还赶趟,但 未来的卷王已经在路上了 哈哈哈。
+
+**不过话说回来,现在互联网求职确实卷!**
+
+但这是社会问题,我们改变不了。
+
+**卷的核心是,好的东西少,但要想的人多!**
+
+**如果你也想要,就要提前准备,提前规划,提前努力!**
+
+也希望录友们都能找到一个自己心仪的工作,加油💪。
+
diff --git a/problems/知识星球精选/专业技能可以这么写.md b/problems/知识星球精选/专业技能可以这么写.md
new file mode 100644
index 00000000..dd616713
--- /dev/null
+++ b/problems/知识星球精选/专业技能可以这么写.md
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+# 你简历里的「专业技能」写的够专业么?
+
+
+其实我几乎每天都要看一些简历,有一些写的不错的,我都会在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里分享一下。
+
+
+这次呢,我再专门说一说简历中的【专业技能】这一栏应该怎么写。
+
+很多同学【专业技能】这块写的很少,其实不是掌握的少,而是没有表达出来。
+
+例如有的同学这么写:
+
+
+
+
+---------------------
+
+
+
+--------------------
+
+
+
+-------------------
+
+
+
+
+这些【专业技能】都写的很少,其实是可以在丰富一些的。
+
+我来给大家拓展一下、
+
+
+* 熟练C++,(列举C++的若干知识点),了解 Java,python,go (适当补充对这些语言的理解)
+* 熟悉常见设计模式(例句一些设计模式)
+* 熟悉linux操作系统vim开发环境,(列举网络编程相关知识,例如epoll,socket等等)
+* 熟悉网络,(列举网络协议相关考点,tcp/ip,http, https, 三次,四次握手,流量控制等等)
+* 数量掌握数据结构与算法(列举常用算法,最好搞透一个算法,说对该算法有独到见解)
+* 数量使用Git,等版本控制
+* 以上为公共写法,下面可以在补充自己的其他领域的内容
+
+
+针对以上这个模板, 再来补充相关内容:
+
+1. 熟悉C/C++,熟练使用C的指针应用及内存管理,C++的封装继承多态,STL常用容器,C++11常用特性(智能指针等) ,了解 Python,Gtest等。
+2. 熟悉常用设计模式(单例模式,工厂模式等)
+3. 熟悉Linux下vim开发环境,了解网络编程,IO多路复用,epoll等等。
+4. 熟悉OSI五层网络模型,熟悉TCP/IP,UDP,HTTP/HTTPS,DNS等网络协议,熟悉TCP三次握手,四次挥手,流量控制,拥塞控制等手段。
+5. 熟悉常用的数据结构(链表、栈、队列、二叉树等),熟练使用排序,贪心,动态规划等算法。
+6. 熟悉使用Git,vscode工具使用。
+
+但需要注意的是,这里写的点,自己一定要熟练掌握,因为简历上写的,面试官一定会问。
+
+这样有一个好处,就是 **缩小面试官的问题范围**, 只要简历上写的,你都准备好了,那么简历上的知识点面试官一定会问,这样你就掌握了主动权。
+
+举一个例子,如果简历上直写:熟悉C++。其他都没介绍,那么面试官指定围绕C++漫天遍野的问起来了,你也猜不透面试官想问啥。
+
+如果简历写熟悉C/C++,熟练使用C的指针应用及内存管理,C++的封装继承多态,STL常用容器,C++11常用特性(智能指针等)。那么面试官基本上只会问,内存管理,多态,STL和C++11的一些特性, **这样你就把面试官的问题都圈在可控范围内**,从而掌握主动权!
+
+这一点很重要,希望大家要有这个思路,去写自己的简历。
+
+
diff --git a/problems/知识星球精选/关于实习大家的疑问.md b/problems/知识星球精选/关于实习大家的疑问.md
new file mode 100644
index 00000000..5d4e695b
--- /dev/null
+++ b/problems/知识星球精选/关于实习大家的疑问.md
@@ -0,0 +1,87 @@
+
+
+
+
+
+# 关于实习,大家可能有点迷茫!
+
+我在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里回答了很多关于实习相关的问题,其实很多录友可能都有这么样的疑问,主要关于实习的问题有如下四点:
+
+* 秋招什么时候开始准备
+* 要不要准备实习
+* 实习是不是重要?
+* 什么时候找实习最有帮助
+* 如何选择实习的offer
+
+下面都分别来说一说:
+
+## 秋招什么时候开始准备
+
+
+
+**准备工作指定是越早越好的**。
+
+准备的越早,在8,9月份就越淡定,每年校招很多同学都会对于准备找工作总感觉赶趟赶趟,结果到了8月份开始慌得一笔了。
+
+正常校招8月份就开始提前批(各大企业提前抢优秀毕业生)了,有的企业甚至7月份就开始。
+
+基本到了7月份可能就没有整块的时间静下心来准备找工作,那时候已经铺天盖地的各种招聘信息,甚至一些同学已经拿到了offer了。
+
+所以准备找工作的内容以7月为终结点比较稳妥,七月份之后以复习为主,有个整体框架,定时复习补充补充,多和同学交流面试经验。
+
+## 要不要准备实习
+
+有的同学是3,4月份准备面实习,然后7、8月份就去企业实习了,**实习有利有弊**。
+
+如果可以去一线互联网公司实习,而且岗位也合适,那当然要去,如果去不了也别难过,因为实习生大部分都是打杂,干的活甚至都写不到简历上。
+
+也有一小部分的实习生能够真正做到项目。
+
+如果没有去实习,就把基础好好补充一下,**基础打好,毕竟对于应届生基础最为重要**, 编程语言、数据结构算法、计算机网络、操作系统、数据库这些都是基础,规划好时间把这些内容学好。
+
+**对于应届生来说,项目经历是锦上添花,不是决定性的**。
+
+有实习经历(前提是实习工作内容是真正的做项目,不是打杂),那么面试的时候面试官可能对项目经历感兴趣,问基础的内容就比较少, 如果没有实习经历,就把基础内容巩固一下,校招是一样的。
+
+## 实习是不是非常重要?
+
+
+
+**大厂的实习经历对秋招还是很有帮助的**。
+
+
+但也不绝对,实习的话会耽误三个月左右,如果没有转正,而且一直在打杂的话,再去找秋招工作,**那时候一些基础的内容就都忘光了,反而秋招很被动**。
+
+现在当然也可以按照准备找实习的状态来要求自己,给自己点压力,毕竟找实习准备的知识和秋招准备的知识差不多。
+
+也可以提前经历一下面试,培养一下面试感觉,数据库方面知识你比较短缺,可以通过大量看这方面的面经迅速补充一下(秋招之前还可以系统看一看)。
+
+如果拿到了大厂的实习offer,就去吧,实习的时候心里要有个秤,如果工作是打杂,就要尽快自己抽时间看基础准备秋招。
+
+**另外需要注意的是,有些公司投递过简历面试没通过是有记录的,所以投递要当心,不要感觉投简历没有成本**,我知道的例如阿里,你每次投简历都有记录,如果实习面试都挂了,秋招的时候面试官也会看当时实习面试的记录(会考虑当时实习面试的结果)。
+
+## 什么时候找实习最有帮助
+
+
+
+6月份那时候基本不招实习生了,找的话也是日常实习(没有转正,实习时间是比较长的,要六个月),如果不是暑期实习就直接准备秋招吧。
+
+**只有应届的暑期实习才有转正的机会,因为企业这样安排也是为了提前发现优秀毕业生!**
+
+例如:今年暑期实习,只招明年毕业的应届生。
+
+
+## 如何选择实习的offer
+
+
+
+如果目标应该是C++后端开发,那客户端实习offer可以选择别去了。 或者 选一个实习时间最短的offer先去着,例如两个月之类的,这样既能体现一下工作流程,也别耽误太多时间(毕竟客户端开发不是你的目标)。
+
+**实习也不是必要的,一要看实习的岗位,是不是你想要的工作,二是实习的内容是不是打杂**,一些实习岗位其实是在浪费时间,如果转正不了的话,秋招就特别被动了,耽误了复习基础的时间。
+
+还有就是**秋招的时候,一定要找小公司先练手,然后在面大公司**。
+
+
+以上基本覆盖了大家对实习的各种疑惑,不过现在已经到了5月份,实习面试基本结束了,如果没有拿到实习offer,大家安心准备秋招吧,依然可以冲刺大厂!
+
+
diff --git a/problems/知识星球精选/关于提前批的一些建议.md b/problems/知识星球精选/关于提前批的一些建议.md
new file mode 100644
index 00000000..415a8b2f
--- /dev/null
+++ b/problems/知识星球精选/关于提前批的一些建议.md
@@ -0,0 +1,72 @@
+
+
+
+
+
+# 秋招和提前批都越来越提前了....
+
+正在准备秋招的录友,可能都感受到了,现在的秋招越来越提前了....
+
+以前提前批,都是 8月份,8月份中序左右,而不少大厂现在就已经提前批了。
+
+不少录友在 公众号留言,和[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,表示提前批来的还是有点快。
+
+
+
+还没有把刷题攻略刷完的录友,要尽快刷完,至少先刷一遍,了解相关题型。
+
+星球里,也有一些录友感受到了提前批的突如其来。
+
+
+
+目前已经开始的提前批有 vivo, tp-link,京东,百度.....
+
+
+
+
+有的录友已经明显紧张起来了。
+
+
+
+其实这才刚刚开始,等 7月份 以后,又是铺天盖地的信息,大家更没心情静下心来看书了。
+
+
+# 关于提前批的一点小建议
+
+**首先我们要知道为什么有提前批?**
+
+提前批不是大厂们呆着没事闲着多给我们一次面试机会,**而是提前抢优秀毕业生**,这一点大家一定要明确。
+
+
+了解了为什么有提前批,这样我们就有正确的心态面对它了。
+
+如果没有准备好,或者自己定位因为不是 “优秀毕业生”,先不要碰提前提。
+
+当然可以先面一些自己不想去的公司的提前批,用来练手。
+
+至于对于自己心仪的公司,如果盲目参加提前批,首先会打乱自己的复习计划,和心态,然后就是提前批挂了后台都是有记录的。
+
+只要是大厂的内部信息化做的比较完善,提前批挂了,是一定会记录的。
+
+**那么提前批有没有影响呢?**
+
+很多招聘宣传的时候说,提前批挂了对秋招没影响,确实在一定程度上没影响,因为提前批挂了,依然可以投递秋招。
+
+然后秋招面试的时候,面试官在不在意你的提前批成绩,就是另一回事了。
+
+我之前内推了一些录友面试腾讯微信支付的部门,面试官和我很熟悉,我最近还和他吃了饭,聊一聊我内推的同学,他说看后台记录有些同学都投过好几次了,他看了之前面试结果的评价之后,就直接pass了。
+
+所以大家可能要想好一个回答,就是面试官可能问:你的提前批为什么挂了?
+
+而且提前批挂了,都是有当时面试官评语的,如果7月份提前批面试,面试官评价:这位候选人基础不行。
+
+秋招的时候,面试官也不会相信,一两个月能把基础补上来了。 即使你的基础其实没问题,只不过恰巧面试中的几个问题没答好而已。
+
+
+对于技术能力确实强的同学,我建议全力以赴准备提前批面试,因为提前批要求就比较高,很容易谈sp offer。而且现在就拿到了大厂offer,比找实习还香。
+
+如果没准备好的同学,建议不要让提前批打乱阵脚,有计划的巩固基础,准备秋招。或者先拿自己不想去的公司的提前批练手。
+
+
+好了,说了这么多,希望对录友们有所帮助,加油💪
+
diff --git a/problems/知识星球精选/写简历的一些问题.md b/problems/知识星球精选/写简历的一些问题.md
new file mode 100644
index 00000000..af42cea1
--- /dev/null
+++ b/problems/知识星球精选/写简历的一些问题.md
@@ -0,0 +1,100 @@
+
+
+
+
+
+# 程序员应该这么写简历!
+
+自运营[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)以来,我已经给星球里的录友们看了 一百多份简历,并准对大家简历上的问题都给出了对应的详细建议。
+
+社招,校招,实习的都有,其实大家的简历看多了,发现有很多共性的问题,这里就和大家分享一下。
+
+我的简历模板也分享出来了,大家在「代码随想录」后台回复:简历模板,就可以获取!
+
+# 简历布局
+
+不少录友的简历布局就很不合理, 有的把专业技能放在最下面了,有的甚至把教育经历放下面了,建议简历布局的顺序是这样:
+
+* 教育工作经历
+* 专业技能
+* 项目经验
+* 荣誉奖项
+* 个人简述
+
+# 教育工作经历
+
+一些录友可能本科学历不是很好,然后 简历上直接不写自己的本科学校。
+
+其实教育经历是最基本的,你不写 面试官也一定会问,问出来 那么感觉更不好,所以关于教育经历,大家是一定要写的。
+
+写本科以后教育经历的就行了,一些录友可能是 高中就读了一些特别牛逼的高中,然后把高中也写出来了,哈哈哈,高中经历真的就不用写了。
+
+还有一些社招的录友,研究生和本科之间空了几年,这几年 一定要说清楚做了些什么,甚至是“编一下”,因为这个面试官也会问的。
+
+# 专业技能
+
+一些录友简历上没有「专业技能」这一栏,或者写的很短。
+
+可能是不知道该写啥,甚至就不写了。
+
+通常「专业技能」是在 「教育工作经历」之后的,我这里给出一个模板,大家按照这个格式来写「专业技能」就可以。
+
+1. 熟练使用 C++,掌握Go,了解 Java、Python、PHP 等编程语言
+2. 熟练使用 linux 下 vim、Git 开发环境
+3. 了解 Linux 下网络编程、TCP/IP 协议
+4. 掌握基础数据结构和算法的基本原理
+5. 英语六级:XXX
+
+
+一些录友会列举自己主修的课程,列了一堆,其实凑篇幅 我是理解的,就是感觉简历太单薄的,列课程来凑。
+
+但大家凑篇幅 尽力在「专业技能」和「项目经验」上凑篇幅,如果把 自己主修可能都列出来,会让面试官感觉没有什么干货。(有的同学甚至靠留白才凑篇幅,这就更不要了)
+
+当然应届生如果有一些课程自己成绩确实很好,可以和「教育经历」写在一起,简单并行列举一下就可以了。
+
+# 项目经验
+
+很多录友写项目经验就是流水账,这是什么项目,自己完成了功能1,2,3,4。堆了很多字。
+
+要知道面试官是不了解你的项目的,面试也只有 一个小时左右的时间,如果堆了很多文字 面试官也懒得去读。
+
+面试官最在意的是什么呢?
+
+**项目中有哪些技术难点,以及 你是如何克服的**。
+
+这是面试官最关心的,也是最能体现出候选人技术深度的问题。
+
+所以大家在描述项目经验的时候,一定要时刻想着,这个项目的难点究竟是什么,要反复问自己这个问题。
+
+可能有的同学说了,我这项目本来就没有难点啊,就是1,2,3,4功能,然后 遇到不会的,百度搜一下,差不多就这样了。
+
+**项目没有难点,也要自己“造难点”**。 因为这个问题是面试官必问的!
+
+所以一定要准备好。
+
+还有不少录友的项目经历都写了 web server,使用线程池 + 非阻塞 socket + epoll(ET 和 LT) + 事件处理 (Reactor 和模拟 Proactor) 等等。
+
+这个项目可能是很多准备后台开发的同学 首选的 项目。
+
+这种自己搞的小项目,**最好把你的代码上传的github上,然后在简历中贴出github地址**,面试官一定会看的。
+
+如果看你的代码写的确实不错,那指定是加分项。比简历上写的天花乱坠都强!
+
+还有的同学项目经历特别多,写了5,6个项目,每个项目都是概述了一下自己做了XXX。
+
+其实面试官,基本就会和你深入聊 2个的项目左右,列举这么多项目没有用的,关键这些项目一看也是技术含量不大。
+
+**所以不用单纯堆项目个数。项目经历 两个足够,把两个项目搞深搞透**
+
+
+# 校园经历
+
+一些录友会把自己学校工作列出一大堆,例如各种学生会啊,创新部门啊之类的。甚至有的会把自己的减肥经历也列举出来。
+
+如果面技术岗位,这一块其实不是面试官关心的,可以在 最后一栏「个人简述」,简单一两句概括一下自己的学生会经历就好,表明自己沟通能力没问题。
+
+关于标明自己有毅力,有恒心,不怕吃苦等等,都简单一句概括。
+
+
+好了,关于简历的问题,我就先分享这些,估计应该击中了不少录友的痛点了。
+
diff --git a/problems/知识星球精选/刷力扣用不用库函数.md b/problems/知识星球精选/刷力扣用不用库函数.md
new file mode 100644
index 00000000..07db8564
--- /dev/null
+++ b/problems/知识星球精选/刷力扣用不用库函数.md
@@ -0,0 +1,34 @@
+
+
+
+
+
+# 究竟什么时候用库函数,什么时候要自己实现
+
+在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里有录友问我,刷题究竟要不要用库函数? 刷题的时候总是禁不住库函数的诱惑,如果都不用库函数一些题目做起来还很麻烦。
+
+估计不少录友都有这个困惑,我来说一说对于库函数的使用。
+
+一些同学可能比较喜欢看力扣上直接调用库函数的评论和题解,**其实我感觉娱乐一下还是可以的,但千万别当真,别沉迷!**
+
+例如:[字符串:151. 翻转字符串里的单词](https://mp.weixin.qq.com/s/X3qpi2v5RSp08mO-W5Vicw)这道题目本身是综合考察同学们对字符串的处理能力,如果 split + reverse的话,那就失去了题目的意义了。
+
+有的同学可能不屑于实现这么简单的功能,直接调库函数完事,把字符串分成一个个单词,一想就是那么一回事,多简单。
+
+相信我,很多面试题都是一想很简单,实现起来一堆问题。 所以刷力扣本来就是为面试,也为了提高自己的代码能力,扎实一点没坏处。
+
+**那么平时写算法题目就全都不用库函数了么?**
+
+当然也不是,这里我给大家提供一个标准。
+
+**如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数**。
+
+**如果库函数仅仅是 解题过程中的一小部分,并且你已经很清楚这个库函数的内部实现原理的话,那么直接用库函数。**
+
+使用库函数最大的忌讳就是不知道这个库函数怎么实现的,也不知道其时间复杂度,上来就用,这样写出来的算法,时间复杂度自己都掌握不好的。
+
+例如for循环里套一个字符串的insert,erase之类的操作,你说时间复杂度是多少呢,很明显是O(n^2)的时间复杂度了。
+
+在刷题的时候本着我说的标准来使用库函数,详细对大家回有所帮助!
+
+
diff --git a/problems/知识星球精选/刷题攻略要刷两遍.md b/problems/知识星球精选/刷题攻略要刷两遍.md
new file mode 100644
index 00000000..1f4fd7f9
--- /dev/null
+++ b/problems/知识星球精选/刷题攻略要刷两遍.md
@@ -0,0 +1,67 @@
+
+
+
+
+
+# 代码随想录上的题目最好刷两遍以上
+
+今天秋招可能要提前很多,往年9月份开始秋招,今天可能9月份就已经结束了,所以 正在准备秋招的录友,还是要抓紧时间了。。
+
+星球里已经有录友的给出了关于秋招提前的信息
+
+
+
+那么在正式秋招之前,大家在准备算法,代码随想录上的题目 应该刷几篇呢?
+
+**至少刷两遍,只刷一遍是不够的**。
+
+只刷一遍基本就会忘,而且关键方法论理解的也不到位,对递归三部曲,回溯三部曲,动规五部曲,只掌握了简单招式,而没有理解真正精髓。
+
+拿到最简单的反转链表来说,只做一遍,下次面试出现基本还是做不出来。
+
+这也是星球里 录友们的 多么痛的领悟!
+
+
+
+**等大家刷第二遍的时候,才能找到触类旁通的感觉!**
+
+第三遍基本就得心应手了。
+
+在[「代码随想录」知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)中,我都是强调大家要至少刷两遍,有时间的话刷三遍,
+
+可以看看星球里录友们的打卡:
+
+
+
+
+
+
+
+有的录友已经开始三刷:
+
+
+
+
+我为什么鼓励大家有时间的话,多刷几遍呢,首先这里的题目都是经典题目,而且在面试汇总也是频频出现,
+
+下面也是星球里的录友总结的面经:
+
+
+
+
+
+
+## 那么已有的题目刷完了,可以刷些什么呢?
+
+我在Github上也做了一些题目的补充,在[上榜之后,都有哪些变化?](https://mp.weixin.qq.com/s/VJBV0qSBthjnbbmW-lctLA)说到了。
+
+对于面试来说除了「代码随想录」上的题目,再了解一下:排序系列,简单图论(深搜,广搜,最小生成树,最短路径等),高级数据结构:并查集,字典树(了解一下),之后就差不多了。随便在leetcode找一些题目保持手感,题量至少300+,会稳一点。
+
+关于深搜和广搜,其实深度优先搜索,我在二叉树的专题中讲解递归遍历,和回溯算法中 都讲了。
+
+广度优先搜索,在二叉树树的层序遍历也讲了。
+
+而图论中主要是在邻接表上进行的深搜和广搜。
+
+面试中还是很少会考察图论,因为图论的代码量往往比较大,不适合在面试中考察,**面试中出现题目概率最大的是二叉树,回溯算法和动态规划!**
+
diff --git a/problems/知识星球精选/博士转行计算机.md b/problems/知识星球精选/博士转行计算机.md
new file mode 100644
index 00000000..66769264
--- /dev/null
+++ b/problems/知识星球精选/博士转行计算机.md
@@ -0,0 +1,53 @@
+
+
+
+
+
+# 本硕非计算机博士,如果找计算机相关工作
+
+在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,有一位博士录友,本硕都不是计算机,博士转的计算机,问了这样一个问题
+
+
+
+一下是我的回答,在这里分享给大家。
+
+我的一些研究生同学,都在读博,有的毕业有的还没毕业,平时和他们聊,对博士就业也是有一定的了解,这里说一说我的建议。
+
+对于博士,强烈建议能去高校就去高校,这样最大程度发挥出了博士的好处,赚国家科研经费的钱还是香的。
+
+虽然现在对青年研究者并不友好,基本经济大头都被实验室boss拿走了。
+
+但高校壁垒高,外界再优秀的人才,也进不去和你竞争,所以可以小范围的卷。出来的话,就是和整个社会AI领域甚至和研发的同学一起卷。
+
+**在企业 是谁有能力谁就上, 在高校,至少你得有博士学位才能上! 这就是很高的门槛了**。
+
+
+而且能给博士提供岗位的企业少之又少,所以博士的就业面反而窄了。
+
+可能有同学想,薪酬要的低一点还不行么,其实博士毕业对薪资还是有要求的,如果薪资和本科,硕士应届生一样的话,自己也接受不了。
+
+所以高校能给博士的机会更多一些,不过现在高校也是 青年科研人员都是 五年合同制,如果没有产出,也要走人了,压力也很大。
+
+及时这样,还是建议能去高校去高校,当然这需要有心善、能力强、有人脉的博导,那是最好的了,(**注意这里选择博导,心善是最重要的一个选项**)
+
+实在去不了高校,接下来我们在看企业。
+
+博士找工作不建议走正式招聘流程,例如走官网投递之类的,面试都没戏。
+
+**AI岗现在对coding能力,工程能力要求都很高,企业现在特别喜欢有科研能力和工程能力的博士**,但这种人才还是稀缺的,大多数博士可能不会做那么多的工程项目,更别说还是本硕是非计算机专业的博士。
+
+所以博士找工作要靠门派,最好你的导师,实验室大boss 有哪些徒弟在外面企业,BAT华为之类的干的很好,能联系上,就是同一门派的师兄弟。
+
+联系上,做一个内推,他们看一下你的博士论文和研究成果,如果行的话,面试基本就是走个流程,这样就很舒服了。
+
+如果上来一波算法题,然后 操作系统,网络 数据库,这样考察下来,基本计算机专业的博士也招架不住,毕竟大多数博士是科研型的,一般来说工程能力比较弱,计算机基础哪些基本也没时间看。
+
+
+再说一说非科班博士如果有机会去面试,**一定要重点突出知识的迁移能力,和对学术的研究能力,这个是硕士本科生所不能具备的**。
+
+企业还是比较喜欢有快速学习能力和知识迁移能力的人,因为技术是不断在变化了,只有学习能力足够强再能跟上。
+
+所以**不能和本科硕士去硬拼专业技能的储备量**,特别是最新最热的技术(因为本来就是非科班嘛), 要体现出博士对技术对知识的理解,这是你的优势。
+
+以上是我的回答,希望多大家有所启发。录友们周末愉快🌹
+
diff --git a/problems/知识星球精选/合适自己的就是最好的.md b/problems/知识星球精选/合适自己的就是最好的.md
new file mode 100644
index 00000000..fda51afa
--- /dev/null
+++ b/problems/知识星球精选/合适自己的就是最好的.md
@@ -0,0 +1,37 @@
+
+
+
+
+
+# 合适自己的,才是最好的!
+
+秋招已经进入下半场了,不少同学也拿到了offer,但不是说非要进大厂,每个人情况都不一样,**合适自己的,就是最好的!**。
+
+[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里有一位录友,就终于拿到了合适自己的offer,并不是大厂,是南京的一家公司,**但很合适自己,其实就非常值得开心**。
+
+
+
+
+
+其实我算是一路见证了这位录友披荆斩棘,**从一开始基础并不好,还是非科班,到 实验室各种不顺利,再到最后面试次次受打击,最后终于拿到自己满意的offer**。
+
+这一路下来确实不容易!
+
+这位录友是从几年五月份加入星球。
+
+
+
+然后就开始每天坚持打卡。 可以看看她每天的打卡内容。
+
+
+
+后面因为天天面试,不能坚持打卡了,也是和大部分同学一样,焦虑并努力着。
+
+
+
+星球里完整的记录了 这位录友 从五月份以来每天的学习内容和学习状态,能感觉出来 **虽然苦难重重,但依然元气满满!**
+
+我在发文的时候 看了一遍她这几个月完整的打卡过程,还是深有感触的。
+
+[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里还有很多很多这样的录友在每日奋斗着,**我相信 等大家拿到offer之后,在回头看一下当初星球里曾经每日打卡的点点滴滴,不仅会感动自己 也会感动每一位见证者**。
+
diff --git a/problems/知识星球精选/备战2022届秋招.md b/problems/知识星球精选/备战2022届秋招.md
new file mode 100644
index 00000000..207a5e2a
--- /dev/null
+++ b/problems/知识星球精选/备战2022届秋招.md
@@ -0,0 +1,67 @@
+
+
+
+
+
+# 要开始准备2022届的秋招了
+
+在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里准备秋招的录友还真不少,也会回答过不少关于秋招的问题。
+
+
+
+能感觉出来慌,不止这一位提问题的录友,很多同学都是这样,就是感觉一天天过的很快,也没干什么事情,然后心里就一直恐慌。
+
+其实**慌主要是因为没有计划**,每天没有目的,然后一天一天的过,秋招越来越近,自然慌的很。
+
+我在这篇里系统性的解答了实习相关的问题,[关于实习,大家可能有点迷茫!](https://mp.weixin.qq.com/s/xcxzi7c78kQGjvZ8hh7taA),也提到了 一般秋招8月份就要正式开始了,那时候各种提前批,面试小道消息,甚至身边一些同学已经拿到了offer,恐怕已经没有时间静下心好好学习了。
+
+所以从现在看是满打满算,还有三个月的准备时间,如果利用好还是挺充足的,不要每天在把时间浪费在各种无聊的活动上。
+
+这里我来列举一下,此时大家应该明确哪些事情:
+
+## 确定自己要面试的岗位
+
+说道选择哪些岗位,一般的回答都是:选择自己感兴趣的呗,兴趣是最好的老师之类的balabala。
+
+但我能亲身体会到,目前咱们的教育环境 也不可能说培养出什么 自己真正的兴趣,在大街上随便问一个人你的兴趣是什么? 基本回答都是:吃喝玩睡呗,还能有啥兴趣。
+
+所以务实的建议是:现在去各大公司校招招聘官网 把所有的岗位都看一看,看看都有哪些要求,结合目前自己的经历和已经掌握的内容,看看哪些岗位可能最接近,然后再问问师兄师姐 这个岗位或者公司如何,最后后就把自己的目标岗位定下来了。
+
+也有很多录友在星球里问我关于一些公司,岗位前景之类的问题,我都会给出相应的建议,这也是我工作过程中接触过了解过的。后面我也把部分内容整理一下,在公众号分享一下。
+
+这样接下来的时间就是全身心去准备这个岗位所需要的技能。
+
+**不少同学在秋招的时候,试试算法岗感觉也行,投投前端感觉也行,尝试后端也不是不可以,甚至再面面产品经理**。
+
+**这样在秋招的时候就很被动了**,哪个岗位都没准备好,哪个岗位还都想试试,大概率是最后都没面上的。
+
+所以现在就要把自己的目标岗位锁定了。 不同岗位之间 要求还是不一样的。 大家去看看招聘官网的要求就可以了。
+
+## 制定学习计划
+
+知道自己的目标岗位了,也知道岗位的要求了,剩下的就是制定详细的计划。
+
+* 编程语言
+* 计算机基础(操作系统,网络,数据库、设计模式)
+* linux相关(客户端岗位应该不需要)
+* 项目
+* 准备自己的博客,github
+
+这几块按照岗位要求,8月份应该学会哪些内容,需要看哪些书等等。
+
+然后以八月份以deadline开始倒推,每个月应该学哪些,每周应该学哪些,每天应该看那些。
+
+把这些都明确了,心里就不慌了,也不会每天浪费大量宝贵的时间。
+
+星球里的录友每天都在坚持打卡,总结自己今天学习的内容,这样就很好,把自己每天学习进度量化。
+
+这样每天过的就很扎实。而且每天的打卡星球里录友和我都可以看到,我也会及时评论评论,大家也相互监督。
+
+给大家看一位录友在星球里的总结:
+
+
+
+大家平时的话,也要养成这种总结的习惯,只有及时总结把自己学过的内容积累下来,才能把时间沉淀下来,要不然就是一边学一边忘的节奏了。
+
+**其实这种习惯,无论是秋招,还是准备跳槽,还是平时工作积累,都非常总要!**
+
diff --git a/problems/知识星球精选/大厂新人培养体系.md b/problems/知识星球精选/大厂新人培养体系.md
new file mode 100644
index 00000000..ccd2f1c2
--- /dev/null
+++ b/problems/知识星球精选/大厂新人培养体系.md
@@ -0,0 +1,81 @@
+
+
+
+
+
+# 大厂的新人培养体系是什么样的
+
+之前我一直在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)和大家讲,能进大厂一定要进大厂,大厂有比较好的培养体系。
+
+也有录友在星球里问我,究竟培养体系应该是什么样的呢? 大厂都会这么培养新人么?
+
+
+
+其实大厂部门也是非常多,不同的领导对待新人的态度也是不一样的。
+
+就拿腾讯来说,腾讯里面 上千个部门,基本就是上千个小公司,只不过外面披一个腾讯的壳子,每个小公司的leader风格截然不同。
+
+赶上什么样的领导了,这就看命运了。
+
+**只能说进大厂,大概率会有一个比较好的培养体系**。
+
+那么好的培养体系是什么呢?
+
+要从两个方面来说:
+
+* 给你详细的学习路线(自我技术提升)
+* 给你有产出的活(用来晋升)
+
+## 详细的学习路线
+
+关于详细的学习路线,一般大厂入职之后配有导师的,导师给你安排的每一个功能,应该带你熟悉整个研发的流程。
+
+一个功能的开发,需要经历如下几步:
+
+1. 看需求文档,确定需求
+2. 这个需求包含了哪些功能
+3. 有哪些难点
+4. 后台架构是什么样的(要有架构图)
+5. 定协议(前后台一起商量),服务与服务之间的,后台与客户端之间的
+6. 设计数据结构+算法=程序
+7. 预估一下容量(各种资源例如带宽,存储,CPU等等)
+8. 考虑一下部署(安全性,容灾,可伸缩性。。。。)
+9. 设计评审
+(上面过程都是在分析)
+10. 编码
+11. 自测
+12. 联调
+13. 交给测试
+14. 代码review
+15. 合入
+16. 发布
+
+可以看出来,写代码仅仅是 其中的一小步。导师应该带你走一遍完整的开发流程,然后告诉一些注意事项,**这样为自己程序员生涯打好基础**。
+
+可能有的同学会感觉:我就开发一个小功能,哪用得着这么多步骤,一把梭哈,直接代码写完。
+
+哈哈,这么想的同学一般是没有参与过大型且流程规范的项目开发。互联网千万级用户的项目,几十上百人一起开发是需要规范的,**所以上面我说的每一步都很重要!**
+
+## 有产出的活
+
+初入职场的同学,可能都非常在意能不能学到东西,也就是自我技术提升,往往忽视了你干的活,是否有产出,能不能用来晋升。
+
+这里就是很多所谓的“套路”,老司机一般挑的活符合如下几点:
+
+* 非常规整(周边没有烂糟的额外工作,例如还要开发各种小工具之类的)
+* 独立模块(不需要和其他人扯皮,省事)
+* 对项目组很重要(既有技术难点,又没有大量的业务增删改查)
+* 风险系数比较低(上线出问题,锅甩不到他这里)
+
+这种活就是好活,用来晋升的利器,而且干着也舒服。
+
+但一个项目,一定会有大量需要打杂的活,写各种脚本,各种处理数据,然后看各种问题,整理文章,汇报,开发边缘工具等等。
+
+新人一般进来都是先打杂的,**但如果领导确实是细心培养你,还是额外给你一个小模块,让你做好,这个小模块就是让你用来晋升的或者转正的**。
+
+这个建议不仅适用于实习生,对于初入职场的同学也很用帮助,这个部门是不是有在培养你,老司机一眼就能看出来,只不过新人可能自己很难发现。
+
+所以需要过来人点拨一下,大家就知道自己现在的处境了。
+
+希望对大家求职和工作有所帮助!
+
diff --git a/problems/知识星球精选/如何权衡实习与秋招复习.md b/problems/知识星球精选/如何权衡实习与秋招复习.md
new file mode 100644
index 00000000..275588df
--- /dev/null
+++ b/problems/知识星球精选/如何权衡实习与秋招复习.md
@@ -0,0 +1,47 @@
+
+
+
+
+
+# 已经在实习的录友如何准备秋招?
+
+最近在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)一位录友问了实习生如何权衡工作和准备秋招的问题。
+
+
+
+
+其实这个问题挺有代表性,我之前一直讲的都是没有去实习的录友应该怎么怎么办,其实已经在大厂实习的录友也有很多烦恼。
+
+这里我说一说已经有大厂实习经历,准备秋招的重点。
+
+一般来说有大厂的实习经历,**面试官的兴趣都在大厂实习的项目经验上**。
+
+所以关于权衡实习和准备基础这两个方面,**可以把主要精力放在项目包装上,其次是看基础**。
+
+要在包装实习项目上多花点心思,实习生做的项目偏上业务很正常,不可能让实习生搞太复杂的,一旦出了问题还得导师背锅。
+
+自己干的活,或者项目很简单 不要紧,可以好好包装一下,如果没有难点,**可以结合业务自己“造”难点**,大厂内部研发文档都特别多而且特别全。
+
+例如整个项目的立项,各个模块的开发,以及研发中遇到的困难,技术选型,上线事故,等等这些都是有完整的文档记录的。(当然大厂也有一些部门研发流程很原始,没有文档,全靠口述)
+
+从这些文档中也可以找出 难点糅合到自己的项目中。
+
+假如线上出了事故,虽然自己不用去排查但可以跟着同事们一起看问题,一起分析,甚至帮他捞捞日志,打打下手。
+
+这次事故的表现,起因,定位等等,排查问题的同事都会记录的清清楚楚,放在项目文档下。
+
+可以把这些文档都多看看,然后就可以转变成自己排查线上事故的经历了。
+
+**这种经历在面试官来看就是很大的加分项了**。
+
+所以在大厂实习,想包装自己的项目方法有很多,只不过一些同学初入职场,对自己有帮助的资料或者内容不太敏感,不善于利用已有的资源。
+
+**需要过来人点一下,基本就上道了,哈哈哈**。
+
+当然不是说只要在大厂实习,基础完全就不用看了,抽空也要看,只不过 项目经验(项目的包装)更重要!
+
+关于提前批,一般来说本厂实习生是不能参加提前批的。
+
+你可以参加 其他大厂的提前批,只不过参加提前批是有代价的,面试不通过都是有记录的,得看自己项目准备的如何,能不能拿出手,而且一边实习一边面试可能也不太方便,如果想试一试,就找自己不想去的企业的提前批试试水!
+
+
diff --git a/problems/知识星球精选/客三消.md b/problems/知识星球精选/客三消.md
new file mode 100644
index 00000000..8a7b5fc6
--- /dev/null
+++ b/problems/知识星球精选/客三消.md
@@ -0,0 +1,95 @@
+
+
+
+
+
+# 客三消!
+
+给大家科普一下:什么是客三消。
+
+翻译过来就是客户端三年消失。
+
+**听起来是不是有点吓人**!这种说法略夸张,但只要能传开,就说明客户端一定有一些困局,并不是空穴来风。
+
+昨天卡哥在朋友圈里分享了一个段子的截图
+
+
+
+然后朋友圈就炸了,上百条的留言,问我这是为啥。
+
+其实这个问题在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里也有录友问过我。
+
+
+
+当时已经详细的回答了一波,估计很多录友都有这方面的困惑,所以在公众号上再来给大家讲一讲。
+
+**关于星球提问中SRE和后端,在这里就不介绍了,卡哥重点说一说,客户端**。
+
+客户端目前比较突出的问题,主要是 这四点:
+
+1. 客户端岗位需求相对较小,而且有越来越小的趋势
+
+2. 技术做深相对较难
+
+3. 客户端晋升相对困难
+
+4. 中年危机 (其实程序员有,不过客户端可能更明显一些)
+
+
+## 岗位需求相对较小
+
+客户端需求的减少,主要是体现在中小厂,或者说创业公司,因为大家都养不起原生客户端,基本都会采用跨端的技术方案,也就是大前端(即一套代码可以编译出各个端的版本,包括安卓,IOS等各种终端)。
+
+这样就节省了很大的人力,不过目前在功能上一定没有 原生客户端体验好。
+
+**但大前端取代客户端是技术趋势!**
+
+如果选择客户端,那么就多少要掌握一些跨端技术方案。
+
+互联网软件的思维,就是轻前端,重后端,为什么PC软件搬到了浏览器上,移动APP搬到小程序上,都是这个道理,一般重头戏在后端。
+
+所以后端的需求才会比较大。
+
+## 技术做深相对较难
+
+这里就不止客户端,其实前端都有这个问题。
+
+关于前端和客户端的区别,其实没有那么严格的定义,大家可以理解 前端包含了客户端。一切可视化皆为前端。
+
+前端框架、渲染引擎 变化相对快,可能你刚熟悉一个框架,然后就换了,最关键是可能还拿不准哪一种框架日后会成为主流,一不小心就跑偏了。
+
+而后端框架变化相对就慢得多,而且 更容易(或者说更有机会)把技术做深,因为 高并发,高可用,低延迟 这些基本都是后端的工作。
+
+正是前端 技术栈更新太快,所以要持续高强度学习 (这种学习可能不是往深去学习,而是 适应一个又一个框架的学习)。
+
+而且前端 很容易陷入需求的反复变化之中,因为一个功能或者界面的修改,都是前端同学的工作量。
+
+后端可能 什么都不用改,接口都是一样的,然后就可以空出时间研究技术。
+
+## 晋升
+
+目前在大厂,客户端职业天花板相对较低,一般情况下,可能到组长就到头了。
+
+搞技术一路升上去,甚至到CTO的,基本都是后端,这也是因为前面讲过的:大部分的互联网产品,重头戏在后端,所有后端更有机会把技术做深,更直白说,后端更有机会在晋升做ppt的时候 “吹牛逼”。
+
+
+## 中年危机
+
+这个就更范范一些了,程序员都有这个危机,不过客户端可能更突出一些。
+
+原生客户端的岗位需求确实会越来越少,如果继续干下去,没有晋升到管理层,然后退居二线公司,发现二线公司都没有原生客户端的岗位,那么就非常被动了。
+
+所以可以往大前端的方向去转。
+
+大前端现在也有很多技术方案,ReactNative和weex(阿里,脸书的方案),Flutter(Google的方案),微信小程序(腾讯的方案)
+
+不过最终哪一个方案一统天下,这还是未知数,所以就需要持续学习咯。
+
+# 总结
+
+以上就是我在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里的详细回答。
+
+注意我这里说的一般情况,当然各个岗位都有佼佼者,或者说大牛,客户端也有大牛,也很香,不过这是极少数,就不在讨论范围内了。
+
+希望对大家理解目前客户端的趋势有所帮助。
+
diff --git a/problems/知识星球精选/技术不好如何选择技术方向.md b/problems/知识星球精选/技术不好如何选择技术方向.md
new file mode 100644
index 00000000..dd13f46b
--- /dev/null
+++ b/problems/知识星球精选/技术不好如何选择技术方向.md
@@ -0,0 +1,32 @@
+
+
+
+
+
+# 技术不太好,也不知道对技术有没有兴趣,我该怎么选?
+
+最近在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里解答了不少录友们的疑惑,其实发现一个挺普遍的问题:
+
+* 我技术很一般
+* 对技术也没有什么追求
+* 要是对什么岗位感兴趣我也不知道
+* 以后自己喜欢干什么也不太清楚
+
+**相信说到了不少录友心里去了, 其实目前应试教育下 确实很难找到自己感兴趣的事情**。
+
+但我想说的是,并不是技术不好就什么都做不了,依然有很多选择,只不过录友们没有接触过,所以就不知道自己接下来要往哪个方向走。
+
+这里我给出一些路线,大家可以参考:
+
+方向一:走纯研发路线,去大厂,如果真的对技术提不上兴趣可能会走的很辛苦。
+
+方向二:如果技术还凑合,不做纯研发,退一步也可以考虑去大厂做测试相关的工作,对技术要求没有那么高,但也需要技术能力,而且技术能力越强越吃香。
+
+方向三:去银行,证券,国企类的企业去做研发岗位,这种国有企业的技术面试相对简单不少,比较轻松,还很稳定,收入虽说不高,但生活足够滋润。
+
+方向四:做toC(面向普通用户)的产品经理,toC产品经理这个岗位就特别卷,因为这个岗位门槛太低了,任何专业的同学都可以去做产品经理。 这样自己所学的技术就基本没有用了,也凸显不出技术上的优势,但如果自己真的对APP之类的各种应用得心应手,优点缺点,用户爽点、日活、次留等等手到擒来,倒可以试一试。
+
+方向五:做toB的产品经理,包括云计算,大数据这些产品都是需要产品经理的,例如百度云,腾讯云,阿里云等等,这种产品本身就是技术产品,所以需要懂技术的产品经理来做设计,即需要产品的抓住需求的能力,也需要懂点技术,既可以发挥自己的技术能力,还可以做产品规划,基本也不用写代码。
+
+对技术要求不高的岗位也挺多的,发展也很好,只要大家多去了解,总会找打符合自己的岗位。
+
diff --git a/problems/知识星球精选/提前批已经开始了.md b/problems/知识星球精选/提前批已经开始了.md
new file mode 100644
index 00000000..ba05b5a9
--- /dev/null
+++ b/problems/知识星球精选/提前批已经开始了.md
@@ -0,0 +1,48 @@
+
+
+
+
+
+# 不知不觉华为提前提已经开始了
+
+最近华为提前批已经开始了,不少同学已经陆续参加了提前批的面试。
+
+在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)上就有录友问我这么个问题:
+
+
+
+华为是比较看重学历的,如果学校比较好(985以上),华为的面试会随意一些,几乎就是走一个流程。
+
+我记得当初校招的时候,华为来我们学校就是 几个大巴把整个计算机学院的同学拉到酒店,批量面试,面试过程差不多就是走个形式,大家基本都拿到offer了。
+
+不是说 非985/211的同学 技术就不好,而是当企业每年要招上万人的时候,通过学历还筛选相对来说是 效率较高且成本最低的一种方式。
+
+不过现在竞争越来越激烈了,华为也很少这种粗暴方式来召人。再说华为给出的薪酬相对互联网大厂也 很有竞争力,发展前景也很不错。
+
+那么在说一说面试的内容。
+
+可能有的同学感觉,我为了面试,准备了这么多,结果面试都没问,就问问项目问问编程语言就完事了。
+
+这其实很正常!
+
+不同的公司,同一个公司不同部门,同一个部门不同面试官 面试风格都不太一样。
+
+可能部门就比较缺人,面试官看一下 简历 学校可以,技术看上去还凑合,项目也还行,那么面试可能就放水一点,然后就过了。
+
+毕竟面试官也很忙,在大厂谁都不想当面试官,都是工作之余加班的工作量,**所以面试官也想快点结束**。
+
+还有另一种可能,就是部门已经招满了,但依然安排了面试,那么面试官就随便问问,然后也不会招人了。
+
+还有一种,就是有的面试官就习惯考察算法题,问完算法了,其他的他也就不会问了。
+
+因为操作系统,网络,数据库这些面试官在面试之前也要突击准备的,工作那么多年,都忘的差不多了,**所以要复习一波,这也是工作(都是加班啊!)**。
+
+甚至可能面试官前天刚要和女朋友过生日,然后就没准备计算机基础方面的内容,索性面试的时候也就不问了.....
+
+这都是有可能的,很多同学可能没做过面试官,所以对一些面试过程就容易想不通,其实面试官也是普普通通的打工仔,他也不想加班,也想只要人品端正,积极肯干,随便召个技术差不多的就算了,哈哈哈。
+
+所以说,面试有的时候也很看缘分的,大家辛辛苦苦造火箭,结果面试都没问是很正常的。
+
+大家也放平心态,把该做的做好,剩下的交个天意了。
+
+
diff --git a/problems/知识星球精选/秋招下半场依然没offer.md b/problems/知识星球精选/秋招下半场依然没offer.md
new file mode 100644
index 00000000..829f82ba
--- /dev/null
+++ b/problems/知识星球精选/秋招下半场依然没offer.md
@@ -0,0 +1,99 @@
+
+
+
+
+
+# 秋招下半场依然没offer,怎么办?
+
+[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里一些录友拿到了满意的offer,也有一些录友,依然没有offer,每天的状态已经不能用焦虑来形容了。
+
+在星球里就有录友向我提问了这样一个问题:
+
+
+
+估计还有公众号上还有很多录友也是这种情况,马上面试,但总感觉哪里都还没准备好,然后还必须要面试,每次面试结果还不理想。
+
+能感受到此时大家非常迫切要知道还有没有什么切实可行的方案 ,只要执行 ,就能拿到offer。
+
+恨不得提前知道面试官的问题,然后把问题都背下来得了。。。。
+
+其实我是非常理解大家的心情的,这个时候怪自己准备的太晚也没有用。
+
+说实话,已经是秋招下半场(接近末尾了),**已经没有针对面试的复习方案了。什么学习路线,突击计划 在这个时候 都没啥作用了**。
+
+现在什么最重要呢?
+
+是**心态**。
+
+心态要稳住,**放低预期,但别放低努力的程度**。
+
+估计参加过面试的同学,都会有这种感觉,面试前一天复习,突击的内容,**第二天面试都不会考!是的,一道都不会考!**
+
+那么为什么还学呢?
+
+就是这股劲不能泄,只要憋住劲,每天面试,复盘,学习,面试再复盘,再学习,最终大家其实都能拿到offer的,只不过是offer的满意程度罢了。
+
+**如果泄了劲,那就真没戏了**。
+
+**可能自暴自弃两天,然后就发现自己啥也学不进去了**。
+
+所以这个时候了,算法题还要一直刷,八股文也要背起来。
+
+讲真,现在刷的题,看的八股文,面试可能也不一定会考,但为什么还要看呢,**就是稳定心态**。
+
+**剩下的就看缘分了!**
+
+面试挺看缘分的, 可能一个面试官对你的评价就是基础不牢,下一家公司面试官对你的评价就是 基础不错,但项目经验不足。
+
+所以此时你自己都蒙了,究竟自己是 基础不牢,还是项目经验不足呢?
+
+其实面试的本质,面试官主观性还是比较强的,可能就是问的几个问题 你都背过,而且背的很深入,那评价就是基础牢了呗。
+
+
+## 在学点技术,冲春招?
+
+[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里还有一位录友,也是类似的情况,秋招感觉很艰难,要不要在学一学微服务分布式之类的,再冲春招。
+
+
+
+其实这个时候,大家也不要再心思学点什么新技术,增加自己的筹码了。
+
+还是那句话,**现在学啥,面试也未必会考!**
+
+就算 现在不参加秋招了,去学 微服务了,分布式。面试的时候 ,面试官未必会考不说,而且 你也未必学的透彻。
+
+再说,春招的岗位 很少,而且优质岗位更少。
+
+所以大家这个时候,就不要等了。
+
+**直接海投,面试,复盘总结,再面试**。
+
+
+## 给参加明年秋招录友的劝告
+
+其实我在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,**看到了太多太多 参加今年秋招的录友 埋怨自己 准备的太晚了,没想到要看的东西这么多,没想到竞争这么激烈**。
+
+所以明年参加秋招的录友,要提前就开始准备,明确自己的岗位,知道岗位的要求,制定自己的计划,然后按计划执行。
+
+**其实多早开始准备,都不算早!**
+
+很多在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里的准大三,研一的录友,都能在星球里感受到 秋招的竞争与激烈。
+
+所以他们也就早早的开始准备了。
+
+来看看星球里 这位录友的提问,**他也才刚刚准大三,就已经为明年的秋招开始精心准备了**。
+
+
+
+估计大多数准大三或者准研一的同学都还没有这种意识。
+
+**但在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,通过每天录友们的打卡,每天都能感受到这种紧迫感**。
+
+正如一位星球里的录友这么说:
+
+
+
+很多录友加入星球之后,**刷贴吧刷剧已经不香了,只有刷星球!**
+
+感觉每天自己被push上去,其实有时候 **大家需要的就是一个氛围,自己一个人很难有提前准备的意识,也很难坚持下来**。
+
diff --git a/problems/知识星球精选/秋招的上半场.md b/problems/知识星球精选/秋招的上半场.md
new file mode 100644
index 00000000..f404e611
--- /dev/null
+++ b/problems/知识星球精选/秋招的上半场.md
@@ -0,0 +1,55 @@
+
+
+
+
+
+# 秋招上半场的总结
+
+八月份已经接近尾声,不少录友已经在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) 已经总结了秋招的上半场。
+
+
+
+可以看出 这位录友也是身经百战,目前也拿到了几个offer。
+
+星球里还有不少录友已经拿到了字节,阿里,百度提前批的offer。
+
+
+
+不过绝大部分录友还在经受着考验,处于焦虑的状态,秋招上半场也是几多欢喜几多愁。
+
+找工作其实是一个很虐心的过程,没有offer、没准备好、面试没发挥好、差一点就能把这题过了 等等,每一个场景都给大家增添一份焦虑。
+
+星球里有一些录友就找到了一个方向,或者一个准备同一家公司的伙伴,就会好一些。
+
+
+
+**有时候,有压力,自己憋着,没人交流,只会压力越来越大**。
+
+对于还没有offer的录友,我对大家的建议也是,**心态要稳住,要适当放低预期,不是所有人都要大厂,但不能放低自己对学习的要求!**
+
+有些同学,经过几次面试的打击之后,直接就自暴自弃,狂玩几天想释放压力,这么做的结果,只会压力越来越大。
+
+所以,**秋招进行时,大家不要过于放松,无论什么情况,只要没有拿到心仪offer,就不能放松,一旦放松之后,换来的就是更看不到希望**。
+
+有的同学可能学校不好,有的同学是转行计算机,一路下来确实艰难。
+
+我在星球里,看到的不仅是大家准备秋招过程的每日学习总结、打卡,也看到了一些录友的不容易。
+
+
+
+说实话,看着评论区,还是很感动的,估计这位 打卡的录友,也得到了很大的鼓励。
+
+这可能是 成千上万 目前正在冲刺秋招应届生 的一个缩影。
+
+面试不仅仅是只看技术,也挺看缘分的,有的同学可能感觉莫名其妙的就挂了,有的同学可能感觉莫名其妙的就拿到了offer。
+
+我就简单列举几个原因。
+
+* 可能部门缺人,或满了
+* 可能是当天面试的同学都不太行,就矬子里拔大个
+* 可能之前有几个优秀的毕业生,但按照之前的标准都没过,然后面试官发现这么下去招不到人了,一下子就把标准降低了,然后轮到了你,你感觉你发挥的并不好,但也给你offer了。
+
+所以面试也有很多很多其他因素,也很看缘分。
+
+大家放平心态就好。
+
diff --git a/problems/知识星球精选/秋招进行中的迷茫与焦虑.md b/problems/知识星球精选/秋招进行中的迷茫与焦虑.md
new file mode 100644
index 00000000..6083c7b1
--- /dev/null
+++ b/problems/知识星球精选/秋招进行中的迷茫与焦虑.md
@@ -0,0 +1,55 @@
+
+
+
+
+
+# 秋招进行时,其实大家都很焦虑
+
+大家应该都发现了,提前批和秋招都越来越提前的,大部分的录友此时的心态还是挺焦虑的。
+
+特别是大三的同学吧,同时面临这找工作和考研两个方向的诱惑。
+
+一位录友就在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)问了我这个问题:
+
+
+
+其实 互联网35岁中年危机,应该吓退了不少小鲜肉,互联网35岁中年危机是真的,网上哪些关于裁员的消息 大多数也都是真的。
+
+但我想说:特别是正在找工作的同学,大家应该都是 95后,00后,可以先不用想那么长远,能把未来五年规划好,就不错不错的了,现在行业的变化,都是两三年一变天,等大家35岁的时候,都不一定啥样了。
+
+而且卡哥还替大家先趟趟路,哈哈哈
+
+现在的大三,最忙的就是两伙人,**考研的和找工作的,这两条路都挺难的**。
+
+另外还有不少录友应该在考研与找工作之间摇摆。可能有的学校大多数都在找工作,有的学校大多数都在考研,不过应该考研占绝大多数。
+
+关于考研我的观点是,如果 本科毕业能拿到大厂或者中厂offer,可以不考研,看看自己比较优秀的学长学姐,毕业都去哪了,是否去了大厂,如果比较优秀的学长学姐 就业也一般,我还是推荐读研的,因为顺便也提升一下学历。
+
+但有的同学是从大一入学就规划了自己 以后直接工作的,这种无论学校如何,我都是比较支持的!
+
+**因为从大一就明确自己的方向,按找工作的要求来学习,一般来说 最后找工作都不会差**。
+
+最危险的是,大一大二没计划,到了大三开始摇摆,考研还是找工作。这种是最危险的,如果在纠结一阵纠结到 现在的话,那基本哪条路都走不好了。
+
+对于现在找工作的录友,可能发现身边的同学都在考研,而且在准备找工作中,可能还没有明确的学习计划,东打一发西扯一下,就会很焦虑,主要也是身边一起工作的同学可能不多,交流的少。
+
+找工作是一个累心的过程,非常累,恨不得哪家公司赶紧给我个offer,让我干啥都行,甚至怀疑自己是不是要再去考研。
+
+**其实这时候,不是自己方法不对,也不是自己选择的路错了,而是需要一个过来人,给你打打气**。
+
+静下心来,把最近开始面试的公司排一下,把自己还要学习的内容做好计划,都列出来,按着一步一步去执行,心里会踏实的多。
+
+再说考研,考研也一点都不轻松,进大厂卷,**现在计算机考研比进大厂还卷(特别是名校计算机)**,如果考研没考上,找工作还挺难的,毕竟考研那套知识,对于找工作来说几乎没啥用。
+
+所以不论是找工作,还是考研,大家都是一样的焦虑,每条路都不好走,但自己一旦选择了,就坚持下来,走好自己的路。
+
+话再说回来,**现在不论身在什么阶段,都离不开“卷”,就算最后进了大厂工作,依然也是卷**。
+
+大家都感觉自己准备面试好辛苦,好心累。其实给你面试的面试官,可能自己手上的模块线上又出问题了,还要担心年底是不是要背锅了,是不是年终没了,晋升不了了,是不是要准备跳槽了,甚至应届生的工资比自己的还高 等等。
+
+**所以面试官也许比你还心累!**
+
+是不是想到这里,心里就舒服点了,哈哈哈哈,其实是有时候自己很迷茫但没有人沟通,就会陷入一个死循环,只要和过来人聊一聊,没啥大不了的。
+
+大家其实这这样。
+
diff --git a/problems/知识星球精选/英语到底重不重要.md b/problems/知识星球精选/英语到底重不重要.md
new file mode 100644
index 00000000..32e6a39b
--- /dev/null
+++ b/problems/知识星球精选/英语到底重不重要.md
@@ -0,0 +1,58 @@
+
+
+
+
+
+# 对程序员来说,英语到底重不重要
+
+在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)有一位录友问了我这么一个问题。
+
+
+
+这个问题我曾经在上学的时候也思考过。
+
+这次正好来好好说一说。
+
+当时我搞ACM的时候都是英文题目的,哪会有中文题目,现在力扣全面汉化也是最近几年的事情。
+
+如今又工作了这么多年后重新看待这个问题,又有更全面的看法了。
+
+其实我想说,**对英语对程序员即重要,也不重要!** 这是要看环境,看背景的。
+
+如果你现在在准备秋招,或者是跳槽,目标是冲刺国内大厂,那么暂时不用花费精力学英语,就算四级都没过,大厂面试官也不会问你过没过四六级的。
+
+貌似华为对英语四六级是有要求的,如果面试BAT,英语不是关键性问题。
+
+但工作之后,英语其实就很重要了,也要注意程序员英语和普通英语是不一样的。
+
+一手的技术资料,和优秀的问答 基本都是英文的,国内的资料都是别人嚼过的,质量参差不齐。
+
+而且国内的问答社区其实环境并不好(懂的人都懂),真正解决问题,还得靠Stack Overflow。
+
+**所以技术文档(英文),Stack Overflow , Quora才是程序员的利器**。
+
+工作以后如果你把程序员英语(注意不是普通英语)练好,其实对技能和视野的提升是很有帮助的。
+
+这里为什么强调是程序员英语呢, 因为有太多专业词是CS特有的,而不是日常英语。
+
+**继承,多态,变量,英文怎么说? 估计可以难住不少人了**。
+
+所以当我们查问题的时候,第一反应 一定是用 中文关键词去搜索,因为我们不知道对应的英文关键词(也懒的去查一下)。
+
+所以英语好,这是一种技术壁垒,可以任意遨游在中文和英文的世界里,有两极的思考!
+
+**那么对程序员来说,英语口语重要么?**
+
+如果你直接想去外企的话,练一练吧,也是挺重要的,如果在国内的话,用处不太大。
+
+那一定有人说了:练好口语 一定是有利的。
+
+这个我也 赞同,练啥其实都有有利的,但我们要看**投入产出比**
+
+我在学校的时候英语口语还是挺不错的,当时学校的留学生我基本都认识,和他们扯扯皮没问题,可工作这些年之后,全!都!还!回!去!了!
+
+所以如果练习口语,一定要有目标,要么就是雅思托付考试要出国,要么就一定要去外企找机会transfer美帝,这样有一个环境可以一直保持下去。
+
+否则,花费大量时间练习,其实仅仅是感动自己,过不了多久,就都还回去。(例如说我,哈哈哈哈)
+
+
diff --git a/problems/知识星球精选/要不要考研.md b/problems/知识星球精选/要不要考研.md
new file mode 100644
index 00000000..a5f2dfa0
--- /dev/null
+++ b/problems/知识星球精选/要不要考研.md
@@ -0,0 +1,45 @@
+
+
+
+
+
+# 到底要不要读研
+
+在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里讨论了一下关于要不要读研的问题。
+
+
+
+其实不少同学纠结于计算机专业要不要考研。
+
+我的观点是 **如果认为自己本科毕业 可以拿到大厂的研发岗offer,那么就不用读研**(除非保研到一个特别好的学校了)。
+
+那么怎么能发现自己本科毕业能不能拿到大厂offer呢。
+
+看看自己学生学哥学姐,大部分人的就业去向,如果很多都可以进入大厂,那么就可以追寻他们的足迹。
+
+如果自己学校本科毕业,就业比较一般,那么就去读研吧。
+
+当然这里还有一个情况,就是算法岗,算法岗一般要求研究生学历以上。但算法岗现在很卷,985高校研究生,找算法岗的工作都很难,既要顶会,也要coding的能力。
+
+目前的现况是很多搞AI的985研究生都在转研发岗,**但如果你依然立志从事人工智能(说明是真的热爱),那么就去读研吧**。
+
+研究生毕业去做研发岗和本科毕业做的研发,其工作其实没区别。那么为什么读研呢。
+
+现在环境就比较卷,两个候选人,实力差不多,一个本科,一个研究生,价钱也差不多,那么大厂就要个研究生呗,在招生简章里看着也好看,说明公司都是高学历人才。
+
+当然一般来说 研究生毕竟又多读了 两三年,普遍会比本科强一些。
+
+**PS:大厂研发岗校招本科和研究生薪资几乎没啥差别**。
+
+
+那么话在说回来,**如果打算考研,那么就争取更好学校的研究生,提升一下学历,把考研所付出的努力最大化**。
+
+最后关于本科生要不要读研:
+
+* 本科如果有实力去大厂做研发,那么就去!
+
+* 如果本科去不了大厂,可以考虑考研,考研是一次提升学历的机会,一旦选择考研这条路,就给自己点压力!
+
+* 如果知道AI岗位目前就业情况,依然要立志从事AI,那么就去读研
+
+
diff --git a/problems/知识星球精选/面试中发散性问题.md b/problems/知识星球精选/面试中发散性问题.md
new file mode 100644
index 00000000..7fb9150f
--- /dev/null
+++ b/problems/知识星球精选/面试中发散性问题.md
@@ -0,0 +1,45 @@
+
+
+
+
+
+# 面试中遇到发散性问题,应该怎么办?
+
+这周在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)有一位录友问了我这么一个问题,我感觉挺有代表性的,应该不少录友在面试中不论是社招还是校招都会遇到这一类的问题。
+
+问题如下:
+
+
+
+首先面试官评价:基本的知识都懂还可以,但是知识碎片化。
+
+因为现在基本大家都是背面经,所以对考点知识点掌握的都不错,确实对整体上理解不够。
+
+但如果想对整体上理解深入,需要花费很大精力的,而且要把书看的很透彻,那这种候选人基本都是拔尖的。
+
+关于操作系统啊,数据库等等,大多数录友应该都是靠面经,其实背面经也是最高效,性价比最高的方式的,如果真的去把书看透,一本《深入理解计算机系统》够看一年了。。。。
+
+所以面试官基本不会因为这个问题而把你pass掉,那位提问的录友也是顺利进入了三面。
+
+那么面试中遇到这种发散性问题应该怎么办呢?
+
+其实他问的这种问题,**就没指望你能说出 正确的答案,这是没有标准答案的**,例如淘宝京东的那种登录的场景 没有经历过 是不知道究竟怎么回事的。
+
+而问你对操作系统的理解,也是千人千面没有标准的。
+
+遇到这种问题,你就结合自己的想法,大胆说,不要说这个我不知道,那个我没遇到过之类的。
+
+你说的解决方案,一定是有问题的,面试官在质疑你的时候,**你要表现出很兴奋,积极和面试官讨论:为什么不对**,然后说出你的观点,结合你所知道的理论知识。
+
+大胆说,不要怕,一般情况你的理论知识都比面试官强,面试官工作好多年了基础知识早忘了,基本都是面试你前一天突击一个小时(暴漏真相了哈哈哈)
+
+**忌讳:面试官一质疑你,你就开始怀疑自己,心想那我说的不对吧,然后就不说话了。。。**
+
+最后这种发散性的问题,也没法去专门准备,因为这种问题主要是**考察候选人对技术的态度和沟通能力!**
+
+所以大家如果在面试遇到这一类发散性问题,一定要积极沟通,**表现出你对技术的追求和优秀的沟通能力**。
+
+**注意 和面试官讨论的时候要面带微笑,不要板着脸,面试官也会喜欢以后和这样的人做同事!**
+
+好咯,心法已经传授给大家了。
+
diff --git a/problems/算法模板.md b/problems/算法模板.md
index b56678ab..9fa4f1a8 100644
--- a/problems/算法模板.md
+++ b/problems/算法模板.md
@@ -8,7 +8,7 @@
## 二分查找法
-```
+```CPP
class Solution {
public:
int searchInsert(vector& nums, int target) {
@@ -33,7 +33,7 @@ public:
## KMP
-```
+```CPP
void kmp(int* next, const string& s){
next[0] = -1;
int j = -1;
@@ -53,7 +53,7 @@ void kmp(int* next, const string& s){
二叉树的定义:
-```
+```CPP
struct TreeNode {
int val;
TreeNode *left;
@@ -65,7 +65,7 @@ struct TreeNode {
### 深度优先遍历(递归)
前序遍历(中左右)
-```
+```CPP
void traversal(TreeNode* cur, vector& vec) {
if (cur == NULL) return;
vec.push_back(cur->val); // 中 ,同时也是处理节点逻辑的地方
@@ -74,7 +74,7 @@ void traversal(TreeNode* cur, vector& vec) {
}
```
中序遍历(左中右)
-```
+```CPP
void traversal(TreeNode* cur, vector& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
@@ -83,7 +83,7 @@ void traversal(TreeNode* cur, vector& vec) {
}
```
后序遍历(左右中)
-```
+```CPP
void traversal(TreeNode* cur, vector& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
@@ -97,7 +97,7 @@ void traversal(TreeNode* cur, vector& vec) {
相关题解:[0094.二叉树的中序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0094.二叉树的中序遍历.md)
前序遍历(中左右)
-```
+```CPP
vector preorderTraversal(TreeNode* root) {
vector result;
stack st;
@@ -123,7 +123,7 @@ vector preorderTraversal(TreeNode* root) {
```
中序遍历(左中右)
-```
+```CPP
vector inorderTraversal(TreeNode* root) {
vector result; // 存放中序遍历的元素
stack st;
@@ -148,7 +148,7 @@ vector inorderTraversal(TreeNode* root) {
```
后序遍历(左右中)
-```
+```CPP
vector postorderTraversal(TreeNode* root) {
vector result;
stack st;
@@ -176,7 +176,7 @@ vector postorderTraversal(TreeNode* root) {
相关题解:[0102.二叉树的层序遍历](https://programmercarl.com/0102.二叉树的层序遍历.html)
-```
+```CPP
vector> levelOrder(TreeNode* root) {
queue que;
if (root != NULL) que.push(root);
@@ -212,7 +212,7 @@ vector> levelOrder(TreeNode* root) {
### 二叉树深度
-```
+```CPP
int getDepth(TreeNode* node) {
if (node == NULL) return 0;
return 1 + max(getDepth(node->left), getDepth(node->right));
@@ -221,7 +221,7 @@ int getDepth(TreeNode* node) {
### 二叉树节点数量
-```
+```CPP
int countNodes(TreeNode* root) {
if (root == NULL) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
@@ -229,7 +229,7 @@ int countNodes(TreeNode* root) {
```
## 回溯算法
-```
+```CPP
void backtracking(参数) {
if (终止条件) {
存放结果;
@@ -247,8 +247,8 @@ void backtracking(参数) {
## 并查集
-```
- int n = 1005; // 更具题意而定
+```CPP
+ int n = 1005; // 根据题意而定
int father[1005];
// 并查集初始化
@@ -280,6 +280,263 @@ void backtracking(参数) {
(持续补充ing)
## 其他语言版本
+JavaScript:
+
+## 二分查找法
+
+使用左闭右闭区间
+
+```javascript
+var search = function (nums, target) {
+ let left = 0, right = nums.length - 1;
+ // 使用左闭右闭区间
+ while (left <= right) {
+ let mid = left + Math.floor((right - left)/2);
+ if (nums[mid] > target) {
+ right = mid - 1; // 去左面闭区间寻找
+ } else if (nums[mid] < target) {
+ left = mid + 1; // 去右面闭区间寻找
+ } else {
+ return mid;
+ }
+ }
+ return -1;
+};
+```
+
+使用左闭右开区间
+
+```javascript
+var search = function (nums, target) {
+ let left = 0, right = nums.length;
+ // 使用左闭右开区间 [left, right)
+ while (left < right) {
+ let mid = left + Math.floor((right - left)/2);
+ if (nums[mid] > target) {
+ right = mid; // 去左面闭区间寻找
+ } else if (nums[mid] < target) {
+ left = mid + 1; // 去右面闭区间寻找
+ } else {
+ return mid;
+ }
+ }
+ return -1;
+};
+```
+
+## KMP
+
+```javascript
+var kmp = function (next, s) {
+ next[0] = -1;
+ let j = -1;
+ for(let i = 1; i < s.length; i++){
+ while (j >= 0 && s[i] !== s[j + 1]) {
+ j = next[j];
+ }
+ if (s[i] === s[j + 1]) {
+ j++;
+ }
+ next[i] = j;
+ }
+}
+```
+
+## 二叉树
+
+### 深度优先遍历(递归)
+
+二叉树节点定义:
+
+```javascript
+function TreeNode (val, left, right) {
+ this.val = (val === undefined ? 0 : val);
+ this.left = (left === undefined ? null : left);
+ this.right = (right === undefined ? null : right);
+}
+```
+
+前序遍历(中左右):
+
+```javascript
+var preorder = function (root, list) {
+ if (root === null) return;
+ list.push(root.val); // 中
+ preorder(root.left, list); // 左
+ preorder(root.right, list); // 右
+}
+```
+
+中序遍历(左中右):
+
+```javascript
+var inorder = function (root, list) {
+ if (root === null) return;
+ inorder(root.left, list); // 左
+ list.push(root.val); // 中
+ inorder(root.right, list); // 右
+}
+```
+
+后序遍历(左右中):
+
+```javascript
+var postorder = function (root, list) {
+ if (root === null) return;
+ postorder(root.left, list); // 左
+ postorder(root.right, list); // 右
+ list.push(root.val); // 中
+}
+```
+
+### 深度优先遍历(迭代)
+
+前序遍历(中左右):
+
+```javascript
+var preorderTraversal = function (root) {
+ let res = [];
+ if (root === null) return rs;
+ let stack = [root],
+ cur = null;
+ while (stack.length) {
+ cur = stack.pop();
+ res.push(cur.val);
+ cur.right && stack.push(cur.right);
+ cur.left && stack.push(cur.left);
+ }
+ return res;
+};
+```
+
+中序遍历(左中右):
+
+```javascript
+var inorderTraversal = function (root) {
+ let res = [];
+ if (root === null) return res;
+ let stack = [];
+ let cur = root;
+ while (stack.length !== 0 || cur !== null) {
+ if (cur !== null) {
+ stack.push(cur);
+ cur = cur.left;
+ } else {
+ cur = stack.pop();
+ res.push(cur.val);
+ cur = cur.right;
+ }
+ }
+ return res;
+};
+```
+
+后序遍历(左右中):
+
+```javascript
+var postorderTraversal = function (root) {
+ let res = [];
+ if (root === null) return res;
+ let stack = [root];
+ let cur = null;
+ while (stack.length) {
+ cur = stack.pop();
+ res.push(cur.val);
+ cur.left && stack.push(cur.left);
+ cur.right && stack.push(cur.right);
+ }
+ return res.reverse()
+};
+```
+
+### 广度优先遍历(队列)
+
+```javascript
+var levelOrder = function (root) {
+ let res = [];
+ if (root === null) return res;
+ let queue = [root];
+ while (queue.length) {
+ let n = queue.length;
+ let temp = [];
+ for (let i = 0; i < n; i++) {
+ let node = queue.shift();
+ temp.push(node.val);
+ node.left &&queue.push(node.left);
+ node.right && queue.push(node.right);
+ }
+ res.push(temp);
+ }
+ return res;
+};
+```
+
+### 二叉树深度
+
+```javascript
+var getDepth = function (node) {
+ if (node === null) return 0;
+ return 1 + Math.max(getDepth(node.left), getDepth(node.right));
+}
+```
+
+### 二叉树节点数量
+
+```javascript
+var countNodes = function (root) {
+ if (root === null) return 0;
+ return 1 + countNodes(root.left) + countNodes(root.right);
+}
+```
+
+## 回溯算法
+
+```javascript
+function backtracking(参数) {
+ if (终止条件) {
+ 存放结果;
+ return;
+ }
+
+ for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
+ 处理节点;
+ backtracking(路径,选择列表); // 递归
+ 回溯,撤销处理结果
+ }
+}
+
+```
+
+## 并查集
+
+```javascript
+ let n = 1005; // 根据题意而定
+ let father = new Array(n).fill(0);
+
+ // 并查集初始化
+ function init () {
+ for (int i = 0; i < n; ++i) {
+ father[i] = i;
+ }
+ }
+ // 并查集里寻根的过程
+ function find (u) {
+ return u === father[u] ? u : father[u] = find(father[u]);
+ }
+ // 将v->u 这条边加入并查集
+ function join(u, v) {
+ u = find(u);
+ v = find(v);
+ if (u === v) return ;
+ father[v] = u;
+ }
+ // 判断 u 和 v是否找到同一个根
+ function same(u, v) {
+ u = find(u);
+ v = find(v);
+ return u === v;
+ }
+```
Java:
@@ -296,4 +553,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/背包总结篇.md b/problems/背包总结篇.md
index f3732c8d..784f8441 100644
--- a/problems/背包总结篇.md
+++ b/problems/背包总结篇.md
@@ -101,4 +101,4 @@
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md
index 2bcded70..a10f92a1 100644
--- a/problems/背包理论基础01背包-1.md
+++ b/problems/背包理论基础01背包-1.md
@@ -34,7 +34,7 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
## 01 背包
-有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品只能用一次**,求解将哪些物品装入背包里物品价值总和最大。
+有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品只能用一次**,求解将哪些物品装入背包里物品价值总和最大。

@@ -123,7 +123,7 @@ for (int j = weight[0]; j <= bagWeight; j++) {
dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化多少呢?
-其实从递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出dp[i][j] 是又左上方数值推导出来了,那么 其他下标初始为什么数值都可以,因为都会被覆盖。
+其实从递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出dp[i][j] 是由左上方数值推导出来了,那么 其他下标初始为什么数值都可以,因为都会被覆盖。
**初始-1,初始-2,初始100,都可以!**
@@ -315,7 +315,6 @@ Python:
def test_2_wei_bag_problem1(bag_size, weight, value) -> int:
rows, cols = len(weight), bag_size + 1
dp = [[0 for _ in range(cols)] for _ in range(rows)]
- res = 0
# 初始化dp数组.
for i in range(rows):
@@ -334,8 +333,6 @@ def test_2_wei_bag_problem1(bag_size, weight, value) -> int:
else:
# 定义dp数组: dp[i][j] 前i个物品里,放进容量为j的背包,价值总和最大是多少。
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur_weight]+ cur_val)
- if dp[i][j] > res:
- res = dp[i][j]
print(dp)
@@ -434,4 +431,4 @@ test();
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md
index 07f74186..9bdbe8bc 100644
--- a/problems/背包理论基础01背包-2.md
+++ b/problems/背包理论基础01背包-2.md
@@ -323,4 +323,4 @@ test();
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/背包问题理论基础多重背包.md b/problems/背包问题理论基础多重背包.md
index f1dfed26..e5eb222d 100644
--- a/problems/背包问题理论基础多重背包.md
+++ b/problems/背包问题理论基础多重背包.md
@@ -5,6 +5,8 @@
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
+
# 动态规划:关于多重背包,你该了解这些!
之前我们已经体统的讲解了01背包和完全背包,如果没有看过的录友,建议先把如下三篇文章仔细阅读一波。
@@ -207,4 +209,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/背包问题理论基础完全背包.md b/problems/背包问题理论基础完全背包.md
index 455a3c33..3420f822 100644
--- a/problems/背包问题理论基础完全背包.md
+++ b/problems/背包问题理论基础完全背包.md
@@ -349,4 +349,4 @@ function test_completePack2() {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/贪心算法总结篇.md b/problems/贪心算法总结篇.md
index 94e292ba..09d22da1 100644
--- a/problems/贪心算法总结篇.md
+++ b/problems/贪心算法总结篇.md
@@ -158,4 +158,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/贪心算法理论基础.md b/problems/贪心算法理论基础.md
index fdb7abf3..f851d66b 100644
--- a/problems/贪心算法理论基础.md
+++ b/problems/贪心算法理论基础.md
@@ -9,8 +9,10 @@
# 关于贪心算法,你该了解这些!
-> 正式开始新的系列了,贪心算法!
-通知:一些录友表示经常看不到每天的文章,现在公众号已经不按照发送时间推荐了,而是根据一些规则乱序推送,所以可能关注了「代码随想录」也一直看不到文章,建议把「代码随想录」设置星标哈,设置星标之后,每天就按发文时间推送了,我每天都是定时8:35发送的,嗷嗷准时,哈哈。
+题目分类大纲如下:
+
+
+
## 什么是贪心
@@ -88,24 +90,8 @@
-
-
-## 其他语言版本
-
-
-Java:
-
-
-Python:
-
-
-Go:
-
-
-
-
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/链表总结篇.md b/problems/链表总结篇.md
index fed8cb60..c0bc197b 100644
--- a/problems/链表总结篇.md
+++ b/problems/链表总结篇.md
@@ -98,4 +98,4 @@
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/链表理论基础.md b/problems/链表理论基础.md
index d210b6bd..0eb61add 100644
--- a/problems/链表理论基础.md
+++ b/problems/链表理论基础.md
@@ -9,9 +9,9 @@
# 关于链表,你该了解这些!
-什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点是又两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
+什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
-链接的入口点称为列表的头结点也就是head。
+链接的入口节点称为链表的头结点也就是head。
如图所示:

@@ -159,4 +159,4 @@ Go:
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+
diff --git a/problems/面试题02.07.链表相交.md b/problems/面试题02.07.链表相交.md
index d59256c6..f283b161 100644
--- a/problems/面试题02.07.链表相交.md
+++ b/problems/面试题02.07.链表相交.md
@@ -258,4 +258,4 @@ var getIntersectionNode = function(headA, headB) {
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
-
+