数据结构
例题时leetcode的题
视频链接
以前看的一个视频 总结性学习
常用的数据结构
优点 | 缺点 | 应用场景 | 例题 | 特点 | |
---|---|---|---|---|---|
数组 | 插入快,能够让我们在o(1)的时间内根据数组的下标查询某个元素 | 缺点:查找慢,删除慢,大小固定当我们需要查询某个元素是否存在的时候需要遍历整个数组 消耗o(n)的时间,删除和添加也是 | 数组便于查找和修改 | 字母异位词242 | 静态储存 |
链表 | 灵活的分配内存空间,能在O(1)的时间内删除或者添加元素 | 查询元素需要O(n)的时间,就是查询元素较慢,动态存储 | 便于插入和删除并且速度快 | 链表的翻转24.25 | 动态储存 |
栈 | 可以用一个单链表来实现,处理完上一次操作后,能在O(1)的时间内查找到更前一次的操作 | 只关心上一次的操作 | 有效的括号20.739 | LIFO | |
队列 | 广度优先搜索 | 滑动窗口最大值 239 | FIFO | ||
双端队列 | 可以利用一个双链表,队列的头尾两端能在O(1)的时间内进行数据的查看,添加和删除 | 实现一个长度动态变化的窗口或者连续的区间 | |||
树 | 结构直观,有普通二叉树,平衡二叉树完全二叉树,二叉搜索树,四叉树,多叉树。特殊的树:红黑树 自平衡二叉搜索树 | 树可以用来考察递归算法掌握熟练度 | 二叉搜索中第k小的元素230 |
高级数据结构
概念 | 基本操作 | 应用场景 | 解题思路 | 例题 | |
---|---|---|---|---|---|
优先队列 | 保证每次取出的元素都是队列中优先级最高的,优先级别可以自定义 二叉堆“利用数组结构来实现完全二叉树 | 向上筛选和向下筛选下标计算办法:父节点:(i-1)/2;左侧子节点2i+1;右侧子节点2i+2 | 从杂乱无章是的数据中按照一定的顺序或者优先级删选数据 | 实现过程比较繁琐,用的时候拿来主义 | 前k个高频元素347 hash表 |
图 | 阶 度 树 森林 环 有向图 无向图 完全有向图 完全无向图 连通图 连通分量 邻接矩阵和邻接链表 | 图的遍历;环的检测 拓扑排序,最短路径算法,连通图相关算法 图的着色 旅行商问题,树的判定 | 深度优先广度优先搜索 | 在社交网络用于描述人与人的关系;地图最短路径的计算;大数据的图论 | 图的遍历785 二分图 |
前缀树 | 字典树 广泛应用于字典查找中 创建的时候确定当前字符为字符串最后一个,把isEnd标记为真,搜索的时候逐个匹配,匹配到继续往下一层匹配,没匹配到立即返回 | 每个节点至少有两个基本属性:children(罗列出每个分支包含的所有字符)和isEnd(布尔值,判断该节点是否为某个字符串的结尾)。根节点是空的 | 类似于百度你输入一个q 下面检索出来很多东西 | 暴力搜索 时间复O(M*N)前缀树 时间复杂度 O(M) | 联想输出功能212 前缀树搜索 单词搜索 |
线段树 | 一种按照二叉树形式存储数据的结构,每个节点保存的都是数组里某一段的总和 | 遍历一个数组时时间复杂度O(n),线段树时间复杂度:O(logn) | 315题 | ||
树状数组 | 利用数组来表示多叉树的性质和优先队列有点类似;优先队列是完全二叉树,树状数组是多叉树;树状数组的第一个元素是空节点;如果节点y是x的爸爸,那么y=x-(x&(-x)) | 遍历一个数组时间复杂度:O(logn) | 力扣208 |
递归和回溯
算法思想:要懂得如何将一个算法规模变小,利用从小规模问题中得到的结果,结合当前的值和情况得出最终结果
公式法:当递归函数的时间执行函数满足T(n)=a·T(n/b)+f(n) f(n)是每次递归完毕后,额外的计算执行时间
ab确定,只看递归部分 时间复杂度是O(n的k次幂) k=log以b为底的a
时间复杂度:递归体和额外部分的时间 谁长听谁的
一样长:O(n的k次幂) logn k=log以b为底的a
回溯;一步步向前试探,对每个测探的情况进行评估,避免走弯路
出现非法的情况,可退到之前的场景,可返回一步或者多步
再去尝试别的方法
使用回溯算法需要保证每次有多种尝试的可能性
例题: 递归 汉诺塔 91解码方法 247中心对称树
回溯法 39组合总合 52n皇后问题
排序
思想 | 时间复杂度 | 空间复杂度 | 整体复杂度 | 例题 | |
---|---|---|---|---|---|
冒泡排序 | 像气泡一样,相邻的元素交换位置,大的数组往后端走终止条件无交换发生 | O(n)~O(n²) | O(1) | ||
插入排序 | 选一个数放在数组的最后。插入排序有两个序列:有序序列和无序序列,从无序序列中拿出一个元素插入有序序列中 | O(n)~O(n²) | O(1) | 147 | |
归并排序 | 分治,把一个复杂的问题拆分成多个子问题来求解,分解数组,知道数组中只剩下一个元素 | O(n) | O(n) | O(nlogn) | |
快速排序 | 分治 选一个基准 把大的放一个数组,小的放一个数组 然后分别对两个数组进行遍历排序 | O(n) | O(logn) | O(nlogn) | |
拓扑排序 | 就是要把图论中的顶点按照相连的性质进行排序(必须有向图 图里没有环) 可以解决大学选课时的先行课的问题,就是学习ssm必须先学spring | O(n) | |||
堆排序 | 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2] | O(nlogn) | |||
桶排序 | 定义多个大小相等有序的桶,将数据放入对应的桶中并排序好。比如:55放置在50~60的桶中, |
归并排序
DFS和BFS
主要考察对树的遍历
深度优先搜索DFS | 广度优先搜索 BFS | |
---|---|---|
使用场景 | 连通性问题,给定一个起点(起始状态)和终点(终点状态)判断是否有一条路径能从起点连接到终点 | 一般用来解决最短路径问题 |
算法思想 | 一条路走到黑 撞墙就回头 直到走出去 | 从起始点开始,一层一层进项每层到起始点的距离是相等的-----------双端BFS 起点和终点同时开始进项广度优先搜索 提高搜索效率 |
递归实现 | 递归实现可以让代码看起来整洁,递归的时候需要将当前程序的变量以及状态压入到系统的栈,压入和弹出栈都需要更多的时间,如果需要压入很深的栈,会造成运行效率低下 | 实现:BFS需要借助的数据结构是队列 |
非递归实现 | 栈的数据结构也支持压入和弹出操作 | |
复杂度分析 | 借助图论的思想:邻接表和邻接矩阵。邻接表(V个顶点E条边),访问每个顶点的时间是O(V),查找所有顶点的邻居的时间是O(E),总的时间复杂度是O(V+E);邻接矩阵:查找每个顶点的邻居需要O(V)的时间,所以查找整个矩阵的时间是O(v²)的时间 | 借助图论的思想:邻接表和邻接矩阵。邻接表(V个顶点E条边),访问每个顶点的时间是O(V),查找所有顶点的邻居的时间是O(E),总的时间复杂度是O(V+E);邻接矩阵:查找每个顶点的邻居需要O(V)的时间,所以查找整个矩阵的时间是O(v²)的时间 |
例题 | 利用DFS在迷宫里找一条路径:假设他是(MxN)的邻接矩阵。时间复杂度O(MxN)空间复杂度为O(MxN) | 利用BFS在迷宫里找一条路径:假设他是(MxN)的邻接矩阵。时间复杂度O(MxN)空间复杂度为O(MxN) |
暴力解题 | 找出所有路径,比较最短的那个,DFS解决的知识连通性问题 不能解决最短路径 | 假设有k堵墙在迷宫里,现在允许拆三堵墙 时间复杂度O(n²+k的w次幂) |
优化方法 | 一边寻找路径,一边记录该点到起点的距离,当发现某个方向过来所需的步数更少,则更新这个点的步数,如果发现步数更多,则不再继续尝试 | 建立分身的方法,出现一个墙的时候,本体继续走,分身打破墙 时间复杂度是O(kxn²),当本体再次遇见墙的时候,将该点放在分身的队列里,分身走完后才从该点开始 |
规划问题
动态规划
两个重要性质:最优子结构(状态转移方程)和重叠子问题 力扣 300 题
结局的动态规划最难的两个地方:
- 如何定义f(n)
- 如何根据f(1),f(2),f(3)…f(n-1)推导出f(n)即状态转移方程 f(n)=max[f(i)]+1
动态规划的解题难点:
- 应该差是用什么样的数据结构来保存什么样的计算结果
- 如何利用保存下来的计算结果推导出状态转移方程
线性规划
基本形式:
- 当前所求的值仅仅依赖于有限个先前计算好的值
- 当前所求的值仅仅依赖于所以先前计算和的值
例题:
- 斐波那契数列
- leetcode 70
- leetcode 198
- 62
区间规划
- 每个子问题的规模又不同区间来定义
- 子问题的最佳状态或结果可以储存在二维数组中
- 这类问题的时间复杂度一般为多项式时间
例题
- 516 最长回文子序列
约束规划
在普通的线性规划和区间规划里,一般题目里有两种需求
- 统计
- 最优解
例题:
- 0~1背包问题
非决定性多项式
时间复杂度
程序运行时间随着问题规模扩大的增长得有多块
- 指数级别的复杂度O(2的n次幂)
- 全排列算法O(n!)
算法
二分搜索算法
定义:也被称作折半搜索,是一种在有序数组中查找某一特定元素的搜索算法
运行前提:数组必须是排好序的 输入的并不一定是数组 也可能是给定一个区域起始和终止的位置
优点:二分搜索也被称为对数搜索 其时间复杂度是O(lgn),是一种非常高效的搜索方式
缺点:
- 要求待查找的数组和区间必须是排好序的
- 若要求动态的删除和插入操作并完成查找,平均复杂度会变成为O(n)
- 采取自平衡的二叉查找树
- 可在O(nlogn)的时间内用给定的数据构建一颗二叉树
- 可在O(logn)的时间内对数据进行搜索
- 可在O(logn)的时间内完成删除和插入操作
当输入的数组或区间是有序的,且不会常变动,要求从中找到一个满足条件的元素 采用二分搜索
核心:
- 确定搜索的范围和区间
- 取中间的数据是否满足条件
- 如果不满足条件,判断应该往那个半边继续进行搜索
贪心算法
直观的算法,难以证明其正确性
定义:贪婪是一种每一步选中都采取在当前状态下最好或最优的选择,从而导致结果是最好的或者最优的算法
优点:对于一些问题,贪婪算法非常的直观有效
缺点:贪心算法不一定正确 过早的做出了决定从而没办法达到最优解 0-1背包问题
贪婪的弊端:
- 总是做出当前看来最好的选择
- 不从整体的角度去考虑,仅对局部的最优解感兴趣
只有当那些局部最优策略能产生全局最优策略的时候 比如0-1背包问题 背包无限大
例子:
- 253