第2章  综合练习

 

3. [题目分析] 题目要求将两个向量逆置,可以先将两个向量分别逆置,再将整个向量逆置。(也可以先将整个向量逆置, 再将两个向量分别逆置)

void reverse(ElemType A[])

//数组A中有m+n个元素,本算法将两个向量逆置,即将前m个元素移至n个元素之后

{ int i;

  for (i=1;i<=m/2;i++)         //将前m个元素逆置

     A[i]<-->A[m-i+1]

for (i=1;i<=n/2;i++)         //将后n个元素逆置

     A[m+i]<-->A[m+n-i+1]

for (i=1;i<=(m+n)/2;i++)     //将前m+n个元素逆置

     A[i]<-->A[m+n-i+1]

}//算法结束

算法讨论】题目中下标从1开始,若用C语言的从0开始,则可写为:

for (i=0;i<m/2;i++)         //将前m个元素逆置

     A[i]<-->A[m-i-1]

for (i=0;i<n/2;i++)         //将后n个元素逆置

     A[m+i]<-->A[m+n-i-1]

for (i=0;i<(m+n)/2;i++)     //将前m+n个元素逆置

     A[i]<-->A[m+n-i-1]

 

4. [题目分析] 计算单链表的长度,即求单链表中元素个数。

  int number(LinkedList la)

   //求不带头结点的单链表的长度

   {i=0;

    p=la;           //p为工作指针

    while (p)

       {i++; p=p->next; }

    return i;

   }//算法结束

 

5. [题目分析] 因为两链表已按元素值递增次序排列,将其合并时,均从第一个结点起进行比较,将小的链入链表中,同时后移链表工作指针。该问题要求结果链表按元素值递减次序排列。故在合并的同时,将链表结点逆置。

LinkedList Union(LinkedList ha,hb)

    ∥ha,hb分别是带头结点的两个单链表的头指针,链表中的元素值按递增序排列,本算法将两链表合并成一个按元素值递减次序排列的单链表。

  { pa=ha->next; pb=hb->next;∥pa,pb分别是链表ha和hb的工作指针

    ha->next=null;           ∥ha作结果链表的头指针,先将结果链表初始化为空。

    while(pa!=null && pb!=null) ∥当两链表均不为空时作

      if(pa->data<=pb->data)

        { r=pa->next;           ∥将pa 的后继结点暂存于r。

          pa->next=ha->next;    ∥将pa结点链于结果表中,同时逆置。

ha->next=pa;

pa=r;                 ∥恢复pa为当前待比较结点。

}

           else

{r=pb->next;∥ 将pb 的后继结点暂存于r。

pb->next=ha->next; ∥将pb结点链于结果表中,同时逆置。

ha->next=pb;

pb=r; ∥恢复pb为当前待比较结点。

}

          if (pa) pb=pa;   //为了下面算法统一,不再单独处理pa

          while(pb!=null)

           {r=pb->next; pb->next=ha->next; ha->next=pb; pb=r; }

          free(hb); return(ha);

        }∥算法Union结束。

算法讨论上面两链表均不为空的表达式也可简写为while(pa && pb),两递增有序表合并成递减有序表时,上述算法是边合并边逆置。也可先合并完,再作链表逆置。后者不如前者优化。算法中最后while语句,哪个表尚未到尾,就将其逆置到结果表中,即将剩余结点依次前插入到结果表的头结点后面。

 

6. [题目分析] 在无序的单链表上,查找最小值结点,要查遍整个链表,初始假定第一结点是最小值结点。当找到最小值结点后,判断数据域的值是否是奇数,若是,则“与其后继结点的值相交换”,即仅仅交换数据域的值,用三个赋值语句即可交换。若与后继结点交换位置,则需交换指针,这时应知道最小值结点的前驱。至于删除后继结点,则通过修改最小值结点的指针域即可。

void  MiniValue(LinkedList la)

la是数据域为正整数且无序的单链表,本算法查找最小值结点且打印。若最小值结点的数值是奇数,

∥则与后继结点值交换;否则,就删除其直接后继结点。

 {p=la->next;   ∥设la是头结点的头指针,p为工作指针。

  pre=p;        ∥pre指向最小值结点,初始假定首元结点值最小。

  while(p->next!=null) ∥p->next是待比较的当前结点。

    {if(p->next->data<pre->data)pre=p->next;

p=p->next; ∥后移指针

}

  printf(“最小值=%d\n”,pre->data);

  if(pre->data%2!=0)   ∥处理奇数

    if(pre->next!=null)∥若该结点没有后继,则不必交换

     {t= pre->data;pre->data=pre->next->data;pre->next->data=t;}∥交换完毕

  else∥处理偶数情况

    if(pre->next!=null)∥若最小值结点是最后一个结点,则无后继

      {u=pre->next;pre->next=u->next;free(u);} ∥释放后继结点空间

 

7. [题目分析]在顺序表中查找指定值, 要从头到尾的查, 若为有序的顺序表,则可采用对分(折半)查找.

   (1) ElemType MiniDelete(SeqList s)

       //在顺序表中删除最小值元素,空出的位置由最后一个元素填补,返回最小值元素

{ElemType min; //min记最小值元素

k=0;          //k记最小值元素下标,先假定第一个元素最小

 for(i=1;i<s.length;i++)

   if (s.data[i]<s.data[k] k=i;

 min=s.data[k];

s.length--;

return min;

}//算法结束

 

(2) 为提高效率,可采用将被删元素空出的位置由最后元素填补的方式解决

void DeleteAllX(SeqList s,ElemType x)

        //在顺序表s中删除所有值为x的元素

     {i=0; j=s.length;   //i,j是数组元素下标

       while (i<j)

         {while(i<j && s.data[i]!=x) i++;  //从左到右查找第一个值为x的元素

while(i<j && s.data[j]==x) j--;  //从右到左查找第一个值不为x的元素

if (i<j) s.data[i++]=s.data[j--];//被删元素空出的位置由最后元素填补

}

           if (s.data[i]==x) s.length=i; else s.length=i+1;  //置线性表的长度

     }//算法结束

 

(3) void DeleteST(SeqList s)

        //在顺序存储的有序表s中删除在给定值在s与t之间的所有元素

     {i=0;

      while(i<s.length && s.data[i]<s) i++;   //从左到右查找第一个值>=s的元素

      if (i==s.length)  {printf(“表中元素都小于s,无s与t间的元素”);exit(0);}

      k=i;  //k是第一个值>=s的元素的下标

while(i<s.length && s.data[i]<=t) i++;   //查找第一个值>t的元素

      for (j=i;j<s.length;j++)  //元素前移,覆盖被删元素

           s.data[k++]=s.data[j]

s.length=k;  //置线性表的长度

     }//算法结束

算法讨论】算法已包括表中最后一个元素也应删除的情况, 请考虑为什么。

 

14.[题目分析]双循环链表的倒置和单链表类似,单链表采用先将第一元素结点与头结点断开, 头结点指针域置空,然后,从第一元素结点起,依次将结点前插到头结点后面. 对双循环链表来说,第二元素结点和前部断开时,要将头结和第一元素结点先构成循环链表,然后从第二元素结点起,依次将结点前插到头结点后面.

void Reverse (DLinkedList DL)

  {p=DL->next->next;     //p为工作指针, 指向第二元素结点(假定循环链表非空)

    DL->prior=DL->next;   //头结点和第一元素结点先构成循环链表,

    DL->next->next=DL;

    While (p!=DL)

        {r=p->next;          //暂存后继

         p->next=DL->next;   //插入到头结点后,要修改4条链

         p->prior=DL;

         DL->next->prior=p;

         DL->next=p;

         p=r; 

         }

  }//算法结束

 

15. 先设定多项式结点结构如下:

     typedef struct node

       {float coef;        //系数

        int exp;           //指数

        struct node *next; //指向后继的指针

       }PolyNode, *Poly;

   void decpoly(Poly py)

       //将稀疏多项式分解为只含奇次幂和偶次幂的两个多项式,利用原结点空间

      {podd=(PolyNode *)malloc(sizeof(PolyNode)); pod->next=pod; //空循环链表

 //建立奇次幂链表的头结点的表头指针, 偶次幂链表的头结点仍用原链表

       pd=podd;     //pd指向奇次幂链表的尾结点

       pn=py;       //pn指向偶次幂链表的尾结点

p=py->next;  //p工作指针

       while (p)

         {r=p->next; //p 的后继结点暂存于r

          if (p->exp % 2 == 0)  //偶次幂链表的结点

             {p->next=pn->next;

              pn->next=p;

              pn=p;

              }

          else               //奇次幂链表的结点

             {p->next=pd->next;

              pd->next=p;

              pd=p;

              }

           p=r;  //取下个结点继续分解

          }//while (p)

          pn->next=py;//将偶次幂链表构成循环链表,即将最后结点的指针域指向原循环链表的头指针py

      }//算法结束

 

17. [题目分析]本题要求将数据域key为整数类型的单链表,按照key 递增的顺序进行就地排序。可以利用直接插入原则把链表整理成递增有序链表。这就要求从第二结点开始,将各结点依次插入到有序链表中。

LinkedList InsertSort(LinkedList la)

   ∥la是带头结点的单链表,本算法按照key 递增的顺序进行就地排序,成为非递减有序链表。

 {if(la->next!=null)∥链表不为空表。

       {p=la->next->next;   ∥p指向第一结点的后继。

la->next->next=null;∥直接插入原则认为第一元素有序,然后从第二元素起依次插入。

while(p!=null)

       {r=p->next;∥暂存p的后继。

        q=la;

        while(q->next!=null && q->next->data<p->data) q=q->next;∥查找插入位置。

        p->next=q->next; ∥将p结点链入链表。

        q->next=p;

        p=r;

}//while

}//if

}//算法结束

 

34.[题目分析] 留下三个链表中公共数据,首先查找两表A和B中公共数据,再去C中找有无该数据。要消除重复元素,应记住前驱,要求时间复杂度O(m+n+p),在查找每个链表时,指针不能回溯。

LinkedList Common(LinkedList A,B,C)

A,B和C是三个带头结点且结点元素值非递减排列的有序表。本算法使A表仅留下三个表均包含的结点,且结点值不重复,释放所有结点。

{pa=A->next;pb=B->next;pc=C->next;∥pa,pb和pc分别是A,B和C三个表的工作指针。

pre=A;∥pre是A表中当前结点的前驱结点的指针。

whilepa && pb && pc)∥当A,B和C表均不空时,查找三表共同元素

  {while(pa && pa->data==pa->next->data)

{u=pa->next;pa=u->next;free(u);}∥删除pa中值相同元素

   if(pa->data<pb->data){u=pa;pa=pa->next;free(u);}∥结点元素值小时,后移指针。

   else if(pa->data>pb->data)pb=pb->next;

        else if (pa && pb) ∥处理A和B表元素值相等的结点

                {while(pc && pc->data<pa->data)pc=pc->next;

                 if(pc)

                    if(pc->data>pa->data)∥pc当前结点值与pa当前结点值不等,pa后移指针。

                         {u=pa;pa=pa->next;free(u);}

                    else  ∥pc,pa和pb对应结点元素值相等。

                     if(pre==A){ pre->next=pa;pre=pa;pa=pa->next} ∥结果表中第一个结点。

else {pre->next=pa;pre=pa;pa=pa->next;}∥将新结点链入A表。

                   pb=pb->next;pc=pc->next;∥链表的工作指针后移。

                  }// if (pa && pb) 处理A和B表元素值相等的结点

}∥while(pa && pb && pc)

ifpa==null)pre->next=null;∥原A表已到尾,置新A表表尾

  else      ∥处理原A表未到尾而B或C到尾的情况

    {pre->next=null;    ∥置A表表尾标记

     while(pa!=null)   ∥删除原A表剩余元素。

       {u=pa;pa=pa->next;free(u);}

}

}

[算法讨论] 算法中A表、B表和C表均从头到尾(严格说B、C中最多一个到尾)遍历一遍,算法时间复杂度符合O(m+n+p)。算法主要由while(pa && pb && pc)控制。三表有一个到尾则结束循环。算法中查到A表与B表和C表的公共元素后,又分三种情况处理:一是三表中第一个公共元素值相等的结点;第二种情况是,尽管不是第一结点,但与前驱结点元素值相同,不能成为结果表中的结点;第三种情况是新结点与前驱结点元素值不同,应链入结果表中,前驱指针也移至当前结点,以便与以后元素值相同的公共结点进行比较。算法最后要给新A表置结尾标记,同时若原A表没到尾,还应释放剩余结点所占的存储空间。