880 likes | 964 Views
线性表回顾. 线性表的定义. 线性表是由 n(n≥0) 个数据元素 a 1 , a 2 , … , a n 组成的有限序列。 n 定义为表的长度 (n=0 时称为空表 ) 。 将非空的线性表 (n > 0) 记作: (a 1 , a 2 , … , a n ) 数据元素 a i (1≤i≤n) 只是个抽象符号,其具体含义在不同情况下可以不同。. 线性表的存储结构. 顺序存储 —— 数组类型 连续的一片存储空间 链式存储 —— 指针变量 能将不连续空间连接起来. 线性表的操作运算. 建立线性表 求线性表的长度 查找一个元素 删除一个元素 插入一个元素
E N D
线性表的定义 线性表是由n(n≥0)个数据元素a1,a2,…,an组成的有限序列。 n定义为表的长度(n=0时称为空表)。 将非空的线性表(n>0)记作:(a1,a2,…,an) 数据元素ai(1≤i≤n)只是个抽象符号,其具体含义在不同情况下可以不同。
线性表的存储结构 顺序存储——数组类型 连续的一片存储空间 链式存储——指针变量 能将不连续空间连接起来
线性表的操作运算 建立线性表 求线性表的长度 查找一个元素 删除一个元素 插入一个元素 线性表中元素排序
指针变量 指针变量的定义方法 type 标识符=^基类型 type point = ^integer; type ch =^char; type point = ^nodenode = record ……end; var p1,p2: point;
指针变量 指针变量的使用方法 动态申请存储单元 new(指针变量); new(p1); nil 释放动态存储单元 dispose(指针变量); dispose(p1);
指针类型 type link=^stu; stu = record name: string[10]; number: integer; end; var t1, t2: link; begin new(t1); new(t2); …… dispose(t1); dispose(t2); end;
指针类型 一个基类型是记录类型的指针类型可以作为其记录类型的一个字段 type classes = ^students;students = record name: string[8]; num: 1..100;link: classes;end;
指针类型 Pascal规定所有数据类型都必须先定义后使用,但只有在定义指针类型时可以例外。如上面classes的基类型students出现在本身定义之前是允许的。 这里的指针类型是递归定义的。
指针变量的赋值和操作 赋值操作 由于指针变量存储的是new()操作所得到的内存地址,因此直接访问指针变量只能得到一个地址,而不能得到真正的数据。要得到指针所指向的数据,应使用如下方式(假设指针为p) p^
赋值操作 new(clas1); new(clas2); clas1^.name := 'liming'; clas1^.number := 90; clas1^.link := clas2; clas2^.name := 'wangping'; clas2^.number := 80; clas2^.link := nil; clas1 liming 90 clas2 wangping 80 nil
指针变量的操作 相同基类型的指针变量的赋值操作 p1 := p2 给指针变量赋nil值 p1 := nil 指针变量的关系运算 p1 = p2 p1 <> nil
p和p^的区别 p和p^ p1:=p2 p1^:=p2^ P1^ 35 28 p1 P1^ 35 p2 P2^
线性链表 指针变量通常与记录一起使用,而记录与记录之间往往存在着一种相互连接的顺序。 按一定的顺序,用指针将记录链接在一起而形成的一个链,我们称之为线性链表。 head 1142 1635 1789 2214 1142 Dat1 Dat2 Dat3 Dat4 1635 1789 2214 nil
线性链表 线性链表中我们称每一个基本的存储空间为“结点”,其形式如下: 链表中的一个元素称为表头,最后一个元素称为表尾。指向表头的指针称为头指针。 数据域 指针域
建立线性链表 线性链表的类型描述type link = ^node;node = record data: …; next: link;end;var head, p: link; 链表是一种动态存储管理结构,在使用过程中根据需要动态分配空间。
建立线性链表 建立线性链表的过程,实质是不断生成新结点,并将新结点链接到链表中的过程。 常用的建立方法有:插入表头、插入表尾和按特定要求插入链表中间。
插入表头建表 算法思路 从空表开始; 读入一个数据,申请一个新结点; 将数据放入新结点的数据域中; 将新结点的指针域指向头指针; 将头指针指向新结点; 重复操作直至结束。
插入表头建表 read(ch); head := nil; while ch <> '#' do begin new(p); p^.data := ch; p^.next := head; head := p; read(ch); end;
插入表尾建表 算法思路 从空表开始; 读入一个数据,申请一个新结点; 将数据放入新结点的数据域中; 将新结点的指针域指向nil; 将表尾结点指针域指向新结点; 重复操作直至结束。
插入表尾建表 read(ch); head := nil; tail := nil; while ch <> '#' do begin new(p); p^.data := ch; p^.next := nil; if head = nil then begin head := p; tail := p; end else begin tail^.next := p; tail := p; end; read(ch); end;
线性链表的遍历与输出 p := head; while p <> nil do begin 访问或输出p^.data; p := p^.next; end;
线性链表结点的插入 插入到表头 p^.next := head; head := p; 插入到表中 p^.next := q^.next; q^.next := p; q := q^.next; 插入到表尾 r^.next := p; r := p;
线性链表结点的删除 删除头结点 p := head; head := head^.next; dispose(p); 删除其他结点 q^.next := p^.next; dispose(p);
线性链表例题 有两个多项式Y1和Y2,请编写程序将两个多项式合并同类项。 Y1=3X7+5X6-4X5-3X2-8X-2 Y2=9X12-6X8-5X7-5X6+X4-8X3+6X2+7X+3 Y=9X12-6X8-2X7-4X5+X4-8X3+3X2-X+1
线性链表例题 问题分析 多项式合并同类项的方法是将指数相同的项系数相加,指数不变。 多项式的每一项的表示方法可以有两种: 用记录数组,A[i].cof和A[i].exp 用指针变量表示,指针变量的基类型是一个记录,其结构为:系数域、指数域和指向下一项的地址域。
线性链表例题 由于事先不知道多项式的指数范围,使用记录数组会比较麻烦,而使用线性链表动态存储会比较适合。 数据结构type link =^node;node = record cof, exp: longint; next: link;end;
线性链表例题 算法描述 建立两个多项式的线性链表L1和L2(按降幂排列) 对于两个线性链表的头结点,执行以下操作: 如果指数域相同,则将系数域相加后放入L1头结点。如相加后系数不为零,则将L1头结点从L1中取出并插入L尾部,将L2头结点删除;否则将L1和L2头结点都删除。 如果指数域不同,则将指数域大的结点从其链表中取出并插入L尾部。 当任意一个链表为空后,将另一个链表的剩余部分直接插入到L尾部。
循环链表 循环链表(Circular Linked List)是线性表的另一种形式的链式存储表示。它的特点是表中最后一个结点的指针域指向头结点,整个链表成为一个由链指针相链接的环,并且将头指针设成指向最后一个结点。空的循环链表由只含一个自成循环的头结点表示。
循环链表 循环链表的操作和单链表基本一致,差别仅在于,判别链表中最后一个结点的条件不再是“后继是否为空”,而是“后继是否为头结点”。
双向链表 双向链表中有两条方向不同的链,即每个结点中除next域存放后继结点地址外,还增加一个指向其直接前趋的指针域front。
双向链表 双向链表的类型描述 type link = ^node; node = record data: …; front, next: link; end; 双向链表的操作 双向链表的基本操作与单向链表类似,只要注意处理好front指针就可以了。
链表例题 Joseph问题:有n个人围坐在一个圆桌周围,把这n个人依次编号为1~n。从编号为1的人开始顺时针报数,数到m1的人出列;然后逆时针方向报数,数到m2的人出列。问:最后谁出列?
链表例题 问题分析 首先静态数组和链表都可以作为本题的存储结构,但考虑到题目中的“出列”操作,采用链表更加适合; 题中要求能双向报数,因此应采用双项链表; 同时报数过程中,当报到排头或排尾后,还需要从排尾或排头继续报数,因此应采用循环链表; 综上所述,采用双向循环链表,正数时沿next指针遍历,逆数时沿front指针遍历。“出列”的数据直接从链表中删除。
链表例题 双向循环链表的建立 head := nil; read(n); for i := 1 to n do begin new(p); p^.data := i; p^.next := nil; if head = nil then begin head := p; tail := head end else begin tail^.next := p; p^.front := tail end; tail := p; read(ch) end; tail^.next := head; head^.front := tail;
链表例题 顺时针报数 for i := 1 to m – 1 do p := p^.next; 出列 q := p^.front; q^.next := p^.next; p^.next^.front := q; dispose(p); 逆时针?
广义表的定义 广义表是线性表的推广。它是指一个表(空表或是由非空元素组成的表),其元素可以是某个确定类型的元素,也可以是子表组成。 (a1, a2, a3, B4, a5 , a6 , B7 …, an) 其中B4、B7分别为一个子表。 B4 = (b1, b2, b3, …, bi) B7 = (d1, d2, …, dj)
广义表的定义 A = ( ) B = (e) C = (a,(b,c,d)) D = (A,B,C)=((),(e),(a,(b,c,d))) E = ((a,(a,b),((a,b),c)))
广义表的定义 广义表通常用圆括号括起来,用逗号分隔其中的元素。 为了区分原子和子表,书写时用大写字母表示广义表的子表,用小写字母表示原子。 若广义表 L 非空(n≥1),则a1是L的表头,其余元素组成的表(a2,…,an)称为L的表尾。 广义表是递归定义的。
广义表的常用表示 E = ( ) E是一个空表,其长度为0。 L = (a, b) L是长度为2的广义表,它的两个元素都是原子,因此它是一个线性表。 A = (x, L) = (x, (a, b)) A是长度为2的广义表,第一个元素是原子x,第二个元素是子表L。
广义表的常用表示 B = (A, y) = ((x, (a, b)), y) B是长度为2的广义表,第一个元素是子表A,第二个元素是原子y。 C = (A,B) = ((x,(a,b)),((x,(a,b)),y)) C的长度为2,两个元素都是子表。 D = (a,D) = (a,(a,(a,(…)))) D的长度为2,第一个元素是原子,第二个元素是D自身,展开后它是一个无限的广义表。
广义表的结构 A=() B=(e) C=(a,(b,c,d)) D=(A,B,C) 树形结构
广义表的基本运算 计算广义表的长度 广义表的输入和输出 建立广义表
广义表的应用 建立广义表 输入广义表 输出广义表
广义表的存储结构 由于广义表中元素及子表中的元素多少不固定,所以一般用动态链接结构。 其结点可表示为 Tag(0,1) Data(Link) Next
广义表的存储结构 A=() B=(e) C=(a,(b,c,d)) nil A 0 e ^ B 0 a 1 ^ C 0 b 0 c 0 d ^
练习 画出下列广义表的链接存储结构 A = (a,b,c) B = (a,(b,(c))) C = ((a,b),(c,d)) D = (a,(b,(c,d)),(e)) E = ((a,(b,( ) ,c),((d) ,e)))
广义表类型的定义 广义表类型的定义和链表定义比较接近,但需要使用可变记录类型。 type node= ^stu; stu = record next: node; case tag: 0..1 of 0: da: …; 1: link: node; end; end;