第二章 线 性 表
2.1 线性表的类型定义
2.2 线性表的顺序表示和实现
2.3 线性表的链式表示和实现
2.3.1 线性链表
2.3.2 循环链表
2.3.3 双向链表
2.4 一元多项式的表示及相加
线性表(Linear List) :由n(n≧0)个数据元素(结点)a1,
a2, …an组成的有限序列。其中数据元素的个数n定义为
表的长度。当n=0时称为空表, n>0时线性表记作:
(a1,a2,…an)
数据元素ai(1≦i≦n)只是一个抽象的符号,其具体含义
在不同的情况下可以不同。
例1、某单位职工人数从1997年到2002年的变化情况:
(6,17,28,50,92,188)
2.1 线性表的逻辑结构
例2、学生健康情况登记表如下:
…… …… …… …… ……
张立立790634 男17 神经衰弱
刘建平790633 男21 健康
陈 红790632 女20 一般
王小林790631 男18 健康
姓 名学 号性 别年龄 健康情况
2.1 线性表的逻辑结构
数据项(Item)、记录(Record)、文件(File)
从以上例子可看出线性表的逻辑特征是:
在非空的线性表,有且仅有一个开始结点a1,它没有
直接前趋,而仅有一个直接后继a2;
有且仅有一个终端结点an,它没有直接后继,而仅有
一个直接前趋a n-1;
其余的内部结点ai(2≦i≦n-1)都有且仅有一个直接前
趋ai-1和一个直接后继ai+1。
线性表是一种典型的线性结构。
数据的运算是定义在逻辑结构上的,而运算的具体
实现则是在存储结构上进行的。
2.1 线性表的逻辑结构
线形表的抽象数据类型定义:
ADT List{
数据对象:D={ai|ai∈ElemSet, i=1,2,…,n,n≧0}
数据关系:R={<ai-1,ai>| ai-1,ai ∈D,i=2,…,n}
基本操作:
1.IiniList(&L) //构造空表L。
2.LengthList(L) //求表L的长度
3.GetElem(L,i,&e) //取元素ai,由e返回ai
4.LocateElem(L,e,compare()) //求e在表L中的序号
5.PriorElem(L,ce,&pre_e) //求ce的前驱,由pre_e返回
6.NextElem(L,ce,&next_e) //求ce的后继,由next_e返回
7.InsertElem(&L,i,e) //在元素ai之前插入新元素e
8.DeleteElem(&L,i) //删除第i个元素
9.EmptyList(L) //判断L是否为空表
10.ClearList(&L) //置L为空表
......
}ADT List
2.1 线性表的逻辑结构
算法2.1 利用两个线性表LA和LB分别表示两个集合A
和B,现要求一个新的集合A=A∪B。
void union(List &La,List Lb)
{ La_len=ListLength(La);
Lb_len=ListLength(Lb);
for(i=1;i<=Lb_len;i++)
{ GetElem(Lb,i,e);
if(!LocateElem(La,e,equal))
ListInsert(La,++La_en,e);
}
}
2.1 线性表的逻辑结构
一. 顺序存储结构
把线性表的结点按逻辑顺序依次存放在一组地址
连续的存储单元里。用这种方法存储的线性表简称顺
序表。
a1
a2
ai
an
1
2
.
..
.
i
n
Loc(a1)
Loc(a1)+L
Loc(a1)+(i-1)*L
Loc(a1)+(n-1)*L
逻辑地址数据元素存储地址
2.2 线性表的顺序存储结构2.2 线性表的顺序存储结构
特点:线形表中相邻的元素ai,ai+1,其存储地址也相
邻。即以元素在内存中的物理地址的相邻关系隐含地表
示元素在逻辑关系上的相邻关系。
地址公式:Loc(ai+1)=Loc(ai)+L
Loc(ai)=Loc(a1)+(i-1)*L
随机存取:访问任何一个数据元素或结点花费同样多时
间。
由于C语言中的一维数组也是采用顺序存储表
示,故可以用数组类型来描述顺序表。又因为除了用
数组来存储线性表的元素之外,顺序表还应该用一个
变量来表示线性表的长度属性,所以我们用结构类型
来定义顺序表类型。
#define ListSize 100
typedef int DataType;
typedef struct{
DataType data[ListSize];
int length;
} Sqlist;
2.2 线性表的顺序存储结构
二. 顺序表上实现的基本操作
在顺序表存储结构中,很容易实现线性表的一些操
作,
如线性表的构造、第i个元素的访问。
注意:C语言中的数组下标从“0”开始,因此,若L是
Sqlist类型的顺序表,则表中第i个元素是L.data[i-1]。
以下主要讨论线性表的插入和删除两种运算。
1. 插入
线性表的插入运算是指在表的第i(1≦i≦n+1个位置
上,插入一个新结点x,使长度为n的线性表
(a1,…a i-1,ai,…,an)
变成长度为n+1的线性表
(a1,…a i-1,x,ai,…,an)
2.2 线性表的顺序存储结构
算法2.2
Status InsertList(Sqlist &L,DataType x,int i)
{ int j;
if(i<1 || i> L.length+1)
return ERROR;
if(L.length>=ListSize)
return OVERFLOW;
for(j= L.length-1;j>=i-1;j--)
L.data[j+1]= L.data[j];
L.data[i-1]=x;
L.length++;
return OK;
}
2.2 线性表的顺序存储结构
演示
算法的时间复杂度分析:
设pi是在长度为n的线性表中第i个位置上插入一
个结点的概率,令Eis表示移动结点的期望值,则在第
i个位置上插入一个结点的移动次数为n-i+1。故
2.2 线性表的顺序存储结构
假设在表中任何位置(1≦i≦n+1)上插入结点的机
会是均等的,即 p1=p2=p3=…=p n+1=1/(n+1),则
1
1
( 1)
n
is i E p n i
( 1) 2
( 1) 1
1
n
n
E n i
n
is
因此算法的平均时间复杂度为O(n)。
2、删除
线性表的删除运算是指将表的第i(1≦i≦n)结点删
除,使长度为n的线性表(a1,…a i-1,ai,a i+1…,
an),
变成长度为n-1的线性表(a1,…a i-1,a i+1,…,an)。
算法2.3
Status DeleteList(Sqlist &L,int i)
{ int j;
if(i<1 || i>L.length) return ERROR;
for(j=i; j<=L.length-1; j++)
L.data[j-1]=L.data[j];
L.length--;
return OK;
}
2.2 线性表的顺序存储结构
演示
算法的时间复杂度分析:
设qi是在长度为n的线性表中第i个位置上删除一个
结点的概率,令Edl表示移动结点的期望值,则在第i个位
置上删除一个结点的移动次数为n-i。故
2.2 线性表的顺序存储结构
假设在表中任何位置(1≦i≦n)上删除结点的机会是均
等的,既 q1=q2=q3=…=qn=1/n,则
n
dl i E q n i
1
( )
2
( )
1
n
n
E n i
n
dl
因此算法的平均时间复杂度为O(n)。
1 顺序存储结构是以元素在物理存储地址上的相邻
关系隐含表示其逻辑关系上的相邻关系。
2 顺序存储结构便于实现读写表中某个元素的值,
求表的长度,求某个元素的直接前驱或后继等操作。
3 对于插入、删除等操作,需要移动大量的元素,
算法的效率不好。
小结
2.2 线性表的顺序存储结构
链表是指用一组任意的存储单元来依次存放线性表的结点,
这组存储单元即可以是连续的,也可以是不连续的。因此,链表
中结点的逻辑次序和物理次序不一定相同。
为了能正确表示结点间的逻辑关系,在存储每个结点值的同
时,还必须存储指示其后继结点的地址(或位置)信息,这个信
息称为指针或链。链表正是通过每个结点的链域将线性表的n个
结点按其逻辑次序链接在一起的。由于上述链表的每一个结点只
有一个链域,故将这种链表称为单链表。
2.3 线性表的链式表示和实现
data next
2.3.1 线性链表
data:数据域,用来存放结点的值。
next:指针域(亦称链域),用来存放结点
的直接后继的地址。
例1、线性表:(bat,cat,eat,fat,hat,jat,lat,mat)的单
链表示意图如下:
bat cat eat … mat ^
单链表是由表头唯一确定,因此单链表可以用头指针的名
字来命名。例如:若头指针名是head,则把链表称为表head。
显然,单链表中每个结点的存储地址是存放在其前趋结点
next域中,而开始结点无前趋,故应设头指针head指向开始结
点。同时,由于终端结点无后继,故终端结点的指针域为空,
即null(图示中也可用^表示)。
head
2.3 线性表的链式表示和实现
用C语言描述的单链表如下:
Typedef char datatype;
Typedef struct node{
datatype data;
struct node *next;
}listnode;
typedef listnode *linklist;
2.3 线性表的链式表示和实现
一、建立单链表
假设线性表中结点的数据类型是字符,逐个输入这些字
符型的结点,并以换行符‘
/n’
为输入结束标记。动态地建立
单链表的常用方法有如下两种:
1、头插法建表
该方法从一个空
表开始,重复读入数
据,生成新结点,将
读入数据存放到新结
点的数据域中,然后
将新结点插入到当前
链表的表头上,直到
读入结束标志为止。
2.3 线性表的链式表示和实现
linklist createlistf(void)
{ char ch;
linklist head;
listnode *p;
head=null;
while (( ch=getchar( ))!=‵/n′){
p=( listnode*)malloc(sizeof(listnode));
p –>data=ch; p–>next=head;
head=p;
}
return (head);
}
2、尾插法建表
头插法建立链表
虽然算法简单,但生
成的链表中结点的次
序和输入的顺序相反。
若希望二者次序一
致,
可采用尾插法建表。
该方法是将新结点插
入到当前链表的表尾
上,为此必须增加
一个尾指针r,使其
始终指向当前链表的
尾结点。
2.3 线性表的链式表示和实现
linklist creater( )
{ char ch;
linklist head;
listnode *p,*r;
head=NULL; r=NULL;
while((ch=getchar( ))!=‵/n′){
p=(listnode *)malloc(sizeof(listnode));
p–>data=ch;
if(head==NULL) head=p;
else r –>next=p;
r=p;
}
if (r!=NULL) r –>next=NULL;
return(head);
}
说明:
第一个生成的结点是开始结点,将开始结点插入到空
表中,是在当前链表的第一个位置上插入,该位置上的插入
操作和链表中其它位置上的插入操作处理是不一样的,原因
是开始结点的位置是存放在头指针(指针变量)中,而其余
结点的位置是在其前趋结点的指针域中。算法中的第一个if
语句就是用来对第一个位置上的插入操作做特殊处理。算法
中的第二个if语句的作用是为了分别处理空表和非空表两种
不同的情况,若读入的第一个字符就是结束标志符,则链表
head是空表,尾指针r亦为空,结点*r不存在;否则链表
head非空,最后一个尾结点*r是终端结点,应将其指针域置
空。
2.3 线性表的链式表示和实现
头结点:如果在链表的开始结点之前附加一个结点,并
称它为头结点,那么会带来以下两个优点:
a、由于开始结点的位置被存放在头结点的指针域
中,
所以在链表的第一个位置上的操作就和在表的其它位置上
的操作一致,无需进行特殊处理;
b、无论链表是否为空,其头指针是指向头结点在的
非空指针(空表中头结点的指针域为空),因此空表和
非空表的处理也就统一了。
注意:以后的内容中没有特别指出时,链表都带有头结点
2.3 线性表的链式表示和实现
linklist createlistf(void)
{ char ch;
linklist head;
listnode *p;
head=(linklist)malloc(sizeof(listnode ));
head->next=NULL;
while (( ch=getchar( ))!=‵/n′){
p=( listnode*)malloc(sizeof(listnode));
p –>data=ch; p–>next=head->next;
head->next=p;
}
return (head);
}
2.3 线性表的链式表示和实现
带有头结点的链表建立算法(头插法)如下:
演示
linklist createlistr1( )
{ char ch;
linklist head;
listnode *p,*r;
head=(linklist)malloc(sizeof(listnode ));
r=head;
while((ch=getchar( ))!=‵/n′{
p=( listnode*)malloc(sizeof(listnode));
p –>data=ch;
p –>next=p;
r=p;
}
r–>next=NULL;
return(head);
}
2.3 线性表的链式表示和实现
带有头结点的链表建立算法(尾插法)如下:
二、查找运算
1、按序号查找
在链表中,当知道
被查找结点的序号pos
时,只能从链表的头指
针出发,顺链域next
个结点往下搜索,直到
搜索到第i个结点为止。
因此,链表不是随机存
取结构。
2.3 线性表的链式表示和实现
Listnode * getnode(linklist head, int pos)
{ int j;
listnode * p;
p=head;j=0;
while(p–>next!=NULL && j<pos){
p=p –>next;
j++;
}
if (pos==j) return p;
else return NULL;
}
Listnode * locatenode(linklist head,int key)
{ listnode * p=head–>next;
while( p && p –>data!=key)
p=p –>next;
return p;
}
2.3 线性表的链式表示和实现
2、按值查找
按值查找是在链表中,查找是否有结点值等于给定值
key的结点,若有的话,则返回首次找到的其值为key的结
点的存储位置;否则返回NULL。查找过程从开始结点出发,
顺着链表逐个将结点的值和给定值key作比较。
三、插入运算
插入运算是将值为x的新结点插入到表中ai-1与ai结点之间。因
此,必须首先找到ai-1的存储位置p,然后生成一个数据域为x的新
结点*q,并令结点*p的指针域指向q,*q的指针域指向结点ai。从
而实现三个结点ai-1,x和ai之间的逻辑关系的变化。
2.3 线性表的链式表示和实现
p
a
i
a
i-1
q
x
2.3 线性表的链式表示和实现
p
a
i
a
i-1
x
1. q->next = p->next
2.
p->next = q
p
a
i
a
i-1
q
x
1. q->next = p->next
q
具体算法如下:
void insertnode(linklist head,datatype x,int i)
{ listnode * p,*q;
p=getnode(head,i-1);
if(p==NULL)
error( “position error”);
q=(listnode *)malloc(sizeof(listnode));
q–>data=x;
q–>next=p–>next;
p–>next=q;
}
2.3 线性表的链式表示和实现
演示
Listnode * getnode(linklist head,int pos)
{ int j;
listnode * p;
p=head; j=0;
while(p–>next && j<pos){
p=p –>next; j++;
}
if (pos==j) return p;
else return NULL;
}
2.3 线性表的链式表示和实现
设链表的长度为n,合法的插入位置是1<=i<=n+1。注意
当i=1时,getnode找到的是头结点,当 i=n+1时,getnode
找到的是结点an。因此,用i-1做实参调用getnode时可完成
插入位置的合法性检查。
四、删除运算
删除运算是将表的第i个结点删去。必须首先找到ai-1的存储位
置p。然后令p–
>next指向ai的直接后继结点,即把ai从链上摘下。
2.3 线性表的链式表示和实现
p
a
i
a
i-1
a
i+1
p
a
i
a
i-1
a
i+1
q
q = p->next
2.3 线性表的链式表示和实现
p
a
i
a
i-1
a
i+1
q
p->next = q->next
p
a
i
a
i-1
a
i+1
q
free(q)
p
a
i-1
a
i+1
free(q)
之后
void deletelist(linklist head, int i)
{ listnode * p, *q;
p=getnode(head,i-1);
if(p= =NULL || p –>next= =NULL)
return ERROR;
q=p–>next;
p–>next=q–>next;
free( q ) ;
}
2.3 线性表的链式表示和实现
设单链表的长度为
n,则删去第i个结点仅
当1<=i<=n时是合法的。
注意,当i=n+1时,虽
然被删结点不存在,但
其前趋结点却存在,它
是终端结点。因此被删
结点的直接前趋*p存在
并不意味着被删结点就
一定存在,仅当*p存在
且*p不是终端结点(即
p!=NULL&&p –
>next!=NU
LL)时,才能确定被删
结点存在。 从上面的讨论可以看出,链表上实
现插入和删除运算,无须移动结点,仅
需修改指针。
演示
循环链表:在单链表中,将终端结点的指针域NULL改为指向表头结点的
或开始结点,就得到了单链形式的循环链表,并简单称为单循环链表。
为了使空表和非空表的处理一致,循环链表中也可设置一个头结
点。这样,空循环链表仅有一个自成循环的头结点表示。如下图所示:
2.3 线性表的链式表示和实现
2.3.2 循环链表
H a1 a2
H
由于循环链表中没有NULL指针,故涉
及遍历操作时,其终止条件就不再像非循
环链表那样判断p或p—
>next是否为空,
而是判断它们是否等于某一指定指针,如
头指针等。
an
2.3 线性表的链式表示和实现
例:求线性表中元素的个数。
int ListLength1(linklist head) // 单链表
{ int j;
listnode *p;
p=head; j=0;
while(p->next!=NULL) { j++; p=p->next; }
return(j);
}
int ListLength2(linklist head) // 循环链表
{ int j;
listnode *p;
p=head; j=0;
while(p->next!=head) { j++; p=p->next; }
return(j);
}
双向链表(Double linked
list):在单链表的每个结点里
再增加一个指向其直接前趋的
指针域prior。这样就形成的链
表中有两个方向不同的链,故
称为双向链表。
2.3 线性表的链式表示和实现
2.3.3 双向链表
typedef struct dlistnode{
datatype data;
struc dlistnode *prior,*next;
}dlistnode;
typedef dlistnode * dlinklist;
dlinklist head;
a1 a2
H
H
an
和单链表类似,双链表一般也是由头指针唯一确定的,增加头
结点也能使双链表上的某些运算变得方便,将头结点和尾结点链接
起来也能构成循环链表,并称之为双向链表。
设指针p指向某一结点,则双向链表结构的对称性可用下式描述:
p->prior->next = p = p->next->prior
即结点*p的存储位置既存放在其前趋结点*(p->prior)的直接
后继指针域中,也存放在它的后继结点*(p->next)的直接前趋指针
域中。
注意:与单链表的插入和删除操作不同的是,在双链表中插入
和删除必须同时修改两个方向上的指针。
2.3 线性表的链式表示和实现
void dinsert(dlistnode *p,datatype x) //在p指向的元素前插入x
{ dlistnode *q=malloc(sizeof(dlistnode));
q->data=x;
q->prior=p->prior;
q->next=p;
p->prior->next=q;
p->prior=q;
}
2.3 线性表的链式表示和实现
双向链表的前插操作算法如下:
void ddelete(dlistnode *p)
{ p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
}
双向链表的删除操作算法如下:
2.4 一元多项式的表示及相加
基本形式:采用顺序存储的线性表表示,表中每个元素表示系数,
指数隐含于下标中。
若以P=(P
0
,P
1
,
…
P
n
)
表示
P
n
(X
)=P
0
+P
1
X
1
+
…
+
P
n
X
n
Q=(Q
0
,Q
1
,
…
Q
m
)
表示
Q
m
(X
)=Q
0
+Q
1
X
1
+
…
+
Q
m
X
m
则和可以表示为
P+Q=(P
0
+Q
0
,P
1
+Q
1
,
…
P
m
+Q
m
,P
m+1
,
…
P
n
) (m<n)
改进形式:多项式的系数和指数均存放在元素中,采用链表表示。
若
P
n
(x
)=P
1
X
e1
+P
2
X
e2
+
…
+
P
m
X
em
其中
P
i
0 i=1,2,
…
m
0
e
1
e
2
…
e
m
=n
则可以表示为
P=((P
1
,e
1
), (P
2
,e
2
),
…
(
P
m
,e
m
))
一、一元多项式的表示
2.4 一元多项式的表示及相加
typedef struct {
float coef;
int expn;
} term, ElemType;
·
多项式:
PA = 7+3x+9x
8
+5x
17
PB = 8x+22x
7
-9x
8
typedef struct Lnode {
ElemType data;
struct Lnode * next;
} Lnode, * LinkList;
PA
7 0
3 1
9 8
5 17
PB
8 1
22 7
-9 8
∧
∧
coef
expn
next
二、一元多项式的相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
2.4 一元多项式的表示及相加
7 0
3 1
9 8
5 17
8 1
22 7
-9 8
∧
∧
qb
qa
PA
PB
2.4 一元多项式的表示及相加
7 0
3 1
9 8
5 17
8 1
22 7
-9 8
∧
∧
qb
qa
ha
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PA
PB
7 0
11 1
9 8
5 17
8 1
22 7
-9 8
∧
∧
qb
qa
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PA
PB
7 0
11 1
9 8
5 17
22 7
-9 8
∧
∧
qb
qa
ha
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PA
PB
PA
7 0
11 1
9 8
5 17
22 7
-9 8
∧
∧
qb
qa
ha
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PB
PA
7 0
11 1
5 17
PB
22 7
∧
qb
qa
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PA
7 0
11 1
5 17
PB
22 7
∧
qb
ha
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
void
AddPolyn( polynomial &
Pa, polynomial &
Pb)
{ ha = Gethead ( Pa ); hb = Gethead ( Pb );
qa = NextPos (Pa, ha ); qb = NextPos (Pb,hb );
while (
qa && qb)
{ a = GetCurElem(qa); b = GetCurElem(qb):
switch
( cmp( a,b) )
{ -1: ha = qa; qa = NextPos ( Pa,ha ); break; //
多项式
PA
中的当前结点的指数值小
0: sum = a.coef + b.coef; //
两个多项式中的当前结点的指数值相同
if ( sum != 0.0 ) { SetCurElem( qa, sum); ha = qa; }
else { DelFirst( ha, qa); FreeNode (qa); }
DelFirst( hb, qb); FreeNode (qb);
qb = NextPos ( Pb,hb ); qa = NextPos ( Pa,ha ); break;
1: DelFirst( hb, qb); InsFirst (ha,qb); qb = NextPos ( Pb,hb ); break;
} //
多项式
PB
中的当前结点的指数值小
}
if ( !ListEmpty(Pb) ) Append( Pa, qb); // PB 不空连接它的剩余部分
FreeNode(Pb);
} // Addpolyn
void
AddPolyn( polynomial &
Pa, polynomial &
Pb)
{ ha = Pa; hb = Pb;
qa = ha->next; qb = hb->next;
while (
qa && qb)
{ a = qa->data; b = qb->data:
switch
( cmp( a.expn,b.expn) )
{ -1: ha = qa; qa = ha->next; break; //
多项式
PA
中的当前结点的指数值小
0: sum = a.coef + b.coef; //
两个多项式中的当前结点的指数值相同
if ( sum != 0.0 ) { qa->data.coef=sum; ha = qa; }
else { ha->next= qa->next; free(qa); }
hb->next=qb->next; free (qb);
qb = hb->next; qa = ha->next; break;
1: hb->next=qb->next; qb->next=qa; ha->next=qb; ha=qb; qb = hb; break;
} //
多项式
PB
中的当前结点的指数值小
}
if ( qb!=Null) pa->next=qb; // PB
不空连接它的剩余部分
free(Pb);
} // Addpolyn
2.1 线性表的类型定义
2.2 线性表的顺序表示和实现
2.3 线性表的链式表示和实现
2.3.1 线性链表
2.3.2 循环链表
2.3.3 双向链表
2.4 一元多项式的表示及相加
线性表(Linear List) :由n(n≧0)个数据元素(结点)a1,
a2, …an组成的有限序列。其中数据元素的个数n定义为
表的长度。当n=0时称为空表, n>0时线性表记作:
(a1,a2,…an)
数据元素ai(1≦i≦n)只是一个抽象的符号,其具体含义
在不同的情况下可以不同。
例1、某单位职工人数从1997年到2002年的变化情况:
(6,17,28,50,92,188)
2.1 线性表的逻辑结构
例2、学生健康情况登记表如下:
…… …… …… …… ……
张立立790634 男17 神经衰弱
刘建平790633 男21 健康
陈 红790632 女20 一般
王小林790631 男18 健康
姓 名学 号性 别年龄 健康情况
2.1 线性表的逻辑结构
数据项(Item)、记录(Record)、文件(File)
从以上例子可看出线性表的逻辑特征是:
在非空的线性表,有且仅有一个开始结点a1,它没有
直接前趋,而仅有一个直接后继a2;
有且仅有一个终端结点an,它没有直接后继,而仅有
一个直接前趋a n-1;
其余的内部结点ai(2≦i≦n-1)都有且仅有一个直接前
趋ai-1和一个直接后继ai+1。
线性表是一种典型的线性结构。
数据的运算是定义在逻辑结构上的,而运算的具体
实现则是在存储结构上进行的。
2.1 线性表的逻辑结构
线形表的抽象数据类型定义:
ADT List{
数据对象:D={ai|ai∈ElemSet, i=1,2,…,n,n≧0}
数据关系:R={<ai-1,ai>| ai-1,ai ∈D,i=2,…,n}
基本操作:
1.IiniList(&L) //构造空表L。
2.LengthList(L) //求表L的长度
3.GetElem(L,i,&e) //取元素ai,由e返回ai
4.LocateElem(L,e,compare()) //求e在表L中的序号
5.PriorElem(L,ce,&pre_e) //求ce的前驱,由pre_e返回
6.NextElem(L,ce,&next_e) //求ce的后继,由next_e返回
7.InsertElem(&L,i,e) //在元素ai之前插入新元素e
8.DeleteElem(&L,i) //删除第i个元素
9.EmptyList(L) //判断L是否为空表
10.ClearList(&L) //置L为空表
......
}ADT List
2.1 线性表的逻辑结构
算法2.1 利用两个线性表LA和LB分别表示两个集合A
和B,现要求一个新的集合A=A∪B。
void union(List &La,List Lb)
{ La_len=ListLength(La);
Lb_len=ListLength(Lb);
for(i=1;i<=Lb_len;i++)
{ GetElem(Lb,i,e);
if(!LocateElem(La,e,equal))
ListInsert(La,++La_en,e);
}
}
2.1 线性表的逻辑结构
一. 顺序存储结构
把线性表的结点按逻辑顺序依次存放在一组地址
连续的存储单元里。用这种方法存储的线性表简称顺
序表。
a1
a2
ai
an
1
2
.
..
.
i
n
Loc(a1)
Loc(a1)+L
Loc(a1)+(i-1)*L
Loc(a1)+(n-1)*L
逻辑地址数据元素存储地址
2.2 线性表的顺序存储结构2.2 线性表的顺序存储结构
特点:线形表中相邻的元素ai,ai+1,其存储地址也相
邻。即以元素在内存中的物理地址的相邻关系隐含地表
示元素在逻辑关系上的相邻关系。
地址公式:Loc(ai+1)=Loc(ai)+L
Loc(ai)=Loc(a1)+(i-1)*L
随机存取:访问任何一个数据元素或结点花费同样多时
间。
由于C语言中的一维数组也是采用顺序存储表
示,故可以用数组类型来描述顺序表。又因为除了用
数组来存储线性表的元素之外,顺序表还应该用一个
变量来表示线性表的长度属性,所以我们用结构类型
来定义顺序表类型。
#define ListSize 100
typedef int DataType;
typedef struct{
DataType data[ListSize];
int length;
} Sqlist;
2.2 线性表的顺序存储结构
二. 顺序表上实现的基本操作
在顺序表存储结构中,很容易实现线性表的一些操
作,
如线性表的构造、第i个元素的访问。
注意:C语言中的数组下标从“0”开始,因此,若L是
Sqlist类型的顺序表,则表中第i个元素是L.data[i-1]。
以下主要讨论线性表的插入和删除两种运算。
1. 插入
线性表的插入运算是指在表的第i(1≦i≦n+1个位置
上,插入一个新结点x,使长度为n的线性表
(a1,…a i-1,ai,…,an)
变成长度为n+1的线性表
(a1,…a i-1,x,ai,…,an)
2.2 线性表的顺序存储结构
算法2.2
Status InsertList(Sqlist &L,DataType x,int i)
{ int j;
if(i<1 || i> L.length+1)
return ERROR;
if(L.length>=ListSize)
return OVERFLOW;
for(j= L.length-1;j>=i-1;j--)
L.data[j+1]= L.data[j];
L.data[i-1]=x;
L.length++;
return OK;
}
2.2 线性表的顺序存储结构
演示
算法的时间复杂度分析:
设pi是在长度为n的线性表中第i个位置上插入一
个结点的概率,令Eis表示移动结点的期望值,则在第
i个位置上插入一个结点的移动次数为n-i+1。故
2.2 线性表的顺序存储结构
假设在表中任何位置(1≦i≦n+1)上插入结点的机
会是均等的,即 p1=p2=p3=…=p n+1=1/(n+1),则
1
1
( 1)
n
is i E p n i
( 1) 2
( 1) 1
1
n
n
E n i
n
is
因此算法的平均时间复杂度为O(n)。
2、删除
线性表的删除运算是指将表的第i(1≦i≦n)结点删
除,使长度为n的线性表(a1,…a i-1,ai,a i+1…,
an),
变成长度为n-1的线性表(a1,…a i-1,a i+1,…,an)。
算法2.3
Status DeleteList(Sqlist &L,int i)
{ int j;
if(i<1 || i>L.length) return ERROR;
for(j=i; j<=L.length-1; j++)
L.data[j-1]=L.data[j];
L.length--;
return OK;
}
2.2 线性表的顺序存储结构
演示
算法的时间复杂度分析:
设qi是在长度为n的线性表中第i个位置上删除一个
结点的概率,令Edl表示移动结点的期望值,则在第i个位
置上删除一个结点的移动次数为n-i。故
2.2 线性表的顺序存储结构
假设在表中任何位置(1≦i≦n)上删除结点的机会是均
等的,既 q1=q2=q3=…=qn=1/n,则
n
dl i E q n i
1
( )
2
( )
1
n
n
E n i
n
dl
因此算法的平均时间复杂度为O(n)。
1 顺序存储结构是以元素在物理存储地址上的相邻
关系隐含表示其逻辑关系上的相邻关系。
2 顺序存储结构便于实现读写表中某个元素的值,
求表的长度,求某个元素的直接前驱或后继等操作。
3 对于插入、删除等操作,需要移动大量的元素,
算法的效率不好。
小结
2.2 线性表的顺序存储结构
链表是指用一组任意的存储单元来依次存放线性表的结点,
这组存储单元即可以是连续的,也可以是不连续的。因此,链表
中结点的逻辑次序和物理次序不一定相同。
为了能正确表示结点间的逻辑关系,在存储每个结点值的同
时,还必须存储指示其后继结点的地址(或位置)信息,这个信
息称为指针或链。链表正是通过每个结点的链域将线性表的n个
结点按其逻辑次序链接在一起的。由于上述链表的每一个结点只
有一个链域,故将这种链表称为单链表。
2.3 线性表的链式表示和实现
data next
2.3.1 线性链表
data:数据域,用来存放结点的值。
next:指针域(亦称链域),用来存放结点
的直接后继的地址。
例1、线性表:(bat,cat,eat,fat,hat,jat,lat,mat)的单
链表示意图如下:
bat cat eat … mat ^
单链表是由表头唯一确定,因此单链表可以用头指针的名
字来命名。例如:若头指针名是head,则把链表称为表head。
显然,单链表中每个结点的存储地址是存放在其前趋结点
next域中,而开始结点无前趋,故应设头指针head指向开始结
点。同时,由于终端结点无后继,故终端结点的指针域为空,
即null(图示中也可用^表示)。
head
2.3 线性表的链式表示和实现
用C语言描述的单链表如下:
Typedef char datatype;
Typedef struct node{
datatype data;
struct node *next;
}listnode;
typedef listnode *linklist;
2.3 线性表的链式表示和实现
一、建立单链表
假设线性表中结点的数据类型是字符,逐个输入这些字
符型的结点,并以换行符‘
/n’
为输入结束标记。动态地建立
单链表的常用方法有如下两种:
1、头插法建表
该方法从一个空
表开始,重复读入数
据,生成新结点,将
读入数据存放到新结
点的数据域中,然后
将新结点插入到当前
链表的表头上,直到
读入结束标志为止。
2.3 线性表的链式表示和实现
linklist createlistf(void)
{ char ch;
linklist head;
listnode *p;
head=null;
while (( ch=getchar( ))!=‵/n′){
p=( listnode*)malloc(sizeof(listnode));
p –>data=ch; p–>next=head;
head=p;
}
return (head);
}
2、尾插法建表
头插法建立链表
虽然算法简单,但生
成的链表中结点的次
序和输入的顺序相反。
若希望二者次序一
致,
可采用尾插法建表。
该方法是将新结点插
入到当前链表的表尾
上,为此必须增加
一个尾指针r,使其
始终指向当前链表的
尾结点。
2.3 线性表的链式表示和实现
linklist creater( )
{ char ch;
linklist head;
listnode *p,*r;
head=NULL; r=NULL;
while((ch=getchar( ))!=‵/n′){
p=(listnode *)malloc(sizeof(listnode));
p–>data=ch;
if(head==NULL) head=p;
else r –>next=p;
r=p;
}
if (r!=NULL) r –>next=NULL;
return(head);
}
说明:
第一个生成的结点是开始结点,将开始结点插入到空
表中,是在当前链表的第一个位置上插入,该位置上的插入
操作和链表中其它位置上的插入操作处理是不一样的,原因
是开始结点的位置是存放在头指针(指针变量)中,而其余
结点的位置是在其前趋结点的指针域中。算法中的第一个if
语句就是用来对第一个位置上的插入操作做特殊处理。算法
中的第二个if语句的作用是为了分别处理空表和非空表两种
不同的情况,若读入的第一个字符就是结束标志符,则链表
head是空表,尾指针r亦为空,结点*r不存在;否则链表
head非空,最后一个尾结点*r是终端结点,应将其指针域置
空。
2.3 线性表的链式表示和实现
头结点:如果在链表的开始结点之前附加一个结点,并
称它为头结点,那么会带来以下两个优点:
a、由于开始结点的位置被存放在头结点的指针域
中,
所以在链表的第一个位置上的操作就和在表的其它位置上
的操作一致,无需进行特殊处理;
b、无论链表是否为空,其头指针是指向头结点在的
非空指针(空表中头结点的指针域为空),因此空表和
非空表的处理也就统一了。
注意:以后的内容中没有特别指出时,链表都带有头结点
2.3 线性表的链式表示和实现
linklist createlistf(void)
{ char ch;
linklist head;
listnode *p;
head=(linklist)malloc(sizeof(listnode ));
head->next=NULL;
while (( ch=getchar( ))!=‵/n′){
p=( listnode*)malloc(sizeof(listnode));
p –>data=ch; p–>next=head->next;
head->next=p;
}
return (head);
}
2.3 线性表的链式表示和实现
带有头结点的链表建立算法(头插法)如下:
演示
linklist createlistr1( )
{ char ch;
linklist head;
listnode *p,*r;
head=(linklist)malloc(sizeof(listnode ));
r=head;
while((ch=getchar( ))!=‵/n′{
p=( listnode*)malloc(sizeof(listnode));
p –>data=ch;
p –>next=p;
r=p;
}
r–>next=NULL;
return(head);
}
2.3 线性表的链式表示和实现
带有头结点的链表建立算法(尾插法)如下:
二、查找运算
1、按序号查找
在链表中,当知道
被查找结点的序号pos
时,只能从链表的头指
针出发,顺链域next
个结点往下搜索,直到
搜索到第i个结点为止。
因此,链表不是随机存
取结构。
2.3 线性表的链式表示和实现
Listnode * getnode(linklist head, int pos)
{ int j;
listnode * p;
p=head;j=0;
while(p–>next!=NULL && j<pos){
p=p –>next;
j++;
}
if (pos==j) return p;
else return NULL;
}
Listnode * locatenode(linklist head,int key)
{ listnode * p=head–>next;
while( p && p –>data!=key)
p=p –>next;
return p;
}
2.3 线性表的链式表示和实现
2、按值查找
按值查找是在链表中,查找是否有结点值等于给定值
key的结点,若有的话,则返回首次找到的其值为key的结
点的存储位置;否则返回NULL。查找过程从开始结点出发,
顺着链表逐个将结点的值和给定值key作比较。
三、插入运算
插入运算是将值为x的新结点插入到表中ai-1与ai结点之间。因
此,必须首先找到ai-1的存储位置p,然后生成一个数据域为x的新
结点*q,并令结点*p的指针域指向q,*q的指针域指向结点ai。从
而实现三个结点ai-1,x和ai之间的逻辑关系的变化。
2.3 线性表的链式表示和实现
p
a
i
a
i-1
q
x
2.3 线性表的链式表示和实现
p
a
i
a
i-1
x
1. q->next = p->next
2.
p->next = q
p
a
i
a
i-1
q
x
1. q->next = p->next
q
具体算法如下:
void insertnode(linklist head,datatype x,int i)
{ listnode * p,*q;
p=getnode(head,i-1);
if(p==NULL)
error( “position error”);
q=(listnode *)malloc(sizeof(listnode));
q–>data=x;
q–>next=p–>next;
p–>next=q;
}
2.3 线性表的链式表示和实现
演示
Listnode * getnode(linklist head,int pos)
{ int j;
listnode * p;
p=head; j=0;
while(p–>next && j<pos){
p=p –>next; j++;
}
if (pos==j) return p;
else return NULL;
}
2.3 线性表的链式表示和实现
设链表的长度为n,合法的插入位置是1<=i<=n+1。注意
当i=1时,getnode找到的是头结点,当 i=n+1时,getnode
找到的是结点an。因此,用i-1做实参调用getnode时可完成
插入位置的合法性检查。
四、删除运算
删除运算是将表的第i个结点删去。必须首先找到ai-1的存储位
置p。然后令p–
>next指向ai的直接后继结点,即把ai从链上摘下。
2.3 线性表的链式表示和实现
p
a
i
a
i-1
a
i+1
p
a
i
a
i-1
a
i+1
q
q = p->next
2.3 线性表的链式表示和实现
p
a
i
a
i-1
a
i+1
q
p->next = q->next
p
a
i
a
i-1
a
i+1
q
free(q)
p
a
i-1
a
i+1
free(q)
之后
void deletelist(linklist head, int i)
{ listnode * p, *q;
p=getnode(head,i-1);
if(p= =NULL || p –>next= =NULL)
return ERROR;
q=p–>next;
p–>next=q–>next;
free( q ) ;
}
2.3 线性表的链式表示和实现
设单链表的长度为
n,则删去第i个结点仅
当1<=i<=n时是合法的。
注意,当i=n+1时,虽
然被删结点不存在,但
其前趋结点却存在,它
是终端结点。因此被删
结点的直接前趋*p存在
并不意味着被删结点就
一定存在,仅当*p存在
且*p不是终端结点(即
p!=NULL&&p –
>next!=NU
LL)时,才能确定被删
结点存在。 从上面的讨论可以看出,链表上实
现插入和删除运算,无须移动结点,仅
需修改指针。
演示
循环链表:在单链表中,将终端结点的指针域NULL改为指向表头结点的
或开始结点,就得到了单链形式的循环链表,并简单称为单循环链表。
为了使空表和非空表的处理一致,循环链表中也可设置一个头结
点。这样,空循环链表仅有一个自成循环的头结点表示。如下图所示:
2.3 线性表的链式表示和实现
2.3.2 循环链表
H a1 a2
H
由于循环链表中没有NULL指针,故涉
及遍历操作时,其终止条件就不再像非循
环链表那样判断p或p—
>next是否为空,
而是判断它们是否等于某一指定指针,如
头指针等。
an
2.3 线性表的链式表示和实现
例:求线性表中元素的个数。
int ListLength1(linklist head) // 单链表
{ int j;
listnode *p;
p=head; j=0;
while(p->next!=NULL) { j++; p=p->next; }
return(j);
}
int ListLength2(linklist head) // 循环链表
{ int j;
listnode *p;
p=head; j=0;
while(p->next!=head) { j++; p=p->next; }
return(j);
}
双向链表(Double linked
list):在单链表的每个结点里
再增加一个指向其直接前趋的
指针域prior。这样就形成的链
表中有两个方向不同的链,故
称为双向链表。
2.3 线性表的链式表示和实现
2.3.3 双向链表
typedef struct dlistnode{
datatype data;
struc dlistnode *prior,*next;
}dlistnode;
typedef dlistnode * dlinklist;
dlinklist head;
a1 a2
H
H
an
和单链表类似,双链表一般也是由头指针唯一确定的,增加头
结点也能使双链表上的某些运算变得方便,将头结点和尾结点链接
起来也能构成循环链表,并称之为双向链表。
设指针p指向某一结点,则双向链表结构的对称性可用下式描述:
p->prior->next = p = p->next->prior
即结点*p的存储位置既存放在其前趋结点*(p->prior)的直接
后继指针域中,也存放在它的后继结点*(p->next)的直接前趋指针
域中。
注意:与单链表的插入和删除操作不同的是,在双链表中插入
和删除必须同时修改两个方向上的指针。
2.3 线性表的链式表示和实现
void dinsert(dlistnode *p,datatype x) //在p指向的元素前插入x
{ dlistnode *q=malloc(sizeof(dlistnode));
q->data=x;
q->prior=p->prior;
q->next=p;
p->prior->next=q;
p->prior=q;
}
2.3 线性表的链式表示和实现
双向链表的前插操作算法如下:
void ddelete(dlistnode *p)
{ p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
}
双向链表的删除操作算法如下:
2.4 一元多项式的表示及相加
基本形式:采用顺序存储的线性表表示,表中每个元素表示系数,
指数隐含于下标中。
若以P=(P
0
,P
1
,
…
P
n
)
表示
P
n
(X
)=P
0
+P
1
X
1
+
…
+
P
n
X
n
Q=(Q
0
,Q
1
,
…
Q
m
)
表示
Q
m
(X
)=Q
0
+Q
1
X
1
+
…
+
Q
m
X
m
则和可以表示为
P+Q=(P
0
+Q
0
,P
1
+Q
1
,
…
P
m
+Q
m
,P
m+1
,
…
P
n
) (m<n)
改进形式:多项式的系数和指数均存放在元素中,采用链表表示。
若
P
n
(x
)=P
1
X
e1
+P
2
X
e2
+
…
+
P
m
X
em
其中
P
i
0 i=1,2,
…
m
0
e
1
e
2
…
e
m
=n
则可以表示为
P=((P
1
,e
1
), (P
2
,e
2
),
…
(
P
m
,e
m
))
一、一元多项式的表示
2.4 一元多项式的表示及相加
typedef struct {
float coef;
int expn;
} term, ElemType;
·
多项式:
PA = 7+3x+9x
8
+5x
17
PB = 8x+22x
7
-9x
8
typedef struct Lnode {
ElemType data;
struct Lnode * next;
} Lnode, * LinkList;
PA
7 0
3 1
9 8
5 17
PB
8 1
22 7
-9 8
∧
∧
coef
expn
next
二、一元多项式的相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
2.4 一元多项式的表示及相加
7 0
3 1
9 8
5 17
8 1
22 7
-9 8
∧
∧
qb
qa
PA
PB
2.4 一元多项式的表示及相加
7 0
3 1
9 8
5 17
8 1
22 7
-9 8
∧
∧
qb
qa
ha
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PA
PB
7 0
11 1
9 8
5 17
8 1
22 7
-9 8
∧
∧
qb
qa
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PA
PB
7 0
11 1
9 8
5 17
22 7
-9 8
∧
∧
qb
qa
ha
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PA
PB
PA
7 0
11 1
9 8
5 17
22 7
-9 8
∧
∧
qb
qa
ha
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PB
PA
7 0
11 1
5 17
PB
22 7
∧
qb
qa
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
PA
7 0
11 1
5 17
PB
22 7
∧
qb
ha
2.4 一元多项式的表示及相加
·
如:PA = 7+3x+9x
8
+5x
17
与
PB = 8x+22x
7
-9x
8
相加;
结果保留在
PA
的单链表之中。
·
算法:
1
)指数相等:若系数之和为零,则两结点都删除。qa
、qb
后移。
否则只删
qb
->
,修改
qa
->
系数为两者之和。
qa
、qb
后移。
2
)qa
->
指数小:
qa
后移。
3
)qa
->
指数大:
将
qb
->
插入在
qa
->
之前。插入之后,qb
后移。
注意:
把第二个多项式的多余部分,插入在
qa
->
之后。
void
AddPolyn( polynomial &
Pa, polynomial &
Pb)
{ ha = Gethead ( Pa ); hb = Gethead ( Pb );
qa = NextPos (Pa, ha ); qb = NextPos (Pb,hb );
while (
qa && qb)
{ a = GetCurElem(qa); b = GetCurElem(qb):
switch
( cmp( a,b) )
{ -1: ha = qa; qa = NextPos ( Pa,ha ); break; //
多项式
PA
中的当前结点的指数值小
0: sum = a.coef + b.coef; //
两个多项式中的当前结点的指数值相同
if ( sum != 0.0 ) { SetCurElem( qa, sum); ha = qa; }
else { DelFirst( ha, qa); FreeNode (qa); }
DelFirst( hb, qb); FreeNode (qb);
qb = NextPos ( Pb,hb ); qa = NextPos ( Pa,ha ); break;
1: DelFirst( hb, qb); InsFirst (ha,qb); qb = NextPos ( Pb,hb ); break;
} //
多项式
PB
中的当前结点的指数值小
}
if ( !ListEmpty(Pb) ) Append( Pa, qb); // PB 不空连接它的剩余部分
FreeNode(Pb);
} // Addpolyn
void
AddPolyn( polynomial &
Pa, polynomial &
Pb)
{ ha = Pa; hb = Pb;
qa = ha->next; qb = hb->next;
while (
qa && qb)
{ a = qa->data; b = qb->data:
switch
( cmp( a.expn,b.expn) )
{ -1: ha = qa; qa = ha->next; break; //
多项式
PA
中的当前结点的指数值小
0: sum = a.coef + b.coef; //
两个多项式中的当前结点的指数值相同
if ( sum != 0.0 ) { qa->data.coef=sum; ha = qa; }
else { ha->next= qa->next; free(qa); }
hb->next=qb->next; free (qb);
qb = hb->next; qa = ha->next; break;
1: hb->next=qb->next; qb->next=qa; ha->next=qb; ha=qb; qb = hb; break;
} //
多项式
PB
中的当前结点的指数值小
}
if ( qb!=Null) pa->next=qb; // PB
不空连接它的剩余部分
free(Pb);
} // Addpolyn