2 2线性表.docx
- 文档编号:8492571
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:24
- 大小:324.10KB
2 2线性表.docx
《2 2线性表.docx》由会员分享,可在线阅读,更多相关《2 2线性表.docx(24页珍藏版)》请在冰豆网上搜索。
22线性表
2.2线性表
2.2.1线性表的定义和运算
一般形式:
L=(a1,a2,…,an)
其中L为线性表,ai(i=1,…,n)是属于某数据对象的元素,n(n≥0)为元素个数称为表长,n=0为空表。
线性表的定义:
L=(D,R)
其中:
D={a1,a2,…,an}
R={
若ai-1≥ai,i=2,3,…,n,则称该线性表为有序表,否则称为无序表。
线性表的基本运算:
插入、删除、查找、排序。
2.2.2顺序存储线性表
1.顺序存储结构
2.顺序存储结构的插入、删除运算
插入
INSERTLIST(V,n,i,x)
1.if(i<1)OR((i>n+1)then{参数错return}(i=n+1表示插入在最后)
2.forj=ntoistep(-1)
3.V[j+1]←V[j]
4.end(j)
5.V[i]←x
6.n←n+1
7.return
删除
DELETELIST(V,n,i)
1.if(i<1)OR((i>n+1)then{参数错return}
2.forj=iton-1
3.V[j]←V[j+1]
4.end(j)
5.n←n-1
6.return
2.2.3线性链表
1.链式存储结构
2.线性链表的基本运算
(1)基本操作
设p,q,s均为指针类型变量,指向数据域为data,指针域为next的结点,表2.2表示线性链表的几项基本操作。
(2)结点的动态生成及回收
设具有数据域date,指针域next的空白链表,其头指针为av。
从空白链表中获取一个结点,由指针P指向,其算法为:
GETNODE(P)
1.p←av
2.av←next(av)//修改空白链表头指针//
3.return
回收一个由P指针指向的结点,放回空白链表的算法为:
REP(P)
1.Next(P)←av
2.av←P
3.return
(3)插入运算
INLINKST(head,a,b)
1.GETNODE(p);data(p)←b;//取得一个新结点p//
2.if(head=nil)then{head←p;next(p)←nil;return}//空白情况//
3.if(data(head)=a)then{next(p)←head;head←p;return}//a为头结点//
4.LOOKFOR(head,a,q)//寻找元素a之前的结点q//
5.next(p)←next(q);next(q)←p
6.return
其中LOOKFOR(head,a,q)为在非空链表中寻找包含指定元素a之前的结点q的算法:
LOOKFOR(head,a,q)
1.q←head
2.While(next(q)≠nil)and(data(next(q))≠a)do
3.q←next(q)//如果表中无a结点,则q指向链表最后一个结点//
(4)删除运算
DELINKST(head,a)
1.if(head=nil)then{空表return}//空表情况//
2.if(data(head)=a)then{s←next(head);RET(head);head←s;return}//a为头结点//
3.LOOKFOR(head,a,q)
4.if(next(q)=nil)then{无此结点return}
5.p←next(q);next(q)←next(p)
6.RET(p)
7.return
3.线性链表的其他形式
4.应用实例---一元多项式相加
Pn(x)=P0+P1X2+…+Pixi+…+Pnxn
设有一元多项式A(x)和B(x),现要求相加结果C(x)=A(x)+B(x)。
其运算规则为:
将两个多项式中指数相同的项对应系数相加,若和不为零,则构成C(x)中的一项;A(x)和B(x)中所有指数不相同的项均复抄到C(x)中。
用带有头结点的线性链表表示多项式A(x),B(X),设指针ha,hb分别为指向多项式链表A(x),B(X)的头指针,指针p,q的初始位置分别指向A(x),B(x)中第一项,则求A(x)+B(x)的运算过程为:
比较p,q所指结点中的指数项,若EXP(p)
这一方法实际上是将B(x)加到A(x)中,最后形成C(x),因此C(x)中的结点不需要重新生成。
算法描述如下:
ADD-POLY(ha,hb)
1.p←next(ha);q←next(hb)
2.pre←ha;hc←ha
//pre指向p的前趋,为c(x)头指针//
3.while(p<>nil)AND(q<>nil)do
4.case
5.EXP(p) 6.{pre←p;p←next(p)} 7.EXP(p)=EXP(q): 8.{x←COEF(p)+COEF(q); 9.if(x<>0)then{COEF(p)←x;pre←p} 10.else{next(pre)←next(p);RET(p)} 11.p←next(pre);u←q;q←next(q);RET(u)} 12.EXP(p)>EXP(q): 13.{u←next(q);next(q)←p; next(pre)←q;pre←q;q←u} 14.end(case) 15.end(while) 16.if(q<>nil)thennext(pre)←q 17.RET(hb)//释放多项式B(x)的头结点// 18.return 2.2.4向量和链表的比较(自学) 2.3栈与队 2.3.1栈的结构和运算 1.栈的定义 栈是限定只能在表的一端进行插入和删除操作的线性表。 允许插入或删除的一端称为栈顶(top),另一端称为栈底(bottom),如图所示。 设栈s=(a1,a2,a3,…,an),称a1为栈底元素,an为栈顶元素。 栈中元素按a1,a2,a3,…,an次序入栈,又按an,…,a2,a1次序退栈。 若n=0,则称为空栈。 因此栈又称为“后进先出”(LIFO,lastinfirstout)线性表。 2.顺序栈 s[1: m]m表示栈允许的最大容量,通常top指示栈顶位置,top=0表示栈空,top=m表示栈满。 PUSF(s,m,top,x)//将元素x入栈// 1.if(top=m)then{‘上溢’,return} 2.top=top+1 3.s[top]=x 4.return POP(s,top,y)//退栈,将栈顶元素送入y中// 1.if(top=0)then{‘下溢’,return} 2.y=s[top] 3.top=top-1 4.return 3.链栈 4.栈的应用 (1)表达式求值 运算符: **/*+-; 优先数: 322110 两个栈: 操作数(NS),运算符(OS) 首先在OS中放入表达式结束符“;”,然后自左至右扫描表达式。 根据扫描的每一符号作如下不同的处理: 1.若为操作数,将其压入NS栈。 2.若为运算符,需看当前OS的栈顶元素: .若当前运算符的优先数大于OS的栈顶运算符,则将当前运算符压入OS栈。 .若当前运算符的优先数不大于OS的栈顶运算符,则从NS栈中弹出两个操作数,设为x,y,再从OS中弹出一个运算符,设为θ,由此构成一条机器操作指令: xθy→T,并将结果T送如NS栈。 .若当前运算符为“;”,且OS栈顶也为“;”,则表示表达式处理结束,此时Ns栈顶元素即为此表达式值。 设表达式A/B**C+D;其求值过程如图所示。 PUSF(s,m,top,x)//将元素x入栈// 1.if(top=m)then{‘上溢’,return} 2.top=top+1 3.s[top]=x 4.return POP(s,top,y)//退栈,将栈顶元素送入y中// 1.if(top=0)then{‘下溢’,return} 2.y=s[top] 3.top=top-1 4.return 表达式求值的算法如下: EXP(OS,topO,NS,topN)//topO,topN为OS,NS 栈顶指示器,初态为零// 1.PUSH(OS,topO,';') 2.t←0//t=0表示扫描下一个符号// 3.while(t<>2)do 4.if(t=0)thenread(w)//w中存放当前读入符号// 5.if(w为操作数)thenPUSH(NS,topN,w) 6.else 7.{POP(OS,topO,q)//查看当前OS栈顶元素q// 8.if(w>q)then{PUSH(OS,topO,w),t←0} 8.else 9.if(q=';')and(w=';')then 10.{POP(NS,topN,z),t←2}//t=2表示处理结束// 10.else POP(NS,topN,x)POP(NS,topN,y) 13.POP(OS,topO,q);x←yqx;//构成一条机器指令// 14.PUSH(NS,topN,x);t←1}//t=1表示继续处理当前符号// 15.} 16.end(while) 17.return 其中TOP(OS,topO,q)为读栈顶元素,算法如下: TOP(s,top,x) 1.if(top=0)then{'栈空',return} 2.x←s[top] 2.return (2)过程嵌套和递归调用 1(n=1,2) Fib(n)= Fib(n-1)+Fib(n-2)(n>2) Fibonacci序列 Fib(n) 1.if(n=1)or(n=2)thenFib←1 2.elseFib←Fib(n-1)+Fib(n-2) 3.return D3,3 D4,3 D2,4 D2,4 D2,4 D2,4 D2,4 D5,4 D7,7 D8,3 D1,5 D1,5 D1,5 D1,5 D1,5 D1,5 D1,5 D1,5 D1,5 D6,5 D6,5 D6,5 D6,5 D6,5 (3)回溯求解算法 设有n件体积分别w1,w2,…wn的物品和一个能装载总体积为T的背包,要求从n件物品中挑选出若干件物品,其体积之和恰好装满背包。 W[1: n]存放n件物品的体积 S[1: n]存放放入背包内物品的序号 T为背包能容纳的体积, I为待选物品序号 每进栈一件物品,就从T中减去该物品的体积,若T-W[i]>=0,则该物品可选,若T-W[i]<0,则该物品不可选,若i>n,则需退栈,若此时栈空,则说明无解。 算法如下: PACK(T,n,W,S,top) 1.top←0;i←1 2.while(T>0)and(i<=n)do 3.if(T-W[i]=0)or((T-W[i]>=0)and(i then {top←top+1;s[top]←i;T←T-W[i]} if(T=0)then{'背包有解'return} else{if(i=n)and(top>0)then//取 出栈顶物品// {i←s[top];top←top-1,T←T+W[i]} i←i+1//准备挑选下一件物品//} 4.end(while) 5.'背包无解'return 2.3.2队的结构和运算 1.队的定义 只允许在一端进行插入,在另一端进行删除的线性表。 插入端: 队尾(rear) 删除端: 排头(front) 用移动rear与front指示器来进行插入和删除 称队为先进先出(FIFO,firstinfirstout)线性表。 1.顺序队 Q[1: m]存放队列元素 m: 允许的最大容量。 初始状态: front=rear=0,队空 假溢出: 入队时rear增1,出队时front增1,当front=rear时队空,当rear=m时无法再继续入队,但此时队中空间并一定满(只是当rear-front=m时才真正为满) 循环队列: 存放队列的数组形成头尾相接的环形。 设CQ[0: m-1]表示最大容量的循环队列,其中头、尾指示器(front,rear)均按顺时针方向前进,rear=front=m-1为初态。 循环队列的插入和删除算法如下: ADDCQ(CQ,m,front,rear,x)//将x插入队列CQ中// 1.rear←(rear+1)modm//mod为模除运算,保证rear循环计数// 2.if(front=rear)then{'队满'return} 3.CQ[rear]←x 4.return DELCQ(CQ,m,front,rear,y)//删除队首元素送入y中// 1.if(front=rear)then{'队空'return} 2.front←(front+1)modm 3.y←CQ[front] 4.return 为了辨别队满与队空,循环队列中永远会空一个位置。 3.链队 当队的容量无法预先估计时,可以采用链表作存储结构,称为链队。 链队的插入、删除算法如下: ADDLINK(rear,front,x)//在链队中插入x结点// 1.GETNODE(p) 2.data(p)←x;next(p)←nil//设置新的队尾元素// 3.next(rear)←p;rear←p//设置新的队尾指针// 4.return DELLINK(front,rear,y)//删除排头元素赋给y// 1.if(rear=front)then{'队空'return} 2.y←data(next(front)); next(front)←next(next(front))//删除排头结点,把头结点链向下一个结点// 3.if(next(front)=nil)thenrear←front 4.return 4.队的应用 1.划分子集问题 已知集合A={a1,a2,…,an},并已知此集合上的关系R={(ai,aj)|ai,aj∈A,i≠j},其中(ai,aj)表示ai与aj之间存在冲突关系。 现要求将A划分成互不相交的子集A1,A2,…,Ak(k≤n),使任何子集的元素均无冲突关系,同时要求划分子集的个数尽可能少。 这类问题可以有各种实际应用背景,例如在安排运动会比赛日程时,需要考虑如何安排比赛项目,才能使同一运动员参加的不同项目不在同一日进行,同时又使比赛总的日程最短。 如运动会共设9个比赛项目,规定每个运动员最多可以参加三个项目,则 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[1: n,1: n]表示,若第i与第j元素有冲突,则R[i,j]=1,否则R[i,j]=0。 对应于上述例子的关系矩阵见图 循环队列cq[0: n-1],存放集合A的元素;数组Result[1: n]用以存放每个元素的分组号;newr[1: n]为工作数组。 以上述例子来说明工作过程: 初试状态: 集合A元素放入cq中,Result及newr置零,设置组号group=1。 如图 (a)所示。 当第1个元素出队时。 将R矩阵中第1行元素中的“1”拷入newr向量对应位置,得到: 凡是与第一元素有冲突的元素处均置为“1”。 这里说明序号为2的元素不能进入第一组,则应将元素2出队后重新入cq的队尾。 继续将元素3出队,并将R矩阵中第三行元素中的“1”拷入newr向量对应位置,得到: ← 如此继续,直到9个元素依次出队后,由newr单元中为“0”的单元序号构成第1组,将“1”标志放入Result向量对应单元中,见图(b)。 将group=2,newr清零,重新对cq中剩余的元素重复上述操作,可得第2组元素,第3组元素……直到cq中front=rear,队空,运算结束(见图(c),(d),(e))。 PROCEDUREDIVISION(R,n,cq,newr,Result); FORk=0TOn-1cq[k]←k+1;//n个元素存入循环队列cq// front←n-1;rear←n-1;//头尾指针赋初值// FORK=1TOnnewr[k]←0//newr向量置初值// group←1;pre←0;//group为当前组号,pre为前一个出队元素编号,初值为零// fi=.t. WHILErear≠front.or.fi=.t.DO//队列非空// Fi=.f. front←front+1;IFfront=n+1THENfront←1; I←cq[front];//I为当前出队元素// IFI THEN//重新开辟新组// group←group+1;Result[I]←group;//记录组号// FORk=1TOnnewr[k]←R[I,k] ELSE IFnewr[I]≠0 THEN//发生冲突元素,重新入队// rear=rear+1;IFrear=n+1THENrear=1; cq[rear]←I ELSE//可以分在当前组// Result[I]←group; FORk=1TOn Newr[k]←newr[k]+R[I,k]; pre←I END(WHILE); 2.4数组 2.4.1数组的定义 一个m行n列的数组可以表示为 2.4.2数组的顺序存储结构 1.按行优先顺序存放 假设每个元素仅占一个单元地址,则元素aij的存储地址可以通过以下关系式计算: 二维数组: Loc(aij)=Loc(a11)+(i-1)*n+(j-1) (1≦i<≦m,1≦j<≦n) 三维数组: Loc(aijk)=Loc(a111)+(i-1)*m*n+(j-1)*n+(k-1) (1≦i≦l,1≦j<≦m,1≦k≦n) 2.按列优先顺序存放 二维数组: Loc(aij)=Loc(a11)+(j-1)*m+(i-1) (1≦i<≦m,1≦j<≦n) 三维数组: Loc(aijk)=Loc(a111)+(k-1)*l*m+(j-1)*l+(i-1) (1≦i<≦l,1≦j<≦m,1≦k<≦n) 3.特殊矩阵的存放方式 (1)下三角阵的存储方式 设下三角阵An,n为 若将其中非零元素按行顺序存放为 {a11,a21,a22,a31,a32,…an1,an2,…,ann} 从第1行至第i-1行的非零元素个数为 因此求取其中非零元素aij的地址可按下列公式计算: Loc(aij)=Loc(a11)+i(i-1)/2+(j-1) (1≦j≦i≦n) (2)三对角阵的存储方式 设An,n为三角阵: 若将其中非零元素按行顺序存放为 {a11,a12,a21,a22,a23,a32,a33,a34,…an,n-1,ann} 求取其中非零元素aij地址的关系式为: Loc(aij)=Loc(a11)+(i-1)*2+(j-1)[Loc(a11)+(3i-1)+j-(i-2)] (i=1,j=1,2或i=n,j=(n-1),n或1 2.4.3稀疏矩阵 1.三元组表示 用三元组表示为(A阵) 行 列 值 1 1 3 1 5 7 2 3 -1 3 1 -1 3 2 -2 5 4 2 转置后的B阵 列 行 值 1 1 3 1 3 -1 2 3 -2 3 2 -1 4 5 2 5 1 7 TRANSMAT(A,B) 1.if(tu<>0)then 2.{q←1//q为转置以后B的行号// 3.forcol=1ton 4.forp=1totu//p为转置前A的行号// 5.ifA[p].j=colthen 6.{B[q].i←A[p].j;B[q].j←A[p].i; 7.B[q].v←A[p].v;q←q+1] 8.end(p) 9.end(col)} 10.return 2.带辅助向量的三元组表示 设两个辅助向量POS和NUM满足下列关系: POS (1)=1 POS(i)=POS(i-1)+NUM(i-1),2≤i≤m POS(i)表示稀疏矩阵中第i行的第一个非零元素在三元组中的行号; NUM(i)表示稀疏矩阵中第i行的非零元素个数。 稀疏矩阵A对应的POS与NUM向量值如下: i 12345 POS 13466 NUM 21201 构造POS与NUM向量的算法如下: POSNUM(B,tu,m,POS,NUM)//B为稀疏矩阵的三元组// 1.forp=1tomNUM(p)←0//初始化NUM向量// 2.forp=1totuNUM[B[p].i]←NUM[B[p].i]+1//I为B中的行下标// 3.POS[1]←1 4.forp=2tomPOS(p)←POS(p-1)+NUM(p-1) 5.return 有了POS与NUM向量后,可以高效地访问稀疏矩阵中的任一非零元素。 设对应某稀疏矩阵的三元组为B,其中数据项i为行下标,j为列下标,v为元素值,则访问稀疏矩阵中x行y列元素的算法为: SRPN(x,y,B,POS,NUM,s) 1.s←0 2.k←POS(x) 3.w
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2线性表 线性