=start){
- res.append('9');
- }else res.append(chars[i]);
+ for (int i = start; i < s.length(); i++) {
+ chars[i] = '9';
}
- return Integer.parseInt(res.toString());
+ return Integer.parseInt(String.valueOf(chars));
}
}
```
diff --git a/problems/0739.每日温度.md b/problems/0739.每日温度.md
index d7489028..710f5eb6 100644
--- a/problems/0739.每日温度.md
+++ b/problems/0739.每日温度.md
@@ -18,7 +18,7 @@
## 思路
-首先想到的当然是暴力解法,两层for循环,把至少需要等待的天数就搜出来了。时间复杂度是$O(n^2)$
+首先想到的当然是暴力解法,两层for循环,把至少需要等待的天数就搜出来了。时间复杂度是O(n^2)
那么接下来在来看看使用单调栈的解法。
@@ -26,13 +26,13 @@
**通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了**。
-时间复杂度为$O(n)$。
+时间复杂度为O(n)。
例如本题其实就是找找到一个元素右边第一个比自己大的元素。
此时就应该想到用单调栈了。
-那么单调栈的原理是什么呢?为什么时间复杂度是$O(n)$就可以找到每一个元素的右边第一个比它大的元素位置呢?
+那么单调栈的原理是什么呢?为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢?
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素的元素,优点是只需要遍历一次。
@@ -164,8 +164,8 @@ public:
}
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
精简的代码是直接把情况一二三都合并到了一起,其实这种代码精简是精简,但思路不是很清晰。
@@ -177,34 +177,60 @@ public:
Java:
```java
-/**
- * 单调栈,栈内顺序要么从大到小 要么从小到大,本题从大到小
- *
- * 入站元素要和当前栈内栈首元素进行比较
- * 若大于栈首则 则与元素下标做差
- * 若大于等于则放入
- *
- * @param temperatures
- * @return
- */
- public static int[] dailyTemperatures(int[] temperatures) {
- Stack stack = new Stack<>();
- int[] res = new int[temperatures.length];
- for (int i = 0; i < temperatures.length; i++) {
- /**
- * 取出下标进行元素值的比较
- */
- while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
- int preIndex = stack.pop();
- res[preIndex] = i - preIndex;
+
+class Solution {
+ // 版本 1
+ public int[] dailyTemperatures(int[] temperatures) {
+
+ int lens=temperatures.length;
+ int []res=new int[lens];
+
+ /**
+ 如果当前遍历的元素 大于栈顶元素,表示 栈顶元素的 右边的最大的元素就是 当前遍历的元素,
+ 所以弹出 栈顶元素,并记录
+ 如果栈不空的话,还要考虑新的栈顶与当前元素的大小关系
+ 否则的话,可以直接入栈。
+ 注意,单调栈里 加入的元素是 下标。
+ */
+ Stackstack=new Stack<>();
+ stack.push(0);
+ for(int i=1;itemperatures[stack.peek()]){
+ res[stack.peek()]=i-stack.peek();
+ stack.pop();
+ }
+ stack.push(i);
}
- /**
- * 注意 放入的是元素位置
- */
- stack.push(i);
}
- return res;
+
+ return res;
}
+
+ //--------这 是一条分界线
+ // 版本 2
+ class Solution {
+ public int[] dailyTemperatures(int[] temperatures) {
+ int lens=temperatures.length;
+ int []res=new int[lens];
+ Stackstack=new Stack<>();
+ for(int i=0;itemperatures[stack.peek()]){
+ res[stack.peek()]=i-stack.peek();
+ stack.pop();
+ }
+ stack.push(i);
+ }
+
+ return res;
+ }
+}
+
+}
```
Python:
``` Python3
diff --git a/problems/0746.使用最小花费爬楼梯.md b/problems/0746.使用最小花费爬楼梯.md
index e94e4d24..c356955a 100644
--- a/problems/0746.使用最小花费爬楼梯.md
+++ b/problems/0746.使用最小花费爬楼梯.md
@@ -113,8 +113,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
还可以优化空间复杂度,因为dp[i]就是由前两位推出来的,那么也不用dp数组了,C++代码如下:
@@ -136,8 +136,8 @@ public:
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(1)
**当然我不建议这么写,能写出版本一就可以了,直观简洁!**
diff --git a/problems/0841.钥匙和房间.md b/problems/0841.钥匙和房间.md
index 8397690e..1cd13065 100644
--- a/problems/0841.钥匙和房间.md
+++ b/problems/0841.钥匙和房间.md
@@ -152,7 +152,6 @@ class Solution {
-Python:
python3
diff --git a/problems/0844.比较含退格的字符串.md b/problems/0844.比较含退格的字符串.md
index 5d629a8c..dccc5404 100644
--- a/problems/0844.比较含退格的字符串.md
+++ b/problems/0844.比较含退格的字符串.md
@@ -36,7 +36,7 @@
## 思路
-本文将给出 空间复杂度$O(n)$的栈模拟方法 以及空间复杂度是$O(1)$的双指针方法。
+本文将给出 空间复杂度O(n)的栈模拟方法 以及空间复杂度是O(1)的双指针方法。
## 普通方法(使用栈的思路)
@@ -71,8 +71,8 @@ public:
}
};
```
-* 时间复杂度:$O(n + m)$,n为S的长度,m为T的长度 ,也可以理解是$O(n)$的时间复杂度
-* 空间复杂度:$O(n + m)$
+* 时间复杂度:O(n + m),n为S的长度,m为T的长度 ,也可以理解是O(n)的时间复杂度
+* 空间复杂度:O(n + m)
当然以上代码,大家可以发现有重复的逻辑处理S,处理T,可以把这块公共逻辑抽离出来,代码精简如下:
@@ -97,12 +97,12 @@ public:
```
性能依然是:
-* 时间复杂度:$O(n + m)$
-* 空间复杂度:$O(n + m)$
+* 时间复杂度:O(n + m)
+* 空间复杂度:O(n + m)
## 优化方法(从后向前双指针)
-当然还可以有使用 $O(1)$ 的空间复杂度来解决该问题。
+当然还可以有使用 O(1) 的空间复杂度来解决该问题。
同时从后向前遍历S和T(i初始为S末尾,j初始为T末尾),记录#的数量,模拟消除的操作,如果#用完了,就开始比较S[i]和S[j]。
@@ -151,8 +151,8 @@ public:
};
```
-* 时间复杂度:$O(n + m)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n + m)
+* 空间复杂度:O(1)
## 其他语言版本
@@ -185,6 +185,36 @@ class Solution {
}
```
+双指针:
+
+```java
+class Solution {
+public static boolean backspaceCompare(String s, String t) {
+ char[] sarray = s.toCharArray();
+ char[] tarray = t.toCharArray();
+ return generate(sarray).equals(generate(tarray));
+ }
+ public static String generate(char[] a){
+ int slow = -1;
+ int fast = 0;
+ if(a.length == 1){
+ return new String(a);
+ } else{
+ for(fast = 0; fast < a.length; fast++){
+ if(a[fast] != '#')
+ a[++slow] = a[fast];
+ else{
+ if(slow >= 0)
+ slow--;
+ }
+ }
+ return new String(a,0,slow + 1);
+ }
+ }
+}
+```
+
+
### python
@@ -205,6 +235,42 @@ class Solution:
return self.get_string(s) == self.get_string(t)
pass
```
+双指针
+```python
+class Solution:
+ def backspaceCompare(self, s: str, t: str) -> bool:
+ s_index, t_index = len(s) - 1, len(t) - 1
+ s_backspace, t_backspace = 0, 0 # 记录s,t的#数量
+ while s_index >= 0 or t_index >= 0: # 使用or,以防长度不一致
+ while s_index >= 0: # 从后向前,消除s的#
+ if s[s_index] == '#':
+ s_index -= 1
+ s_backspace += 1
+ else:
+ if s_backspace > 0:
+ s_index -= 1
+ s_backspace -= 1
+ else:
+ break
+ while t_index >= 0: # 从后向前,消除t的#
+ if t[t_index] == '#':
+ t_index -= 1
+ t_backspace += 1
+ else:
+ if t_backspace > 0:
+ t_index -= 1
+ t_backspace -= 1
+ else:
+ break
+ if s_index >= 0 and t_index >= 0: # 后半部分#消除完了,接下来比较当前位的值
+ if s[s_index] != t[t_index]:
+ return False
+ elif s_index >= 0 or t_index >= 0: # 一个字符串找到了待比较的字符,另一个没有,返回False
+ return False
+ s_index -= 1
+ t_index -= 1
+ return True
+```
### Go
@@ -226,6 +292,51 @@ func backspaceCompare(s string, t string) bool {
return getString(s) == getString(t)
}
+```
+双指针
+```go
+func backspaceCompare(s string, t string) bool {
+ s_index, t_index := len(s) - 1, len(t) - 1
+ s_backspace, t_backspace := 0, 0 // 记录s,t的#数量
+ for s_index >= 0 || t_index >= 0 { // 使用or,以防长度不一致
+ for s_index >= 0 { // 从后向前,消除s的#
+ if s[s_index] == '#' {
+ s_index--
+ s_backspace++
+ } else {
+ if s_backspace > 0 {
+ s_index--
+ s_backspace--
+ } else {
+ break
+ }
+ }
+ }
+ for t_index >= 0 { // 从后向前,消除t的#
+ if t[t_index] == '#' {
+ t_index--
+ t_backspace++
+ } else {
+ if t_backspace > 0 {
+ t_index--
+ t_backspace--
+ } else {
+ break
+ }
+ }
+ }
+ if s_index >= 0 && t_index >= 0 { // 后半部分#消除完了,接下来比较当前位的值
+ if s[s_index] != t[t_index] {
+ return false
+ }
+ } else if s_index >= 0 || t_index >= 0 { // 一个字符串找到了待比较的字符,另一个没有,返回false
+ return false
+ }
+ s_index--
+ t_index--
+ }
+ return true
+}
```
### JavaScript
diff --git a/problems/0860.柠檬水找零.md b/problems/0860.柠檬水找零.md
index 01bd1a3b..ffd5490d 100644
--- a/problems/0860.柠檬水找零.md
+++ b/problems/0860.柠檬水找零.md
@@ -128,24 +128,24 @@ public:
```java
class Solution {
public boolean lemonadeChange(int[] bills) {
- int cash_5 = 0;
- int cash_10 = 0;
+ int five = 0;
+ int ten = 0;
for (int i = 0; i < bills.length; i++) {
if (bills[i] == 5) {
- cash_5++;
+ five++;
} else if (bills[i] == 10) {
- cash_5--;
- cash_10++;
+ five--;
+ ten++;
} else if (bills[i] == 20) {
- if (cash_10 > 0) {
- cash_10--;
- cash_5--;
+ if (ten > 0) {
+ ten--;
+ five--;
} else {
- cash_5 -= 3;
+ five -= 3;
}
}
- if (cash_5 < 0 || cash_10 < 0) return false;
+ if (five < 0 || ten < 0) return false;
}
return true;
@@ -157,7 +157,7 @@ class Solution {
```python
class Solution:
def lemonadeChange(self, bills: List[int]) -> bool:
- five, ten, twenty = 0, 0, 0
+ five, ten = 0, 0
for bill in bills:
if bill == 5:
five += 1
@@ -169,10 +169,8 @@ class Solution:
if ten > 0 and five > 0:
ten -= 1
five -= 1
- twenty += 1
elif five > 2:
five -= 3
- twenty += 1
else:
return False
return True
diff --git a/problems/0922.按奇偶排序数组II.md b/problems/0922.按奇偶排序数组II.md
index 19675e7f..cb564fb6 100644
--- a/problems/0922.按奇偶排序数组II.md
+++ b/problems/0922.按奇偶排序数组II.md
@@ -26,11 +26,11 @@
## 思路
-这道题目直接的想法可能是两层for循环再加上used数组表示使用过的元素。这样的的时间复杂度是$O(n^2)$。
+这道题目直接的想法可能是两层for循环再加上used数组表示使用过的元素。这样的的时间复杂度是O(n^2)。
### 方法一
-其实这道题可以用很朴实的方法,时间复杂度就就是$O(n)$了,C++代码如下:
+其实这道题可以用很朴实的方法,时间复杂度就就是O(n)了,C++代码如下:
```CPP
class Solution {
@@ -57,8 +57,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
### 方法二
@@ -86,8 +86,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
### 方法三
@@ -109,10 +109,10 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(1)
-这里时间复杂度并不是$O(n^2)$,因为偶数位和奇数位都只操作一次,不是n/2 * n/2的关系,而是n/2 + n/2的关系!
+这里时间复杂度并不是O(n^2),因为偶数位和奇数位都只操作一次,不是n/2 * n/2的关系,而是n/2 + n/2的关系!
## 其他语言版本
diff --git a/problems/0925.长按键入.md b/problems/0925.长按键入.md
index 3aacee5c..0ef5a3d7 100644
--- a/problems/0925.长按键入.md
+++ b/problems/0925.长按键入.md
@@ -90,8 +90,8 @@ public:
```
-时间复杂度:$O(n)$
-空间复杂度:$O(1)$
+时间复杂度:O(n)
+空间复杂度:O(1)
## 其他语言版本
diff --git a/problems/0941.有效的山脉数组.md b/problems/0941.有效的山脉数组.md
index a2444cc1..4b7a978c 100644
--- a/problems/0941.有效的山脉数组.md
+++ b/problems/0941.有效的山脉数组.md
@@ -106,22 +106,15 @@ class Solution {
```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
+ left, right = 0, len(arr)-1
+
+ while left < len(arr)-1 and arr[left+1] > arr[left]:
+ left += 1
+
+ while right > 0 and arr[right-1] > arr[right]:
+ right -= 1
+
+ return left == right and right != 0 and left != len(arr)-1
```
diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md
index 433060a5..35c3ccdc 100644
--- a/problems/0968.监控二叉树.md
+++ b/problems/0968.监控二叉树.md
@@ -316,28 +316,44 @@ public:
### Java
```java
class Solution {
- private int count = 0;
+ int res=0;
public int minCameraCover(TreeNode root) {
- if (trval(root) == 0) count++;
- return count;
+ // 对根节点的状态做检验,防止根节点是无覆盖状态 .
+ if(minCame(root)==0){
+ res++;
+ }
+ return res;
}
-
- private int trval(TreeNode root) {
- if (root == null) return -1;
-
- int left = trval(root.left);
- int right = trval(root.right);
-
- if (left == 0 || right == 0) {
- count++;
+ /**
+ 节点的状态值:
+ 0 表示无覆盖
+ 1 表示 有摄像头
+ 2 表示有覆盖
+ 后序遍历,根据左右节点的情况,来判读 自己的状态
+ */
+ public int minCame(TreeNode root){
+ if(root==null){
+ // 空节点默认为 有覆盖状态,避免在叶子节点上放摄像头
return 2;
}
-
- if (left == 2 || right == 2) {
+ int left=minCame(root.left);
+ int right=minCame(root.right);
+
+ // 如果左右节点都覆盖了的话, 那么本节点的状态就应该是无覆盖,没有摄像头
+ if(left==2&&right==2){
+ //(2,2)
+ return 0;
+ }else if(left==0||right==0){
+ // 左右节点都是无覆盖状态,那 根节点此时应该放一个摄像头
+ // (0,0) (0,1) (0,2) (1,0) (2,0)
+ // 状态值为 1 摄像头数 ++;
+ res++;
return 1;
+ }else{
+ // 左右节点的 状态为 (1,1) (1,2) (2,1) 也就是左右节点至少存在 1个摄像头,
+ // 那么本节点就是处于被覆盖状态
+ return 2;
}
-
- return 0;
}
}
```
@@ -391,7 +407,7 @@ class Solution:
result += 1
return result
-```
+```
### Go
```go
diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md
index b11fa7ef..24276bcf 100644
--- a/problems/0977.有序数组的平方.md
+++ b/problems/0977.有序数组的平方.md
@@ -40,7 +40,7 @@ public:
};
```
-这个时间复杂度是 $O(n + n\log n)$, 可以说是$O(n\log n)$的时间复杂度,但为了和下面双指针法算法时间复杂度有鲜明对比,我记为 $O(n + n\log n)$。
+这个时间复杂度是 O(n + nlogn), 可以说是O(nlogn)的时间复杂度,但为了和下面双指针法算法时间复杂度有鲜明对比,我记为 O(n + nlog n)。
## 双指针法
@@ -83,7 +83,7 @@ public:
};
```
-此时的时间复杂度为$O(n)$,相对于暴力排序的解法$O(n + n\log n)$还是提升不少的。
+此时的时间复杂度为O(n),相对于暴力排序的解法O(n + nlog n)还是提升不少的。
**这里还是说一下,大家不必太在意leetcode上执行用时,打败多少多少用户,这个就是一个玩具,非常不准确。**
diff --git a/problems/1002.查找常用字符.md b/problems/1002.查找常用字符.md
index 7c5566d3..36937b0b 100644
--- a/problems/1002.查找常用字符.md
+++ b/problems/1002.查找常用字符.md
@@ -253,6 +253,43 @@ var commonChars = function (words) {
return res
};
```
+
+TypeScript
+```ts
+ console.time("test")
+ let str: string = ""
+ //设置一个用字母组成的map字典
+ let map = new Map()
+ //给所有map设置初始值为0
+ let wordInitial: [string, number][] = words[0]
+ .split("")
+ .map((item) => [item, 0])
+ //如果有重复字母,就把重复字母的数量加1
+ for (let word of words[0]) {
+ map.set(word, map.has(word) ? map.get(word) + 1 : 1)
+ }
+ for (let i = 1; i < words.length; i++) {
+ const mapWord = new Map(wordInitial)
+ for (let j = 0; j < words[i].length; j++) {
+ if (!map.has(words[i][j])) continue
+ //mapWord中的字母的个数不能高于当前map的个数,多于则不能添加
+ if (map.get(words[i][j]) > mapWord.get(words[i][j])) {
+ mapWord.set(
+ words[i][j],
+ mapWord.has(words[i][j]) ? mapWord!.get(words[i][j]) + 1 : 1
+ )
+ }
+ }
+ //每次重新初始化map
+ map = mapWord
+ }
+ for (let [key, value] of map) {
+ str += key.repeat(value)
+ }
+ console.timeEnd("test")
+ return str.split("")
+```
+
GO
```golang
func commonChars(words []string) []string {
diff --git a/problems/1047.删除字符串中的所有相邻重复项.md b/problems/1047.删除字符串中的所有相邻重复项.md
index d6eefd07..54455f62 100644
--- a/problems/1047.删除字符串中的所有相邻重复项.md
+++ b/problems/1047.删除字符串中的所有相邻重复项.md
@@ -194,7 +194,7 @@ class Solution {
```
Python:
-```python3
+```python
# 方法一,使用栈,推荐!
class Solution:
def removeDuplicates(self, s: str) -> str:
@@ -207,7 +207,7 @@ class Solution:
return "".join(res) # 字符串拼接
```
-```python3
+```python
# 方法二,使用双指针模拟栈,如果不让用栈可以作为备选方法。
class Solution:
def removeDuplicates(self, s: str) -> str:
@@ -267,8 +267,32 @@ var removeDuplicates = function(s) {
};
```
+TypeScript:
+
+```typescript
+function removeDuplicates(s: string): string {
+ const helperStack: string[] = [];
+ let i: number = 0;
+ while (i < s.length) {
+ let top: string = helperStack[helperStack.length - 1];
+ if (top === s[i]) {
+ helperStack.pop();
+ } else {
+ helperStack.push(s[i]);
+ }
+ i++;
+ }
+ let res: string = '';
+ while (helperStack.length > 0) {
+ res = helperStack.pop() + res;
+ }
+ return res;
+};
+```
+
C:
方法一:使用栈
+
```c
char * removeDuplicates(char * s){
//求出字符串长度
@@ -322,14 +346,12 @@ char * removeDuplicates(char * s){
Swift:
```swift
func removeDuplicates(_ s: String) -> String {
- let array = Array(s)
var stack = [Character]()
- for c in array {
- let last: Character? = stack.last
- if stack.isEmpty || last != c {
- stack.append(c)
- } else {
+ for c in s {
+ if stack.last == c {
stack.removeLast()
+ } else {
+ stack.append(c)
}
}
return String(stack)
diff --git a/problems/1049.最后一块石头的重量II.md b/problems/1049.最后一块石头的重量II.md
index d64e7e56..ee0ddef2 100644
--- a/problems/1049.最后一块石头的重量II.md
+++ b/problems/1049.最后一块石头的重量II.md
@@ -136,8 +136,8 @@ public:
```
-* 时间复杂度:$O(m × n)$ , m是石头总重量(准确的说是总重量的一半),n为石头块数
-* 空间复杂度:$O(m)$
+* 时间复杂度:O(m × n) , m是石头总重量(准确的说是总重量的一半),n为石头块数
+* 空间复杂度:O(m)
## 总结
@@ -153,6 +153,8 @@ public:
Java:
+
+一维数组版本
```Java
class Solution {
public int lastStoneWeightII(int[] stones) {
@@ -173,6 +175,41 @@ class Solution {
return sum - 2 * dp[target];
}
}
+```
+二维数组版本(便于理解)
+```Java
+class Solution {
+ public int lastStoneWeightII(int[] stones) {
+ int sum = 0;
+ for (int s : stones) {
+ sum += s;
+ }
+
+ int target = sum / 2;
+ //初始化,dp[i][j]为可以放0-i物品,背包容量为j的情况下背包中的最大价值
+ int[][] dp = new int[stones.length][target + 1];
+ //dp[i][0]默认初始化为0
+ //dp[0][j]取决于stones[0]
+ for (int j = stones[0]; j <= target; j++) {
+ dp[0][j] = stones[0];
+ }
+
+ for (int i = 1; i < stones.length; i++) {
+ for (int j = 1; j <= target; j++) {//注意是等于
+ if (j >= stones[i]) {
+ //不放:dp[i - 1][j] 放:dp[i - 1][j - stones[i]] + stones[i]
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - stones[i]] + stones[i]);
+ } else {
+ dp[i][j] = dp[i - 1][j];
+ }
+ }
+ }
+
+ System.out.println(dp[stones.length - 1][target]);
+ return (sum - dp[stones.length - 1][target]) - dp[stones.length - 1][target];
+ }
+}
+
```
Python:
diff --git a/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md b/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
index 4de56597..24302b2d 100644
--- a/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
+++ b/problems/O(n)的算法居然超时了,此时的n究竟是多大?.md
@@ -3,7 +3,7 @@
参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-# 程序提交之后为什么会超时?$O(n)$的算法会超时,n究竟是多大?
+# 程序提交之后为什么会超时?O(n)的算法会超时,n究竟是多大?
一些同学可能对计算机运行的速度还没有概念,就是感觉计算机运行速度应该会很快,那么在leetcode上做算法题目的时候为什么会超时呢?
@@ -18,9 +18,9 @@
也就是说程序运行的时间超过了规定的时间,一般OJ(online judge)的超时时间就是1s,也就是用例数据输入后最多要1s内得到结果,暂时还不清楚leetcode的判题规则,下文为了方便讲解,暂定超时时间就是1s。
-如果写出了一个$O(n)$的算法 ,其实可以估算出来n是多大的时候算法的执行时间就会超过1s了。
+如果写出了一个O(n)的算法 ,其实可以估算出来n是多大的时候算法的执行时间就会超过1s了。
-如果n的规模已经足够让$O(n)$的算法运行时间超过了1s,就应该考虑log(n)的解法了。
+如果n的规模已经足够让O(n)的算法运行时间超过了1s,就应该考虑log(n)的解法了。
# 从硬件配置看计算机的性能
@@ -63,7 +63,7 @@
测试硬件:2015年MacPro,CPU配置:2.7 GHz Dual-Core Intel Core i5
-实现三个函数,时间复杂度分别是 $O(n)$ , $O(n^2)$, $O(n\log n)$,使用加法运算来统一测试。
+实现三个函数,时间复杂度分别是 O(n) , O(n^2), O(nlog n),使用加法运算来统一测试。
```CPP
// O(n)
@@ -128,19 +128,19 @@ int main() {

-O(n)的算法,1s内大概计算机可以运行 5 * (10^8)次计算,可以推测一下$O(n^2)$ 的算法应该1s可以处理的数量级的规模是 5 * (10^8)开根号,实验数据如下。
+O(n)的算法,1s内大概计算机可以运行 5 * (10^8)次计算,可以推测一下O(n^2) 的算法应该1s可以处理的数量级的规模是 5 * (10^8)开根号,实验数据如下。

O(n^2)的算法,1s内大概计算机可以运行 22500次计算,验证了刚刚的推测。
-在推测一下$O(n\log n)$的话, 1s可以处理的数据规模是什么呢?
+在推测一下O(nlogn)的话, 1s可以处理的数据规模是什么呢?
-理论上应该是比 $O(n)$少一个数量级,因为$\log n$的复杂度 其实是很快,看一下实验数据。
+理论上应该是比 O(n)少一个数量级,因为logn的复杂度 其实是很快,看一下实验数据。

-$O(n \logn)$的算法,1s内大概计算机可以运行 2 * (10^7)次计算,符合预期。
+O(nlogn)的算法,1s内大概计算机可以运行 2 * (10^7)次计算,符合预期。
这是在我个人PC上测出来的数据,不能说是十分精确,但数量级是差不多的,大家也可以在自己的计算机上测一下。
@@ -148,7 +148,7 @@ $O(n \logn)$的算法,1s内大概计算机可以运行 2 * (10^7)次计算,

-至于$O(\log n)$和$O(n^3)$ 等等这些时间复杂度在1s内可以处理的多大的数据规模,大家可以自己写一写代码去测一下了。
+至于O(log n)和O(n^3) 等等这些时间复杂度在1s内可以处理的多大的数据规模,大家可以自己写一写代码去测一下了。
# 完整测试代码
@@ -209,7 +209,7 @@ int main() {
# 总结
-本文详细分析了在leetcode上做题程序为什么会有超时,以及从硬件配置上大体知道CPU的执行速度,然后亲自做一个实验来看看$O(n)$的算法,跑一秒钟,这个n究竟是做大,最后给出不同时间复杂度,一秒内可以运算出来的n的大小。
+本文详细分析了在leetcode上做题程序为什么会有超时,以及从硬件配置上大体知道CPU的执行速度,然后亲自做一个实验来看看O(n)的算法,跑一秒钟,这个n究竟是做大,最后给出不同时间复杂度,一秒内可以运算出来的n的大小。
建议录友们也都自己做一做实验,测一测,看看是不是和我的测出来的结果差不多。
diff --git a/problems/二叉树中递归带着回溯.md b/problems/二叉树中递归带着回溯.md
index 20b87f87..1e9f9cbf 100644
--- a/problems/二叉树中递归带着回溯.md
+++ b/problems/二叉树中递归带着回溯.md
@@ -171,7 +171,7 @@ if (cur->right) {
## 其他语言版本
-Java:
+### Java:
100. 相同的树:递归代码
```java
class Solution {
@@ -252,7 +252,7 @@ Java:
}
```
-Python:
+### Python:
100.相同的树
> 递归法
@@ -332,7 +332,7 @@ class Solution:
self.traversal(cur.right, path+"->", result) #右 回溯就隐藏在这里
```
-Go:
+### Go:
100.相同的树
```go
@@ -436,7 +436,7 @@ func traversal(root *TreeNode,result *[]string,path *[]int){
}
```
-JavaScript:
+### JavaScript:
100.相同的树
```javascript
@@ -516,5 +516,107 @@ var binaryTreePaths = function(root) {
```
+### TypeScript:
+
+> 相同的树
+
+```typescript
+function isSameTree(p: TreeNode | null, q: TreeNode | null): boolean {
+ if (p === null && q === null) return true;
+ if (p === null || q === null) return false;
+ if (p.val !== q.val) return false;
+ let bool1: boolean, bool2: boolean;
+ bool1 = isSameTree(p.left, q.left);
+ bool2 = isSameTree(p.right, q.right);
+ return bool1 && bool2;
+};
+```
+
+> 二叉树的不同路径
+
+```typescript
+function binaryTreePaths(root: TreeNode | null): string[] {
+ function recur(node: TreeNode, nodeSeqArr: number[], resArr: string[]): void {
+ nodeSeqArr.push(node.val);
+ if (node.left === null && node.right === null) {
+ resArr.push(nodeSeqArr.join('->'));
+ }
+ if (node.left !== null) {
+ recur(node.left, nodeSeqArr, resArr);
+ nodeSeqArr.pop();
+ }
+ if (node.right !== null) {
+ recur(node.right, nodeSeqArr, resArr);
+ nodeSeqArr.pop();
+ }
+ }
+ let nodeSeqArr: number[] = [];
+ let resArr: string[] = [];
+ if (root === null) return resArr;
+ recur(root, nodeSeqArr, resArr);
+ return resArr;
+};
+```
+
+
+
+
+### Swift:
+> 100.相同的树
+```swift
+// 递归
+func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool {
+ return _isSameTree3(p, q)
+}
+func _isSameTree3(_ p: TreeNode?, _ q: TreeNode?) -> Bool {
+ if p == nil && q == nil {
+ return true
+ } else if p == nil && q != nil {
+ return false
+ } else if p != nil && q == nil {
+ return false
+ } else if p!.val != q!.val {
+ return false
+ }
+ let leftSide = _isSameTree3(p!.left, q!.left)
+ let rightSide = _isSameTree3(p!.right, q!.right)
+ return leftSide && rightSide
+}
+```
+
+> 257.二叉树的不同路径
+```swift
+// 递归/回溯
+func binaryTreePaths(_ root: TreeNode?) -> [String] {
+ var res = [String]()
+ guard let root = root else {
+ return res
+ }
+ var paths = [Int]()
+ _binaryTreePaths3(root, res: &res, paths: &paths)
+ return res
+}
+func _binaryTreePaths3(_ root: TreeNode, res: inout [String], paths: inout [Int]) {
+ paths.append(root.val)
+ if root.left == nil && root.right == nil {
+ var str = ""
+ for i in 0 ..< (paths.count - 1) {
+ str.append("\(paths[i])->")
+ }
+ str.append("\(paths.last!)")
+ res.append(str)
+ }
+ if let left = root.left {
+ _binaryTreePaths3(left, res: &res, paths: &paths)
+ paths.removeLast()
+ }
+ if let right = root.right {
+ _binaryTreePaths3(right, res: &res, paths: &paths)
+ paths.removeLast()
+ }
+}
+```
+
+
-----------------------
diff --git a/problems/二叉树总结篇.md b/problems/二叉树总结篇.md
index d1332e09..73faffa6 100644
--- a/problems/二叉树总结篇.md
+++ b/problems/二叉树总结篇.md
@@ -152,7 +152,7 @@

-这个图是 [代码随想录知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) 成员:[青](https://wx.zsxq.com/dweb2/index/footprint/185251215558842),所画,总结的非常好,分享给大家。
+这个图是 [代码随想录知识星球](https://programmercarl.com/other/kstar.html) 成员:[青](https://wx.zsxq.com/dweb2/index/footprint/185251215558842),所画,总结的非常好,分享给大家。
**最后,二叉树系列就这么完美结束了,估计这应该是最长的系列了,感谢大家33天的坚持与陪伴,接下来我们又要开始新的系列了「回溯算法」!**
diff --git a/problems/二叉树理论基础.md b/problems/二叉树理论基础.md
index cc899850..9c151e32 100644
--- a/problems/二叉树理论基础.md
+++ b/problems/二叉树理论基础.md
@@ -33,7 +33,7 @@
什么是完全二叉树?
-完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^h -1 个节点。
+完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。
**大家要自己看完全二叉树的定义,很多同学对完全二叉树其实不是真正的懂了。**
@@ -154,7 +154,7 @@
C++代码如下:
-```
+```cpp
struct TreeNode {
int val;
TreeNode *left;
@@ -163,7 +163,7 @@ struct TreeNode {
};
```
-大家会发现二叉树的定义 和链表是差不多的,相对于链表 ,二叉树的节点里多了一个指针, 有两个指针,指向左右孩子.
+大家会发现二叉树的定义 和链表是差不多的,相对于链表 ,二叉树的节点里多了一个指针, 有两个指针,指向左右孩子。
这里要提醒大家要注意二叉树节点定义的书写方式。
@@ -177,7 +177,7 @@ struct TreeNode {
本篇我们介绍了二叉树的种类、存储方式、遍历方式以及定义,比较全面的介绍了二叉树各个方面的重点,帮助大家扫一遍基础。
-**说道二叉树,就不得不说递归,很多同学对递归都是又熟悉又陌生,递归的代码一般很简短,但每次都是一看就会,一写就废。**
+**说到二叉树,就不得不说递归,很多同学对递归都是又熟悉又陌生,递归的代码一般很简短,但每次都是一看就会,一写就废。**
## 其他语言版本
@@ -227,7 +227,23 @@ function TreeNode(val, left, right) {
}
```
+TypeScript:
+
+```typescript
+class TreeNode {
+ public val: number;
+ public left: TreeNode | null;
+ public right: TreeNode | null;
+ constructor(val?: number, left?: TreeNode, right?: TreeNode) {
+ this.val = val === undefined ? 0 : val;
+ this.left = left === undefined ? null : left;
+ this.right = right === undefined ? null : right;
+ }
+}
+```
+
Swift:
+
```Swift
class TreeNode {
var value: T
diff --git a/problems/二叉树的统一迭代法.md b/problems/二叉树的统一迭代法.md
index 2ff84817..f6edf586 100644
--- a/problems/二叉树的统一迭代法.md
+++ b/problems/二叉树的统一迭代法.md
@@ -522,7 +522,75 @@ var postorderTraversal = function(root, res = []) {
```
+TypeScript:
+```typescript
+// 前序遍历(迭代法)
+function preorderTraversal(root: TreeNode | null): number[] {
+ let helperStack: (TreeNode | null)[] = [];
+ let res: number[] = [];
+ let curNode: TreeNode | null;
+ if (root === null) return res;
+ helperStack.push(root);
+ while (helperStack.length > 0) {
+ curNode = helperStack.pop()!;
+ if (curNode !== null) {
+ if (curNode.right !== null) helperStack.push(curNode.right);
+ helperStack.push(curNode);
+ helperStack.push(null);
+ if (curNode.left !== null) helperStack.push(curNode.left);
+ } else {
+ curNode = helperStack.pop()!;
+ res.push(curNode.val);
+ }
+ }
+ return res;
+};
+
+// 中序遍历(迭代法)
+function inorderTraversal(root: TreeNode | null): number[] {
+ let helperStack: (TreeNode | null)[] = [];
+ let res: number[] = [];
+ let curNode: TreeNode | null;
+ if (root === null) return res;
+ helperStack.push(root);
+ while (helperStack.length > 0) {
+ curNode = helperStack.pop()!;
+ if (curNode !== null) {
+ if (curNode.right !== null) helperStack.push(curNode.right);
+ helperStack.push(curNode);
+ helperStack.push(null);
+ if (curNode.left !== null) helperStack.push(curNode.left);
+ } else {
+ curNode = helperStack.pop()!;
+ res.push(curNode.val);
+ }
+ }
+ return res;
+};
+
+// 后序遍历(迭代法)
+function postorderTraversal(root: TreeNode | null): number[] {
+ let helperStack: (TreeNode | null)[] = [];
+ let res: number[] = [];
+ let curNode: TreeNode | null;
+ if (root === null) return res;
+ helperStack.push(root);
+ while (helperStack.length > 0) {
+ curNode = helperStack.pop()!;
+ if (curNode !== null) {
+ if (curNode.right !== null) helperStack.push(curNode.right);
+ helperStack.push(curNode);
+ helperStack.push(null);
+ if (curNode.left !== null) helperStack.push(curNode.left);
+ } else {
+ curNode = helperStack.pop()!;
+ res.push(curNode.val);
+ }
+ }
+ return res;
+};
+```
-----------------------
diff --git a/problems/二叉树的迭代遍历.md b/problems/二叉树的迭代遍历.md
index 4cb94cb5..8164724b 100644
--- a/problems/二叉树的迭代遍历.md
+++ b/problems/二叉树的迭代遍历.md
@@ -390,7 +390,7 @@ func inorderTraversal(root *TreeNode) []int {
}
```
-javaScript
+javaScript:
```js
@@ -454,77 +454,118 @@ var postorderTraversal = function(root, res = []) {
};
```
-Swift:
+TypeScript:
-> 迭代法前序遍历
-```swift
-func preorderTraversal(_ root: TreeNode?) -> [Int] {
- var res = [Int]()
- if root == nil {
- return res
+```typescript
+// 前序遍历(迭代法)
+function preorderTraversal(root: TreeNode | null): number[] {
+ if (root === null) return [];
+ let res: number[] = [];
+ let helperStack: TreeNode[] = [];
+ let curNode: TreeNode = root;
+ helperStack.push(curNode);
+ while (helperStack.length > 0) {
+ curNode = helperStack.pop()!;
+ res.push(curNode.val);
+ if (curNode.right !== null) helperStack.push(curNode.right);
+ if (curNode.left !== null) helperStack.push(curNode.left);
}
- var stack = [TreeNode]()
- stack.append(root!)
- while !stack.isEmpty {
- let node = stack.popLast()!
- res.append(node.val)
- if node.right != nil {
- stack.append(node.right!)
- }
- if node.left != nil {
- stack.append(node.left!)
- }
- }
- return res
-}
-```
+ return res;
+};
-> 迭代法中序遍历
-```swift
-func inorderTraversal(_ root: TreeNode?) -> [Int] {
- var res = [Int]()
- if root == nil {
- return res
- }
- var stack = [TreeNode]()
- var cur: TreeNode? = root
- while cur != nil || !stack.isEmpty {
- if cur != nil {
- stack.append(cur!)
- cur = cur!.left
+// 中序遍历(迭代法)
+function inorderTraversal(root: TreeNode | null): number[] {
+ let helperStack: TreeNode[] = [];
+ let res: number[] = [];
+ if (root === null) return res;
+ let curNode: TreeNode | null = root;
+ while (curNode !== null || helperStack.length > 0) {
+ if (curNode !== null) {
+ helperStack.push(curNode);
+ curNode = curNode.left;
} else {
- cur = stack.popLast()
- res.append(cur!.val)
- cur = cur!.right
+ curNode = helperStack.pop()!;
+ res.push(curNode.val);
+ curNode = curNode.right;
}
}
- return res
-}
+ return res;
+};
+
+// 后序遍历(迭代法)
+function postorderTraversal(root: TreeNode | null): number[] {
+ let helperStack: TreeNode[] = [];
+ let res: number[] = [];
+ let curNode: TreeNode;
+ if (root === null) return res;
+ helperStack.push(root);
+ while (helperStack.length > 0) {
+ curNode = helperStack.pop()!;
+ res.push(curNode.val);
+ if (curNode.left !== null) helperStack.push(curNode.left);
+ if (curNode.right !== null) helperStack.push(curNode.right);
+ }
+ return res.reverse();
+};
```
-> 迭代法后序遍历
+Swift:
+
```swift
-func postorderTraversal(_ root: TreeNode?) -> [Int] {
- var res = [Int]()
- if root == nil {
- return res
- }
- var stack = [TreeNode]()
- stack.append(root!)
- // res 存储 中 -> 右 -> 左
+// 前序遍历迭代法
+func preorderTraversal(_ root: TreeNode?) -> [Int] {
+ var result = [Int]()
+ guard let root = root else { return result }
+ var stack = [root]
while !stack.isEmpty {
- let node = stack.popLast()!
- res.append(node.val)
- if node.left != nil {
- stack.append(node.left!)
+ let current = stack.removeLast()
+ // 先右后左,这样出栈的时候才是左右顺序
+ if let node = current.right { // 右
+ stack.append(node)
}
- if node.right != nil {
- stack.append(node.right!)
+ if let node = current.left { // 左
+ stack.append(node)
+ }
+ result.append(current.val) // 中
+ }
+ return result
+}
+
+// 后序遍历迭代法
+func postorderTraversal(_ root: TreeNode?) -> [Int] {
+ var result = [Int]()
+ guard let root = root else { return result }
+ var stack = [root]
+ while !stack.isEmpty {
+ let current = stack.removeLast()
+ // 与前序相反,即中右左,最后结果还需反转才是后序
+ if let node = current.left { // 左
+ stack.append(node)
+ }
+ if let node = current.right { // 右
+ stack.append(node)
+ }
+ result.append(current.val) // 中
+ }
+ return result.reversed()
+}
+
+// 中序遍历迭代法
+func inorderTraversal(_ root: TreeNode?) -> [Int] {
+ var result = [Int]()
+ var stack = [TreeNode]()
+ var current: TreeNode! = root
+ while current != nil || !stack.isEmpty {
+ if current != nil { // 先访问到最左叶子
+ stack.append(current)
+ current = current.left // 左
+ } else {
+ current = stack.removeLast()
+ result.append(current.val) // 中
+ current = current.right // 右
}
}
- // res 翻转
- res.reverse()
- return res
+ return result
}
```
diff --git a/problems/二叉树的递归遍历.md b/problems/二叉树的递归遍历.md
index 45b576e7..35d19d7b 100644
--- a/problems/二叉树的递归遍历.md
+++ b/problems/二叉树的递归遍历.md
@@ -34,19 +34,19 @@
1. **确定递归函数的参数和返回值**:因为要打印出前序遍历节点的数值,所以参数里需要传入vector在放节点的数值,除了这一点就不需要在处理什么数据了也不需要有返回值,所以递归函数返回类型就是void,代码如下:
-```
+```cpp
void traversal(TreeNode* cur, vector& vec)
```
2. **确定终止条件**:在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:
-```
+```cpp
if (cur == NULL) return;
```
3. **确定单层递归的逻辑**:前序遍历是中左右的循序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:
-```
+```cpp
vec.push_back(cur->val); // 中
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
@@ -116,19 +116,19 @@ Java:
```Java
// 前序遍历·递归·LC144_二叉树的前序遍历
class Solution {
- ArrayList preOrderReverse(TreeNode root) {
- ArrayList result = new ArrayList();
- preOrder(root, result);
+ public List preorderTraversal(TreeNode root) {
+ List result = new ArrayList();
+ preorder(root, result);
return result;
}
- void preOrder(TreeNode root, ArrayList result) {
+ public void preorder(TreeNode root, List result) {
if (root == null) {
return;
}
- result.add(root.val); // 注意这一句
- preOrder(root.left, result);
- preOrder(root.right, result);
+ result.add(root.val);
+ preorder(root.left, result);
+ preorder(root.right, result);
}
}
// 中序遍历·递归·LC94_二叉树的中序遍历
@@ -168,7 +168,7 @@ class Solution {
```
Python:
-```python3
+```python
# 前序遍历-递归-LC144_二叉树的前序遍历
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
@@ -270,40 +270,6 @@ func postorderTraversal(root *TreeNode) (res []int) {
}
```
-javaScript:
-
-```js
-
-前序遍历:
-
-var preorderTraversal = function(root, res = []) {
- if (!root) return res;
- res.push(root.val);
- preorderTraversal(root.left, res)
- preorderTraversal(root.right, res)
- return res;
-};
-
-中序遍历:
-
-var inorderTraversal = function(root, res = []) {
- if (!root) return res;
- inorderTraversal(root.left, res);
- res.push(root.val);
- inorderTraversal(root.right, res);
- return res;
-};
-
-后序遍历:
-
-var postorderTraversal = function(root, res = []) {
- if (!root) return res;
- postorderTraversal(root.left, res);
- postorderTraversal(root.right, res);
- res.push(root.val);
- return res;
-};
-```
Javascript版本:
前序遍历:
@@ -358,7 +324,51 @@ var postorderTraversal = function(root) {
};
```
+TypeScript:
+
+```typescript
+// 前序遍历
+function preorderTraversal(node: TreeNode | null): number[] {
+ function traverse(node: TreeNode | null, res: number[]): void {
+ if (node === null) return;
+ res.push(node.val);
+ traverse(node.left, res);
+ traverse(node.right, res);
+ }
+ const res: number[] = [];
+ traverse(node, res);
+ return res;
+}
+
+// 中序遍历
+function inorderTraversal(node: TreeNode | null): number[] {
+ function traverse(node: TreeNode | null, res: number[]): void {
+ if (node === null) return;
+ traverse(node.left, res);
+ res.push(node.val);
+ traverse(node.right, res);
+ }
+ const res: number[] = [];
+ traverse(node, res);
+ return res;
+}
+
+// 后序遍历
+function postorderTraversal(node: TreeNode | null): number[] {
+ function traverse(node: TreeNode | null, res: number[]): void {
+ if (node === null) return;
+ traverse(node.left, res);
+ traverse(node.right, res);
+ res.push(node.val);
+ }
+ const res: number[] = [];
+ traverse(node, res);
+ return res;
+}
+```
+
C:
+
```c
//前序遍历:
void preOrderTraversal(struct TreeNode* root, int* ret, int* returnSize) {
diff --git a/problems/关于时间复杂度,你不知道的都在这里!.md b/problems/关于时间复杂度,你不知道的都在这里!.md
index b6477249..a0fd6d92 100644
--- a/problems/关于时间复杂度,你不知道的都在这里!.md
+++ b/problems/关于时间复杂度,你不知道的都在这里!.md
@@ -16,21 +16,21 @@
那么该如何估计程序运行时间呢,通常会估算算法的操作单元数量来代表程序消耗的时间,这里默认CPU的每个单元运行消耗的时间都是相同的。
-假设算法的问题规模为n,那么操作单元数量便用函数f(n)来表示,随着数据规模n的增大,算法执行时间的增长率和f(n)的增长率相同,这称作为算法的渐近时间复杂度,简称时间复杂度,记为 $O(f(n))$。
+假设算法的问题规模为n,那么操作单元数量便用函数f(n)来表示,随着数据规模n的增大,算法执行时间的增长率和f(n)的增长率相同,这称作为算法的渐近时间复杂度,简称时间复杂度,记为 O(f(n))。
## 什么是大O
-这里的大O是指什么呢,说到时间复杂度,**大家都知道$O(n)$,$O(n^2)$,却说不清什么是大O**。
+这里的大O是指什么呢,说到时间复杂度,**大家都知道O(n),O(n^2),却说不清什么是大O**。
算法导论给出的解释:**大O用来表示上界的**,当用它作为算法的最坏情况运行时间的上界,就是对任意数据输入的运行时间的上界。
-同样算法导论给出了例子:拿插入排序来说,插入排序的时间复杂度我们都说是$O(n^2)$ 。
+同样算法导论给出了例子:拿插入排序来说,插入排序的时间复杂度我们都说是O(n^2) 。
-输入数据的形式对程序运算时间是有很大影响的,在数据本来有序的情况下时间复杂度是$O(n)$,但如果数据是逆序的话,插入排序的时间复杂度就是$O(n^2)$,也就对于所有输入情况来说,最坏是$O(n^2)$ 的时间复杂度,所以称插入排序的时间复杂度为$O(n^2)$。
+输入数据的形式对程序运算时间是有很大影响的,在数据本来有序的情况下时间复杂度是O(n),但如果数据是逆序的话,插入排序的时间复杂度就是O(n^2),也就对于所有输入情况来说,最坏是O(n^2) 的时间复杂度,所以称插入排序的时间复杂度为O(n^2)。
-同样的同理再看一下快速排序,都知道快速排序是$O(n\log n)$,但是当数据已经有序情况下,快速排序的时间复杂度是$O(n^2)$ 的,**所以严格从大O的定义来讲,快速排序的时间复杂度应该是$O(n^2)$**。
+同样的同理再看一下快速排序,都知道快速排序是O(nlog n),但是当数据已经有序情况下,快速排序的时间复杂度是O(n^2) 的,**所以严格从大O的定义来讲,快速排序的时间复杂度应该是O(n^2)**。
-**但是我们依然说快速排序是$O(n\log n)$的时间复杂度,这个就是业内的一个默认规定,这里说的O代表的就是一般情况,而不是严格的上界**。如图所示:
+**但是我们依然说快速排序是O(nlog n)的时间复杂度,这个就是业内的一个默认规定,这里说的O代表的就是一般情况,而不是严格的上界**。如图所示:

我们主要关心的还是一般情况下的数据形式。
@@ -44,11 +44,11 @@

-在决定使用哪些算法的时候,不是时间复杂越低的越好(因为简化后的时间复杂度忽略了常数项等等),要考虑数据规模,如果数据规模很小甚至可以用$O(n^2)$的算法比$O(n)$的更合适(在有常数项的时候)。
+在决定使用哪些算法的时候,不是时间复杂越低的越好(因为简化后的时间复杂度忽略了常数项等等),要考虑数据规模,如果数据规模很小甚至可以用O(n^2)的算法比O(n)的更合适(在有常数项的时候)。
-就像上图中 $O(5n^2)$ 和 $O(100n)$ 在n为20之前 很明显 $O(5n^2)$是更优的,所花费的时间也是最少的。
+就像上图中 O(5n^2) 和 O(100n) 在n为20之前 很明显 O(5n^2)是更优的,所花费的时间也是最少的。
-那为什么在计算时间复杂度的时候要忽略常数项系数呢,也就说$O(100n)$ 就是$O(n)$的时间复杂度,$O(5n^2)$ 就是$O(n^2)$的时间复杂度,而且要默认$O(n)$ 优于$O(n^2)$ 呢 ?
+那为什么在计算时间复杂度的时候要忽略常数项系数呢,也就说O(100n) 就是O(n)的时间复杂度,O(5n^2) 就是O(n^2)的时间复杂度,而且要默认O(n) 优于O(n^2) 呢 ?
这里就又涉及到大O的定义,**因为大O就是数据量级突破一个点且数据量级非常大的情况下所表现出的时间复杂度,这个数据量也就是常数项系数已经不起决定性作用的数据量**。
@@ -56,13 +56,13 @@
**所以我们说的时间复杂度都是省略常数项系数的,是因为一般情况下都是默认数据规模足够的大,基于这样的事实,给出的算法时间复杂的的一个排行如下所示**:
-O(1) 常数阶 < $O(\log n)$ 对数阶 < $O(n)$ 线性阶 < $O(n^2)$ 平方阶 < $O(n^3)$ 立方阶 < $O(2^n)$指数阶
+O(1) 常数阶 < O(\log n) 对数阶 < O(n) 线性阶 < O(n^2) 平方阶 < O(n^3) 立方阶 < O(2^n)指数阶
但是也要注意大常数,如果这个常数非常大,例如10^7 ,10^9 ,那么常数就是不得不考虑的因素了。
## 复杂表达式的化简
-有时候我们去计算时间复杂度的时候发现不是一个简单的$O(n)$ 或者$O(n^2)$, 而是一个复杂的表达式,例如:
+有时候我们去计算时间复杂度的时候发现不是一个简单的O(n) 或者O(n^2), 而是一个复杂的表达式,例如:
```
O(2*n^2 + 10*n + 1000)
@@ -88,19 +88,19 @@ O(n^2 + n)
O(n^2)
```
-如果这一步理解有困难,那也可以做提取n的操作,变成$O(n(n+1))$,省略加法常数项后也就别变成了:
+如果这一步理解有困难,那也可以做提取n的操作,变成O(n(n+1)),省略加法常数项后也就别变成了:
```
O(n^2)
```
-所以最后我们说:这个算法的算法时间复杂度是$O(n^2)$ 。
+所以最后我们说:这个算法的算法时间复杂度是O(n^2) 。
-也可以用另一种简化的思路,其实当n大于40的时候, 这个复杂度会恒小于$O(3 × n^2)$,
-$O(2 × n^2 + 10 × n + 1000)$ < $O(3 × n^2)$,所以说最后省略掉常数项系数最终时间复杂度也是$O(n^2)$。
+也可以用另一种简化的思路,其实当n大于40的时候, 这个复杂度会恒小于O(3 × n^2),
+O(2 × n^2 + 10 × n + 1000) < O(3 × n^2),所以说最后省略掉常数项系数最终时间复杂度也是O(n^2)。
-## $O(\log n)$中的log是以什么为底?
+## O(log n)中的log是以什么为底?
平时说这个算法的时间复杂度是logn的,那么一定是log 以2为底n的对数么?
@@ -123,21 +123,21 @@ $O(2 × n^2 + 10 × n + 1000)$ < $O(3 × n^2)$,所以说最后省略掉常数
通过这道面试题目,来分析一下时间复杂度。题目描述:找出n个字符串中相同的两个字符串(假设这里只有两个相同的字符串)。
-如果是暴力枚举的话,时间复杂度是多少呢,是$O(n^2)$么?
+如果是暴力枚举的话,时间复杂度是多少呢,是O(n^2)么?
-这里一些同学会忽略了字符串比较的时间消耗,这里并不像int 型数字做比较那么简单,除了$n^2$次的遍历次数外,字符串比较依然要消耗m次操作(m也就是字母串的长度),所以时间复杂度是$O(m × n × n)$。
+这里一些同学会忽略了字符串比较的时间消耗,这里并不像int 型数字做比较那么简单,除了n^2次的遍历次数外,字符串比较依然要消耗m次操作(m也就是字母串的长度),所以时间复杂度是O(m × n × n)。
接下来再想一下其他解题思路。
先排对n个字符串按字典序来排序,排序后n个字符串就是有序的,意味着两个相同的字符串就是挨在一起,然后在遍历一遍n个字符串,这样就找到两个相同的字符串了。
-那看看这种算法的时间复杂度,快速排序时间复杂度为$O(n\log n)$,依然要考虑字符串的长度是m,那么快速排序每次的比较都要有m次的字符比较的操作,就是$O(m × n × \log n)$。
+那看看这种算法的时间复杂度,快速排序时间复杂度为O(nlog n),依然要考虑字符串的长度是m,那么快速排序每次的比较都要有m次的字符比较的操作,就是O(m × n × log n)。
-之后还要遍历一遍这n个字符串找出两个相同的字符串,别忘了遍历的时候依然要比较字符串,所以总共的时间复杂度是 $O(m × n × \log n + n × m)$。
+之后还要遍历一遍这n个字符串找出两个相同的字符串,别忘了遍历的时候依然要比较字符串,所以总共的时间复杂度是 O(m × n × log n + n × m)。
-我们对$O(m × n × \log n + n × m)$进行简化操作,把$m × n$提取出来变成$O(m × n × (\log n + 1)$),再省略常数项最后的时间复杂度是$O(m × n × \log n)$。
+我们对O(m × n × log n + n × m)进行简化操作,把m × n提取出来变成O(m × n × (log n + 1)),再省略常数项最后的时间复杂度是O(m × n × log n)。
-最后很明显$O(m × n × \log n)$ 要优于$O(m × n × n)$!
+最后很明显O(m × n × log n) 要优于O(m × n × n)!
所以先把字符串集合排序再遍历一遍找到两个相同字符串的方法要比直接暴力枚举的方式更快。
diff --git a/problems/前序/ACM模式如何构建二叉树.md b/problems/前序/ACM模式如何构建二叉树.md
index d3b2656e..bd2e9780 100644
--- a/problems/前序/ACM模式如何构建二叉树.md
+++ b/problems/前序/ACM模式如何构建二叉树.md
@@ -1,13 +1,4 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
-
# 力扣上如何自己构造二叉树输入用例?
经常有录友问,二叉树的题目中输入用例在ACM模式下应该怎么构造呢?
@@ -45,21 +36,7 @@

-那么此时大家是不是应该知道了,数组如何转化成 二叉树了。**如果父节点的数组下标是i,那么它的左孩子下标就是i * 2 + 1,右孩子下标就是 i * 2 + 2**。计算过程为:
-
-如果父节点在第$k$层,第$m,m \in [0,2^k]$个节点,则其左孩子所在的位置必然为$k+1$层,第$2*(m-1)+1$个节点。
-
-- 计算父节点在数组中的索引:
- $$
- index_{father}=(\sum_{i=0}^{i=k-1}2^i)+m-1=2^k-1+m-1
- $$
-
-- 计算左子节点在数组的索引:
- $$
- index_{left}=(\sum_{i=0}^{i=k}2^i)+2*m-1-1=2^{k+1}+2m-3
- $$
-
-- 故左孩子的下表为$index_{left}=index_{father}\times2+1$,同理可得到右子孩子的索引关系。也可以直接在左子孩子的基础上`+1`。
+那么此时大家是不是应该知道了,数组如何转化成 二叉树了。**如果父节点的数组下标是i,那么它的左孩子下标就是i * 2 + 1,右孩子下标就是 i * 2 + 2**。
那么这里又有同学疑惑了,这些我都懂了,但我还是不知道 应该 怎么构造。
@@ -251,7 +228,4 @@ int main() {
```
-----------------------
-* 作者微信:[程序员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/前序/BAT级别技术面试流程和注意事项都在这里了.md b/problems/前序/BAT级别技术面试流程和注意事项都在这里了.md
index c5797739..27940f1b 100644
--- a/problems/前序/BAT级别技术面试流程和注意事项都在这里了.md
+++ b/problems/前序/BAT级别技术面试流程和注意事项都在这里了.md
@@ -1,12 +1,5 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
+# 大厂技术面试流程和注意事项
大型互联网企业一般通过几轮技术面试来考察大家的各项能力,一般流程如下:
@@ -218,7 +211,4 @@ leetcode是专门针对算法练习的题库,leetcode现在也推出了中文
-----------------------
-* 作者微信:[程序员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/前序/On的算法居然超时了,此时的n究竟是多大?.md b/problems/前序/On的算法居然超时了,此时的n究竟是多大?.md
index 9a56937c..20a48e19 100644
--- a/problems/前序/On的算法居然超时了,此时的n究竟是多大?.md
+++ b/problems/前序/On的算法居然超时了,此时的n究竟是多大?.md
@@ -1,18 +1,12 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+# On的算法居然超时了,此时的n究竟是多大?
一些同学可能对计算机运行的速度还没有概念,就是感觉计算机运行速度应该会很快,那么在leetcode上做算法题目的时候为什么会超时呢?
计算机究竟1s可以执行多少次操作呢? 接下来探讨一下这个问题。
-# 超时是怎么回事
+## 超时是怎么回事

@@ -24,7 +18,7 @@
如果n的规模已经足够让$O(n)$的算法运行时间超过了1s,就应该考虑log(n)的解法了。
-# 从硬件配置看计算机的性能
+## 从硬件配置看计算机的性能
计算机的运算速度主要看CPU的配置,以2015年MacPro为例,CPU配置:2.7 GHz Dual-Core Intel Core i5 。
@@ -43,7 +37,7 @@
所以我们的程序在计算机上究竟1s真正能执行多少次操作呢?
-# 做个测试实验
+## 做个测试实验
在写测试程序测1s内处理多大数量级数据的时候,有三点需要注意:
@@ -152,7 +146,7 @@ $O(n\log n)$的算法,1s内大概计算机可以运行 2 * (10^7)次计算,
至于 $O(\log n)$ 和 $O(n^3)$ 等等这些时间复杂度在1s内可以处理的多大的数据规模,大家可以自己写一写代码去测一下了。
-# 完整测试代码
+## 完整测试代码
```CPP
#include
@@ -264,7 +258,7 @@ public class TimeComplexity {
}
```
-# 总结
+## 总结
本文详细分析了在leetcode上做题程序为什么会有超时,以及从硬件配置上大体知道CPU的执行速度,然后亲自做一个实验来看看$O(n)$的算法,跑一秒钟,这个n究竟是做大,最后给出不同时间复杂度,一秒内可以运算出来的n的大小。
@@ -280,7 +274,4 @@ public class TimeComplexity {
-----------------------
-* 作者微信:[程序员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 08c15895..6309ef58 100644
--- a/problems/前序/上海互联网公司总结.md
+++ b/problems/前序/上海互联网公司总结.md
@@ -1,11 +1,3 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
# 上海互联网公司总结
@@ -130,7 +122,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/前序/什么是核心代码模式,什么又是ACM模式?.md b/problems/前序/什么是核心代码模式,什么又是ACM模式?.md
index 3c5fb4e4..0b9d230f 100644
--- a/problems/前序/什么是核心代码模式,什么又是ACM模式?.md
+++ b/problems/前序/什么是核心代码模式,什么又是ACM模式?.md
@@ -1,11 +1,5 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+# 什么是核心代码模式,什么又是ACM模式?
现在很多企业都在牛客上进行面试,**很多录友和我反馈说搞不懂牛客上输入代码的ACM模式**。
@@ -119,7 +113,4 @@ int main() {
-----------------------
-* 作者微信:[程序员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 b48665e5..4ab94a51 100644
--- a/problems/前序/代码风格.md
+++ b/problems/前序/代码风格.md
@@ -1,13 +1,3 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
-
-
# 看了这么多代码,谈一谈代码风格!
@@ -142,11 +132,6 @@ Google规范是 大括号和 控制语句保持同一行的,我个人也很认
就酱,以后我还会陆续分享,关于代码,求职,学习工作之类的内容。
-
-
-
-----------------------
-* 作者微信:[程序员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 cfcbed1a..f19984e6 100644
--- a/problems/前序/关于时间复杂度,你不知道的都在这里!.md
+++ b/problems/前序/关于时间复杂度,你不知道的都在这里!.md
@@ -1,11 +1,5 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+# 关于时间复杂度,你不知道的都在这里!
相信每一位录友都接触过时间复杂度,但又对时间复杂度的认识处于一种朦胧的状态,所以是时候对时间复杂度来一个深度的剖析了。
@@ -15,7 +9,7 @@
* 什么是大O
* 不同数据规模的差异
* 复杂表达式的化简
-* $O(\log n)$中的log是以什么为底?
+* O(log n)中的log是以什么为底?
* 举一个例子
@@ -29,21 +23,21 @@
那么该如何估计程序运行时间呢,通常会估算算法的操作单元数量来代表程序消耗的时间,这里默认CPU的每个单元运行消耗的时间都是相同的。
-假设算法的问题规模为n,那么操作单元数量便用函数f(n)来表示,随着数据规模n的增大,算法执行时间的增长率和f(n)的增长率相同,这称作为算法的渐近时间复杂度,简称时间复杂度,记为 $O(f(n)$)。
+假设算法的问题规模为n,那么操作单元数量便用函数f(n)来表示,随着数据规模n的增大,算法执行时间的增长率和f(n)的增长率相同,这称作为算法的渐近时间复杂度,简称时间复杂度,记为 O(f(n))。
## 什么是大O
-这里的大O是指什么呢,说到时间复杂度,**大家都知道$O(n)$,$O(n^2)$,却说不清什么是大O**。
+这里的大O是指什么呢,说到时间复杂度,**大家都知道O(n),O(n^2),却说不清什么是大O**。
算法导论给出的解释:**大O用来表示上界的**,当用它作为算法的最坏情况运行时间的上界,就是对任意数据输入的运行时间的上界。
-同样算法导论给出了例子:拿插入排序来说,插入排序的时间复杂度我们都说是$O(n^2)$ 。
+同样算法导论给出了例子:拿插入排序来说,插入排序的时间复杂度我们都说是O(n^2) 。
-输入数据的形式对程序运算时间是有很大影响的,在数据本来有序的情况下时间复杂度是$O(n)$,但如果数据是逆序的话,插入排序的时间复杂度就是$O(n^2)$,也就对于所有输入情况来说,最坏是$O(n^2)$ 的时间复杂度,所以称插入排序的时间复杂度为$O(n^2)$。
+输入数据的形式对程序运算时间是有很大影响的,在数据本来有序的情况下时间复杂度是O(n),但如果数据是逆序的话,插入排序的时间复杂度就是O(n^2),也就对于所有输入情况来说,最坏是O(n^2) 的时间复杂度,所以称插入排序的时间复杂度为O(n^2)。
-同样的同理再看一下快速排序,都知道快速排序是$O(n\log n)$,但是当数据已经有序情况下,快速排序的时间复杂度是$O(n^2)$ 的,**所以严格从大O的定义来讲,快速排序的时间复杂度应该是$O(n^2)$**。
+同样的同理再看一下快速排序,都知道快速排序是O(nlogn),但是当数据已经有序情况下,快速排序的时间复杂度是O(n^2) 的,**所以严格从大O的定义来讲,快速排序的时间复杂度应该是O(n^2)**。
-**但是我们依然说快速排序是$O(n\log n)$的时间复杂度,这个就是业内的一个默认规定,这里说的O代表的就是一般情况,而不是严格的上界**。如图所示:
+**但是我们依然说快速排序是O(nlogn)的时间复杂度,这个就是业内的一个默认规定,这里说的O代表的就是一般情况,而不是严格的上界**。如图所示:

我们主要关心的还是一般情况下的数据形式。
@@ -57,11 +51,11 @@

-在决定使用哪些算法的时候,不是时间复杂越低的越好(因为简化后的时间复杂度忽略了常数项等等),要考虑数据规模,如果数据规模很小甚至可以用$O(n^2)$的算法比$O(n)$的更合适(在有常数项的时候)。
+在决定使用哪些算法的时候,不是时间复杂越低的越好(因为简化后的时间复杂度忽略了常数项等等),要考虑数据规模,如果数据规模很小甚至可以用O(n^2)的算法比O(n)的更合适(在有常数项的时候)。
-就像上图中 $O(5n^2)$ 和 $O(100n)$ 在n为20之前 很明显 $O(5n^2)$是更优的,所花费的时间也是最少的。
+就像上图中 O(5n^2) 和 O(100n) 在n为20之前 很明显 O(5n^2)是更优的,所花费的时间也是最少的。
-那为什么在计算时间复杂度的时候要忽略常数项系数呢,也就说$O(100n)$ 就是$O(n)$的时间复杂度,$O(5n^2)$ 就是$O(n^2)$的时间复杂度,而且要默认$O(n)$ 优于$O(n^2)$ 呢 ?
+那为什么在计算时间复杂度的时候要忽略常数项系数呢,也就说O(100n) 就是O(n)的时间复杂度,O(5n^2) 就是O(n^2)的时间复杂度,而且要默认O(n) 优于O(n^2) 呢 ?
这里就又涉及到大O的定义,**因为大O就是数据量级突破一个点且数据量级非常大的情况下所表现出的时间复杂度,这个数据量也就是常数项系数已经不起决定性作用的数据量**。
@@ -69,13 +63,13 @@
**所以我们说的时间复杂度都是省略常数项系数的,是因为一般情况下都是默认数据规模足够的大,基于这样的事实,给出的算法时间复杂的的一个排行如下所示**:
-O(1)常数阶 < $O(\log n)$对数阶 < $O(n)$线性阶 < $O(n^2)$平方阶 < $O(n^3)$立方阶 < $O(2^n)$指数阶
+O(1)常数阶 < O(logn)对数阶 < O(n)线性阶 < O(n^2)平方阶 < O(n^3)立方阶 < O(2^n)指数阶
但是也要注意大常数,如果这个常数非常大,例如10^7 ,10^9 ,那么常数就是不得不考虑的因素了。
## 复杂表达式的化简
-有时候我们去计算时间复杂度的时候发现不是一个简单的$O(n)$ 或者$O(n^2)$, 而是一个复杂的表达式,例如:
+有时候我们去计算时间复杂度的时候发现不是一个简单的O(n) 或者O(n^2), 而是一个复杂的表达式,例如:
```
O(2*n^2 + 10*n + 1000)
@@ -101,19 +95,19 @@ O(n^2 + n)
O(n^2)
```
-如果这一步理解有困难,那也可以做提取n的操作,变成$O(n(n+1)$) ,省略加法常数项后也就别变成了:
+如果这一步理解有困难,那也可以做提取n的操作,变成O(n(n+1)) ,省略加法常数项后也就别变成了:
```
O(n^2)
```
-所以最后我们说:这个算法的算法时间复杂度是$O(n^2)$ 。
+所以最后我们说:这个算法的算法时间复杂度是O(n^2) 。
-也可以用另一种简化的思路,其实当n大于40的时候, 这个复杂度会恒小于$O(3 × n^2)$,
-$O(2 × n^2 + 10 × n + 1000) < O(3 × n^2)$,所以说最后省略掉常数项系数最终时间复杂度也是$O(n^2)$。
+也可以用另一种简化的思路,其实当n大于40的时候, 这个复杂度会恒小于O(3 × n^2),
+O(2 × n^2 + 10 × n + 1000) < O(3 × n^2),所以说最后省略掉常数项系数最终时间复杂度也是O(n^2)。
-## $O(\log n)$中的log是以什么为底?
+## O(logn)中的log是以什么为底?
平时说这个算法的时间复杂度是logn的,那么一定是log 以2为底n的对数么?
@@ -136,21 +130,21 @@ $O(2 × n^2 + 10 × n + 1000) < O(3 × n^2)$,所以说最后省略掉常数项
通过这道面试题目,来分析一下时间复杂度。题目描述:找出n个字符串中相同的两个字符串(假设这里只有两个相同的字符串)。
-如果是暴力枚举的话,时间复杂度是多少呢,是$O(n^2)$么?
+如果是暴力枚举的话,时间复杂度是多少呢,是O(n^2)么?
-这里一些同学会忽略了字符串比较的时间消耗,这里并不像int 型数字做比较那么简单,除了n^2 次的遍历次数外,字符串比较依然要消耗m次操作(m也就是字母串的长度),所以时间复杂度是$O(m × n × n)$。
+这里一些同学会忽略了字符串比较的时间消耗,这里并不像int 型数字做比较那么简单,除了n^2 次的遍历次数外,字符串比较依然要消耗m次操作(m也就是字母串的长度),所以时间复杂度是O(m × n × n)。
接下来再想一下其他解题思路。
先排对n个字符串按字典序来排序,排序后n个字符串就是有序的,意味着两个相同的字符串就是挨在一起,然后在遍历一遍n个字符串,这样就找到两个相同的字符串了。
-那看看这种算法的时间复杂度,快速排序时间复杂度为$O(n\log n)$,依然要考虑字符串的长度是m,那么快速排序每次的比较都要有m次的字符比较的操作,就是$O(m × n × \log n)$ 。
+那看看这种算法的时间复杂度,快速排序时间复杂度为O(nlogn),依然要考虑字符串的长度是m,那么快速排序每次的比较都要有m次的字符比较的操作,就是O(m × n × log n) 。
-之后还要遍历一遍这n个字符串找出两个相同的字符串,别忘了遍历的时候依然要比较字符串,所以总共的时间复杂度是 $O(m × n × \log n + n × m)$。
+之后还要遍历一遍这n个字符串找出两个相同的字符串,别忘了遍历的时候依然要比较字符串,所以总共的时间复杂度是 O(m × n × logn + n × m)。
-我们对$O(m × n × \log n + n × m)$ 进行简化操作,把$m × n$提取出来变成 $O(m × n × (\log n + 1)$),再省略常数项最后的时间复杂度是 $O(m × n × \log n)$。
+我们对O(m × n × log n + n × m) 进行简化操作,把m × n提取出来变成 O(m × n × (logn + 1)),再省略常数项最后的时间复杂度是 O(m × n × log n)。
-最后很明显$O(m × n × \log n)$ 要优于$O(m × n × n)$!
+最后很明显O(m × n × logn) 要优于O(m × n × n)!
所以先把字符串集合排序再遍历一遍找到两个相同字符串的方法要比直接暴力枚举的方式更快。
@@ -170,7 +164,4 @@ $O(2 × n^2 + 10 × n + 1000) < O(3 × n^2)$,所以说最后省略掉常数项
-----------------------
-* 作者微信:[程序员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 95ffe597..19384fd9 100644
--- a/problems/前序/关于空间复杂度,可能有几个疑问?.md
+++ b/problems/前序/关于空间复杂度,可能有几个疑问?.md
@@ -1,24 +1,16 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
# 空间复杂度分析
* [关于时间复杂度,你不知道的都在这里!](https://programmercarl.com/前序/关于时间复杂度,你不知道的都在这里!.html)
-* [$O(n)$的算法居然超时了,此时的n究竟是多大?](https://programmercarl.com/前序/On的算法居然超时了,此时的n究竟是多大?.html)
+* [O(n)的算法居然超时了,此时的n究竟是多大?](https://programmercarl.com/前序/On的算法居然超时了,此时的n究竟是多大?.html)
* [通过一道面试题目,讲一讲递归算法的时间复杂度!](https://programmercarl.com/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.html)
那么一直还没有讲空间复杂度,所以打算陆续来补上,内容不难,大家可以读一遍文章就有整体的了解了。
什么是空间复杂度呢?
-是对一个算法在运行过程中占用内存空间大小的量度,记做$S(n)=O(f(n)$。
+是对一个算法在运行过程中占用内存空间大小的量度,记做S(n)=O(f(n)。
空间复杂度(Space Complexity)记作S(n) 依然使用大O来表示。利用程序的空间复杂度,可以对程序运行中需要多少内存有个预先估计。
@@ -49,11 +41,11 @@ for (int i = 0; i < n; i++) {
}
```
-第一段代码可以看出,随着n的变化,所需开辟的内存空间并不会随着n的变化而变化。即此算法空间复杂度为一个常量,所以表示为大$O(1)$。
+第一段代码可以看出,随着n的变化,所需开辟的内存空间并不会随着n的变化而变化。即此算法空间复杂度为一个常量,所以表示为大O(1)。
-什么时候的空间复杂度是$O(n)$?
+什么时候的空间复杂度是O(n)?
-当消耗空间和输入参数n保持线性增长,这样的空间复杂度为$O(n)$,来看一下这段C++代码
+当消耗空间和输入参数n保持线性增长,这样的空间复杂度为O(n),来看一下这段C++代码
```CPP
int* a = new int(n);
for (int i = 0; i < n; i++) {
@@ -61,9 +53,9 @@ for (int i = 0; i < n; i++) {
}
```
-我们定义了一个数组出来,这个数组占用的大小为n,虽然有一个for循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,随着n的增大,开辟的内存大小呈线性增长,即 $O(n)$。
+我们定义了一个数组出来,这个数组占用的大小为n,虽然有一个for循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,随着n的增大,开辟的内存大小呈线性增长,即 O(n)。
-其他的 $O(n^2)$, $O(n^3)$ 我想大家应该都可以以此例举出来了,**那么思考一下 什么时候空间复杂度是 $O(\log n)$呢?**
+其他的 O(n^2), O(n^3) 我想大家应该都可以以此例举出来了,**那么思考一下 什么时候空间复杂度是 O(logn)呢?**
空间复杂度是logn的情况确实有些特殊,其实是在**递归的时候,会出现空间复杂度为logn的情况**。
@@ -73,7 +65,4 @@ for (int i = 0; i < n; 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/前序/刷了这么多题,你了解自己代码的内存消耗么?.md b/problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md
index 3fccfb22..f4aa4b6e 100644
--- a/problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md
+++ b/problems/前序/刷了这么多题,你了解自己代码的内存消耗么?.md
@@ -1,13 +1,6 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
+# 刷了这么多题,你了解自己代码的内存消耗么?
理解代码的内存消耗,最关键是要知道自己所用编程语言的内存管理。
@@ -150,7 +143,4 @@ char型的数据和int型的数据挨在一起,该int数据从地址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/前序/力扣上的代码想在本地编译运行?.md b/problems/前序/力扣上的代码想在本地编译运行?.md
index c4899a20..bcef3886 100644
--- a/problems/前序/力扣上的代码想在本地编译运行?.md
+++ b/problems/前序/力扣上的代码想在本地编译运行?.md
@@ -1,10 +1,4 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+# 力扣上的代码想在本地编译运行?
很多录友都问过我一个问题,就是力扣上的代码如何在本地编译运行?
@@ -67,7 +61,4 @@ int main() {
-----------------------
-* 作者微信:[程序员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 0e22dad6..a10cab06 100644
--- a/problems/前序/北京互联网公司总结.md
+++ b/problems/前序/北京互联网公司总结.md
@@ -1,15 +1,8 @@
-
-
-
-
-
-
+# 北京互联网公司总结
+
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
-# 北京互联网公司总结
-
**个人总结难免有所疏忽,欢迎大家补充,公司好坏没有排名哈!**
如果要在北京找工作,这份list可以作为一个大纲,寻找自己合适的公司。
@@ -116,7 +109,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 ae41c899..b8b1641b 100644
--- a/problems/前序/广州互联网公司总结.md
+++ b/problems/前序/广州互联网公司总结.md
@@ -1,19 +1,14 @@
-
-
-
-
-
-
+# 广州互联网公司总结
+
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-# 广州互联网公司总结
-
**个人总结难免有所疏忽,欢迎大家补充,公司好坏没有排名哈!**
## 一线互联网
* 微信(总部) 有点难进!
+* 字节跳动(广州)
## 二线
* 网易(总部)主要是游戏
@@ -79,7 +74,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 d44800cd..f6a575f6 100644
--- a/problems/前序/成都互联网公司总结.md
+++ b/problems/前序/成都互联网公司总结.md
@@ -1,15 +1,7 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
-
-
# 成都互联网公司总结
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
**排名不分先后,个人总结难免有所疏漏,欢迎补充!**
@@ -77,7 +69,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 326a176b..6154cfe5 100644
--- a/problems/前序/杭州互联网公司总结.md
+++ b/problems/前序/杭州互联网公司总结.md
@@ -1,15 +1,8 @@
-
-
-
-
-
-
+# 杭州互联网公司总结
+
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
-# 杭州互联网公司总结
-
**个人总结难免有所疏忽,欢迎大家补充,公司好坏没有排名哈!**
## 一线互联网
@@ -87,7 +80,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 9e089315..3d548abb 100644
--- a/problems/前序/深圳互联网公司总结.md
+++ b/problems/前序/深圳互联网公司总结.md
@@ -1,14 +1,8 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
# 深圳互联网公司总结
+欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
**个人总结难免有所疏忽,欢迎大家补充,公司好坏没有排名哈!**
## 一线互联网
@@ -82,7 +76,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 b76fb036..a2f6ee3b 100644
--- a/problems/前序/程序员写文档工具.md
+++ b/problems/前序/程序员写文档工具.md
@@ -1,16 +1,10 @@
-
-
-
-
-
-
+
+# 程序员应该用什么用具来写文档?
+
欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
-# 程序员应该用什么用具来写文档?
-
Carl平时写东西,都是统一使用markdown,包括题解啊,笔记啊,所以这里给大家安利一波markdown对程序员的重要性!
程序员为什么要学习markdown呢?
@@ -135,9 +129,5 @@ Markdown支持部分html,例如这样
-
-----------------------
-* 作者微信:[程序员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 f47516dc..e64f547a 100644
--- a/problems/前序/程序员简历.md
+++ b/problems/前序/程序员简历.md
@@ -1,16 +1,5 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
-
-
# 程序员的简历应该这么写!!(附简历模板)
-
Carl校招社招都拿过大厂的offer,同时也看过很多应聘者的简历,这里把自己总结的简历技巧以及常见问题给大家梳理一下。
## 简历篇幅
@@ -116,7 +105,7 @@ Carl校招社招都拿过大厂的offer,同时也看过很多应聘者的简

-这里是简历模板中Markdown的代码:https://github.com/youngyangyang04/Markdown-Resume-Template ,可以fork到自己Github仓库上,按照这个模板来修改自己的简历。
+这里是简历模板中Markdown的代码:[https://github.com/youngyangyang04/Markdown-Resume-Template](https://github.com/youngyangyang04/Markdown-Resume-Template) ,可以fork到自己Github仓库上,按照这个模板来修改自己的简历。
**Word版本的简历,大家可以在公众号「代码随想录」后台回复:简历模板,就可以获取!**
@@ -133,7 +122,4 @@ Carl校招社招都拿过大厂的offer,同时也看过很多应聘者的简
-----------------------
-* 作者微信:[程序员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 3f18f9d1..6099747a 100644
--- a/problems/前序/编程素养部分的吹毛求疵.md
+++ b/problems/前序/编程素养部分的吹毛求疵.md
@@ -13,7 +13,7 @@
- 左孩子和右孩子的下标不太好理解。我给出证明过程:
- 如果父节点在第$k$层,第$m,m \in [0,2^k]$个节点,则其左孩子所在的位置必然为$k+1$层,第$2*(m-1)+1$个节点。
+ 如果父节点在第k层,第$m,m \in [0,2^k]$个节点,则其左孩子所在的位置必然为$k+1$层,第$2*(m-1)+1$个节点。
- 计算父节点在数组中的索引:
$$
diff --git a/problems/前序/递归算法的时间与空间复杂度分析.md b/problems/前序/递归算法的时间与空间复杂度分析.md
index 4dd340a6..142358da 100644
--- a/problems/前序/递归算法的时间与空间复杂度分析.md
+++ b/problems/前序/递归算法的时间与空间复杂度分析.md
@@ -1,11 +1,3 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
# 递归算法的时间与空间复杂度分析!
@@ -34,7 +26,7 @@ int fibonacci(int i) {
在讲解递归时间复杂度的时候,我们提到了递归算法的时间复杂度本质上是要看: **递归的次数 * 每次递归的时间复杂度**。
-可以看出上面的代码每次递归都是$O(1)$的操作。再来看递归了多少次,这里将i为5作为输入的递归过程 抽象成一棵递归树,如图:
+可以看出上面的代码每次递归都是O(1)的操作。再来看递归了多少次,这里将i为5作为输入的递归过程 抽象成一棵递归树,如图:

@@ -44,7 +36,7 @@ int fibonacci(int i) {
我们之前也有说到,一棵深度(按根节点深度为1)为k的二叉树最多可以有 2^k - 1 个节点。
-所以该递归算法的时间复杂度为$O(2^n)$,这个复杂度是非常大的,随着n的增大,耗时是指数上升的。
+所以该递归算法的时间复杂度为O(2^n),这个复杂度是非常大的,随着n的增大,耗时是指数上升的。
来做一个实验,大家可以有一个直观的感受。
@@ -93,7 +85,7 @@ int main()
* n = 40,耗时:837 ms
* n = 50,耗时:110306 ms
-可以看出,$O(2^n)$这种指数级别的复杂度是非常大的。
+可以看出,O(2^n)这种指数级别的复杂度是非常大的。
所以这种求斐波那契数的算法看似简洁,其实时间复杂度非常高,一般不推荐这样来实现斐波那契。
@@ -127,14 +119,14 @@ int fibonacci(int first, int second, int n) {
这里相当于用first和second来记录当前相加的两个数值,此时就不用两次递归了。
-因为每次递归的时候n减1,即只是递归了n次,所以时间复杂度是 $O(n)$。
+因为每次递归的时候n减1,即只是递归了n次,所以时间复杂度是 O(n)。
-同理递归的深度依然是n,每次递归所需的空间也是常数,所以空间复杂度依然是$O(n)$。
+同理递归的深度依然是n,每次递归所需的空间也是常数,所以空间复杂度依然是O(n)。
代码(版本二)的复杂度如下:
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
此时再来测一下耗时情况验证一下:
@@ -206,7 +198,7 @@ int main()
递归第n个斐波那契数的话,递归调用栈的深度就是n。
-那么每次递归的空间复杂度是$O(1)$, 调用栈深度为n,所以这段递归代码的空间复杂度就是$O(n)$。
+那么每次递归的空间复杂度是O(1), 调用栈深度为n,所以这段递归代码的空间复杂度就是O(n)。
```CPP
int fibonacci(int i) {
@@ -241,24 +233,24 @@ int binary_search( int arr[], int l, int r, int x) {
}
```
-都知道二分查找的时间复杂度是$O(\log n)$,那么递归二分查找的空间复杂度是多少呢?
+都知道二分查找的时间复杂度是O(logn),那么递归二分查找的空间复杂度是多少呢?
我们依然看 **每次递归的空间复杂度和递归的深度**
每次递归的空间复杂度可以看出主要就是参数里传入的这个arr数组,但需要注意的是在C/C++中函数传递数组参数,不是整个数组拷贝一份传入函数而是传入的数组首元素地址。
-**也就是说每一层递归都是公用一块数组地址空间的**,所以 每次递归的空间复杂度是常数即:$O(1)$。
+**也就是说每一层递归都是公用一块数组地址空间的**,所以 每次递归的空间复杂度是常数即:O(1)。
-再来看递归的深度,二分查找的递归深度是logn ,递归深度就是调用栈的长度,那么这段代码的空间复杂度为 $1 * logn = O(logn)$。
+再来看递归的深度,二分查找的递归深度是logn ,递归深度就是调用栈的长度,那么这段代码的空间复杂度为 1 * logn = O(logn)。
-大家要注意自己所用的语言在传递函数参数的时,是拷贝整个数值还是拷贝地址,如果是拷贝整个数值那么该二分法的空间复杂度就是$O(n\log n)$。
+大家要注意自己所用的语言在传递函数参数的时,是拷贝整个数值还是拷贝地址,如果是拷贝整个数值那么该二分法的空间复杂度就是O(nlogn)。
## 总结
本章我们详细分析了递归实现的求斐波那契和二分法的空间复杂度,同时也对时间复杂度做了分析。
-特别是两种递归实现的求斐波那契数列,其时间复杂度截然不容,我们还做了实验,验证了时间复杂度为$O(2^n)$是非常耗时的。
+特别是两种递归实现的求斐波那契数列,其时间复杂度截然不容,我们还做了实验,验证了时间复杂度为O(2^n)是非常耗时的。
通过本篇大家应该对递归算法的时间复杂度和空间复杂度有更加深刻的理解了。
@@ -269,7 +261,4 @@ int binary_search( int arr[], int l, int r, int x) {
-----------------------
-* 作者微信:[程序员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 8780122f..b2db92f5 100644
--- a/problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md
+++ b/problems/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.md
@@ -1,13 +1,3 @@
-
-
-
-
-
-
-欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
-
-
-
# 通过一道面试题目,讲一讲递归算法的时间复杂度!
@@ -15,13 +5,13 @@
相信很多同学对递归算法的时间复杂度都很模糊,那么这篇来给大家通透的讲一讲。
-**同一道题目,同样使用递归算法,有的同学会写出了$O(n)$的代码,有的同学就写出了$O(\log n)$的代码**。
+**同一道题目,同样使用递归算法,有的同学会写出了O(n)的代码,有的同学就写出了O(logn)的代码**。
这是为什么呢?
如果对递归的时间复杂度理解的不够深入的话,就会这样!
-那么我通过一道简单的面试题,模拟面试的场景,来带大家逐步分析递归算法的时间复杂度,最后找出最优解,来看看同样是递归,怎么就写成了$O(n)$的代码。
+那么我通过一道简单的面试题,模拟面试的场景,来带大家逐步分析递归算法的时间复杂度,最后找出最优解,来看看同样是递归,怎么就写成了O(n)的代码。
面试题:求x的n次方
@@ -36,7 +26,7 @@ int function1(int x, int n) {
return result;
}
```
-时间复杂度为$O(n)$,此时面试官会说,有没有效率更好的算法呢。
+时间复杂度为O(n),此时面试官会说,有没有效率更好的算法呢。
**如果此时没有思路,不要说:我不会,我不知道了等等**。
@@ -54,11 +44,11 @@ int function2(int x, int n) {
```
面试官问:“那么这个代码的时间复杂度是多少?”。
-一些同学可能一看到递归就想到了$O(\log n)$,其实并不是这样,递归算法的时间复杂度本质上是要看: **递归的次数 * 每次递归中的操作次数**。
+一些同学可能一看到递归就想到了O(log n),其实并不是这样,递归算法的时间复杂度本质上是要看: **递归的次数 * 每次递归中的操作次数**。
那再来看代码,这里递归了几次呢?
-每次n-1,递归了n次时间复杂度是$O(n)$,每次进行了一个乘法操作,乘法操作的时间复杂度一个常数项$O(1)$,所以这份代码的时间复杂度是 $n × 1 = O(n)$。
+每次n-1,递归了n次时间复杂度是O(n),每次进行了一个乘法操作,乘法操作的时间复杂度一个常数项O(1),所以这份代码的时间复杂度是 n × 1 = O(n)。
这个时间复杂度就没有达到面试官的预期。于是又写出了如下的递归算法的代码:
@@ -91,11 +81,11 @@ int function3(int x, int n) {

-**时间复杂度忽略掉常数项`-1`之后,这个递归算法的时间复杂度依然是$O(n)$**。对,你没看错,依然是$O(n)$的时间复杂度!
+**时间复杂度忽略掉常数项`-1`之后,这个递归算法的时间复杂度依然是O(n)**。对,你没看错,依然是O(n)的时间复杂度!
-此时面试官就会说:“这个递归的算法依然还是$O(n)$啊”, 很明显没有达到面试官的预期。
+此时面试官就会说:“这个递归的算法依然还是O(n)啊”, 很明显没有达到面试官的预期。
-那么$O(\log n)$的递归算法应该怎么写呢?
+那么O(logn)的递归算法应该怎么写呢?
想一想刚刚给出的那份递归算法的代码,是不是有哪里比较冗余呢,其实有重复计算的部分。
@@ -118,7 +108,7 @@ int function4(int x, int n) {
依然还是看他递归了多少次,可以看到这里仅仅有一个递归调用,且每次都是n/2 ,所以这里我们一共调用了log以2为底n的对数次。
-**每次递归了做都是一次乘法操作,这也是一个常数项的操作,那么这个递归算法的时间复杂度才是真正的$O(\log n)$**。
+**每次递归了做都是一次乘法操作,这也是一个常数项的操作,那么这个递归算法的时间复杂度才是真正的O(logn)**。
此时大家最后写出了这样的代码并且将时间复杂度分析的非常清晰,相信面试官是比较满意的。
@@ -126,11 +116,11 @@ int function4(int x, int n) {
对于递归的时间复杂度,毕竟初学者有时候会迷糊,刷过很多题的老手依然迷糊。
-**本篇我用一道非常简单的面试题目:求x的n次方,来逐步分析递归算法的时间复杂度,注意不要一看到递归就想到了$O(\log n)$!**
+**本篇我用一道非常简单的面试题目:求x的n次方,来逐步分析递归算法的时间复杂度,注意不要一看到递归就想到了O(logn)!**
-同样使用递归,有的同学可以写出$O(\log n)$的代码,有的同学还可以写出$O(n)$的代码。
+同样使用递归,有的同学可以写出O(logn)的代码,有的同学还可以写出O(n)的代码。
-对于function3 这样的递归实现,很容易让人感觉这是$O(\log n)$的时间复杂度,其实这是$O(n)$的算法!
+对于function3 这样的递归实现,很容易让人感觉这是O(log n)的时间复杂度,其实这是O(n)的算法!
```CPP
int function3(int x, int n) {
@@ -152,7 +142,4 @@ int function3(int x, int 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/剑指Offer05.替换空格.md b/problems/剑指Offer05.替换空格.md
index d0f382c8..037bd427 100644
--- a/problems/剑指Offer05.替换空格.md
+++ b/problems/剑指Offer05.替换空格.md
@@ -29,7 +29,7 @@ i指向新长度的末尾,j指向旧长度的末尾。
有同学问了,为什么要从后向前填充,从前向后填充不行么?
-从前向后填充就是$O(n^2)$的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。
+从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。
**其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。**
@@ -74,8 +74,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(1)
此时算上本题,我们已经做了七道双指针相关的题目了分别是:
@@ -121,6 +121,37 @@ for (int i = 0; i < a.size(); i++) {
## 其他语言版本
+C:
+```C
+char* replaceSpace(char* s){
+ //统计空格数量
+ int count = 0;
+ int len = strlen(s);
+ for (int i = 0; i < len; i++) {
+ if (s[i] == ' ') {
+ count++;
+ }
+ }
+
+ //为新数组分配空间
+ int newLen = len + count * 2;
+ char* result = malloc(sizeof(char) * newLen + 1);
+ //填充新数组并替换空格
+ for (int i = len - 1, j = newLen - 1; i >= 0; i--, j--) {
+ if (s[i] != ' ') {
+ result[j] = s[i];
+ } else {
+ result[j--] = '0';
+ result[j--] = '2';
+ result[j] = '%';
+ }
+ }
+ result[newLen] = '\0';
+
+ return result;
+}
+```
+
Java:
```Java
@@ -260,8 +291,24 @@ class Solution:
```
+```python
+class Solution:
+ def replaceSpace(self, s: str) -> str:
+ # method 1 - Very rude
+ return "%20".join(s.split(" "))
+
+ # method 2 - Reverse the s when counting in for loop, then update from the end.
+ n = len(s)
+ for e, i in enumerate(s[::-1]):
+ print(i, e)
+ if i == " ":
+ s = s[: n - (e + 1)] + "%20" + s[n - e:]
+ print("")
+ return s
+```
javaScript:
+
```js
/**
* @param {string} s
@@ -298,6 +345,33 @@ javaScript:
};
```
+TypeScript:
+
+```typescript
+function replaceSpace(s: string): string {
+ let arr: string[] = s.split('');
+ let spaceNum: number = 0;
+ let oldLength: number = arr.length;
+ for (let i = 0; i < oldLength; i++) {
+ if (arr[i] === ' ') {
+ spaceNum++;
+ }
+ }
+ arr.length = oldLength + 2 * spaceNum;
+ let cur: number = oldLength - 1;
+ for (let i = arr.length - 1; i >= 0; i--, cur--) {
+ if (arr[cur] !== ' ') {
+ arr[i] = arr[cur]
+ } else {
+ arr[i] = '0';
+ arr[--i] = '2';
+ arr[--i] = '%';
+ }
+ }
+ return arr.join('');
+};
+```
+
Swift:
```swift
diff --git a/problems/剑指Offer58-II.左旋转字符串.md b/problems/剑指Offer58-II.左旋转字符串.md
index c391d661..fec83e1d 100644
--- a/problems/剑指Offer58-II.左旋转字符串.md
+++ b/problems/剑指Offer58-II.左旋转字符串.md
@@ -86,7 +86,7 @@ public:
# 题外话
一些同学热衷于使用substr,来做这道题。
-其实使用substr 和 反转 时间复杂度是一样的 ,都是$O(n)$,但是使用substr申请了额外空间,所以空间复杂度是$O(n)$,而反转方法的空间复杂度是$O(1)$。
+其实使用substr 和 反转 时间复杂度是一样的 ,都是O(n),但是使用substr申请了额外空间,所以空间复杂度是O(n),而反转方法的空间复杂度是O(1)。
**如果想让这套题目有意义,就不要申请额外空间。**
@@ -209,6 +209,61 @@ var reverseLeftWords = function(s, n) {
};
```
+版本二(在原字符串上操作):
+
+```js
+/**
+ * @param {string} s
+ * @param {number} n
+ * @return {string}
+ */
+var reverseLeftWords = function (s, n) {
+ /** Utils */
+ function reverseWords(strArr, start, end) {
+ let temp;
+ while (start < end) {
+ temp = strArr[start];
+ strArr[start] = strArr[end];
+ strArr[end] = temp;
+ start++;
+ end--;
+ }
+ }
+ /** Main code */
+ let strArr = s.split('');
+ let length = strArr.length;
+ reverseWords(strArr, 0, length - 1);
+ reverseWords(strArr, 0, length - n - 1);
+ reverseWords(strArr, length - n, length - 1);
+ return strArr.join('');
+};
+```
+
+TypeScript:
+
+```typescript
+function reverseLeftWords(s: string, n: number): string {
+ /** Utils */
+ function reverseWords(strArr: string[], start: number, end: number): void {
+ let temp: string;
+ while (start < end) {
+ temp = strArr[start];
+ strArr[start] = strArr[end];
+ strArr[end] = temp;
+ start++;
+ end--;
+ }
+ }
+ /** Main code */
+ let strArr: string[] = s.split('');
+ let length: number = strArr.length;
+ reverseWords(strArr, 0, length - 1);
+ reverseWords(strArr, 0, length - n - 1);
+ reverseWords(strArr, length - n, length - 1);
+ return strArr.join('');
+};
+```
+
Swift:
```swift
diff --git a/problems/动态规划-股票问题总结篇.md b/problems/动态规划-股票问题总结篇.md
index e1fb477b..47a9b34b 100644
--- a/problems/动态规划-股票问题总结篇.md
+++ b/problems/动态规划-股票问题总结篇.md
@@ -72,8 +72,8 @@ public:
}
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
使用滚动数组,代码如下:
@@ -95,8 +95,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(1)
## 买卖股票的最佳时机II
@@ -121,8 +121,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(1)
【动态规划】
@@ -162,8 +162,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
## 买卖股票的最佳时机III
@@ -226,8 +226,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n × 5)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n × 5)
当然,大家可以看到力扣官方题解里的一种优化空间写法,我这里给出对应的C++版本:
@@ -251,8 +251,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(1)
**这种写法看上去简单,其实思路很绕,不建议大家这么写,这么思考,很容易把自己绕进去!** 对于本题,把版本一的写法研究明白,足以!
@@ -404,8 +404,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
## 买卖股票的最佳时机含手续费
@@ -456,8 +456,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
## 总结
diff --git a/problems/动态规划总结篇.md b/problems/动态规划总结篇.md
index 699d4435..cc973b23 100644
--- a/problems/动态规划总结篇.md
+++ b/problems/动态规划总结篇.md
@@ -118,7 +118,7 @@

-这个图是 [代码随想录知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) 成员:[青](https://wx.zsxq.com/dweb2/index/footprint/185251215558842),所画,总结的非常好,分享给大家。
+这个图是 [代码随想录知识星球](https://programmercarl.com/other/kstar.html) 成员:[青](https://wx.zsxq.com/dweb2/index/footprint/185251215558842),所画,总结的非常好,分享给大家。
这已经是全网对动规最深刻的讲解系列了。
diff --git a/problems/动态规划理论基础.md b/problems/动态规划理论基础.md
index e94295a5..66971fce 100644
--- a/problems/动态规划理论基础.md
+++ b/problems/动态规划理论基础.md
@@ -16,7 +16,7 @@
所以动态规划中每一个状态一定是由上一个状态推导出来的,**这一点就区分于贪心**,贪心没有状态推导,而是从局部直接选最优的,
-在[关于贪心算法,你该了解这些!](https://mp.weixin.qq.com/s/A9MHJi1a5uugFaqp8QJFWg)中我举了一个背包问题的例子。
+在[关于贪心算法,你该了解这些!](https://programmercarl.com/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html)中我举了一个背包问题的例子。
例如:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品只能用一次**,求解将哪些物品装入背包里物品价值总和最大。
diff --git a/problems/双指针总结.md b/problems/双指针总结.md
index e866aa66..06752cac 100644
--- a/problems/双指针总结.md
+++ b/problems/双指针总结.md
@@ -22,7 +22,7 @@ for (int i = 0; i < array.size(); i++) {
}
```
-这个代码看上去好像是$O(n)$的时间复杂度,其实是$O(n^2)$的时间复杂度,因为erase操作也是$O(n)$的操作。
+这个代码看上去好像是O(n)的时间复杂度,其实是O(n^2)的时间复杂度,因为erase操作也是O(n)的操作。
所以此时使用双指针法才展现出效率的优势:**通过两个指针在一个for循环下完成两个for循环的工作。**
@@ -30,7 +30,7 @@ for (int i = 0; i < array.size(); i++) {
在[字符串:这道题目,使用库函数一行代码搞定](https://programmercarl.com/0344.反转字符串.html)中讲解了反转字符串,注意这里强调要原地反转,要不然就失去了题目的意义。
-使用双指针法,**定义两个指针(也可以说是索引下标),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。**,时间复杂度是$O(n)$。
+使用双指针法,**定义两个指针(也可以说是索引下标),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。**,时间复杂度是O(n)。
在[替换空格](https://programmercarl.com/剑指Offer05.替换空格.html) 中介绍使用双指针填充字符串的方法,如果想把这道题目做到极致,就不要只用额外的辅助空间了!
@@ -38,13 +38,13 @@ for (int i = 0; i < array.size(); i++) {
有同学问了,为什么要从后向前填充,从前向后填充不行么?
-从前向后填充就是$O(n^2)$的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。
+从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。
**其实很多数组(字符串)填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。**
-那么在[字符串:花式反转还不够!](https://programmercarl.com/0151.翻转字符串里的单词.html)中,我们使用双指针法,用$O(n)$的时间复杂度完成字符串删除类的操作,因为题目要产出冗余空格。
+那么在[字符串:花式反转还不够!](https://programmercarl.com/0151.翻转字符串里的单词.html)中,我们使用双指针法,用O(n)的时间复杂度完成字符串删除类的操作,因为题目要产出冗余空格。
-**在删除冗余空格的过程中,如果不注意代码效率,很容易写成了$O(n^2)$的时间复杂度。其实使用双指针法$O(n)$就可以搞定。**
+**在删除冗余空格的过程中,如果不注意代码效率,很容易写成了O(n^2)的时间复杂度。其实使用双指针法O(n)就可以搞定。**
**主要还是大家用erase用的比较随意,一定要注意for循环下用erase的情况,一般可以用双指针写效率更高!**
@@ -74,22 +74,22 @@ for (int i = 0; i < array.size(); i++) {
去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
-时间复杂度可以做到$O(n^2)$,但还是比较费时的,因为不好做剪枝操作。
+时间复杂度可以做到O(n^2),但还是比较费时的,因为不好做剪枝操作。
所以这道题目使用双指针法才是最为合适的,用双指针做这道题目才能就能真正体会到,**通过前后两个指针不算向中间逼近,在一个for循环下完成两个for循环的工作。**
-只用双指针法时间复杂度为$O(n^2)$,但比哈希法的$O(n^2)$效率高得多,哈希法在使用两层for循环的时候,能做的剪枝操作很有限。
+只用双指针法时间复杂度为O(n^2),但比哈希法的O(n^2)效率高得多,哈希法在使用两层for循环的时候,能做的剪枝操作很有限。
在[双指针法:一样的道理,能解决四数之和](https://programmercarl.com/0018.四数之和.html)中,讲到了四数之和,其实思路是一样的,**在三数之和的基础上再套一层for循环,依然是使用双指针法。**
-对于三数之和使用双指针法就是将原本暴力$O(n^3)$的解法,降为$O(n^2)$的解法,四数之和的双指针解法就是将原本暴力$O(n^4)$的解法,降为$O(n^3)$的解法。
+对于三数之和使用双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。
同样的道理,五数之和,n数之和都是在这个基础上累加。
# 总结
-本文中一共介绍了leetcode上九道使用双指针解决问题的经典题目,除了链表一些题目一定要使用双指针,其他题目都是使用双指针来提高效率,一般是将$O(n^2)$的时间复杂度,降为$O(n)$。
+本文中一共介绍了leetcode上九道使用双指针解决问题的经典题目,除了链表一些题目一定要使用双指针,其他题目都是使用双指针来提高效率,一般是将O(n^2)的时间复杂度,降为$O(n)$。
建议大家可以把文中涉及到的题目在好好做一做,琢磨琢磨,基本对双指针法就不在话下了。
diff --git a/problems/周总结/20200927二叉树周末总结.md b/problems/周总结/20200927二叉树周末总结.md
index 60f02205..ff8f67d4 100644
--- a/problems/周总结/20200927二叉树周末总结.md
+++ b/problems/周总结/20200927二叉树周末总结.md
@@ -44,7 +44,7 @@ a->right = NULL;
在介绍前中后序遍历的时候,有递归和迭代(非递归),还有一种牛逼的遍历方式:morris遍历。
-morris遍历是二叉树遍历算法的超强进阶算法,morris遍历可以将非递归遍历中的空间复杂度降为$O(1)$,感兴趣大家就去查一查学习学习,比较小众,面试几乎不会考。我其实也没有研究过,就不做过多介绍了。
+morris遍历是二叉树遍历算法的超强进阶算法,morris遍历可以将非递归遍历中的空间复杂度降为O(1),感兴趣大家就去查一查学习学习,比较小众,面试几乎不会考。我其实也没有研究过,就不做过多介绍了。
## 周二
diff --git a/problems/周总结/20201003二叉树周末总结.md b/problems/周总结/20201003二叉树周末总结.md
index a0b8c2dd..18bbf37f 100644
--- a/problems/周总结/20201003二叉树周末总结.md
+++ b/problems/周总结/20201003二叉树周末总结.md
@@ -34,8 +34,8 @@ public:
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
- bool outside = compare(left->left, right->right); // 左子树:左、 右子树:左 (相对于求对称二叉树,只需改一下这里的顺序)
- bool inside = compare(left->right, right->left); // 左子树:右、 右子树:右
+ bool outside = compare(left->left, right->left); // 左子树:左、 右子树:左 (相对于求对称二叉树,只需改一下这里的顺序)
+ bool inside = compare(left->right, right->right); // 左子树:右、 右子树:右
bool isSame = outside && inside; // 左子树:中、 右子树:中 (逻辑处理)
return isSame;
diff --git a/problems/周总结/20201112回溯周末总结.md b/problems/周总结/20201112回溯周末总结.md
index c61de4bb..af08097b 100644
--- a/problems/周总结/20201112回溯周末总结.md
+++ b/problems/周总结/20201112回溯周末总结.md
@@ -76,7 +76,7 @@
* 空间复杂度:$O(n)$,递归深度为n,所以系统栈所用空间为$O(n)$,每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为$O(n)$。
排列问题分析:
-* 时间复杂度:$O(n!)$,这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * ..... 1 = n!。
+* 时间复杂度:$O(n!)$,这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * ..... 1 = n!。每个叶子节点都会有一个构造全排列填进数组的操作(对应的代码:`result.push_back(path)`),该操作的复杂度为$O(n)$。所以,最终时间复杂度为:n * n!,简化为$O(n!)$。
* 空间复杂度:$O(n)$,和子集问题同理。
组合问题分析:
diff --git a/problems/周总结/20201126贪心周末总结.md b/problems/周总结/20201126贪心周末总结.md
index 02fccc25..e310c0f8 100644
--- a/problems/周总结/20201126贪心周末总结.md
+++ b/problems/周总结/20201126贪心周末总结.md
@@ -41,7 +41,7 @@
一些录友不清楚[贪心算法:分发饼干](https://programmercarl.com/0455.分发饼干.html)中时间复杂度是怎么来的?
-就是快排$O(n\log n)$,遍历$O(n)$,加一起就是还是$O(n\log n)$。
+就是快排O(nlog n),遍历O(n),加一起就是还是O(nlogn)。
## 周三
diff --git a/problems/周总结/20201210复杂度分析周末总结.md b/problems/周总结/20201210复杂度分析周末总结.md
index 1b404bf0..5e5f696d 100644
--- a/problems/周总结/20201210复杂度分析周末总结.md
+++ b/problems/周总结/20201210复杂度分析周末总结.md
@@ -70,9 +70,9 @@
# 周三
-在[$O(n)$的算法居然超时了,此时的n究竟是多大?](https://programmercarl.com/前序/On的算法居然超时了,此时的n究竟是多大?.html)中介绍了大家在leetcode上提交代码经常遇到的一个问题-超时!
+在[O(n)的算法居然超时了,此时的n究竟是多大?](https://programmercarl.com/前序/On的算法居然超时了,此时的n究竟是多大?.html)中介绍了大家在leetcode上提交代码经常遇到的一个问题-超时!
-估计很多录友知道算法超时了,但没有注意过 $O(n)$的算法,如果1s内出结果,这个n究竟是多大?
+估计很多录友知道算法超时了,但没有注意过 O(n)的算法,如果1s内出结果,这个n究竟是多大?
文中从计算机硬件出发,分析计算机的计算性能,然后亲自做实验,整理出数据如下:
@@ -95,7 +95,7 @@
文中给出了四个版本的代码实现,并逐一分析了其时间复杂度。
-此时大家就会发现,同一道题目,同样使用递归算法,有的同学会写出了$O(n)$的代码,有的同学就写出了$O(\log n)$的代码。
+此时大家就会发现,同一道题目,同样使用递归算法,有的同学会写出了O(n)的代码,有的同学就写出了$O(\log n)$的代码。
其本质是要对递归的时间复杂度有清晰的认识,才能运用递归来有效的解决问题!
diff --git a/problems/周总结/20201217贪心周末总结.md b/problems/周总结/20201217贪心周末总结.md
index e9d22d6e..4d12f92a 100644
--- a/problems/周总结/20201217贪心周末总结.md
+++ b/problems/周总结/20201217贪心周末总结.md
@@ -8,7 +8,7 @@
在[贪心算法:加油站](https://programmercarl.com/0134.加油站.html)中给出每一个加油站的汽油和开到这个加油站的消耗,问汽车能不能开一圈。
-这道题目咋眼一看,感觉是一道模拟题,模拟一下汽车从每一个节点出发看看能不能开一圈,时间复杂度是$O(n^2)$。
+这道题目咋眼一看,感觉是一道模拟题,模拟一下汽车从每一个节点出发看看能不能开一圈,时间复杂度是O(n^2)。
即使用模拟这种情况,也挺考察代码技巧的。
diff --git a/problems/周总结/20210225动规周末总结.md b/problems/周总结/20210225动规周末总结.md
index 0bf9dbdb..21cc53ad 100644
--- a/problems/周总结/20210225动规周末总结.md
+++ b/problems/周总结/20210225动规周末总结.md
@@ -211,8 +211,8 @@ public:
};
```
-* 时间复杂度:$O(n^2)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n^2)
+* 空间复杂度:O(1)
贪心解法代码如下:
@@ -233,8 +233,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(1)
动规解法,版本一,代码如下:
@@ -256,8 +256,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(n)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(n)
从递推公式可以看出,dp[i]只是依赖于dp[i - 1]的状态。
@@ -282,8 +282,8 @@ public:
};
```
-* 时间复杂度:$O(n)$
-* 空间复杂度:$O(1)$
+* 时间复杂度:O(n)
+* 空间复杂度:O(1)
建议先写出版本一,然后在版本一的基础上优化成版本二,而不是直接就写出版本二。
diff --git a/problems/哈希表理论基础.md b/problems/哈希表理论基础.md
index 40a8d0ca..3b6c5ce5 100644
--- a/problems/哈希表理论基础.md
+++ b/problems/哈希表理论基础.md
@@ -22,7 +22,7 @@
例如要查询一个名字是否在这所学校里。
-要枚举的话时间复杂度是$O(n)$,但如果使用哈希表的话, 只需要$O(1)$就可以做到。
+要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
我们只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。
@@ -88,17 +88,17 @@
|集合 |底层实现 | 是否有序 |数值是否可以重复 | 能否更改数值|查询效率 |增删效率|
|---|---| --- |---| --- | --- | ---|
-|std::set |红黑树 |有序 |否 |否 | $O(\log n)$|$O(\log n)$ |
-|std::multiset | 红黑树|有序 |是 | 否| $O(\log n)$ |$O(\log n)$ |
-|std::unordered_set |哈希表 |无序 |否 |否 |$O(1)$ | $O(1)$|
+|std::set |红黑树 |有序 |否 |否 | O(log n)|O(log n) |
+|std::multiset | 红黑树|有序 |是 | 否| O(logn) |O(logn) |
+|std::unordered_set |哈希表 |无序 |否 |否 |O(1) | O(1)|
std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
|映射 |底层实现 | 是否有序 |数值是否可以重复 | 能否更改数值|查询效率 |增删效率|
|---|---| --- |---| --- | --- | ---|
-|std::map |红黑树 |key有序 |key不可重复 |key不可修改 | $O(\log n)$|$O(\log n)$ |
-|std::multimap | 红黑树|key有序 | key可重复 | key不可修改|$O(\log n)$ |$O(\log n)$ |
-|std::unordered_map |哈希表 | key无序 |key不可重复 |key不可修改 |$O(1)$ | $O(1)$|
+|std::map |红黑树 |key有序 |key不可重复 |key不可修改 | O(logn)|O(logn) |
+|std::multimap | 红黑树|key有序 | key可重复 | key不可修改|O(log n) |O(log n) |
+|std::unordered_map |哈希表 | key无序 |key不可重复 |key不可修改 |O(1) | O(1)|
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。
diff --git a/problems/回溯总结.md b/problems/回溯总结.md
index af171243..54ac485b 100644
--- a/problems/回溯总结.md
+++ b/problems/回溯总结.md
@@ -302,11 +302,11 @@ if (startIndex >= nums.size()) { // 终止条件可以不加
**而使用used数组在时间复杂度上几乎没有额外负担!**
-**使用set去重,不仅时间复杂度高了,空间复杂度也高了**,在[本周小结!(回溯算法系列三)](https://programmercarl.com/周总结/20201112回溯周末总结.html)中分析过,组合,子集,排列问题的空间复杂度都是$O(n)$,但如果使用set去重,空间复杂度就变成了$O(n^2)$,因为每一层递归都有一个set集合,系统栈空间是n,每一个空间都有set集合。
+**使用set去重,不仅时间复杂度高了,空间复杂度也高了**,在[本周小结!(回溯算法系列三)](https://programmercarl.com/周总结/20201112回溯周末总结.html)中分析过,组合,子集,排列问题的空间复杂度都是O(n),但如果使用set去重,空间复杂度就变成了O(n^2),因为每一层递归都有一个set集合,系统栈空间是n,每一个空间都有set集合。
-那有同学可能疑惑 用used数组也是占用$O(n)$的空间啊?
+那有同学可能疑惑 用used数组也是占用O(n)的空间啊?
-used数组可是全局变量,每层与每层之间公用一个used数组,所以空间复杂度是$O(n + n)$,最终空间复杂度还是$O(n)$。
+used数组可是全局变量,每层与每层之间公用一个used数组,所以空间复杂度是O(n + n),最终空间复杂度还是O(n)。
# 重新安排行程(图论额外拓展)
@@ -380,24 +380,24 @@ used数组可是全局变量,每层与每层之间公用一个used数组,所
以下在计算空间复杂度的时候我都把系统栈(不是数据结构里的栈)所占空间算进去。
子集问题分析:
-* 时间复杂度:$O(2^n)$,因为每一个元素的状态无外乎取与不取,所以时间复杂度为$O(2^n)$
-* 空间复杂度:$O(n)$,递归深度为n,所以系统栈所用空间为$O(n)$,每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为$O(n)$
+* 时间复杂度:O(2^n),因为每一个元素的状态无外乎取与不取,所以时间复杂度为O(2^n)
+* 空间复杂度:O(n),递归深度为n,所以系统栈所用空间为O(n),每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为O(n)
排列问题分析:
-* 时间复杂度:$O(n!)$,这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * ..... 1 = n!。
-* 空间复杂度:$O(n)$,和子集问题同理。
+* 时间复杂度:O(n!),这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * ..... 1 = n!。
+* 空间复杂度:O(n),和子集问题同理。
组合问题分析:
-* 时间复杂度:$O(2^n)$,组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
-* 空间复杂度:$O(n)$,和子集问题同理。
+* 时间复杂度:O(2^n),组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
+* 空间复杂度:O(n),和子集问题同理。
N皇后问题分析:
-* 时间复杂度:$O(n!)$ ,其实如果看树形图的话,直觉上是$O(n^n)$,但皇后之间不能见面所以在搜索的过程中是有剪枝的,最差也就是O(n!),n!表示n * (n-1) * .... * 1。
-* 空间复杂度:$O(n)$,和子集问题同理。
+* 时间复杂度:O(n!) ,其实如果看树形图的话,直觉上是O(n^n),但皇后之间不能见面所以在搜索的过程中是有剪枝的,最差也就是O(n!),n!表示n * (n-1) * .... * 1。
+* 空间复杂度:O(n),和子集问题同理。
解数独问题分析:
-* 时间复杂度:$O(9^m)$ , m是'.'的数目。
-* 空间复杂度:$O(n^2)$,递归的深度是n^2
+* 时间复杂度:O(9^m) , m是'.'的数目。
+* 空间复杂度:O(n^2),递归的深度是n^2
**一般说道回溯算法的复杂度,都说是指数级别的时间复杂度,这也算是一个概括吧!**
@@ -432,7 +432,7 @@ N皇后问题分析:

-这个图是 [代码随想录知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) 成员:[莫非毛](https://wx.zsxq.com/dweb2/index/footprint/828844212542),所画,总结的非常好,分享给大家。
+这个图是 [代码随想录知识星球](https://programmercarl.com/other/kstar.html) 成员:[莫非毛](https://wx.zsxq.com/dweb2/index/footprint/828844212542),所画,总结的非常好,分享给大家。
**回溯算法系列正式结束,新的系列终将开始,录友们准备开启新的征程!**
diff --git a/problems/回溯算法去重问题的另一种写法.md b/problems/回溯算法去重问题的另一种写法.md
index 7a601493..f48097e1 100644
--- a/problems/回溯算法去重问题的另一种写法.md
+++ b/problems/回溯算法去重问题的另一种写法.md
@@ -226,11 +226,11 @@ public:
**而使用used数组在时间复杂度上几乎没有额外负担!**
-**使用set去重,不仅时间复杂度高了,空间复杂度也高了**,在[本周小结!(回溯算法系列三)](https://programmercarl.com/周总结/20201112回溯周末总结.html)中分析过,组合,子集,排列问题的空间复杂度都是$O(n)$,但如果使用set去重,空间复杂度就变成了$O(n^2)$,因为每一层递归都有一个set集合,系统栈空间是n,每一个空间都有set集合。
+**使用set去重,不仅时间复杂度高了,空间复杂度也高了**,在[本周小结!(回溯算法系列三)](https://programmercarl.com/周总结/20201112回溯周末总结.html)中分析过,组合,子集,排列问题的空间复杂度都是O(n),但如果使用set去重,空间复杂度就变成了O(n^2),因为每一层递归都有一个set集合,系统栈空间是n,每一个空间都有set集合。
-那有同学可能疑惑 用used数组也是占用$O(n)$的空间啊?
+那有同学可能疑惑 用used数组也是占用O(n)的空间啊?
-used数组可是全局变量,每层与每层之间公用一个used数组,所以空间复杂度是$O(n + n)$,最终空间复杂度还是$O(n)$。
+used数组可是全局变量,每层与每层之间公用一个used数组,所以空间复杂度是O(n + n),最终空间复杂度还是O(n)。
## 总结
@@ -365,6 +365,87 @@ class Solution:
return res
```
+TypeScript:
+
+**90.子集II**
+
+```typescript
+function subsetsWithDup(nums: number[]): number[][] {
+ nums.sort((a, b) => a - b);
+ const resArr: number[][] = [];
+ backTraking(nums, 0, []);
+ return resArr;
+ function backTraking(nums: number[], startIndex: number, route: number[]): void {
+ resArr.push(route.slice());
+ const helperSet: Set = new Set();
+ for (let i = startIndex, length = nums.length; i < length; i++) {
+ if (helperSet.has(nums[i])) continue;
+ helperSet.add(nums[i]);
+ route.push(nums[i]);
+ backTraking(nums, i + 1, route);
+ route.pop();
+ }
+ }
+};
+```
+
+**40. 组合总和 II**
+
+```typescript
+function combinationSum2(candidates: number[], target: number): number[][] {
+ candidates.sort((a, b) => a - b);
+ const resArr: number[][] = [];
+ backTracking(candidates, target, 0, 0, []);
+ return resArr;
+ function backTracking(
+ candidates: number[], target: number,
+ curSum: number, startIndex: number, route: number[]
+ ) {
+ if (curSum > target) return;
+ if (curSum === target) {
+ resArr.push(route.slice());
+ return;
+ }
+ const helperSet: Set = new Set();
+ for (let i = startIndex, length = candidates.length; i < length; i++) {
+ let tempVal: number = candidates[i];
+ if (helperSet.has(tempVal)) continue;
+ helperSet.add(tempVal);
+ route.push(tempVal);
+ backTracking(candidates, target, curSum + tempVal, i + 1, route);
+ route.pop();
+
+ }
+ }
+};
+```
+
+**47. 全排列 II**
+
+```typescript
+function permuteUnique(nums: number[]): number[][] {
+ const resArr: number[][] = [];
+ const usedArr: boolean[] = [];
+ backTracking(nums, []);
+ return resArr;
+ function backTracking(nums: number[], route: number[]): void {
+ if (nums.length === route.length) {
+ resArr.push(route.slice());
+ return;
+ }
+ const usedSet: Set = new Set();
+ for (let i = 0, length = nums.length; i < length; i++) {
+ if (usedArr[i] === true || usedSet.has(nums[i])) continue;
+ usedSet.add(nums[i]);
+ route.push(nums[i]);
+ usedArr[i] = true;
+ backTracking(nums, route);
+ usedArr[i] = false;
+ route.pop();
+ }
+ }
+};
+```
Go:
diff --git a/problems/字符串总结.md b/problems/字符串总结.md
index 469cb743..1993b44b 100644
--- a/problems/字符串总结.md
+++ b/problems/字符串总结.md
@@ -57,15 +57,15 @@ for (int i = 0; i < a.size(); i++) {
在[344.反转字符串](https://programmercarl.com/0344.反转字符串.html) ,我们使用双指针法实现了反转字符串的操作,**双指针法在数组,链表和字符串中很常用。**
-接着在[字符串:替换空格](https://programmercarl.com/剑指Offer05.替换空格.html),同样还是使用双指针法在时间复杂度$O(n)$的情况下完成替换空格。
+接着在[字符串:替换空格](https://programmercarl.com/剑指Offer05.替换空格.html),同样还是使用双指针法在时间复杂度O(n)的情况下完成替换空格。
**其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。**
那么针对数组删除操作的问题,其实在[27. 移除元素](https://programmercarl.com/0027.移除元素.html)中就已经提到了使用双指针法进行移除操作。
-同样的道理在[151.翻转字符串里的单词](https://programmercarl.com/0151.翻转字符串里的单词.html)中我们使用$O(n)$的时间复杂度,完成了删除冗余空格。
+同样的道理在[151.翻转字符串里的单词](https://programmercarl.com/0151.翻转字符串里的单词.html)中我们使用O(n)的时间复杂度,完成了删除冗余空格。
-一些同学会使用for循环里调用库函数erase来移除元素,这其实是$O(n^2)$的操作,因为erase就是$O(n)$的操作,所以这也是典型的不知道库函数的时间复杂度,上来就用的案例了。
+一些同学会使用for循环里调用库函数erase来移除元素,这其实是O(n^2)的操作,因为erase就是O(n)的操作,所以这也是典型的不知道库函数的时间复杂度,上来就用的案例了。
# 反转系列
diff --git a/problems/数组总结篇.md b/problems/数组总结篇.md
index d3d4a1e2..d256298b 100644
--- a/problems/数组总结篇.md
+++ b/problems/数组总结篇.md
@@ -67,8 +67,8 @@
可以使用暴力解法,通过这道题目,如果追求更优的算法,建议试一试用二分法,来解决这道题目
-* 暴力解法时间复杂度:$O(n)$
-* 二分法时间复杂度:$O(\log n)$
+* 暴力解法时间复杂度:O(n)
+* 二分法时间复杂度:O(logn)
在这道题目中我们讲到了**循环不变量原则**,只有在循环中坚持对区间的定义,才能清楚的把握循环中的各种细节。
@@ -81,8 +81,8 @@
双指针法(快慢指针法):**通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。**
-* 暴力解法时间复杂度:$O(n^2)$
-* 双指针时间复杂度:$O(n)$
+* 暴力解法时间复杂度:O(n^2)
+* 双指针时间复杂度:O(n)
这道题目迷惑了不少同学,纠结于数组中的元素为什么不能删除,主要是因为以下两点:
@@ -97,12 +97,12 @@
本题介绍了数组操作中的另一个重要思想:滑动窗口。
-* 暴力解法时间复杂度:$O(n^2)$
-* 滑动窗口时间复杂度:$O(n)$
+* 暴力解法时间复杂度:O(n^2)
+* 滑动窗口时间复杂度:O(n)
本题中,主要要理解滑动窗口如何移动 窗口起始位置,达到动态更新窗口大小的,从而得出长度最小的符合条件的长度。
-**滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将$O(n^2)$的暴力解法降为$O(n)$。**
+**滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。**
如果没有接触过这一类的方法,很难想到类似的解题思路,滑动窗口方法还是很巧妙的。
diff --git a/problems/栈与队列总结.md b/problems/栈与队列总结.md
index 8ec96a29..15093ca7 100644
--- a/problems/栈与队列总结.md
+++ b/problems/栈与队列总结.md
@@ -158,22 +158,5 @@ cd a/b/c/../../
好了,栈与队列我们就总结到这里了,接下来Carl就要带大家开启新的篇章了,大家加油!
-
-
-
-## 其他语言版本
-
-
-Java:
-
-
-Python:
-
-
-Go:
-
-
-
-
-----------------------
diff --git a/problems/栈与队列理论基础.md b/problems/栈与队列理论基础.md
index b9811b29..8c76614f 100644
--- a/problems/栈与队列理论基础.md
+++ b/problems/栈与队列理论基础.md
@@ -15,7 +15,7 @@
那么我这里在列出四个关于栈的问题,大家可以思考一下。以下是以C++为例,相信使用其他编程语言的同学也对应思考一下,自己使用的编程语言里栈和队列是什么样的。
1. C++中stack 是容器么?
-2. 我们使用的stack是属于那个版本的STL?
+2. 我们使用的stack是属于哪个版本的STL?
3. 我们使用的STL中stack是如何实现的?
4. stack 提供迭代器来遍历stack空间么?
@@ -67,7 +67,7 @@ deque是一个双向队列,只要封住一段,只开通另一端就可以实
我们也可以指定vector为栈的底层实现,初始化语句如下:
-```
+```cpp
std::stack > third; // 使用vector为底层容器的栈
```
@@ -77,7 +77,7 @@ std::stack > third; // 使用vector为底层容器的栈
也可以指定list 为起底层实现,初始化queue的语句如下:
-```
+```cpp
std::queue> third; // 定义以list为底层容器的队列
```
diff --git a/problems/根据身高重建队列(vector原理讲解).md b/problems/根据身高重建队列(vector原理讲解).md
index 11a72e2d..e229f397 100644
--- a/problems/根据身高重建队列(vector原理讲解).md
+++ b/problems/根据身高重建队列(vector原理讲解).md
@@ -33,7 +33,7 @@ public:
耗时如下:

-其直观上来看数组的insert操作是$O(n)$的,整体代码的时间复杂度是$O(n^2)$。
+其直观上来看数组的insert操作是O(n)的,整体代码的时间复杂度是O(n^2)。
这么一分析好像和版本二链表实现的时间复杂度是一样的啊,为什么提交之后效率会差距这么大呢?
```CPP
@@ -97,7 +97,7 @@ for (int i = 0; i < vec.size(); i++) {
**同时也注意此时capicity和size的变化,关键的地方我都标红了**。
-而在[贪心算法:根据身高重建队列](https://programmercarl.com/0406.根据身高重建队列.html)中,我们使用vector来做insert的操作,此时大家可会发现,**虽然表面上复杂度是$O(n^2)$,但是其底层都不知道额外做了多少次全量拷贝了,所以算上vector的底层拷贝,整体时间复杂度可以认为是$O(n^2 + t × n)$级别的,t是底层拷贝的次数**。
+而在[贪心算法:根据身高重建队列](https://programmercarl.com/0406.根据身高重建队列.html)中,我们使用vector来做insert的操作,此时大家可会发现,**虽然表面上复杂度是O(n^2),但是其底层都不知道额外做了多少次全量拷贝了,所以算上vector的底层拷贝,整体时间复杂度可以认为是O(n^2 + t × n)级别的,t是底层拷贝的次数**。
那么是不是可以直接确定好vector的大小,不让它在动态扩容了,例如在[贪心算法:根据身高重建队列](https://programmercarl.com/0406.根据身高重建队列.html)中已经给出了有people.size这么多的人,可以定义好一个固定大小的vector,这样我们就可以控制vector,不让它底层动态扩容。
@@ -133,7 +133,7 @@ public:

-这份代码就是不让vector动态扩容,全程我们自己模拟insert的操作,大家也可以直观的看出是一个$O(n^2)$的方法了。
+这份代码就是不让vector动态扩容,全程我们自己模拟insert的操作,大家也可以直观的看出是一个O(n^2)的方法了。
但这份代码在leetcode上统计的耗时甚至比版本一的还高,我们都不让它动态扩容了,为什么耗时更高了呢?
@@ -151,7 +151,7 @@ public:
大家应该发现了,编程语言中一个普通容器的insert,delete的使用,都可能对写出来的算法的有很大影响!
-如果抛开语言谈算法,除非从来不用代码写算法纯分析,**否则的话,语言功底不到位$O(n)$的算法可以写出$O(n^2)$的性能**,哈哈。
+如果抛开语言谈算法,除非从来不用代码写算法纯分析,**否则的话,语言功底不到位O(n)的算法可以写出O(n^2)的性能**,哈哈。
相信在这里学习算法的录友们,都是想在软件行业长远发展的,都是要从事编程的工作,那么一定要深耕好一门编程语言,这个非常重要!
@@ -171,6 +171,14 @@ Python:
Go:
+Go中slice的`append`操作和C++中vector的扩容机制基本相同。
+
+说是基本呢,其实是因为大家平时刷题和工作中遇到的数据不会特别大。
+
+具体来说,当当前slice的长度小于**1024**时,执行`append`操作,新slice的capacity会变成当前的2倍;而当slice长度大于等于**1024**时,slice的扩容变成了每次增加当前slice长度的**1/4**。
+
+在Go Slice的底层实现中,如果capacity不够时,会做一个reslice的操作,底层数组也会重新被复制到另一块内存区域中,所以`append`一个元素,不一定是O(1), 也可能是O(n)哦。
+
diff --git a/problems/知识星球精选/HR特意刁难非科班.md b/problems/知识星球精选/HR特意刁难非科班.md
index c59debf2..d5d39fe3 100644
--- a/problems/知识星球精选/HR特意刁难非科班.md
+++ b/problems/知识星球精选/HR特意刁难非科班.md
@@ -1,6 +1,6 @@
-
+
@@ -8,7 +8,7 @@
不少录友都是非科班转程序员,或者进军互联网的,但有一些HR在HR面的时候特意刁难大家。
-正如[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,这位录友所遭受的情景。
+正如[知识星球](https://programmercarl.com/other/kstar.html)里,这位录友所遭受的情景。

diff --git a/problems/知识星球精选/HR面注意事项.md b/problems/知识星球精选/HR面注意事项.md
index 6a0a26f1..5dba672c 100644
--- a/problems/知识星球精选/HR面注意事项.md
+++ b/problems/知识星球精选/HR面注意事项.md
@@ -1,12 +1,12 @@
-
+
# HR面注意事项
-[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里已经有一些录友开始准备HR面。
+[知识星球](https://programmercarl.com/other/kstar.html)里已经有一些录友开始准备HR面。

@@ -86,4 +86,4 @@ HR朋友的回答是:你不说真相,我会认为你可能对技术有追求
---------------
-加入「代码随想录」知识星球,[点击这里](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
+加入「代码随想录」知识星球,[点击这里](https://programmercarl.com/other/kstar.html)
diff --git a/problems/知识星球精选/offer对比-决赛圈.md b/problems/知识星球精选/offer对比-决赛圈.md
index 1f91730f..081ae5ec 100644
--- a/problems/知识星球精选/offer对比-决赛圈.md
+++ b/problems/知识星球精选/offer对比-决赛圈.md
@@ -1,5 +1,5 @@
-
+
@@ -7,7 +7,7 @@
秋招已经结束了,该开奖的差不多都陆续开奖了,很多录友的也进入了offer决赛圈。
-我每天都在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,回答十几个offer对比的问题,我也是结合自己过来人的经验给大家做做分析,我也选几个案例,在公众号上也给大家分享一下,希望对大家有所启发。
+我每天都在[知识星球](https://programmercarl.com/other/kstar.html)里,回答十几个offer对比的问题,我也是结合自己过来人的经验给大家做做分析,我也选几个案例,在公众号上也给大家分享一下,希望对大家有所启发。
以下是知识星球里的部分问答:
diff --git a/problems/知识星球精选/offer总决赛,何去何从.md b/problems/知识星球精选/offer总决赛,何去何从.md
index 01745ae3..e22c5d4a 100644
--- a/problems/知识星球精选/offer总决赛,何去何从.md
+++ b/problems/知识星球精选/offer总决赛,何去何从.md
@@ -1,12 +1,12 @@
-
+
# offer总决赛,何去何从!
-最近在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)上,给至少300位录友做了offer选择,准对大家的情况,结合我的经验做一做分析。
+最近在[知识星球](https://programmercarl.com/other/kstar.html)上,给至少300位录友做了offer选择,准对大家的情况,结合我的经验做一做分析。
希望可以给大家带来不一样的分析视角,帮大家少走弯路。
diff --git a/problems/知识星球精选/offer的选择.md b/problems/知识星球精选/offer的选择.md
index b9b40dea..106bc5f8 100644
--- a/problems/知识星球精选/offer的选择.md
+++ b/problems/知识星球精选/offer的选择.md
@@ -1,6 +1,6 @@
-
+
@@ -10,7 +10,7 @@
不过大部分同学应该拿到的是 两个大厂offer,或者说拿到两个小厂offer,还要考虑岗位,业务,公司前景,那么就要纠结如何选择了。
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,我已经给很多录友提供了选择offer的建议,这里也分享出来,希望对大家在选择offer上有所启发。
+在[知识星球](https://programmercarl.com/other/kstar.html)里,我已经给很多录友提供了选择offer的建议,这里也分享出来,希望对大家在选择offer上有所启发。
## 保研与工作
diff --git a/problems/知识星球精选/不一样的七夕.md b/problems/知识星球精选/不一样的七夕.md
index a670e078..40d15ecd 100644
--- a/problems/知识星球精选/不一样的七夕.md
+++ b/problems/知识星球精选/不一样的七夕.md
@@ -1,11 +1,11 @@
-
+
# 特殊的七夕
-昨天在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)发了一个状态:
+昨天在[知识星球](https://programmercarl.com/other/kstar.html)发了一个状态:

diff --git a/problems/知识星球精选/不喜欢写代码怎么办.md b/problems/知识星球精选/不喜欢写代码怎么办.md
index 9bc624bb..84d372f0 100644
--- a/problems/知识星球精选/不喜欢写代码怎么办.md
+++ b/problems/知识星球精选/不喜欢写代码怎么办.md
@@ -1,7 +1,7 @@
# 看到代码就抵触!怎么办?
-最近在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,看到了不少录友,其实是不喜欢写代码,看到 哪些八股文都是很抵触的。
+最近在[知识星球](https://programmercarl.com/other/kstar.html)里,看到了不少录友,其实是不喜欢写代码,看到 哪些八股文都是很抵触的。
其实是一个普遍现象,我在星球里分享了一下,我对这一情况的一些想法。
diff --git a/problems/知识星球精选/不少录友想放弃秋招.md b/problems/知识星球精选/不少录友想放弃秋招.md
index 721a9313..81c99503 100644
--- a/problems/知识星球精选/不少录友想放弃秋招.md
+++ b/problems/知识星球精选/不少录友想放弃秋招.md
@@ -1,5 +1,5 @@
-
+
@@ -7,7 +7,7 @@
马上就要九月份了,互联网大厂的秋招的序幕早已拉开。
-发现[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里有一部分录友想放弃秋招,直接准备明年的春招,估计关注公众号的录友也有不少有这种想法的。
+发现[知识星球](https://programmercarl.com/other/kstar.html)里有一部分录友想放弃秋招,直接准备明年的春招,估计关注公众号的录友也有不少有这种想法的。

@@ -50,7 +50,7 @@
所以也给明年找工作的录友们(2023届)提一个醒,现在就要系统性的准备起来了,因为明年春季实习招聘 是一个很好的进大厂的机会,剩下的时间也不是很多了。
-来看看[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,一位准大三的录友准备的情况
+来看看[知识星球](https://programmercarl.com/other/kstar.html)里,一位准大三的录友准备的情况

@@ -60,7 +60,7 @@
**我已经预感到 这两位 等到秋招的时候就是稳稳的offer收割机**。
-[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)还有很多已经开始提前准备,或者看了 星球发文状态就开始着手准备的录友了。
+[知识星球](https://programmercarl.com/other/kstar.html)还有很多已经开始提前准备,或者看了 星球发文状态就开始着手准备的录友了。
所以 **所谓的大牛,都是 很早就规划自己要学的东西,很早就开始向过来人请教应该如何找工作,很早就知道自己应该学哪些技术,看哪些书, 这样等到找工作的时候,才是剑锋出鞘的时候**。
diff --git a/problems/知识星球精选/专业技能可以这么写.md b/problems/知识星球精选/专业技能可以这么写.md
index dd616713..a1b7ba3a 100644
--- a/problems/知识星球精选/专业技能可以这么写.md
+++ b/problems/知识星球精选/专业技能可以这么写.md
@@ -1,5 +1,5 @@
-
+
@@ -8,7 +8,7 @@
# 你简历里的「专业技能」写的够专业么?
-其实我几乎每天都要看一些简历,有一些写的不错的,我都会在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里分享一下。
+其实我几乎每天都要看一些简历,有一些写的不错的,我都会在[知识星球](https://programmercarl.com/other/kstar.html)里分享一下。

这次呢,我再专门说一说简历中的【专业技能】这一栏应该怎么写。
diff --git a/problems/知识星球精选/入职后担心代码能力跟不上.md b/problems/知识星球精选/入职后担心代码能力跟不上.md
index c2704525..58b8c32c 100644
--- a/problems/知识星球精选/入职后担心代码能力跟不上.md
+++ b/problems/知识星球精选/入职后担心代码能力跟不上.md
@@ -1,12 +1,12 @@
-
+
# 入职后担心代码能力跟不上
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)上,很多录友已经担心自己去了公司工作以后,代码能力跟不上,会压力很大。
+在[知识星球](https://programmercarl.com/other/kstar.html)上,很多录友已经担心自己去了公司工作以后,代码能力跟不上,会压力很大。

diff --git a/problems/知识星球精选/关于实习大家的疑问.md b/problems/知识星球精选/关于实习大家的疑问.md
index 5d4e695b..88de4436 100644
--- a/problems/知识星球精选/关于实习大家的疑问.md
+++ b/problems/知识星球精选/关于实习大家的疑问.md
@@ -1,11 +1,11 @@
-
+
# 关于实习,大家可能有点迷茫!
-我在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里回答了很多关于实习相关的问题,其实很多录友可能都有这么样的疑问,主要关于实习的问题有如下四点:
+我在[知识星球](https://programmercarl.com/other/kstar.html)里回答了很多关于实习相关的问题,其实很多录友可能都有这么样的疑问,主要关于实习的问题有如下四点:
* 秋招什么时候开始准备
* 要不要准备实习
diff --git a/problems/知识星球精选/关于提前批的一些建议.md b/problems/知识星球精选/关于提前批的一些建议.md
index 415a8b2f..6a316bd2 100644
--- a/problems/知识星球精选/关于提前批的一些建议.md
+++ b/problems/知识星球精选/关于提前批的一些建议.md
@@ -1,5 +1,5 @@
-
+
@@ -9,7 +9,7 @@
以前提前批,都是 8月份,8月份中序左右,而不少大厂现在就已经提前批了。
-不少录友在 公众号留言,和[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,表示提前批来的还是有点快。
+不少录友在 公众号留言,和[知识星球](https://programmercarl.com/other/kstar.html)里,表示提前批来的还是有点快。

diff --git a/problems/知识星球精选/写简历的一些问题.md b/problems/知识星球精选/写简历的一些问题.md
index af42cea1..426eb2a6 100644
--- a/problems/知识星球精选/写简历的一些问题.md
+++ b/problems/知识星球精选/写简历的一些问题.md
@@ -1,11 +1,11 @@
-
+
# 程序员应该这么写简历!
-自运营[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)以来,我已经给星球里的录友们看了 一百多份简历,并准对大家简历上的问题都给出了对应的详细建议。
+自运营[知识星球](https://programmercarl.com/other/kstar.html)以来,我已经给星球里的录友们看了 一百多份简历,并准对大家简历上的问题都给出了对应的详细建议。
社招,校招,实习的都有,其实大家的简历看多了,发现有很多共性的问题,这里就和大家分享一下。
diff --git a/problems/知识星球精选/初入大三选择考研VS工作.md b/problems/知识星球精选/初入大三选择考研VS工作.md
index ba675761..f602e9e9 100644
--- a/problems/知识星球精选/初入大三选择考研VS工作.md
+++ b/problems/知识星球精选/初入大三选择考研VS工作.md
@@ -1,6 +1,6 @@
-
+
@@ -8,7 +8,7 @@
9月份开学季,已过,一些录友也升入大三了,升入大三摆在自己面前最大的问题就是,考研还是找工作?
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里就有录友问我这样一个问题, 其实每个人情况不一样,做出的选择也不一样,这里给大家分享一下,相信对你也会有启发。
+在[知识星球](https://programmercarl.com/other/kstar.html)里就有录友问我这样一个问题, 其实每个人情况不一样,做出的选择也不一样,这里给大家分享一下,相信对你也会有启发。

diff --git a/problems/知识星球精选/刷力扣用不用库函数.md b/problems/知识星球精选/刷力扣用不用库函数.md
index 73f2a2d5..c8e2f5c6 100644
--- a/problems/知识星球精选/刷力扣用不用库函数.md
+++ b/problems/知识星球精选/刷力扣用不用库函数.md
@@ -1,11 +1,11 @@
-
+
# 究竟什么时候用库函数,什么时候要自己实现
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里有录友问我,刷题究竟要不要用库函数? 刷题的时候总是禁不住库函数的诱惑,如果都不用库函数一些题目做起来还很麻烦。
+在[知识星球](https://programmercarl.com/other/kstar.html)里有录友问我,刷题究竟要不要用库函数? 刷题的时候总是禁不住库函数的诱惑,如果都不用库函数一些题目做起来还很麻烦。
估计不少录友都有这个困惑,我来说一说对于库函数的使用。
@@ -27,7 +27,7 @@
使用库函数最大的忌讳就是不知道这个库函数怎么实现的,也不知道其时间复杂度,上来就用,这样写出来的算法,时间复杂度自己都掌握不好的。
-例如for循环里套一个字符串的insert,erase之类的操作,你说时间复杂度是多少呢,很明显是$O(n^2)$的时间复杂度了。
+例如for循环里套一个字符串的insert,erase之类的操作,你说时间复杂度是多少呢,很明显是O(n^2)的时间复杂度了。
在刷题的时候本着我说的标准来使用库函数,详细对大家回有所帮助!
diff --git a/problems/知识星球精选/刷题攻略要刷两遍.md b/problems/知识星球精选/刷题攻略要刷两遍.md
index 1f4fd7f9..285a5728 100644
--- a/problems/知识星球精选/刷题攻略要刷两遍.md
+++ b/problems/知识星球精选/刷题攻略要刷两遍.md
@@ -1,5 +1,5 @@
-
+
@@ -27,7 +27,7 @@
第三遍基本就得心应手了。
-在[「代码随想录」知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)中,我都是强调大家要至少刷两遍,有时间的话刷三遍,
+在[「代码随想录」知识星球](https://programmercarl.com/other/kstar.html)中,我都是强调大家要至少刷两遍,有时间的话刷三遍,
可以看看星球里录友们的打卡:
diff --git a/problems/知识星球精选/博士转行计算机.md b/problems/知识星球精选/博士转行计算机.md
index 66769264..4da79e97 100644
--- a/problems/知识星球精选/博士转行计算机.md
+++ b/problems/知识星球精选/博士转行计算机.md
@@ -1,11 +1,11 @@
-
+
# 本硕非计算机博士,如果找计算机相关工作
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,有一位博士录友,本硕都不是计算机,博士转的计算机,问了这样一个问题
+在[知识星球](https://programmercarl.com/other/kstar.html)里,有一位博士录友,本硕都不是计算机,博士转的计算机,问了这样一个问题

diff --git a/problems/知识星球精选/合适自己的就是最好的.md b/problems/知识星球精选/合适自己的就是最好的.md
index fda51afa..dc31f5a7 100644
--- a/problems/知识星球精选/合适自己的就是最好的.md
+++ b/problems/知识星球精选/合适自己的就是最好的.md
@@ -1,5 +1,5 @@
-
+
@@ -7,7 +7,7 @@
秋招已经进入下半场了,不少同学也拿到了offer,但不是说非要进大厂,每个人情况都不一样,**合适自己的,就是最好的!**。
-[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里有一位录友,就终于拿到了合适自己的offer,并不是大厂,是南京的一家公司,**但很合适自己,其实就非常值得开心**。
+[知识星球](https://programmercarl.com/other/kstar.html)里有一位录友,就终于拿到了合适自己的offer,并不是大厂,是南京的一家公司,**但很合适自己,其实就非常值得开心**。

@@ -33,5 +33,5 @@
我在发文的时候 看了一遍她这几个月完整的打卡过程,还是深有感触的。
-[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里还有很多很多这样的录友在每日奋斗着,**我相信 等大家拿到offer之后,在回头看一下当初星球里曾经每日打卡的点点滴滴,不仅会感动自己 也会感动每一位见证者**。
+[知识星球](https://programmercarl.com/other/kstar.html)里还有很多很多这样的录友在每日奋斗着,**我相信 等大家拿到offer之后,在回头看一下当初星球里曾经每日打卡的点点滴滴,不仅会感动自己 也会感动每一位见证者**。
diff --git a/problems/知识星球精选/备战2022届秋招.md b/problems/知识星球精选/备战2022届秋招.md
index 207a5e2a..55c2a3bf 100644
--- a/problems/知识星球精选/备战2022届秋招.md
+++ b/problems/知识星球精选/备战2022届秋招.md
@@ -1,11 +1,11 @@
-
+
# 要开始准备2022届的秋招了
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里准备秋招的录友还真不少,也会回答过不少关于秋招的问题。
+在[知识星球](https://programmercarl.com/other/kstar.html)里准备秋招的录友还真不少,也会回答过不少关于秋招的问题。

diff --git a/problems/知识星球精选/大厂新人培养体系.md b/problems/知识星球精选/大厂新人培养体系.md
index ccd2f1c2..0e905e42 100644
--- a/problems/知识星球精选/大厂新人培养体系.md
+++ b/problems/知识星球精选/大厂新人培养体系.md
@@ -1,11 +1,11 @@
-
+
# 大厂的新人培养体系是什么样的
-之前我一直在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)和大家讲,能进大厂一定要进大厂,大厂有比较好的培养体系。
+之前我一直在[知识星球](https://programmercarl.com/other/kstar.html)和大家讲,能进大厂一定要进大厂,大厂有比较好的培养体系。
也有录友在星球里问我,究竟培养体系应该是什么样的呢? 大厂都会这么培养新人么?
diff --git a/problems/知识星球精选/天下乌鸦一般黑.md b/problems/知识星球精选/天下乌鸦一般黑.md
index 29543747..ccd1c326 100644
--- a/problems/知识星球精选/天下乌鸦一般黑.md
+++ b/problems/知识星球精选/天下乌鸦一般黑.md
@@ -1,6 +1,6 @@
-
+
@@ -8,7 +8,7 @@
相信大家应该经常在 各大论坛啊之类的 看到对各个互联网公司的评价,有风评好的,也有风评不好的。
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里有录友问我这样一个问题:
+在[知识星球](https://programmercarl.com/other/kstar.html)里有录友问我这样一个问题:

diff --git a/problems/知识星球精选/如何权衡实习与秋招复习.md b/problems/知识星球精选/如何权衡实习与秋招复习.md
index 275588df..07e2bba6 100644
--- a/problems/知识星球精选/如何权衡实习与秋招复习.md
+++ b/problems/知识星球精选/如何权衡实习与秋招复习.md
@@ -1,11 +1,11 @@
-
+
# 已经在实习的录友如何准备秋招?
-最近在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)一位录友问了实习生如何权衡工作和准备秋招的问题。
+最近在[知识星球](https://programmercarl.com/other/kstar.html)一位录友问了实习生如何权衡工作和准备秋招的问题。

diff --git a/problems/知识星球精选/客三消.md b/problems/知识星球精选/客三消.md
index 8a7b5fc6..6b81ab2c 100644
--- a/problems/知识星球精选/客三消.md
+++ b/problems/知识星球精选/客三消.md
@@ -1,5 +1,5 @@
-
+
@@ -17,7 +17,7 @@
然后朋友圈就炸了,上百条的留言,问我这是为啥。
-其实这个问题在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里也有录友问过我。
+其实这个问题在[知识星球](https://programmercarl.com/other/kstar.html)里也有录友问过我。

@@ -87,7 +87,7 @@
# 总结
-以上就是我在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里的详细回答。
+以上就是我在[知识星球](https://programmercarl.com/other/kstar.html)里的详细回答。
注意我这里说的一般情况,当然各个岗位都有佼佼者,或者说大牛,客户端也有大牛,也很香,不过这是极少数,就不在讨论范围内了。
diff --git a/problems/知识星球精选/技术不好如何选择技术方向.md b/problems/知识星球精选/技术不好如何选择技术方向.md
index dd13f46b..4ad4659b 100644
--- a/problems/知识星球精选/技术不好如何选择技术方向.md
+++ b/problems/知识星球精选/技术不好如何选择技术方向.md
@@ -1,11 +1,11 @@
-
+
# 技术不太好,也不知道对技术有没有兴趣,我该怎么选?
-最近在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里解答了不少录友们的疑惑,其实发现一个挺普遍的问题:
+最近在[知识星球](https://programmercarl.com/other/kstar.html)里解答了不少录友们的疑惑,其实发现一个挺普遍的问题:
* 我技术很一般
* 对技术也没有什么追求
diff --git a/problems/知识星球精选/提前批已经开始了.md b/problems/知识星球精选/提前批已经开始了.md
index ba05b5a9..3e255746 100644
--- a/problems/知识星球精选/提前批已经开始了.md
+++ b/problems/知识星球精选/提前批已经开始了.md
@@ -1,5 +1,5 @@
-
+
@@ -7,7 +7,7 @@
最近华为提前批已经开始了,不少同学已经陆续参加了提前批的面试。
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)上就有录友问我这么个问题:
+在[知识星球](https://programmercarl.com/other/kstar.html)上就有录友问我这么个问题:

diff --git a/problems/知识星球精选/秋招下半场依然没offer.md b/problems/知识星球精选/秋招下半场依然没offer.md
index 829f82ba..5862dd32 100644
--- a/problems/知识星球精选/秋招下半场依然没offer.md
+++ b/problems/知识星球精选/秋招下半场依然没offer.md
@@ -1,11 +1,11 @@
-
+
# 秋招下半场依然没offer,怎么办?
-[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里一些录友拿到了满意的offer,也有一些录友,依然没有offer,每天的状态已经不能用焦虑来形容了。
+[知识星球](https://programmercarl.com/other/kstar.html)里一些录友拿到了满意的offer,也有一些录友,依然没有offer,每天的状态已经不能用焦虑来形容了。
在星球里就有录友向我提问了这样一个问题:
@@ -52,7 +52,7 @@
## 在学点技术,冲春招?
-[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里还有一位录友,也是类似的情况,秋招感觉很艰难,要不要在学一学微服务分布式之类的,再冲春招。
+[知识星球](https://programmercarl.com/other/kstar.html)里还有一位录友,也是类似的情况,秋招感觉很艰难,要不要在学一学微服务分布式之类的,再冲春招。

@@ -71,13 +71,13 @@
## 给参加明年秋招录友的劝告
-其实我在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,**看到了太多太多 参加今年秋招的录友 埋怨自己 准备的太晚了,没想到要看的东西这么多,没想到竞争这么激烈**。
+其实我在[知识星球](https://programmercarl.com/other/kstar.html)里,**看到了太多太多 参加今年秋招的录友 埋怨自己 准备的太晚了,没想到要看的东西这么多,没想到竞争这么激烈**。
所以明年参加秋招的录友,要提前就开始准备,明确自己的岗位,知道岗位的要求,制定自己的计划,然后按计划执行。
**其实多早开始准备,都不算早!**
-很多在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里的准大三,研一的录友,都能在星球里感受到 秋招的竞争与激烈。
+很多在[知识星球](https://programmercarl.com/other/kstar.html)里的准大三,研一的录友,都能在星球里感受到 秋招的竞争与激烈。
所以他们也就早早的开始准备了。
@@ -87,7 +87,7 @@
估计大多数准大三或者准研一的同学都还没有这种意识。
-**但在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,通过每天录友们的打卡,每天都能感受到这种紧迫感**。
+**但在[知识星球](https://programmercarl.com/other/kstar.html)里,通过每天录友们的打卡,每天都能感受到这种紧迫感**。
正如一位星球里的录友这么说:
diff --git a/problems/知识星球精选/秋招开奖.md b/problems/知识星球精选/秋招开奖.md
index 368596b6..82686785 100644
--- a/problems/知识星球精选/秋招开奖.md
+++ b/problems/知识星球精选/秋招开奖.md
@@ -1,6 +1,6 @@
-
+
@@ -8,7 +8,7 @@
最近秋招的录友已经陆续开奖了,同时开奖多少,也是offer选择的一个重要因素,毕竟谁能和钱过意不去呢。
-[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里这位录友拿到的百度offer薪资确实很高
+[知识星球](https://programmercarl.com/other/kstar.html)里这位录友拿到的百度offer薪资确实很高

diff --git a/problems/知识星球精选/秋招总结1.md b/problems/知识星球精选/秋招总结1.md
index efec67ee..2aec24dc 100644
--- a/problems/知识星球精选/秋招总结1.md
+++ b/problems/知识星球精选/秋招总结1.md
@@ -1,5 +1,5 @@
-
+
@@ -11,7 +11,7 @@
时间总是过得很快,但曾经焦虑的小伙,现在也拿到几个offer了,不一定人人都要冲大厂,卷算法,卷后端,合适自己就好,要不然会把自己搞的很累。
-以下是他的秋招总结,**写的很用心,说了很多面试中使用的方法,发在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里,立刻就引来星球小伙伴们的围观**,算是给星球里明年要秋招的录友做了一个参考。
+以下是他的秋招总结,**写的很用心,说了很多面试中使用的方法,发在[知识星球](https://programmercarl.com/other/kstar.html)里,立刻就引来星球小伙伴们的围观**,算是给星球里明年要秋招的录友做了一个参考。

diff --git a/problems/知识星球精选/秋招总结2.md b/problems/知识星球精选/秋招总结2.md
index 7f4b6770..897a7ec3 100644
--- a/problems/知识星球精选/秋招总结2.md
+++ b/problems/知识星球精选/秋招总结2.md
@@ -1,10 +1,10 @@
-
+
# 倒霉透顶,触底反弹!
-星球里不少录友秋招已经陆续结束了,很多录友都在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里写下了自己的秋招总结,但今天这位录友很特殊,甚至我给她修改简历的时候我都“有点愁”。
+星球里不少录友秋招已经陆续结束了,很多录友都在[知识星球](https://programmercarl.com/other/kstar.html)里写下了自己的秋招总结,但今天这位录友很特殊,甚至我给她修改简历的时候我都“有点愁”。
他的秋招过程也是极其坎坷,**逼签、被养鱼最后收到感谢信、校招流程收到实习offer,还有数不清的简历挂……**,可能是太倒霉了,最后触底反弹,接到了百度的offer,虽然是白菜价,但真的很不错了。
diff --git a/problems/知识星球精选/秋招总结3.md b/problems/知识星球精选/秋招总结3.md
index 895c1b8c..05a677ed 100644
--- a/problems/知识星球精选/秋招总结3.md
+++ b/problems/知识星球精选/秋招总结3.md
@@ -1,4 +1,4 @@
-
+
@@ -6,7 +6,7 @@
其实无论社招,还是校招,心态都很重要,例如,别人那个一堆offer,自己陷入深深的焦虑。 面试分明感觉自己表现的不错,结果就是挂了。面试中遇到了面试官的否定,然后就开始自我怀疑,等等等。
-如果你也遇到这些问题,可以认真读完[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里一位录友的总结,他是非科班,机械转码,今年5月份加入的星球,坚持打卡几个月,如果也获得自己心仪的offer,他的心路历程对大家会很有启发。
+如果你也遇到这些问题,可以认真读完[知识星球](https://programmercarl.com/other/kstar.html)里一位录友的总结,他是非科班,机械转码,今年5月份加入的星球,坚持打卡几个月,如果也获得自己心仪的offer,他的心路历程对大家会很有启发。

diff --git a/problems/知识星球精选/秋招的上半场.md b/problems/知识星球精选/秋招的上半场.md
index f404e611..6c817577 100644
--- a/problems/知识星球精选/秋招的上半场.md
+++ b/problems/知识星球精选/秋招的上半场.md
@@ -1,11 +1,11 @@
-
+
# 秋招上半场的总结
-八月份已经接近尾声,不少录友已经在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) 已经总结了秋招的上半场。
+八月份已经接近尾声,不少录友已经在[知识星球](https://programmercarl.com/other/kstar.html) 已经总结了秋招的上半场。

diff --git a/problems/知识星球精选/秋招进行中的迷茫与焦虑.md b/problems/知识星球精选/秋招进行中的迷茫与焦虑.md
index 6083c7b1..24e7760c 100644
--- a/problems/知识星球精选/秋招进行中的迷茫与焦虑.md
+++ b/problems/知识星球精选/秋招进行中的迷茫与焦虑.md
@@ -1,5 +1,5 @@
-
+
@@ -9,7 +9,7 @@
特别是大三的同学吧,同时面临这找工作和考研两个方向的诱惑。
-一位录友就在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)问了我这个问题:
+一位录友就在[知识星球](https://programmercarl.com/other/kstar.html)问了我这个问题:

diff --git a/problems/知识星球精选/英语到底重不重要.md b/problems/知识星球精选/英语到底重不重要.md
index 32e6a39b..5ee7fc2d 100644
--- a/problems/知识星球精选/英语到底重不重要.md
+++ b/problems/知识星球精选/英语到底重不重要.md
@@ -1,11 +1,11 @@
-
+
# 对程序员来说,英语到底重不重要
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)有一位录友问了我这么一个问题。
+在[知识星球](https://programmercarl.com/other/kstar.html)有一位录友问了我这么一个问题。

diff --git a/problems/知识星球精选/要不要考研.md b/problems/知识星球精选/要不要考研.md
index a5f2dfa0..180e5d13 100644
--- a/problems/知识星球精选/要不要考研.md
+++ b/problems/知识星球精选/要不要考研.md
@@ -1,11 +1,11 @@
-
+
# 到底要不要读研
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里讨论了一下关于要不要读研的问题。
+在[知识星球](https://programmercarl.com/other/kstar.html)里讨论了一下关于要不要读研的问题。

diff --git a/problems/知识星球精选/逼签.md b/problems/知识星球精选/逼签.md
index 90e4e67d..f75e3642 100644
--- a/problems/知识星球精选/逼签.md
+++ b/problems/知识星球精选/逼签.md
@@ -7,7 +7,7 @@
如果是心仪的公司要求三天内签三方,我相信大家就没有被逼签的感觉了,哈哈哈
-[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)里很多录友都问我,XX公司又要逼签了,怎么办。 我在公众号也分享一下,希望对大家有所帮助。
+[知识星球](https://programmercarl.com/other/kstar.html)里很多录友都问我,XX公司又要逼签了,怎么办。 我在公众号也分享一下,希望对大家有所帮助。

diff --git a/problems/知识星球精选/非科班2021秋招总结.md b/problems/知识星球精选/非科班2021秋招总结.md
index c2c7ed33..b92420ad 100644
--- a/problems/知识星球精选/非科班2021秋招总结.md
+++ b/problems/知识星球精选/非科班2021秋招总结.md
@@ -1,6 +1,6 @@
-
+
@@ -10,7 +10,7 @@
其中一位录友写的很好,所以想分享出来 给公众号上的录友也看一看,相信对大家有所启发,特别是明年要找工作的录友,值得好好看一看。
-这篇总结首发在代码随想录[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)上,立刻就获得了60个赞,很多评论,我这里放出一个截图:
+这篇总结首发在代码随想录[知识星球](https://programmercarl.com/other/kstar.html)上,立刻就获得了60个赞,很多评论,我这里放出一个截图:

diff --git a/problems/知识星球精选/非科班的困扰.md b/problems/知识星球精选/非科班的困扰.md
index d5fea532..470b233b 100644
--- a/problems/知识星球精选/非科班的困扰.md
+++ b/problems/知识星球精选/非科班的困扰.md
@@ -1,11 +1,11 @@
-
+
# 非科班的困扰!
-在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) 里很多录友都是非科班转码的,也是要准备求职,或者准备明年秋招,非科班的录友其实对 准备找工作所需要的知识不太清楚,对其难度也不太清楚,所有总感觉准备起来心里没有底。
+在[知识星球](https://programmercarl.com/other/kstar.html) 里很多录友都是非科班转码的,也是要准备求职,或者准备明年秋招,非科班的录友其实对 准备找工作所需要的知识不太清楚,对其难度也不太清楚,所有总感觉准备起来心里没有底。
例如星球里有这位录友的提问:
diff --git a/problems/知识星球精选/面试中发散性问题.md b/problems/知识星球精选/面试中发散性问题.md
index 7fb9150f..5cade944 100644
--- a/problems/知识星球精选/面试中发散性问题.md
+++ b/problems/知识星球精选/面试中发散性问题.md
@@ -1,11 +1,11 @@
-
+
# 面试中遇到发散性问题,应该怎么办?
-这周在[知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)有一位录友问了我这么一个问题,我感觉挺有代表性的,应该不少录友在面试中不论是社招还是校招都会遇到这一类的问题。
+这周在[知识星球](https://programmercarl.com/other/kstar.html)有一位录友问了我这么一个问题,我感觉挺有代表性的,应该不少录友在面试中不论是社招还是校招都会遇到这一类的问题。
问题如下:
diff --git a/problems/算法模板.md b/problems/算法模板.md
index a4136511..789d8f80 100644
--- a/problems/算法模板.md
+++ b/problems/算法模板.md
@@ -394,7 +394,7 @@ var postorder = function (root, list) {
```javascript
var preorderTraversal = function (root) {
let res = [];
- if (root === null) return rs;
+ if (root === null) return res;
let stack = [root],
cur = null;
while (stack.length) {
@@ -536,6 +536,269 @@ function backtracking(参数) {
}
```
+TypeScript:
+
+## 二分查找法
+
+使用左闭右闭区间
+
+```typescript
+var search = function (nums: number[], target: number): number {
+ let left: number = 0, right: number = nums.length - 1;
+ // 使用左闭右闭区间
+ while (left <= right) {
+ let mid: number = 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;
+};
+```
+
+使用左闭右开区间
+
+```typescript
+var search = function (nums: number[], target: number): number {
+ let left: number = 0, right: number = nums.length;
+ // 使用左闭右开区间 [left, right)
+ while (left < right) {
+ let mid: number = 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
+
+```typescript
+var kmp = function (next: number[], s: number): void {
+ next[0] = -1;
+ let j: number = -1;
+ for(let i: number = 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;
+ }
+}
+```
+
+## 二叉树
+
+### 深度优先遍历(递归)
+
+二叉树节点定义:
+
+```typescript
+class TreeNode {
+ val: number
+ left: TreeNode | null
+ right: TreeNode | null
+ constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
+ this.val = (val===undefined ? 0 : val)
+ this.left = (left===undefined ? null : left)
+ this.right = (right===undefined ? null : right)
+ }
+}
+```
+
+前序遍历(中左右):
+
+```typescript
+var preorder = function (root: TreeNode | null, list: number[]): void {
+ if (root === null) return;
+ list.push(root.val); // 中
+ preorder(root.left, list); // 左
+ preorder(root.right, list); // 右
+}
+```
+
+中序遍历(左中右):
+
+```typescript
+var inorder = function (root: TreeNode | null, list: number[]): void {
+ if (root === null) return;
+ inorder(root.left, list); // 左
+ list.push(root.val); // 中
+ inorder(root.right, list); // 右
+}
+```
+
+后序遍历(左右中):
+
+```typescript
+var postorder = function (root: TreeNode | null, list: number[]): void {
+ if (root === null) return;
+ postorder(root.left, list); // 左
+ postorder(root.right, list); // 右
+ list.push(root.val); // 中
+}
+```
+
+### 深度优先遍历(迭代)
+
+前序遍历(中左右):
+
+```typescript
+var preorderTraversal = function (root: TreeNode | null): number[] {
+ let res: number[] = [];
+ if (root === null) return res;
+ let stack: TreeNode[] = [root],
+ cur: TreeNode | null = 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;
+};
+```
+
+中序遍历(左中右):
+
+```typescript
+var inorderTraversal = function (root: TreeNode | null): number[] {
+ let res: number[] = [];
+ if (root === null) return res;
+ let stack: TreeNode[] = [];
+ let cur: TreeNode | null = 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;
+};
+```
+
+后序遍历(左右中):
+
+```typescript
+var postorderTraversal = function (root: TreeNode | null): number[] {
+ let res: number[] = [];
+ if (root === null) return res;
+ let stack: TreeNode[] = [root];
+ let cur: TreeNode | null = 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()
+};
+```
+
+### 广度优先遍历(队列)
+
+```typescript
+var levelOrder = function (root: TreeNode | null): number[] {
+ let res: number[] = [];
+ if (root === null) return res;
+ let queue: TreeNode[] = [root];
+ while (queue.length) {
+ let n: number = queue.length;
+ let temp: number[] = [];
+ for (let i: number = 0; i < n; i++) {
+ let node: TreeNode = queue.shift();
+ temp.push(node.val);
+ node.left && queue.push(node.left);
+ node.right && queue.push(node.right);
+ }
+ res.push(temp);
+ }
+ return res;
+};
+```
+
+### 二叉树深度
+
+```typescript
+var getDepth = function (node: TreNode | null): number {
+ if (node === null) return 0;
+ return 1 + Math.max(getDepth(node.left), getDepth(node.right));
+}
+```
+
+### 二叉树节点数量
+
+```typescript
+var countNodes = function (root: TreeNode | null): number {
+ if (root === null) return 0;
+ return 1 + countNodes(root.left) + countNodes(root.right);
+}
+```
+
+## 回溯算法
+
+```typescript
+function backtracking(参数) {
+ if (终止条件) {
+ 存放结果;
+ return;
+ }
+
+ for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
+ 处理节点;
+ backtracking(路径,选择列表); // 递归
+ 回溯,撤销处理结果
+ }
+}
+
+```
+
+## 并查集
+
+```typescript
+ let n: number = 1005; // 根据题意而定
+ let father: number[] = new Array(n).fill(0);
+
+ // 并查集初始化
+ function init () {
+ for (int i: number = 0; i < n; ++i) {
+ father[i] = i;
+ }
+ }
+ // 并查集里寻根的过程
+ function find (u: number): number {
+ return u === father[u] ? u : father[u] = find(father[u]);
+ }
+ // 将v->u 这条边加入并查集
+ function join(u: number, v: number) {
+ u = find(u);
+ v = find(v);
+ if (u === v) return ;
+ father[v] = u;
+ }
+ // 判断 u 和 v是否找到同一个根
+ function same(u: number, v: number): boolean {
+ u = find(u);
+ v = find(v);
+ return u === v;
+ }
+```
+
Java:
diff --git a/problems/背包总结篇.md b/problems/背包总结篇.md
index f80bcf29..a7852de3 100644
--- a/problems/背包总结篇.md
+++ b/problems/背包总结篇.md
@@ -82,6 +82,7 @@
## 总结
+
**这篇背包问题总结篇是对背包问题的高度概括,讲最关键的两部:递推公式和遍历顺序,结合力扣上的题目全都抽象出来了**。
**而且每一个点,我都给出了对应的力扣题目**。
@@ -90,7 +91,11 @@
如果把我本篇总结出来的内容都掌握的话,可以说对背包问题理解的就很深刻了,用来对付面试中的背包问题绰绰有余!
+背包问题总结:
+
+
+这个图是 [代码随想录知识星球](https://programmercarl.com/other/kstar.html) 成员:[海螺人](https://wx.zsxq.com/dweb2/index/footprint/844412858822412),所画结的非常好,分享给大家。
diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md
index 4367aff9..fe940b4c 100644
--- a/problems/背包理论基础01背包-1.md
+++ b/problems/背包理论基础01背包-1.md
@@ -3,11 +3,12 @@
参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
+
# 动态规划:关于01背包问题,你该了解这些!
这周我们正式开始讲解背包问题!
-背包问题的经典资料当然是:背包九讲。在公众号「代码随想录」后台回复:背包九讲,就可以获得背包九讲的PDF。
+背包问题的经典资料当然是:背包九讲。在公众号「代码随想录」后台回复:背包九讲,就可以获得背包九讲的pdf。
但说实话,背包九讲对于小白来说确实不太友好,看起来还是有点费劲的,而且都是伪代码理解起来也吃力。
@@ -32,7 +33,7 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
## 01 背包
-有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品只能用一次**,求解将哪些物品装入背包里物品价值总和最大。
+有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。**每件物品只能用一次**,求解将哪些物品装入背包里物品价值总和最大。

@@ -40,7 +41,7 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
这样其实是没有从底向上去思考,而是习惯性想到了背包,那么暴力的解法应该是怎么样的呢?
-每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是$O(2^n)$,这里的n表示物品数量。
+每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是$o(2^n)$,这里的n表示物品数量。
**所以暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!**
@@ -109,7 +110,7 @@ for (int j = 0 ; j < weight[0]; j++) { // 当然这一步,如果把dp数组
dp[0][j] = 0;
}
// 正序遍历
-for (int j = weight[0]; j <= bagWeight; j++) {
+for (int j = weight[0]; j <= bagweight; j++) {
dp[0][j] = value[0];
}
```
@@ -135,8 +136,8 @@ dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化
```
// 初始化 dp
-vector> dp(weight.size(), vector(bagWeight + 1, 0));
-for (int j = weight[0]; j <= bagWeight; j++) {
+vector> dp(weight.size(), vector(bagweight + 1, 0));
+for (int j = weight[0]; j <= bagweight; j++) {
dp[0][j] = value[0];
}
@@ -160,7 +161,7 @@ for (int j = weight[0]; j <= bagWeight; j++) {
```
// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品
- for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
+ for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
@@ -174,7 +175,7 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品
```
// weight数组的大小 就是物品个数
-for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
+for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
for(int i = 1; i < weight.size(); i++) { // 遍历物品
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
@@ -219,32 +220,32 @@ dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向(包括
主要就是自己没有动手推导一下dp数组的演变过程,如果推导明白了,代码写出来就算有问题,只要把dp数组打印出来,对比一下和自己推导的有什么差异,很快就可以发现问题了。
-## 完整C++测试代码
+## 完整c++测试代码
-```CPP
+```cpp
void test_2_wei_bag_problem1() {
vector weight = {1, 3, 4};
vector value = {15, 20, 30};
- int bagWeight = 4;
+ int bagweight = 4;
// 二维数组
- vector> dp(weight.size(), vector(bagWeight + 1, 0));
+ vector> dp(weight.size(), vector(bagweight + 1, 0));
// 初始化
- for (int j = weight[0]; j <= bagWeight; j++) {
+ for (int j = weight[0]; j <= bagweight; j++) {
dp[0][j] = value[0];
}
// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品
- for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
+ for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
- cout << dp[weight.size() - 1][bagWeight] << endl;
+ cout << dp[weight.size() - 1][bagweight] << endl;
}
int main() {
@@ -267,27 +268,27 @@ int main() {
## 其他语言版本
-Java:
+### java
```java
public static void main(String[] args) {
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
- int bagSize = 4;
- testWeightBagProblem(weight, value, bagSize);
+ int bagsize = 4;
+ testweightbagproblem(weight, value, bagsize);
}
- public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){
- int wLen = weight.length, value0 = 0;
+ public static void testweightbagproblem(int[] weight, int[] value, int bagsize){
+ int wlen = weight.length, value0 = 0;
//定义dp数组:dp[i][j]表示背包容量为j时,前i个物品能获得的最大价值
- int[][] dp = new int[wLen + 1][bagSize + 1];
+ int[][] dp = new int[wlen + 1][bagsize + 1];
//初始化:背包容量为0时,能获得的价值都为0
- for (int i = 0; i <= wLen; i++){
+ for (int i = 0; i <= wlen; i++){
dp[i][0] = value0;
}
//遍历顺序:先遍历物品,再遍历背包容量
- for (int i = 1; i <= wLen; i++){
- for (int j = 1; j <= bagSize; j++){
+ for (int i = 1; i <= wlen; i++){
+ for (int j = 1; j <= bagsize; j++){
if (j < weight[i - 1]){
dp[i][j] = dp[i - 1][j];
}else{
@@ -296,8 +297,8 @@ Java:
}
}
//打印dp数组
- for (int i = 0; i <= wLen; i++){
- for (int j = 0; j <= bagSize; j++){
+ for (int i = 0; i <= wlen; i++){
+ for (int j = 0; j <= bagsize; j++){
System.out.print(dp[i][j] + " ");
}
System.out.print("\n");
@@ -305,10 +306,7 @@ Java:
}
```
-
-
-
-Python:
+### python
```python
def test_2_wei_bag_problem1(bag_size, weight, value) -> int:
rows, cols = len(weight), bag_size + 1
@@ -343,26 +341,26 @@ if __name__ == "__main__":
```
-Go:
+### go
```go
-func test_2_wei_bag_problem1(weight, value []int, bagWeight int) int {
+func test_2_wei_bag_problem1(weight, value []int, bagweight int) int {
// 定义dp数组
dp := make([][]int, len(weight))
for i, _ := range dp {
- dp[i] = make([]int, bagWeight+1)
+ dp[i] = make([]int, bagweight+1)
}
// 初始化
- for j := bagWeight; j >= weight[0]; j-- {
+ for j := bagweight; j >= weight[0]; j-- {
dp[0][j] = dp[0][j-weight[0]] + value[0]
}
// 递推公式
for i := 1; i < len(weight); i++ {
//正序,也可以倒序
- for j := weight[i];j<= bagWeight ; j++ {
+ for j := weight[i];j<= bagweight ; j++ {
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])
}
}
- return dp[len(weight)-1][bagWeight]
+ return dp[len(weight)-1][bagweight]
}
func max(a,b int) int {
@@ -379,19 +377,19 @@ func main() {
}
```
-javaScript:
+### javascript
```js
-function testWeightBagProblem (wight, value, size) {
+function testweightbagproblem (wight, value, size) {
const len = wight.length,
- dp = Array.from({length: len + 1}).map(
- () => Array(size + 1).fill(0)
+ dp = array.from({length: len + 1}).map(
+ () => array(size + 1).fill(0)
);
for(let i = 1; i <= len; i++) {
for(let j = 0; j <= size; j++) {
if(wight[i - 1] <= j) {
- dp[i][j] = Math.max(
+ dp[i][j] = math.max(
dp[i - 1][j],
value[i - 1] + dp[i - 1][j - wight[i - 1]]
)
diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md
index a57bae10..dabdfb2d 100644
--- a/problems/背包理论基础01背包-2.md
+++ b/problems/背包理论基础01背包-2.md
@@ -210,7 +210,7 @@ int main() {
## 其他语言版本
-Java:
+### Java
```java
public static void main(String[] args) {
@@ -240,7 +240,7 @@ Java:
-Python:
+### Python
```python
def test_1_wei_bag_problem():
weight = [1, 3, 4]
@@ -260,7 +260,7 @@ def test_1_wei_bag_problem():
test_1_wei_bag_problem()
```
-Go:
+### Go
```go
func test_1_wei_bag_problem(weight, value []int, bagWeight int) int {
// 定义 and 初始化
@@ -292,7 +292,7 @@ func main() {
}
```
-javaScript:
+### javaScript
```js
diff --git a/problems/背包问题理论基础多重背包.md b/problems/背包问题理论基础多重背包.md
index 80b9f8a1..a988db2c 100644
--- a/problems/背包问题理论基础多重背包.md
+++ b/problems/背包问题理论基础多重背包.md
@@ -89,7 +89,7 @@ int main() {
```
-* 时间复杂度:$O(m × n × k)$,m:物品种类个数,n背包容量,k单类物品数量
+* 时间复杂度:O(m × n × k),m:物品种类个数,n背包容量,k单类物品数量
也有另一种实现方式,就是把每种商品遍历的个数放在01背包里面在遍历一遍。
@@ -125,7 +125,7 @@ int main() {
}
```
-* 时间复杂度:$O(m × n × k)$,m:物品种类个数,n背包容量,k单类物品数量
+* 时间复杂度:O(m × n × k),m:物品种类个数,n背包容量,k单类物品数量
从代码里可以看出是01背包里面在加一个for循环遍历一个每种商品的数量。 和01背包还是如出一辙的。
@@ -245,7 +245,94 @@ if __name__ == '__main__':
Go:
+```go
+package theory
+import "log"
+
+// 多重背包可以化解为 01 背包
+func multiplePack(weight, value, nums []int, bagWeight int) int {
+
+ for i := 0; i < len(nums); i++ {
+ for nums[i] > 1 {
+ weight = append(weight, weight[i])
+ value = append(value, value[i])
+ nums[i]--
+ }
+ }
+ log.Println(weight)
+ log.Println(value)
+
+ res := make([]int, bagWeight+1)
+ for i := 0; i < len(weight); i++ {
+ for j := bagWeight; j >= weight[i]; j-- {
+ res[j] = getMax(res[j], res[j-weight[i]]+value[i])
+ }
+ log.Println(res)
+ }
+
+ return res[bagWeight]
+}
+```
+
+> 单元测试
+
+```go
+package theory
+
+import "testing"
+
+func Test_multiplePack(t *testing.T) {
+ type args struct {
+ weight []int
+ value []int
+ nums []int
+ bagWeight int
+ }
+ tests := []struct {
+ name string
+ args args
+ want int
+ }{
+ {
+ name: "one",
+ args: args{
+ weight: []int{1, 3, 4},
+ value: []int{15, 20, 30},
+ nums: []int{2, 3, 2},
+ bagWeight: 10,
+ },
+ want: 90,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := multiplePack(tt.args.weight, tt.args.value, tt.args.nums, tt.args.bagWeight); got != tt.want {
+ t.Errorf("multiplePack() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+```
+
+> 输出
+
+```
+=== RUN Test_multiplePack
+=== RUN Test_multiplePack/one
+2022/03/02 21:09:05 [1 3 4 1 3 3 4]
+2022/03/02 21:09:05 [15 20 30 15 20 20 30]
+2022/03/02 21:09:05 [0 15 15 15 15 15 15 15 15 15 15]
+2022/03/02 21:09:05 [0 15 15 20 35 35 35 35 35 35 35]
+2022/03/02 21:09:05 [0 15 15 20 35 45 45 50 65 65 65]
+2022/03/02 21:09:05 [0 15 30 30 35 50 60 60 65 80 80]
+2022/03/02 21:09:05 [0 15 30 30 35 50 60 60 70 80 80]
+2022/03/02 21:09:05 [0 15 30 30 35 50 60 60 70 80 80]
+2022/03/02 21:09:05 [0 15 30 30 35 50 60 60 70 80 90]
+--- PASS: Test_multiplePack (0.00s)
+ --- PASS: Test_multiplePack/one (0.00s)
+PASS
+```
-----------------------
diff --git a/problems/背包问题理论基础完全背包.md b/problems/背包问题理论基础完全背包.md
index 3cc8557c..faa1dc46 100644
--- a/problems/背包问题理论基础完全背包.md
+++ b/problems/背包问题理论基础完全背包.md
@@ -52,7 +52,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品
```CPP
// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
- for(int j = weight[i]; j < bagWeight ; j++) { // 遍历背包容量
+ for(int j = weight[i]; j <= bagWeight ; j++) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
@@ -94,7 +94,7 @@ dp状态图如下:
看了这两个图,大家就会理解,完全背包中,两个for循环的先后循序,都不影响计算dp[j]所需要的值(这个值就是下标j之前所对应的dp[j])。
-先遍历被背包在遍历物品,代码如下:
+先遍历背包在遍历物品,代码如下:
```CPP
// 先遍历背包,再遍历物品
@@ -183,11 +183,9 @@ private static void testCompletePack(){
int[] value = {15, 20, 30};
int bagWeight = 4;
int[] dp = new int[bagWeight + 1];
- for (int i = 0; i < weight.length; i++){
- for (int j = 1; j <= bagWeight; j++){
- if (j - weight[i] >= 0){
- dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
- }
+ for (int i = 0; i < weight.length; i++){ // 遍历物品
+ for (int j = weight[i]; j <= bagWeight; j++){ // 遍历背包容量
+ dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
}
for (int maxValue : dp){
@@ -201,8 +199,8 @@ private static void testCompletePackAnotherWay(){
int[] value = {15, 20, 30};
int bagWeight = 4;
int[] dp = new int[bagWeight + 1];
- for (int i = 1; i <= bagWeight; i++){
- for (int j = 0; j < weight.length; j++){
+ for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
+ for (int j = 0; j < weight.length; j++){ // 遍历物品
if (i - weight[j] >= 0){
dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
}
@@ -218,7 +216,7 @@ private static void testCompletePackAnotherWay(){
Python:
-```python3
+```python
# 先遍历物品,再遍历背包
def test_complete_pack1():
weight = [1, 3, 4]
diff --git a/problems/贪心算法总结篇.md b/problems/贪心算法总结篇.md
index 6da43ea3..6539501f 100644
--- a/problems/贪心算法总结篇.md
+++ b/problems/贪心算法总结篇.md
@@ -127,9 +127,9 @@ Carl个人认为:如果找出局部最优并可以推出全局最优,就是
贪心专题汇聚为一张图:
-
+
-这个图是 [代码随想录知识星球](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) 成员:[青](https://wx.zsxq.com/dweb2/index/footprint/185251215558842)所画,总结的非常好,分享给大家。
+这个图是 [代码随想录知识星球](https://programmercarl.com/other/kstar.html) 成员:[海螺人](https://wx.zsxq.com/dweb2/index/footprint/844412858822412)所画,总结的非常好,分享给大家。
很多没有接触过贪心的同学都会感觉贪心有啥可学的,但只要跟着「代码随想录」坚持下来之后,就会发现,贪心是一种很重要的算法思维而且并不简单,贪心往往妙的出其不意,触不及防!
@@ -145,18 +145,6 @@ Carl个人认为:如果找出局部最优并可以推出全局最优,就是
**一个系列的结束,又是一个新系列的开始,我们将在明年第一个工作日正式开始动态规划,来不及解释了,录友们上车别掉队,我们又要开始新的征程!**
-## 其他语言版本
-
-
-Java:
-
-
-Python:
-
-
-Go:
-
-
-----------------------
diff --git a/problems/链表理论基础.md b/problems/链表理论基础.md
index 109aa1ed..095282f5 100644
--- a/problems/链表理论基础.md
+++ b/problems/链表理论基础.md
@@ -120,9 +120,9 @@ head->val = 5;

-可以看出链表的增添和删除都是$O(1)$操作,也不会影响到其他节点。
+可以看出链表的增添和删除都是O(1)操作,也不会影响到其他节点。
-但是要注意,要是删除第五个节点,需要从头节点查找到第四个节点通过next指针进行删除操作,查找的时间复杂度是$O(n)$。
+但是要注意,要是删除第五个节点,需要从头节点查找到第四个节点通过next指针进行删除操作,查找的时间复杂度是O(n)。
# 性能分析
@@ -195,10 +195,20 @@ class ListNode {
```
Python:
-
+```python
+class ListNode:
+ def __init__(self, val, next=None):
+ self.val = val
+ self.next = next
+```
Go:
-
+```go
+type ListNode struct {
+ Val int
+ Next *ListNode
+}
+```
diff --git a/problems/面试题 02.07. 解法更新.md b/problems/面试题 02.07. 解法更新.md
new file mode 100644
index 00000000..6115d02e
--- /dev/null
+++ b/problems/面试题 02.07. 解法更新.md
@@ -0,0 +1,41 @@
+# 双指针,不计算链表长度
+设置指向headA和headB的指针pa、pb,分别遍历两个链表,每次循环同时更新pa和pb。
+* 当链表A遍历完之后,即pa为空时,将pa指向headB;
+* 当链表B遍历完之后,即pa为空时,将pb指向headA;
+* 当pa与pb相等时,即指向同一个节点,该节点即为相交起始节点。
+* 若链表不相交,则pa、pb同时为空时退出循环,即如果链表不相交,pa与pb在遍历过全部节点后同时指向结尾空节点,此时退出循环,返回空。
+# 证明思路
+设链表A不相交部分长度为a,链表B不相交部分长度为b,两个链表相交部分长度为c。
+在pa指向链表A时,即pa为空之前,pa经过链表A不相交部分和相交部分,走过的长度为a+c;
+pa指向链表B后,在移动相交节点之前经过链表B不相交部分,走过的长度为b,总合为a+c+b。
+同理,pb走过长度的总合为b+c+a。二者相等,即pa与pb可同时到达相交起始节点。
+该方法可避免计算具体链表长度。
+```cpp
+class Solution {
+public:
+ ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
+ //链表为空时,返回空指针
+ if(headA == nullptr || headB == nullptr) return nullptr;
+ ListNode* pa = headA;
+ ListNode* pb = headB;
+ //pa与pb在遍历过全部节点后,同时指向结尾空节点时退出循环
+ while(pa != nullptr || pb != nullptr){
+ //pa为空时,将pa指向headB
+ if(pa == nullptr){
+ pa = headB;
+ }
+ //pa为空时,将pb指向headA
+ if(pb == nullptr){
+ pb = headA;
+ }
+ //pa与pb相等时,返回相交起始节点
+ if(pa == pb){
+ return pa;
+ }
+ pa = pa->next;
+ pb = pb->next;
+ }
+ return nullptr;
+ }
+};
+```
diff --git a/problems/面试题02.07.链表相交.md b/problems/面试题02.07.链表相交.md
index e8198aa0..2e7226de 100644
--- a/problems/面试题02.07.链表相交.md
+++ b/problems/面试题02.07.链表相交.md
@@ -224,12 +224,14 @@ var getIntersectionNode = function(headA, headB) {
lenA = getListLen(headA),
lenB = getListLen(headB);
if(lenA < lenB) {
+ // 下面交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
+ // 如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
[curA, curB] = [curB, curA];
[lenA, lenB] = [lenB, lenA];
}
let i = lenA - lenB;
while(i-- > 0) {
- curA = curA.next
+ curA = curA.next;
}
while(curA && curA !== curB) {
curA = curA.next;
@@ -239,6 +241,43 @@ var getIntersectionNode = function(headA, headB) {
};
```
+TypeScript:
+
+```typescript
+function getIntersectionNode(headA: ListNode | null, headB: ListNode | null): ListNode | null {
+ let sizeA: number = 0,
+ sizeB: number = 0;
+ let curA: ListNode | null = headA,
+ curB: ListNode | null = headB;
+ while (curA) {
+ sizeA++;
+ curA = curA.next;
+ }
+ while (curB) {
+ sizeB++;
+ curB = curB.next;
+ }
+ curA = headA;
+ curB = headB;
+ if (sizeA < sizeB) {
+ [sizeA, sizeB] = [sizeB, sizeA];
+ [curA, curB] = [curB, curA];
+ }
+ let gap = sizeA - sizeB;
+ while (gap-- && curA) {
+ curA = curA.next;
+ }
+ while (curA && curB) {
+ if (curA === curB) {
+ return curA;
+ }
+ curA = curA.next;
+ curB = curB.next;
+ }
+ return null;
+};
+```
+
C:
```c