大话数据结构第3-7章(读书笔记)
第三章 线性结构
Tips: 数组的长度总大于线性表的长度
3.1线性表的顺序存储
存取读入数据时,都是O(1),插入删除数据时,都是O(n)
结构 | 优点 | 缺点 |
---|---|---|
线性表顺序存储 | 无需增加额外的存储空间,快速存储 | 插入删除需要移动大量元素,难以确定存储容量,造成存储空间的“碎片” |
3.2线性表的链式存储(单链表)
链式存储分为 单链表,静态链表,循环,双向链表,这里着重讲单链表
1.含义
存储数据元素信息的域成为数据域
存储直接后继位置的域称为指针域
结点的含义:指针域和数据域组成数据元素的存储映像
每个结点只包含一个指针域,称之为单链表
2.特点
一般,最左边为头指针,最右边最后一个结点的指针指向空。
一般会在单链表的第一个结点之前设置一个节点,称为头结点,可以不存储任何信息,头结点的指针是指向第一个结点的指针
如果线性表为空表,那么头结点的指针域为空
异同 | 头指针 | 头结点 |
---|---|---|
1 | 指向第一个结点的指针,具有标识作用,且头指针不可为空,是必要因素 | 一般此数据域无意义,不是必要要素 |
3.3单链表的读取
读取算法思路:
1.声明一个指针p,指向链表第一个结点,初始化j从1开始;
2.当j<i时遍历链表,让p的指针向后移动,不断指向下一个节点,j累加1
3.若到链表末尾p为空的时候,则说明第i个结点不存在
4.否则查找成功,返回结点p的数据。
算法思路
3.4单链表的插入与删除
- 插入s: s-next = p-next; p-next = s (p-next-next = p-next,用s取代p-next)
- 删除q:q=p-next; p-next = q-next (p-next= p-next-next,用q取代p-next)
插入算法思路:
1.声明一指针P指向链表头结点,初始化j从1开始;
2.当j<i时遍历链表,让p的指针向后移动,不断指向下一个节点,j累加1
3.若到链表末尾p为空的时候,则说明第i个结点不存在4.否则查找成功,在系统中生成一个空结点s
5.将数据元素e 赋值给 s-data
6.单链表的插入标准语句s-next = p-next; p-next = s
7.返回成功
算法思路
删除算法思路:
1.声明一指针P指向链表头结点,初始化j从1开始;
2.当j<i时遍历链表,让p的指针向后移动,不断指向下一个节点,j累加1
3.若到链表末尾p为空的时候,则说明第i个结点不存在4.否则查找成功,将与删除的结点p-next 赋值给 q
5.单链表的删除标准语句q=p-next; p-next = q-next
6.将q结点中的数据赋值给e,作为返回
7.释放q结点
8.返回成功
算法思路
插入与删除的时间复杂度:O(n)
3.5单链表的整表创建与删除
创建单链表的整表是一种动态结构,该过程是一个动态生成链表的过程
过程:空表–建立各元素结点–逐个插入链表
创建算法思路:(头插法) or 尾插法
1.声明一指针p和计数器变量i
2.初始化一个空链表L
3.让L的头结点的指针指向NULL,也就是建立一个带头结点的单链表
4.循环:
- 生成一新节点赋值给p
- 随机生成一数字赋值给p的数据域 p-data
- 将p插入到头结点与前一新节点之间
算法思路
删除算法思路:
1.声明一指针p和指针q
2.将第一个结点赋值给p
3.循环:
- 将下一个结点赋值给q
- 释放p
- 将q 赋值给 p
算法思路
3.6顺序存储vs单链表
- 顺序存储:查找O(1),插入O(n),需要预分配空间
- eg注册信息
- 单链表: 查找O(n),插入O(1),不需要预分配空间
- eg游戏装备
当线性表中的元素个数变化比较大,适合单链表。
当线性表中事先知道该表的长度,比如一年12月,一星期7天等,适合顺序存储
第四章 栈与队列
- 栈:后进先出,只在表尾进行插入删除的线性表
- 队列:先进先出,只在一端进行插入,另一端进行删除的线性表
4.1 栈stack
将允许插入和删除的一端成为栈顶(top),另一端称为栈底(bottom);插入与删除都必须在栈顶进行,这也限制了插删的位置,栈底是固定的。
栈又称作 后进先出的线性表(Last in first out ),简称LIFO结构
生活中的例子:网页的后退键,word的撤销键
栈的插入与删除
栈的插入(push)为 进栈,压栈,入栈,删除(pop)称为出栈或弹栈
4.1.1 栈的顺序存储
空栈的top为-1,栈中有两个元素top=1,栈中有5个元素top=4
进栈出栈的时间复杂度为O(1),但需要事先确定一个固定的长度,存在内存空间浪费的问题,但是优势就是存取时定位方便
4.1.2 栈的链式存储
进栈出栈的时间复杂度为O(1),但要求每个元素有指针域,增加内存开销,不需要提前确定长度
4.1.3 栈的应用–递归
** 经典的递归例子: 斐波那契数列(Fibonacci)**
- 可以使用迭代和递归进行实现
- 迭代:循环结构
- 递归:选择结构,结构清晰,更简洁,减少内存
斐波那契数列(Fibonacci)
中缀表达式和后缀表达式转化
方法:从左到右遍历中缀表达式,若是数字就输出,若是符号,则判断其余栈顶符号的优先级,紧挨着符号右侧项进行输出
中缀:9+(3-1)*3+10/2
后缀:9 3 1- 3*+ 10 2/+
4.2 队列queue
4.2.1队列的定义
允许插入的一端称为队尾,允许删除的一端称为队头,a1是队头,an是队尾
队列又称作 先进先出的线性表(Firsr in first out ),简称FIFO结构
生活中的例子:排队;电脑卡机,然后又运行过来,会把之前的操作按顺序进行一遍
4.2.2 队列的顺序存储
插入为O(1),删除为O(n)
4.2.3 队列的链式存储
循环队列(顺序):可以确定队列长度最大值的情况下使用
链队列(链式): 无法预估队列的长度
时间复杂度都为O(1)
第五章 串String–P213
定义:由零个或多个字符组成的有限序列,又称字符串。
一般操作有:查找位置,得到位置,替换子串
编码:
ASCII 编码:7位二进制表示一个字符,总128字符
ASCII扩展:8位二进制表示一个字符,总256字符
Unicode编码:16位二进制表示一个字符,总6.5万个字符,前256同上
5.1 串的顺序存储
堆
5.2 串的算法
5.2.1 朴素模式匹配
5.2.2 KMP模式匹配
(适用于模式与主串之间存在许多部分匹配的情况)
第六章 树Tree–P255
定义:
树是n(n>=0)个结点的有限集,n=0为空树
特点
- 1.有且仅有一个特定的称为根(Root)的结点
- 2.n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2…,Tm
- 3.每一个集合本身又是一棵树,并且成为根的子树(Subtree)
6.1结点概念
6.1.1 结点分类
树的结点:包括 一个数据元素 及 若干指向其子树的分支
结点的度:结点拥有的子树数称为结点的度(De-gree)
叶节点:度为0的结点称为叶节点(Leaf)或终端结点
分支结点:度不为0的结点称为分支结点或非终端结点
内部节点:除根节点之外的分支结点
树的度:树内各节点的度的最大值
6.2.2结点间关系
结点的孩子child:结点的子树,该结点称为孩子的父节点parent
兄弟sibling:同一个父节点下面的孩子之间的互称
结点的祖先:从根到该结点所经分支上所有节点,有1个或多个
6.2 树的存储结构
深度(Depth):树的层数称为树的高度深度
森林(Forest):是m棵互不相交的树的集合。对树中每个结点而言,其子树的集合称为森林
异同 | 线性结构 | 树结构 |
---|---|---|
01 | 第一个数据元素:无前驱;最后一个:无后继;中间元素:前驱+后继 | 根结点:无双亲;叶结点:无孩子,可以多个;中间结点:一个双亲多个孩子 |
三种方式:双亲表示法,孩子表示法,孩子兄弟表示法
6.2.1双亲表示法
- 结构:[data][parent] data储存数据信息,parent存储数组下标
- 时间复杂度为O(1) ;parent为-1表示根节点;该例子的数的度为3
6.2.2孩子表示法
- 孩子链表的孩子结点–结构:[child][next]
child为数据域,用于保存表头数据中的下标;
next为指针域,保存指向下一结点的指针
- 表头数据的表头结点–结构:[data][firstchild]
data为数据域,存储某结点的数据信息
firstchild 为头指针域,存储头指针,即左子树
6.2.3孩子兄弟表示法
- 结构:[data][firstchild][rightsib]
data为数据域,存储某结点的数据信息
firstchild 为指针域,存储头指针,即左子树
right-sib 为指针域,存储该节点的右兄弟地址
6.3二叉树
特点:
1.每个结点最多两颗子树,度一定小于等于2,可以为0,1,2
2.左子树和右子树是有顺序的,次序不可颠倒
3.即使某结点只有一颗子树,也要区分是左还是右
4.五种形态:1.空二叉树
2.只有一个根结点
3.根结点只有左子树
4.根结点只有右子树
5.根结点有左、右子树
6.3.1特殊二叉树
- 斜树
只有左子树 or 只有右子树 的二叉树 - 满二叉树
每个分支结点都有左右子树,那么称为满二叉树 - 完全二叉树
相当于满二叉树的一部分,数值一定要是连续的
完全二叉树特点:
1.叶子结点只能出现在最下两层
2.最下层的叶子一定集中在左部连续位置
3.倒数第二层,若有叶子结点,一定都在右部连续位置
4.结点度数为1,那么该结点只有左,没有右
5.同样结点树的二叉树,完全二叉树的深度最小
以下是完全二叉树,数值连续
以下不是完全二叉树,数值不连续
—2018-11-06 P291
6.3.2 二叉树的性质
推荐网址:Latex公式汇总
性质1
在二叉树的第i层上至多有 2 i − 1 2^{i-1} 2i−1个结点(i>=1)
eg: 第一层1 第二层2 第三层4 第四层8
性质2
深度为K的二叉树至多有 2 k − 1 2^k -1 2k−1 个结点
eg: 一层1 二层1+2 三层1+2+4 四层1+2+4+8
性质3(难理解,记住)
对任何一颗二叉树T,如果其终端叶子结点数为n0,度为2的结点数为n2,那么 n 0 = x 2 + 1 n_0 =x_2 + 1 n0=x2+1
设n1是度数为1的结点数,那么数的总结点数为: n = n 0 + x 1 + x 2 n = n_0 + x_1 + x_2 n=n0+x1+x2
性质4
具有n个结点的完全二叉树的深度为:
∣ log 2 n + 1 ∣ |\log_2 n + 1| ∣log2n+1∣
性质5
对于完全二叉树来说,分枝结点中,左子树通常为该双亲节点*2,右子树在此基础加1
6.3.3 二叉树的存储结构
顺序存储会占用大量内存,适用性不强,所以着重讲链式存储
- 结构[lchild][data][rchild]
data 数据域
child指针域
6.4 二叉树的遍历
含义:
二叉树的遍历(traversing binary tree) 是指从根结点出发,按照某种**【次序】依次【访问】**二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。
【技巧】
已知前序和中序,可以唯一确定一颗二叉树
已知后序和中序,可以唯一确定一颗二叉树
已知前序和后序,不一定可以确定二叉树
前序遍历:
根节点在前
(1)先遍历根结点左侧的所有左子树,然后左侧所有右子树(次序是不同的根结点从下到上)
(2)右侧所有左子树,右侧所有右子树(次序是同结点依次向下只有右子树,那么从上到下)
(3)顺序:根节点-左子树-右子树中序遍历:
根节点在中
(1)先遍历最下层的左侧的第一个左子树
(2)顺序:左子树-根结点-右子树后序遍历:
根节点在后
(1)先遍历最下层的左侧第一个左子树,然后同级别的右子树(如果没有左子树,则从右子树开始)
(2)顺序:左子树-右子树-根结点主要讲四种:前序,中序,后序,层序
四种的前提:若二叉树为空,则空操作返回
前序遍历
- 先访问根结点,然后前序遍历左子树,再前序遍历右子树
ABDGHCEIF
中序遍历
- 从根结点开始,中序遍历根节点的左子树,然后访问根节点,中序遍历右子树
GDHBAEICF
后序遍历
- 从左到右先叶子后结点的方法访问左右子树,最后访问根结点
GHDBIEFCA
6.5线索二叉树
6.5二叉树的应用–赫夫曼编码
(数据压缩)
第七章 图Graph–P364
定义:
图是由顶点的有穷非空集合和顶点之间边的集合组成,表示为G(V,E),其中G表示一个图,V是图G中顶点的集合,E是图G中边的集合
7.1深度优先与广度优先
深度优先类似于树的前序遍历,广度优先类似于树的层序遍历
两者的时间复杂度是一样的
深度优先适合 目标更明确,以找到目标为目的
广度优先适合 在不断扩大便利范围时找到相对最优解