导图社区 基本算法与数据结构
基本算法与数据结构 以及代码模板 个人学习笔记。数据结构是以某种形式将数据组织在一起的集合,它不仅存储数据,还支持访问和处理数据的操作。算法是为求解一个问题需要遵循的、被清楚指定的简单指令的集合。更多干货内容赶快收藏起来慢慢看吧!
编辑于2021-04-08 12:16:41数据结构&算法
概念
数据结构
一维结构
基础
Array数组
LinkedList链表
高级
Stack栈
Queue队列
DeQueue 双端队列
Set集合
Map映射
二维结构
基础
Tree树
Graph图
高级
BinarySearchTree(Red-Black Tree,AVL) 二叉搜索树
Heap堆
Disjoint Set并查集
Trie 字典树
复杂结构
Bitwise位运算
BloomFilter布隆过滤器
LRUCache缓存
算法
if-else
for
recursion递归
Search搜索 Depth first search 深度优先 Breadth first search 广度优先
Dynamic Programing动态规划
Binary Search 二分查找
Greedy 贪心
Math 数学 Geometry几何
算法的基石是泛化寻找重复单元
方法论
面试审题技巧
Clarification 沟通清楚题意
Possible solutions 1.compare(time/space) 2.optimal(加强) 找出多个解决方案时间空间复杂度低的方案
Coding 多写
Test cases 编写测试用例有始有终
职业训练法
拆分知识点
刻意练习
重复再重复至少做5遍 "五毒神掌"
1. 10分钟思考 没有答案就看题解 背诵默写解法
2. 马上自己写 跑在LeetCode
3. 一天后做一遍
4. 一周后做一遍
5. 面试前做一遍
FeedBack 反馈
主动反馈 看高手代码
被动反馈 高手指点 教练看你打
解题思路
升维/空间换时间 数据结构中存储更多的内容便于查找
“五毒神掌” 切忌只做一遍 重复到肌肉记忆
找到最近重复子问题
时间空间复杂度
主定理计算递归时间复杂度
经验
递归和循环效率本身差不多 递归效率差是因为自己程序处理没加缓存导致重复计算
数据结构
Array数组
随机查找O(1)
添加删除需要挪动元素O(n)
LinkedList链表
添加删除O(1)
查找O(n)
Stack栈
FILO 先入后出(first in last out)
添加删除O(1)
Queue队列
FIFO先入先出(first in first out)
添加删除O(1)
Deque双端队列
Double-Ended Queue双端队列
两端都可以进出
PriorityQueue 优先队列
插入O(1)
删除O(logn)
多种实现
heap
bst
treap
SkipList跳表
查找O(logn)
添加删除O(logn)
用空间换时间 增加多级索引
链表元素必须有序
空间复杂度O(n) 维护额外索引需要的空间
一维结构
HashTable哈希表
Java HashSet 使用HashMap实现
TreeMap TreeSet使用红黑树实现
Tree树
LinkedList是特殊化的Tree Tree是特殊化的Graph
遍历(前中后是对与根的)
前序遍历 根左右
中序遍历 左根右
后序遍历 左右根
Heap堆
可以快速找到最大或最小元素的数据结构
二叉堆
一般通过数组实现
斐波那契堆 效率高 更复杂

Graph图
有点有边
边
有方向或者无方向
权重为1或其他
点
邻接表 数组+链表 邻接矩阵 二维数组
二维结构
Trie字典树
单词查找树
最大限度的减少无谓的字符串比较 效率比哈希表高
基本性质
节点本身不存完整单词
根节点到某一节点、路径经过的字符串连接起来、为该字符串
每个节点的所有子节点路径代表的字符不同
核心思想
空间换时间
利用公共前缀降低查询的时间开销

Disjoint Set并查集
平衡查找树
概念
分为AVL树、红黑树等
AVL树
由于数的查找时间复杂度等于深度 所以平衡查找树会记录左右子树高度差[-1,0,1]范围内 当高度差绝对值>1的时候需要(左旋 右旋 左右旋 右左旋)
旋转操作

总结
1. 平衡二叉搜索树
2. 每个节点存balance factor={-1,0,1}
3. 四种旋转操作
不足:节点需要额外存储factor信息 且调整次数频繁
Red-black Tree红黑树
概念
近似平衡二叉树 左右子树高度差小于两倍
特点
1. 每个节点要么是红色要么是黑色
2. 根节点是黑色
3. 每个叶子节点是黑色(NIL节点 空节点)
4. 不能有相接的两个红色节点
5. 从任意节点到每个叶子节点的所有路径包含相同数目的黑色节点
总结

AVL插入比红黑树慢 由于红黑树允许高度差是两倍以内 更少的旋转操作
AVL查询比红黑树快 因为是完全平衡的
https://www.bigocheatsheet.com/
算法
递归
递归和循环基本一致
盗梦空间就是个递归
四部曲
终止条件
重复单元
下钻迭代
清理状态
代码模板
public void rec(int level,int max,int param){ //terminator 终止条件 if(level>max){ dealResult();//处理结果 return; } //process logic 处理逻辑 process(level,param); //drill down 下钻 rec(level+1,max,param); //reverse states 清除状态 一般都使用局部变量、很少使用 }
重点
不要人肉递归
找最近重复子问题
归纳出重复单元
数学归纳法思维
n成立n+1也成立
回溯
八皇后
分治
分解成多个子问题(递归求斐波那契f(n)=f(n-1)+f(n-2))
代码模板
public int devideConquer(Problem problem){ //terminator 问题为空的时候终止 if(problem==null){ int res=processLastResult(); return res; } //split 分解子问题 SubProblem sub=splitProblem(problem); //drill down 分别处理子问题 int res0=devideConquer(sub[0]); int res1=devideConquer(sub[1]); //merge 合并结果 int result =processResult(res0,res1); //revert states }
贪心Greedy
每一步都选择当下最优 从而希望全局最优
贪心算法与动态规划的不同在于 它对每个子问题的解决方案都做出选择 不能回退 动态规划会保存以前的运算结果 并根据结果选择 有回退功能
重点在于证明局部贪心可以保证全局最优
深度优先搜索DFS
二叉树前序遍历就是DFS
先下钻到最底层 在回溯
使用栈
代码模板
public static List<List<Integer>> levelorder(TreeNode root) { List<List<Integer>> res = new ArrayList<>(); if (root == null) return res; dfs(root, 0, res); return res; } public static void dfs(TreeNode root, int level, List<List<Integer>> res) { if (res.size() == level) res.add(new ArrayList<>());//新增加一层的时候初始化list res.get(level).add(root.val);//找到当前层list 放入自己 if (root.left != null) dfs(root.left, level + 1, res);//下钻左孩子 if (root.right != null) dfs(root.right, level + 1, res);//下钻右孩子 }
广度优先搜索BFS
像水波纹一层一层扩散
使用队列
代码模板
public static List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new ArrayList<>(); Queue<TreeNode> queue = new LinkedList<>();//利用队列FIFO的特点 queue.add(root);//加入第一层的根节点 while (!queue.isEmpty()) {//一层循环一次 int size = queue.size();//取当前层的个数 List<Integer> level = new ArrayList<>();//存当前层的节点 for (int i = 0; i < size; i++) { TreeNode first = queue.poll(); level.add(first.val);//取出当前层元素 if (first.left != null) queue.add(first.left);//放入下一层元素 if ((first.right != null)) queue.add(first.right);//放入下一层元素 } res.add(level); } return res; }
二分查找
条件/使用场景
目标哈数单调性 单调递增/减少(有序)
存在上下界
可以随机访问(有下标)
时间复杂度O(logn)
模板
动态规划 Dynamic Programming
和分治区别
动态规划和递归或者分治没有根本上的区别(关键看有没有最优子结构)
共性:寻找重复子问题
差异性:dp存在最优子结构 中途可以淘汰次优解
关键点
1. 最优子结构 opt[b]=best_of(opt[n-1],opt[n-2])
2. 存储中间状态 opt[i]
3. 递推公式(美其名曰 状态转移方程 DP方程)
Fib: opt[i]=opt[i-1]+opt[i-2]
二维路径:opt[i,j]=opt[i,j+1]+opt[i+1,j] 且判断是否为空地
经验
一般从后向前循环
模板

高级搜索
不重复
剪枝
双向BFS
启发式搜索 A*
位运算

应用
BloomFilter
判断成不存在一定不存在
判断成存在 有可能不存在
有可能有假阳性错误
LRU Cache
Hash表+双向链表
Java中LinkedHashMap实现
排序
十大排序 cnblogs.com/onepixel/p/7674659.html


堆排序
快速排序
归并排序
O(nlogn) 重点

字符串匹配算法
KMP
找出最长前缀和后缀的部分 每次直接前缀位置挪动到后缀 减少遍历次数
Rabin-Karp
暴力法+hash字符串比较加速
不能使用系统string的hash函数(需要遍历字符串O(n)) 使用自己实现的针对滑动窗口O(1)的hash函数
暴力法
枚举起点不能优化