基础算法串讲

线性数据结构

链表

  • std::list 是 STL 中的链表
  • 特点:是一条链,空间复杂度 \(O(n)\)
    • 插入与删除十分方便,时间复杂度 \(O(1)\)
    • 寻找与查询数据比较麻烦,时间复杂度 \(O(n)\)
  • 数组大小固定,链表大小可动态调整
    • 注意:std::vector 不算数组,是数据结构
  • 链表的分类
    • 单向链表:每一个结点只存储下一个结点的指针
    • (单/双向)循环链表:尾结点的指针指向首结点
    • 双向链表:一个结点存储上下结点的指针(prevnext
  • 判断插入元素指令是否正确:判断是否会有数据覆盖(模拟)

栈(stack, vector)

  • 开一个口,先进后出
  • 常用函数
    • push() 压栈
    • pop() 如果非空则弹栈
    • top() 栈顶元素
  • 可以用两个栈实现一个队列
  • 给定入栈序列,判断出栈序列是否合法
    • 草稿纸模拟一个栈,右边写进出栈序列
    • 验证出栈队列中的元素是否能顺利弹出(如果能出立刻出)
  • 给定出栈序列,判断入栈序列是否合法

队列(queue, deque)

  • 开两个口,先进先出
  • 常用函数
    • push() 进队列
    • pop() 出队列
    • front() 首元素
  • 双向队列 deque 可以实现栈

递推,递归

递归

  • 在函数定义中使用函数本身的方法
  • 思想是直接或间接地调用函数自身
  • 递归利用堆栈解题
  • 从未知信息一步步推向已知信息

递推

  • 从已知信息一步步推向未知信息,找的是前后关系(递推式)
    • 如斐波那契数列:\(f_n=f_{n-1}+f_{n-2}\)

简单图论

  • 一些点用边连起来(可能是抽象概念)
  • 边的方向
    • 有向边:代表方向性
    • 无向边:无方向性,相当于两条有向边
  • 边的权值
    • 带权图:边上带有边权
    • 正权图:边权都为正数
  • 图的分类
    • 有向图
    • 无向图
    • 混合图
  • 结点度数(无向图入度=出度)
    • 入度:有向图有多少条边指向这一个结点
    • 出度:有向图有
  • 特殊情况
    • 自环:结点的边指向自己
    • 重边:两条相同的边
    • 简单图:无重边、自环
    • 连通图:无向图任意两点之间都相互可达
    • 有向图强连通:无向图任意两点之间都相互可达,\(n\) 个结点,至少有 \(n\) 条边(一个环)
    • 无向连通图如果 \(n\) 个结点,必须有 \(n-1\) 条边(一条链)
    • 稠密图:一张图的边数接近点数的平方
    • 稀疏图:边数远小于点数的平方,没有严格定义
    • 完全图:任意不同两点间均有边(无向)/两条方向不同的边(有向)
  • 图的存储
    • 邻接矩阵:二维数组存边 e[u][v]:u 连 v
      • 不带权:0/1
      • 带权图:0/边权
    • 邻接表
      • 使用一个支持动态添加元素的数据结构构成的数组来存边,如 vector

简单树

  • 树:图里面没有环,并且是联通
  • 一般情况下,有环的图一定不是
  • 无根树制定一个结点为根,则为有根树
  • 性质
    • 边数 = 点数 - 1
    • 任意两点之间仅有一条简单路径
    • 任意不同两点间加边可以得到图中唯一一个环
  • 概念及定义
    • 父亲:从该结点到根结点的简单路径上的第二个结点
    • 祖先:从该结点到根结点的简单路径上除它本身结点
    • 子结点/后代:父亲结点反过来
    • 结点深度:结点到根结点简单路径上的边数
    • 高度:所有结点深度的最大值
    • 子树:某个结点和它所有的后代和边组成的树
  • 常见特殊树
    • 链:与任意结点相连的边不超过两条的树
    • 菊花:结点 u 与其他所有结点均相连(必须直接连)
    • 二叉树:每个结点最多有两个子结点

二叉树

  • 二叉树:每个节点最多只有两个儿子的有根树
  • 完整二叉树:每个结点的子结点树一定为 0 或 2
  • 完全二叉树:只有最下面两层节点度数可以小于 2,且最下面一层结点都集中在该层最左边的连续位置上
  • 满二叉树/完美二叉树:所有叶节点深度相同的二叉树
  • 前中后序遍历:根左右,左根右,左右根
  • 序列反推:根据中序遍历序列和另外一个序列可以得出第三个序列(根据前序/有序遍历获得某子树的根节点,采用分治思想)
  • 前序遍历伪代码
void 前序遍历(int x) {
    输出:print 当前点编号
    递归:前序遍历(左结点)
    递归:前序遍历(右结点)
}
  • 中序遍历伪代码
void 中序遍历(int x) {
    递归:中序遍历(左结点)
    输出:print 当前点编号
    递归:中序遍历(右结点)
}
  • 后序遍历伪代码
void 后序遍历(int x) {
    递归:后序遍历(左结点)
    递归:后序遍历(右结点)
    输出:print 当前点编号
}

搜索

  • 深度优先搜索用栈
  • 广度优先搜索用队列

排序

算法名称 最好时间复杂度 最坏时间复杂度 平均时间复杂度 是否稳定
冒泡排序 \(O(n)\) \(O(n^2)\) \(O(n^2)\)
选择排序 \(O(n^2)\) \(O(n^2)\) \(O(n^2)\)
插入排序 \(O(n)\) \(O(n^2)\) \(O(n^2)\)
计数排序(设值域为 w) \(O(n+w)\) \(O(n+w)\) \(O(n+w)\)
归并排序 \(O(nlogn)\) \(O(nlogn)\) \(O(nlogn)\)
快速排序 \(O(nlogn)\) \(O(n^2)\) \(O(nlogn)\)

杂项

  • 前后缀表式转中缀表达式:栈
posted @ 2023-08-01 20:47  SuperUser777  阅读(24)  评论(0)    收藏  举报