因本人最近在恶补数据结构,学识经验有限,如有不正之处望读者指正,不胜感激;也望借此平台留下学习笔记以温故而知新。这一篇博客主要是最近刚开始接触大话数据结构一书,写的通俗易懂,很多图表帮忙理解,所以讲随手笔记分享至此,希望对您有所帮助。
数据结构绪论
数据结构的定义:
逻辑结构:集合、线性结构、树形结构、图形结构
物理结构:顺序、链接
算法绪论
分析算法运行时间时,要把基本操作的数量与输入规模关联起来,即将基本操作的数量表示成输入规模的函数
大O阶算法时间复杂度:
1、用常熟1取代时间中的所有加法常熟
2、在修改后的运行次数函数中,只保留最高阶项
3、如果最高阶项存在且不是1,去除与这个项相乘的常数
顺序结构和分支结构:O(1)
算法时间复杂度:
常数阶、线性阶、对数阶、平方阶
常用时间复杂度所耗费的时间从小到大依次是:
线性表(链表)
线性表
插入算法:
删除算法:
线性表顺序存储结构的优缺点:
头指针和头结点的异同
单链表数据的读取
单链表插入结点数据的思路
单链表删除结点的思路代码
单链表整表创建的算法思路:
单链表整表创建的代码:
单链表整表删除的算法思路
单链表整表删除的算法代码
单链表结构和顺序存储结构对比
其他链表结构1:静态链表(用数组描述的链表)
静态链表的插入:
1、将所有未被使用过的以及已经被删除的分量用游标连成一个链表
并将第一个结点作为待插入新结点
2、在第i个元素之前插入数据
静态表的删除
1、释放结点函数
2、删除列表中结点数据
静态链表特点
其他链表结构2:循环链表
其他链表结构3:双向链表
双向链表的插入
顺序:先搞定s的前驱和后继,在搞定后结点的前驱,最后搞定前结点的后继
双向链表的删除
线性表总结
栈与队列
栈:本质是线性表
栈顶:允许插入和删除数据的一端
特点:后进先出(LIFO结构:Last In First Out)
栈的顺序结构定义:
进栈:
出栈:
两栈共享空间
插入元素的代码
出栈:
栈的链式存储结构(链栈)
进栈
出栈
栈有一个很重要的应用:在程序设计语言中实现了递归。
栈的应用:用于四则运算(采用后缀表达式,从左到右遍历表达式的每个数字和符号,
遇到是数字就进栈,遇到是符号就将栈顶两个数字出栈,进行运算,运算结果进栈)
队列(先进先出FIFO,First In First Out)
本质:只允许一端进行插入操作(队尾),另一端进行删除的线性表(对头)
队列顺序存储
循环队列
计算队列长度公式:
循环队列满的条件是
循环队列入队代码
循环队列出队代码
队列的链式存储(只能尾进头出的单链表)
链队列入队操作:
链队列出队操作
串
串的顺序存储
串的链式存储
实现Index操作代码:
KMP模式匹配算法原理
首先定义模式匹配字符串各位位置的变化为一个数组,其函数定义如下
一个例子:
计算当前要匹配的串的数组
使用KMP实现Index操作
树
树相关结点定义:度
结点的层次:
数据结构对比
树的存储结构:双亲表示法、孩子表示法、孩子兄弟法
双亲表示法:data是数据域,parent是指针域
根据结点找双亲结点的时间复杂度为o(1)
孩子表示法
方案一:统一度
方案二:每个结点指针域的个数等于该结点的度
方案一增加了空指针的浪费,方案二在运算时带来时间上的损耗
是以改进得到如下孩子表示法
孩子兄弟表示法(把一颗复杂的树变成了一颗二叉树)
二叉树
对于只有两种结果的情形,都适合用树状结构来建模
满二叉树
完全二叉树(如果编号为i的结点与同样深度的满二叉树中编号为i的结点在
二叉树中位置完全相同)
二叉树性质:
1、在二叉树的第i层上至多有2i-1个结点
2、深度为k的二叉树至多有2k-1个结点
3、对任何一颗二叉树,如果其终端结点数为n0,
度为2的结点数为n2,则满足:n0=n2+1
4、具有n个结点的完全二叉树的深度为[log2(n)] + 1
其中[x]表示不大于x的最大整数
5、一颗有n个结点的完全二叉树(深度为[log2(n)] + 1)的结点按层编号,对任
一结点i有:
1 如果i=1,则结点是二叉树的根;如果i>1,则其双亲是结点[i/2]
2 如果2i>n,则结点无左孩子,否则其左孩子是结点2i
3 如果2i+1>n,则结点i无有孩子,否则其右孩子是结点2i+1。
二叉树的顺序存储结构(一般只用于完全二叉树)
一颗完全二叉树图示
其顺序存储
二叉链表
结点结构图
遍历二叉树
前序遍历:ABDGHCEIF
中序遍历:GDHBAEICF
后序遍历:GHDBIEFCA
层序遍历:ABCDEFGHI
二叉树的前序遍历算法:
二叉树的中序遍历算法
二叉树的后序遍历算法
注意:
已知前序遍历和中序遍历,可以唯一确定一颗二叉树
已知后序遍历和中序遍历,可以唯一确定一颗二叉树
但是:已知前序和后序,不能确定唯一一颗二叉树
线索二叉树
将一颗二叉树进行中序遍历后,将所有空指针改为指向前驱和后继结点的示例,
空心箭头实线为前驱,虚线黑箭头为后继
即使改写成这样的双向链表后,依然存在问题,如何区分某一结点的lchild是指向
左孩子还是指向前驱,rchild是指向右孩子还是指向后继(增加0、1标识域)
其中,ltag为0 指向结点的左孩子,为1指向结点的前驱
rtag为0 指向结点的右孩子,为1指向结点的后继
线索二叉树的实现
中序遍历线索化的递归函数代码
二叉树线索链表
二叉线索链表代码(link=0, thread=1)
说明:如果在所用的二叉树需要经常遍历或者查找结点时需要某种遍历序列中的前驱和
后继,那么采用线索二叉链表的存储结构就是非常不错的选择
树、森林与二叉树的转换
树转换为二叉树
示例:
森林转换为二叉树
示例:
二叉树转换为树
1、加线。若某结点的左孩子结点存在,则将这个左孩子的右孩子结点,右孩子的右孩子
结点。右孩子的右孩子的右孩子的结点。。。,反正就是左孩子的n个右孩子结点都作为
此结点的孩子。将该结点与这些右孩子结点用线连接起来
2、去线。删除原二叉树中所有结点与其右孩子结点的连线
3、层次调整。使之结构层次分明
示例:
二叉树转换为森林
示例
说明:当以二叉链表作树的存储结构时,树的先根遍历和后根遍历完全可以借用
二叉树的前序遍历和中序遍历的算法来实现。
赫夫曼树
构造一颗有n个叶子结点的二叉树,每个叶子结点带权w,每个叶子的路径长度为k,则
带权路径长度WPL最小的二叉树称作赫夫曼树,也叫最优二叉树
赫夫曼树的构造:
具体过程:
结果如下:
赫夫曼编码
设需要编码的字符集已知,各个字符在电文中出现的次数获频率集合已知,
以字符集作为叶子结点,以频率作为叶子结点的权重来构造一颗赫夫曼树。
规定赫夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经
过的路径分支组成的0和1的序列便是该结点对应字符的编码。
图
图的概念与定义
由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V,E),G表示一个图,V是图中顶点的集合,E是图中边的集合
无向图的概念
在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧,则为有向完全图
假设有两个图,其中一个图的所有顶点和边都包含于另一个图中,则称这个图为子图
特点:对于无向图,边数就是各顶点度数和的一半
对于有向图,有向弧的条数等于各顶点的出度,也等于各顶点的入度
连通图:
顶点间都是连通的就是连通图,否则不是
连通分量:无向图中的极大连通子图
在有向图中,对于每一对顶点,都存在路径,则称图为强连通图
连通图的生成树:是一个极小的连通子图,含有图中全部顶点,但只有足以构成一棵树的n-1条边
图的定义与术语总结
图的存储结构
图的邻接矩阵存储方式是用两个数组来表示图,一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或者弧的信息
通过这个矩阵,就有一下推断
对于网而言,就是将0和1标识的有无改变成对应关系的权重值
图的邻接矩阵存储结构的代码实现
根据定义的结构,创建无向网图
邻接表
根据以上数据存储结构,可以给出关于结点定义的代码
基于结点定义的代码,无向图的邻接表创建代码如下
十字链表
有向图的一种存储方式
重新定义顶点表结点结构
重新定义边表结构
邻接多重表
对于无向图的邻接表
边集数组
由两个一维数组构成。一个是存储顶点的信息,另一个是存储边的信息;这个边数组每个数据元素由一条边的起点下标、终点下标和权组成。
图的遍历
从图中某一个顶点出发访问其余顶点,且使每一个顶点仅被访问一次。
两种常用方法:深度优先遍历和广度优先遍历
深度优先遍历(DFS):
从图中某个顶点出发,访问此顶点,然后从顶点未被访问的邻接点出发深度优先遍历图,直到图中所有和顶点有路径相通的顶点都被访问到
使用邻接矩阵的方式进行深度优先递归算法代码
使用邻接表的方式进行深度优先递归算法代码
广度优先遍历(BFS):
逐渐扩大搜索范围,类似于树的层次遍历
邻接矩阵结构的广度优先遍历算法
说明:两种遍历算法在时间复杂度上一样的,不同之处在于对顶点访问的顺序不同。深度优先更适合目标比较明确,以找到目标为主要目的的情况;而广度优先遍历更适合在不断扩大遍历范围时找到相对最优解的情况
最小生成树
构造联通网的最小代价生成树
查找最小生成树的算法有两种:普里姆和克鲁斯卡尔算法
普里姆算法:示例
说明:普里姆算法是以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树
克鲁斯卡尔算法
首先使用边集数组结构存储数据
则得到如下数据表示:
克鲁斯卡尔算法代码
最短路径
定义:
非网图:两顶点之间经过的边数最少的路径
网图:两顶点之间经过的边上权值之和最少的路径,并且路径上的第一个顶点是源点,最后一个顶点是终点
迪杰斯特拉算法:按照路径长度递增的次序产生最短路径的算法
具体代码实现
说明:计算任一顶点到其余所有顶点的最短路径,其时间复杂度为O(n3)
弗洛伊德算法
具体应用示例
因为要求所有顶点到所有顶点的最短路径,所以pathmatrix和shortpathtable都是二维数组
求最短路径: