Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >
(LeetCode系列)刷题记

  1. 在线阅读
  2. 查看对应源码

静下心回顾自己的2021,才发现这一年自己竟然经历了这么多重要的事情,实习,工作,爱情;是的,就是这样一个人生转折点的路上,庆幸的是自己可以有这么多选项,有趣的是这也是自己最后一年以学生身份做的个人总结(明年总结的时候就是一个社畜了,莫名想说无忧无虑的学生时光真好,不仅仅是因为毕业以后一天就没有几个小时真正属于自己,更因为成年人的时间永远都用来生存而不是生活。

回想2021,这一年属实经历了很多,不仅拿到了通往下一站的门票,还有幸习得了一些新技能,比如重拾之前的摄影爱好,也感谢全哥带领并悉心指导,经过两次游泳终于在可以在水里站立起来了;还和咯噔还有邓颖从下午场开始连玩两场剧本杀一直到凌晨1点(年轻么就是要疯狂);也很幸运这一年做了几场分享,给学弟学妹们做了两场分享(秋招分享+iTeach大赛分享);甚至还作为采访者帮助学弟完成马克思作业的一部分;不过最幸运的是是认识了你,最开心的便是2021.12.12和你表白的那天,想了一个瞒天过海的方案,不过还是没有骗过你,让你生了气,好在终于在进入你实验室的那一瞬间你仿佛知道发生了什么,谢谢你来到我身边!

今年的大事记:

2021-06-07 ~ 2021-09-18

实习:阿里云-洛神云网络-ALB产品组实习

2021-07-18 ~ 2021-12-01

工作:备战秋招

2021-11-16 - now

love ing

学习

备战实习+工作

后面有时间会整理一下面经,围绕着Go实习+秋招的40多场技术面试,在这里还是要衷心感谢雷兄,晓辉师弟,雅晶师妹以及室友阿鑫,如果没有你们借校园卡去预约研讨室,可能我的求职之旅不会这样一帆风顺。。。。

  1. 虽然大多数时间在刷题和备战工作,但每天大多都是一个规律也就是蹂躏算法或者被算法蹂躏…….然后就是八股文之类的,僵硬~
  2. 发现刷题其实对自己语法层面的提升也有一定好处,比如经常会涉及到数据结构的内置操作,可以细心去看一下内部的阻止结构以及源码

参加线下技术分享大会

2021-04-10

Gopher Meetup 武汉站:博主求职的方向是Go,说来也是缘分,之前水群的时候认识了华科的,后来在朋友圈看到他发的一个Gopher Meetup武汉站的邀请,看了一下那两天没啥时间安排,就怂恿着海金和凯琦去了,有幸见到了谢大,曹大,茶歇的时候厚着脸皮加了曹大微信😂(不得不说曹大人太Nice了,后面找实习曹大还帮忙推了一下)

2021-06-26 ~ 2021-06-27

Gopher China 2021大会:从顺义赶到西北旺太远了,单程1个小时40多分钟地铁😂

todo:这里要放图

实习

有幸来到阿里云-洛神云网络实习,感谢柱总和商总的关怀和支持,还有mentor淮安师兄以及mentor乃斌师兄的悉心指导,有幸体会到了阿里内部生态的完善,感受到了组里学习与娱乐氛围的浓厚,来之前已经准备好接受脉脉中传说中的pua;遗憾的是hc有限,没有与各位师兄成为同事,不过希望以后有机会一起并肩作战

没想到第一份实习氛围就这么Nice,脉脉中说的pua一丝都没体会到,就是大家任务比较紧而已,但是一想到每次上线之后的成就觉得很酷

看完脉脉上对阿里的吐槽:

  • 来之前: 卷王 + pua
  • 来之后: 每周一次的技术交流 + 每周一次的饭局 + 每天各位师兄开着各种逗比的玩笑🤣

todo:放几张实习的图,

总结:

  1. 内部整个生态的完善(研发+产品管理+内部效率工具以及一些机制)+ 大牛云集的师兄们
  2. 大家都在提倡反内卷,每次一提到什么事情都是顺其自然,其实真正的顺其自然不代表你不努力,而是你在努力的前提下去让结果顺其自然。

校招

感谢汪晓祯的内推,面微软的时候有幸结识了几位伙伴,记得群里的一位小伙伴说去微软就是为祖国尽一份力,去拖慢美帝科技进步的步伐(🤣),最后也感谢华科的一位大佬组织的候选人交流群

面试详情请戳:我的微软面试之旅

其他

This is a picture without description
This is a picture without description
This is a picture without description

todo: 放照片

11月分享自己的求职经历 + 认识

面试流程

按照一个搬砖的胖子 所讲的,大致流程如下:

  1. 平行面
  2. Leader面
  3. AA面(最终的大老板面)

HXyFGL

面试流程

平行面(第一面) 2021-11-08

自我介绍 + 算法(汉诺塔非递归)

平行面(第二面) 2021-11-10

自我介绍 + 二叉树的遍历(层次遍历不分层打印,层次遍历分层打印,层次遍历交叉层打印) + 最后闲聊

leader面 2021-11-12

自我介绍 + 实习项目介绍 + 行李箱开锁(3个密码,每次只能转一个,如果3个都相同将会死锁)

AA面 2021-11-30

等这么晚的原因就是国内的老板们太忙了

自我介绍 + 八股文扩展(估计teams带宽,操作系统内存分配 + 堆栈) + 两个链表相交(说思路) + 扩展到求两个树的第一个公共节点(讲思路) + 求平方根(写)

总结

自己投递的是cmd,部门处于快速扩张中,主要做的将我们传统的windows 365的业务搬移到云端

经过几轮面试的经历之后,还是感慨微软注重求职者的逻辑与解决问题的能力,通过算法考察体现,甚至算法中遇到的一些细节还会涉及到计算机的一些基础知识,比如我在写求解平方根代码的时候,直接使用if a * a > b这个代码,导致a*a结果可能溢出,面试官问了溢出的底层原理(所有数字在操作系统中都是通过二进制的补码来表示的,如果发生溢出将会丢弃高位)

方法一:二分

  • 时间复杂度:O(logN)
  • 空间复杂度:O(1)

注意点:

  1. mid*mix > x的时候容易溢出,改成mid > x/mid
  2. mid := (l+r)>>1的时候也容易溢出,改成mid := l + (r-l)>>1
不保留小数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//不保留小数
func Sqrt(x int) int {

if x <= 0 {
return 0
}

l, r := 1, x-1
for l <= r {
mid := l + (r-l)>>1
if mid > x/mid {
r = mid - 1
} else if mid < x/mid { //Tips:写成x/mid用来确认不会溢出
l = mid + 1
} else {
return mid
}
}

return r
}

求两棵树的第一个交点

首先判断两棵树是否相交,可以根据判断两个链表是否相交扩展而来,如果相交的话,将第1个链表的末尾指向第1个链表的头部将会成环,因此借鉴这种思路

自己的思路如下:KlYSSU

验证两棵树是否相交

思路:Morris先序遍历,遍历第1棵树的时候将叶节点的左指针指向root1,然后遍历root2,如果发现某一个节点左指针指向第1个树根,说明相交,包含了两个树中有一个是子树的情况

时间复杂度:O(N)
空间复杂度:O(1)

前言

两个链表相交涉及到关于链表的一系列问题:其中包含了如下子问题,并且涉及到了大量的算法思想,比如快慢指针

  1. 判断链表是否有环
  2. 链表有环的情况下找到环的入口节点
  3. 判断两个链表是否相交
  4. 相交的情况下找到相交时候的第一个节点

对应的7种情况

6aSs8P

解决代码

执行测试命令:go test -v intersection_test.go intersection.go -test.run TestIntersection

解决代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package InterviewCoding

/**
* @Author: yirufeng
* @Date: 2021/11/25 8:21 上午
* @Email: yirufeng@foxmail.com
* @GitHub: https://www.github.com/sivanWu0222
* @Desc:
**/

type LinkedListNode struct {
Val int
Next *LinkedListNode
}

/*
题目:返回两个链表相交的节点
步骤:
1. 明确题目中的意思,两个链表有无环,是否一定相交(如果不一定的话,我们需要判断是否相交,只有在相交的情况下我们才可以返回相交的第一个节点)
2. 分情况进行讨论:
2.1 有环
2.1.1 一定相交:
直接找到相交的节点即可
2.1.2 不一定相交:
首先判断是否相交
相交的时候,找到相交的第一个节点并返回
2.2 无环
2.2.1 一定相交:
2.2.2 不一定相交:
首先判断是否相交
相交的时候,找到相交的第一个节点并返回
2.3 1个有环1个无环,说明一定不会相交,直接返回Nil就可以了
*/

//返回两个链表的相交节点,如果不相交返回Nil,否则返回相交的第一个节点
func Intersection(l1, l2 *LinkedListNode) *LinkedListNode {
//如果有一个为空,直接说明不相交
if l1 == nil || l2 == nil {
return nil
}

//判断两个链表是否有环,如果有环就返回环的起始节点
startOfl1Ring, startOfl2Ring := startNodeOfRing(l1), startNodeOfRing(l2)
//如果1个有1个没有,说明白不相交,直接返回nil
if ((startOfl1Ring == nil) && (startOfl2Ring != nil)) || ((startOfl1Ring != nil) && (startOfl2Ring == nil)) {
return nil
}

var ret *LinkedListNode
if startOfl1Ring != nil { //如果有环
ret = findIntersectionNodeWhenBothHaveCycle(l1, l2, startOfl1Ring, startOfl2Ring)
} else { //如果没有环:此时就转换为我们正常的题解了
ret = findIntersectionNodeWhenBothNoCycle(l1, l2)
}

return ret
}

//当两个链表都没有环的时候,找到两个链表的相交节点(可能相交,可能不相交)
//三种解法去找出第一个相交的节点
func findIntersectionNodeWhenBothNoCycle(l1, l2 *LinkedListNode) *LinkedListNode {
//1. 获得第1个链表第2个链表的长度以及各自链表的最后一个节点
l1Length, l2Length := 1, 1
l1LastNode, l2LastNode := l1, l2
for l1LastNode.Next != nil {
l1Length, l1LastNode = l1Length+1, l1LastNode.Next
}
for l2LastNode.Next != nil {
l2Length, l2LastNode = l2Length+1, l2LastNode.Next
}

//判断各自最后一个节点是否相等,如果不相等,说明不相交,直接返回nil
if l1LastNode != l2LastNode {
return nil
}

//先走长度差
l1LastNode, l2LastNode = l1, l2
if l1Length > l2Length {
for i := 0; i < l1Length-l2Length; i++ {
l1LastNode = l1LastNode.Next
}
} else {
for i := 0; i < l2Length-l1Length; i++ {
l2LastNode = l2LastNode.Next
}
}

//然后一起走,如果中途相等直接返回
for l1LastNode != l2LastNode {
l1LastNode, l2LastNode = l1LastNode.Next, l2LastNode.Next
}

return l1LastNode
}

//当两个链表都有环的时候,找到两个链表的相交节点(可能相交,可能不相交(各自有一个自己的环))
func findIntersectionNodeWhenBothHaveCycle(l1, l2, startOfl1Ring, startOfl2Ring *LinkedListNode) *LinkedListNode {
//判断两个链表是否有环的时候我们可以找到链表有环时候环的第一个节点
//如果两个环的起始节点都相同,此时问题转变为l1的起始节点到自己拥有环的开始节点和l2的起始节点到自己拥有环的开始节点的第一个相交节点
if startOfl1Ring == startOfl2Ring {
l1Temp, l2Temp := l1, l2
//转变为求两个无环单链表的相交节点
var l1TempLength, l2TempLength int

for l1Temp != startOfl1Ring {
l1Temp, l1TempLength = l1Temp.Next, l1TempLength+1
}

for l2Temp != startOfl2Ring {
l2Temp, l2TempLength = l2Temp.Next, l2TempLength+1
}

//长的先走
if l1TempLength > l2TempLength {
for i := 0; i < l1TempLength-l2TempLength; i++ {
l1Temp = l1Temp.Next
}
} else {
for i := 0; i < l2TempLength-l1TempLength; i++ {
l2Temp = l2Temp.Next
}
}

//此时一起走
for l1Temp != l2Temp {
l1Temp, l2Temp = l1Temp.Next, l2Temp.Next
}
return l1Temp

} else { //如果两个环的起始节点不同,让其中一个环开始走,走到起始节点为止,如果中途会出现和另外一个环起始节点相同,说明相交,否则各自成环
l1Temp := startOfl1Ring.Next
for l1Temp != startOfl1Ring {
if l1Temp == startOfl2Ring {
return startOfl2Ring
}
l1Temp = l1Temp.Next
}
}

return nil
}

//返回链表是否有环,如果有的话直接返回true,否则返回false
func startNodeOfRing(head *LinkedListNode) *LinkedListNode {
fast, slow := head, head
for fast != nil && fast.Next != nil {
fast, slow = fast.Next.Next, slow.Next
//说明此时相遇,表明有环
if fast == slow {
break
}
}

//这里是第二个注意点,这里不仅仅要判断fast是否为空,还要判断fast.Next是否为空,
//因为让链表停止上面for循环的有两个条件:1. fast为空 2. fast.Next为空
//判断一下是因为fast为空或者fast.Next为空还是fast==slow退出的
if fast == nil || fast.Next == nil {
return nil
}

//走到这里说明有环,此时让fast指向head,然后fast与slow每次都走一步,一起相遇的时候返回即可
fast = head
for fast != slow {
fast, slow = fast.Next, slow.Next
}
return fast
}
测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package InterviewCoding

import (
"testing"
)

/**
* @Author: yirufeng
* @Date: 2021/11/25 11:05 上午
* @Email: yirufeng@foxmail.com
* @GitHub: https://www.github.com/sivanWu0222
* @Desc:
**/

//测试两个链表
func TestIntersection(t *testing.T) {
//模拟场景
//1.1 两个不带环且不相交的链表
l1, l2 := constructTwoLinkedListWithoutCycleAndNotIntersection()
if Intersection(l1, l2) != nil {
t.Fatalf("expect no intersection node.")
}

//1.2 两个不带环但是相交的链表:LeetCode原题
//已修复,由于&&写成了||
l1, l2 = constructTwoLinkedListWithoutCycleButIntersection()
if Intersection(l1, l2) == nil {
t.Log(Intersection(l1, l2))
t.Fatalf("expect to have intersection node.")
}

//2. 1个链表有环,1个无环
l1, l2 = constructTwoLinkedListButOneHasCycle()
if Intersection(l1, l2) != nil {
t.Fatalf("expect no intersection node.")
}
//3. 两个链表都有环
//3.1 两个链表都有环,但是两个链表的环都是各自的
l1, l2 = constructTwoLinkedBothHasOwnCycle()
if Intersection(l1, l2) != nil {
t.Fatalf("expect no intersection node.")
}

//3.2.1 两个链表都有环,并且两个链表是相交的,且交点在进入环之前
//todo: 有问题
l1, l2 = constructTwoLinkedListBothHasCycleAndIntersection()
if Intersection(l1, l2) == nil {
t.Fatalf("expect to have intersection node.")
}
//3.2.2 两个链表都有环,并且两个链表是相交的,且交点在环上
l1, l2 = constructTwoLinkedListBothHasCycleAndIntersectionII()
if Intersection(l1, l2) == nil {
t.Fatalf("expect to have intersection node.")
}
}

//两个不带环但是相交的链表
func constructTwoLinkedListWithoutCycleButIntersection() (l1, l2 *LinkedListNode) {
node := &LinkedListNode{Val: 5}
l1, l2 = &LinkedListNode{Val: 3}, &LinkedListNode{Val: 4, Next: &LinkedListNode{Val: 6, Next: node}}
l1.Next, l2.Next = node, node
return l1, l2
}

//两个不带环且不相交的链表
func constructTwoLinkedListWithoutCycleAndNotIntersection() (l1, l2 *LinkedListNode) {
l1, l2 = &LinkedListNode{Val: 3, Next: &LinkedListNode{Val: 4}}, &LinkedListNode{Val: 5, Next: &LinkedListNode{Val: 6}}
return l1, l2
}

//构造两个链表1个有环1个无环
func constructTwoLinkedListButOneHasCycle() (l1, l2 *LinkedListNode) {
node, node2 := &LinkedListNode{Val: 2}, &LinkedListNode{Val: 3}
node.Next, node2.Next = node2, node
l1, l2 = &LinkedListNode{Val: 1, Next: node}, &LinkedListNode{Val: 3}
return l1, l2
}

//构造两个都有环的链表,但是两个链表的环都是各自的
func constructTwoLinkedBothHasOwnCycle() (l1, l2 *LinkedListNode) {
node, node2 := &LinkedListNode{Val: 2}, &LinkedListNode{Val: 3}
node.Next, node2.Next = node2, node
node3, node4 := &LinkedListNode{Val: 4}, &LinkedListNode{Val: 5}
node3.Next, node4.Next = node4, node3
l1, l2 = &LinkedListNode{Val: 1, Next: node}, &LinkedListNode{Val: 3, Next: node3}
return l1, l2
}

//构造两个都有环的链表,但是两个链表都是相交的且相交的起点位于环的入口之前
func constructTwoLinkedListBothHasCycleAndIntersection() (l1, l2 *LinkedListNode) {
node, node2 := &LinkedListNode{Val: 4}, &LinkedListNode{Val: 5}
node.Next, node2.Next = node2, node
node3 := &LinkedListNode{Val: 6, Next: node}
l1, l2 = &LinkedListNode{Val: 1, Next: node3}, &LinkedListNode{Val: 3, Next: node3}
return l1, l2
}

//构造两个都有环的链表,但是两个链表都是相交的且相交的起点位于环上
func constructTwoLinkedListBothHasCycleAndIntersectionII() (l1, l2 *LinkedListNode) {
node, node2 := &LinkedListNode{Val: 4}, &LinkedListNode{Val: 5}
node.Next, node2.Next = node2, node
l1, l2 = &LinkedListNode{Val: 1, Next: node}, &LinkedListNode{Val: 3, Next: &LinkedListNode{Val: 6, Next: node2}}
return l1, l2
}

Morris遍历:

优点:不同于我们之前递归遍历或者借助栈来进行树的遍历,因为Morris遍历可以在保证时间复杂度依然为O(N)的情况下可以将空间复杂度降低为O(1)

假设当前节点为cur,最一开始来到树根,什么时候停呢,当cur来到nil的时候就停止

  1. 如果当前节点没有左子树,直接向右移动
  2. 如果当前节点有左子树,找到当前节点左子树的最右节点mostRight
    1. 如果mostRight的右指针指向null,那么mostRight.Right=Cur,cur=cur.Left
    2. 如果mostRight的右指针指向cur,那么mostRight.Right=nil,cur=cur.Right

遍历之后发现一个问题:任何一个节点,只要有左孩子Morris遍历的时候会遍历两次,也就是有左树的节点,左树不为空,首先遍历当前节点,然后遍历左树,遍历之后再回到当前节点,所以当前节点会遍历两次。因为用这个就可以知道是第一次来到自己还是第二次来到自己。

Morris先序遍历:

第一次遍历到该节点的时候直接打印(没有左子树的节点会遍历到一次,有左子树的节点会遍历到两次,但是我们第一次就应该打印)

Morris先序遍历代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func MorrisPreOrderTraverse(root *TreeNode) []int {
if root == nil {
return nil
}

var mostRight *TreeNode
cur := root
var ret []int
for cur != nil {
//如果当前节点没有左子树
if cur.Left == nil { //当前节点向右移动 (只有没有左子树的节点会走这一步)
ret = append(ret, cur.Val)
cur = cur.Right
} else { //找到当前节点左子树的最右节点
mostRight = cur.Left

for mostRight.Right != nil && mostRight.Right != cur {
mostRight = mostRight.Right
}
//此时mostRight就是当前节点左子树的最右节点
if mostRight.Right == nil { //说明是第一次来到
ret = append(ret, cur.Val)
mostRight.Right, cur = cur, cur.Left
} else { //说明是第二次来到(只有有左子树的时候才会有第二次来到)
mostRight.Right, cur = nil, cur.Right
}
}
}

return ret
}

docsify

主要用于平常的文档撰写,或者大量一系列的文章合集

安装以及使用

假设已经安装了node

  1. 安装:npm i docsify-cli -g
  2. 使用:docsify init ./docs
  3. 进入docs目录,启动预览:docsify serve ./

参考

  1. docsify中文网
  2. 自己的docsify配置
  3. 有了docsify神器,从此爱上看文档

前端

https://hellogithub.com/ 上有很多精选各种语言的开源项目,比如前端,Go,C等等

  1. GitHub 上一本开源的前端技术书籍:《前端内参》。该书共有 11 章,覆盖了技术面试、JavaScript 特性解析、数据结构与算法、主流框架、开发工具、编程范式、设计原则与编程范式等内容。
  2. 如果你是刚开始学前端的同学,又在找练手的项目,不妨来看下这个 GitHub 项目:50projects50days。该项目一共含有 50 个纯使用 HTML、CSS 和 JavaScript 实现的小 Demo,这当中有加载动画、时钟主题、密码生成器、动画倒计时、在线绘图等多个小应用。
    • GitHub:github.com/bradtraversy/50projects50days
  3. Map of Javascript,开源的 JavaScript、数据结构与算法知识图谱。可用于日常工作或学习时的查漏补缺(高清大图见 GitHub Repo)。
    • GitHub:github.com/mechaniac/Map-of-Javascript
  4. GitHub 上一个基于 Vue3.x + TypeScript 的在线演示文稿应用:PPTist。还原了大部分 PPT 常用功能,支持文字、图片、形状、线条、图表、表格等 6 种最常用的元素类型。每种元素都拥有高度可编辑能力,同时支持丰富的快捷键和右键菜单,尽可能还原本地桌面应用的使用体验。
  5. 微软在 GitHub 开源了一份 Web 开发教程:《给初学者看的 Web 开发教程》(Web Development for Beginners)。课时为期 12 周,共 24 节,主要讲解 JavaScript、CSS、HTML 相关的基础知识,并通过开发打字游戏、浏览器扩展、太空游戏等多个项目,带你了解 Web 开发的整体流程。为了让开发者可以更为充分的吸收课程知识,每节课均附有课前课后测验、课程说明、草图笔记、项目挑战、作业任务等内容。
    • GitHub:github.com/microsoft/Web-Dev-For-Beginners
  6. 一款开源的单词记忆与英语肌肉记忆锻炼软件:Qwerty Learner。主要将英语单词的记忆与键盘输入的肌肉记忆相结合,可在背诵单词的同时巩固肌肉记忆。此外,该软件还为程序员内置了计算机常用单词库与 API,方便大家练习工作中常用的单词、提高输入速度。
  7. GitHub 上一个前端知识库:CSS Protips,里面收集并整理了 CSS 相关的一些开发及使用技巧。
    • GitHub:github.com/AllThingsSmitty/css-protips
  8. 前不久,国外开发者 bedimcode 在 GitHub 开源了一个万圣节网站:Responsive Halloween Website 🎃,并附带了完整的项目开发教程。该网站主要具备响应式布局、滚动动画、移动端优化、多平台设备兼容等特点。整体设计风格优雅、简洁,感兴趣的同学可以拿去练练手
  9. 剑指前端 Offer: https://github.com/hzfe/awesome-interview
  10. GitHub 上一个开源的音乐网站管理系统:Music Website,实现了歌单、播放器、用户及音乐管理等常用功能。前端基于 Vue,后端采用 Spring Boot + MyBatis + MySQL 进行实现,作者为该项目提供了项目实现文档,感兴趣的同学可以看下。
    • GitHub:github.com/Yin-Hongwei/music-website
  11. GitHub 上一个基于 Go 与 Typescript 开发的看板开源软件:taskcafe。支持对任务进行筛选过滤、打标签、添加截止日期、分配成员、制定流程等操作。
    • GitHub:github.com/JordanKnott/taskcafe
  12. GitHub 上看到一款颜值颇高的第三方网易云播放器:YesPlayMusic。基于 Vue.js 全家桶开发,内置所有音乐播放器基础功能,支持 MV 播放、暗黑模式、自定义快捷键等功能。
    • GitHub:github.com/qier222/YesPlayMusic
  13. 《图解 React 源码系列》,作者将通过大量配图,把 react 原理表述清楚。主要包含 React 基础概念、运行核心、数据管理、交互、高频算法等内容。
    • GitHub:github.com/7kms/react-illustration-series
  14. 分享 GitHub 上一本开源的前端技术书籍:《带你入门前端工程》,主要记录作者在近两年来,在前端工程师实践经验和学习心得方面的总结。书中大部分内容以理论知识 + 代码示例 + 图片进行讲解,部分章节会提供实践教程。
    • GitHub:github.com/woai3c/introduction-to-front-end-engineering
  15. 一个值得前端开发者收藏学习的 GitHub 项目:Browser 2020。里面收集了浏览器中一些较为冷门,却异常实用的接口,如商品支付、社交网络分享、消息推送、视频画中画、剪切板、AR 预览、用户身份凭证管理等多种接口。如果你想给自己的 Web 项目加点新功能,或许可以在上面找找灵感。
    • GitHub:github.com/luruke/browser-2020
  16. 推荐 GitHub 上一本开源的前端书籍:《深入理解 TypeScript》(TypeScript Deep Dive)。该书将从基础到深入,全面阐述 TypeScript 的各种魔法,并结合实际场景用例,让你更深入的理解 TypeScript。
    • 中文版:github.com/jkchao/typescript-book-chinese
    • 原文版:github.com/basarat/typescript-book
  17. OnlineJudge,青岛大学开源的一款在线评测系统,基于 Python 与 Vue 编写而成。
    • GitHub:github.com/QingdaoU/OnlineJudge
    • 主要拥有以下特性:
      • 基于 Docker,真正一键部署;
      • 前后端分离,模块化编程,微服务;
      • ACM/OI 两种比赛模式、实时/非实时评判;
      • 丰富的可视化图表;
      • 支持 Template Problem,可添加函数题甚至填空题;
      • 管理员权限划分;
      • 多语言支持;
      • Markdown & MathJax 支持;
      • 比赛用户 IP 限制 (CIDR ranges)。

最近梳理项目结构的时候,对于Go中项目执行流程有点模糊,因此有了本文的诞生方便巩固复习

我们知道整个Go项目只能有一个全局的main()函数,但是Go项目可以有多个不同的package,每一个package下都可以有很多go文件,每一个go文件可以导入其他package,并且每一个go文件可以包含自己的常量定义,全局变量,init()以及其他函数。总结下来,一个Go程序中通常由导入的包,常量,变量,init(),其他函数,main()构成,那么具体的执行流程是怎样的呢?

注意不可以因为导入包造成循环依赖

正常的执行流程:

  1. 首先进入main函数所在的go文件,假设为main.go
    1. 执行 main.go 中的 import 语句,然后进入到其他的pkg中,
      1. 重复我们进入main.go中的类似流程:首先执行import导包,然后定义常量和全局变量并执行init
    2. 执行const定义的常量
    3. 执行var定义的变量
    4. 执行init()对应的初始化函数
    5. 执行main()启动程序

注意点:

  1. 同一个包下面的多个go文件,如果每一个go文件中都有一个init函数,那么执行顺序就是go文件的导入顺序,并且同包下的不同 go 文件,按照文件名“从小到大”排序顺序执行
  2. 其他的包只有被 main 包 import 才会执行,按照 import 的先后顺序执行,也就是按照 main 包中 import 的顺序调用其包中的 init() 函数
  3. 一个包被其它多个包 import,但只能被初始化一次
  4. 对同一个 go 文件的 init( ) 调用顺序是从上到下的
  5. 在同一个 package 中,可以多个文件中定义 init 方法
  6. 在同一个 go 文件中,可以重复定义 init 方法
  7. 在同一个 package 中,不同文件中的 init 方法的执行按照文件名先后执行各个文件中的 init 方法
  8. 所有 init 函数都在同⼀个 goroutine 内执⾏。 所有 init 函数结束后才会执⾏ main.main 函数。

参考文章

  1. Go语言的执行顺序(转)

1 / 10