数据结构笔记(一)

程序=数据结构+算法

数据结构(C语言版)(第2版)

https://weread.qq.com/web/reader/b57320b071db572cb578fb5

数据结构与算法基础(青岛大学-王卓)

https://www.bilibili.com/video/BV1nJ411V7bd

2023.1.14-19.11


第一章 绪论

1.1数据结构的研究内容

无法用数学公式或方程来描述,是一些非数值计算的程序设计问题。

描述非数值计算问题的数学模型不是数学方程,而是诸如表,树,和图之类的具有逻辑关系的数据。

数据结构是非数值计算的程序设计中计算机的操作对象以及他们之间的关系和操作。

1.2基本概念和术语

数据(Data):客观事物的符号表示,是所有能输入到计算机中并被计算机程序处理的符号的总称。

数据元素(Data Element):是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。

数据项(Data Item):是组成数据元素的、有独立含义的、不可分割的最小单位。

数据对象(Data Object):是性质相同的数据元素的集合,是数据的一个子集。

数据结构(Data Structure):是相互之间存在一种或多种特定关系的数据元素的集合。

 

数据结构包括逻辑结构和存储结构两个层次。

逻辑结构:从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。

线性结构

(1)集合结构:数据元素之间除了“属于同一集合”的关系外,别无其他关系。

(2)线性结构:数据元素之间存在一对一的关系。

非线性结构

(3)树结构:数据元素之间存在一对多的关系。

(4)图结构或网状结构:数据元素之间存在多对多的关系。

存储结构:数据对象在计算机中的存储表示称为数据的存储结构,也称为物理结构。

(1)顺序存储结构:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系,通常借助程序设计语言的数组类型来描述。

(2)链式存储结构:每个结点附加指针字段,用于存放后继元素的存储地址。

 

数据类型和抽象数据类型

数据类型(Data Type):顺序存储结构可以借助程序设计语言的数组类型描述,链式存储结构可以借助指针类型描述,数据类型和数据结构的概念密切相关。

抽象数据类型(Abstract Data Type,ADT):表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称,具体包括三部分:数据对象、数据对象上关系的集合以及对数据对象的基本操作的集合。

ADT 抽象数据类型名{
  数据对象:〈数据对象的定义〉
  数据关系:〈数据关系的定义〉
  基本操作:〈基本操作的定义〉
}ADT 抽象数据类型名

基本操作的定义格式为:

基本操作名(参数表)
  初始条件:〈初始条件描述〉
  操作结果:〈操作结果描述〉

基本操作有两种参数:赋值参数只为操作提供输入值;

引用参数以“&”打头,除可提供输入值外,还将返回操作结果。

 

1.3 抽象数据类型的表示与实现

(1)预定义常量及类型

  //函数结果状态代码
  #define OK 1
  #define ERROR 0
  #define OVERFLOW -2
  //Status是函数返回值类型,其值是函数结果状态代码。
  typedef int Status;

(2)数据结构的表示(存储结构)用类型定义(typedef)描述;数据元素类型约定为ElemType,由用户在使用该数据类型时自行定义。

(3)基本操作的算法都用如下格式的函数来描述:

  函数类型 函数名(函数参数表)
  {
    //算法说明
    语句序列
  }//函数名

(4)内存的动态分配与释放。

分配空间 指针变量=new数据类型;
释放空间 delete指针变量;

(5)赋值语句:

简单赋值 变量名=表达式;

串联赋值 变量名1=变量名2=...=变量名n=表达式;

成组赋值 (变量名1, ..., 变量名n)=(表达式1, ..., 表达式n);

结构赋值 结构名1=结构名2; 结构名=(值1, 值2, ..., 值n);

条件赋值 变量名=条件表达式 ? 表达式T:表达式F;

交换赋值 变量名1 <-->变量名2;

(6)选择语句:

条件语句1 if (表达式) 语句;
条件语句2 if (表达式) 语句;
  else 语句;
开关语句 switch (表达式)
{
  case 值1: 语句序列1 ;break;
  case 值2: 语句序列2 ;break;
  …
  case 值n: 语句序列n;break;
  default: 语句序列n+1;
}

(7)循环语句:

  for语句 for (表达式1; 条件; 表达式2) 语句;
  while语句 while (条件) 语句;
  do-while语句 do {
    语句序列;
  } while (条件);

(8)结束语句:

函数结束语句 return 表达式;
return;
  case或循环结束语句 break;
异常结束语句 exit (异常代码);

(9)输入输出语句使用C++流式输入输出的形式:

输入语句 cin>>变量1>>…>>变量n;
输出语句 cout<<表达式1<<…<<表达式n;

(10)基本函数:

求最大值 Max (表达式1,...,表达式n)
求最小值 Min (表达式1,...,表达式n)

 

1.4 算法和算法分析

1.4.1 算法的定义及特性

算法(Algorithm)是为了解决某类问题而规定的一个有限长的操作序列。

一个算法必须满足以下五个重要特性。

(1)有穷性。一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成。

(2)确定性。对于每种情况下所应执行的操作,在算法中都有确切的规定,不会产生二义性,使算法的执行者或阅读者都能明确其含义及如何执行。

(3)可行性。算法中的所有操作都可以通过已经实现的基本操作运算执行有限次来实现。

(4)输入。一个算法有零个或多个输入。当用函数描述算法时,输入往往是通过形参表示的,在它们被调用时,从主调函数获得输入值。

(5)输出。一个算法有一个或多个输出,它们是算法进行信息加工后得到的结果,无输出的算法没有任何意义。当用函数描述算法时,输出多用返回值或引用类型的形参表示。

 

1.4.2 评价算法优劣的基本标准

(1)正确性。在合理的数据输入下,能够在有限的运行时间内得到正确的结果。

(2)可读性。一个好的算法,首先应便于人们理解和相互交流,其次才是机器可执行性。可读性强的算法有助于人们对算法的理解,而难懂的算法易于隐藏错误,且难于调试和修改。

(3)健壮性。当输入的数据非法时,好的算法能适当地做出正确反应或进行相应处理,而不会产生一些莫名其妙的输出结果。

(4)高效性。高效性包括时间和空间两个方面。时间高效是指算法设计合理,执行效率高,可以用时间复杂度来度量;空间高效是指算法占用存储容量合理,可以用空间复杂度来度量。时间复杂度和空间复杂度是衡量算法的两个主要指标。

 

1.4.3 算法的时间复杂度

1.问题规模和语句频度

问题规模是算法求解问题输入量的多少,是问题大小的本质表示,一般用整数n表示。

问题规模n对不同的问题含义不同,例如,在排序运算中n为参加排序的记录数,在矩阵运算中n为矩阵的阶数,在多项式运算中n为多项式的项数,在集合运算中n为集合中元素的个数,在树的有关运算中n为树的结点个数,在图的有关运算中n为图的顶点数或边数。显然,n越大算法的执行时间越长。

一条语句的重复执行次数称作语句频度(Frequency Count)。

 

该算法中所有语句频度之和,是矩阵阶数n的函数,用f(n)表示之。换句话说,上例算法的执行时间与f(n)成正比。

f(n)=2n3+3n2+2n+1

2.算法的时间复杂度定义

一般情况下,算法中基本语句重复执行的次数是问题规模n的某个函数f(n),算法的时间量度记作T(n)=O(f(n))它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称做算法的渐近时间复杂度,简称时间复杂度(Time Complexity)。

3.算法的时间复杂度分析举例

分析算法时间复杂度的基本方法为:找出所有语句中语句频度最大的那条语句作为基本语句,计算基本语句的频度得到问题规模n的某个函数f(n),取其数量级用符号“O”表示即可。

常量阶示例。

 

 两条语句频度均为1,算法的执行时间是一个与问题规模n无关的常数,所以算法的时间复杂度为T(n)=O(1),称为常量阶。

线性阶示例。

 

 环体内两条基本语句的频度均为f(n)=n,所以算法的时间复杂度为T(n)=O(n),称为线性阶。

平方阶示例。

 

 对循环语句只需考虑循环体中语句的执行次数,以上程序段中频度最大的语句是(6),其频度为f(n)=n2,所以该算法的时间复杂度为T(n)=O(n2),称为平方阶。多数情况下,当有若干个循环语句时,算法的时间复杂度是由最深层循环内的基本语句的频度f(n)决定的。

立方阶示例。

 

 显见,该程序段中频度最大的语句是(5),这条最深层循环内的基本语句的频度,依赖于各层循环变量的取值,由内向外可分析出语句(5)的执行次数为:

则该算法的时间复杂度为T(n)=O(n3),称为立方阶。

对数阶示例。

 

 设循环体内两条基本语句的频度为f(n),则有2f(n)≤n,f(n)≤log2n,所以算法的时间复杂度为T(n)=O(log2n),称为对数阶。

常见的时间复杂度按数量级递增排列依次为:常量阶O(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶O(nlog2n)、平方阶O(n2)、立方阶O(n3)、……、k次方阶O(nk)、指数阶O(2n)等。

 

 一般情况下,随着n的增大,T(n)的增长较慢的算法为较优的算法。显然,时间复杂度为指数阶O(2n)的算法效率极低,当n值稍大时就无法应用。应该尽可能选择使用多项式阶O(nk)的算法,而避免使用指数阶的算法。

4.最好、最坏和平均时间复杂度

称算法在最好情况下的时间复杂度为最好时间复杂度,指的是算法计算量可能达到的最小值;称算法在最坏情况下的时间复杂度为最坏时间复杂度,指的是算法计算量可能达到的最大值;算法的平均时间复杂度是指算法在所有可能情况下,按照输入实例以等概率出现时,算法计算量的加权平均值。

1.4.4 算法的空间复杂度

关于算法的存储空间需求,类似于算法的时间复杂度,我们采用渐近空间复杂度(Space Complexity)作为算法所需存储空间的量度,简称空间复杂度,它也是问题规模n的函数,记作:S(n)=O(f (n))

一般情况下,一个程序在机器上执行时,除了需要寄存本身所用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的辅助存储空间。其中,对于输入数据所占的具体存储量取决于问题本身,与算法无关,这样只需分析该算法在实现时所需要的辅助空间就可以了。若算法执行时所需要的辅助空间相对于输入数据量而言是个常数,则称这个算法为原地工作,辅助空间为O(1),本节中前面的示例都是如此。

下面举一简单示例说明如何求算法的空间复杂度。

 

算法1仅需要另外借助一个变量t,与问题规模n大小无关,所以其空间复杂度为O(1)。

算法2需要另外借助一个大小为n的辅助数组b,所以其空间复杂度为O(n)。

对于一个算法,其时间复杂度和空间复杂度往往是相互影响的,当追求一个较好的时间复杂度时,可能会导致占用较多的存储空间,即可能会使空间复杂度的性能变差,反之亦然。不过,通常情况下,鉴于运算空间较为充足,人们都以算法的时间复杂度作为算法优劣的衡量指标

 

posted @ 2023-01-22 16:00  youdao67  阅读(178)  评论(0编辑  收藏  举报