1. 程式人生 > >資料結構實踐(有的資料結構課後習題答案),紅色是答案

資料結構實踐(有的資料結構課後習題答案),紅色是答案

1章  緒論

習題

1.簡述下列概念:資料、資料元素、資料項、資料物件、資料結構、邏輯結構、儲存結構、抽象資料型別。

2.試舉一個數據結構的例子,敘述其邏輯結構和儲存結構兩方面的含義和相互關係。

3.簡述邏輯結構的四種基本關係並畫出它們的關係圖。

4.儲存結構由哪兩種基本的儲存方法實現?

5選擇題

1)在資料結構中,從邏輯上可以把資料結構分成(   )。

A.動態結構和靜態結構     B.緊湊結構和非緊湊結構

C線性結構和非線性結構   D.內部結構和外部結構

2)與資料元素本身的形式、內容、相對位置、個數無關的是資料的(   )。

A.儲存結構               B

.儲存實現

C.邏輯結構               D.運算實現

3)通常要求同一邏輯結構中的所有資料元素具有相同的特性,這意味著(   )。

   A.資料具有同一特點

B不僅資料元素所包含的資料項的個數要相同,而且對應資料項的型別要一致

C.每個資料元素都一樣

D.資料元素所包含的資料項的個數要相等

4)以下說法正確的是(   )。

A.資料元素是資料的最小單位

B.資料項是資料的基本單位

C.資料結構是帶有結構的各資料項的集合

D一些表面上很不相同的資料可以有相同的邏輯結構

5以下與資料的儲存結構無關的術語是(   )。

A.順序佇列       B. 連結串列        C.

 有序表          D.  鏈棧

6以下資料結構中,(  )是非線性資料結構

A.樹        B.字串       C.隊           D.棧

6.試分析下面各程式段的時間複雜度。

1x=90; y=100; 

while(y>0)

if(x>100)

 {x=x-10;y--;}

else x++;

2for (i=0;  i<n; i++)

for (j=0; j<m; j++)

a[i][j]=0;

3s=0;

     for i=0; i<n; i++)

for(j=0; j<n; j++)

         s+=B[i][j];

sum=s;

4i=1;

     while(i<=n)

        i=i*3;

5x=0;

for(i=1; i<n; i++)

   for (j=1; j<=n-i; j++)

x++;

6x=n; //n>1

y=0;

while(x≥(y+1)* (y+1))

    y++;

1O1

2Om*n

3On2

4Olog3n

5)因為x++共執行了n-1+n-2+……+1= n(n-1)/2,所以執行時間為On2

6O()

2章  線性表

1選擇題

1一個向量第一個元素的儲存地址是100,每個元素的長度為2,則第5個元素的地址是(   )。

A.110            B108         C.100          D.120

2n個結點的順序表中,演算法的時間複雜度是O(1)的操作是(   )。

A訪問第i個結點(1in)和求第i個結點的直接前驅(2in) 

B.在第i個結點後插入一個新結點(1in

C.刪除第i個結點(1in

Dn個結點從小到大排序

3 向一個有127個元素的順序表中插入一個新元素並保持原來順序不變,平均要移動  的元素個數為(   )。

A.8      B63.5        C.63      D.7

4連結儲存的儲存結構所佔儲存空間(   )。

A分兩部分,一部分存放結點值,另一部分存放表示結點間關係的指標

B.只有一部分,存放結點值

C.只有一部分,儲存表示結點間關係的指標

D.分兩部分,一部分存放結點值,另一部分存放結點所佔單元數

5線性表若採用鏈式儲存結構時,要求記憶體中可用儲存單元的地址(   )。

A必須是連續的        B.部分地址必須是連續的

C.一定是不連續的      D連續或不連續都可以

6線性表L在(   )情況下適用於使用鏈式結構實現。

A需經常修改L中的結點值      .需不斷對L進行刪除插入 

C.L中含有大量的結點          D.L中結點結構複雜

7單鏈表的儲存密度(   )。

A大於1        B等於1      C小於1    D不能確定

8)將兩個各有n個元素的有序表歸併成一個有序表,其最少的比較次數是(   )。

An            B.2n-1        C.2n        D.n-1

9在一個長度為n的順序表中,在第i個元素(1≤i≤n+1)之前插入一個新元素時須向後移動(   )個元素。

A.n-i           B.n-i+1       C.n-i-1      D.i

(10) 線性表L=(a1,a2,……an),下列說法正確的是(   )。

A.每個元素都有一個直接前驅和一個直接後繼

B.線性表中至少有一個元素

C.表中諸元素的排列必須是由小到大或由大到小

D.除第一個和最後一個元素外,其餘每個元素都有一個且僅有一個直接前驅和直接後繼。

(11) 若指定有n個元素的向量,則建立一個有序單鏈表的時間複雜性的量級是(   )。

A.O(1)          B.O(n)            CO(n2)          D.O(nlog2n)

(12) 以下說法錯誤的是(   )。

A.求表長、定位這兩種運算在採用順序儲存結構時實現的效率不比採用鏈式儲存結構時實現的效率低

B.順序儲存的線性表可以隨機存取

C.由於順序儲存要求連續的儲存區域,所以在儲存管理上不夠靈活

D.線性表的鏈式儲存結構優於順序儲存結構

(13) 在單鏈表中,要將s所指結點插入到p所指結點之後,其語句應為(   )。

A.s->next=p+1; p->next=s;

B.(*p).next=s; (*s).next=(*p).next;

C.s->next=p->next; p->next=s->next;

D.s->next=p->next; p->next=s;        

 (14) 在雙向連結串列儲存結構中,刪除p所指的結點時須修改指標(   )。

A.p->next->prior=p->prior; p->prior->next=p->next;

B.p->next=p->next->next; p->next->prior=p;

C.p->prior->next=p; p->prior=p->prior->prior;

D.p->prior=p->next->next; p->next=p->prior->prior;

(15) 在雙向迴圈連結串列中,在p指標所指的結點後插入q所指向的新結點,其修改指標的操作是(   )。

A.p->next=q; q->prior=p; p->next->prior=q; q->next=q;

B.p->next=q; p->next->prior=q; q->prior=p; q->next=p->next;

C.q->prior=p; q->next=p->next; p->next->prior=q; p->next=q;

D.q->prior=p; q->next=p->next; p->next=q; p->next->prior=q;

2.演算法設計題

1將兩個遞增的有序連結串列合併為一個遞增的有序連結串列。要求結果連結串列仍使用原來兩個連結串列的儲存空間, 不另外佔用其它的儲存空間。表中不允許有重複的資料。

void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc){

pa=La->next;  pb=Lb->next;

   Lc=pc=La;             //用La的頭結點作為Lc的頭結點 

   while(pa && pb){

      if(pa->data<pb->data){ pc->next=pa;pc=pa;pa=pa->next;}

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

      else {// 相等時取La的元素,刪除Lb的元素

               pc->next=pa;pc=pa;pa=pa->next;

               q=pb->next;delete pb ;pb =q;}

   }

   pc->next=pa?pa:pb;    //插入剩餘段  

   delete Lb;             //釋放Lb的頭結點}  

2將兩個非遞減的有序連結串列合併為一個非遞增的有序連結串列。要求結果連結串列仍使用原來兩個連結串列的儲存空間, 不另外佔用其它的儲存空間。表中允許有重複的資料。

void union(LinkList& La, LinkList& Lb, LinkList& Lc, ) {

  pa = La->next;  pb = Lb->next;              // 初始化

  Lc=pc=La; //用La的頭結點作為Lc的頭結點 

  Lc->next = NULL;

  while ( pa || pb ) {

     if  ( !pa )  { q = pb;  pb = pb->next; }

     else if  ( !pb )  { q = pa;  pa = pa->next; } 

     else if (pa->data <= pb->data )     { q = pa;  pa = pa->next; }

     else  { q = pb;  pb = pb->next; }

     q->next = Lc->next;  Lc->next = q;    // 插入

  }

  delete Lb;             //釋放Lb的頭結點}   

3已知兩個連結串列A和B分別表示兩個集合,其元素遞增排列。請設計演算法求出A與B的交集,並存放於A連結串列中。

void Mix(LinkList& La, LinkList& Lb, LinkList& Lc, ) {

pa=la->next;pb=lb->next;∥設工作指標papb

Lc=pc=La; //用La的頭結點作為Lc的頭結點

while(pa&&pb) 

if(pa->data==pb->data)∥交集併入結果表中。

   { pc->next=pa;pc=pa;pa=pa->next;

 u=pb;pb=pb->next; delete u;}

elseif(pa->data<pb->data) {u=pa;pa=pa->next; delete u;}

else {u=pb; pb=pb->next; delete u;}

while(pa){ u=pa; pa=pa->next; delete u;}∥ 釋放結點空間

while(pb) {u=pb; pb=pb->next; delete u;}∥釋放結點空間

pc->next=null;∥置連結串列尾標記。

delete Lb;   ∥注: 本演算法中也可對B表不作釋放空間的處理

4已知兩個連結串列A和B分別表示兩個集合,其元素遞增排列。請設計演算法求出兩個集合A和B 的差集(即僅由在A中出現而不在B中出現的元素所構成的集合),並以同樣的形式儲存,同時返回該集合的元素個數。

void  DifferenceLinkedList  AB*n

AB均是帶頭結點的遞增有序的單鏈表,分別儲存了一個集合,本演算法求兩集合的差集,儲存於單鏈表A中,*n是結果集合中元素個數,呼叫時為0

{p=A->nextpq分別是連結串列AB的工作指標。

 q=B->next pre=ApreAp所指結點的前驅結點的指標。

whilep!=null && q!=null

ifp->data<q->data{pre=pp=p->next*n++ A連結串列中當前結點指標後移。

elseifp->data>q->dataq=q->nextB連結串列中當前結點指標後移。

else {pre->next=p->next∥處理AB中元素值相同的結點,應刪除。

      u=p p=p->nextdelete u}   ∥刪除結點

5設計演算法將一個帶頭結點的單鏈表A分解為兩個具有相同結構的連結串列B、C,其中B表的結點為A表中值小於零的結點,而C表的結點為A表中值大於零的結點(連結串列A的元素型別為整型,要求B、C表利用A表的結點)。

6設計一個演算法,通過一趟遍歷在單鏈表中確定值最大的結點。

ElemType Max (LinkList L ){

if(L->next==NULL) return NULL;

pmax=L->next; //假定第一個結點中資料具有最大值

p=L->next->next;

while(p != NULL ){//如果下一個結點存在

if(p->data > pmax->data) pmax=p;

p=p->next;

}

return pmax->data;

7設計一個演算法,通過遍歷一趟,將連結串列中所有結點的連結方向逆轉,仍利用原表的儲存空間。

void  inverse(LinkList &L) {

    // 逆置帶頭結點的單鏈表 L

    p=L->next;  L->next=NULL;

    while ( p) {

        q=p->next;    // q指向*p的後繼

        p->next=L->next;

        L->next=p;       // *p插入在頭結點之後

        p = q;

    }

}

8設計一個演算法,刪除遞增有序連結串列中值大於mink且小於maxk的所有元素(mink和maxk是給定的兩個引數,其值可以和表中的元素相同,也可以不同 )。

void delete(LinkList &L, int mink, int maxk) {

   p=L->next; //首元結點

   while (p && p->data<=mink)

      { pre=p;  p=p->next; } //查詢第一個值>mink的結點

   if (p) {

       while (p && p->data<maxk)  p=p->next;

                      // 查詢第一個值 ≥maxk 的結點

      q=pre->next;   pre->next=p;  // 修改指標

      while (q!=p) 

         { s=q->next;  delete q;  q=s; } // 釋放結點空間

   }//if

}

9已知p指向雙向迴圈連結串列中的一個結點,其結點結構為data、prior、next三個域,寫出演算法change(p),交換p所指向的結點和它的字首結點的順序。

知道雙向迴圈連結串列中的一個結點,與前驅交換涉及到四個結點(p結點,前驅結點,前驅的前驅結點,後繼結點)六條鏈。

void  ExchangeLinkedList p

p是雙向迴圈連結串列中的一個結點,本演算法將p所指結點與其前驅結點交換。

{q=p->llink

 q->llink->rlink=pp的前驅的前驅之後繼為p

 p->llink=q->llinkp的前驅指向其前驅的前驅。

 q->rlink=p->rlinkp的前驅的後繼為p的後繼。

 q->llink=pp與其前驅交換

 p->rlink->llink=qp的後繼的前驅指向原p的前驅

 p->rlink=qp的後繼指向其原來的前驅

}∥演算法exchange結束。

10已知長度為n的線性表A採用順序儲存結構,請寫一時間複雜度為O(n)、空間複雜度為O(1)的演算法,該演算法刪除線性表中所有值為item的資料元素。

[題目分析在順序儲存的線性表上刪除元素,通常要涉及到一系列元素的移動(刪第i個元素,第i+1至第n個元素要依次前移)。本題要求刪除線性表中所有值為item的資料元素,並未要求元素間的相對位置不變。因此可以考慮設頭尾兩個指標(i=1j=n),從兩端向中間移動,凡遇到值item的資料元素時,直接將右端元素左移至值為item的資料元素位置。

void  DeleteElemType A[ ]int  n

A是有n個元素的一維陣列,本演算法刪除A中所有值為item的元素。

{i=1j=n;∥設定陣列低、高階指標(下標)。

whilei<j

   {whilei<j && A[i]!=itemi++∥若值不為item,左移指標。

ifi<jwhilei<j && A[j]==itemj--;∥若右端元素值為item,指標左移

ifi<jA[i++]=A[j--]

   }

[演算法討論] 因元素只掃描一趟,演算法時間複雜度為O(n)。刪除元素未使用其它輔助空間,最後線性表中的元素個數是j。

3章  棧和佇列

習題

1.選擇題

1若讓元素12345依次進棧,則出棧次序不可能出現在(  )種情況。

A.54321   B.21543   C.4312   D.23541

2)若已知一個棧的入棧序列是123,…,n,其輸出序列為p1,p2,p3,…,pn,若p1=n,則pi(  )。

    A.i               B.n-i               C.n-i+1            D.不確定

3)陣列Q[n]用來表示一個迴圈佇列,f為當前佇列頭元素的前一位置,r為隊尾元素的位置,假定佇列中元素的個數小於n,計算佇列中元素個數的公式為(  )。

A.r-f             B.(n+f-r)%n       C.n+r-f           D.n+r-f)%n

4)鏈式棧結點為:(data,link),top指向棧頂.若想摘除棧頂結點,並將刪除結點的值儲存到x,則應執行操作(  )。

A.x=top->data;top=top->link;   B.top=top->link;x=top->link;    

C.x=top;top=top->link       D.x=top->link

5設有一個遞迴演算法如下

        int fact(int n) {  //n大於等於0

             if(n<=0) return 1;

             else return n*fact(n-1);        }

則計算fact(n)需要呼叫該函式的次數為(  )。

A. n+1              B. n-1              C. n                  D. n+2

6棧在 (  )中有所應用。

A.遞迴呼叫       B.函式呼叫      C.表示式求值        D.前三個選項都有

7為解決計算機主機與印表機間速度不匹配問題,通常設一個列印資料緩衝區。主機將要輸出的資料依次寫入該緩衝區,而印表機則依次從該緩衝區中取出資料。該緩衝區的邏輯結構應該是(  )。

A.佇列           B.棧            C. 線性表           D.有序表

8設棧S和佇列Q的初始狀態為空,元素e1e2e3e4e5e6依次進入棧S,一個元素出棧後即進入Q,若6個元素出隊的序列是e2e4e3e6e5e1,則棧S的容量至少應該是( )。

A.2             B.3              C.4                D. 6

9在一個具有n個單元的順序棧中,假設以地址高階作為棧底,以top作為棧頂指標,則當作進棧處理時,top的變化為( )。 

A.top不變        B.top=0           C.top--              D.top++

10設計一個判別表示式中左,右括號是否配對出現的演算法,採用( )資料結構最佳。

A.線性表的順序儲存結構              B.佇列     

C. 線性表的鏈式儲存結構     D. 

11用連結方式儲存的佇列,在進行刪除運算時( )。

A. 僅修改頭指標                      B. 僅修改尾指標

C. 頭、尾指標都要修改                D. 頭、尾指標可能都要修改

12迴圈佇列儲存在陣列A[0..m]中,則入隊時的操作為( )。

A. rear=rear+1                       B. rear=(rear+1)%(m-1)

  C. rear=(rear+1)%m                   D. rear=(rear+1)%(m+1) 

13最大容量為n的迴圈佇列,隊尾指標是rear,隊頭是front,則隊空的條件是( )。

  A. (rear+1)%n==front                  B. rear==front                                                          

C.rear+1==front                      D. (rear-l)%n==front

14棧和佇列的共同點是( )。

A. 都是先進先出                       B. 都是先進後出   

C. 只允許在端點處插入和刪除元素       D. 沒有共同點

15一個遞迴演算法必須包括( )。

A. 遞迴部分                           B. 終止條件和遞迴部分

C. 迭代部分                           D. 終止條件和迭代部分

2迴文是指正讀反讀均相同的字元序列,如“abba”和“abdba”均是迴文,但“good”不是迴文。試寫一個演算法判定給定的字元向量是否為迴文。(提示:將一半字元入棧

根據提示,演算法可設計為: //以下為順序棧的儲存結構定義 #define StackSize 100 //假定預分配的棧空間最多為100個元素 typedef char DataType;//假定棧元素的資料型別為字元 typedef struct{  DataType data[StackSize];  int top; }SeqStack;  int IsHuiwen( char *t)  {//判斷t字元向量是否為迴文,若是,返回1,否則返回0   SeqStack s;   int i , len;   char temp;   InitStack( &s);   len=strlen(t); //求向量長度   for ( i=0; i<len/2; i++)//將一半字元入棧    Push( &s, t[i]);   while( !EmptyStack( &s))    {// 每彈出一個字元與相應字元比較     temp=Pop (&s);     if( temp!=S[i])  return 0 ;// 不等則返回0     else i++;    }    return 1 ; // 比較完畢均相等則返回 1  }

3設從鍵盤輸入一整數的序列:a1, a2, a3,…,an,試編寫演算法實現:用棧結構儲存輸入的整數,當ai≠-1時,將ai進棧;當ai=-1時,輸出棧頂整數並出棧。演算法應對異常情況(入棧滿等)給出相應的資訊。

#define maxsize 棧空間容量

void InOutS(int s[maxsize])

      //s是元素為整數的棧,本演算法進行入棧和退棧操作。

   {int top=0;             //top為棧頂指標,定義top=0時為棧空。

for(i=1; i<=n; i++)    //n個整數序列作處理。

     {scanf(“%d”,&x);    //從鍵盤讀入整數序列。

if(x!=-1)           // 讀入的整數不等於-1時入棧。

if(top==maxsize-1){printf(“棧滿\n”);exit(0);}else s[++top]=x; //x入棧。

else   //讀入的整數等於-1時退棧。

       {if(top==0){printf(“棧空\n”);exit(0);} else printf(“出棧元素是%d\n”,s[top--])}}

     }//演算法結束。

4從鍵盤上輸入一個字尾表示式,試編寫演算法計算表示式的值。規定:逆波蘭表示式的長度不超過一行,以$符作為輸入結束,運算元之間用空格分隔,操作符只可能有+、-、*、/四種運算。例如:234 34+2*$。

 [題目分析]逆波蘭表示式(即字尾表示式)求值規則如下:設立運算數棧OPND,對錶達式從左到右掃描(讀入),當表示式中掃描到數時,壓入OPND棧。當掃描到運算子時,從OPND退出兩個數,進行相應運算,結果再壓入OPND棧。這個過程一直進行到讀出表示式結束符$,這時OPND棧中只有一個數,就是結果。

float expr( )

//從鍵盤輸入逆波蘭表示式,以‘$’表示輸入結束,本演算法求逆波蘭式表示式的值。

float OPND[30];   // OPND是運算元棧。

init(OPND);       //兩棧初始化。

float num=0.0;    //數字初始化。

  scanf (“%c”,&x);//x是字元型變數。

while(x!=’$’)

   {switch

      {case0’<=x<=’9’:while((x>=’0’&&x<=’9’)||x==’.’)  //拼數

if(x!=’.’)   //處理整數

{num=num*10+ord(x)-ord(‘0’); scanf(“%c”,&x);}

else           //處理小數部分。

                            {scale=10.0; scanf(“%c”,&x);

while(x>=’0’&&x<=’9’)

                               {num=num+(ord(x)-ord(‘0’)/scale;

                                scale=scale*10;  scanf(“%c”,&x); }

                             }//else

                             push(OPND,num); num=0.0;//數壓入棧,下個數初始化

case x= ’:break;  //遇空格,繼續讀下一個字元。

case x=+’:push(OPND,pop(OPND)+pop(OPND));break;

case x=-:x1=pop(OPND);x2=pop(OPND);push(OPND,x2-x1);break;

case x=*’:push(OPND,pop(OPND)*pop(OPND));break;

case x=/:x1=pop(OPND);x2=pop(OPND);push(OPND,x2/x1);break;

default:       //其它符號不作處理。

     }//結束switch

     scanf(“%c”,&x);//讀入表示式中下一個字元。

   }//結束whilex=$’

  printf(“字尾表示式的值為%f”,pop(OPND));

}//演算法結束。

[演算法討論]假設輸入的字尾表示式是正確的,未作錯誤檢查。演算法中拼數部分是核心。若遇到大於等於‘0’且小於等於‘9’的字元,認為是數。這種字元的序號減去字元‘0’的序號得出數。對於整數,每讀入一個數字字元,前面得到的部分數要乘上10再加新讀入的數得到新的部分數。當讀到小數點,認為數的整數部分已完,要接著處理小數部分。小數部分的數要除以10(或10的冪數)變成十分位,百分位,千分位數等等,與前面部分數相加。在拼數過程中,若遇非數字字元,表示數已拼完,將數壓入棧中,並且將變數num