1 数据结构的基本概念和算法
考点
- 数据结构基本概念
- 算法
1.1 数据结构的基本概念
- 数据
数据是指所有能输入到计算机中的描述客观事物的符号,包括文本、声音、 图像、符号等。
- 数据元素
数据元素十数据的基本单位,也称节点或记录。
- 数据项
数据项表示有独立含义的数据最小单位,也称域。若干个数据项构成一个数 据元素,数据项是不可分割的最小单位。
- 数据类型
数据的不可分割的单位是数据类型。
- 逻辑结构和存储结构
- 逻辑结构:数据元素间抽象化的相互关系,与数据的存储无关,独立于计算机,他是从具体问题中抽象出来的数学模型。
逻辑结构有集合、线性结构、树形结构、图形结构。
- 存储结构:数据元素及其关系在计算机中的存储方式。
存储结构可以分为四种:顺序存储、链式存储、散列存储和索引存储。
1.2 算法
- 概念
算法是指对特定问题求解步骤的一种描述。通常是以算法执行所耗费的时间和空间所占用的 来判断一个算法的优劣。
- 算法的特点
- 有穷性
- 确定性
- 可行性
- 输入
- 输出
- 好算法的标准
- 正确性
- 易读性
- 健壮性
- 高效性
- 时间复杂度
算法运行需要的时间,一般将算法基本运算的执行次数作为时间复杂度的度量标准。
- 语句频度
语句重复执行的次数。
1.3 注意
- 线性表的顺序存储是一种随机存取的存储结构,线性表的链式存储结构是一种顺序存取的存储结构。
2 线性表的顺序表示和实现
考点
- 线性表的类型定义
- 线性表的顺序表示和实现
2.1 线性表的类型定义
- 线性表是由n个相同类型的数据元素组成的有限序列,它是最基本最常用的一种线性结构。顾名思义,线性表就像是一条线,不会分叉。线性表有 唯一的开始和结束,除了第一个元素外,每个元素都有唯一的直接前驱:除了最后一个元素外,每个元素都有唯一的直接后继。
- 顺序表的两种存储结构为顺序表和链表
- 线性表是具有n个数据元素的优先序列。
2.2 线性表的顺序表示和实现
- 顺序表的定义
顺序表采用顺序存储方式,即逻辑上相邻的数据在计算机内的存储位置也 是相邻的。顺序存储方式,元素存储是连续的,中间不允许有空,可以快速定位第几个元素,但是插入和删除时需要移动大量元素。
当需要随机查找线性表的元素时,宜采用顺序表作存储结构。
- 线性表的操作——初始化
初始化是指顺序表分配一段预定义大小的连续空间,用 记录这段空 间的基地址,当前空间内没有任何数据元素,因此元素的实际个数为0。假设我们已经预定义了一个最大空间数MAXSIZE,那么就用new分配大小为MAXSIZE的空间,分配成功会返回空间的首地址,分配失败会返回空指针。
- 线性表的操作——创建
- 线性表的操作——取值
顺序表中的任何一个元素都可以立即找到,称为随机存取方式。例如,要取第i个元素,只要i值是合法的(1<=i<=Length),那么立即就可以找到该元素。由于下标是从0开始的,因此第i个元素,其下标为i-1,即对应元素为L.elem[i-1]。
- 线性表的操作——插入
在顺序表中第i个位置之前插入一个元素e,需要从最后一个元素开始,后 移一位,直到把第i个元素也后移一位,然后把e放入第i个位置。
- 线性表的操作——删除
在顺序表中删除第i个元素,需要把该元素暂存到变量e中,然后从i+1个元素 开始前移,直到把第n个元素也前移一位,即可完成删除操作。
2.3 注意
3 线性表的链式表示和实现
考点
- 单链表
- 双向链表
- 循环链表
3.1 单链表
- 单链表的定义
如图所示每个节点包含两个域:数据域和指针域。数据域存储数据元素,指 针域存储下一个节点的地址,因此指针指向的类型也是节点类型。每个指针都指向下一个节点,都是朝一个方向的,这样的链表称为单向链表或单链表。
- 单链表的操作——创建(插入)
创建单链表分为头插法和尾插法两种,头插法是指每次把新节点插到头节 点之后,其创建的单链表和数据输入顺序正好相反,因此也称为逆序建表。尾插 法是指每次把新节点链接到链表的尾部,其创建的单链表和数据输入顺序一致,因此也称为正序建表。
接下来,我们讲尾插法建表。尾插法每次把新节点链接到链表的尾部,其创 建的单链表 和数据输入顺序一致。
- 单链表的操作——查找
在一个单链表中查找是否存在元素e,可以定义一个p指针,指向第一个元素节点,比较p指向节点的数据域是否等于e。如果相等,查找成功,返回true ;如果不等,则p指向p的下一个节点,继续比较,如果p为空,查找失败,返回false.
- 单链表的操作——删除
删除一个节点,实际上是把这个节点跳过去。根据单向链表向后操作的特性,要想跳过第i个节点,就必须先找到第i-1个节点,否则是无法跳过去的。
3.2 双向链表
- 双向链表的定义
单链表只能向后操作,不可以向前操作。为了向前、向后操作方便,可以给 每个元素附 加两个指针域,一个存储前一个元素的地址,另一个存储下一个元素的地址。这种链表称为双向链表。
- 双向链表操作——插入
单链表只有一个指针域,是向后操作的,不可以向前处理,因此单链表如果在第i 个节点之前插入一个元素,就必须先找到第i-1个节点。在第i个节点之 前插入一个元素相当于把新节点放在第i-1个节点之后。而双向链表不需要, 因为有两个指针,可以向前后两个方向操作,直接找到第i个节点,就可以把新节点插入第i个节点之前。
3.3 循环链表
单链表中,只能向后,不能向前。如果从当前节点开始,无法访问该节点前 面的节点,而最后一个节点的指针指向头节点,形成一个环,就可以从任何一个 节点出发,访问所有的节点,这就是循环链表。循环链表和普通链表的区别就是最后一个节点的后继指向了头节点。
3.4 注意
- 单链表中删除值为x的节点。
- 单链表中实现单链表的逆转。
- 单链表中实现将两个已排序的单链表归并成一个链表。
4 线性表的应用
4.1 线性表的应用
- 合并有序顺序表
题 1. 将两个有序(非递减)顺序表La和Lb合并为一个新的有序(非递减) 顺序表。
- 合并有序链表
题 1. 将两个有序(非递减)单链表La和Lb合并为一个新的有序(非递减)单链表。
- 就地逆置单链表
题 1.将带有头节点的单链表就地逆置。即元素的顺序逆转,而辅助空间复杂度为O(1)。
- 查找链表的中间节点
题 1.带有头节点的单链表 ,设计一个尽可能高效的算法求L中的中间节点。
- 删除链表中的重复元素
题 1.用单链表保存m个整数,节点的结构(data,next),且|data|<=n(n为正整数)。现要求设计一个时间复杂度尽可能高效的算法,对于链表中data的绝对值相等
的节点,仅保留第一次出现的节点而删除其余绝对值相等的节点。
4.2 注意
5 栈
考点
- 顺序栈的定义和操作
- 链栈的定义和操作
5.1 顺序栈的定义和操作
- 栈的类型定义
这种后进先出(LastFirstOut,LIFO)的线性序列,称为“栈”。栈也是一种线性表,只不过它是操作受限的线性表,只能在一端进出操作。进出的一端称为栈顶( top),另一端称为栈底(base)。栈可以用顺序存储,也可以用链式存储,分别称为顺序栈和链栈.
- 顺序栈
先看顺序栈的存储方式,顺序栈需要两个指 针, 指向栈底, 指向栈顶。
栈定义好了之后,还要定义一个最大分配空间,顺序结构都是如此,需要 预先分配空间,因此可以采用宏定义。
- 顺序栈的操作——初始化
初始化一个空栈,动态分配Maxsize大小的空间,用S.top和S.base指向该空间的基地址。
- 顺序栈的操作——入栈
入栈前要判断是否栈满,如果栈已满,则入栈失败;否则将元素放入栈顶, 栈顶指针向 上移动一个位置(top++)。
- 顺序栈的操作——出栈
出栈前要判断是否栈空,如果栈是空的,则出栈失败;否则将栈顶元素暂存 给一个变量,栈顶指针向下移动一个位置(top–)。
- 顺序栈的操作——取栈顶元素
取栈顶元素和出栈不同。取栈顶元素只是把栈顶元素复制一份,栈顶指针未移动,栈内元素个数未变。
而出栈是指栈顶指针向下移动一个位置,栈内不再包 含这个元素。
5.2 链栈的定义和操作
顺序栈是分配一段连续的空间,需要两个指针: base指向栈底,top指向栈顶。而链栈每个节点的地址是不连续的,只需要一个栈顶指针即可。
链栈的每个节点都包含两个域:数据域和指针域。可以把链栈看作一个不带头节点的单链表,但只能在头部进行插入、删除、取值等操作,不可以在中间和尾部操作。因此,可以按单链表的方法定义链栈的结构体。
- 链栈的操作——入栈
入栈是将新元素节点压入栈顶。因为链栈中第一个节点为栈顶,因此将新元 素节点插到第一个节点的前面,然后修改栈顶指针指向新节点即可。有点像摞盘子,将新节点摞到栈顶之上,新节点成为新的栈顶。
- 链栈的操作——出栈
出栈就是把栈顶元素删除,让栈顶指针指向下一个节点,然后释放该节点空 间。
- 链栈的操作——取栈顶元素
取栈顶元素和出栈不同,取栈顶元素只是把栈顶元素复制一份,栈顶指针并没有改变。
6 队列
考点
- 顺序队列的定义和操作
- 循环队列的定义和操作
- 链队列的定义和操作
6.1 顺序队列的定义和操作
- 队列的定义和操作
这种先进先出(First In First Out,FIFO)的线性序列,称为“队列”。队列也是一种线性表,只不过它是操作受限的线性表,只能在两端操作:一端进,一 端出。进的一端称为队尾(rear) ,出的一端称为队头(front)。队列可以用顺序存储,也可以用链式存储。
简述栈和队列的异同点。 答案:相同点:都是线性表 不同点:区别在于不同的读写方式,队列:按先进先出原则,出队入队操作发生在存储区的两端 堆栈:按后进先出原则,进栈出栈操作发生在存储区同一端。
- 顺序队列
队列的顺序存储采用一段连续的空间存储数据元素,并用两个整型变量记录队头和队尾元素的下标,队列的入队列和出队列操作:
6.2 循环队列的定义和操作
- 队空
无论队头和队尾在什么位置,只要Q.rear和Q.front指向同一个位置,就认为是队空。如果将循环队列中的一维数组画成环形图。
循环队列队空的判定条件为:Q.front == Q.rear。
- 队满
在此采用浪费一个空间的方法,当队尾Q.rear的下一个位置Q.front时,就认为是队满。 但是Q.rear向后移动一个位置(Q.rear+1)后,很有可能超出了数组的最大下标,这时它的下一个位置 应该为0,就(Q.rear+1)%Maxsize=Q.front。
什么是队列的“假上溢”现象?试给出两种解决方法,并做简单描述。
由于头尾指针都不断前移,超出向量空间。这时整个向量空间及队列是空的却产生了"上溢"现象 解决方法:当数据出队时,将数据整体向前移动,这样就会不会出现 假溢出 。 另一种方案是:将数组看成循环的,这样的话,即使尾端数据已经塞 满,但是由于 结构 是循环的,可以继续将队头的空位当作队头插入数据,从而解决 假溢出。
- 循环队列的操作——入队
入队时,首先将元素x放入Q.rear所指空间,然后Qrear后移一位。
入队操作,当Q.rear后移一位时,为了处理临界状态(Q.rear+1=Maxsize),需要加1后取余运算。
- 循环队列的操作——出队
先用变量保存队头元素,然后队头Q.front后移一位。
出队操作,当 Q.front 后移一位时,为处理临界状态 (Q.front+1=Maxsize),需要加1后取余运算.
- 求队列的长度
通过前面的分析,我们已经知道循环队列中元素个数为: (Q.rear-Q.front+Maxsize)%Maxsize,循环队列中元素个数即为循环队列的长度。
6.3 链队列的定义和操作
顺序队列是分配一段连续的空间,用两个整型下标front和rear分别指向队头和队尾。而链队列类似一个单链表,需要两个指针front和rear分别指向队头和队尾。从队头出队,从队尾入队,为了出队时删除元素方便,可以增加一个头节点。
- 链队列操作——入队
- 链队列的操作——出列
出队相当于删除第一个数据元素,即将第一个数据元素节点跳过去。首先 用 指针指向第一个数据节点,然后跳过该节点,即Q.front->next=p->next,
6.4 注意
7 栈和队列的应用
7.1 栈的应用
- 数值的转换:
将一个十进制n转换为二进制数
- 回文判定
回文是指正读反读均相同的字符序列,如“ abcd”和“abcscba”均是回文,
也就是说字符串沿中心线对称,但“foot”和“bed”不是回文。试写一个算法
判定给定的字符串是否为回文。
7.2 队列的应用
双端队列的遍历:
双端队列的遍历,即从头到尾输出整个队列中的元素,在输出过程中,队头 和队尾并不移动,因此借助一个暂时变量即可。
7.3 注意
8 串的定义和基本操作
考点
- 串的定义
- 串的存储方式
- 串的基本操作
8.1 串的基本概念
- 串:又称字符串,是由零个或多个字符组成的有限序列。 字符串通常用双引号括起来,例如S=“abcdefg”,S为字符串的名字,双引号里面的内容为字符串的值。
- 串长:串中字符的个数,例如S的串长为6.
- 空串:零个字符的串,串长为0.
- 子串:串中任意个连续的字符组成的子序列,称为该串的子串,原串称为子串的主串。
- 空格串:全部由空格组成的串为空格串。
8.2 串的存储方式
- 字符串的顺序存储。
顺序存储是用一段连续的空间存储字符串。可以预先分配一个固定长度Maxsize的空间,在这个空间中存储字符串。
顺序存储的三种方式。
- 以’\0’表示字符串结束
- 在0空间存储字符串的长度
- 结构体变量存储字符串的长度
- 字符串的链式存储结构
和顺序表一样,顺序存储的串在插入和删除操作时,需要移动大量元素,因 此也可以采用链表的形式存储。
单链表存储字符串时,虽然插入和删除非常容易,但是这样做也有一个问 题:一个节点只存储一个字符,如果需要存储的字符特别多,会浪费很多空 间。因此也可以考虑一个节点存储多个字符的形式,例如一个节点存储3个字符,最后一个节点不够 个时用#代替。
8.3 串的基本操作
串的基本运算:
- 求串长StrLength(s):
操作条件:串s存在;
操作结果:求出串s的长度。
- 串赋值StrAssign(s1,s2):
操作条件: s1是一个串变量, s2或者是一个串常量,或者是一个串变量(通常s2是一个串常量时称为串赋值,是一个串变量称为串拷贝)。
操作结果:将s2的串赋值给s1,s1原来的值被覆盖掉。
- 连接操作:
操作条件:串s1,s2存在。
操作结果:两个串的连接就是将一个串的串值紧接着放在另一个串的的后 面,连接成一个串,前者是产生新串s,s1和s2不改变;后者是在s1的后面连接s2的串值, s1改变,s2不改变。
- 求子串SubStr(s,i,len):
操作条件:串s存在,
操作结果:返回从串 的第i个字符开始的长度为len的子串。 得到的是空串。
- 串比较StrCmp(s1,s2):
操作条件:串s1,s2存在。
操作结果:若s1==s2,操作返回值为0;若s1<s2,返回值<0;若s1>s2,返回值>0。
- 子串定位StrIndex(s,t):找子串t在s中首次出现的位置,否则返回值为-1.
操作条件:串s1,s2存在。
操作结果:若t属于s,则操作返回t在s首次出现的位置,否则返回-1.
- 插入
- 穿删除
- 串替换
8.3 注意
- 串是每个节点仅由一个字符串组成的线性表。
- 在串顺序存储结构中,实现串操作的原操作为字符序列的复制。
- 串与线性表在逻辑结构上极为相似,区别仅在于串的数据约束对象为字符集;在基本操作上差别 很大,线性表的基本操作大多数以单个元素作为操作对象,而串的基本操作通常以串的整体作为操作对象。
- 两个串相等的充分必要条件是两个串的长度相等且各个对应位置的字符都相等。
9 串的模式匹配算法
考点
- 串的模式匹配算法
9.1 串的模式匹配算法
- 模式匹配:子串的定位运算称为串的模式匹配或串匹配。
假设两个串S、T,设S为子串,也称为正文串;T为子串,也称模式。在主串S中查找与模式T相匹配的子串,如果查找成功,返回匹配的子串第一个字符在主串中的位置。
最笨的办法就是穷举所有S的所有子串,判断是否与T匹配,该算法称为
BF(Brute Force)算法。
- 算法步骤
- 从S第1个字符开始,与T第1个字符比较,如果相等,继续比较下一 个字符,否则转向下一步;
- 从S第2个字符开始,与T第1个字符比较,如果相等,继续比较下一 个字符,否则转向下一步;
- 从S第3个字符开始,与T第1个字符比较,如果相等,继续比较下一 个字符,否则转向下一步;
- ……
- 如果T比较完毕,则返回T在S中第一个字符出现的位置;
- 如果S比较完毕,则返回0,说明T在S中未出现。
- 代码实现
10 数组定义和存储结构
考点
- 数组的定义
- 数组的存储结构
10.1 数组的定义
数组是由相同类型的数据元素构成的有限集合。
- 一维数组可以看做一个线性表。
- 二维数组也可以看作一个线性X=(X0, X1, X2,……,Xn-1),只不过每一个数据元素X,也是一个线性表。
10.2 数组的存储结构
数组一般采用顺序存储结构,因为存储单元是一维的,而数组可以是多维的, 如何用一组连续的存储单元来存储多维数组呢?以二维数组为例,可以按行序存储,即先存第一行,再存第二行……也可以按列序存储,先存第一列,再存第二列…
- 按行存储
- 按列存储
10.3 注意
二维数组定义,第二个方括号中的表达式不能为空。
11 矩阵的压缩存储和广义表
考点
- 压缩矩阵的定义
- 对称矩阵
- 三角矩阵
- 广义表
(未完待续)