概述
- 目标:时间效率高,存储量低
时间复杂度和空间复杂度
- 大O记法
- 常见的时间复杂度(从上到下,复杂度依次增加)
O(1) 常数阶
O(logn) 对数阶
O(n) 线性阶
O(nlog)
O(n^2) 平方阶
O(n^3) 立方阶
O(2^n) 指数阶
O(n!)
O(n^n) - 最坏情况和平均情况
- 空间复杂度
线性表
- 定义:由零个或多个数据元素组成的有限序列。
- 前驱,后继
抽象数据类型
- eg: point(x,y,z)
描述抽象数据类型的标准格式:
ADT 抽象数据类型名 Data 数据元素之间逻辑关系的定义 Operation 操作 endADT
- 示例1:A与B并集
- 线性表的存储结构:顺序存储结构、链式存储结构
- 顺序存储结构封装需要三个属性
- 起始位置
- 最大存储容量
- 当前长度
- 插入操作、删除操作
- 线性表顺序存储结构:存、读数据时间复杂度是O(1),插入、删除时间复杂度是O(n)
链式存储结构
- 结点(Node)
- 数据域
- 指针域
- 指针/链
- n个结点链接成一个链表
- 单链表
- 头指针:第一个结点的存储位置
- 第一个结点的数据域一般不存储信息,也可以存放链表的长度
- 头指针不能为空
- null:最后一个结点指针为空
- 头指针:第一个结点的存储位置
添加结点:
s->next = p->next p->next = s
删除结点:
p->next = p->next->next
- 结点(Node)
单链表的整表创建
- 头插法
- 尾插法
- 单链表的整表删除
- 顺序结构与链表的比较:
- 顺序结构适合:经常查询的场合
- 链表结构适合:经常插入和删除的场合
- 静态链表
- 插入操作
- 删除操作
- 循环链表
- 初始化
- 插入
- 删除
- 双向链表:用空间换取时间
- 插入:代码顺序很重要
- 删除
单链表小结:腾讯面试题
题目:快速找到未知长度单链表的中间节点。
思路:使用快慢指针
约瑟夫问题
题目:一个圈,每报数到3,则自杀,直到所有人都自杀了。
思路:循环链表
判断单链表中有环
思路:快慢指针/一个指针每次走一步,另一个指针每次都从头走
魔术师发牌问题
拉丁方阵问题
栈
- 栈是一种重要的线性结构
- 栈是一个后进先出的线性表,它要求只在表尾进行删除和插入操作。
- 术语:
- 栈顶(Top)
- 栈底(Bottom)
- 进栈(Push)
- 出栈(Pop)
- 栈的顺序存储结构(常用)
- 栈的链式存储结构
将二进制数转换为十进制数,二进制转换为八进制
逆波兰计算器
中缀表达式转换为后缀表达式
队列
- 队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
- 先进先出
- 从后面入队列,从前面出队列
- 队列的顺序存储结构
- 队列的链式存储结构(常用):链队列
- 循环队列
递归
- 递归效率低,最好用迭代代替
- 斐波那契数列的递归实现
- 递归实现字符串反向输出:利用了递归的回退
- 分治思想:
- eg:折半查找
汉诺塔(递归)
八皇后问题(递归)
字符串
- String:串可以是空串
- BF算法
- KMP算法
树
- 度:结点拥有的子树数
- 分支结点、叶节点
- 根为第一层
- 深度
- 森林
树的存储结构
- 双亲表示法
- 孩子表示法
- 双亲孩子表示法
二叉树
- 不存在度大于2的结点(但是可以是1或者0)
- 左子树和右子树是有顺序的
- 二叉树的五种基本形态
- 特殊二叉树
- 斜树
- 满二叉树
- 定义:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
- 完全二叉树
- 定义:对一颗具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点位置完全相同,则这棵二叉树称为完全二叉树。
- 二叉树性质
- 在二叉树的第i层上至多有2^(n-1)个结点(i>=1)
- 深度为k的二叉树至多有2^k-1个结点(k>=1)
- 对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1
- 具有n个结点的完全二叉树的深度为down{(log2 n)}+1
- 二叉树的存储结构
- 顺序存储
- 链式存储(常用):二叉链表
- 二叉树的遍历
- 定义:二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次
- 前序遍历
- 中序遍历
- 后序遍历
- 层序遍历
- 线索二叉树
- 增加了线索,指示是左右孩子还是前驱后继
- 中序遍历
树、森林及二叉树的相互转换
- 树转换成二叉树
- (1)在树中所有的兄弟结点之间加一连线
- (2)对每个结点,除了保留与其长子的连线外,去掉该结点与其他孩子的连线
- 森林转换成二叉树
- (1)先将森林中的每棵树变为二叉树
- (2)再将二叉树的根结点视为兄弟从左至右连在一起,就形成了一棵二叉树
- 二叉树到树、森林的转换
- (1)若结点x是其双亲y的左孩子,则把x的右孩子,右孩子的右孩子,……,都与y用连线连起来
- (2)去掉所有双亲到右孩子之间的连线
- 树与森林的遍历
- 树的遍历:先根遍历、后根遍历
- 森林的遍历:先根遍历、后根遍历
- 重要性质:
- 树、森林的先根遍历和二叉树的前序遍历结果相同
- 树、森林的后根遍历和二叉树的中序遍历结果相同
Huffman树
- 基本概念
- 节点的路径长度
- 从根结点到该结点的路径上的连接数
- 树的路径长度
- 树中每个叶子结点的路径长度之和
- 结点带权路径长度
- 结点的路径长度与结点权值的乘积
- 树的带权路径长度(WPL,Weighted Path Length)
- 树中所有叶子结点的带权路径长度之和
- 节点的路径长度
- Huffman树:WPL值最小
- 如何构造
- 权值从小到大排序,后面类似编码的过程,小的是左孩子,大的是右孩子
图
定义
是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V,E)。其中G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
- 无向边:无向偶(Vi,Vj)表示
- 有向边:有向偶
图的存储结构
- 邻接矩阵
- 邻接表
- 十字链表:对有向图来说,有时候需要建立邻接表和逆邻接表2个表。十字链表可以把邻接表和逆邻接表很好整合在一起。
- 邻接多重表
- 边集数组
图的遍历
- 深度优先遍历(DFS)
- 递归
- 骑士周游问题(马踏棋盘算法)
- 哈密尔顿路径
- 广度优先遍历(BFS)
最小生成树
- 普里姆算法(Prim算法):适合稠密图
- 克鲁斯卡尔算法(Kruskal算法):适合稀疏图
最短路径
- 迪杰斯特拉算法(Dijkstra算法):O(n^2)
- 弗洛伊德算法(Floyd算法):O(n^3)
拓扑排序
- 无环图(DAG图)
- AOV网:在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的图,我们称之为AOV网(Activity On Vertex Network)。
- 拓扑序列
关键路径
- AOE网:在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,我们称之为AOE网(Activity on Edge Network)。
查找算法
- 静态查找
- 顺序查找(可以优化,加一个哨兵):O(n)
- 折半查找:O(log2n),下面两种是折半查找的改进:
- 插值查找(按比例查找):关键在mid的计算上,O(log2n),适合数据比较均匀的情况
- 斐波那契查找(黄金比例查找)
- 线性索引查找
- 稠密索引
- 分块索引
- 倒排索引
- 动态查找
- 二叉排序树
- 二叉排序树又称二叉查找树,它或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不为空,则左子树上所有结点的值均小于它的根结构的值;
- 若它的右子树不为空,则右子树上所有结点的值均大于它的根结构的值;
- 它的左、右子树也分别为二叉排序树。
- 查找
- 插入
- 删除(比较麻烦)
- 删除叶子结点
- 删掉只有左子树或者右子树的结点
- 删除有左子树和右子树的结点
- 二叉排序树又称二叉查找树,它或者是一棵空树,或者是具有下列性质的二叉树:
- 平衡二叉排序树
- 平衡二叉树要么是一棵空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。
- 左旋,右旋
- 多路查找树
- 2-3树
- 2-3树的所有叶子都在同一层次
- 插入
- 空树:直接构造一个2结点
- 插入到2结点的叶子结点:直接插入,构造成3结点
- 插入到3结点的叶子,但是双亲结点是2结点:扩展双亲
- 一直是3结点:增加树的高度,从下向上拆
- 删除
- 位于3结点的叶子结点:直接删
- 位于2结点的叶子结点
- 双亲是2结点,且双亲有一个3结点孩子:左旋转
- 双亲是2结点,且双亲有一个2结点孩子:找后继,然后旋转
- 双亲是3结点:双亲变成2结点
- 所有结点都是2结点:减少树的高度
- 不是叶子结点
- 位于2结点的双亲
- 位于3结点的双亲
- 2-3-4树
- 插入
- 删除
- B树
- B树是一种平衡的多路查找树,2-3树和2-3-4树都是B树的特例。我们把结点最大的孩子树数目称为B树的阶,因此,2-3树是3阶B树,2-3-4树是4阶B树。
- 2-3树
- 散列表(哈希表)
- 记录的存储位置=f(关键字)
- 适合一对一查找
- 散列函数
- 构造原则:计算简单、分布均匀
- 直接定址法
- 数字分析法
- 平方取中法
- 折叠法
- 除留余数法
- 随机数法
- 冲突处理
- 开放定址法
- 再散列函数法
- 链地址法
- 公共溢出区法
- 二叉排序树
排序
冒泡排序
O(n^2)
选择排序
O(n^2),移动的次数少了
直接(简单)插入排序
希尔排序
O(nlogn)
直接插入排序在基本有序的情况下,效率是很高的
对直接插入排序的改进
堆排序
O(nlogn)
对选择排序的改进
完全二叉树
大顶堆、小顶堆