第七章数据结构及其应用.docx
- 文档编号:29583137
- 上传时间:2023-07-24
- 格式:DOCX
- 页数:24
- 大小:26.60KB
第七章数据结构及其应用.docx
《第七章数据结构及其应用.docx》由会员分享,可在线阅读,更多相关《第七章数据结构及其应用.docx(24页珍藏版)》请在冰豆网上搜索。
第七章数据结构及其应用
第七章 数据结构及其应用
数字,字符,声音,图像,表格等信息,均可输入计算机中进行处理。
在计算机科学中,象这种能输入到计算机中并被计算机程序处理的信息,都可称为数据。
数据的基本单位是数据元素。
数据之间存在有线性与非线性两种基本的逻辑结构,同时在存储结构上还有顺序和链式之分。
数据结构则是研究数据元素的逻辑结构,存储结构和与之有关的各种基本操作的一门学科。
作为一个程序设计者,应当掌握好数据结构的有关知识,在解题时针对问题的特点,选择适当的数据结构,并构造算法,编出优美高效的好程序。
本章将介绍一些线性的数据结构及其基本操作。
第一节 线性表
“线性表”是指由有限多个类型相同的数据元素组成的集合,它有以下的特点:
(1)有唯一的头结点(即第一个数据元素)和尾结点(即最后一个数据元素);
(2)除结点外,集合中的每个数据元素均只有一个前驱;
(3)除尾结点外,集合中的每一个数据元素均只有一个后继。
“线性表”是一种运用非常广范的数据结构。
例一、某旅馆有100个房间,以1到100编号,第一个服务员来了,他将所有的房门都打开,第二个服务员再把所有编号是2的倍数的房门都关上,第三个服务员对编号是3的倍数的房门原来开的关上,原来关上的打开,此后的第四,五...服务员均照此办理。
问第100个服务员走进后,有哪几扇门是开着的。
解:
Pascal程序:
Programlt7_1_1;
usescrt;
vardoor:
array[1..100]ofboolean;1到100号房门状态,false--关,true--开
i,j:
integer;
begin
clrscr;
fillchar(door,sizeof(door),true);第一个服务员打开全部房门
fori:
=2to100doi表示服务员号码
forj:
=1to100divido
door[i*j]:
=notdoor[i*j];对房号为i的倍数的房门进行相反处理
write('Thecodeofopeningdooris:
');
fori:
=1to100do
ifdoor[i]thenwrite(i,'');
end.
分析:
(1)这里用door[1..100]来存储1到100号房门的开关状态,即是一种线性表,其中:
door[1]可以看成是头结点,而door[100]是尾结点;同时由于数组在内存中是按顺序占有一片连续的内存空间,因此这种存储结构即是顺序存储结构。
(2)这里用布尔变量true和false分别表示开和关两种状态,对某一房门进行相反处理时只要取反(not)即可,比如:
若door[10]为false,notdoor[10]则为true。
例二、插入排序:
在一个文本文件中存放的N个人的姓名,文本文件的格式为:
第一行为N,以下第二至第N+1行分别是N个人的姓名(姓名不重复,由英文字母组成,长度不超过10),请编一个程序,将这些姓名按字典顺序排列。
解:
Pascal程序:
Programlt7_1_2;
usescrt;
typepoint=^people;定义结点类型
people=record
name:
string[10];name--数据域,存放姓名
next:
point;next--指针域,存放后继结点的地址
end;
varhead:
point;
n:
integer;
procedureinit;初始化
begin
new(head);head^.next:
=nil;定义头结点,初始链表为空
end;
procedureinsert(p:
point);将P指向的结点插入以head开头的线性表中
varq1,q2:
point;
begin
ifhead^.next=nilthenhead^.next:
=p将P指向的结点插入空链表
else
begin
q1:
=head;q2:
=q1^.next;
while(q2<>nil)and(q2^.name
beginq1:
=q2;q2:
=q1^.next;end;查找结点p应插入的位置
q1^.next:
=p;p^.next:
=q2;将p插入q1之后q2之前
end;
end;
procedurework;
vari:
integer;
fn:
string;
f:
text;
p:
point;
begin
write('Filename:
');readln(fn);
assign(f,fn);reset(f);
readln(f,n);
fori:
=1tondo
begin
new(p);p^.next:
=nil;
readln(f,p^.name);
insert(p);
end;
end;
procedureprint;打印
varp:
point;
begin
p:
=head^.next;
whilep<>nildo
begin
writeln(p^.name);
p:
=p^.next;
end;
end;
begin
clrscr;
init;
work;
print;
end.
分析:
(1)排序有多种方法,插入排序是其中的一种,其原理同摸扑克牌类似:
摸到一张牌,把它按大小顺序插入牌中,每一张都如此处理,摸完后,得到的就是一副有序的扑克牌。
本题可以用插入排序求解;
(2)为了减少移动数据的工作,可以采用链式存储结构。
每个结点由两个域构成,数据域(用来存放姓名)和指针域(用来存放后继结点的地址)。
如图A是将1,3,6,7按顺序构成一个线性链表的示意图。
这样在这个有序表中插入一个5时,只需对指针进行相应地操作即可,如下图B:
┌─┬─┐┌─┬─┐┌─┬─┐┌─┬─┐
头结点→│1│--→│3│--→│6│--→│7│^│←尾结点
└─┴─┘└─┴─┘└─┴─┘└─┴─┘
图A
┌─┬─┐┌─┬─┐┌─┬─┐┌─┬─┐
头结点→│1│--→│3│││6│--→│7│^│←尾结点
└─┴─┘└─┴┼┘└↑┴─┘└─┴─┘
↓┌┘
┌─┬┼┐
│5││←插入的结点
└─┴─┘
图B
练习一
1、求1987乘幂的尾数:
M和N是自然数,N>M>=1,而1987^M与1987^N的末三位数相同,求最小的M和N。
分析:
(1)本题只须记录1987的乘幂的末三位数,故不必高精度计算;
(2)用数组a[1..n]存储1987的1至n次幂的末三位数;
(3)n的初始值为2,计算1987的n次幂的末三位数,并和1987的1至n-1次幂进行比较,若无相等的,则n=n+1,重复(3);否则,第一次找到相等的,即是所求的m,n值。
2、一个特殊的数列:
写出两个1,然后在它们中间插入2成为121,下一步是在任意两个相邻的和数为4的数之间插入3,成为13231;再下一步又在任意两个相邻的和数为4的数之间插入4,成为1432341,...,由键盘输入N(1<=N<=9),求出用上面方法构造出来的序列,其最后插入的数为N。
分析:
字符串也可以看做是一个特殊的线性表,本题初始串是11,对应N=1时的情况;然后在串中寻找相应的位置,依次插入2,3,...,K。
3、求序列的第300项:
把所有3的方幂及互不相等的3的方幂和排列成一个递增序列:
1,3,4,9,10,12,13,...,求这个序列的第300项。
分析:
本题可以用一个线性表来记录这个递增的序列,通过递推可以将整个序列构造出来。
方法如下:
(1)数组a存放线性表,t为尾指针,b存放3的幂,初始时t=1,b=1;
(2)将b放入表尾,尾指针加1;a[t]←b;t←t+1;
(3)将b依次与1至t-1的元素相加,按顺序放入表尾;
(4)重复
(2),(3),直至第300项放入表中。
4、约瑟夫环(Joseph)
编号为1,2,...,N的N个人按顺时针方向围成一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值M,从第一个人开始按顺时针方向自1开始报数,报到M时停止,报M的人出列,将他的密码作为新的M值,从他在顺时针方向上的第一个人开始重新从1报数,如此下去,直至所有有人全部出列为止。
试设计一个程序求出列的顺序。
分析:
这是一个数学游戏。
N个人围成一圈,依次报数,可以用一个循环链表模拟这一过程。
将链表的表尾指向表头,即形成循环链表。
从某个人开始报数,报到M的人出列,也就是在在循环链表中删除相应的结点,然后依次删除完所有的结点,此时链表为空,头指针与尾指针相等。
在具体操作中,要注意删除头结点和尾结点时指针的处理,谨防出错。
5、多项式的加法:
试编程完成两个一元多项式的加法。
分析:
大家都知道,两个一元多项式相加实际上就是合并同类项,最后结果一般要求按字母的升幂或降幂排列,比如:
(2X^10+4X+1)+(3X^5-4X+2)=2X^10+3X^5+3。
那么一元多项式在计算机中如何表示呢?
为了节省空间,一个元多项式一般用一个线性表表示,线性表的每一个结点包括两个域:
一个用来存放系数,一个用来存放指数,比如,上面相加的两个一元多项式可以分别表示为:
┌─┬─┬─┐┌─┬─┬─┐┌─┬─┬─┐
│2│10│─→│4│1│─→│1│0│^│
└─┴─┴─┘└─┴─┴─┘└─┴─┴─┘
┌─┬─┬─┐┌─┬─┬─┐┌─┬─┬─┐
│3│5│─→│-4│1│─→│2│0│^│
└─┴─┴─┘└─┴─┴─┘└─┴─┴─┘
这样两个一元多项式相加就可以看成是归并两个链表。
第二节 队列
在日常生活中有许多“队列“的例子,如车站售票口买票的队伍,排在前面的人先买到票离开队伍,后来的人则加入队伍的末尾等候买票;其特点是“先进先出”(FirstInFirstOut)或“后进后出”(LastInLastOut)。
“队列”是在一端插入,另一端删除的特殊的线性表。
进行删除的一端称为“队首”,进行插入的一端称为“队尾”(如下图);插入也叫入队,删除则叫出队;在对队列进行操作时,一定要注意一头一尾。
─┬──┬──┬──┬──┬─
出队←│a1│a2│...│an│←入队
─┴──┴──┴──┴──┴─
↑↑
队头队尾
例1、有N张牌,记为1,2,...,N,应当怎样排放,才能使:
打开第一张是1,然后报两张依次放在末尾;打开上面一张,刚好是2,再依次打开上面一张,刚好是3;如此继续下去,直至打开最后一张是N。
写一个程序解决这个问题。
解:
Pascal程序:
Programlt7_2_1;
usescrt;
vara,b:
array[1..1000]ofinteger;
i,j,t,h,n:
integer;
begin
clrscr;
write('N=');readln(n);
fori:
=1tondoa[i]:
=i;
a[1]:
=1;h:
=2;t:
=n;b[1]:
=1;
fori:
=2tondo
begin
forj:
=1toido
begin
inc(t);a[t]:
=a[h];inc(h);
end;
b[a[h]]:
=i;inc(h);
end;
fori:
=1tondo
write(b[i]:
5);
end.
分析:
这是一个典型队列的例子,请大家仔细体会在队列操作过程中头指针和尾指针的变化。
例2、集合的前N个元素:
编一个程序,按递增次序生成集合M的最小的N个数,M的定义如下:
(1)数1属于M;
(2)如果X属于M,则Y=2*X+1和Z=3*1也属于M;
(3)此外再没有别的数属于M。
解:
Pascal程序:
Programlt7_2_1;
usescrt;
vara,b:
array[1..1000]ofinteger;
x,ha,hb,t,total,n:
integer;
begin
clrscr;
write('N=');readln(n);
x:
=1;a[1]:
=1;
ha:
=1;hb:
=1;t:
=0;total:
=1;
whiletotal<=ndo
begin
write(x:
5);
inc(t);
a[t]:
=2*x+1;b[t]:
=3*x+1;
ifa[ha]>b[hb]thenbegin
x:
=b[hb];inc(hb);
end
elsebegin
x:
=a[ha];
ifa[ha]=b[hb]theninc(hb);
inc(ha);
end;
inc(total);
end;
end.
分析:
可以用两个队列来存放Y和Z中的数,分别用数组a和数组b存放。
然后递推求出第N项,方法如下:
(1)令ha和hb分别为队列a和队列b的头指针,它们的尾指针为t。
初始时,X=1,ha=hb=t=1;
(2)将2*x+1和3*x+1分别放入队列a和队列b的队尾,尾指针加1。
即:
a[t]←2*x+1,b[t]←3*x+1,t←t+1;
(3)将队列a和队列b的头结点进行比较,可能有三种情况:
(A)a[ha]>b[hb]
(B)a[ha]=b[hb]
(C)a[ha]
将比较的小者取出送入X,取出数的队列的头指针相应加1。
(4)重复
(2),(3)直至取出第N项为止。
注意:
数组的上标定到1000,当N较大时会出现队满溢出的情况,可以将上标定大些,或改用循环链表进行改进。
练习二
1、高精度加法:
设计一个程序实现两个正整数(长度不超过200位)的求和运算。
解:
Pascal程序:
Programlx7_2_1;
usescrt;
vara,b:
string;
c,i,x,y,lm,ha,hb:
integer;
m:
array[1..300]ofinteger;
begin
clrscr;
write('A=');readln(a);
write('B=');readln(b);
ha:
=length(a);hb:
=length(b);
c:
=0;lm:
=1;
while(ha>0)or(hb>0)do
begin
ifha>0thenx:
=ord(a[ha])-48
elsex:
=0;
ifhb>0theny:
=ord(b[hb])-48
elsey:
=0;
m[lm]:
=x+y+c;
c:
=m[lm]div10;
m[lm]:
=m[lm]mod10;
inc(lm);dec(ha);dec(hb);
end;
ifc>0thenm[lm]:
=c
elsedec(lm);
write(a,'+',b,'=');
fori:
=lmdownto1do
write(m[i]);
end.
2、基数排序:
将278,109,063,930,589,184,505,269,008,083利用基数排序法按从小到大的顺序排列。
分析:
基数排序法是一种基于对关键字进行分配和收集的排序法,常用最低位优先法(LeastSignificantDigitfirst),简称LSD法。
它先从最低位的关键字开始进行排序,然后对次低位关键字进行排序,依此类推,直到对最高位进行排序为止。
例如本题,关键字是各位上的数码,从0到9共10个。
首先,将需要排序的数放在队列A中,如图:
3、有1至2N的自然数,按从小到大的顺序排成一列,对这2N个数进行如下操作:
(1)将这2N个数等分成A,B两组,即:
A组:
1,2,...,N;B组:
N+1,N+2,...,2N
(2)轮流从A,B两组中按顺序取数:
1,N+1,2,N+1,...,N,2N
(3)再将取过的数等分为两组,并重复上述操作,直到这2N个数又按从小到大的顺序排好为止。
例如:
当N=3时,操作如下:
初始序列为:
1,2,3,4,5,6
(1)1,4,2,5,3,6
(2)1,5,4,3,2,6
(3)1,3,5,2,4,6
(4)1,2,3,4,5,6
分析:
将1至2N的自然数分成两组,用两个队列来模拟上述过程即可。
4、有一个数,它的末位数字是N,将N移到这个数的首位,得到的新数恰好是原数的N倍,现输入N的值,求满足条件的最小的数。
第三节 栈
“栈”是一种先进后出(FirstInLastOut)或后进先出(LastInFirstOut)的数据结构。
日常生活中也常能见到它的实例,如压入弹夹的子弹,最先压进去的子弹最后射出,而最后压入的子弹则最先发射出来。
“栈”是一种只能在一端进行插入和删除的特殊的线性表,进行插入和删除的一端称为“栈顶”,而不动的一端称为栈底(如下图)。
插入的操作也称为进栈(PUSH),删除的操作也称为出栈(POP)。
出栈←─┐┌──进栈
││↓│
├───┤
栈顶→│an│
├───┤
│...│
├───┤
│a2│
├───┤
栈底→│a1│
└───┘
例1、算术表达式的处理:
由键盘输入一个算术表达式(含有+,-,*,/,(,)运算),且运算结果为整数),编一程序求该算术表达式的值。
分析:
表达式的运算是程序设计中一个基本的问题,利用栈求解是一种简单易行的方法,下面介绍的是算符优先法。
比如:
4+2*3-10/5
我们都知道,四则运算的法则是:
(1)先乘除后加减,同级运算从左到右;
(2)有括号先算括号。
因此上例的结果是8。
算符优先法需要两个栈:
一个是操作数栈,用来存放操作数,记为SN;另一个是操作符栈用来存放运算符,记为SP。
具体处理方法如下:
(1)将SN,SP置为空栈;
(2)从左开始扫描表达式,若是操作数压入SN中;若是操作符则与SP的栈顶操作符比较优先级有两种可能:
(a)优先级小于栈顶算符,此时从SN中弹出两个操作数,从SP弹出一个操作符,实施运算,结果压入SN的栈顶。
(b)优先级大于栈顶算符,此时将操作符压入SP中。
(3)重复操作
(2)直至表达式扫描完毕,这时SP应为空栈,而SN只有一个操作数,即为最后的结果。
为了方便起见,可以将#作为表达式的结束标志,初始化时在SP的栈底压入#,并将其优先级规定为最低。
下面给出的是计算4+2*3-10/5#的示意图
步骤SNSP读入字符│说明
─────────────────┼──────────
1空#4│将4压入SN
24#+│将+压入SP
34#+2│将2压入SN
442#+*│将*压入SP
542#+*3│将3压入SN
6423#+*-│-的优先级小于*,因此将SN中的3,2弹出,
│将SP中的*弹出,将2*3的结果压入SN中
746#+-│-的优先级小于其左边的+,因此将SN中的
│4,6弹出,将SP中的+弹出,将4+6的结果压
│入SN中
810#-│-压入SP中
910#-10│10压入SN中
101010#-/│将/压入SP中
111010#-/5│将5压入SN中
1210105#-/#│#优先级小于/,故SN中的10,5弹出,SP中
│的/弹出,将10/5的结果压入SN中
13102#-#│#优先级小于-,故SN中的10,2弹出,SP中
│的-弹出,将10-2的结果压入SN中
148##│#与#相遇,运算结束,SN中的8是最后计算
│的结果
解:
Pascal程序:
Programlt7_3_1;
usescrt;
constnumber:
setofchar=['0’..'9'];
op:
setofchar=['+','-','*','/','(',')'];
varexpr:
string;
sp:
array[1..100]ofchar;
sn:
array[1..100]ofinteger;
t,tp,n,tn:
integer;
functioncan_cal(ch:
char):
boolean;
begin
if(ch='#')or(ch=')')or((sp[tp]in['*','/'])and(chin['+','-']))
thencan_cal:
=trueelsecan_cal:
=false;
end;
procedurecal;
begin
casesp[tp]of
'+':
sn[tn-1]:
=sn[tn-1]+sn[tn];
'-':
sn[tn-1]:
=sn[tn-1]-sn[tn];
'*':
sn[tn-1]:
=sn[tn-1]*sn[tn];
'/':
sn[tn-1]:
=sn[tn-1]divsn[tn];
end;
dec(tn);dec(tp);
end;
begin
clrscr;
write('Expression:
');readln(expr);
write(expr+'=');
expr:
=expr+'#';
tn:
=0;tp:
=1;sp[1]:
='#';t:
=1;
repeat
ifexpr[t]innumberthen
begin
n:
=0;
repeat
n:
=n*10+ord(expr[t])-48;
inc(t);
untilnot(expr[t]innumber);
inc(tn);sn[tn]:
=n;
end
elsebegin
if(expr[t]='(')ornotcan_cal(expr[t])then
begin
inc(tp);sp[tp]:
=expr[t];inc(t);
end
elseifexpr[t]=')'then
begin
whilesp[tp]<>'('docal;
dec(tp);inc(t);
end
elsecal;
end;
until(expr[t]='#')and(sp[tp]='#');
writeln(sn[1]);
end.
练习三
1、假设一个算术表达式中可包含三种括号:
圆括号“(”和“)”;方括号“[”和“]”以及花括号“{”和“}”,且这三种括号可按任意的次序嵌套使用,试利用栈的运算,判别给定的表达式中所含括号是否正确配对出现。
分析:
如果括号只有一种,比如说是“(”和“)”,则判断是否正确匹配可以
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第七 数据结构 及其 应用
![提示](https://static.bdocx.com/images/bang_tan.gif)