1、数据结构的存储方式
数组由于是紧凑连续存储,可以随机访问,通过索引快速找到对应元素,而且相对节约存储空间。但正因为连续存储,内存空间必须一次性分配够,所以说数组如果要扩容,需要重新分配一块更大的空间,再把数据全部复制过去,时间复杂度 O(N);而且你如果想在数组中间进行插入和删除,每次必须搬移后面的所有数据以保持连续,时间复杂度 O(N)。
链表因为元素不连续,而是靠指针指向下一个元素的位置,所以不存在数组的扩容问题;如果知道某一元素的前驱和后驱,操作指针即可删除该元素或者插入新元素,时间复杂度 O(1)。但是正因为存储空间不连续,你无法根据一个索引算出对应元素的地址,所以不能随机访问;而且由于每个元素必须存储指向前后元素位置的指针,会消耗相对更多的储存空间。
2、数据结构基本操作——遍历+访问
线性
for/while
void traverse(int[] arr) {
for (int i = 0; i < arr.length; i++) {
// 迭代访问 arr[i]
}
}
非线性
递归
链表遍历框架–兼具迭代和递归结构
class ListNode{
int val;
ListNode next;
}
void traverse(ListNode head){
for(ListNode p=head; p!=null; p=p.next;){
//迭代访问 p.val
}
}
void traverse(ListNode head){
//递归访问
traverse(head.next);
}
二叉树遍历框架–典型非线性递归遍历结构
class TreeNode{
int val;
TreeNode left, right;
}
void traverse(TreeNode root){
// 前序遍历
traverse(root.left);
//中序遍历
traverse(root.right);
//后序遍历
}
N 叉树遍历框架
class TreeNode{
int val;
TreeNode[] children;
}
void traverse(TreeNode root){
for(TreeNode child : root.children){
traverse(child)
}
}
3、算法
3.1、二叉树
3.1、DFS(深度优先),BFS(广度优先)
3.2、动态规划
动规问题一般形式就是求最值
比如求最长递增子序列,最小编辑距离
求解动态规划核心问题是穷举
动规穷举特点——存在重叠子问题
暴力穷举效率低下,需要备忘录或 DP table 优化穷举过程
动规问题一定具备最优子结构,才能通过子问题的最值得到原问题最值
列出正确的状态转移方程才能正确地穷举
# 自顶向下递归的动态规划
def dp(状态1, 状态2, ...):
for 选择 in 所有可能的选择:
# 此时的状态已经因为做了选择而改变
result = 求最值(result, dp(状态1, 状态2, ...))
return result
# 自底向上迭代的动态规划
# 初始化 base case
dp[0][0][...] = base case
# 进行状态转移
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][...] = 求最值(选择1,选择2...)
1、labuladong题目
2、牛客网题目
3、力扣题目 动态规划-题目列表
213. 打家劫舍 II ➡ Code ➡ Code2
3.2、回溯算法
主要是一个决策树的遍历过程
路径:已做出的选择
选择列表:当前可以做的选择
结束条件:到达决策树底层,无法再做选择
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
3.3、双指针
快慢指针解决链表中的问题,比如典型的判定链表中是否包含环;
左右指针解决数组(或者字符串)中的问题,比如二分查找。
3.3、滑动窗口
解决子串、子数组问题
4、其他
4.1、排列组合
排列与组合的定义和公式-高中知识点
Java8排列(6行代码实现)
组合算法Java实现-可以修改下start起始为0并判断已有字符存在可修改为排列算法
4.2、排序算法