Skip to content

Commit 57a26f7

Browse files
committed
add algorithm
1 parent 14691c5 commit 57a26f7

27 files changed

Lines changed: 2653 additions & 0 deletions

AdavancedPart/KMP开发.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
KMP开发
2+
===
3+
4+
Kotlin Multiplatform(简称 KMP)是 JetBrains 推出的开源跨平台开发框架。
5+
6+
它可以通过共享 Kotlin 编写的业务逻辑代码实现多平台复用。
7+
8+
9+
从应用场景来看,KMP 不仅局限于移动端,它支持 iOS、Android、Web、桌面端(Windows/macOS/Linux)以及服务器端的代码共享。这种扩展性使得开发者能够用同一套代码库构建全平台应用,大幅提升开发效率。
10+
11+
12+
13+
KMP 有三大编译目标,分别是: Kotlin/JVM、Kotlin/Native、Kotlin/JS。通过编译不同的目标文件实现各端的跨平台能力。除此之外,KMP 还实验性地支持 WebAssembly(Kotlin/Wasm)编译目标,不过目前实际应用场景相对较少。
14+
15+
16+
### KMP编译器
17+
18+
19+
我们知道,一个语言的编译需要经过词法分析和语法分析,解析成抽象语法树 AST。
20+
而 KMP 为了将 Kotlin 源代码编译成不同的目标平台代码,就需要将 Kotlin 的编译产物进一步向不同的平台转化。
21+
Kotlin 语言的编译,与向不同的平台转化,明显是不同的职责,需要解耦,所以 KMP 的编译器必然有两个部分,也就是编译器前端(Frontend)与编译器后端(Backend)。
22+
Frontend 会将 AST 进一步转换为 Kotlin IR(Kotlin Intermediate Representation),是 Kotlin 源代码的中间表示形式,Kotlin IR 是编译器前端的输出,也是编译器后端的输入。
23+
Backend 则会吧 Kotlin IR 转换为不同平台的中间表示形式,最终生成目标代码。
24+
25+
作者:A0微声z
26+
链接:https://juejin.cn/post/7507888457705275455
27+
来源:稀土掘金
28+
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
29+
30+
31+
---
32+
33+
- 邮箱 :charon.chui@gmail.com
34+
- Good Luck!

Algorithm/37.矩阵置零.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
37.矩阵置零
2+
===
3+
4+
5+
6+
### 题目
7+
8+
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
9+
10+
11+
示例:
12+
13+
![image](https://raw.githubusercontent.com/CharonChui/Pictures/master/leetcode_setzeros_1.png?raw=true)
14+
15+
- 输入: matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
16+
- 输出: [[0,0,0,0],[0,4,5,0],[0,3,1,0]]
17+
18+
提示:
19+
20+
- m == matrix.length
21+
- n == matrix[0].length
22+
- 1 <= m, n <= 200
23+
- -231 <= matrix[i][j] <= 231 - 1
24+
25+
26+
进阶:
27+
28+
- 一个直观的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解决方案。
29+
- 一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
30+
31+
你能想出一个仅使用常量空间的解决方案吗?
32+
33+
34+
### 思路
35+
36+
37+
思路一: 用 O(m+n)额外空间
38+
39+
- 两遍扫matrix,第一遍用集合记录哪些行,哪些列有0;第二遍置0
40+
41+
```java
42+
class Solution {
43+
public void setZeroes(int[][] matrix) {
44+
Set<Integer> row_zero = new HashSet<>();
45+
Set<Integer> col_zero = new HashSet<>();
46+
int row = matrix.length;
47+
int col = matrix[0].length;
48+
for (int i = 0; i < row; i++) {
49+
for (int j = 0; j < col; j++) {
50+
if (matrix[i][j] == 0) {
51+
row_zero.add(i);
52+
col_zero.add(j);
53+
}
54+
}
55+
}
56+
for (int i = 0; i < row; i++) {
57+
for (int j = 0; j < col; j++) {
58+
if (row_zero.contains(i) || col_zero.contains(j)) matrix[i][j] = 0;
59+
}
60+
}
61+
}
62+
}
63+
```
64+
65+
思路二: 用O(1)空间
66+
67+
关键思想: 用matrix第一行和第一列记录该行该列是否有0,作为标志位
68+
69+
但是对于第一行,和第一列要设置一个标志位,为了防止自己这一行(一列)也有0的情况.注释写在代码里,直接看代码很好理解!
70+
71+
思路:第一次循环
72+
1、0行数组的每个元素临时标识该元素所在列是否有0,0列数组的每个元素临时标识该元素所在行是否有0。
73+
2、判断每行的第0列是否为0,有则赋值额外的标识字段,该字段的作用是用于后续对称遍历的时候,赋值所有行的0列是否应该赋值0使用
74+
75+
```
76+
比如原始数组为
77+
[
78+
[2,1,2,3],
79+
[2,1,2,3],
80+
[3,0,5,2],
81+
[1,3,0,5]
82+
]
83+
84+
85+
// 如果当前遍历位置为0,则该行0列设置标识,以便后续使用
86+
// 如果当前遍历位置为0,则0行该列设置标识,以便后续使用
87+
[
88+
[2,0,0,3],
89+
[2,1,2,3],
90+
[0,0,5,2],
91+
[0,3,0,5]
92+
]
93+
// 第二次循环-从右下角遍历处理:行从最后一行遍历(直到0行),列从最后一列遍历(直到第一列)
94+
// 使用0行数组、0列数组判断当前遍历位置是否应该置0
95+
// 注意需要额外使用标识字段判断该行0列是否应该置0
96+
// 处理完成之后
97+
[
98+
[2,0,0,3],
99+
[2,0,0,3],
100+
[0,0,0,0],
101+
[0,0,0,0]
102+
]
103+
104+
```
105+
106+
107+
```java
108+
109+
class Solution {
110+
public void setZeroes(int[][] matrix) {
111+
int row = matrix.length;
112+
int col = matrix[0].length;
113+
boolean row0_flag = false;
114+
boolean col0_flag = false;
115+
// 第一行是否有零
116+
for (int j = 0; j < col; j++) {
117+
if (matrix[0][j] == 0) {
118+
row0_flag = true;
119+
break;
120+
}
121+
}
122+
// 第一列是否有零
123+
for (int i = 0; i < row; i++) {
124+
if (matrix[i][0] == 0) {
125+
col0_flag = true;
126+
break;
127+
}
128+
}
129+
// 把第一行第一列作为标志位
130+
for (int i = 1; i < row; i++) {
131+
for (int j = 1; j < col; j++) {
132+
if (matrix[i][j] == 0) {
133+
matrix[i][0] = matrix[0][j] = 0;
134+
}
135+
}
136+
}
137+
// 置0
138+
for (int i = 1; i < row; i++) {
139+
for (int j = 1; j < col; j++) {
140+
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
141+
matrix[i][j] = 0;
142+
}
143+
}
144+
}
145+
if (row0_flag) {
146+
for (int j = 0; j < col; j++) {
147+
matrix[0][j] = 0;
148+
}
149+
}
150+
if (col0_flag) {
151+
for (int i = 0; i < row; i++) {
152+
matrix[i][0] = 0;
153+
}
154+
}
155+
}
156+
}
157+
```
158+
159+
160+
161+
---
162+
- 邮箱 :charon.chui@gmail.com
163+
- Good Luck!
164+
165+

Algorithm/38.生命游戏.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
38.生命游戏
2+
===
3+
4+
5+
### 题目
6+
7+
8+
生命游戏,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
9+
10+
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
11+
12+
- 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
13+
- 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
14+
- 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
15+
- 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
16+
17+
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是 同时 发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。
18+
19+
给定当前 board 的状态,更新 board 到下一个状态。
20+
21+
注意 你不需要返回任何东西。
22+
23+
24+
示例1:
25+
26+
27+
![image](https://raw.githubusercontent.com/CharonChui/Pictures/master/leetcode_gameOfLife_1.jpg?raw=true)
28+
29+
- 输入:board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
30+
- 输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
31+
32+
33+
示例2:
34+
35+
![image](https://raw.githubusercontent.com/CharonChui/Pictures/master/leetcode_gameOfLife_1.jpg?raw=true)
36+
37+
- 输入:board = [[1,1],[1,0]]
38+
- 输出:[[1,1],[1,1]]
39+
40+
41+
提示:
42+
43+
- m == board.length
44+
- n == board[i].length
45+
- 1 <= m, n <= 25
46+
- board[i][j] 为 0 或 1
47+
48+
49+
进阶:
50+
51+
你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
52+
本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?
53+
54+
55+
### 思路
56+
57+
简化规则:
58+
1. 原来是活的,周围有2-3个活的,成为活的
59+
2. 原来是死的,周围有3个活的,成为活的
60+
3. 其他都是死了
61+
62+
这道题主要就是模拟,遍历每一个格子,然后统计其周围八个格子的活细胞个数,来看这个格子的状态是否改变。
63+
但难点在于:如果这个格子的状态改变,不能直接改变。这样会影响后面格子的统计。即题目中说的:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
64+
65+
因此我们需要使用特殊值去标记发生改变的格子,从而根据特殊值可以知道这个格子原状态是什么,要更新的状态是什么。
66+
67+
68+
- 可以使用 2表示活细胞变成死细胞,3表示死细胞变成活细胞。【这样的好处是最终是死细胞的都是偶数,活细胞的都是奇数,模2即结果;】
69+
70+
也可以用下面的方式:
71+
72+
- 由于每个位置的细胞的状态是取决于当前四周其他状态的,而且每个细胞的状态是同时变化的,所以不能一个一个地更新,只能在一个新的数组里创建新的状态。
73+
74+
- 当然上面所说的也不是绝对的,因为这道题目的输入是int[][],矩阵是 int 类型的,有 32 位,而状态只有 0,1 两种,只需一位且只有最低位用上了,我们用其他位存储下一个状态即可,相当于用原矩阵当一个复制的矩阵。
75+
76+
- 所以可以,原有的最低位存储的是当前状态,那倒数第二低位存储下一个状态就行了。
77+
78+
79+
```java
80+
class Solution {
81+
public void gameOfLife(int[][] board) {
82+
int m = board.length; // 行数
83+
int n = board[0].length; // 列数
84+
int count = 0; // 统计每个格子周围八个位置的活细胞数
85+
for(int i = 0; i < m; i++){
86+
for(int j = 0; j < n; j++){
87+
count = 0; // 每个格子计数重置为0
88+
for(int x = -1; x <= 1; x++){ // -1 0 1 分别代表当前位置左边的格子、当前格子、当前位置右边的格子
89+
for(int y = -1; y <= 1; y++){ // -1 0 1 分别代表当前位置下边的格子、当前格子、当前位置上边的格子
90+
// 枚举周围八个位置,其中去掉本身(x = y = 0)和越界(靠近边缘)的情况
91+
if((x == 0 && y == 0) || i + x < 0 || i + x >= m || j + y < 0 || j + y >= n)continue;
92+
// 如果周围格子是活细胞(1)或者是活细胞变死细胞(2)的,都算一个活细胞
93+
if(board[i + x][j + y] == 1 || board[i + x][j + y] == 2) {
94+
count++;
95+
}
96+
}
97+
}
98+
99+
if(board[i][j] == 1 && (count < 2 || count > 3)) {
100+
board[i][j] = 2; // 格子本身是活细胞,周围满足变成死细胞的条件,标记为2
101+
}
102+
103+
if(board[i][j] == 0 && count == 3) {
104+
board[i][j] = 3; // 格子本身是死细胞,周围满足复活条件,标记为3
105+
}
106+
}
107+
}
108+
109+
for(int i = 0; i < m; i++){
110+
for(int j = 0; j < n; j++){
111+
// 死细胞为0,活细胞变成死细胞为2,都为偶数,模2为0,刚好是死细胞
112+
// 活细胞为1,死细胞变成活细胞为3,都为奇数,模2为1,刚好是活细胞
113+
board[i][j] %= 2;
114+
}
115+
}
116+
}
117+
}
118+
```
119+
120+
121+
122+
123+
124+
125+
126+
127+
128+
129+
130+
131+
132+
133+
---
134+
- 邮箱 :charon.chui@gmail.com
135+
- Good Luck!
136+
137+

0 commit comments

Comments
 (0)