1.01k likes | 1.1k Views
运算符知识(自己看) 补充记录(简单) 数组存储 线性表 ( 单向链表简单应用) 双向、循环(了解). 学习内容. 表达式是用运算符号或小括号将常量、变量、函数连接成的式子。 Pascal 表达式中只有小括号。运算符也称为算符,算符的操作对象称为操作数。 运算符按带操作数的个数分为两类: 单目运算符:对一个操作数操作。 - (负号), + (正号) 双目运算符:对两个操作数操作。 根据运算符运算的意义不同分为 算术运算、逻辑运算、关系运算 。 根据运算符的优先级可以将运算符分为 单目 运算、 “ 乘 ” 的关系运算、 “ 和 ” 的关系运算、关系运算。. 运算符.
E N D
运算符知识(自己看) 补充记录(简单) 数组存储 线性表(单向链表简单应用) 双向、循环(了解) 学习内容
表达式是用运算符号或小括号将常量、变量、函数连接成的式子。Pascal表达式中只有小括号。运算符也称为算符,算符的操作对象称为操作数。表达式是用运算符号或小括号将常量、变量、函数连接成的式子。Pascal表达式中只有小括号。运算符也称为算符,算符的操作对象称为操作数。 运算符按带操作数的个数分为两类: 单目运算符:对一个操作数操作。-(负号),+(正号) 双目运算符:对两个操作数操作。 根据运算符运算的意义不同分为算术运算、逻辑运算、关系运算。 根据运算符的优先级可以将运算符分为单目运算、“乘”的关系运算、“和”的关系运算、关系运算。 运算符
算术运算符 一共有8个。操作数都是数值型,结果也是数值型。单目运算符有(+)取正、(-)取负。 双目运算符有(+)加、(-)减、(*)乘、(/)除、(DIV)取商、(Mod)取模。 “/”左右的操作数是数值型,结果是实型数。 Div 左右的操作数是整型,结果是整型(两数之商)。 Mod 左右的操作数是整型数,结果是整型数(两数相除之余)。 在PASCAL只有上面8种数学运算。其它的就只能利用这8种运算的组合通过语句来实现。如a^2(a的平方)可以化成a*a。XY可写成exp(y*ln(X))
关系运算是指同一类型的两个数据进行比较,结果是一个布尔类型值。关系运算是指同一类型的两个数据进行比较,结果是一个布尔类型值。 用小括号、>、<、>=、<=、=、<>将两个算术表达式连接起来的式子就称为关系表达式(比较式)。如:3+7>8,x+y<10,2*7<=13等都是关系表达式。 关系表达式的值 true,false 2<3 87.5>=100 ‘A’<‘a’ False<true
布尔运算是对布尔型数据进行运算,即操作数都是布尔型数据,结果是布尔型。布尔运算是对布尔型数据进行运算,即操作数都是布尔型数据,结果是布尔型。 布尔型运算符共有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
1、内层小括号先计算 2、函数先求值 3、单目运算符(+,-,not) 4、乘的关系双目运算符(*,/,div,mod,and) 5、加的关系双目运算符(+,-,or) 6、关系运算符(<,<=,>,>=,=,<>) 在同级运算中,按从左到右的顺序计算。 运算符的优先级 (a>=0) and (a<100)
补充:记录类型 记录的定义:type 类型标识符=record 字段名1:类型1; 字段名2:类型2;... 字段名n:类型n;end; type data=recordname:string[8];sex:boolean;end;varpt:data;a:array[1..10] of data;
输入10名学生的基本情况(学号、姓名、成绩)后,计算每个学生的平均分,然后根据平均分对学生的记录进行从大到小的排序,最后输出排序后的所有学生的信息。输入10名学生的基本情况(学号、姓名、成绩)后,计算每个学生的平均分,然后根据平均分对学生的记录进行从大到小的排序,最后输出排序后的所有学生的信息。 10 10001 stu1 84 66 53 10002 stu2 90 67 59 …… • 输入10位同学的个人信息,存储在记录数组中; • 计算平均分; • 根据平均分进行排序; • 输出记录。
const n=10 type 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; {定义学生的记录类型} {学生记录数据的输入过程}
{求学生的平均成绩} procedure average(st:stud):real; var i:integer;s:real; begin for i:=1 to n do with st[i] do begin 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;
{学生记录的输出} procedure outputstud(st:studdata); var i,j:integer; begin for i:=1 to n do with st[i] do begin 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.
数据结构(Data Structure) 一般包括以下三方面内容: ①数据元素及其关系在计算机存储器内的表示,称为数据的存储结构(Storage Structure); ② 数据的逻辑结构(Logical Structure); ③ 数据的运算,即对数据施加的操作。 数据的运算定义在数据的逻辑结构上,每种逻辑结构都有一个运算的集合。最常用的检索、插入、删除、更新、排序等运算实际上只是在抽象的数据上所施加的一系列抽象的操作。
数据的逻辑结构分类 (1)线性结构 线性结构的逻辑特征是:若结构是非空集,则有且仅有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前趋和一个直接后继。 线性表是一个典型的线性结构。线性表是一种常用的数据结构.在实际应用中,线性表都是以栈、队列、字符串、数组等特殊线性表的形式来使用的 (2)非线性结构 非线性结构的逻辑特征是:一个结点可能有多个直接前趋和直接后继。多维数组、广义表、树和图等数据结构都是非线性结构。
也叫物理学结构,是数据的逻辑结构在计算机中的存储方式。它不仅要实现数据元素本身的存储还要实现 数据之间逻辑关系的存储。 方法主要有顺序与链式。 存储结构
内存单元编号 s a1 s+1 a2 a3 … … s+i ai … … s+n an 线性表的顺序存储结构 顺序存储 顺序存储是线性表的一种最简单的存储结构,存储方式是:在内存中为线性表开辟一块连续的存储空间。 假设S为内存中a1的地址,每个元素占一个存储单元。
二维数组的一个形象比喻—— 多个纵队形成的方块 m * n a11 a12 … a1n a21 … aij … amn 二维数组与线性表 按行优先顺序存储 线性表的顺序存储结构
二维数组的一个形象比喻——多个纵队形成的方块 m * n 推广到一般的二维数组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表示所占内存 二维数组在内存的存 储方式是线性的。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的位置,结果应该是是什么呢!
广义表 又称列表,是线性表的推广。就是说,广义表中的元素不仅可以是数或一个结构,而且推广到可以是一个表。所以,广义表是n个元素a1,a2,a3...an的有限序列,其中的ai或者是原子或者是一个广义表。
可以这么认为单独的为指针,多个指针连在一起就成了链表可以这么认为单独的为指针,多个指针连在一起就成了链表
静态存贮和动态存贮 1、静态存储 程序中的变量一经说明,计算机操作系统就会在内存空间中分配相应的存贮单元,其中变量名是存贮单元的地址,而变量的值是存贮单元的内容,且该存贮单元自始至终都被该变量所占用,直到程序结束。如果变量是局部变量,那么在它的作用域内,一经说明也占有一定的存贮单元,直到退出其作用域为止。 这样的变量,在程序执行过程中,不能随时使用随时分配存贮空间,也不能在程序执行的过程中,释放这些空间。也就是说,一旦给这些变量分配存贮空间,无论程序是否还需要使用,它们都要占用一定的存贮空间,以便给用户存贮数据。我们称具有这样特点的存贮为静态存贮,它所对应的变量称为静态变量。如字符类型、数组类型、记录类型等。 优点是存贮方便,查找容易,可以通过一个简单的公式随机存取表中的任一元素,逻辑关系上相邻的两个元素在物理位置上也是相邻的,很容易找到前趋与后继元素; 缺点是在线性表的长度不确定时,必须分配足够大的存储空间,经常浪费了宝贵的存储资源;而线性表的容量一经定义确定后就难以扩充;在插入和删除线性表的元素时,需要移动大量的元素,时间效率也比较差。
2、动态存贮 在程序执行过程中,通过向操作系统申请存贮空间或释放存贮空间的命令,达到动态管理计算机的存贮空间,以保证存贮空间的充分利用。存贮空间可以随时申请、随时释放,这样的存贮方式称为动态存贮,其变量称为动态变量。指针变量即为动态变量。 动态存储所需要的空间可以是不连续的,这样有利于充分利用零散的小空间。但缺点是无法用O(1)的时间实现存取了。如何用这些零散的空间存储数组这些大规模数据呢?如何表示这些数据之间的逻辑关系呢?为了表示这些物理存储单元之间的逻辑关系,对于每个数据元素来说,除了要存储它本身的信息(数据域data)外,还要存储它的直接后继元素的存储位置(指针域,一般用link或next表示)。我们往往把这两部分信息合在一起称为一个“结点node”。 N个结点链接在一起就构成了一个链表。N=0时,称为空链表。同时,为了按照逻辑顺序对链表中的元素进行各种操作,我们需要定义一个变量用来存储整个链表的第一个结点的物理位置,这个变量称为“头指针,一般用H或head表示”。也可以把头指针定义成一个结点,称为“头结点”,头结点的数据域可以不存储任何信息,也可以存储线性表的长度等附加信息,头结点的指针域(头指针)存储指向第一个结点的指针,若线性表为空表,则头结点的指针域为空(NIL)。由于最后一个元素没有后继,所以线性表中最后一个结点的指针域为空(NIL)。由于此链表中的每个结点都只包含一个指针域,故称为“线性链表”或“单向链表”。
指针变量是针对动态存储方式而引入的一种数据类型。 指针变量的值是内存中某一存储单元的地址.在Pascal中,为了表示指针变量 和它所指向的地址单元之间的联系,使用了“^”来表示指向关系。如下图为指针变量与简单变量比较如下: A:=0 ; P^:=0 ; 简单变量A 即为存储地址,其存储值为0;而指针变量p 的值表示的是获取的存储地址,由它指向的地址单元存储的值为0即p^:=0。 通过地址找内容
1.指针变量定义 (1)指针变量的定义有两种方法: 方法 1:先定义指针类型,再定义指针变量。 type 指针类型标识符 = ^基类型标识符; var 指针变量名 : 指针类型标识符; 这里的基类型可以是除文件类型以外的所有类型。 例:type point = ^integer; var p1, p2 : point; 方法 2:直接定义指针变量 var 指针变量名 : ^基类型标识符; 例:var p1, p2 : ^integer;
内存示意图如下(阴影部分表示该单元已被占用):内存示意图如下(阴影部分表示该单元已被占用): new(H); H^:=123; new(H); H^:=234;
2001 h h ? ? 2001 2001 3、指针变量的使用: (1)申请存储单元:new(指针变量); New(h); 2001 系统将自动分配一个存放数据的存储单元,并将该单元的地址赋给指针变量h。存储单元的大小由h的基类型决定。 (2)释放存储单元:dispose(指针变量) Dispose(h); 系统收回指针变量h所指的内存单元,此时指针变量h所指的内存单元的值不可用,即h变成无确切指向。系统收回指针变量所指的内存单元另作它用,此时指针变量的值变成无定义。注意,我们应该养成一个好的习惯,就是及时释放不用的动态存储单元,很多同学使用指针变量时就知道new(p),而不知道及时dispose(p),最后造成内存空间溢出错误、出现死循环甚至死机现象。
对指针变量的操作 (1)赋值操作 在使用指针变量时, 要注意两个值: 一个是指针变量的值, 它表示某个内存单元的地址; 另一个是指针变量所指向的内存单元中所包含的值。 一般通过new过程来实现地址的获取,而指针变量所指内存单元的值就同其它变量一样,通过输入语句或赋值语句来实现。 P^:=3
调试 program xx1; var p1,p2:^integer; begin new(p1); p2:=p1; p1^:=3; writeln(p1^); writeln(p2^); end.
3010 3011 … 4020 4021 4022 4023 4024 p2 p1 p1 4024 4024 3011 … 对指针变量的操作 (1) 同一基类型的指针变量的操作 同一基类型的指针变量,它们之间可以互相赋值, Var p1,p2:^integer; 200 100 begin new(p1);p1^:=100; new(p2);p2^:=200; p1:=p2; end. dispose(p1);p1:=p2; 200 将变量p2的值(存储单元的地址)赋给变量p1,变量p1和p2同时指向变量p2所指的存储单元。 想一想:如果将p1:=p2改为p1^:=p2^,结果会怎么样?
p1^:=p2^ 指针域的赋值 (存储单元赋值) p1:=p2 指针变量的赋值(存储单元地址) (2)指针变量p1置空 (nil) 当希望某个指针变量不指向任何存贮空间时,可以赋值为空即 NIL p1:=nil 由于指针变量代表的是存储单元地址,它不能用输出语句进行打印,故在调试程序时要多加小心。 指针主要应用在链式存储中
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
a1 a2 a3 a4 head a1 a2 a3 a4 (1)结点 : 数据域 指针域 (2)线性链表的几种形式: 单向链表、双向链表、循环链表 其特点如下: next data 不带符加头结点的链表 带符加头结点的链表
type link=^node; node=record data:dataty; next:link end; ster
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. 体验链表的建立与输出 q q head ^ ^ 2 3 P P P 体验头结点的建立,变量赋值 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. 1 ^
(方法一):头插法建表 算法思路: 从一个空表开始,重复读入数据,生成新结点,将读入数据存放在新结点的数据域中,然后将新结点插入到当前链表的表头上,直到读入结束标志为止。
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
方法 2:尾插法建表 算法思路:从一个空表开始,重复读入数据,生成新结点,将读入数据存放在新结点的数据域中,然后将新结点插入到当前链表的表尾上,直到读入结束标志为止。 注意: 1、采用尾插法建表,生成的链表中结点的次序和输入顺序一致 2、必须增加一个尾指针r,使其始终指向当前链表的尾结点
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. 注意:在线性链表操作中,一定要注意保存链表第一个结点的地址,不能丢失,否则链表无法访问。所以习惯用head保存第一个结点地址。
三、线性链表的遍历(输出、查找以及求长度)三、线性链表的遍历(输出、查找以及求长度) 链表的遍历输出过程就是从表头结点开始链接的顺序依次访问至表尾的过程。 (1)设临时工作变量p指针指向链表的头结点(头结点的地址不能丢失和改变,否则会丢失整个链表); (2)while p<>nil do begin 输出、查找、求长度操作; p移向后一个结点; end; 输出操作: Write(p^.data) 查找操作: If x=p^.data then 相关操作 求链表长度: Inc(i)
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;
线性链表结点的插入 由于线性链表结点的物理地址不一定是连续的,那么对结点的插入实际上就是改变线性链表中某结点的后继指针的值。 根据插入位置的不同,结点的插入可分三种不同的情况: 表头插入、表中插入、表尾插入
这两步操作顺序能颠倒吗?为什么? s 想一想: 线性链表结点的插入 前提:将s结点插在p结点之后 • 表头插入 • 表中插入 • 表尾插入 new(s);readln(x);s^.data:=x; s^.next:=head; head:=s head p nil 表头插入 x
s 线性链表结点的插入 前提:将s结点插在p结点之后 • 表头插入 • 表中插入 • 表尾插入 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 nil 表中插入 x
s 线性链表结点的插入 前提:将s结点插在p结点之后 • 表头插入 • 表中插入 • 表尾插入 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 表尾插入结点又如何实现呢? new(s);readln(x);s^.data:=x;s^.next:=nil; p^.next:=s head p nil 表尾插入 x nil
例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; s^.next:=head^.next;head^.next:=s; {插在第一个结点之前} {用指针P指向单链表中的第j个结点} {若条件成立则表明查找成功}
5、单链表的删除 线性链表结点的删除一般分两步完成: (1)把要删除的结点地址赋给一个临时变量;把要删除的结点的指针域赋给要删除的结点的前一结点的指针域; (2)将要删除的结点删除,释放存储单元。 删除结点时需要用到两个指针变量: 变量q:表示要删除结点的前一结点的地址,变量r:表示要删除的结点的地址。 删除结点 q^.next:=r^.next; Dispose(r);
head nil 单链表的删除 • 前提:删除p结点后的r结点 • 表头删除: • 表中删除: • 表尾删除: p:=head;r:=p^.next;p^.next:=r^.next; dispose(r); p r 表头删除
head nil 单链表的删除 • 前提:删除p结点后的r结点 • 表头删除: • 表中删除: • 表尾删除: p:=head;r:=p^.next;p^.next:=r^.next; dispose(r); p^.next:=r^.next; dispose(r); p r 表中删除
head nil 单链表的删除 • 前提:删除p结点后的r结点 • 表头删除: • 表中删除: • 表尾删除: p:=head;r:=p^.next;p^.next:=r^.next; dispose(r); p^.next:=r^.next; dispose(r); p^.next:=nil; dispose(r); 或者:p^.next:=r^.next; dispose(r); p r nil 表尾删除