目录
例3:建立一个带头结点的线性链表,用以存放输入的二进制数,链表中每个结点的data域存放一个二进制位。并在此链表上实现对二进制数加1的运算 。
一 应用
一元多项式的表示及相加
★ 一元多项式可按升幂的形式写成:
Pn(x) = p0+p1xe1+p2xe2+…+pnxen,
★ 其中ei为第i项的指数,pi是指数ei的项的系数,(且1≤e1≤e2≤…≤en)
★ 假设Qm(x)是一个一元多项式,则它也可以用一个线性表Q来表示。即: Q= (q0,q1,q2, …,qm )
★ 若假设m<n,则两个多项式相加的结果Rn(x)= Pn(x) + Qm(x),也可以用线性表R来表示:
R=(p0+q0,p1+ q1,,p2+ q2,…,pm+ qm ,pm+1,…,pn)
一元多项式的顺序存储表示
一元多项式的顺序存储表示1
一元多项式Pn(x)的顺序表示有两种。
法1、只存储该一元多项式各项的系数,每个系数所对应的指数项则隐含在存储系数的顺序表的下标中。即p[0]存系数p0,对应为x0的系数,p[1]存系数p1,对应为x1的系数,……p[n]存系数pn,对应为xn的系数。采用这种存储方法使得多项式的相加运算的算法定义十分简单,只需将下标相同的单元的内容相加即可。适合于存储表示非零系数多的多项式。
法2、采用只存储非零项的方法,此时每个非零项需要存储:非零项系数和非零项指数。适合存储表示非零项少的多项式。
一元多项式的链式存储表示
在链式存储中,对一元多项式只存非零项,则该多项式中每一非零项由两部分构成(指数项和系数项),用单链表存储表示的结点结构为:
用单链表存储多项式的结点结构如下:
struct Polynode
{
int coef;
int exp;
Polynode *next;
} Polynode , * Polylist;
建立一元多项式链式存储的算法
【算法思想】通过键盘输入一组多项式的系数和指数,用尾插法建立一元多项式的链表。以输入系数0为结束标志,并约定建立多项式链表时,总是按指数从小到大的顺序排列。
【算法描述】:
Polylist polycreate()
{ Polynode *head, *rear, *s; int c,e;
rear=head =(Polynode *)malloc(sizeof(Polynode));
/*建立多项式的头结点, rear 始终指向单链表的尾*/
scanf(“%d,%d”,&c,&e);/*键入多项式的系数和指数项*/
while(c!=0) /*若c=0,则代表多项式的输入结束*/
{ s=(Polynode*)malloc(sizeof(Polynode)); /*申请新的结点*/
s->coef=c ; s->exp=e ;rear->next=s ; /*在当前表尾做插入*/
rear=s; scanf(“%d,%d”,&c,&e); }
rear->next=NULL;/*将表的最后一个结点的next置NULL,以示表结束*/
return(head);}
一元多项式的单链表表示示意图
两个一元多项式相加
运算规则:两个多项式中所有指数相同的项的对应系数相加,若和不为零,则构成“和多项式”中的一项;所有指数不相同的项均复抄到“和多项式”中。算法实现,算法演示
若p->exp< q->exp,则结点p所指的结点应 是“和多项式”中的一项,令指针p后移;
若p->exp>q->exp,则结点q所指的结点应是“和多项式”中的一项,将结点q插入在结点p之前,且令指针q在原来的链表上后移;
若p->exp=q->exp,则将两个结点中的系数相加,当和不为零时修改结点p的系数域,释放q结点;若和为零,则和多项式中无此项,从A中删去p结点,同时释放p和q结点。
两个多项式相加算法实现
void polyadd(Polylist polya;Polylist polyb)
{
……/* p和q分别指向polya和polyb链表中的当前计算结点*/
……/* pre指向和多项式链表中的尾结点*/
while p!=NULL && q!=NULL)
{ if (p->exp< q->exp)
{ …/*将p结点加入到和多项式中*/}
else if ( p->exp= =q->exp)
{ …/*若指数相等,则相应的系数相加。若系数为0则删除p,q节点*/}
else{ …/*将q结点加入到和多项式中*/}
}
…../*将多项式polya或polyb中剩余的结点加入到和多项式中*/
}
二 典型题例
例1:已知顺序表L中的数据元素类型为int。设计算法将其调整为左右两部分,左边的元素(即排在前面的)均为奇数,右边所有元素(即排在后面的)均为偶数,并要求算法的时间复杂度为O(n),空间复杂度为O(1)。
问题分析:
初见此题,可能会想到额外申请1个顺序表空间,之后依次从顺序表L中选择奇数放入新表前部分,选择偶数放在新表的后半部分。但是题目要求空间复杂度为O(1),很显然上述方法是不可行的。既然要求空间复杂度为O(1),说明只能借助1个辅助空间。分析题目要求,其实我们只需要将位于表左半部分的偶数与位于表右半部分的奇数通过一个辅助变量进行交换即可,为此我们可以设置两个位置指示器i和j,i初值为0,j初值为L->last,当L->elem[i]为偶数,L->elem[j]为奇数时,则将L->elem[i] 与L->elem[j]交换;否则,L->elem[i]为奇数,i++, L->elem[j]为偶数,j++。这样既可以保证算法的时间复杂度为O(n),亦可保证空间复杂度为O(1)。
算法描述:
AdjustSqlist(SeqList *L) /*
{int i=0,j=L->last;
while(i<j)
{ while(L->elem[i]%2!=0)
i++; /*从表的左半部分开始检测,若为奇数,则i加1,直到找到偶数为止*/
while(L->elem[j]%2= =0)
j--;/* 从表的右半部分开始检测,若为偶数,则j减1,直到找到奇数为止*/
if(i<j)
{t= L->elem[i];
L->elem[i]= L->elem[j];
L->elem[j]=t;}/*交换*/
}
}/*end of AdjustSqlist*/
例2:算法实现带头结点单链表的就地逆置问题。
【问题分析】逆置就是使得表中内容由原来的(a1,a2,…,ai-1,ai,ai+1, …,an)变为(an,an-1,…,ai+1,ai,ai-1, …,a1)。就地逆置就是不需要额外申请结点空间,只需要利用原有的表中的节点空间。若对顺序表中的元素进行逆置,可以借助于“交换”前后相应元素;对单链表中的元素进行逆置,则不能按“交换”思路,因为对于链表中第i个结点需要顺链查找第n-i+1(链表长度为n)个结点,逆置链表的时间复杂度将达O(n2)。
算法思路:逆置后的单链表初始为空,表中的结点不是新生成的,而是从原链表中依次“删除”,再逐个头插入到逆置表中(类同算法2.5头查法创建链表)。设逆置链表的初态为空表,“删除”已知链表中的第一个结点,然后将它“插入”到逆置链表的“表头”,即使它成为逆置链表中“新”的第一个结点,如此循环,直至原链表为空表止。
根据单链表的特点,通过头指针L我们可以顺着每个结点的next域,依次访问到a1,a2,a3…an-1,an;2)我们可以借鉴前面讲到过的头插入法建链表的方法,因为头插入法建链表又称为逆序建表法3)唯一不同的是,我们不需要重新申请结点空间,而只需要从原有单链表上依次“摘下”结点,之后插入到单链表头结点和表中第一个结点之间即可。如图所示
【算法描述】
void ReverseList(LinkList L)
/*逆置带头结点的单链表L */
{ p=L->next; /* P为原链表的当前处理结点*/
L->next=NULL; /*逆置单链表初始为空表*/
while(p!=NULL) /*当原链表未处理完*/
{ q=p->next; /*q指针保留原链表当前处理结点的下一个结点*/
p->next=L->next; L->next=p; /*将当前处理结点p插入到逆置表L的表头*/
p=q; /*p指向下一个待插入的结点*/
} /*end of while*/
}/*end of ReverstList*/
【思考】已知一个带头结点的单链表L,设计算法实现:以表中第一个元素作为标准,将单链表中所有值小于第一个元素的结点均放在第一个元素结点之前,所有值大于第一个元素的结点均放在第一个元素结点之后。(提示:此题可以利用头插法)
例3:
建立一个带头结点的线性链表,用以存放输入的二进制数,链表中每个结点的data域存放一个二进制位。并在此链表上实现对二进制数加1的运算 。
【问题分析】
①建链表:带二进制数可用带头结点的单链表存储,第一个结点存储二进制数的最高位,依次存储,最后一个结点存储二进制数的最低位。
②二进制加法规则:实现二进制数加1运算,方向从低位往高位找到第一个值为0的位,从该位开始,对后面所有低位进行求反运算。
③链表实现二进制加1时,从高位往低位与运算方向正好相反,从第一个结点开始找,找出最后一个值域为0的结点,把该结点值域赋为1,其后所有结点的值域赋为0。
④若在链表中未找到值域为0的结点,则表示该二进制数各位均为1,此时,申请一新结点,值域为1,插入到头结点与原链表的第一个结点之间,成为新链表的第一个结点,其后所有结点的值域赋为0。
void BinAdd(LinkList l) /*用带头结点的单链表L存储二进制数,实现加1运算*/
{ Node *q,*r,*temp,*s;
q=l->next;
r=l;
while(q!=NULL) /*查找最后一个值域为0的结点*/
{if(q->data == 0)
r = q; q = q->next;
}
if (r != l)
r->data = 1; /*将最后一个值域为0的结点的值域赋为1*/
else /*未找到值域为0的结点*/
{ temp = r->next;
s=(Node*)malloc(sizeof(Node)); /*申请新结点*/
s->data=1; /*值域赋为1*/
s->next=temp;
r->next = s; /*插入到头结点之后*/
r = s;
}
r = r->next;
while(r!=NULL) /*将后面的所有结点的值域赋为0*/
{r->data = 0;
r = r->next; }
}/*BinAdd结束*/