Go 语言—数据结构和算法项目推荐
95% 的算法都是基于这 6 种算法思想
0. 算法
1.算法的特性
- 有穷性 2. 确定性 3.可行性 4. 输入 5. 输出
2. 好算法
- 正确性 2.可读性 3.健壮性(非法输入处理) 4.高效率和低存储量需求
3. 算法的时间复杂度
4. 空间复杂度
1. 数据结构的时间复杂度
-
图的时间复杂度O(n)
一种是邻接表存储,一种是邻接矩阵存储。对于BFS,使用邻接表每个顶点都要遍历一次所以时间复杂度是O(V)(V表示点的个数),在搜索了邻接的顶点时每条边又至少遍历一次,所以总的时间复杂度应该是O(V+E)(E表示边的个数)。对于邻接矩阵,不存储边的信息,每个顶点遍历一次是要走完矩阵的,所以时间复杂度是O(VV).对于不管是使用邻接矩阵存储还是邻接表存储,都要使用一个辅助的队列,所以邻接点都入队一次的空间复杂度是O(V)。
对于DFS,不需要用队列,可以使用递归,递归就需要一个递归的工作栈,所以空间复杂度是O(V),同样的DFS也有邻接表存储和邻接矩阵存储,使用邻接矩阵仍是所有的结点都要遍历一次,所以时间复杂度是O(VV),使用存储表存储,访问所有顶点的时间复杂度是O(V),访问邻接点时间复杂度是O(E),所以总的时间复杂度是O(V+E)。这是遍历算法,如果是搜索算法就是取最坏的情况所有节点访问一次也就是O(V) -
图搜索算法(DFS(深度优先),BFS(广度优先))😮(n)
2. string
字符串的子串必须是连续的,如"abcd"的子串有"ab","bcd"等,但是"ac"不是"abcd"的子串。因此,对于长度为n的字符串,共有1+2+…+n个非空子串,即(n+1)*n/2个,是O(n^2)级别的
字符串的子序列不需要是连续的,因此对于长度为n的字符串的子序列,每个字符都有选或不选两种可能。 因此其子序列的数量是指数级别O(2^n)的
3. 不好的代码风格
- 异常检测:需要对空对象进行特判;
- 变量命名:尽量不要用单个字符进行命名;
- 代码美观:在必要的位置需要加上空格;
- 缩进过多:通过用函数包装代码块减少代码的缩进。(尽量不超过3层)
4.快速提高 Coding Quality 的十二个技巧
- Coding Style 相关:
• 二元运算符两边加空格,单元运算符不加空格
• 花括号和 for, if 之间要加空格(Java),圆括号和 if 之间要加空格
• 用空行分隔开不同的逻辑块
• 逗号后面加空格 - Readability 相关
• 函数名和变量名用1-2个单词作为名称
• 确保一个函数内部不超过 3 层缩进(indention)(缩进通常使用4个空格)
• 多用子函数来减少入口函数的代码量
• 多用 continue 少用 if - Bug Free 相关
• 不管有没有可能出问题,都要对入口函数的参数进行异常检测
• 访问一个下标的时候,一定要确保这个下标不会越界
• 访问一个对象的属性或者方法时,一定要确保这个对象不是空
• 不用全局变量
5. 查找字符串子串第一次出现的索引
Rabin-Karp算法(时间复杂度O(n+m))
6. 时间复杂度
优化:升维,空间换时间
二叉树遍历 O(n)
排好序的二维矩阵O(n)
归并排序 O(n log n)
归并排序和快速排序
7.空间复杂度
和时间复杂度类似,但是更加简单。
主要有两条原则:
数组的长度
如果代码里开了数组:
数组的长度,基本上就是空间复杂度。
一维数组:O(n)
二维数组:O(n^2)
递归的深度(特殊说明)
如果有递归:
递归最深的深度,就是空间复杂度的最大值。
O(n)
数组 + 递归
两者之间的最大值,就是空间复杂度
8. 跳表
优点:原理简单、实现简单,方便扩展,效率更高
缺点:维护成本高,插入、删除时间复杂度O(logn),插入和删除会导致索引不工整
使用案例:Redis.zset、LevelDB
随机查询时间复杂度O(logn)
空间复杂度O(n)
9. leetcode
-
二叉树的中序遍历
https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ -
二叉树的前序遍历
https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ -
二叉树的后序遍历
https://leetcode-cn.com/problems/binary-tree-postorder-traversal/ -
N叉树的前序遍历
https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/ -
N叉树的层序遍历
https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/ -
爬楼梯
https://leetcode-cn.com/problems/climbing-stairs/ -
括号生成
https://leetcode-cn.com/problems/generate-parentheses/ -
翻转二叉树
https://leetcode-cn.com/problems/invert-binary-tree/ -
验证二叉搜索树
https://leetcode-cn.com/problems/validate-binary-search-tree/ -
二叉树的最大深度
https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ -
二叉树的最小深度
https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ -
二叉树的序列化与反序列化
https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/ -
二叉树的最近公共祖先
https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/ -
从前序与中序遍历序列构造二叉树
https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ -
组合
https://leetcode-cn.com/problems/combinations/ -
全排列
https://leetcode-cn.com/problems/permutations/ -
全排列 II
https://leetcode-cn.com/problems/permutations-ii/
10.递归
python
def recursion(level, param1, param2, ...):
# recursion terminator 递归终止条件
if level > MAX_LEVEL:
process_result
return
# process logic in current level 当前层的代码处理逻辑
process(level, data...)
# drill down 进入到下一层递归中去
self.recursion(level+1, p1, ...)
# reverse the current level status if needed 状态回复
Java
public void recur(int level,int param){
//terminator
if(level>MAX_LEVEL){
//process result
return;
}
//process current logic
process(level,param);
//drill down
recur(level:level+1,newParam)
//restore current status
}
js
const recursion = (level, params) =>{
// recursion terminator
if(level > MAX_LEVEL){
process_result
return }
// process current level process(level, params)
//drill down
recursion(level+1, params)
//clean current level status if needed }