數據結構習題1:線性表

1.設順序表用數組A[]表示,表中元素存儲在數組下標1~m+n的範圍內,前m個元素遞增有序,後n個元素也遞增有序,設計一個算法,使得整個順序表有序。
(1) 給出算法的基本設計思想。
(2) 根據設計思想,採用C或C++語言描述算法,並在關鍵之處給出註釋。
(3) 說明你所設計的算法的時間複雜度和空間複雜度。前端

解:node

(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) 算法的時間和空間複雜度code

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存儲。設計一個算法將其逆置。要求不能創建新結點,只能經過表中已有結點的從新組合來完成

相關文章
相關標籤/搜索