第一章 绪论
1.1 数据结构的基本概念
1.1.1 数据、数据元素、数据元素的基本类型
数据
是人们利用文字符号、数字符号以及其他规定的符号对现实世界的事物及其活动所作的抽象描述。
表示一个事物的一组数据称作一个
数据元素
。
构成数据元素的数据称作该数据元素的
数据项
。
例如,要描述学生信息,可包括学生的学号、姓名、性别、年龄等
数据
。学生的学生的学号、姓名、性别、年龄等数据构成学生情况描述的
数据项。
包括学号、姓名、性别、年龄等数据项的一组数据构成学生信息的一个
数据元素
。
学号 |
姓名 |
性别 |
年龄 |
2023001 |
张三 |
男 |
18 |
2023002 |
李四 |
女 |
19 |
2023003 |
王五 |
男 |
20 |
2023004 |
牛二 |
男 |
16 |
图1-1 学生信息表
1.1.2 数据的逻辑结构
数据元素之间的相互联系方式称为
数据的逻辑结构
。
数据的逻辑结构有两个要素:一是数据元素,二是关系。
按照数据元素之间的相互联系方式,数据的逻辑结构主要可分为
集合结构、线性结构、树状结构和图结构
四种。
![]() (a)集合结构
|
![]()
(b)线性结构 |
![]()
(c)树状结构 |
![]()
(d)图结构 |
图1-2 四种基本逻辑结构关系图
(1)集合结构
数据元素之间除了“属于同一集合”的关系外,别无其他关系。例如,确定一名学生是否为班级成员,只需将班级看作一个集合结构。
(2)线性结构
数据元素之间存在一对一的关系。
(3)树状结构
数据元素之间存在一对多的关系。
(4)图结构
数据元素之间存在多对多的关系。
其中,集合结构、树结构、图结构都属于
非线性结构
。
1.1.3 数据的存储结构(物理结构)
数据元素在计算机中的存储方式称为
数据的存储结构
。
数据存储结构的基本形式有两种:一是
顺序存储结构
,另一种是
链式存储结构
。
(1)顺序存储结构
顺序存储结构是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系,通常借助程序设计语言的
数组
类型来描述。顺序存储结构要求所有的元素一次存放在一片
连续的存储空间
中。

图1-3 顺序存储结构示意图
(2)链式存储结构
链式存储结构,无需占用一整块存储空间。但是为了表示结点之间的关系,需要给每个结点附加指针字段,用于存放后继元素的存储地址。所以链式存储结构通常借助于程序设计语言的
指针
类型来描述。

图1-4 链式存储结构示意图
1.2 算法和算法的时间复杂度
算法
就是描述求解问题方法的
操作步骤集合
。
1.4.1 任何算法都应满足以下性质
(1)
输入性
:具有零个或若干个输入量;
(2)
输出性
:至少产生一个输出量或执行一个有意义的操作;
(3)
有限性
:执行语句的序列是有限;
(4)
确定性
:每条语句的含义明确、无二义性;
(5)
可执行性
:每条语句都应在有限的时间内完成。
1.4.2 算法设计满足以下目标
(1)
正确性;
(2)
可读性;
(3)
健壮性;
(4)
高时间效率;
(5)
高空间效率。
1.4.3 算法的时间效率分析
(1)代码的时间复杂度

(2)
最好、最坏和平均时间复杂度
最好时间复杂度
,指的是算法计算量可能达到的最小值。
最坏时间复杂度
,指的是算法计算量可能达到的最大值。
平均时间复杂度
,是指算法在所有可能情况下,按照输入实例以等概率出现时,算法计算量的加权平均值。
第一章课后习题
1.简述下列概念:数据、数据元素、数据项、数据对象、数据结构、逻辑结构、存储结构、抽象数据类型。
答:数据:是客观事物的符号表示,指所有能输入到计算机中并被计算机程序处理的符号的总称。
数据元素:是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。在有些情况下,数据元素也称为元素、结点、记录等。
数据项:是组成数据元素的、有独立含义的、不可分割的最小单位。
数据对象:是性质相同的数据元素的集合,是数据的一个子集。
数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。
逻辑结构:从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。
存储结构:数据对象在计算机中的存储表示,也称为物理结构。
抽象数据类型:由用户定义的,表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称。具体包括三部分:数据对象、数据对象上关系的集合和对数据对象的基本操作的集合。
2.试举一个数据结构的例子,叙述其逻辑结构和存储结构两方面的含义和相互关系。
例如有一张学生基本信息表,包括学生的学号、姓名、性别、籍贯、专业等。每个学生基本信息记录对应一个数据元素,学生记录按顺序号排列,形成了学生基本信息记录的线性序列。对于整个表来说,只有一个开始结点(它的前面无记录)和一个终端结点(它的后面无记录),其他的结点则各有一个也只有一个直接前趋和直接后继。学生记录之间的这种关系就确定了学生表的逻辑结构,即线性结构。
这些学生记录在计算机中的存储表示就是存储结构。如果用连续的存储单元(如用数组表示)来存放这些记录,则称为顺序存储结构;如果存储单元不连续,而是随机存放各个记录,然后用指针进行链接,则称为链式存储结构。
即相同的逻辑结构,可以对应不同的存储结构。
3.简述逻辑结构的四种基本关系并画出它们的关系图。
(1)集合结构
数据元素之间除了“属于同一集合”的关系外,别无其他关系。例如,确定一名学生是否为班级成员,只需将班级看做一个集合结构。
(2)线性结构
数据元素之间存在一对一的关系。例如,将学生信息数据按照其入学报到的时间先后顺序进行排列,将组成一个线性结构。
(3)树结构
数据元素之间存在一对多的关系。例如,在班级的管理体系中,班长管理多个组长,每位组长管理多名组员,从而构成树形结构。
(4)图结构或网状结构
数据元素之间存在多对多的关系。例如,多位同学之间的朋友关系,任何两位同学都可以是朋友,从而构成图形结构或网状结构。
其中树结构和图结构都属于非线性结构。
4.存储结构由哪两种基本的存储方法实现?
(1)顺序存储结构
顺序存储结构是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系,通常借助程序设计语言的数组类型来描述。
(2)链式存储结构
顺序存储结构要求所有的元素依次存放在一片连续的存储空间中,而链式存储结构,无需占用一整块存储空间。但为了表示结点之间的关系,需要给每个结点附加指针字段,用于存放后继元素的存储地址。所以链式存储结构通常借助于程序设计语言的指针类型来描述。
5.选择题
(1)在数据结构中,从逻辑上可以把数据结构分成( )。
A.动态结构和静态结构 B.紧凑结构和非紧凑结构
C.线性结构和非线性结构 D.内部结构和外部结构
答案:C
(2)与数据元素本身的形式、内容、相对位置、个数无关的是数据的( )。
A.存储结构 B.存储实现
C.逻辑结构 D.运算实现
答案:C
(3)通常要求同一逻辑结构中的所有数据元素具有相同的特性,这意味着( )。
A.数据具有同一特点
B.不仅数据元素所包含的数据项的个数要相同,而且对应数据项的类型要一致
C.每个数据元素都一样
D.数据元素所包含的数据项的个数要相等
答案:B
(4)以下说法正确的是( )。
A.数据元素是数据的最小单位
B.数据项是数据的基本单位
C.数据结构是带有结构的各数据项的集合
D.一些表面上很不相同的数据可以有相同的逻辑结构
答案:D
解释:数据元素是数据的基本单位,数据项是数据的最小单位,数据结构是带有结构的各数据元素的集合。
(5)算法的时间复杂度取决于( )。
A.问题的规模 B.待处理数据的初态
C.计算机的配置 D.A和B
答案:D
解释:算法的时间复杂度不仅与问题的规模有关,还与问题的其他因素有关。如某些排序的算法,其执行时间与待排序记录的初始状态有关。为此,有时会对算法有最好、最坏以及平均时间复杂度的评价。
(6)以下数据结构中,( )是非线性数据结构
A.树 B.字符串 C.队列 D.栈
答案:A
6.试分析下面各程序段的时间复杂度。
(1)
x = 90;
y = 100;
while
(y > 0) {
if
(x > 100) {
x = x -10;
y–;
}
else
x++;
}
答案:O(1)
解释:程序的执行次数为常数阶。
(2)
for
(i = 0; i < n; i++) {
for
(j = 0; j < m; j++) {
a[i][j]= 0;
}
}
答案:O(m*n)
解释:语句a[i] [j]=0; 的执行次数为m*n。
(3)
s = 0;
for
(i = 0; i < n; i++) {
for
(j = 0; j < n; j++) {
s +=B[i][j];
sum =s;
}
}
答案:O(n2)
解释:语句 s+=B[i] [j]; 的执行次数为n2。
(4)
i = 1;
while
(i <= n) {
i = i * 3;
}
答案:O(log3n)
解释:语句i=i*3;的执行次数为log3n (有关对数公式:如果ax = N,记做 x = logaN)
设 i = i*3 执行了 x 次,则 3x = n ;故 x = log3n
(5)
x = 0;
for
(i = 1; i < n; i++) {
for
(j = 1; j <= n – i; j++) {
x++;
}
}
答案:O(n2)
解释:语句x++;的执行次数为 n-1+n-2+……+1= n(n-1)/2。
i = 1 时,循环执行 n-1 次
i = 2 时,循环执行 n-2 次,
i = 3 时,循环执行 n-3 次,
……
i = n-1 时,循环执行 1 次
则相加: (n-1)+(n-2)+(n-3)+……+3+2+1 = n(n-1)/2
一共有 n-1 个式子,前后相加等于 n ,则总和为 n(n-1)/2
(6)
x = n;
//n>1
y = 0;
while
(x ≥ (y + 1) * (y + 1)){
y++;
}
答案:O(
n
)
解释:x≥(y+1)*(y+1) = n≥(y+1)2
开根号:
n≥(y+1)
n-1≥y
则
y≤n-1
x=n; //n>1
y=0;
while(
y≤n-1
){
y++;
}
y++;执行了
n
次。则时间复杂度为:O(
n
)
第二章 线性表
2.1线性表
2.1.1 线性表的定义和特点
线性表
是一种由
n
(
n
≥0)个相同类型数据元素
a
0,
a
1,…,
a
n-1组成的线性结构。
线性结构的特点
是,除了第一个和最后一个元素外,每个元素只有一个前驱元素和一个后继元素。线性表可以在
任意位置
进行插入和删除元素操作。
2.1.2 线性表的抽象数据类型
用符号DateType表示抽象元素的数据类型。当具体软件设计要求的线性表的元素类型为int或char类型是,可重新定义。C语言相应语句是:
typedef int DataType;
或
typedef char DateType;
2.2 线性表的顺序表示和实现
线性表的
顺序存储
又被称为
顺序表。

图2-1 顺序表的存储结构
由于线性表的长度可变,且所需最大存储空间随问题的不同而不同,在C语言中通常使用
动态分配的一维数组
表示线性表。
用C语言描述图2-1所示的顺序表,定义结构体SeqList如下。
typedef struct{
DataType list[MaxSize];
int size;
}SeqList;
其中DataType为数组元素的数据类型,MaxSize表示数组元素的最大个数,list表示顺序表的数组成员,size表示顺序表中当前存储的数组元素个数成员,且必须满足条件size<=MaxSize,SeqList是结构名。
完整代码如下:
#include <stdio.h> //包含printf()
#define MaxSize 100 //定义MaxSize为100
typedef int DataType; //定义DataType为int
typedef struct{
DataType list[MaxSize];
int size;
}SeqList;
//初始化顺序表L
void ListInitiate(SeqList* L) {
L->size = 0; //定义初始元素个数
}
//求当前元素个数
int ListLength(SeqList L) {
return L.size;
}
//插入元素
int ListInsert(SeqList* L, int i, DataType x) {
//在顺序表的第i(0~size)个位置前插入x
//插入成功返回1,失败返回0
int j;
if (L->size >= MaxSize) {
printf("顺序表已满无法插入!\n");
return 0;
}
else if (i<0 || i>L->size) {
printf("参数不合法!\n");
return 0;
}
else {
//从后向前依次后移数据,为插入做准备
for (j = L->size; j > i; j--)
L->list[j] = L->list[j - 1];
L->list[i] = x;
L->size++;
return 1;
}
}
//删除元素
int ListDelete(SeqList* L, int i, DataType* x) {
//删除顺序表L中的第i(0~size)个位置处的元素并保存到x中
//删除成功返回1,失败返回0
int j;
if (L->size <= 0) {
printf("顺序表已空无元素可删!\n");
return 0;
}
else if (i<0 || i>L->size - 1) {
printf("参数i不合法!\n");
return 0;
}
else {
*x = L->list[i];
//从前向后依次前移
for (j = i + 1; j <= L->size - 1; j++) {
L->list[j - 1] = L->list[j];
}
/*for (j = i ; j <= L->size ; j++) {
L->list[j] = L->list[j+1];
}*/
L->size--;
return 1;
}
}
//取元素
int ListGet(SeqList L, int i, DataType * x) {
if (i<0 || i>L.size - 1) {
printf("参数i不合法!\n");
return 0;
}
else {
*x = L.list[i];
return 1;
}
}
//主函数
void main(void) {
SeqList myList;
DataType i, x;
ListInitiate(&myList); //初始化函数调用
for (i = 0; i < 10; i++) //插入10个元素
ListInsert(&myList, i, i + 1); //插入函数调用
ListDelete(&myList, 4, &x); //删除函数调用
//显示顺序表当前元素
for (i = 0; i < ListLength(myList); i++)
{
ListGet(myList, i, &x); //取元素函数调用
printf("%d ", x);
}
printf("\n");
ListInsert(&myList, 8, 1); //插入函数调用
//显示顺序表当前元素
for (i = 0; i < ListLength(myList); i++)
{
ListGet(myList, i, &x);
printf("%d ", x);
}
printf("\n");
int j =0;
ListGet(myList, 12, &j); //取元素函数调用
printf("%d ", j);
}
2.3 线性表的链式表示和实现
2.3.1 单链表的表示方法

图2-2 单链表的结点结构
定义单链表的结构体如下:
typedef struct Node{
DataType data;
struct Node *next;
}SLNode;
其中,data域用来存放元素,next域用来存放指向下一个结点的指针。
单链表有带头节点结构和不带头节点结构。

(a) 空链表 (b)非空链表
图2-3 带头节点的空链表
2.3.2 单链表的操作实现
#include <stdio.h> //包含printf()
#include <malloc.h> //包含malloc()等函数
typedef int DataType; //定义DataType为int
typedef struct Node{
DataType data;
struct Node *next;
}SLNode;
//初始化点链表集合
void ListInitiate(SLNode** head) {
*head = (SLNode*)malloc(sizeof(SLNode)); //申请头节点,由head指示其地址
(*head)->next = NULL; //置结束标记NULL
}
//求当前元素个数
int ListLength(SLNode* head) {
SLNode* p = head;
int size = 0;
while (p->next != NULL) {
p = p->next;
size++;
}
return size;
}
//插入节点
int ListInsert(SLNode* head, int i, DataType x) {
SLNode* p, * q;
int j;
p = head;
j = -1;
while (p->next != NULL && j < i - 1) {
//最终让指针p指向i-1个节点
p = p->next;
j++;
}
if (j != i - 1) {
printf("插入元素位置参数错!\n");
return 0;
}
q = (SLNode*)malloc(sizeof(SLNode)); //生成新节点
q->data = x; //新节点数据域赋值
q->next = p->next;
p->next = q;
return 1;
}
//删除节点
int ListDelete(SLNode* head, int i, DataType *x) {
SLNode* p, * q;
int j;
p = head;
j = -1;
while (p->next != NULL && p->next->next != NULL && j < i - 1) {
//循环结束时指针p指向第i-1个节点
p = p->next;
j++;
}
if (j != i - 1) {
printf("删除元素位置参数错!\n");
return 0;
}
q = p->next; //指针q指向第i个节点
*x = q->data; //把指针q所指节点的数据域赋予x
p->next = p->next->next; //删除
free(q); //释放指针q所指节点的内存空间
return 1;
}
//取元素
int ListGet(SLNode* head, int i, DataType* x) {
SLNode* p;
int j;
p = head;
j = -1;
while (p->next != NULL && j < i) {
p = p->next;
j++;
}
if (j != i) {
printf("取元素位置参数错!\n");
return 0;
}
*x = p->data;
return 1;
}
//撤销单链表
void Destory(SLNode** head) {
SLNode* p, * q;
p = *head;
while (p != NULL) {
q = p;
p = p->next;
free(q);
}
*head = NULL;
}
//主函数
void main(void) {
SLNode* head; //定义头指针变量
DataType i, x;
ListInitiate(&head); //初始化
for (i = 0; i < 10; i++) { //插入10个元素
ListInsert(head, i, i + 1);
}
ListDelete(head, 4, &x); //删除第i=4(即第5个)元素
for (i = 0; i < ListLength(head); i++) { //显示当前元素
ListGet(head, i, &x); //取元素
printf("%d ", x); //显示
}
Destory(&head); //撤销单链表
}