学习内容

101
学学学学 1. 学学学学学 学学学学 () 2. 学学学学 学学学 () 3. 学学学学 4. 学学学 ( 学学学学学学学学5. 学学 学学学 学学学 、()

Upload: isabella-adrian

Post on 30-Dec-2015

36 views

Category:

Documents


6 download

DESCRIPTION

运算符知识(自己看) 补充记录(简单) 数组存储 线性表 ( 单向链表简单应用) 双向、循环(了解). 学习内容. 表达式是用运算符号或小括号将常量、变量、函数连接成的式子。 Pascal 表达式中只有小括号。运算符也称为算符,算符的操作对象称为操作数。 运算符按带操作数的个数分为两类: 单目运算符:对一个操作数操作。 - (负号), + (正号) 双目运算符:对两个操作数操作。 根据运算符运算的意义不同分为 算术运算、逻辑运算、关系运算 。 根据运算符的优先级可以将运算符分为 单目 运算、 “ 乘 ” 的关系运算、 “ 和 ” 的关系运算、关系运算。. 运算符. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 学习内容

学习内容

1. 运算符知识(自己看)2. 补充记录(简单)3. 数组存储4. 线性表 ( 单向链表简单应用)5. 双向、循环(了解)

Page 2: 学习内容

运算符 表达式是用运算符号或小括号将常量、变量、函数连接成的式

子。 Pascal 表达式中只有小括号。运算符也称为算符,算符的操作对象称为操作数。

运算符按带操作数的个数分为两类:

1. 单目运算符:对一个操作数操作。 - (负号), + (正号)

2. 双目运算符:对两个操作数操作。 根据运算符运算的意义不同分为算术运算、逻辑运算、关系运算。 根据运算符的优先级可以将运算符分为单目运算、“乘”的关系运

算、“和”的关系运算、关系运算。

Page 3: 学习内容

算术运算符

一共有 8 个。操作数都是数值型,结果也是数值型。单目运算符有(+)取正、

(-)取负。

双目运算符有(+)加、(-)减、(*)乘、( / )除、( DIV )取商、( Mo

d )取模。

“/” 左右的操作数是数值型,结果是实型数。

Div 左右的操作数是整型,结果是整型(两数之商)。

Mod 左右的操作数是整型数,结果是整型数(两数相除之余)。在 PASCAL 只有上面 8 种数学运算。其它的就只能利用这 8 种运算的组合通过语句

来实现。如 a^2 ( a 的平方)可以化成 a*a 。 XY 可写成 exp(y*ln(X))

Page 4: 学习内容

关系运算是指同一类型的两个数据进行比较,结果是一个布尔类型值。

用小括号、 > 、 < 、 >= 、 <= 、 = 、 <> 将两个算术表达式连接起来的式子就称为关系表达式(比较式)。如: 3+7>8,x+y<10,2*7<=13 等都是关系表达式。

关系表达式的值 true,false

2<3 87.5 > =100 ‘A’<‘a’ False<true

Page 5: 学习内容

布尔运算符 布尔运算是对布尔型数据进行运算,即操作数都是布尔型数据,

结果是布尔型。

布尔型运算符共有 4 个: not( 取反 ) and( 与 ) or( 或 ) xor( 异或 )

not :结果是与操作数相反的布尔值

and :两个操作数都为真,结果为真,否则为假

or :两个操作有一个为真,结果为真,否则为假

Xor: 两个操作数不一样为真 , 否则为假

1 xor 1=0 0 xor 0=0 1 xor 0=1 0 xor 1=1

Page 6: 学习内容

运算符的优先级

1 、内层小括号先计算

2 、函数先求值

3 、单目运算符(+ , - ,not )

4 、乘的关系双目运算符( *,/,div,mod,and )

5 、加的关系双目运算符(+ , - ,or )

6 、关系运算符( <,<=,>,>=,=,<> )

在同级运算中,按从左到右的顺序计算。

(a>=0) and (a<100)

Page 7: 学习内容

补充:记录类型

记录的定义:   type 类型标识符 =record     字段名 1 :类型 1 ;     字段名 2 :类型 2 ;       ...     字段名 n :类型 n ;     end;

type data=record      name:string[8];      sex:boolean;     end;    var     pt:data;     a:array[1..10] of data;

Page 8: 学习内容

输入 10 名学生的基本情况(学号、姓名、成绩)后,计算每个学生的平均分,然后根据平均分对学生的记录进行从大到小的排序,最后输出排序后的所有学生的信息。

1010001 stu184 66 5310002 stu290 67 59

……

输入 10 位同学的个人信息,存储在记录数组中;

计算平均分;根据平均分进行排序;输出记录。

Page 9: 学习内容

const n=10type studata=record num:1..10000; name:string; score:array[1..3] of real;

avg:real; end; var st: array[1..n] of studata; ch:char;procedure inputstud(var st:stud);var i,j:integer;begin readln(n); for i:=1 to n do begin

readln(st[i].num); readln(st[i].name);

for j:=1 to 3 do readln(stu[i].score[j]);{ 三门功课 } end;end;

{ 定义学生的记录类型 }

{学生记录数据的输入过程}

Page 10: 学习内容

{求学生的平均成绩} procedure

average(st:stud):real;   var i:integer;s:real; begin

for i:=1 to n dowith st[i] dobegin s:=0; for j:=1 to 3 do

s:=s+score[j] avg:=s/3;end;

end;

{根据平均分排序}procedure qsort(var

st:studata);var i,j:integer; tp:studata; begin for i:=1 to n-1 do for j:=1 to n-i do begin if st[j].avg<st[j+1].avg

then begin tp:=st[j]; st[j]:=st[j+1]; st[j+1]:=tp; end; end; end;

Page 11: 学习内容

{学生记录的输出} procedure

outputstud(st:studdata); var i,j:integer; begin

for i:=1 to n dowith st[i] dobegin write(num,’ ’,name); for j:=1 to 3 do write(‘ ‘,score[j]); write(‘ ‘,avg);

writeln;end;

end;

{ 主程序 }

begin inputstud(st); average(st); qsort(st); outputstud(st ) ;end.

Page 12: 学习内容

数据结构( Data Structure )

一般包括以下三方面内容:① 数据元素及其关系在计算机存储器内的表示,称为数据

的存储结构( Storage Structure );② 数据的逻辑结构( Logical Structure );③ 数据的运算,即对数据施加的操作。   数据的运算定义在数据的逻辑结构上,每种逻辑结构

都有一个运算的集合。最常用的检索、插入、删除、更新、排序等运算实际上只是在抽象的数据上所施加的一系列抽象的操作。 

Page 13: 学习内容

数据的逻辑结构分类( 1 )线性结构线性结构的逻辑特征是:若结构是非空集,则有且仅有一个开始结点和一个终端

结点,并且所有结点都最多只有一个直接前趋和一个直接后继。线性表是一个典型的线性结构。线性表是一种常用的数据结构 . 在实际应用中,

线性表都是以栈、队列、字符串、数组等特殊线性表的形式来使用的( 2 )非线性结构非线性结构的逻辑特征是:一个结点可能有多个直接前趋和直接后继。多维数组、

广义表、树和图等数据结构都是非线性结构。

Page 14: 学习内容

也叫物理学结构,是数据的逻辑结构在计算机中的存储方式。它不仅要实现数据元素本身的存储还要实现 数据之间逻辑关系的存储。

方法主要有顺序与链式。

存储结构

Page 15: 学习内容

顺序存储

顺序存储是线性表的一种最简单的存储结构,存储方式是:在内存中为线性表开辟一块连续的存储空间。

s+n

s+i

s+1

s

an

ai

a3

a2

a1

内存单元编号

假设假设 SS 为内存中为内存中 a1a1 的地址,每个的地址,每个元素占一个存储单元。元素占一个存储单元。

Page 16: 学习内容

二维数组与线性表二维数组的一个形象比喻——多个纵队形成的方块 m * n

a11 a12 a13 a14 … a1n

a21 a22 a23 a24 … a2n

a31 a32 a33 a34 … a3n

… … … … … …

am1 am2 am3 am4 … amn

amn

aij

a21

a1n

a12

a11

按行优先顺序存储按行优先顺序存储

Page 17: 学习内容

2×3 数组存储( a )逻辑状态( b )以行为主序( c )以列为主序

Page 18: 学习内容

二维数组的一个形象比喻——多个纵队形成的方块 m * n

a11 a12 a13 a14 …… a1n

a21 a22 a23 a24 …… a2n

a31 a32 a33 a34 …… a3n

…… …… …… …… …… ……

am1 am2 am3 am4 …… amn

二维数组在内存的存储方式是线性的。

1 :按照行存储:即先存储第一行然后在存

储第二行,那么 aij 的值应该是 A11+ ( i-1)*n+j-1

2: 1 :按照列存储:即先存储第一列然后在存

储第二列,那么 aij 的值应该是 A11+ ( j-1)*m+i-1( 很好记啊, I,j调换位置

* 的值 n->m )思考:如果数组的定义为 var num:array[2..n,2..m] ,要求AIJ 的位置,结果应该是是什么呢!

推广到一般的二维数组 A[C1-D1][C2-D2],A[i][j] 存储地址计算公式:以行为序:首地址 + (( i-C1 ) * ( D2-C2+1 ) + ( j-C2 )) *L

以列为序:首地址 + (( j-C2 ) * ( D1-C1+1 ) + ( i-C1 )) *L

L 表示所占内存

Page 19: 学习内容

广义表 又称列表,是线性表的推广。就是说,广义表中的元素不仅可以是数或一个结构,而且推广到可以是一个表。所以,广义表是 n个元素 a1 , a2 , a3...an 的有限序列,其中的 ai或者是原子或者是一个广义表。

Page 20: 学习内容

数据的链式存储

Page 21: 学习内容

可以这么认为单独的为指针,多个指针连在一起就成了链表

Page 22: 学习内容

静态存贮和动态存贮 1 、静态存储程序中的变量一经说明,计算机操作系统就会在内存空间中分配相

应的存贮单元,其中变量名是存贮单元的地址,而变量的值是存贮单元的内容,且该存贮单元自始至终都被该变量所占用,直到程序结束。如果变量是局部变量,那么在它的作用域内,一经说明也占有一定的存贮单元,直到退出其作用域为止。

这样的变量,在程序执行过程中,不能随时使用随时分配存贮空间,也不能在程序执行的过程中,释放这些空间。也就是说,一旦给这些变量分配存贮空间,无论程序是否还需要使用,它们都要占用一定的存贮空间,以便给用户存贮数据。我们称具有这样特点的存贮为静态存贮,它所对应的变量称为静态变量。如字符类型、数组类型、记录类型等。

优点是存贮方便,查找容易,可以通过一个简单的公式随机存取表中的任一元素,逻辑关系上相邻的两个元素在物理位置上也是相邻的,很容易找到前趋与后继元素;

缺点是在线性表的长度不确定时,必须分配足够大的存储空间,经常浪费了宝贵的存储资源;而线性表的容量一经定义确定后就难以扩充;在插入和删除线性表的元素时,需要移动大量的元素,时间效率也比较差。

Page 23: 学习内容

2 、动态存贮在程序执行过程中,通过向操作系统申请存贮空间或释放存贮空间的

命令,达到动态管理计算机的存贮空间,以保证存贮空间的充分利用。存贮空间可以随时申请、随时释放,这样的存贮方式称为动态存贮,其变量称为动态变量。指针变量即为动态变量。动态存储所需要的空间可以是不连续的,这样有利于充分利用零散的

小空间。但缺点是无法用 O ( 1 )的时间实现存取了。如何用这些零散的空间存储数组这些大规模数据呢?如何表示这些数据之间的逻辑关系呢?为了表示这些物理存储单元之间的逻辑关系,对于每个数据元素来说,除了要存储它本身的信息(数据域 data )外,还要存储它的直接后继元素的存储位置(指针域,一般用 link 或 next 表示)。我们往往把这两部分信息合在一起称为一个“结点 node” 。 N 个结点链接在一起就构成了一个链表。 N=0 时,称为空链表。同时,为了按照逻辑顺序对链表中的元素进行各种操作,我们需要定义一个变量用来存储整个链表的第一个结点的物理位置,这个变量称为“头指针,一般用 H 或 head 表示”。也可以把头指针定义成一个结点,称为“头结点”,头结点的数据域可以不存储任何信息,也可以存储线性表的长度等附加信息,头结点的指针域(头指针)存储指向第一个结点的指针,若线性表为空表,则头结点的指针域为空( NIL )。由于最后一个元素没有后继,所以线性表中最后一个结点的指针域为空( NIL )。由于此链表中的每个结点都只包含一个指针域,故称为“线性链表”或“单向链表”。

Page 24: 学习内容

指针变量是针对动态存储方式而引入的一种数据类型。 指针变量的值是内存中某一存储单元的地址 . 在 Pascal 中,为了表示指针变量和它所指向的地址单元之间的联系,使用了“ ^” 来表示指向关系。如下图为指针变量与简单变量比较如下: A:=0 ; P^:=0 ; 简单变量 A 即为存储地址,其存储值为 0 ;而指针变量 p 的值表示的是获取的存储地址,由它指向的地址单元存储的值为 0 即 p^:=0 。

通过地址找内容

Page 25: 学习内容

1.指针变量定义

( 1 )指针变量的定义有两种方法: 方法 1 :先定义指针类型,再定义指针变量。 type 指针类型标识符 = ^ 基类型标识符;

var 指针变量名 : 指针类型标识符;

这里的基类型可以是除文件类型以外的所有类型。

例: type point = ^integer;

var p1, p2 : point;

方法 2 :直接定义指针变量

var 指针变量名 : ^ 基类型标识符;

例: var p1, p2 : ^integer;

Page 26: 学习内容

new(H);H^:=12

3;new(H);H^:=23

4;

内存示意图如下(阴影部分表示该单元已被占用):

Page 27: 学习内容

3、指针变量的使用:(1) 申请存储单元: new( 指针变量 );

New(h);

系统将自动分配一个存放数据的存储单元,并将该单元的地址赋给指针变量 h 。存储单元的大小由h的基类型决定。

h 2001 2001 ?

(2)释放存储单元: dispose( 指针变量 )Dispose(h);

系统收回指针变量h 所指的内存单元,此时指针变量h 所指的内存单元的值不可用,即 h 变成无确切指向。系统收回指针变量所指的内存单元另作它用,此时指针变量的值变成无定义。注意,我们应该养成一个好的习惯,就是及时释放不用的动态存储单元,很多同学使用指针变量时就知道 new(p) ,而不知道及时 dispose(p) ,最后造成内存空间溢出错误、出现死循环甚至死机现象。

h 2001 2001 ?

Page 28: 学习内容

对指针变量的操作( 1 )赋值操作 在使用指针变量时, 要注意两个值: 一个是指针变量的值, 它表示某个内存单元的地址;另一个是指针变量所指向的内存单元中所包含的值。一般通过 new 过程来实现地址的获取,而指针变量所指内存单元的值就同其它变量一样,通过输入语句或赋值语句来实现。P^:=3

Page 29: 学习内容

program xx1;var p1,p2:^integer;begin new(p1); p2:=p1; p1^:=3; writeln(p1^); writeln(p2^);end.

调试

Page 30: 学习内容

3011p1

Var p1,p2:^integer;

begin

new(p1);p1^:=100;

new(p2);p2^:=200;

p1:=p2;

end.

3010

3011

4020

4021

4022

4023

4024

100100

4024p2

200200

将变量 p2 的值(存储单元的地址)赋给变量p1 ,变量 p1 和 p2 同时指向变量 p2 所指的存储单元。

dispose(p1);p1:=p2 ;

对指针变量的操作(1) 同一基类型的指针变量的操作 同一基类型的指针变量,它们之间可以互相赋值,

想一想:如果将 p1:=p2改为 p1^:=p2^, 结果会怎么样?

4024p1200200

Page 31: 学习内容

指针域的赋值 (存储单元赋值)指针变量的赋值(存储单元地址)

由于指针变量代表的是存储单元地址,它不能用输出语句进行打印,故在调试程序时要多加小心。

(2)指针变量 p1 置空 ( nil )

p1^:=p2^p1^:=p2^

p1:=p2p1:=p2

p1:=nilp1:=nil

当希望某个指针变量不指向任何存贮空间时,可以赋值为空即 NIL

指针主要应用在链式存储中

Page 32: 学习内容

3、常见的线性表的基本运算 InitList ( L ):构造一个空的线性表 L ListLength ( L ):求线性表 L中的结点个数 GetNode ( L , i ):取线性表 L中的第 i个结

点 LocateNode ( L , x ):在 L中查找值为 x

的结点,并返回该结点在 L中的位置 InsertList ( L , x , i ):在线性表 L的第 i

个位置上插入一个值为 x 的新结点,插入后,表 L的长度加 1

DeleteList ( L , i ):删除线性表 L的第 i个结点,删除后表 L的长度减 1

Page 33: 学习内容

( 1)结点 : 数据域 指针域

( 2)线性链表的几种形式: 单向链表、双向链表、循环链表其特点如下:

data next

不带符加头结点的链表

head

a1 a2 a3 a4

a1 a2 a3 a4

带符加头结点的链表

Page 34: 学习内容

type link=^node ; node=record data : dataty ; next : link end ; ster

Page 35: 学习内容

program ex;type point=^node; node=record data:integer; next:point; end;var head,p,q: point;begin new(head); head^.data:=1; head^.next:=nil; p:=head; write(p^.data);

end.

program ex;type point=^node; node=record data:integer; next:point; end;var head,p,q: point; i:integer;begin new(head); head^.data:=1; head^.next:=nil; p:=head; for i:=2 to 4 do begin new(q);q^.data:=i;q^.next:=nil; p^.next:=q;p:=q; end; p:=head; for i:=1 to 5 do begin write(p^.data); p:=p^.next; end;end.

体验头结点的建立,变量赋值体验链表的建立与输出

head1

P

^ 2

q

^

P

3

q

^

P

Page 36: 学习内容

(方法一):头插法建表算法思路:  从一个空表开始 , 重复读入数据 ,生成新结点 ,将读入数据存放在新结点的数据域中 , 然后将新结点插入到当前链表的表头上 ,直到读入结束标志为止。

Page 37: 学习内容

program xx;type point=^node; node=record data:integer; next:point; end;var p,head:point;i,x:integer;begin head:=nil; read(x); while x<>0 do begin if head=nil then begin new(p);p^.data:=x;p^.next:=nil;head:=p; end else begin new(p);p^.data:=x;p^.next:=head;head:=p; end; read(x); end; p:=head; while p<>nil do begin write(p^.data); p:=p^.next; end;end.

P

Page 38: 学习内容

方法 2 :尾插法建表 算法思路:从一个空表开始 ,重复读入数据 , 生成新结点 ,将读入数据存放在新结点的数据域中 , 然后将新结点插入到当前链表的表尾上 , 直到读入结束标志为止。

注意:   1、采用尾插法建表,生成的链表中结点的次序和输入顺序一致  2、必须增加一个尾指针 r, 使其始终指向当前链表的尾结点

Page 39: 学习内容

Program ex3_2 (input,output); type point=^node; node=record data:integer; next:point; end; var p,q,head:point; begin head:=nil; read(x); while x>0 do begin if head=nil then begin new(p); p^.data:=x;p^.next:=nil; q:=p; head:=p end else begin new(p); p^.data:=x;p^next:=nil; q^.next:=p; q:=p; end; read(x); end; end.

注意:在线性链表操作中,一定注意:在线性链表操作中,一定要注意保存链表第一个结点的地要注意保存链表第一个结点的地址,不能丢失,否则链表无法访址,不能丢失,否则链表无法访问。所以习惯用问。所以习惯用 headhead保存第一保存第一

个结点地址。个结点地址。

Page 40: 学习内容

三、线性链表的遍历(输出、查找以及求长度)链表的遍历输出过程就是从表头结点开始链接的顺序依次访问至表尾的过程。(1)设临时工作变量p 指针指向链表的头结点 ( 头结点的地址不能丢失和改变,否则会丢失整个链表 );(2)while p<>nil do

begin

输出、查找、求长度操作; p 移向后一个结点; end;

输出操作:输出操作:Write(p^.data)Write(p^.data)

查找操作:查找操作:If x=p^.data then If x=p^.data then 相相关操作关操作求链表长度:求链表长度:Inc(i)Inc(i)

Page 41: 学习内容

procedure print(head:point);

begin

p:=head;

while p<>nil do

begin

write(p^.data:5);

p:=p^.next;

end;

end;

function len (head:point):integer;

begin

p:=head;x:=0;

while p<>nil do

begin

inc(x);p:=p^.next;

end;

len:=x;

end;

Page 42: 学习内容

线性链表结点的插入

由于线性链表结点的物理地址不一定是连续的,那么对结点的插入实际上就是改变线性链表中某结点的后继指针的值。根据插入位置的不同,结点的插入可分三种不同的情况:

表头插入、表中插入、表尾插入

Page 43: 学习内容

线性链表结点的插入

表头插入

表中插入

表尾插入

new(s);readln(x);s^.data:=x; s^.next:=head; head:=s

headp

s

nil

x表头插入

这两步操作顺序能颠倒吗?为什

么?

想一想:想一想:

前提:将 s结点插在 p结点之后

Page 44: 学习内容

想一想:表中插入结点如何实现?

new(s);readln(x);s^.data:=x; s^.next:=head; head:=s

线性链表结点的插入

表头插入

表中插入

表尾插入

new(s);readln(x);s^.data:=x;s^.next:=p^.next; p^.next:=s

head p

s

nil

x表中插入

前提:将 s结点插在 p结点之后

Page 45: 学习内容

表尾插入结点又如何实现呢?

nil

new(s);readln(x);s^.data:=x;s^.next:=p^.next; p^.next:=s

new(s);readln(x);s^.data:=x; s^.next:=head; head:=s

线性链表结点的插入前提:将 s结点插在 p结点之后

表头插入

表中插入

表尾插入 new(s);readln(x);s^.data:=x;s^.next:=nil; p^.next:=s

head p

sx

表尾插入nil

Page 46: 学习内容

例 3-3 在单链表中第 i 个结点( i>=0 )之后插入一个结点(值为 X )

procedure insert(var head:point ;i , X:integer);

Var s , p:point; J:integer; Begin (1)New(s);s^.data:=X; (2)If i=0 then begin s^.next:=head; head:=s;

end (3)否则 begin p:=head; J:=1; While (p<>nil) and (j<i) do {找位置 } Begin J:=j+1; P:=p^.next; End; (4) If (p<>nil) then begin

s^.next:=p^.next;p^.next:=s;end Else writeln(‘not found i’) end End;

{ 插在第一个结点之前 }{ 用指针 P指向单链表中的第 j 个结点 }

{ 若条件成立则表明查找成功 }

s^.next:=head^.next;head^.next:=s;

Page 47: 学习内容

5、单链表的删除线性链表结点的删除一般分两步完成:

删除结点q^.next:=r^.next;

Dispose(r);

( 1)把要删除的结点地址赋给一个临时变量;把要删除的结点的指针域赋给要删除的结点的前一结点的指针域;( 2)将要删除的结点删除,释放存储单元。

删除结点时需要用到两个指针变量:

变量 q :表示要删除结点的前一结点

的地址,变量 r :表示要删除的结点

的地址。

Page 48: 学习内容

单链表的删除 前提:删除p结点后的 r结点 表头删除: 表中删除: 表尾删除:

p:=head;r:=p^.next;p^.next:=r^.next; dispose(r);

表头删除head

nil

rp

Page 49: 学习内容

单链表的删除 前提:删除p结点后的 r结点 表头删除: 表中删除: 表尾删除:

p:=head;r:=p^.next;p^.next:=r^.next; dispose(r);

p^.next:=r^.next; dispose(r);

表中删除head

nilrp

Page 50: 学习内容

单链表的删除 前提:删除p结点后的 r结点 表头删除: 表中删除: 表尾删除:

p:=head;r:=p^.next;p^.next:=r^.next; dispose(r);

p^.next:=r^.next; dispose(r);

p^.next:=nil; dispose(r);

表尾删除head

nilrp

或者: p^.next:=r^.next; dispose(r);

nil

Page 51: 学习内容

线性链表中操作可分为:线性链表的建立、输出、结点的查找、求链表长度、结点的插入和删除。这些操作最关键的就是在于如何改变结点指针域的值。

线性链表的访问基本方法是:从头结点开始沿线性链表方向进行探求,一般用于指向刚查过的结点地址,另一个用于指向下一个待查的结点地址。结束访问的条件有两个:一个是结点地址为 Nil,另一个是找到了相应的结点。

容易出错的是:当p 为 nil时,取 p^.data时出错,因为 p 是 nil , p^.data 的值无意义。

小结:小结:

Page 52: 学习内容

顺序表顺序表 链 表链 表

空间分配

静态分配。程序执行前须确定存储规模。估计过大造成空间浪费,估计太小使空间溢出机会增多。

动态分配 ,只要内存空间尚有空闲,就不会产生溢出。当线性表的长度变化较大,难以估计存储规模时,宜采用动态链表作存储结构为好。

时间存取

随机存取结构,查找方便,但插入和删除操作很费时。

顺序存取结构,链表中的结点,需从头指针起顺着链扫描才能取得。

插入删除操作

在顺序表中进行插入和删除,平均要移动表中近一半的结点,尤其是当每个结点的信息量较大时,移动结点的时间开销就相当可观。

在链表进行插入和删除,只需要修改指针。对于频繁进行插入和删除的线性表,宜采用链表做存储结构。若表的插入和删除主要发生在表的首尾两端,则采用尾指针表示的单循环链表为宜。

Page 53: 学习内容

线性表具有如下的结构特点:①均匀性:虽然不同数据表的数据元素可以是各种各样的,但对于同一线性表的各数据必定具有相同的数据类型和长度。②有序性:数据元素之间的相对位置是线性的,即存在唯一的“第一个”和“最后一个”的数据元素,除第一个和最后一个外,其他的元素前都只有一个数据元素 ( 直接前趋 )和后面只有一个数据元素 ( 直接后继 ) 。

考题 :

线性表若采用链表存贮结构,要求内存中可用存贮单元地址(  ) (NOIP6)A.必须连续   B. 部分地址必须连续   C. 一定不连续   D. 连续不连续均可

Page 54: 学习内容

双向链表

每个结点有两个指针域和若干数据域,其中一个指针域指向它的直接前趋结点,一个指向它的直接后继结点。它的优点是访问、插入、删除更方便,速度也快了。实质上是以空间换时间。

数据结构的定义:type pointer=^nodetype; nodetype=record data:datatype; pre,next:pointer {pre 指向前趋, next 指向后继 } end;var head,p,q,r:pointer;

Page 55: 学习内容

下面给出双向链表的插入和删除过程。

Page 56: 学习内容

循环链表

Page 57: 学习内容

1 、单向循环链表:最后一个结点的指针指向头结点。如下图:

2 、双向循环链表:最后一个结点的后继指针指向头结点,且头结点的前趋指针指向最后一个结点。

Page 58: 学习内容

循环单链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个环。和单链表相比,循环单链表的长处是从链尾到链头比较方便。当要处理的数据元素序列具有环型结构特点时,适合于采用循环单链表。循环链表的插入、删除运算基本同单向链表,只是查找时判别条件不同而已;但是这种循环链表实现各种运算时的危险之处在于:链表没有明显的尾端,可能使算法进入死循环,所以判断条件应该用curr.next<>head 替换单向链表的 curr.next<>null 完成遍历所有结点。

Page 59: 学习内容

特点:从表中任一结点出发均可找到表中其他所有结点。

rear^.next^.next

a1 an

rear

用尾指针表示的非空循环单链表

Page 60: 学习内容

Tg15.7 、在带尾指针(链表指针 clist指向尾结点)的非空循环单链表中每个结点都以 next字段的指针指向下一个节点。假定其中已经有 2 个以上的结点。下面哪些说法是正确的: A) 如果 p指向一个待插入的新结点,在头部插入一个元素的语句序列为:p^.next:= clist^.next; clist^.next:= p; B) 如果 p指向一个待插入的新结点,在尾部插入一个元素的语句序列为:p^.next:= clist; clist^.next:= p; C) 在头部删除一个结点的语句序列为:p:= clist^.next; clist^.next:= clist^.next^.next; dispose(p); D) 在尾部删除一个结点的语句序列为。p:= clist; clist:= clist ^.next; dispose(p);B 应为 p^.next:=clist^.next;clist^.next:=p;D 中要循环找到尾指针的上一个元素才能进行删除

clist

p1

Page 61: 学习内容

双链表的插入 注意关键的四个指针域的变化

x

① ②③ ④

x

①② ③ ④

Page 62: 学习内容

Tg16 9. 双向链表中有两个指针域 llink 和 rlink ,分别指向该结点的前驱及后继。设 p指向链表中的一个结点,他的左右结点均为非空。现要求删除结点 p ,则下列语句序列中正确的是 (    ) 。A . p>rlink->llink=p->rlink;     p->llink->rlink=p->llink; delete p;B . p->llink->rlink=p->rlink;     p->rlink->llink = p->llink; delete p;C . p->rlink->llink = p->llink;     p->rlink->llink ->rlink = p->rlink; delete p;D . p->llink->rlink = p->rlink;     p->llink->rlink->link = p->llink; delete p;

P

P^.llink P^.rlink

P^.rlink

P^.llink

Page 63: 学习内容

a

b

X

y

3

6

Page 64: 学习内容

a

b

X

y

3

6

Page 65: 学习内容

( 3 )索引存储结构:首先把一个线性表(主表)按照一定的函数关系或条件划分成若干个子表,为每个子表分别建立一个索引项,由所有这些索引项构成主表的一个索引表,于是采用顺序或链接的方式来分别存储索引表和每个子表

Page 66: 学习内容

( 4 )散列存储结构:以线性表中的每个元素的关键字 K 为自变量,通过一种函数 h(k) 计算出函数值,把这个值解释为一块连续存储空间(即数组空间)的单元地址(即下标),将该元素存储到这个单元中。散列存储中使用的函数 h(k) ,称为散列函数或哈希函数。

Page 67: 学习内容

哈希表(散列查找 )一 基本概念

散列查找,也称为哈希查找。它既是一种查找方法,又是一种存贮方法,称为散列存贮。散列存贮的内存存放形式也称为哈希表或散列表。

散列查找,与以前介绍的查找方法完全不同,前面介绍的所有查找都是基于待查关键字与表中元素进行比较而实现的查找方法,而散列查找是通过构造哈希函数来得到待查关键字的地址,按理论分析真正不需要用到比较的一种查找方法。

例如,要找关键字为 k 的元素,则只需求出函数值 H( k ), H ( k )为给定的哈希函数,代表关键字 k 在存贮区中的地址,而存贮区为一块连续的内存单元,可用一个一维数组 ( 或链表 ) 来表示。

Page 68: 学习内容

一般的线性表,树中,记录在结构中的相对位置是随机

的,即和记录的关键字之间不存在确定的关系,因此,

在结构中查找记录时需进行一系列和关键字的比较。这

一类查找方法建立在“比较“的基础上,查找的效率依

赖于查找过程中所进行的比较次数。

理想的情况是能直接找到需要的记录,因此必须在记录

的存储位置和它的关键字之间建立一个确定的对应关系 f ,

使每个关键字和结构中一个唯一的存储位置相对应。

Page 69: 学习内容

如果我们以学生姓名为关键字,如何建立查找表,使得根据姓名可以直接找到相应

记录呢?

哈希表最常见的例子是以学生学号为关键字的成绩表,1号学生的记录位置哈希表最常见的例子是以学生学号为关键字的成绩表,1号学生的记录位置

在第一条,10号学生的记录位置在第10条在第一条,10号学生的记录位置在第10条 ......

如果两个同学分别叫 刘丽 刘兰 该如何处理这两条记录?这个问题是哈希表不可避免的,即冲突现象

如果不连续会浪费很多

Page 70: 学习内容

二、 哈希函数的构造

哈希函数的构造目标是使哈希地址尽可能均匀地分布在散列空间上,同时使计算尽可能简单,冲突次数少。具体常用的构造方法有如下几种:

1 .直接定址法

可表示为 H ( k ) =a.k+b ,其中 a 、 b 均为常数。

这种方法计算特别简单,并且不会发生冲突,但当关键字分布不连续时,会出现很多空闲单元,将造成大量存贮单元的浪费。如学号 h(k)=k

2 .数字分析法

对关键字序列进行分析,取那些位上数字变化多的、频率大的作为哈希函数地址。

Page 71: 学习内容

例如,对如下的关键字序列:

9 9 3 4 6 5 3 2

9 9 3 7 2 2 4 2

9 9 3 8 7 4 3 3

9 9 3 0 1 3 6 7

9 9 3 2 2 8 1 7

9 9 3 3 8 9 6 7

9 9 3 5 4 1 5 7

9 9 3 6 8 5 3 7

9 9 3 6 8 5 3 2

......

通过对上述关键字序列分析,发现前 3 位相同,第 8位只可取 2 、 3 、 7 ,因此,这四位不可取。中间的四位的数字变化多些,可看成是随机的,若规定地址取 3 位,则哈希函数可取它的第 4 、 5 、6 位。于是有:

H ( 99346532 )= 465

H ( 99372242 )= 722

H ( 99387433 )= 874

H ( 99301367 )= 016

H ( 99322817 )= 228

Page 72: 学习内容

3 .平方取中法

取关键字平方后的中间几位为哈希函数地址。这是一种比较常用的哈希函数构造方法,但在选定哈希函数时不一定知道关键字的全部信息,取其中哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,因此,可以使用随机分布的关键字得到哈希函数地址。

如图中,随机给出一些关键字,并取平方后的第 2 到 4位为函数地址。

关键字 (关键字)2 函数地址 0100 0010000 010 1100 1210000 210 1200 1440000 440 1160 1370400 370 2061 4310541 310

利用平方取中法得到散列函数地址

Page 73: 学习内容

4 .折叠法

将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希函数地址,称为折叠法。

例如,假设关键字为某人身份证号码 430104681015355 ,则可以用 4 位为一组进行叠加,即有 5355 + 8101 + 1046 +430 = 14932 ,舍去高位,则有 H ( 430104681015355 )=4932 为该身份证关键字的哈希函数地址。

5 .除留余数法

该方法是用关键字序列中的关键字 k除以一个整数 p 所得余数作为哈希函数的地址,即

H ( k )= k % p 。 P<=m m 为哈希表长度

Page 74: 学习内容

除留余数法计算简单,适用范围广,是一种最常使用的方法。这种方法的关键是选取较理想的 p 值,使得每一个关键字通过该函数转换后映射到散列空间上任一地址的概率都相等,从而尽可能减少发生冲突的可能性。一般情形下, p 取为一个素数较理想,并且要求装填因子 α最好是在 0.6 0.9∽ 之间,所以 p 最好取 1.1n 1.7n∽ 之间的一个素数较好,其中 n 为哈希表中待装元素个数。

6. 随机法

选择一个随机函数,取关键值的随机函数值为它的哈希地址,即 H(key)=random(key)

random() 不能是一般的随机函数,固定的参数必须返回确定的值。

Page 75: 学习内容

三、 解决冲突的方法

由于哈希存贮中选取的哈希函数不是线性函数,将大的关键值的取值空间映射到小的地址空间中,故不可避免地会产生冲突,下面给出常见的解决冲突方法。

1 .开放定址法

开放定址法就是从发生冲突的那个单元开始,按照一定的次序,从哈希表中找出一个空闲的存储单元,把发生冲突的待插入关键字存储到该单元中,从而解决冲突的发生。在哈希表未满时,处理冲突需要的“下一个”空地址在哈希表中解决。

开放定址法利用下列公式求“下一个”空地址

Hi=(H(key)+di) MOD m i=1,2,…K(K<=m-1)

其中 H ( key) 为哈希函数, m 为哈希表长度, di 为增量序列

增量 di 有三种取法:(1)  线性探测再散列: di = ci 。最简单的情况 c=1(2)  平方探测再散列: di = 12, -12, 22, -22, …,(3) 随机探测再散列: di 是一组伪随机数列

Page 76: 学习内容

例: 给定关键字序列为 19 , 14 , 23 , 1 , 68 , 20 , 84 , 27 , 55 ,11 , 10 , 79 ,哈希函数 H ( k ) =k%13 ,哈希表空间地址为 0 12∽ ,试用线性探查法建立哈希存贮 (哈希表 ) 结构。

得到的哈希表如下图所示

14 1 68 27 79 19 20 84 55 23 11 10

0 1 2 3 4 9 10 7 8 5 6 11 12

用线性探查建立的哈希表

( 1 )线性探查法

Page 77: 学习内容

2. 链地址法

链地址法也称拉链法,是把相互发生冲突的同义词用一个单链表链接起来,若干组同义词可以组成若干个单链表

例:对给定的关键字序列 19,14,23,1,68,20,84,27,55,11,10,79 ,给定哈希函数为 H(k)=k%13 ,试用拉链法解决冲突建立哈希表。

Page 78: 学习内容

0

1

2

3

4

5

6

7

8

9

10

11

12

^

^

^

^

^

^

^

14 1 27 79 ̂

68 55 ̂

19 84 ̂

20 ̂

23 10 ̂

11 ̂

拉链法解决冲突的散列表

Page 79: 学习内容

(8tg ) 设有一个含有 13 个元素的 Hash 表 (O~12),Hash 函数是 :H(key)=key % 13, 其中 % 是求余数运算。用线性探查法解决冲突 , 则对于序列 (2 、 8 、 31 、 20 、 19 、 18 、 53 、 27),18 应放在第几号格中( ) 。A) 5 B) 9 C) 4 D) 0

3 、 使 1 -8 号 格 字 的 访 问 顺 序 为 :8 、 2 、 6 、 5 、 7 、 3 、 1 、 4 ,则下图中的空格中应填入( )。

A ) 6 B ) 0 C ) 5 D ) 3

1 2 3 4 5 6 7 8

4 6 1 -1 7 3 2

某空间中存放的是下一个空间的地址

nil8

2

2

6

6

-1

Page 80: 学习内容

2009.dx8 、散列表的地址区间为 0-10,散列函数为 H(K)=K mod 11 。采用开地址法的线性探查法处理冲突,并将关键字序列26 , 25 , 72 , 38 , 8 , 18 , 59 存储到散列表中,这些元素存入散列表的顺序并不确定。假定之前散列表为空,则元素 59 存放在散列表中的可能地址有:

A) 5 B) 7 C) 9 D) 10

26 , 25 , 72 , 38 , 8 , 18 , 59

0 1 2 3 4 5 6 7 8 9 10

A. 按 26,59 顺序存储 , 则 59放到 5

B. 按 38,72,59 顺序存储 , 则 59放到 7

C. 按 26,38,72,18,8,59 顺序存储 , 则 59放到 9

D.因为 25放到 3, 不会对其他数据产生影响 , 其余 5 个数据 ( 不算 59),

最多只能占去 4,5,6,7,8五个位置 , 所以 59 不可能放到 10

4 3 6 5 8 6 4

Page 81: 学习内容

例:表长为 11 的哈希表中已填有关键字为 17 , 60 , 29 的记录, 哈希函数为 H(key)=key MOD 11 。现有第 4 个记录,其关键字为 38 , 按三种处理冲突的方法,将它填入哈希表中。

0 1 2 3 4 5 6 7 8 9 1060 17 29

(1) H(38)=38 MOD 11=5 冲突 H1=(5+1) MOD 11=6 冲突 H2=(5+2) MOD 11=7 冲突 H3=(5+3) MOD 11=8 不冲突

38

(2) H(38)=38 MOD 11=5 冲突 H1=(5+1²) MOD 11=6 冲突 H2=(5 - 1²) MOD 11=4 不冲突

38

(3) H(38)=38 MOD 11=5 冲突 设伪随机数序列为 9 ,则: H1=(5+9) MOD 11=3 不冲突

38

Hi = (H(key) + di) MOD m

Page 82: 学习内容

例:已知一组关键字 ( 19, 14, 23, 1, 68, 20, 84, 27, 55, 11, 10, 79 ) 哈希函数为 H(key) = key MOD 13, 哈希表长为 m=16 , 设每个记录的查找概率相等,计算 ASL 。 (1) 用线性探测再散列处理冲突,即 Hi=(H(key)+di) MOD m

H(55)=55 mod 13= 3 冲突 H1=(3+1) mod 16 = 4 冲突 H2=(3+2) mod 16 = 5

H(79)=70 mod 13 = 1 冲突 H1=(1+1) mod 16 = 2 冲突 H2=(1+2) mod 16 = 3 冲突 H3=(1+3) mod 16 = 4 冲突 H4=(1+4) mod 16 = 5 冲突 H5=(1+5) mod 16 = 6 冲突 H6=(1+6) mod 16 = 7 冲突 H7=(1+7) mod 16 = 8 冲突 H8=(1+8) mod 16 = 9

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

ASL=(1*6+2+3*3+4+9)/12=2.5

14 1 68 27 55 19 20 84 79 23 11 10

解: (1) 用线性探测再散列处理冲突

H(14)=14 mod 13 = 1H(23)=23 mod 13 = 10H(1)=1 mod 13 = 1 冲突 H1=(1+1) mod16 = 2H(68)=68 mod 13 = 3H(20)=20 mod 13 = 7H(84)=84 mod 13 = 6 冲突 H1=(6+1) mod 16 = 7 冲突 H2=(6+2) mod 16 = 8

H(27)=27 mod 13 = 1 冲突 H1=(1+1) mod 16 = 2 冲突 H2=(1+2) mod 16 = 3 冲突 H3=(1+3) mod 16 = 4

H(11)=11 mod 13 = 11H(10)=10 mod 13 = 10 冲突 H1=(10+1) mod 16 = 11 冲突 H2=(10+2) mod 16 = 12

H(19)=19 mod 13 = 6

Page 83: 学习内容

该问题实际上是解决如何在链表中删除结点。解题的关键有两点: 1、沿链表顺序对结点的数据域逐个进行是否为素数的判断; 2、删除时要分清是删除链表中的结点还是链表尾的结点。

例 3-4、设单链表 h 中存有若干个正整数,删除所有值为素数的结点。

算法分析:算法分析:

1、根据数据输入的顺序建立链表;

2、从表头结点开始沿链表方向对结点的数据域逐个进行是否为素数的判断,删除是素数的结点:

if p 结点数据域为素数 then

if p 结点是表头结点 then 修改表头 else 删除 p 结点 ;

3、输出删除后的链表。

Page 84: 学习内容

program ex3_4; type point=^node; node=record data:integer; next:point end; var head1:point;function sushu(x:integer):boolean;var i:integer; found:boolean;begin found:=true; for i:=2 to x-1 do if x mod i=0 then found:=false; sushu:=foundend; procedure creat(var head:point);

var x:integer; p,q:point;begin head:=nil;q:=head;read(x); while x>0 do begin if head=nil then begin new(p); head:=p; p^.data:=x;q:=p; end else begin new(p);p^.data:=x; q^.next:=p;q:=p; end; read(x); end; q^.next:=nil end;

Page 85: 学习内容

procedure print(head:point); var p:point; begin p:=head; if p=nil then write('no solution') else while p<>nil do begin write(p^.data:6); p:=p^.next; end; writeln end;procedure delete(var head:point);var p,q:point;begin p:=head;q:=head; 

while p<>nil do begin if sushu(p^.data) then if p=head then begin head:=p^.next; q:=head end else q^.next:=p^.next else q:=p; p:=p^.next end; end; begin write('input a link:'); creat(head1); delete(head1); print(head1)end.

Page 86: 学习内容

线性表的典型实例

约瑟夫问题M 个人围成一圈,从第一个人开始报数,数到 N 的人出圈;再由下一个人开始报数,数到 N(N>1) 的人出圈;…… .. 。输出依次出圈的人的编号。 M 的值预先选定, N由键盘输入。

分析:要解决这道问题,首先需要构造一个环表,构造环的方法很简单,只要存储每个人的下一个即可,当然,最后一个人的下一个就是第一个人,这样就构成了一个环,构造环以后,就可进行删除操作,直到环剩下一个人为止。

Page 87: 学习内容

约瑟夫问题 (采用数组 )

const max=100;var a:array [1..max] of integer; i,j,m,n:integer;begin write('m & n:'); readln(m,n); for i:=1 to m-1 do a[i] := i + 1; { 构造环 --循环链表 } a[m] := 1; j := 1; repeat for i := 1 to n – 2 do j := a[j]; writeln(a[j]); a[j] := a[a[j]]; j := a[j]; until j=a[j]; writeln(j)end.

Page 88: 学习内容

约瑟夫问题 (采用链表 )

readln(m,n); { 构造环 --循环链表 } for i:=1 to m do begin new(q); q^.data:=i; if i=1 then head:=q else p^.next:=q; p:=q; if i=m then p^.next:=head; end; {做出圈操作—删除链表节点 } p:=head; repeat for i:=1 to n-2 do p:=p^.next ; {报数 } q:=p^.next; p^.next:=p^.next^.next; { 删除并且重新开始报数 } writeln(q^.data); {显示出圈人员信息 } dispose(q); p:=p^.next; { 下一次开始报数 } until p=p^.next ; writeln(p^.data); {显示最后出圈人 }

Page 89: 学习内容

例 3-5 线性链表的归并运算:将下列两个有序线性表进行归并。线性表( 1)是: {3, 5, 8, 11, 13}线性表( 2)是: {1, 4, 5, 9, 15}归并后的线性表为: {1, 3, 4, 5, 8, 9, 11, 13, 15}

( 1)线性表中的结点是按数据域由小到大进行排列的,根据两个线性表中结点数据域的大小进行归并运算;哪个表中的数据小就归并哪一个;

1、建立链表 2、归并

问题分析:

( 2)当两个线性表中有一个已归并完毕,则另一个线性表的剩余部分全部复制到所建立的新线性表中。

( 3)如果出现同值时,则选一个值。

Page 90: 学习内容

p1:=head1;p2:=head2;

while (p1<>nil) and (p2<>nil do

begin

if p1^.data<=p2^.data

then 将链表 (1)的当前结点加入到新链表中 ,p1 指针后移 ;

else 将链表 (1)的当前结点加入到新链表中 ,p2 指针后移 ;

end;

if p1<>nil then 将链表 (1) 剩余的结点连接到新表的后面 ;

if p1<>nil then 将链表 (1) 剩余的结点连接到新表的后面 ;

归并算法:

Page 91: 学习内容

procedure combine(var head3:point;head1,head2:point);

var p1,p2,q,r:point; begin new(head3);r:=head3; p1:=head1;p2:=head2; while (p1<>nil) and (p2<>nil) do if p1^.data<=p2^.data then begin new(q);r^.next:=q;q^.data:=p1^.data; r:=q; p1:=p1^.next; end else begin new(q);r^.next:=q;q^.data:=p2^.data;r:=q; p2:=p2^.next; end; if p1<>nil then r^.next:=p1; if p2<>nil then r^.next:=p2; end;

Page 92: 学习内容

[[问题描述问题描述 ]] 对任意给定的一个自然数对任意给定的一个自然数 nn (( n<=100n<=100 ),),将分母小于等于将分母小于等于 nn 的不可约的真分数按上升次序排序,的不可约的真分数按上升次序排序,并且在第一个分数前加并且在第一个分数前加 0/10/1 ,而在最后一个分数后加,而在最后一个分数后加1/1,1/1,这个序列称为这个序列称为 nn 级的法雷序列。级的法雷序列。当当 n=8n=8时序列为:时序列为: 0/1, 1/8, 1/7, 1/6,1/5, 0/1, 1/8, 1/7, 1/6,1/5, 1/4,2/7,1/3,3/8, 2/5,3/7,1/2,4/7,3/5,5/8,2/3,5/7,3/4, 1/4,2/7,1/3,3/8, 2/5,3/7,1/2,4/7,3/5,5/8,2/3,5/7,3/4, 4/5,5/6,6/7,7/8,1/14/5,5/6,6/7,7/8,1/1 。。分析 (1)首先需要两个线性表分别存放分子和分母,fz,fm:array[1..maxn] of integer;

[[ 例例 3-1] 3-1] 法雷序列法雷序列

编程求出编程求出 nn 级的法雷序列,每行输出级的法雷序列,每行输出 1010 个分数。个分数。

Page 93: 学习内容

(3)怎样才能产生中间的若干数呢? 分母的范围是 2---n,而分子呢?怎样设计?(4)得到的新分数怎样插入到表中?需要与当前表中数据比较大小,才能确定插入位置。for m:=2 to n do for z:=1 to m-1 do begin k:=1; while z*fm[k]>m*fz[k] do k:=k+1; if z*fm[k]<>m*fz[k] then begin 插入表中 end; end;

(2) 初始化,将两个线性表中分别存储最初的两个值fz[1]:=0;fm[1]:=1;fz[2]:=1;fm[2]:=1;total:=2; total 记录线性表中数据的个数即最后的下标

Page 94: 学习内容

(5)插入表中

(6)打印 for i:=1 to total do begin write(fz[i],’/’,fm[i],‘ ’); if i mod 10=0 then writeln; end;

for i:=total downto k do fz[i+1]:=fz[i];for i:=total downto k do fm[i+1]:=fm[i];fz[k]:=z;fm[k]:=m;total:=total+1;

Page 95: 学习内容

      (1)设和存放在 S 中 , 确定第一个数和最后 一个数的位置。假设圆盘上 5 为第一个数,12为最后一个数。则可将这 20个数放在 a 数组中。数组的下标取 0-19。   0≤i<17时, s=a[i]+a[i+1]+a[i+2]+a[i+3]   i=17时,   s=a[i]+a[i+1]+a[i+2]+a[0]   i=18时,   s=a[i]+a[i+1]+a[0]+a[1]   i=19时,   s=a[i]+a[0]+a[1]+a[2]+a[3]

[例 3-2]圆盘找数,如图所示:找出 4 个相邻的数,使其相加之和最大和最小的是哪 4 个数 ,给出它们的起始位置。 分析:本题实际上就是先求出相邻的四个

数的和,然后找出最大数和最小数。如何求相邻的四个数的和呢?

Page 96: 学习内容

i=17时, a[0]=a[(i+3) mod 20];i=18时, a[1]=a[(i+3) mod 20)…, 即 0≤i≤19时,都有 s=a[i]+a[(i+1)mod 20]+a[(i+2)mod 20) +a[(i+3)mod 20)成立。

var a:array[0..19] of integer; s,max,min:integer; smax,smin:integer; {smax,smin 记录最大和以及最小和的起始位置 } i:integer;

Page 97: 学习内容

begin for i:=0 to 19 do read(a[i]); max:=-maxint; min:=maxint; for i:=0 to 19 do begin s:=a[i]+a[(i+1) mod 20]+a[(i+2) mod 20] +a[(i+3) mod 20]; if s>max then begin max:=s;smax:=i;end; if s<min then begin min:=s; smin:=i; end; end; writeln(‘start from’,smax+1); writeln(‘start from ’,smin+1); end.

{ 设置最大值和最小值的初始值 }

Page 98: 学习内容

[[ 例例 3-3]3-3] 约瑟夫问题:约瑟夫问题:设有n个人依次围成一圈,从第1个人开始报数,设有n个人依次围成一圈,从第1个人开始报数,数到第m个人出列,然后从出列的下一个人开始报数到第m个人出列,然后从出列的下一个人开始报数,数到第m个人又出列,数,数到第m个人又出列,……,如此反复到所有的,如此反复到所有的人全部出列为止。设n个人的编号分别为人全部出列为止。设n个人的编号分别为 11 ,, 22 ,,……,, nn ,打印出出列的顺序。 ,打印出出列的顺序。 分析 : 本题这里利用循环链的思想解题。n人围成一圈,把一人看成一个结点,n人之间的关系采用链接方式,即每一结点有一个前继结点和一个后继结点,每一个结点有一个指针指向下一个结点,最后一个结点指针指向第一个结点。当m人出列时,将m结点的前继结点指针指向m结点的后继结点指针,即把m结点驱出循环链。

Page 99: 学习内容

1 、数组模拟建立循环链表。a[i]存放下一个编号位。设立’指针’ j 指向当前

结点,则移动结点过程为 j:=a[j];当数到 m 时, m结点出链 : a[j]:=a[a[j]]。

2 、设立指针,指向当前结点,设立计数器,计数数到多少人;3 、沿’链’移动指针,每移动一个结点,计数器值加

1,当计数器值为m时,则m结点出链。计数器值置为1;

4 、重复3,直到 n 个结点均出链为止。

Page 100: 学习内容

7

7

7

287

4 2

87

54

12

187654

3

287654321

77448822551166334

67

12

574

484

47

Page 101: 学习内容

program ex3_3; const n=14;m=4;{ 设有 10 个人 , 报到 4 的人出列 } var a:array[1..n] of integer;   i,j,k,p:integer; begin   for i:=1 to n-1 do a[i]:=i+1;{ 建立链表 }   a[n]:=1;j:=n;k:=1;p:=0;{ 第 n 人指向第 1 人 , 并置初始 }   repeat    j:=a[j];k:=k+1;{ 报数 , 计数器加 1}    if k=m then { 数到 m,m 人出队 , 计数器置 1}     begin      write(a[j]:4);p:=p+1;a[j]:=a[a[j]];k:=1;     end   until p=n;{ 直到 n 个人均出队为止 } end.