1

1.设顺序表用数组A[]表示,表中元素存储在数组下标1~m+n的范围内,前m个元素递增有序,后n个元素也递增有序,设计一个算法,使得整个顺序表有序。
(1) 给出算法的基本设计思想。
(2) 根据设计思想,采用C或C++语言描述算法,并在关键之处给出注释。
(3) 说明你所设计的算法的时间复杂度和空间复杂度。

解:

(1) 算法的基本设计思想:

将数组A[]中的m+n个元素(假设元素为int型)看成两个顺序表:表L和表R。将数组当前状态看作是起始状态,即此时表LA[]中前m个元素构成,表RA[]中后n个元素构成。要使A[]m+n个元素整体有序,只需将表R中的元素逐个插入表L中的合适位置即可。

插入过程:取表R中的第一个元素A[m+1]存入辅助变量temp中,让temp逐个与A[m],A[m-1],...,A[1]进行比较,当temp<A[j](1≤j≤m)时,将A[j]后移一位,否则将temp存入A[j+1]中。重复上述过程,继续插入A[m+1],A[m+3],...,A[m+n],最终A[]`中元素整体有序。

(2) 算法描述:

void Insert(int A[],int m,int n)
{
    int i,j;
    int temp;                //辅助变量,用来暂存待插入元素
    for(i=m+1;i<=m+n;++i)    //将A[m+1,...,m+n]插入到A[1,..,m]中
    {
        temp=A[i];
        for(j=i-1;j>=1 && temp<A[j];--j)
            A[j+1]=A[j];    //整体元素后移,以便腾出一个位置插入temp
        A[j+1]=temp;
     }
}

(3) 算法的时间和空间复杂度

I. 本题的规模有mn共同决定。取最内层循环中A[j+1]=A[j];这一句作为基本操作,其执行次数在最坏的情况下为R中的每个元素都小于L中的所有元素;又因为R中元素递增有序,则对于每个R中的元素,要将其插入正确位置都必须进行m次移动,R中共有n个元素,因此有;

f(m,n)=mn

由此可见,本算法的时间复杂度为O(mn)

II. 算法额外空间中只有一个变量temp,因此空间复杂度为O(1)


2.已知递增有序的单链表AB(AB中元素个数分别为mn,且AB都带有头结点)分别存储了一个集合,请设计算法,以求出两个AB的差集A-B(仅由在A中出现而不在B中出现的元素所构成的集合)。将差集保存在单链表A中,并保持元素的递增有序性。

(1) 给出算法的基本设计思想。
(2) 根据设计思想,采用C或C++语言描述算法,并在关键之处给出注释。
(3) 说明你所设计的算法的时间复杂度。

解:

(1) 算法的基本设计思想

只需从A中删去AB中共有的元素即可。由于两个链表中元素是递增有序的,所以可以这么做:

设置两个指针pq开始时分别指向AB的开始结点。循环进行以下判断和操作:如果p所指结点的值小于q所指结点的值,则p后移一位;如果q所指结点的值小于p所指结点的值,则q后移一位;如果两者所指结点的值相同,则删除p所指结点。最后pq任一指针为NULL的时候算法结束。

(2) 算法描述

void Difference(LNode *A,LNode *B)
{
    LNode *p=A->next,*q=B->Next;    //p和q分别是链表A和B的工作指针
    LNode *pre=A;                   //pre为A中p所指结点的前驱结点的工作指针
    LNode *r;                       //用来删除相同结点p
    while(p!=NULL && q!=NULL)
    {
        if(p->data < q->data)
        {
            pre=p;
            p=p->next;              //A链表中当前结点指针往后移
        }
       else if(p->data > q->data)
       {
           q=q->next;               //B链表中当前结点指针往后移
       else
       {
           pre->next=p->next;       //处理A、B中元素值相同的结点,应删除
           r=p;
           p=p->next;
           free(r);                //删除结点
        }
    }
}

(3) 算法的时间复杂度分析

由算法描述可知,算法的规模由mn共同确定。算法中有一个单层循环,循环内的所有操作都是常数级的,因此可以赢循环执行的次数作为基本操作执行的次数。可见循环执行的次数即为pq两指针沿着各自链表移动的次数,考虑最坏的情况,即pq都走完了自己所在的链表,循环执行m+n次,因此时间复杂度为O(m+n)


3.设计一个算法,将顺序表中的所有元素逆置

解:

(1) 算法的基本设计思想:

两个变量ij指示顺序表的第一个元素和最后一个元素,交换ij所指元素,然后i向后移动一个位置,j向前移动一个位置,如此循环,直到ij相遇时结束,此时顺序表L中的元素已经逆置。

(2) 算法描述:

void reverse(Sqlist &L)    //L要改变,用引用型
{
    int i,j;
    int temp;              //辅助变量,用于交换
    for(i=1,j=L.length;i<j;++i,--j)    //当i与j相遇时循环结束
    {
        temp=L.data[i];
        L.data[i]=L.data[j];
        L.data[j]=temp;
    }
}

注意:本题中for循环的执行条件要写成i<j,不要写成i!=j。如果数组中元素有偶数个,则ij有可能出现互相跨越状态。若条件设为i!=j,则i继续往右走,j继续往左走,会互相跨越对方,循环不会结束。


4.设计一个算法,从一给定的顺序表L删除下标i~j(i≤j,包括ij)的所有元素,假定都是合法的。

解:

(1) 算法的基本设计思想:

本题是顺序表删除算法的扩展,可以采用如下方法解决:从第j+1个元素开始到最后一个元素为止,用这之间的每个元素去覆盖从这个元素开始往前数j-i+1个元素,即可完成删除i~j的所有元素。

(2) 算法描述:

void delete(Sqlist &L,int i,int j)    //L要改变,用引用型
{
    int k,l;    //l是表长
    l=j-i+1;    
    for(k=j+1;k<L.length;++k)
    {
        L.data[k-1]=L.data[k];        //用第k个元素去覆盖它前边的第l个元素
    }
    L.length-=l;                      //表长改变
}

5.有一个顺序表L,且元素为整型数据,设计一个算法,将L中所有小于表头元素的整数放在前半部分,大于表头元素的整数放在后半部分,数组从下标1开始存储。

解:

(1) 算法的基本设计思想:

先将L的第一个元素存于temp中,然后定义两个整型变量iji从左往右扫描,j从右往左扫描,边扫描边交换。

要注意以下几点:

  1. ij是轮流移动的,即当i找到比2大的元素时,将i所指元素放入j所指位置,i停在当前位置不动,j开始移动。j找到比2小的元素时,将j所指元素放在i所指位置,j停在当前位置不变,i开始移动,如此交替,直到i==j

  2. 每次元素覆盖(如执行L.data[i]=L.data[j];不会造成元素丢失,因为在这之前被覆盖位置的元素已经存入其他位置。

(2) 算法描述:

void move(Sqlist &L)    //L要改变,所以用引用型
{
    int temp;
    int i=1,j=L.length;
    temp=L.data[i];
    while(i<j)
    {    
        /*关键步骤开始*/
        while(i<j&L.data[j]>temp) --j;    //j从右往左扫描,当来到第一个比temp
                                          //小的元素时停止,并且每走一步都要判断
                                          //i是否小于j,这个判断容易遗漏
        if(i<j)                           //检测看是否已满足i<j,这一步同样很重要
        {
            L.data[i]=L.data[j];          //移动元素
            ++i;                          //i右移一位
        }
        
        while(i<j&&L.data[i]<temp) ++i;   //与上面处理类似
        
        if(i<j)                           //与上面处理类似
        {
            L.data[j]=L.data[i];          //与上面处理类似
            --j;
        }
            /*关键步骤结束*/
        }
        L.data[i]=temp;                   //将表首位置放在最终位置
}

6.有一个递增非空单链表,设计一个算法删除域重复的结点。例如,{1,1,2,3,3,3,4,4,7,7,7,9,9,9}经过删除后变成{1,2,3,4,5}

解法一:

(1)算法的基本设计思想:

定义指针p指向起始结点。将p所指的当前结点值域和其直接后继值域比较。如果当前结点值域等于后继结点值域,则删除后继结点;否则p指向后继结点。重复以上过程,直到p 的后继结点为空。

(2)算法描述:

void delsll(LNode *L)
{
    LNode *p=L->next,*q;
    while(p->next!=NULL)
    {
        if(p->data==p->next->data)    //找到重复结点并删除
        {
            q=p->next;
            p->next=q->next;
            free(q);
        }
        else
            p=p->next;
        }
}

解法二:

(1)算法的基本设计思想:

依次将原序列中每个连续相等子序列的第一个元素移动到表的前端,将剩余元素删除即可。

p指向起始结点。qp的后继结点开始扫描,q每来到一个新结点的时候进行检测:当q->data等于p->data的时候,什么都不做……


7.设计一个算法删除单链表L(有头结点)中的一个最小值结点

解:

(1)算法的基本设计思想:

p从头到尾扫描链表,pre指向*p结点的前驱,用minp保存值最小的结点指针,minpre指向minp的前驱。一边扫描,一边比较,将最小值结点放到minp中。

(2)算法描述:

void delminnode(LNode *L)
{
    LNode *pre=L,*p=pre->next,*minp=p,*minpre=pre;
    while(p!=NULL)              //查找最小值结点minp以及前驱结点minpre
    {
        if(p->data<minp->data)
        {
            minp=p;
            minpre=pre;
        }
        pre=p;
        p=pre->next;
    }
    minpre->next=minp->next;    //删除*minp结点
    free(minp);
}

8.有一个线性表,采用带头结点的单链表L存储。设计一个算法将其逆置。要求不能建立新结点,只能通过表中已有结点的重新组合来完成


黄奋斗
163 声望19 粉丝

引用和评论

0 条评论