大话数据结构笔记整理(1)

大话数据结构笔记整理(1)

这几天刷题遇到了瓶颈,就想着学习一波数据结构,好友推荐了《大话数据结构》,为了让自己日后来反思自己2020年7月20日是多么的蒟蒻,于是决定写一篇笔记博客。

第一章,数据结构绪论

  • 数据:是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合。
  • 数据元素: 是组成数据的、有一定意义的基本单位,在计算机中通常作为整体处理。也被称为记录。
  • 数据项:一个数据元素可以由若干个数据项组成。数据项是数据不可分割的最小单位。
  • 数据对象:是性质相同的数据元素的集合,是数据的子集。
  • 数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。

逻辑结构与物理结构

1. 逻辑结构:是指数据对象中数据元素之间的相互关系。分为以下4种:

  • 集合机构:集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系。各个数据元素是”平等”的,类似于数学中的集合。
  • 线性结构:线性结构中的数据元素之间是一对一的关系。
  • 树形结构:树形结构中的数据元素之间存在一种一对多的层次关系。
  • 图形结构:图形结构的数据元素是多对多的关系。

2. 物理结构:是指数据的逻辑结构在计算机中的存储形式。

  • 顺序存储结构:是把数据元素放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的。
  • 链式存储结构:是把数据元素存放在任意的存储单元里,这组单元可以是连续的,也可以是不连续的。

抽象数据类型

数据类型:是指一组性质相同的值得集合及定义在此集合上的一些操作的总称。
C语言中,按照取值的不同,可以分为两类:

  • 原子类型:是不可以再分解的基本类型,包括整型、实型、字符型等。
  • 结构类型:由若干个类型组合而成,是可以再分解的。例如,整型数组是由若干整型数组组成的。

抽象数据类型(Abstract Data Type, ADT):是指一个数学模型及定义在该模型上的一组操作。抽象数据类型体现了程序设计中问题分解、抽象和信息隐藏的特性。



第二章,算法

算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有序序列,并且每条指令表示一个或多个操作。

算法的特性

算法的五个基本特性:输入输出、有穷性、确定性、可行性。

  • 算法具有零个或多个输入
  • 算法至少有一个或多个输出
  • 算法在执行有限的步骤后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成
  • 算法的每一步骤都具有确定的含义,不会出现二义性
  • 算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成

算法设计的要求

  1. 正确性:算法的正确性是指算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案。

    • 算法程序没有语法错误
    • 算法程序对于合法的输入数据能够产生满足要求的输出结果
    • 算法程序对于非法的输入数据能够产生满足规格说明的结果(一般情况下,将此作为判断一个算法是否正确的标准)
    • 算法程序对于精心选择的,甚至***难的测试数据都有满足要求的输出结果
  2. 可读性:算法设计的另一个目的是为了便于阅读、理解和交流。

  3. 健壮性:当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结果。

  4. 时间效率高和存储量低:设计算法应该尽量满足时间效率高和存储量低的需求。

算法效率的度量方法

  1. 事后统计方法:这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。
  2. 事前分析估算方法:在计算机程序编制前,依据统计方法对算法进行估算,一个程序的运行时间,依赖于算法的好坏和问题的输入规模,在分析程序的运行时间时,最重要的是把程序看成是独立于程序设计语言的算法或一系列步骤。

函数的渐近式增长

函数的渐近增长:给定两个函数 f(n) 和 g(n), 如果存在一个整数N,使得对于所有的 n > N, f(n) 总是比 g(n) 大,那么,我们说 f(n) 的增长渐近快于 g(n)。

判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注主项(最高阶项)的阶数。

算法时间复杂度

在进行算法分析时,语句总的执行次数 T(n) 是关于问题规模 n 的函数,进而分析 T(n) 随 n 的变化情况并确定 T(n) 的数量级。算法的时间复杂度,也就是算法的时间度量,记作:T(n) = O(f(n))。它表示随问题规模 n 的增大,算法执行时间的增长率和 f(n) 的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中 f(n) 是问题规模 n 的某个函数。

推导大 O 阶方法

  1. 用常数 1 取代运行时间中的所有加法常数。
  2. 在修改后的运行次数函数中,只保留最高阶项。
  3. 如果最高阶存在且不是 1,则去除与这个项相乘的常数。
    得到的结果就是大 O 阶。
  • 常数阶:O(1):不管n为多少,执行的次数都是恒定的,不会随着n的变大而发生变化,其时间复杂度为O(1)
  • 线性阶:O(n):循环结构中的代码需要执行n次,其时间复杂度为O(n)。
  • 对数阶:O(logn):有多少个2相乘后大于n,则会退出循环。
  • 平方阶:O(n^2):两层循环嵌套。

常见的时间复杂度

算法空间复杂度

算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记作:S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。

当不用限定词地使用”复杂度”时,通常都是指时间复杂度



第三章,线性结构

线性表

1、零个或多个数据元素的有限序列

2、除第一个元素外,每一个元素有且只有一个直接前驱元素,除了最后一个元素外,每一个元素有且只有一个直接后继元素,数据元素之间的关系是一对一的关系。

3、在较复杂的线性表中,一个数据元素可以由若干个数据项组成

线性表的顺序存储结构

线性表的顺序存储结构,指的是用一段地址连续的存储单元一次存储线性表的数据元素

顺序存储方式

在内存中占据一定的内存空间,然后把相同数据类型的数据元素依次存放在这块空地中

属性:

  • 存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置
  • 线性表的最大存储容量:数组长度MaxSize
  • 线性表的当前长度:length

存储器中的每个存储单元都有自己的编号,这个编号称为地址

对于每个线性表位置的存入或者取出数据,对于计算机来说都是相等的时间(),也就是一个常数,因此它的时间复杂度为O(1)

顺序存储结构的插入与删除

插入:
  • 如果插入位置不合理,抛出异常
  • 如果线性表长度大于等于数组长度,则抛出异常或动态增加容量
  • 从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置
  • 将要插入元素填入位置i处
  • 表长加1
删除:
  • 如果删除位置不合理,抛出异常
  • 取出删除元素
  • 从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置
  • 表长度减1

插入或删除时,平均移动次数和最中间的那个元素移动次数相等,为(n-1)/2

插入和删除时,时间复杂度为O(n)

线性表顺序存储结构的优缺点


线性表的链式存储结构

用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的,这就意味着,这些数据元素可以存在内存未被占用的任意位置。链式结构中,除了要存储数据元素信息外,还要存储它的后继元素的存储地址。

链表中第一个结点的存储位置叫做头指针,之后的每一个结点,其实就是上一个的后继指针指向的位置,最后一个结点的指针为null。

为了更加方便地对链表进行操作,有时会在单链表的第一个结点前附设一个结点,称为头结点,头结点的数据域可以不存储任何信息,也可以存储如线性表的长度等附加信息,头结点的指针域存储指向第一个结点的指针。

头指针与头结点的异同:
  • 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
  • 头指针具有标识作用,所以常用头指针冠以链表的名字。
  • 无论链表是否为空,头指针均不为空,头指针是链表的必要元素。
  • 头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义(也可存放链表的长度)。
  • 有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其它结点的操作就统一了。
  • 头结点不一定是链表必须要素。
单链表的读取

单链表中查找某一个元素,必须要从头开始找

获得链表第i个数据的算法思路:
  • 声明一个结点p指向链表第一个结点,初始化j从1开始。
  • 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1。
  • 若到链表末尾p为空,则说明第i个元素不存在。
  • 否则查找成功,返回结点p的数据。

时间复杂度为O(n)

单链表的插入与删除
s->next=p->next; p->next=s;
单链表第i个数据插入结点的算法思路:
删除结点
q=p->next;   p->next=q->next;
单链表第i个数据删除结点的算法思路:

单链表在查询、插入和删除操作上的时间复杂度都是O(n),对于插入或者删除数据越频繁的操作,单链表的效率优势就越是明显。

单链表的整表创建
单链表整表创建的算法思路:
  • 声明一结点p和计数器变量i
  • 初始化一空链表L
  • 让L的头结点的指针指向NULL,即建立一个带头结点的单链表

循环:

  • 生成一新结点赋值给p
  • 随机生成一数字赋值给p的数据域p->data
  • 将p插入到头结点与前一新结点之间(头插法,始终让新结点在第一的位置),也可以将p插入到终端结点的后面(尾插法)
单链表的整表删除
单链表整表删除的算法思路如下:
  • 声明一结点p和q
  • 将第一个结点赋值给p

循环

  • 将下一结点赋值给q
  • 释放p
  • 将q赋值给p

单链表结构与顺序存储结构优缺点


静态链表

用数组描述的链表叫做静态链表(数组中的元素由两个数据域组成,data和cur)

数组中的第一个元素(下标为0)的cur存放备用链表的第一个结点的下标(即下一个元素插入存放的位置),数组的最后一个元素的cur则存放第一个有数值的元素的下标(即存放链头的位置)。

静态链表的插入操作

将元素”丙”插入到”乙”和”丁”之间

静态链表的删除操作

将元素”甲”删除

静态链表优缺点

循环链表

将单链表中终端结点的指针端由空指针改为指向头指针,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。

双向链表

双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。

非空的循环的带头结点的双向链表如下图所示:
双向链表在插入和删除时,需要更改两个指针变量
s->prior = p;
s->next = p->next;
p->next->prior = s;
p->next = s;
删除结点p,只需要下面两步骤
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);

因为还不太会使用Typora,导致上传到博客园图片显示不了,

导致白白辜负了自己整理图片等的几个小时,暂时就先用文字吧。

等什么时候找到一个合适的图床网站后再说吧,看到时候想不想的起来。

最后分享一个趣事:在家10天喝了30瓶牛奶,居然让我的肩宽、胸肌、肱二头肌等都大了一圈。————别想歪了,我是男的!

posted @ 2020-07-21 00:41  东玉家的熊猫  阅读(610)  评论(0编辑  收藏  举报