数据结构(C语言版)目录第1章绪论
第2章线性表
第3章栈和队列
第4章串
第5章数组
第6章树
第7章图
第8章查找
第9章排序
第10章文件
数据结构(C语言版)第1章绪论1.1
数据结构的基本概念和术语
1.2 算法描述与分析
1.3 实习:常用算法实现及分析
习题1
数据结构(C语言版)1.1
数据结构的基本概念和术语1.1.1 引例
首先分析学籍档案类问题。设一个班级有50个学生,这个
班级的学籍表如表1.1所示。
表1.1 学籍表序号
学号姓名性别英语数学物理
01
200303
01
李明男869180
02
200303
02
马琳男768385
50
200303
50
刘薇薇女889390数据结构(C语言版)
我们可以把表中每个学生的信息看成一个记录,表中的每
个记录又由7个数据项组成。该学籍表由50个记录组成,记录
之间是一种顺序关系。这种表通常称为线性表,数据之间的逻
辑结构称为线性结构,其主要操作有检索、查找、插入或删除
等。
又如,对于学院的行政机构,可以把该学院的名称看成
树根,把下设的若干个系看成它的树枝中间结点,把每个系分
出的若干专业方向看成树叶,这样就形成一个树型结构,如图
1.1所示。数据结构(C语言版)
图1.1 专业设置理工学院
机械工程材料工程信息工程
机械制造与自动化工业设计模具设计热处理计算机科学与技术数据结构(C语言版)
树中的每个结点可以包含较多的信息,结点之间的关系不
再是顺序的,而是分层、分叉的结构。树型结构的主要操作有
遍历、查找、插入或删除等。
最后分析交通问题。如果把若干个城镇看成若干个顶点,
再把城镇之间的道路看成边,它们可以构成一个网状的图(如图
1.2所示),这种关系称为图型结构或网状结构。在实际应用中,
假设某地区有5个城镇,有一调查小组要对该地区每个城镇进行
调查研究,并且每个城镇仅能调查一次,试问调查路线怎样设
计才能以最高的效率完成此项工作?这是一个图论方面的问题。
交通图的存储和管理确实不属于单纯的数值计算问题,而是一
种非数值的信息处理问题。数据结构(C语言版)
图1.2 交通示意图A地B地D地C地E地数据结构(C语言版)
1.1.2
数据结构有关概念及术语
一般来说,数据结构研究的是一类普通数据的表示及其相
关的运算操作。数据结构是一门主要研究怎样合理地组织数据,
建立合适的数据结构,提高计算机执行程序所用的时间效率和
空间效率的学科。1968年,美国的D.E.Knuth教授开创了数据结
构的最初体系,他的名著《计算机程序设计技巧》较为系统地
阐述了数据的逻辑结构和存储结构及其操作。
“数据结构”是计算机专业的一门专业基础课。它为操作
系统、数据库原理、编译原理等后继专业课程的学习奠定了基
础。数据结构涉及到各方面的知识,如计算机硬件范围中的存
储装置和存取方法,计算机软件范围中的文件系统、数据的动
态存储与管理、信息检索,数学范围中的许多算法知识。数据结构(C语言版)
在计算机科学中,数据(Data)是描述客观事物的数字、字
符以及所有能够输入到计算机中并被计算机处理的信息的总称。
除了数字、字符之外,还有用英文、汉字或其他语种字母组成
的词组、语句,以及表示图形、图像和声音等的信息,这些也
可称为数据。
数据元素(Data Element)是数据的基本单位,在计算机中通
常作为一个整体进行考虑和处理。例如,图1.1“专业设置树”
中的一个专业,图1.2“交通图”中的一个城镇都可称为一个数
据元素。数据元素除了可以是一个数字或一个字符串以外,它
也可以由一个或多个数据项组成。例如,表1.1中每个学生的学
籍信息作为一个数据元素,在表中占一行。每个数据元素由序
号、学号、姓名、性别、英语成绩等7个数据项组成。数据项
(Data Item)是有独立含义的数据的最小单位,有时也称为字段
(Field)。数据结构(C语言版)
数据对象(Data Object)是具有相同性质的数据元素的集合,
是数据的一个子集。例如,整数数据对象是集合N={0,±1,
±2,…},字母字符数据对象是集合C={‘A’, ‘B’, …, ‘Z’}。本节表
1.1中的学籍表也可看成一个数据对象。
数据结构(Data Structure)是带有结构的数据元素的集合,它
是指数据元素之间的相互关系,即数据的组织形式。我们把数
据元素间的逻辑上的联系,称为数据的逻辑结构。常见的数据
结构有前文所介绍的线性结构、树型结构、图型结构。数据的
逻辑结构体现数据元素间的抽象化相互关系,并不涉及数据元
素在计算机中具体的存储方式,是独立于计算机的。数据结构(C语言版)
然而,讨论数据结构的目的是为了在计算机中实现对数据
的操作,因此还需要研究如何在计算机中表示数据。数据的逻
辑结构在计算机存储设备中的映像被称为数据的存储结构,也
可以说数据的存储结构是逻辑结构在计算机存储器中的实现,
又称物理结构。数据的存储结构是依赖于计算机的。常见的存
储结构有顺序存储结构、链式存储结构等。关于它们的详细解
释将在以后的章节中逐步给出。
通常所谓的“数据结构”是指数据的逻辑结构、数据的存
储结构以及定义在它们之上的一组运算。不论是存储结构的设
计,还是运算的算法设计,都必须考虑存储空间的开销和运行
时间的效率。因此,“数据结构”课程不仅讲授数据信息在计
算机中的组织和表示方法,同时也训练学生高效地解决复杂问
题程序设计的能力。数据结构(C语言版)1.2 算法描述与分析1.2.1 什么是算法
在解决实际问题时,当确定了数据的逻辑结构和存储结构
之后,需进一步研究与之相关的一组操作(也称运算),主要有
插入、删除、排序、查找等。为了实现某种操作(如查找),常
常需要设计一种算法。算法(Algorithm)是对特定问题求解步骤
的一种描述,是指令的有限序列。描述算法需要一种语言,可
以是自然语言、数学语言或者是某种计算机语言。算法一般具
有下列5个重要特性:
数据结构(C语言版)
(1) 输入:一个算法应该有零个、一个或多个输入。
(2) 有穷性:一个算法必须在执行有穷步骤之后正常结束,
而不能形成无穷循环。
(3) 确定性:算法中的每一条指令必须有确切的含义,不能
产生多义性。
(4) 可行性:算法中的每一个指令必须是切实可执行的,即
原则上可以通过已经实现的基本运算执行有限次来实现。
(5) 输出:一个算法应该至少有一个输出,这些输出是同输
入有某种特定关系的量。
数据结构(C语言版)
1.2.2 算法描述工具——C语言
如何选择描述数据结构和算法的语言是十分重要的问题。
传统的描述方法是用PASCAL语言。在Windows环境下涌现出
一系列功能强大、面向对象的描述工具,如Visual C++,
Borland C++,Visual Basic,Visual FoxPro等。近年来在计算机
科学研究、系统开发、教学以及应用开发中,C语言的使用越
来越广泛。因此,本教材采用C语言进行算法描述。为了能够
简明扼要地描述算法,突出算法的思路,而不拘泥于语言语法
的细节,本书有以下约定:
数据结构(C语言版)
(1) 问题的规模尺寸用MAXSIZE表示,约定在宏定义中已
经预先定义过,例如:
#define MAXSIZE 100
(2) 数据元素的类型一般写成ELEMTP,可以认为在宏定义
中预先定义过,例如:
#define ELEMTP int
在上机实验时根据需要,可临时用其他某个具体的类型标
识符来代替。
数据结构(C语言版)
(3) 一个算法要以函数形式给出:
类型标识符函数名(带类型说明的形参表)
{语句组}
例如:
int add (int a,int b)
{int c;
c=a+b;
return©;
}
除了形参类型说明放在圆括号中之外,在描述算法的函数
中其他变量的类型说明一般省略不写,这样使算法的处理过程
更加突出明了。
数据结构(C语言版)
(4) 关于数据存储结构的类型定义以及全局变量的说明等均
应在写算法之前进行说明。
下面的例子给出了书写算法的一般步骤。
例1.1 有n个整数,将它们按由大到小的顺序排序,并且输
出。
分析:n个数据的逻辑结构是线性表(a1,a2,a3,…,an);选用
一维数组作存储结构。每个数组元素有两个域:一个是数据的
序号域,一个是数据的值域。
数据结构(C语言版)struct node
{int num; /序号域/
int data;/值域/
}
/算法描述1.1/
void simsort(struct node a [MAXSIZE], int n)/数组a的数据由主函数提供/
{int i,j, m;
for(i=1;i<n;i++)
for(j=1;j<=n;j++)
if(a[i].data<a[j].data)
{m=a[i];a[i]=a[j];a[j]=m;}
for(i=i;i<=n;i++)
printf(“%8d %8d %10d\n”,i,a[i].num,a[j].data);
}数据结构(C语言版)
1.2.3 算法分析技术初步
著名的计算机科学家N.沃思提出了一个有名的公式:算法+
数据结构=程序。由此可见,数据结构和算法是程序的两大要素,
二者相辅相成,缺一不可。一种数据结构的优劣是在实现其各
种运算的算法中体现的。对数据结构的分析实质上也就是对实
现其多种运算的算法的分析。评价一个算法应从四个方面进行:
正确性、简单性、运行时间、占用空间。但主要看这个算法所
要占用机器资源的多少。而在这些资源中时间和空间是两个最
主要的方面,因此算法分析中最关心的也就是算法所需的时间
代价和空间代价。数据结构(C语言版)
- 空间
所谓算法的空间代价(或称空间复杂性),是指当问题的规
模以某种单位由1增至n时,解决该问题的算法实现所占用的空
间也以某种单位由1增至f(n),并称该算法的空间代价是f(n)。数据结构(C语言版) - 时间
(1) 语句频度(Frequency Count):指的是在一个算法中该语
句重复执行的次数。
(2) 算法的渐近时间复杂度(Asymptotic Time Complexity):
算法中基本操作重复执行的次数依据算法中最大语句频度来估
算,它是问题规模n的某个函数f(n),算法的时间量度记作
T(n)=O(f(n)),表示随着问题规模n的增大,算法执行时间的增长
率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称时间
复杂度。时间复杂度往往不是精确的执行次数,而是估算的数
量级。它着重体现的是随着问题规模的增大,算法执行时间增
长的变化趋势。数据结构(C语言版)1.3 实习:常用算法实现及分析例如,在下列三个程序段中:
(a) x=x+1;
(b) for(i=1;i<=n;i++) x=x+1;
© for(j=1;j<=n;j++)
for(k=1;k<=n;k++) x=x+1;
语句x=x+1的频度分别为1、n和n2,则(a)、(b)、©的时间复杂
度分别是O(1)、O(n)、O(n2)。由此可见,随着问题规模的增大,
其时间消耗也在增大。
数据结构(C语言版)
下面以©程序段为例,进行时间复杂度的分析。
步骤1:先把所有语句改为基本操作。
j=1;1
a: if j<=nn+1
{k=1;n1
b: if k<=nn(n+1)
{x=x+1;nn
k++;nn
goto b;
}
j++;n*1
goto a;
}
数据结构(C语言版)
步骤2:分析每一条语句的语句频度,标到每条语句后边,
如上。
步骤3:统计总的语句频度:1+n+1+n+n(n+1)+n2+
n2+n=3n2+4n+2
。
步骤4:判断最大语句频度为n2,所以时间复杂度为
O(n2)。
其中O表示等价无穷小。
数据结构(C语言版)
现在来分析例1.1中算法1.1的时间复杂度。算法中有一个
二重循环,if语句的执行频度为
n+(n-1)+(n-2)+…+3+2+1= 2
)1n(n+数量级为
O(n2)。算法中输出语句的频度为n,数量级为
O(n)。该算法的时间复杂度以if语句的执行频度来估算(忽略输
出部分),则记为O(n2)
。算法还可能呈现的时间复杂度有指数阶
O(lbn)等。
数据结构(C语言版)习
题11. 简述下列术语:数据元素,数据,数据对象,数据结构,
存储结构。 - 试写一算法,自大至小依次输出顺序读入的3个整数x、
y和z的值。分析算法的元素比较次数和元素移动次数。 - 举出一个数据结构的例子,叙述其逻辑结构、存储结构、
运算等三方面的内容。 - 叙述算法的定义及其重要特性。
数据结构(C语言版) - 分析并写出下面的各语句组所代表的算法的时间复杂度。
(1) {for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
for(k=1;k<=j;k++)
{s=i+k;printf(“%d”,s);}
}
数据结构(C语言版)
(2) {i=1;k=0;
while(i<=n-1)
{k=k+10i;
i++;
}
}
(3) {i=1;k=0;n=100;
do{k=k+10i;
i++;
}while(i= =n);
}
数据结构(C语言版)
(4) {i=1;j=0;
while(i+j<=n)
{if(i>j) j++;
else i++;
}
}
(5) {x=n;/n>1/
y=0;
while(x>=(y+1)(y+1)) y++;
}
数据结构(C语言版)
(6) {m=91;n=100;
while(n>0)
{if(m>0){m=m-1;n–;}
else m++;
}
}
数据结构(C语言版)第2章线性表2.1
线性表引例
2.2 线性表的定义和基本运算
2.3 线性表的顺序存储结构
2.4 线性表的链式存储结构
2.5 循环链表和双向链表
2.6 实习:线性表的应用实例
习题2
数据结构(C语言版)2.1
线性表引例例2.1 某大学欲进行一次数学竞赛,约有200名学生报名
参赛。现将报名登记表(如表2.1所示)存入计算机以便完成如下
工作:
(1) 能正确录入学生记录;
(2) 按成绩对该表进行重新排序;
(3) 按学号或姓名查询学生成绩。
数据结构(C语言版)
表2.1 报名登记表学号
姓名性别成绩
2003张三男84
2024李四男79
2035王五女75
数据结构(C语言版)2.2 线性表的定义和基本运算2.2.1 线性表的概念
线性表是指n(n≥0)个具有相同类型数据元素(或称结点)的有
限序列,可表示为(a1,a2,…,ai,…,an)。其中,ai代表一个数据元
素,a1称为表头(或头结点),an称为表尾(或尾结点),ai(0≤i<n)
称为ai+1的直接前驱,ai+1称为ai的直接后继。线性表中数据元
素的个数称为线性表的长度,长度为0的线性表称为空表,记
为()。
数据结构(C语言版)
在不同的问题中,数据元素代表的具体含义不同,它可
以是一个数字﹑一个字符,也可以是一句话,甚至其他更复杂
的信息。例如:
线性表L1: (12, 58, 45, 2, 45, 46), 其元素为数字;
线性表L2: (a, g, r, d, s, t), 其元素为字母。
表2.1也是一个线性表,其数据元素较为复杂,每个学生
的学号﹑姓名﹑性别﹑成绩构成一个数据元素。这种由若干数
据项构成的数据元素常称为记录,含有大量记录的线性表称为
文件。
数据结构(C语言版)
2.2.2 表的基本运算
线性表是一种相当灵活的数据结构,对其数据元素可以进
行各种运算(操作)。如对表2.1,应不仅能查询成绩,还能根据
需要增加或删除学生记录。下面给出线性表一些基本运算的含
义,这些运算的实现算法后面将具体讨论。
(1) Initiate (L):初始化运算。该函数用于设定一个空的线
性表L。
(2) Length (L):求长度函数。该函数返回给定线性表L中
数据元素的个数。
(3) Get (L, i ):取元素操作。若1≤i≤Length(L),则函数值
为给定线性表中第i个数据元素,否则为空元素NULL。
数据结构(C语言版)
(4) Prior (L, x):求前驱函数。当x在线性表L中,且其位序
大于1,则函数值为x的直接前驱,否则为空元素。
(5) Next (L, x):求后继函数。当x在线性表L中,且其位序
小于Length(L), 则函数值为x的直接后继,否则为空元素。
(6) Locate (L,x):定位操作。如线性表中存在和x 相等的数
据元素,则返回该数据元素的位序。若满足条件的元素不惟一,
则返回最小的位序。
(7) Insert (L, i, x): 前插操作。若1≤i≤Length(L)+1,则在线
性表L中第i个元素之前插入新结点x。
数据结构(C语言版)
(8) Delete (L, i): 删除操作。若1≤i≤Length(L),则删除线性
表L中第i个元素。
(9) Empty (L):判空函数。若L为空表,则返回值为1(表示
“真”),否则返回值为0(表示“假”)。
(10) Clear (L):置空操作。将线性表L 值为空表。
利用这些基本运算还可实现对线性表的各种复杂操作。如
将两个线性表进行合并,重新复制一个线性表,对线性表中的
元素按某个数据项递增(或递减)的顺序进行重新排序等。读者
可将以上基本运算应用于表2.1,理解在具体问题中各种运算的
具体含义。
数据结构(C语言版)2.3
线性表的顺序存储结构2.3.1 向量的存储特点
在计算机内,线性表可以用不同的方式来存储。其中最简
单﹑最常用的方式就是顺序存储,即用一组连续的存储单元依
次存放线性表中的元素。这种顺序存储的线性表称为顺序表,
又叫向量。
假设线性表每个元素占s个存储单元,并以其所占的第一个
单元的存储地址作为数据元素的存储位置, 则线性表中第i+1个
元素的存储位置Loc(ai+1)
和第i个数据元素的存储位置Loc(ai)之
间满足下列关系:Loc(ai
+1) = Loc(ai) + s
数据结构(C语言版)
设线性表的起始位置(或称基址)是Loc(a1),
因每个元素所占用
的空间大小相同,则元素ai的存放位置为:
Loc(ai)=Loc(a1)+s(i
-1)
由此可见,线性表的顺序存储结构是用数据元素在计算机
内“物理位置相邻”来表示数据元素之间的逻辑相邻关系,其
特点是向量中逻辑上相邻的结点在计算机的存储结构中也相邻,
如图2.1所示。而且,只要知道了向量的基地址,由上式即可确
定向量中任一数据元素的地址,从而对其可随机存取。
数据结构(C语言版)
图2.1 线性表的顺序存储结构示意图元素在线性
表中的位置存储地址内存状态1b
2b+
s…b+s (i-1)…
b+s (maxsize-1)
n
n-
1
…i…数据结构(C语言版)
在C语言中,可以用一维数组来描述向量。
define maxsize N; /*
设置线性表的最大长度为N, N为整数*/typedef struct
{datatype data[maxsize+1]; /datatype
为元素的数据类
型,它可是Turbo C中/
/允许的任何数据类型/ int last; /*
记录当前表中元素的个数*/} Sqlist;
数据结构(C语言版)
上述描述方法,将线性表顺序存储结构中的信息封装隐
藏在类型Sqlist结构中。data数组描述了线性表中数据元素占用
的空间,数组中第i个分量就是线性表中第i个数据元素。last描
述了当前表中数据元素的个数即表长。
说明:在C语言中,数组的下标是从0开始的,但为了算
法描述方便,本书中凡涉及数组的算法,规定下标从1开始,
这样,读者可不必考虑下标为0的数组元素。
数据结构(C语言版)
2.3.2 向量中基本运算的实现
- 定位操作Locate (L, x)
定位操作返回线性表L中值和x相同的第一个元素的位置。
算法如下:
/算法描述2.1/
Int Locate (Sqlist L, Datatype x)
{int i=1;
while ((i<L.last) && (L.data[i]!=x)) i++;
if (i<=L.last) return (i);
else return (0 );
}
数据结构(C语言版)
也可按数据元素的某个关键数据项进行数据元素的定位。例
如,表2.1所示的学生成绩表可按学号或姓名进行定位,只需将
上面算法中data[i]和x换成相应数据项即可,请读者参阅后面实
例。
算法描述2.1的基本操作是“进行两个元素之间的比较”。
若线性表L中存在值和x相等的数据元素ai,则需进行
i
(1≤i≤L.last) 次比较,否则,进行L.last次比较,直至i超出表长。
所以该算法的时间复杂度为O(n)。
数据结构(C语言版)
2.向量的插入运算Insert(L,i,x)
线性表的插入操作是指在线性表的第i-1个元素和第i 元素
之间插入一个新的数据元素,使长度为n的线性表(a1, a2, …, ai,
ai+1, …, an ),
变成长度为n+1的线性表(a1, a2, …, ai, x, ai+1, …,
an+1)
。显然数据元素ai和ai+1的逻辑关系发生了变化,对向量而
言,逻辑上相邻的数据元素在物理位置上也相邻,因此,必须
将第i (i<n+1)至第n个元素依次向后移一个位置,空出位置放入
x,才能反映这个逻辑关系上的变化。其具体算法如下:
数据结构(C语言版)
/算法描述2.2/
void Insert (Sqlist L, int i, Datatype x )
{int j;
if (i<1 || i >L.last ) printf (“infeasible position! \n”);
else { if ( L.last+1>maxsize ) printf(“overflow!\n”);
else {for (j=L.last; j>=i; j–) L.data[j+1]=L.data[j];
L.data[i]=x; L.last++;
}
}
}
数据结构(C语言版) - 向量的删除运算Delete (L, x)
与向量的插入运算道理相同,当删除线性表中第i 个元素时,
也改变了原数据间的逻辑关系,故需将第i+1(i<n+1)至第n个元
素依次向前移一个位置来反映这个变化。算法如下:
/算法描述2.3/
void Delete (Sqlist L, int i)
{int j;
if (i<1 || i>L.last ) printf (“infeasible \n”);
else {for (j=i; j<L.last; j++)
L.data[j]=L.data[j+1];
L.last–; }
}
数据结构(C语言版)
从算法2.2和2.3可以看出,在向量中某个位置插入或删除
一个数据元素时,其时间主要耗费在移动元素上,故应将移动
元素的操作作为预估算法时间复杂度的基本操作。假定在线性
表中任意位置插入元素的概率相等,即p=1/(n+1), 那么在长度
为n 的线性表中插入一个元素时所需移动元素的平均次数为
Einsert=
∑pi*(n-i+1) 1n
1
+= ∑(n-i+1)2
n=
数据结构(C语言版)
同理, 在线性表中删除任意一个元素时所需移动元素的平均次数为
Edelete=∑pd*(n
-i+1)
= ∑(n-i+1)
= n
12
1n−所以
, 对于长度为n的线性表, 算法Insert和算法Delete的时
间复杂度均为O(n)。
数据结构(C语言版)2.4
线性表的链式存储结构2.4.1 线性链表
与线性表的顺序存储结构不同,链式存储结构用一组任意
的存储单元(可以是连续的,也可以是不连续的)来存储线性表
的数据元素。为表示相邻数据元素之间的逻辑关系,将每个存
储结点分为两个域:数据域用来存放一个数据元素的自身信息;
指针域用来存放该数据元素直接后继的存储位置。这样,可以
通过指针域中存放的信息(称为指针或链)将n个结点连接成一个
链表,即成为线性表的链式存储结构。由于这种存储结构中每
个结点只有一个指针域,故又将其称为线性单链表或单向链表。
数据结构(C语言版)
图2.2给出了线性表(A,B,C,D)的链式存储结构。由于最后一
个元素没有直接后继,则其结点的指针域应为“空”(NULL)。
另外,由图2.2可以看出,头指针指向链表中第一个结点的存储
位置,每个元素的存储位置都包含在其直接前驱结点的指针域
中,因此,单向链表的存取必须从头指针开始,它是一种非随
机存取的存储结构。
用线性链表表示线性表时,数据元素之间的逻辑关系由结
点中的指针指示,故逻辑上相邻的数据元素其物理位置不要求
紧邻,这与线性表的顺序存储结构完全不同。
数据结构(C语言版)
图2.2 单向链表的存储结构示意图内存状态
数据域指针域2000H
A2002H
B2006H
C3205H…
…
…
…D
NULL
2002H…
2006H…3205H存储地址
头指针head
2000H数据结构(C语言版)
我们在使用链表时往往只关心它所表示的数据元素之间的
逻辑顺序,而不是每个元素在存储器中的实际位置。因此,为
了分析方便,把链表画成用箭头相连接的结点的序列,结点之
间的箭头表示链域中的指针, 如图2.2可画成图2.3所示的形式。
图2.3 单向链表的逻辑状态图A
BCDhead数据结构(C语言版)
根据上述分析,单向链表可由头指针惟一确定,故在C语
言中可用指针数据类型来描述。
Typedef struct Node
{Datatype data;
struct Node *next;
}Node, *LList;数据结构(C语言版)
一个单向链表对应一个头指针head,head是一个LList类型
的变量,即它是一个指向Node类型结点的指针变量,并指向单
向链表的第1个结点,通过它可以访问该单向链表。若头指针
为“空”(即head=NULL),则表示一个空表。
一般在单向链表中附加一个头结点,其指针域指向链表的
第一个结点,而其数据域可以存储一些如链表长度之类的附加
信息,也可以什么都不存储。这样,链表的头指针将指向头结
点。如图2.4所示,表空的条件是头结点的指针域为“空”,即
head->next=NULL。数据结构(C语言版)
图2.4 带表头的单链表4
ABCDhead
headD数据结构(C语言版)
2.4.2 单向链表基本运算的实现 - 取单链表中元素Get (L , i)
该函数返回线性表L中第i个数据元素的值。算法思路:从头
指针出发,借用指针p,从第1个结点开始,顺着后继指针向后寻
找第i个元素。若存在第i 个元素,即1≤i<Length(L),则通过p返
回该元素的值。数据结构(C语言版)
/算法描述2.4/
Datatype GetLList(LList L, int i)
{ LList p;
int j =1;
p=L->next;
while( p!=NULL && j<i ) {p=p->next; j++; }
if (p= = NULL || j>i ) printf(“no this data!\n”);
else return(p->data);
}
该算法的基本操作是比较j和i 并后移指针,若第i 个元素存
在,则需执行基本操作i-1次,否则执行n次,故算法2.4的时
间复杂度均为O(n)。数据结构(C语言版) - 定位函数Locate(L, x)
该函数在线性链表中寻找值与x相等的数据元素,若有,则
返回其存储位置,否则返回NULL。其算法2.5思路与算法2.4相
似, 其时间复杂度均也为O(n)。
/算法描述2.5/
LList Locate (LList L, Datatype x)
{LList p;
p=L->next;
while (p!=NULL && p->data!=x ) p=p->next;
return §;
}数据结构(C语言版) - 单链表的插入Insert (L, i,x)
该函数在线性链表第i个元素之前插入一个数据元素x。算法
思路:先生成一个包含数据元素x的新结点(用s指向它),再找到
链表中第i-1个结点(用p指向它),修改这两个结点的指针即可。
指针修改如图2.5所示,用语句描述为:
s->next=p->next;
p->next=s;
注意:修改指针的顺序,若先修改第i-1个结点的指针,使
其指向待插结点,那么,第i
个结点的地址将丢失,链表“断开”,
待插结点将无法与第i个结点链接。数据结构(C语言版)
图2.5 单向链表中插入结点时指针的变化情况
(a) 插入前; (b) 插入后4
ABCDheadp
(a)
4ABCDheadp
x
s
(b)数据结构(C语言版)
/算法描述2.6/
void InsetLList(LList L, int i, Datatype x)
{LList p, s;
int j=0;
p= L;
while (p!=NULL && j<i-1 ) {p=p->next; j++; }
if (p= = NULL || j>i-1) printf(“No this position!\n”);
else {s=(LList) malloc (sizeof (Node) );
s->data=x;
s->next=p->next;
p->next=s;}
}数据结构(C语言版) - 单链表的删除Delete( L, i)
该函数删除线性链表中第i个数据结点。显然,只要找到第i
-1个结点修改其指针使它跳过第i个结点,而直接指向第i+1个
结点即可。但要注意,删除的结点应及时向系统释放,以便系
统再次利用。指针变化如图2.6所示,语句描述为
p->next=p->next->next ;数据结构(C语言版)
图2.6 单向链表中删除结点时指针的变化情况
4ABCDhead数据结构(C语言版)
其具体算法描述如下:
/算法描述2.7/
void Delete (LList L, int i)
{LList p, q;
int j=0;
p=L;
while ( p!=NULL && j<i-1 ) {p=p->next; j++; }
if ( p= = NULL || j>i-1) printf(" No this data!\n");
else { q=p->next;
p->next =p->next->next;
free (q);
}
}数据结构(C语言版)
由于在单向链表中插入和删除结点时,仅需修改相应结点
的指针,而不需移动元素,该程序的执行时间主要耗费在查找
结点上,由算法2.4知访问结点的时间复杂度为O(n), 所以算法
2.6和算法2.7的时间复杂度均为O(n)。数据结构(C语言版) - 单链表的建立Crt-LList(L,n)
建立线性表的链式存储结构的过程就是一个动态生成链表
的过程,即从“空表”的初始状态起,依次建立各元素结点,
并逐个插入链表。下面是一个从表尾到表头建立单链表的算法,
其时间复杂度是O(n)。数据结构(C语言版)
/算法描述2.8/
void Crt-LList(LList h, int n)
{LList p,q;
int i;
h=(LList)malloc(sizeof (Node) );
h->next=NULL; p=h;
for(i=1;i<=n;i++)
{q=(LList)malloc(sizeof(Node));
scanf(“%&”,&q->data); q->next=NULL;
p->next=q; p=q;}
}数据结构(C语言版)
说明:上面算法中分别引用了Turbo C 语言的两个标准函
数malloc()和free()。设p为LList 型变量,则执行
p=(LList)malloc(sizeof (Node)) 的作用是向系统申请一个Node型
的结点,同时让p指向该结点;执行free§的作用是向系统释放
一个由p所指的Node型的结点,已释放的空间可供系统再次使
用。数据结构(C语言版)2.5 循环链表和双向链表2.5.1 循环链表
循环链表是另一种形式的链式存储结构。其特点是表中最
后一个结点的指针域指向头结点,整个链表呈环状。从表中任
意结点出发都可到达其他结点,如图2.7所示为单循环链表。
图2.7 单循环链表
(a) 非空表;(b) 空表B
CD
headhead
(a)(b)数据结构(C语言版)
循环链表和单链表算法实现基本相同,差别仅在于前者算
法中的循环条件是判p或p->next是否为空,而后者是判它们是
否等于头指针。有时为了简化某些操作在链表中设立尾指针,
而不是头指针。例如,将两个用循环链表存储的线性表合并成
一个线性表,此时仅需将一个表的表尾和另一个表的表头相连
即可。指针变化如图2.8所示,用语句描述为:
p=A->next;
A->next =B->next->next ;
B->next=p;
操作只改变了两个指针值,其算法的时间复杂度均为O(1)。数据结构(C语言版)
图2.8 循环链表合并示意图
(a) 合并前;(b) 合并后A
BB
A
(a)(b)
…
…
…
…数据结构(C语言版)
2.5.2 双向链表
单向链表的结点只有一个指示其直接后继的指针域, 顺着
某结点的指针可很容易地访问其后诸结点。但若要访问某结点
的直接前驱,前驱虽与该结点相邻却无法直达,此时需从表头
出发,且寻访时要记录相关信息。为克服单向链表这种访问方
式的单向性,特设计了双向链表, 如图2.9(b)所示。
显然,在双向链表的结点中应有两个指针域,一个指向直
接后继,一个指向直接前驱,如图2.9(a)所示。双向链表在
Turbo C语言中可描述如下:数据结构(C语言版)
typedef struct dnode
{datatype data;
struct dnode *prior;
struct dnode *next;
}DNode, *DList;数据结构(C语言版)
图2.9 双向链表示例
(a) 结点结构;(b) 双向链表prior
datanextAheadBC
(a)(b)数据结构(C语言版)
图2.10 双向循环链表示例
(a) 空表;(b) 非空表headAheadB
C
(a)(b)数据结构(C语言版)
在双向链表中,Length(L),Get(L,i),Locate(L,x)等操作仅
涉及一个方向的指针,其算法描述与单链表相同。但插入和删
除操作有所不同,在双向链表中需同时修改两个方向的指针,
图2.11和图2.12分别显示了删除和插入结点时指针的修改情况,
其具体算法读者可自己完成。数据结构(C语言版)
图2.11 双向链表中删除结点时指针的修改情况A
BC
p数据结构(C语言版)
图2.12 双向链表中插入结点时指针的修改情况A
B
C
p数据结构(C语言版)
在双向链表中删除结点时指针的变化用语句描述为:
p->prior->next=p->next;
p->next->prior=p->prior;
free§;
在双向链表中插入结点时指针的变化用语句描述为:
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;数据结构(C语言版)
2.5.3 线性表的顺序存储结构和链式存储结构的比较
在计算机中,线性表有两类不同的存储结构:顺序存储结
构和链式存储结构,它们各有特点。顺序表的特点是逻辑上相
邻的结点在存储结构中也相邻,它是一种可随机存取存储结构,
在C语言中,用一维数组来描述,有以下三方面的缺点:
(1) 在插入和删除结点时,需移动大量元素;
(2) 在对长度较大的线性表预先分配空间时,必须按最大空
间分配,从而使存储空间得不到充分利用;数据结构(C语言版)
(3) 表的容量难以扩充。
链式存储结构的特点是逻辑上相邻的数据元素其物理位置
不要求紧邻,它是一种非随机存取存储结构,在C语言中用
“结点指针”来描述。它克服了顺序表上述的三个缺点,但却
不具备像顺序表那样随机存取的优点。
在实践中应仔细分析,根据不同研究对象的特点和经常进
行的操作选择合适的存储结构。数据结构(C语言版)2.6 实习:线性表的应用实例实例1:利用顺序表实现例2.1的完整C语言程序。
def maxsize 250
typedef struct
{ struct student
{int num;
char *name;
char gender;
float score;
} data[maxsize+1];
int last;
} Sqlist;
数据结构(C语言版)
include “stdio.h”
void Initiate(L)
Sqlist L;
{L.last=0;}
int Locate ( L, x)
Sqlist L;
int x ;
{int i=1;
while (i<L.last && L.data[i].num<>x) i++;
if (i<=L.last) return (i);
else return (0 );
}
数据结构(C语言版)
void sort(L)
Sqlist L;
{int i, j;
student x;
for(i=2; i<L.last;i++)
{L.data[0]=L.data[1]; j=i-1; x=L.data[i].num;
while (x<L.data[j].num) {L.data[j+1]=L.data[j];j=j-1;}
L.data[j+1]=L.data[0];}
}
main()
{ Sqlist L;
int i=1, num;
Initiate(L);
数据结构(C语言版)
printf(“input data, please!( “-1”-------------end)\n”);
scanf(“num=%d”, &L.data[i].num);
While(L.data[i].num<>-1&&i<=maxsize)
{scanf(“name=%s,sex=%c,score=%f\n”,&L.data[i].name,&L.data[i].
sex,&L.data[i].score);
i++;
scanf(“num=%d”, &Ldata[i].num);}
Sort(L);
for(i=1; i<=L.last;i++)
printf(“%d %s %c %f
n”, L.data[i].num, L.data[i].name, L.data[i].sex,
L.data[i].score);
数据结构(C语言版)
printf(“do you want to find a student?(y\n) “);
while(getchar()==‘y’)
{printf(” input the number of the student ! “);
scanf(”%d”, &num);
i=Locate(L, num);
printf(“%d, %s %c %f\n”, L.data[i].num, L.data[i].name,
L.data[i].sex, L.data[i].score);}
}
数据结构(C语言版)
实例2:多项式相加问题。
- 存储结构的选取
任一一元多项式可表示为Pn(x)=P0+P1x+P2x2+…+Pnxn,显然,
由其n+1个系数可惟一确定该多项式。故一元多项式可用一个仅
存储其系数的线性表来表示,多项式指数i隐含于Pi的序号中。
P=( P0,
P1,P2,…,Pn)
若采用顺序存储结构来存储这个线性表,那么多项式相加
的算法实现十分容易,同位序元素相加即可。
数据结构(C语言版)
但当多项式的次数很高而且变化很大时,采用这种顺序
存储结构极不合理。例如,多项式S(x)= 1+ 3x+12x999需用一长
度为1000的线性表来表示,而表中仅有三个非零元素,这样将
大量浪费内存空间。此时可考虑另一种表示方法,如线性表
S(x)可表示成S=((1,0),(3,1), (12,999)),其元素包含两个数据
项:系数项和指数项。
这种表示方法在计算机内对应两种存储方式:当只对多项
式进行访问、求值等不改变多项式指数(即表的长度不变化)的
操作时,宜采用顺序存储结构;当要对多项式进行加法、减法、
乘法等改变多项式指数的操作时,宜采用链式存储结构。
数据结构(C语言版)
2)一元多项加法运算的实现
采用单链表结构来实现多项加法运算,无非是前述单向链表
基本运算的综合应用。其数据结构描述如下,
typedefstuctPnode
{floatcoef;
intexp;
structpnode*next;
}Pnode,*Ploytp;
图2.13给出了多项式A(x) = 15+ 6x+ 9x7+3x18和
B(x)=
4x+5x6+ 16x7的链式存储结构
(设一元多项式均按升幂形式存储,
首指针为-1)。
数据结构(C语言版)
图2.13 一元多项式的存储-
1
0151679183A(x)-
11465716B(x)数据结构(C语言版)
若上例A+B结果仍存于A中,根据一元多项式相加的运算规
则,其实质是将B逐项按指数分情况合并于“和多项式”A中。
设p, q分别指向A, B的第一个结点,如图2.14所示,其算法思路
如下:
(1) p->expexp, 应使指针后移p=p->next,如图2.14(a)所
示。
(2) p->exp=q->exp, 将两个结点系数相加,若系数和不为零,
则修改p->ceof,并借助s释放当前q结点, 而使q指向多项式B的下
一个结点,如图2.14(b)所示;若系数和为零,则应借助s释放p, q
结点, 而使p, q分别指向多项式A,B的下一个结点。数据结构(C语言版)
(3) p->exp > q->exp,将q结点在p结点之前插入A中, 并使q
指向多项式B的下一个结点,如图2.14©所示。
直到q=NULL为止或p=NULL,将B的剩余项链到A尾为止。
最后释放B的头结点。数据结构(C语言版)-10151679183A-11465716B
C
p
p
p
(a)-
10151679183A-11465716B
Cp
s
(b)
qq-
10151679183
A
C-
11465716B
q
©图
2.14
多
项
式
相
加
运
算
示
例
数据结构(C语言版)
下面给出从接收多项式到完成多项式相加运算的完整C语言程序。
include “stdio.h”
void Crt-Polytp (h, n)
Polytp h;
int n;
{ Polytp,q;
int i;
h=( Polytp)malloc(sizeof (Pnode) );
h->next=NULL; p=h;
for(i=1;i<=n;i++)
{q=( Polytp)malloc(sizeof(Pnode));
scanf(“%f,%d”,&q->ceof,&q->exp); q->next=NULL;
p->next=q; p=q;}
}
数据结构(C语言版)
int Cmp(a,b)
float a,b;
{ if(a<b)return(-1);
if(a=b)return(0);
if(a>b)return(1);
}
void Add Poly(pa, pb,pc)
Polytp pa,pb,pc;
{ Polytp p,q,pre,s;
p=pa->next; q=pb->next;
pre=pa; pc=pa;
while(p!=NULL & q<> NULL)
数据结构(C语言版)
{w=cmp(p->exp, q->exp);
switch(w)
{case -1: pre=p; p=p->next; break;
case 0: sum=p->coef+q->coef;
if (sum<>0) {p->coef=sum; pre=p; }
else {pre ->next=p->next; free§; }
p=pre->next;
s=q; q=q->next; free(s); break;
case 1: s=q->next; q->next=p; pre->next=q;
pre=q; q=s; break;
}
}
数据结构(C语言版)
if (pb<>NULL) pre->next=q;
free(pb);
}
main()
{Ploytp pa,pb.pc,q;
int n1 , n2;
printf(" input the length of pa and pb");
scanf(“n1= %d, n2=%d”, &n1, &n2);
数据结构(C语言版)
Crt-Polytp (pa,n1);
Crt-Polytp (pb,n2);
AddPolytp(pa,pb,pc);
p=pc->next;
printf(“pc=pa+pb=”);
while(p<>NULL)
{printf(" %f,%d", p->ceof, p->exp); p=p->next;}
}
数据结构(C语言版)习
题21.判断下列概念的正确性:
(1) 线性表在物理存储空间中也一定是连续的。
(2) 链表的物理存储结构具有与链表一样的顺序。
(3) 链表的删除算法很简单,因为当删去链表中某个结点后,
计算机会自动地将后继的各个单元向前移动。
2.试比较顺序存储结构和链式存储结构的优缺点。
数据结构(C语言版)
3.试写出一个计算链表中数据元素结点个数的算法,其
中指针p指向该链表的第一个结点。
4.试设计实现在单链表中删去值相同的多余结点的算法。
5.有一个性表(a1,
a2,…,an),它存储在有附加表头结
点的单链表中,写一个算法,求出该线性表中值为x的元素的
序号。如果x不存在,则输出序号为0。
6. 写一个算法将一单链表逆置。要求操作在原链表上进
行。
数据结构(C语言版)
7.设有两个链表A、B,它们的数据元素分别为(x1,
x2,…,
xm)
和(y1,y2,…,yn)。写一个算法将它们合并为一个线性表C,
使得:
当m≥n时,C=xl,
y1,x2,y2,…,xn,yn,…,xm;
当m<n时,C=yl,
xl,y2,x2,…,ym,xm,…,yn。
8.在一个非递减有序线性表中,插入一个值为x的元素,使
插入后的线性仍为非递减有序。试分别用向量和单链表编写算
法。
9.写一个算法将值为b的结点插在链表中值为a的结点之
后。如果值为a的结点不存在,则插入在表尾。
数据结构(C语言版)第3章栈和队列3.1
栈和队列引例
3.2 栈
3.3 顺序栈的存储结构及算法实现
3.4 链式栈
3.5 队列
3.6 实习: 栈的应用实例
习题3
数据结构(C语言版)3.1
栈和队列引例任一表达式都可看成是由操作数,运算符和界限符组成的
一个串。其中,操作数可以是常数也可以是变量或常量的标识
符,运算符可以是算术运算符,关系运算符和逻辑运算符等,
界限符包括左右括号和表达式结束符等,例表达式7+4*(8-3)。
计算机要完成表达式的求值,必须正确的解释表达式,将其翻
译成正确的机器指令序列。要了解计算机的求值过程,必须先
研究栈的性质。
数据结构(C语言版)3.2
栈3.2.1栈的定义和基本运算
栈是限定只能在表尾进行插入和删除的线性表,并将表尾
称为栈顶,表头称为栈底。图3—1给出了非空栈s=(A,B,C,D)的
示意图。
图3—1 非空栈示意图
数据结构(C语言版)
由于限定只能在栈顶进行操作,所以栈中元素必按A,B,C,D
次序进栈,按D,C,B,A次序出栈,即按"后进先出"(或“先进后出")
的原则进行操作的。这一特点可用生活中的实例形象说明。例
如每次只能容一个人进出的死胡同就相当一个栈,胡同口相当
于栈顶,而胡同的另一端则为栈底。
数据结构(C语言版)
3.2.2栈的基本运算
(1)判栈空Empty(S). 若栈为空则返回“真“,否则返回”
假“;
(2)入栈操作(压栈)Push(S,x) 将新元素压入栈中,使其
成为栈顶元素;
(3)出栈操作(弹栈)Pop(S,x) 若栈不空则返回栈顶元素,
并从栈中删除栈顶元素,否则返回NULL;
(4)取栈顶元素Pettop(s,x) 若栈不空则返回栈顶元素,否则
返回NULL;
(5)置空栈Clear(s) 将当前栈设定为空栈;
(6)求元素个数CurrenSize(s) 求当前栈中元素的个数。
数据结构(C语言版)3.3
顺序栈的存储结构及算法实现3.3.1顺序栈
顺序栈利用一组连续的存储单元存放从栈底到栈顶的诸元
素。同时用一指针top指示当前栈顶元素的位置,C语言中用一
维数组来描述。
#define maxsize N
typedef struct
{Datatype data[maxsize+1];
int top;
} Stack;
数据结构(C语言版)
(a)空栈(b)一般情况©满栈
图3—2栈中元素和栈顶指针之间的关系
数据结构(C语言版)
3.3.2顺序栈的基本运算的实现
判栈空,置空栈,求元素个数算法容易实现,只要判断或
改变s.top的值即可。下面仅给出进栈,出栈,取栈顶元素等函
数的算法实现。
- 压栈
/算法描述3-1/
int Push(Stack S, Datatype x)
{if (S.top==maxsize) {printf(“overflow\n”); return(0);}
S.data[++S.top]=x; return(1);
}
数据结构(C语言版) - 出栈
/算法描述3-2/
int Pop(Stack s, Datatype x)
{if(S.top==0){printf(“nudertflow\n”);return(0)};
x=S.data[S.top]; S.top–;
return(1);
}
数据结构(C语言版) - 取栈顶元素
/算法描述3-3/
int Gettop(Stack S, Datatype x)
{if(S.top==0){printf(“nodata\N”);return(0);}
x=S.data[top]; return(1);
}
数据结构(C语言版)3.4
链式栈链式栈的组织形式和单链表类似, 其类型说明如下
Tyoedef struct Node
{Datatype data;
struct Node *next;
}Node, *LStack;
数据结构(C语言版)
一个链栈由其栈顶指针唯一确定.设TOP是LStack形变量,
则TOP指向栈顶元素。图3—3为链栈示意图。top=NULL为判
断栈空的条件。对链栈除非整个可用空间都被占满,否则不会
出现栈满的情形。其操作是线性链表操作的特例,易实现. 请
读者自行补充。
数据结构(C语言版)
图3.3 链栈示意图
数据结构(C语言版)3.5
队列3.5.1队列的定义和运算
和栈相反,队列是一种“先进先出”的线性表。他只允许
再表的一端(称表尾)插入元素,在表的另一端(表头)删除
元素。队列和日常生活中的排队是一致的,最早进入队列的元
素最早得到服务。图3—4给出可队列示意图。
图3.4 队列示意图
数据结构(C语言版)
队列的操作与栈的操作类似,不同的是删除运算是在表头进
行。
(1)判队空EmptyQueue(Q) 若队列为空则返回“真“,否则返
回”假“;
(2)入队InQueue(Q,x) 将新元素x插入到队尾;
(3) 出队OutQueue(Q,x) 若队列不空则返回队首的第一个元
素元素,并从队列中删除该元素,否则返回NULL;
(4)置空队列InitQueue(Q) 将当队列设定为空队列;
(5)求元素个数CurrentSize(Q) 返回当前队列中元素的个数。
数据结构(C语言版)
3.5.2.队列的存储结构及其算法实现
和栈类似,队列的顺序存储结构用一个向量来存储队列中
的元素,不过还需两个指针来指示队头元素和对尾元素在队列
中的当前位置。C语言描述如下:
define maxsize N
typedef stuct
{Datatype adta[maxsize+1];
int front, rear;
} Queue;
数据结构(C语言版)
(a) 满队(b)一般情况© 空队
图3—5顺序队列示意图
数据结构(C语言版)
3.5.3 顺序队列的基本运算
(1)判队空
/算法描述3-4/
int EmptyQueue(Queue Q)
{if (Q.frontQ.rear) return(1);
else return(0);}
数据结构(C语言版)
2.入队
/算法描述3.5/
int InQueue(Queue Q, Datatype x)
{if (Q.rearmaxsize) {printf(“overflow!”); return(0);}
Q.rear++;
Q.data[Q.rear]=x;
return(1);}
数据结构(C语言版)
3 . 出队
/算法描述3-6/
int OutQueue(Queue Q, Datatype x)
{if(EmptyQueue(Q)){printf(“underflow!”); return(0);}
x=Q.data[Q.front+1];
Q.front++;
if(EmptyQueue(Q)){Q.front=0; Q.rear=0}
return(1);}
数据结构(C语言版)
3.5.4 循环队列
上述算法中判队列满的条件为Q.rearmaxsize. 用它来分
析一下图3—5所示的队列状态, 此时,maxsize=5,
Q.front=3,Q.rear=5, 显然不能作入队操作. 但队列中实际元素个
数并未达到maxsize, 这种现象称之为假溢出. 解决这一问题的一
个办法是在发生假溢出时将队列中全部元素向前移动指头指针
为零, 但这样很浪费时间. 另一种办法是设想一个循环队列, 假
想Q.data[1]接在Q.data[maxsize]之后, 如图3—6所示(由于循环队
列的特点,队列中的元素可以存放在数组中下标从0到maxsize-1
的共maxsize各位置).
数据结构(C语言版)
(a) 空队(b)非空队©满队
图3.6 循环队列示意图
数据结构(C语言版)
从上图不难看出, 无论空队还是满队都有Q.front=Q.rear. 从
而无法判断属于那一种情况. 为此可少用一个元素空间, 而以队
尾指针加1等于头指针来作为满队的标志.即,
队空: Q.front=Q.rear;
队满: Q.front=(Q.rear.+1)%maxsize;
循环队列的置空算法和非循环队列的置空算法相同, 其入
队和出队算法如下,
数据结构(C语言版)
(1) 入队
/算法描述3-7/
int InQueue(Queue Q, Datatype x)
{if ((Q.rear+1)% maxsize Q.front) {printf(“overflow!”); return(0);}
Q.rear=(Q.rear++)% maxsize
Q.data[Q.rear]=x;
Return(1);}
数据结构(C语言版)
(2) 出队
/算法描述3-8/
nt OutQueue(Queue Q, Datatype x)
{if(EmptyQueue(Q)){printf(“underflow!”); return(0);}
Q.front= Q.front++% maxsize
x=Q.data[Q.front];
return(1);}
数据结构(C语言版)3.6
实习: 栈的应用实例1.表达式的构成
任一表达式都可看成是由操作数,运算符和界限符组成的
一个串。其中,操作数可以是常数也可以是变量或常量的标识
符,运算符可以是算术运算符,关系运算符和逻辑运算符等,
界限符包括左右括号和表达式结束符等,例表达式7+4*(8-3)。
为论述方便,这里仅介绍简单算术表达式的求值问题。
数据结构(C语言版)
2.算符的优先关系
要对表达式正确求值,必须正确的解释表达式,将其翻译
成正确的机器指令序列。例如,要对表达式3*(7-2)求值,首
先要了解算术运算的规则。即
1)从左到右;
2)先括号内,后括号外;
3)先乘除,后加减;
故,该表达式的计算步骤应为3*(7-2)=35=15。
数据结构(C语言版)p1
±/()@
+>><<<>>
->><<<>>
>>>><>>
/>>>><>>
(<<<<<=
)>>>>>>
@<<<<<=表
3.1算符优先级表
数据结构(C语言版)
表中空白表示运算符p1,p2不可能相遇的情况,若相遇则
表明出现了语法错误。“#”是表达式的结束符,为算法方便在
表达式的左边也虚设一个“#”使其配对。这样,当“(”=“)”
时表示左右括号相遇,括号内运算已经结束;同理,当
“#”=“#”时表示整个表达式求值完毕。
数据结构(C语言版)
3.算法思路
使用两个栈S1和S2,其中,S1为运算符栈,用以寄存运算
符,而S2为操作数栈,用以寄存操作数或运算结果。其算法思
路如下,
1)首先设置两栈为空,将“#”作为表达式起始符压入运算
符栈S1作为栈底元素;
数据结构(C语言版)
2)依次读入表达式的每个字符,若是操作数则进入操作
数栈S2;若是运算符,则与S1的栈顶运算符比较优先级,若栈
顶运算符优先级低,则进入栈S1,若栈顶运算符优先级高,则
弹出S1的栈顶运算符,并从栈S2中弹出两个操作数,作相应运
算后,将结果压入操作数栈S2,然后再次与S1的栈顶运算符比
较优先级,直至栈顶运算符优先级低为止
3)当S1的栈顶运算符为“#”时,表达式求值结束,操作
数栈S2中的数即为表达式的值。
数据结构(C语言版)
表3.2求表达式3(7-2)时的栈变化
数据结构(C语言版)
4.
完整的C语言程序
file1
#define maxsize 30
typedef struct
{char data[maxsize+1];
int top;
} Stack;
int Push(S,x)
Stack S;
Char x;
{if (S.topmaxsize) {printf(“overflow\n”); return(0);}
S.data[++S.top]=x; return(1);
}
数据结构(C语言版)
int Pop(s,x)
Stack S;
Char x;
{if(S.top0){printf(“nudertflow\n”);return(0)};
x=S.data[S.top]; S.top–;
return(1);
}
file2
char readtop(S)
Stack S;
{char a;
数据结构(C语言版)
Pop(S, a);
Push(S, a);
Return( a);
}
double operate(ch, x, y)
char ch;
double x,y;
{double z;
switch (ch)
{case ‘+’ : z=x+y; break;
case ‘-’ : z=x-y; break;
case ‘’ : z=xy; break;
数据结构(C语言版)
case ‘/’ : z=x/y; break;}
return(z);}
int precede(p1,p2)
{ int flag;
switch (p1)
{case ‘+’ : if(p2==’’ || p2==‘/’ || p2== ‘(’ ) flag=-1;
else flag=1;
break;
case ‘-’ : if(p2=='’ || p2==‘/’ || p2== ‘(’ ) flag=-1;
else flag=1;
break;
case '’ : if(p2==‘(’ ) flag=-1;
else flag=1;
break;
数据结构(C语言版)
case ‘/’ : if(p2==‘(’ ) flag=-1;
else flag=1;
break;
case ‘(’ : if(p2==‘)’) flag=0;
else if(p2==‘#’)printf(“error operator!\n”);
else flag=-1;
break;
case ‘)’ : if(p2==‘(’ )printf(“error operator!\n”);
else flag=1;
break;
数据结构(C语言版)
case ‘# ’ : if(p2==’)’ )printf(“error operator!\n”);
else if(p2==‘#’ ) flag=0;
else flag=-1;
break;‘}
return(flag);}
double calcul( a);
char a[];
{Stack S1, S2;
double x, y, z;
char r, ch;
int I;
数据结构(C语言版)
push(S1,’#‘); r=a[I];
while(r<>’#’ || readtop(S1)<>‘#’)
{if(r<=‘9’ && r>=‘0’){ x=0;
while(r<=‘9’ && r>=‘0’)
{x=x10+r-‘0’;
r=a[++I];}
push(S2,x);}
else switch(precede(S1,r))
数据结构(C语言版)
{case -1: push(S1,r); r=a[++I]; break;
case 0: pop(S1,ch); r=a[++I]; break;
case 1: pop(S1, ch); pop(S2, x1); pop(S2, x2); push(S2,
operate(ch, x1.x2)); r=a[++I]; break;}}
return(read(S2));
}
数据结构(C语言版)习题
31.假定有编号为A、B、C、D的4辆列车,顺序开进一个栈
式结构的站台,请写出开出车站站台的列车顺序(注:每一列车
由站台开出时均可进栈,出栈开出站台,但不允许出栈后回退)。
写出每一种可能的序列。
2.已知堆栈采用链式存储结构,初始时为空,试画出a、b、
c、d 4个元素依次以后堆栈的状态,然后再画出此时的栈顶元素
出栈后的状态。
3.写出链栈的取栈顶元素和置栈空的算法。
数据结构(C语言版)
4.写出多个链表栈中取第j个链表栈顶元素值的算法。
5.写出计算表达式3+4/25,8—6时操作数栈和运算符栈
的变化情况。
6.课文中规定:无论是循环队列还是链表队列,队头指
针总是指向队头元素的前一位置,队尾指针指向队尾元素。试
画出有两个元素A、B的不同存储结构的图示,及将这两个元素
出对后循环队列和链表队列的状态示意图。
7.对于一个具有m个单元的循环队列,写出求队列中元素
个数的公式。
数据结构(C语言版)
8.对于一个具有n个单元(n≥2)的循环队列,若从进入第一
个元素开始,每隔T1个时间单位进入下一个元素,同时从进入
第一个元素开始,每隔t2(t2≥t1)个时间单位处理一个元素并令其
出队。试编写一个算法,求出在第几个元素进队时将发生溢出。
9.假设以带头结点的循环链表表示队列ÿ