软件技术基础线性表.docx
《软件技术基础线性表.docx》由会员分享,可在线阅读,更多相关《软件技术基础线性表.docx(31页珍藏版)》请在冰豆网上搜索。
![软件技术基础线性表.docx](https://file1.bdocx.com/fileroot1/2022-11/20/784f88f3-5c17-42b9-820b-1c3e15813c29/784f88f3-5c17-42b9-820b-1c3e15813c291.gif)
软件技术基础线性表
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∈D,2≤i≤n}
若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)EXP(q),则q所指的结点为C(x)中的一项,将q结点插入p结点之前,并将q指针后移一个结点;若EXP(p)=EXP(q),则将两个结点中的系数相加,当和不为零时,修改p结点中的系数,回收q结点;否则删去p结点,同时回收p,q结点。
这一方法实际上是将B(x)加到A(x)中,最后形成C(x),因此C(x)中的结点不需要重新生成。
算法描述如下:
A(x)=5-3x+13x5,B(x)=3x-8x4-6x5+7x8。
(EXP:
exponentCOEF:
coefficient)
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(ithen
{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为前一个出队元素编号,初值为零//(pre←9);
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);
cq[k]Result[I]
1
2
3
4
5
6
7
8
9
0
1
0
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
9
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)+3(i-1)-1+j-(i-2)-1]
(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)}
11.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
21