计算机考研——数据结构笔记

数据结构

第一章:绪论

1.1基本概念和术语

1.1.1 概念
  • 数据:信息的载体,程序加工的原料

  • 数据元素:数据的基本单位,由数据项组成

  • 数据对象:及有相同性质的数据元素的组合,是数据的子集

  • 数据类型:是一个值的集合,和定义在这个集合上操作的总称,分为:

    ①原子类型,②结构类型,③抽象数据类型

  • 数据结构:即数据+结构,是相互之间存在一种或多种特定关系的数据元素的集合,其中数据元素之间的关系被称为结构

1.1.2 数据结构三要素
  1. 数据的逻辑结构:

    • 线性结构:

      线性表:数据元素间只存在一对一的关系

    • 非线性结构:

      集合:数据元素之间除了“同属一个集合”外无其他关系

      树形结构:数据元素之间存在一对多的关系

      网状结构:数据元素之间存在多对多的关系

  2. 数据的存储结构:

    是数据结构在计算机中的映像,是通过计算机语言实现的逻辑结构

    • 顺序存储:

      定义:把逻辑上相邻的元素存储在物理位置相邻的存储单元中

      优点:可以实现随机存取,每个元素占用最少的存储空间

      缺点:只能使用相邻的一整块单元,外部碎片较多

    • 链式存储:

      定义:通过数据元素中的指针来指示元素之间的逻辑关系

      优点:不会出现碎片现象,能充分利用所有存储单元

      缺点:数据元素因指针而占用更多的存储空间,且只能顺序存取

    • 索引存储:

      定义:在存储元素信息的同时,建立一个由关键字+地址组成的索引表

      优点:检索速度快

      缺点:索引表占用额外的空间,且对数据进行操作时可能需要修改索引表,导致所需时间变长

    • 散列存储(哈希存储):

      定义:通过某种函数和元素的关键字直接计算出元素的存储地址

      优点:对数据元素的增删改查变快

      缺点:若散列函数不好,可能出现存储单元的冲突,为解决这些冲突会增加时间和空间的开销

  3. 数据的运算:

    包括运算的定义和实现,定义指出运算的功能,实现指出运算的具体步骤

1.2 算法和算法评价

1.2.1 算法概念

​ 算法即对解决问题的方法的一系列代码描述。

1.2.2 时空复杂度的计算

一、时间复杂度

​ 算法的时间复杂度是算法中基本运算的执行次数的数量级,记为O(f(n)),而基本运算是指算法中最深层循环的语句。在一般情况下,分析时间复杂度是分析最坏情况下的时间复杂度。

​ 分析时间复杂度有以下两条规则:

​ ①加法规则:
T ( n ) = T 1 ( n ) + T 2 ( n ) = O ( f ( n ) ) + O ( g ( n ) ) = O ( m a x ( f ( n ) , g ( n ) ) ) T(n)=T_1(n)+T_2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n))) T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n)))
​ ②乘法规则:
T ( n ) = T 1 ( n ) × T 2 ( n ) = O ( f ( n ) ) × O ( g ( n ) ) = O ( f ( n ) × g ( n ) ) T(n)=T_1(n)×T_2(n)=O(f(n))×O(g(n))=O(f(n)×g(n)) T(n)=T1(n)×T2(n)=O(f(n))×O(g(n))=O(f(n)×g(n))
​ 常见的渐进时间复杂度为:
O ( 1 ) < O ( log ⁡ 2 n ) < O ( n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) O(1)<O(\log_2n)<O(n)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n) O(1)<O(log2n)<O(n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
二、空间复杂度

​ 算法的空间复杂度是该算法所需的(即算法执行过程中额外所需的)存储空间,记为O(g(n))。

第二章 线性表

2.1 线性表的定义和基本操作

2.1.1 线性表的定义

​ 线性表是一种逻辑结构,是具有相同数据类型的n个数据元素的有限序列。其中每个非表头元素仅有一个直接前驱元素,非表尾元素仅有一个直接后继元素。

  • 表中元素有限
  • 表中元素具有逻辑上的顺序性,表中元素有其先后次序
  • 表中元素都是数据元素,每个元素都是单个元素
  • 表中每个元素的数据类型相同,这使得每个元素占有的存储空间相等
  • 表中元素具有抽象性,仅讨论元素之间的逻辑关系,不考虑元素本身
2.1.2 线性表的基本操作

​ 常见的基本操作有:初始化表、求表长、按值查找、按位(索引)查找、插入、删除、输出、判空、销毁等。

​ 与数组不同的是,线性表的位序是从1开始的

2.2 顺序表

2.2.1 顺序表的定义

​ 顺序表是使用一组连续的存储单元依次存储数据元素的存储结构。

​ 顺序表的空间分配方式分为两种,即静态分配和动态分配。静态分配会在初始化时就定义好顺序表的最大长度,而动态分配中,一旦顺序表的的数据空间被占满,那么就会另寻一块新的、更大的内存空间,再将原数据全部拷贝到新的空间中。

​ 顺序表的优点:

  1. 可随机访问:可通过首元素地址和元素序号在O(1)时间内找到指定元素
  2. 存储密度高:每个节点只存储数据

​ 顺序表的缺点:

  1. 元素的插入和删除需要移动大量元素
  2. 顺序表需要一段连续的地址空间,不够灵活
2.2.2 顺序表的基本操作
  1. 初始化
  2. 插入元素
  3. 删除元素
  4. 按值查找

相关代码见: SqList.cpp

2.3 线性表的链式表示

2.3.1 单链表的定义

​ 单链表是指通过一组任意的存储单元来存储线性表中的数据元素,同时为了建立起数据元素之间的线性关系,对每个链表节点,除了存放数据之外,还需要存放指向其后继元素的指针。

​ 在初始化单链表的时候,一般以头指针L标识一个单链表,也可使用一个节点作为头部,其数据域一般为空,也可以用于存储表长等信息。

​ 引入头节点的好处:

  1. 由于第一个数据节点的位置被存放于头指针的指针域中,所以在链表第一个位置上的操作与其他位置上的操作相同,无需特殊处理。
  2. 无论链表是否为空,其头指针都是指向头节点的非空指针,使得空表与非空表的处理得到统一。
2.3.2 单链表的操作实现
  1. 初始化
  2. 求表长
  3. 按序号查找结点
  4. 按值查找节点
  5. 插入节点
  6. 删除节点
  7. 头插法建立单链表
  8. 尾插法建立单链表

相关代码见:LinkList.cpp

注:对于删除操作,即便有能够获取尾节点位置的方法,也不能够做到O(1)内直接删除尾节点,因为删除前需要将尾节点的上一个节点的next置NULL,而这需要遍历链表获取

2.3.3 双链表

​ 相较于单链表的结构,双链表多了一个prior指针,指向数据元素的直接前驱,表头的prior和表位的next均为NULL。

​ 相较于单链表的操作,双链表在插入、删除的时候能够保证修改时不断链。

相关代码见:DLinkList.cpp

2.3.4 循环链表
  1. 循环单链表:

    ​ 循环单链表与单链表的区别在于:表中的尾节点的next指针指向头节点,是整个链表形成一个环。

  2. 循环双链表:

    ​ 循环双链表与双链表的区别在于:表中头节点的prior指针指向尾节点,尾节点的next指针指向头节点。

当表为空表时,循环链表头节点的next和prior(如果有)均指向头节点本身。

2.3.5 静态链表

​ 静态链表使用数组来表示的链式存储结构,使用前会分配一块连续的内存存储数据,其节点依旧是由数据域data和指针next组成,但其指针存放的是数组中的索引值。实例如下:

索引datanext
0b4
1a1
2d-1
3
4c2

​ 如上图所示,该链表的起始位置是数组中索引为1处,其next存储的是其后继元素对应的索引。

2.3.6 顺序表与链表的对比
  1. 读写方式:

    ​ 顺序表可以随机存取,也可以顺序存取;链表只能顺序存取。

  2. 逻辑结构与物理结构:

    ​ 顺序表采用顺序存储,逻辑上相邻的元素,物理结构上也相邻;链表采用链式存储,物理存储位置不一定相邻,元素之间的逻辑关系是通过指针链接来表示的。

  3. 查找、插入和删除:

    ​ 查找:顺序表无序时,时间复杂度为O(n),有序时,采用折半查找后时间复杂度为O(logn);链表时间复杂度始终为O(n)。

    ​ 插入和删除:找到元素插入或删除的位置后,顺序表平均需要移动n/2的元素方可完成,时间复杂度为O(n);链表则只需改变指针指向的节点即可,时间复杂度为O(1).

  4. 空间分配:

    ​ 顺序表:对于静态分配内存的顺序表,其不够灵活的存储方式使得这种结构容易出现内存溢出;对于动态分配内存的顺序表,其每一次扩充存储空间都需要遍历一遍数组,时间开销极大。

​ 链表:链式存储使得只要有空间就能够分配,但是由于每个节点都带有指针,使得存储密度不大。

第三章 栈、队列和数组

3.1 栈

3.1.1 栈的概念

​ 即只允许在一端进行插入或删除的线性表。

3.1.2 栈的顺序存储结构
  1. 初始化
  2. 判栈空
  3. 元素入栈
  4. 元素出栈
  5. 读栈顶元素

相关代码见:SqStack.cpp

  • 共享栈:

    ​ 利用栈底元素相对不变的特性,使两个顺序栈共享一个一维数组空间,以达到节省空间,减少外部碎片的目的。两个顺序栈的栈底分别为一维数组空间的两端,判断栈满的条件为|top1-top0| == 0。

  • 栈的链式存储:

    ​ 类似于使用头插法实现的单链表。

3.2 队列

3.2.1 队列的概念

​ 即只允许在一段进行插入,另一端进行删除的线性表。

3.2.2 队列的顺序存储结构
  • 队列:

    ​ 进队操作:送值到队尾,再将队尾指针加一

    ​ 出队操作:从对头取值,再将队头指针加一

    假溢出:由于进出队操作只会使指针的值不断增加,故经过若干次出入队操作后,即便队列中的data域为空,但头尾指针仍会指向data域外的区域,导致假溢出

  • 循环队列:

    ​ 对每次进出队操作后头/尾指针加一的操作对MaxSize取余。

  • 区分队满和队空:

    1. 牺牲一个单元来区分队满和队空:

      队满:(rear + 1) % MaxSize == front

      队空:front == rear

    2. 增设size数据成员,删除成功减一,插入成功加一

      队满:size == MaxSize

      队空:size == 0

    3. 增设tag数据成员,删除成功置0,插入成功置1

      队满:front == rear && tag == 1

      队空:front == rear && tag == 0

3.2.3 队列的链式存储结构
  • 一般而言,对应同时拥有头尾指针的单链表
3.2.4 双端队列
  • 是指允许两端均可进行插入和删除操作的队列
  • 操作受限的双端队列:
    • 输入受限:两端均可进行删除,只有其中一端可进行插入
    • 输出受限:两端均可进行插入,只有其中一段可进行删除

3.3 栈和队列的应用

3.3.1 栈在括号匹配中的应用

相关代码见:PareMatching.cpp

3.3.2 栈在表达式求值中的应用
  • 中缀表达式:形如 A+B*(C-D)+E/F的算术表达式

  • 后缀表达式:形如 ABCD-*+EF/+ 的算术表达式

  • 中缀转后缀:

    ​ 从左往右遍历

    1. 遇到操作数:加入后缀表达式
    2. 遇到运算符:若其优先级高于栈顶运算符(除括号外),则入栈;否则,依次弹出运算符,直到遇到括号为止,再将其入栈
    3. 遇到界限符(括号):若为左括号,直接入栈;为右括号,一次弹出栈中的运算符,直到遇到左括号
  • 后缀表达式求值:

    ​ 若为操作数,直接入栈;若为运算符,从栈中依次退出两个操作数X和Y,形成运算指令 XY ,再将结果压入栈中

3.3.3 栈在递归中的应用

​ 当调用一个递归函数时,一般函数会层层嵌套调用,这时系统会开辟一块递归调用栈来存储数据,若递归次数过多,同样会造成栈溢出。

3.3.4 队列在计算机系统中的应用
  • 缓冲区:

    ​ 当输出设备输出速度低于输入设备输入的速度时,便可设置一个缓冲区域,输入设备将数据输入缓冲区,缓冲区满后便可先处理其他作业,待输出设备按先进先出原则取出数据并输出完毕后,再向输入设备发起请求,这样便保证输出数据的正确,也可提高输入设备的效率。

  • CPU资源调度竞争:

    ​ 在一个多终端的计算机系统中,多个用户需要CPU执行程序,那么这些请求将被排成队列由CPU按照先进先出的原则分别执行。

3.4 数组和特殊矩阵

3.4.1 数组的定义

数组不会的话考啥研

3.4.2 数组的存储结构

数组不会的话考啥研

3.4.3 特殊矩阵的压缩存储
  1. 对称矩阵:

    ​ 由于矩阵中上三角区和下三角区对称,故直接用一维数组来存储下三角区和主对角线的元素即可。

    ​ 若将a[i][j] (1≤i,j≤n)中的数据压缩至b[k],则效标的对应关系如下:
    k = { i ( i − 1 ) 2 + j − 1 , 如果  i ≥ j j ( j − 1 ) 2 + i − 1 , 如果  i < j k = \left\{ \begin{array}{ll} \frac{i(i-1)}{2}+j-1, & \text{如果 } i \geq j \\ \frac{j(j-1)}{2}+i-1, & \text{如果 } i < j \end{array} \right. k={2i(i1)+j1,2j(j1)+i1,如果 ij如果 i<j

  2. 三角矩阵:

    • 下三角矩阵:
      和对称矩阵相似,但不同在于压缩后的数组多一位用于存储上三角的数据。
      行优先: k = { i ( i − 1 ) 2 + j − 1 , 如果  i ≥ j n ( n + 1 ) 2 , 如果  i < j 列优先: k = { ( j − 1 ) ( 2 n − j + 2 ) 2 + i − j , 如果  i ≥ j n ( n + 1 ) 2 , 如果  i < j 行优先: k = \left\{ \begin{array}{ll} \frac{i(i-1)}{2}+j-1, & \text{如果 } i \geq j \\ \frac{n(n+1)}{2}, & \text{如果 } i < j \end{array} \right.\\ 列优先: k = \left\{ \begin{array}{ll} \frac{(j-1)(2n-j+2)}{2}+i-j, & \text{如果 } i \geq j \\ \frac{n(n+1)}{2}, & \text{如果 } i < j \end{array} \right. 行优先:k={2i(i1)+j1,2n(n+1),如果 ij如果 i<j列优先:k={2(j1)(2nj+2)+ij,2n(n+1),如果 ij如果 i<j

    • 上三角矩阵:
      行优先: k = { ( i − 1 ) ( 2 n − i + 2 ) 2 + j − i , 如果  i ≤ j n ( n + 1 ) 2 , 如果  i > j 列优先: k = { j ( j − 1 ) 2 + i − 1 , 如果  i ≤ j n ( n + 1 ) 2 , 如果  i > j 行优先: k = \left\{ \begin{array}{ll} \frac{(i-1)(2n-i+2)}{2}+j-i, & \text{如果 } i \leq j \\ \frac{n(n+1)}{2}, & \text{如果 } i > j \end{array} \right.\\ 列优先: k = \left\{ \begin{array}{ll} \frac{j(j-1)}{2}+i-1, & \text{如果 } i \leq j \\ \frac{n(n+1)}{2}, & \text{如果 } i > j \end{array} \right. 行优先:k={2(i1)(2ni+2)+ji,2n(n+1),如果 ij如果 i>j列优先:k={2j(j1)+i1,2n(n+1),如果 ij如果 i>j

  3. 三对角矩阵:
    k = 3 ( i − 1 ) + ( j − i ) = 2 i + j − 3 k=3(i-1)+(j-i)=2i+j-3 k=3(i1)+(ji)=2i+j3

  4. 稀疏矩阵:

    ​ 当矩阵中非零元素个数远少于为零元素个数时,可直接使用三元组存储非零元素及其对应下标。当然也需要存储矩阵的行数和列数。

    ​ 但会这导致矩阵失去随机存取的特性,变为只读形式。

  • 28
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值