第七章数据结构及其应用.docx

上传人:b****8 文档编号:29583137 上传时间:2023-07-24 格式:DOCX 页数:24 大小:26.60KB
下载 相关 举报
第七章数据结构及其应用.docx_第1页
第1页 / 共24页
第七章数据结构及其应用.docx_第2页
第2页 / 共24页
第七章数据结构及其应用.docx_第3页
第3页 / 共24页
第七章数据结构及其应用.docx_第4页
第4页 / 共24页
第七章数据结构及其应用.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

第七章数据结构及其应用.docx

《第七章数据结构及其应用.docx》由会员分享,可在线阅读,更多相关《第七章数据结构及其应用.docx(24页珍藏版)》请在冰豆网上搜索。

第七章数据结构及其应用.docx

第七章数据结构及其应用

第七章 数据结构及其应用

数字,字符,声音,图像,表格等信息,均可输入计算机中进行处理。

在计算机科学中,象这种能输入到计算机中并被计算机程序处理的信息,都可称为数据。

数据的基本单位是数据元素。

数据之间存在有线性与非线性两种基本的逻辑结构,同时在存储结构上还有顺序和链式之分。

数据结构则是研究数据元素的逻辑结构,存储结构和与之有关的各种基本操作的一门学科。

作为一个程序设计者,应当掌握好数据结构的有关知识,在解题时针对问题的特点,选择适当的数据结构,并构造算法,编出优美高效的好程序。

本章将介绍一些线性的数据结构及其基本操作。

第一节 线性表

“线性表”是指由有限多个类型相同的数据元素组成的集合,它有以下的特点:

(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、假设一个算术表达式中可包含三种括号:

圆括号“(”和“)”;方括号“[”和“]”以及花括号“{”和“}”,且这三种括号可按任意的次序嵌套使用,试利用栈的运算,判别给定的表达式中所含括号是否正确配对出现。

分析:

如果括号只有一种,比如说是“(”和“)”,则判断是否正确匹配可以

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1