650 likes | 795 Views
第三章 线性表. 3.1 线性表的类型定义 3.2 线性表的顺序表示和实现 3.3 线性表的链式表示和实现. 线性表是最常用且最简单的一种数据结构。线性表的逻辑结构是 n 个数据元素的有限序列: ( a1, a2 ,a3, … an) n 为线性表的长度( n≥0), 线性表中元素的个数 n 定义为线性表的长度,为 0 时称为空表。 n=0 的表称为空表 ; 同一个线性表中必须是相同的数据类型 ; 数据元素可以是一个数、一个符号、也可以是一幅图、一页书或更复杂的信息 在数据元素的非空有限集中
E N D
第三章 线性表 3.1 线性表的类型定义3.2 线性表的顺序表示和实现 3.3 线性表的链式表示和实现
线性表是最常用且最简单的一种数据结构。线性表的逻辑结构是n个数据元素的有限序列: (a1, a2 ,a3,…an) n为线性表的长度(n≥0),线性表中元素的个数n定义为线性表的长度,为0时称为空表。n=0的表称为空表; 同一个线性表中必须是相同的数据类型; 数据元素可以是一个数、一个符号、也可以是一幅图、一页书或更复杂的信息 在数据元素的非空有限集中 (1)存在唯一的一个被称做“第一个”的数据元素; (2)存在唯一的一个被称做“最后一个”的数据元素; (3)除第一个之外,集合中的每个数据元素均只有一个前驱; (4)除最后一个之外,集合中每个数据元素均只有一个后继。 3.1 线性表的类型定义
线性表例: 1、 ai是ai+1的直接前驱元素,ai+1是ai的直接后继元素 在非空表中的每个数据元素都有一个确定的位置。ai是第i个元素,把i称为数据元素ai在线性中的位序。 2、 3、
线性表按其存储结构可分为顺序表和链表。 • 用顺序存储结构存储的线性表称为顺序表; • 用链式存储结构存储的线性表称为链表 • 线性表的基本运算主要有: • (1)在两个确定的元素之间插入一个新的元素; • (2)删除线性表中某个元素; • (3)按某种要求查找线性表中的一个元素,需要时,还可找到元素进行值的更新
3.2 线性表的顺序表示和实现 一、线性表的顺序表示 用一组地址连续的存储单元依次存储线性表的数据元素。C语言中的数组即采用顺序存储方式。假设线性表的每个元素需占用l个存储单元,并以所占的第一个单元的存储地址作为数据元素的存储位置。则存在如下关系: LOC(ai+1)=LOC(ai)+l LOC(ai)=LOC(a1)+(i-1)*l 式中LOC(a1)是线性表的第一个数据元素的存储位置,通常称做线性表的起始位置或基地址。常用b表示。 线性表的这种机内表示称做线性表的顺序存储结构或顺序映象。 称顺序存储结构的线性表为顺序表。顺序表的特点是以元素在计算机内物理位置相邻来表示线性表中数据元素之间的逻辑关系。
二、顺序存储结构的线性表操作及C语言运算 插入,在线性表(a1, a2…,ai,ai+1…,an)的第i个位置插入元素x 删除:在表长为n的线性表(a1,a2,…ai-1,ai,ai+1…an)中删除第i个数据元素,通常还需将第i+1个至第n个元素向前推动一个位置,即(a1, a2 ,…,ai-1,ai+1,…,an • 顺序表的不足: • 在顺序表中插入或删除元素时,每进行一次插入或删除,都要移动近乎一半的元素。 • 对于长度可变的线性表,必须按可能达到的最大长度分配空间
3.3 线性表的链式表示和实现 1 线性链表的概念: 以链式结构存储的线性表称之为线性链表。特点是该线性表中的数据元素可以用任意的存储单元来存储。线性表中逻辑相邻的两元素的存储空间可以是不连续的。为表示逻辑上的顺序关系,对表的每个数据元素除存储本身的信息之外,还需存储一个指示其直接衙继的信息。这两部分信息组成数据元素的存储映象,称为结点。
(1)单链表(线性链表):链式存储的线性表 结点除信息域外还含有一个指针域,用来指出其后继结点的位置 最后一个结点没有后继结点,指针它的指针域为空(记为NIL 或∧)。另外还需要设置一个指针head,指向单链表的第一个结点
链表的一个重要特点是插入、删除运算灵活方便,不需移动结点,只要改变结点中指针域的值即可链表的一个重要特点是插入、删除运算灵活方便,不需移动结点,只要改变结点中指针域的值即可 插 入 删 除
(3)双向链表:设有一个指向后继结点的指针和一个指向前驱结点的指针(3)双向链表:设有一个指向后继结点的指针和一个指向前驱结点的指针 (2)循环链表:循环链表和单链表的差别仅在于链表中最后一个结点的指针域不为“NIL”,而是指向头一个结点,成为一个由链指针链结的环
第四章 栈和队列 4.1 栈4.2 栈的应用4.4 队列4.5 队列的应用
进栈 出栈 ... 栈顶 an ……... 栈s=(a1,a2,……,an) a2 栈底 a1 4.1 栈一、栈(STACK)的定义 栈是限定仅在表尾进行插入或删除操作的线性表。栈的表尾称为栈顶(top),表头称为栈底(bottom) ,不含元素的空表称为空栈。 栈也一种特殊的线性表,先进后出(FILO)或后进先出(LIFO) ,它的运算规则受到一些约束和限定,故又称限定性数据结构 。
(1)栈的结构特点栈的物理存储可以用顺序存储结构,也可以用链式存储结构(1)栈的结构特点栈的物理存储可以用顺序存储结构,也可以用链式存储结构 (2)栈的运算设置一个空栈; 判定栈是否为空; 进栈、退栈; 读取栈顶元素等
顺序栈 实现:一维数组s[M] F E 5 5 5 top top top top top top top D 4 4 4 C 3 3 3 B top top top top top top 2 2 2 A 1 1 1 top=0 0 0 0 栈空 栈空 栈满 F E D C B A 出栈 进栈 设数组维数为M top=0,栈空,此时出栈,则下溢(underflow) top=M,栈满,此时入栈,则上溢(overflow) 栈顶指针top,指向实际栈顶 后的空位置,初值为0
入栈算法 int push(int s[],int x,int top) { if(top==M) { printf("overflow"); return(-M); } s[top]=x; return(++top); } 0 M-1 栈1底 栈1顶 栈2顶 栈2底 出栈算法 • int pop(int s[],int top,int *q) • { if(top==0) • { printf("stack empty"); • return(0); • } • *q=s[--top]; • return(top); • } 在一个程序中同时使用两个栈
链栈 栈顶 栈底 …... top ^ data link top top 栈底 p x …... ^ • 入栈算法 • JD *lzjz(JD *top,int x) • { JD *p; • p=(JD *)malloc(sizeof(JD)); • p->data=x; • p->link=top; • top=p; • return(p); • } 结点定义 typedef struct node { int data; struct node *link; }JD;
栈底 top …... ^ • 出栈算法 JD *lztz(JD *top,int *p) { JD *q; if(top!=NULL) { q=top; *p=top->data; top=top->link; free(q); } return(top); }
子过程2 子过程1 子过程3 t s s 主程序 r r r s t r r s r 4.2 栈的应用1 过程的嵌套调用
2 递归过程及其实现 • 递归:函数直接或间接的调用自身叫递归 • 实现:建立递归工作栈 例 递归的执行情况分析 void print(int w) { int i; if ( w!=0) { print(w-1); for(i=1;i<=w;++i) printf(“%3d,”,w); printf(“/n”); } } 运行结果: 1, 2,2, 3,3,3,
w w 1 0 w print(0); 2 返回 w (4)输出:1 (4)输出:1 主程序 print(1); 3 (3) 输出:2, 2 (3) 输出:2, 2 print(2); w=3; (2) 输出:3, 3, 3 (2) 输出:3, 3, 3 print(w) (1) (1) top (3)w=1 (1 ) 3 (4)w=0 (2)w=2 (3)w=1 (2) 2 (1)w=3 (2)w=2 (1) 3 top (1)w=3 top (4) 0 top (3) 1 (3) 1 top (2)w=2 (2) 2 (2) 2 (1)w=3 (1) 3 (1) 3 (1)w=3 top top top 结束 递归调用执行情况如下:
Tower of Hanoi问题: 有A,B,C三个塔座,A上套有n个直径不同的圆盘,按直径从小到大叠放,形如宝塔,编号1,2,3……n。要求将n个圆盘从A移到C,叠放顺序不变,移动过程中遵循下列原则: 每次只能移一个圆盘 圆盘可在三个塔座上任意移动 任何时刻,每个塔座上不能将大盘压到小盘上 • 解决方法: • n=1时,直接把圆盘从A移到C • n>1时,先把上面n-1个圆盘从A移到B,然后将n号盘从A移到C,再将n-1个盘从B移到C。即把求解n个圆盘的Hanoi问题转化为求解n-1个圆盘的Hanoi问题,依次类推,直至转化成只有一个圆盘的Hanoi问题
n x y z 返回地址 算法 void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) } void move(int h,char c,char f) { printf("%d:%c--->%c\n",h,c,f); } main() { int m; printf("Input the number of disks:"); scanf("%d",&m); printf("The steps to moving %3d disks:\n",m); hanoi(m,'A','B','C'); (0) } • 执行情况: • 递归工作栈保存内容:形参n,x,y,z和返回地址 • 返回地址用行编号表示
1 2 3 A B C A B C 3 A B C 0 1 A B C 6 2 A C B 6 3 A B C 0 2 A C B 6 3 A B C 0 3 A B C 0 2 A C B 6 main() { int m; printf("Input number of disks”); scanf("%d",&m); printf(”Steps : %3d disks”,m); hanoi(m,'A','B','C'); (0) } void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) }
A B C A B C 3 A B C 0 2 A C B 6 1 C A B 8 3 A B C 0 2 A C B 6 3 A B C 0 3 A B C 0 2 A C B 6 main() { int m; printf("Input the number of disks scanf("%d",&m); printf("The steps to moving %3d hanoi(m,'A','B','C'); (0) } void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) }
A B C A B C 3 A B C 0 2 B A C 8 3 A B C 0 2 B A C 8 1 B C A 6 3 A B C 0 2 B A C 8 3 A B C 0 main() { int m; printf("Input the number of disks scanf("%d",&m); printf("The steps to moving %3d hanoi(m,'A','B','C'); (0) } void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) }
A B C A B C 3 A B C 0 2 B A C 8 1 A B C 8 3 A B C 0 2 B A C 8 3 A B C 0 3 A B C 0 2 B A C 8 栈空 main() { int m; printf("Input the number of disks scanf("%d",&m); printf("The steps to moving %3d hanoi(m,'A','B','C'); (0) } void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) }
8 159 8 8 19 2 2 3 7 例 把十进制数159转换成八进制数 余 7 3 top top top top 余 3 7 7 余 2 0 2 (159)10=(237)8 3 7 • 多进制输出
6 18 3 * 4 + - - - 12 6 6 6 2 操作数 操作数 操作数 操作数 操作数 运算符 运算符 运算符 运算符 运算符 表达式求值 中缀表达式 后缀表达式(RPN) a*b+c ab*c+ a+b*c abc*+ a+(b*c+d)/e abc*d+e/+ 中缀表达式:操作数栈和运算符栈 例 计算 2+4-3*6
15 3 top top top top top top 4 4 5 19 4 3 7 后缀表达式求值步骤: 1、读入表达式一个字符 2、若是操作数,压入栈,转4 3、若是运算符,从栈中弹出2个数,将运算结果再压入栈 4、若表达式输入完毕,栈顶即表达式值; 若表达式未输入完,转1 后缀表达式:435*+ 例 计算 4+3*5
(7) (3) (2) (1) (6) (4) (5) R [ 7][ 7 ] 1 2 3 4 5 6 7 0 1 1 1 1 1 0 1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 0 0 0 0 1 0 1 0 0 1 1 0 0 1 0 1 0 1 1 0 1 0 1 1 0 1 0 1 1 0 1 1 0 0 0 0 0 0 0 0 0 • 地图四染色问题 1 2 3 4 2 4 3 1 2 3 4 3 1# 紫色 2# 黄色 3# 红色 4# 绿色
4.4 队列 队列的定义及特点 定义:队列是限定只能在表的一端进行插入,在表的另一端进行删除的线性表 队尾(rear)——允许插入的一端 队头(front)——允许删除的一端 队列特点:先进先出(FIFO) 出队 a1 a2 a3…………………….an 入队 队列Q=(a1,a2,……,an) front rear 出队 出队 a1 a2 a3…………………….an 入队 入队 端1 端2 • 双端队列
链队列 结点定义 头结点 队头 队尾 …... front ^ rear typedef struct node { int data; struct node *link; }JD; 设队首、队尾指针front和rear, front指向头结点,rear指向队尾
^ ^ x出队 x y ^ rear front y出队 空队 x入队 x ^ rear rear front front front rear y入队 x y ^ rear front
出队算法 • int ldsc(JD *front,JD *rear) • { JD *s; • int x; • if(front==rear) • return(-1); • s=front->link; • front->link=s->link; • if(s->link==NULL) • rear=front; • x=s->data; • free(s); • return(x); • } • 入队算法 • JD *ldcr(JD *rear,int x) • { JD *p; • p=(JD *)malloc(sizeof(JD)); • p->data=x; • p->link=NULL; • rear->link=p; • return(p); • }
队列的顺序存储结构 实现:用一维数组实现sq[M] rear J6 J5 5 5 5 5 rear J4 4 4 4 4 rear rear rear J3 front 3 3 3 3 front front front J2 2 2 2 2 J1 1 1 1 1 front=-1 rear=-1 front front J4,J5,J6入队 J1,J2,J3出队 J1,J1,J3入队 0 0 0 0 队空 J3 J2 J1 设两个指针front,rear,约定: rear指示队尾元素; front指示队头元素前一位置 初值front=rear=-1 空队列条件:front==rear 入队列:sq[++rear]=x; 出队列:x=sq[++front];
存在问题 设数组维数为M,则: 当front=-1,rear=M-1时,再有元素入队发生溢出——真溢出 当front-1,rear=M-1时,再有元素入队发生溢出——假溢出 解决方案 队首固定,每次出队剩余元素向下移动——浪费时间 循环队列 基本思想:把队列设想成环形,让sq[0]接在sq[M-1]之后,若rear+1==M,则令rear=0; M-1 rear …... 0 1 …... front • 实现:利用“模”运算 • 入队: rear=(rear+1)%M; sq[rear]=x; • 出队: front=(front+1)%M; x=sq[front]; • 队满、队空判定条件
front rear 5 4 0 3 1 2 rear J5 J6 J4 5 4 0 J4,J5,J6出队 3 1 2 front J7,J8,J9入队 J5 初始状态 J6 J4 5 4 0 3 1 J7 J9 2 J8 front rear 队空:front==rear 队满:front==rear 解决方案: 1.另外设一个标志以区别队空、队满 2.少用一个元素空间: 队空:front==rear 队满:(rear+1)%M==front
入队算法: • void en_cycque(int sq[],int front,int rear,int x) • { if(((rear+1)%M)==front) • printf("overflow"); • else • { rear=(rear+1)%M; • sq[rear]=x; • } • } 出队算法: • int dl_cycque(int sq[],int front,int rear,int *q) • { if(front==rear) • return(0); • else • { front=(front+1)%M; • *q=sq[front]; • return(1); • } • }
队列应用举例划分子集问题 问题描述:已知集合A={a1,a2,……an},及集合上的关系R={ (ai,aj) | ai,ajA, ij},其中(ai,aj)表示ai与aj间存在冲突关系。要求将A划分成互不相交的子集A1,A2,……Ak,(kn),使任何子集中的元素均无冲突关系,同时要求分子集个数尽可能少 例 A={1,2,3,4,5,6,7,8,9} R={ (2,8), (9,4), (2,9), (2,1), (2,5), (6,2), (5,9), (5,6), (5,4), (7,5), (7,6), (3,7), (6,3) } 可行的子集划分为: A1={ 1,3,4,8 } A2={ 2,7 } A3={ 5 } A4={ 6,9 }
算法思想:利用循环筛选。从第一个元素开始,凡与第一个元素无冲突的元素划归一组;再将剩下的元素重新找出互不冲突的划归第二组;直到所有元素进组算法思想:利用循环筛选。从第一个元素开始,凡与第一个元素无冲突的元素划归一组;再将剩下的元素重新找出互不冲突的划归第二组;直到所有元素进组 所用数据结构 冲突关系矩阵 r[i][j]=1, i,j有冲突 r[i][j]=0, i,j无冲突 循环队列cq[n] 数组result[n]存放每个元素分组号 工作数组newr[n]
工作过程 初始状态:A中元素放于cq中,result和newr数组清零,组号group=1 第一个元素出队,将r矩阵中第一行“1”拷入newr中对应位置,这样,凡与第一个元素有冲突的元素在newr中对应位置处均为“1”,下一个元素出队 若其在newr中对应位置为“1”,有冲突,重新插入cq队尾,参加下一次分组 若其在newr中对应位置为“0”, 无冲突,可划归本组;再将r矩阵中该元素对应行中的“1”拷入newr中 如此反复,直到9个元素依次出队,由newr中为“0”的单元对应的元素构成第1组,将组号group值“1”写入result对应单元中 令group=2,newr清零,对cq中元素重复上述操作,直到cq中front==rear,即队空,运算结束
void division (int r[][N],int n, int cq[], int newr[],int result[]) { int k,i,pre,group; for(k=0;k<n;k++) cq[k]=k+1; front=n-1; rear=n-1; for(k=0;k<n;k++) newr[k]=0; group=1; pre=0;
do{ front=(front+1)%n; i=cq[front]; if(i<pre) { group++; result[i-1]=group; for(k=0;k<n;k++) newr[k]=r[i-1][k]; } else if(newr[i-1]!=0) { rear=(rear+1)%n; cq[rear]=i; } else { result[i-1]=group; for(k=0;k<n;k++) newr[k]=newr[k]+r[i-1][k]; } pre=i; }while(rear!=front); }
0 1 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 1 R= 0 1 1 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 cq 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 r 初始 f newr result • 算法描述 R={ (2,8), (9,4), (2,9), (2,1), (2,5), (6,2), (5,9), (5,6), (5,4), (7,5), (7,6), (3,7), (6,3) }
0 1 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 1 R= 0 1 1 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 2 3 4 5 6 7 8 9 r f newr result R={ (2,8), (9,4), (2,9), (2,1), (2,5), (6,2), (5,9), (5,6), (5,4), (7,5), (7,6), (3,7), (6,3) } cq
0 1 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 1 R= 0 1 1 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 2 3 4 5 6 7 8 9 r f newr result R={ (2,8), (9,4), (2,9), (2,1), (2,5), (6,2), (5,9), (5,6), (5,4), (7,5), (7,6), (3,7), (6,3) } cq
0 1 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 1 R= 0 1 1 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 1 0 1 0 0 0 0 0 0 0 1 0 0 0 11 0 0 2 4 5 6 7 8 9 r f newr result R={ (2,8), (9,4), (2,9), (2,1), (2,5), (6,2), (5,9), (5,6), (5,4), (7,5), (7,6), (3,7), (6,3) } cq
0 1 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 1 R= 0 1 1 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 1 0 11 0 0 0 0 0 0 1 0 0 111 0 1 2 5 6 7 8 9 r f newr result R={ (2,8), (9,4), (2,9), (2,1), (2,5), (6,2), (5,9), (5,6), (5,4), (7,5), (7,6), (3,7), (6,3) } cq
0 1 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 1 0 1 R= 0 1 1 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 1 0 11 0 0 0 0 0 0 1 0 0 111 0 1 25 6 7 8 9 r f newr result R={ (2,8), (9,4), (2,9), (2,1), (2,5), (6,2), (5,9), (5,6), (5,4), (7,5), (7,6), (3,7), (6,3) } cq