一、問題描述
線性表合並是程序設計語言編譯中的一個最基本的問題,現在有兩個線性表LA和LB,其中的元素都是按照非遞減有序排列的,要將兩個LA和LB歸並為一個新的線性表LC,使得LC中的元素仍然是非遞減有序的。
本實驗的合並方式有兩種。第一種是分別取LA和LB的第一個元素,即各自的最小的元素進行比較,選擇較小的元素加入LC尾部,然后重復以上步驟;當LA表空了或者LB表空了的時候,將另一個表剩下的元素按照順序加入LC的尾部,從而保證LC中元素有序。第二種方式是以LA為母表,將LB中的元素向LA中插入,直到LB表空,得到的新的LA表就是最終需要的LC表。
本實驗采用線性表實現,采用了鏈式表示和順序表示兩種實現方式。根據各自的特點,鏈式表示對應了第二種合並方式,而順序表示對應了第一種合並方式。
二、數據結構——線性表
1、鏈式表示:
鏈式表示的特點是用一組任意的存儲單元存儲線性表的數據元素,每個元素包括兩個域——數據域和指針域。其中數據域是存儲數據信息的域,本實驗中默認所處理的數據元素都是在整型(int)范圍內的數據;指針域中存儲一個指針,指向當前元素的下一個元素的地址。n個結點按照如上關系連接起來,形成一個鏈表,就是線性表的鏈式表示。
由於鏈式表示對於數據的插入、刪除操作比較方便,而查找一個元素的效率比較低下,於是選擇用第二種合並方式,即以LA為母表,將LB中的元素一個一個插入LA中。
首先,每個結點的是一個node型的變量,包含一個int型變量Num和一個node*型的指針變量next。正如上文所描述,Num保存該結點的數值,next保存邏輯上下一個結點的地址。然后定義了一個名叫MyList的類,其中有private型的變量包含線性表自身的基本變量,比如元素個數、首地址等等;還包括public型的線性表的基本操作函數,比如初始化(InitList)、清除(ClearList)、打印(PrintList)等等。
2、順序表示:
順序表示是指的用一段地址連續的區域存儲一個線性表,用物理存儲位置的連續表示線性表的順序關系。這就要求元素之間維持嚴格的物理位置關系,在訪問變得簡單的同時,對線性表的修改操作給線性表的維護帶來了很大的麻煩。
由於順序表示對於數據的操作比較方便,而對線性表的數據進行操作比較麻煩且效率低下,故選擇第一種合並方式,即將LA和LB合並到一個新的線性表LC中。
首先,申請一段連續的空間,當空間不夠時申請一個更大的空間,再把之前的數據搬過去。基本功能與鏈式表示基本相同,實現上略有差別。
三、算法的設計和實現
1、第一種合並方式
(1)建立線性表LA和LB,並讀入數據。
(2)建立空線性表LC。
(3)分別選取LA中未讀過的最小的元素和LB中未讀過的最小的元素,進行比較,將較小的元素加入新的線性表LC中,較大元素視作未讀過的元素。
(4)重復步驟(2)直到LA或者LB的元素都被讀過了。若LA中的元素都被度過了,則將LB中剩下未讀的元素按順序依次添加到LC的尾部;否則,將LA中的剩下未讀的元素按順序依次添加到LC的尾部。
(5)得到的LC就是最終結果。
2、第二種合並方式
(1)建立線性表LA和LB,並讀入數據。
(2)用aIndex標記LA中讀到元素的位置,將其位置的元素與LB中第一個元素進行比較。
(3)若LA中當前元素較小,則aIndex向后移,重復(2)直到LB為空或者LA到末端;否則將LB中的第一個元素插入LA的當前位置,aIndex向后移,刪除LB中的第一個元素,重復(2)直到LB為空或者LA到末端。
(4)若LB為空,則LA已經是最終結果;否則,將LB剩下的元素按順序依次加入LA的末端,得到最終結果。
四、預期結果和實驗中的問題
1、預期結果是程序可以正確地合成兩個線性表LA和LB,如LA={1,1,2},LB={1,2,2,3},則得到的結果應該是{1,1,1,2,2,2,3}。
2、實際運行中曾遇到的問題:
(1)使用順序結構表示線性表的時候,可能會出現初始申請的空間不夠的情況,需要額外申請一個更大的空間,把之前的元素全部復制過去,然后把之前的空間釋放掉。
(2)由於鏈式表示和順序表示各有特點,在MyList類中的函數有些細節不一樣,比如有兩個函數,分別是PriorElem(e,&Pre_e):若e是L中的元素,則返回e的前軀,以及NextElem(e,&Next_e):若e是L中的元素,則返回e的后繼。由於順序表示的訪問上的便利,我多寫了一個函數ElemPos(e,&Pos):若e是L中的元素,則返回e的位置。借助這個函數,之前兩個函數的實現簡單了許多。而這對於鏈式表示是沒有太大的必要的。
(3)我第一次寫順序表示的鏈表時,我的合並函數是每合並一個元素,則將其從原表中刪去。而順序表示在刪除數據上的笨拙之處立刻顯現出來,時間復雜度變到了O(n^2),這是非常不明智的。修改之后的實現時間復雜度為O(n)。下面是修改前的合並函數:
1 void MergeList(MyList La, MyList Lb, MyList &Lc) 2 { 3 int aElem, bElem, cLen = 0; 4 Lc.InitList(); 5 while((!La.ListEmpty()) && (!Lb.ListEmpty())) 6 { 7 La.GetElem(1, aElem); 8 Lb.GetElem(1, bElem); 9 if(aElem <= bElem) 10 { 11 Lc.ListInsert(++cLen, aElem); 12 La.ListDelete(1, aElem); 13 } 14 else 15 { 16 Lc.ListInsert(++cLen, bElem); 17 Lb.ListDelete(1, bElem); 18 } 19 } 20 while(!La.ListEmpty()) 21 { 22 La.ListDelete(1, aElem); 23 Lc.ListInsert(++cLen, aElem); 24 } 25 while(!Lb.ListEmpty()) 26 { 27 Lb.ListDelete(1, bElem); 28 Lc.ListInsert(++cLen, bElem); 29 } 30 }
附:c++源代碼:
1、第一種合並方式,順序表示

1 #include <iostream> 2 #include <cstdio> 3 #include <new> 4 5 using namespace std; 6 7 #define LIST_INIT_SIZE 100 //線性表存儲空間的初始分配量 8 #define LISTINCREMENT 10 //線性表存儲空間的分配增量 9 10 class MyList 11 { 12 private: 13 int *Elem; //存儲空間基址 14 int Len; //當前元素個數 15 int ListSize; //當前分配的儲存容量 16 17 public: 18 void InitList() //構造一個空的線性表 19 { 20 Elem = new int(LIST_INIT_SIZE); 21 Len = 0; 22 ListSize = LIST_INIT_SIZE; 23 } 24 void ClearList() //重置為空表 25 { 26 delete Elem; 27 Len = 0; 28 } 29 bool ListEmpty() //判斷L是否為空表 30 { 31 return Len == 0; 32 } 33 int ListLength() //返回L中數據元素個數 34 { 35 return Len; 36 } 37 bool GetElem(int Pos, int &RetElem) //返回第Pos個元素,出錯返回true 38 { 39 if(Pos < 1 || Pos > Len) 40 { 41 printf("Wrong position!\n"); 42 return true; 43 } 44 RetElem = Elem[Pos - 1]; 45 return false; 46 } 47 //LocateElem(L, e, compare()) //返回L中第一個與e滿足關系compare()的元素的位序,不存在返回0 48 bool ElemPos(int El, int &Pos) //若El是L中的元素,返回e的位置,失敗時返回true 49 { 50 int i; 51 for(i = 0; i < Len; i++) 52 if(El == Elem[i]) 53 break; 54 if(i < Len) 55 { 56 Pos = i + 1; 57 return false; 58 } 59 printf("Cannot find the element!\n"); 60 return true; 61 } 62 bool PriorElem(int El, int &Pre_e) //若El是L中的元素,返回e的前軀,失敗時返回true 63 { 64 int Pos; 65 bool flag = ElemPos(El, Pos); 66 if(flag) 67 { 68 printf("Cannot find the element!\n"); 69 return true; 70 } 71 else 72 { 73 if(Pos == 1) 74 { 75 printf("Cannot find the precursor!\n"); 76 return true; 77 } 78 else 79 Pre_e = Elem[Pos - 2]; 80 } 81 return false; 82 } 83 bool NextElem(int El, int &Next_e) //若El是L中的元素,返回e的后繼,錯誤時返回true 84 { 85 int Pos; 86 bool flag = ElemPos(El, Pos); 87 if(flag) 88 { 89 printf("Cannot find the element!\n"); 90 return true; 91 } 92 else 93 { 94 if(Pos == Len) 95 { 96 printf("Cannot find the successor!\n"); 97 return true; 98 } 99 Next_e = Elem[Pos]; 100 } 101 return false; 102 } 103 bool ListInsert(int Pos, int El) //在Pos位置插入元素El,失敗時返回true 104 { 105 if(Pos < 1 || Pos > Len + 1) 106 { 107 printf("Wrong position!\n"); 108 return true; 109 } 110 if(Len + 1 > ListSize) //當前存儲空間不夠,需要增加分配 111 { 112 int *NewElem = new int(ListSize + LISTINCREMENT); 113 int i; 114 for(i = 0; i < Len; i++) 115 NewElem[i] = Elem[i]; 116 delete Elem; 117 Elem = NewElem; 118 ListSize += LISTINCREMENT; 119 } 120 if(Pos == Len + 1) 121 Elem[Pos - 1] = El; 122 else 123 { 124 int i; 125 for(i = Len; i > Pos - 1; i--) 126 Elem[i] = Elem[i - 1]; 127 Elem[Pos - 1] = El; 128 } 129 Len++; 130 return false; 131 } 132 bool ListDelete(int Pos, int &El) //刪除Pos位置的元素,用El返回,錯誤時返回true 133 { 134 if(Pos < 1 || Pos > Len) 135 { 136 printf("Wrong position!\n"); 137 return true; 138 } 139 El = Elem[Pos - 1]; 140 int i; 141 for(i = Pos - 1; i < Len - 1; i++) 142 Elem[i] = Elem[i + 1]; 143 Len--; 144 return false; 145 } 146 void PrintList() 147 { 148 if(ListEmpty()) 149 { 150 printf("The list is empty!\n"); 151 return ; 152 } 153 int i; 154 for(i = 0; i < Len - 1; i++) 155 printf("%d ", Elem[i]); 156 printf("%d\n", Elem[i]); 157 } 158 }; 159 160 void Read(MyList &L) 161 { 162 int n, i, Elem; 163 L.InitList(); 164 printf("Please input a number n.\n"); 165 scanf("%d", &n); 166 printf("Please input n non-decreasing numbers.\n"); 167 for(i = 1; i <= n; i++) 168 { 169 scanf("%d", &Elem); 170 L.ListInsert(i, Elem); 171 } 172 } 173 174 void MergeList(MyList La, MyList Lb, MyList &Lc) 175 { 176 int aElem, bElem, cLen = 0; 177 Lc.InitList(); 178 while((!La.ListEmpty()) && (!Lb.ListEmpty())) 179 { 180 La.GetElem(1, aElem); 181 Lb.GetElem(1, bElem); 182 if(aElem <= bElem) 183 { 184 Lc.ListInsert(++cLen, aElem); 185 La.ListDelete(1, aElem); 186 } 187 else 188 { 189 Lc.ListInsert(++cLen, bElem); 190 Lb.ListDelete(1, bElem); 191 } 192 } 193 while(!La.ListEmpty()) 194 { 195 La.ListDelete(1, aElem); 196 Lc.ListInsert(++cLen, aElem); 197 } 198 while(!Lb.ListEmpty()) 199 { 200 Lb.ListDelete(1, bElem); 201 Lc.ListInsert(++cLen, bElem); 202 } 203 } 204 205 int main() 206 { 207 MyList La, Lb, Lc; 208 Read(La); 209 Read(Lb); 210 MergeList(La, Lb, Lc); 211 Lc.PrintList(); 212 return 0; 213 }
1、第二種合並方式,鏈式表示

1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 struct node 7 { 8 int Num; 9 node *next; 10 }; 11 12 class MyList 13 { 14 private: 15 int Len; 16 node *pHead; 17 18 public: 19 void InitList()//構造一個空的線性表 20 { 21 Len = 0; 22 pHead = NULL; 23 } 24 void ClearList()//重置為空表 25 { 26 node *Tmp; 27 while(pHead) 28 { 29 Tmp = pHead; 30 pHead = pHead -> next; 31 delete Tmp; 32 } 33 Len = 0; 34 } 35 bool ListEmpty()//判斷L是否為空表 36 { 37 return pHead == NULL; 38 } 39 int ListLength()//返回L中數據元素個數 40 { 41 return Len; 42 } 43 bool GetElem(int Pos, int &e)//返回第Pos個元素,出錯返回true 44 { 45 if(Pos < 1 || Pos > Len) 46 { 47 printf("Wrong position!\n"); 48 return true; 49 } 50 node *Cur = pHead; 51 int Index = 0; 52 while(++Index < Pos && Cur) 53 Cur = Cur -> next; 54 e = Cur -> Num; 55 return false; 56 } 57 //LocateElem(L, e, compare())//返回L中第一個與e滿足關系compare()的元素的位序,不存在返回0 58 bool PriorElem(int e, int &Pre_e)//若e是L中的元素,返回e的前軀,失敗時返回true 59 { 60 if(pHead -> Num == e) 61 { 62 printf("Cannot find the precursor!\n"); 63 return true; 64 } 65 node *Cur = pHead, *Prev; 66 while(Cur) 67 { 68 if(Cur -> Num == e) 69 break; 70 Prev = Cur; 71 Cur = Cur -> next; 72 } 73 if(!Cur) 74 { 75 printf("Cannot find the element!\n"); 76 return true; 77 } 78 Pre_e = Prev -> Num; 79 return false; 80 } 81 bool NextElem(int e, int &Next_e)//若e是L中的元素,返回e的后繼,錯誤時返回true 82 { 83 node *Cur = pHead; 84 while(Cur) 85 { 86 if(Cur -> Num == e) 87 break; 88 Cur = Cur -> next; 89 } 90 if(!Cur) 91 { 92 printf("Cannot find the element!\n"); 93 return true; 94 } 95 Cur = Cur -> next; 96 if(!Cur) 97 { 98 printf("Cannot find the successor!\n"); 99 return true; 100 } 101 Next_e = Cur -> Num; 102 return false; 103 } 104 bool ListInsert(int Pos, int e)//在Pos位置插入元素e,失敗時返回true 105 { 106 if(Pos < 1 || Pos > Len + 1) 107 { 108 printf("Wrong position!\n"); 109 return true; 110 } 111 node *InsElem = new node; 112 if(Pos == 1) 113 { 114 InsElem -> next = pHead; 115 pHead = InsElem; 116 InsElem -> Num = e; 117 } 118 else 119 { 120 node *Cur = pHead; 121 int Index = 0; 122 while(++Index + 1 < Pos && Cur) 123 Cur = Cur -> next; 124 InsElem -> next = Cur -> next; 125 Cur -> next = InsElem; 126 InsElem -> Num = e; 127 } 128 Len++; 129 return false; 130 } 131 bool ListDelete(int Pos, int &e)//刪除Pos位置的元素,用e返回,錯誤時返回true 132 { 133 if(Pos < 1 || Pos > Len) 134 { 135 printf("Wrong position!\n"); 136 return true; 137 } 138 node *DelElem = pHead; 139 if(Pos == 1) 140 { 141 pHead = DelElem -> next; 142 e = DelElem -> Num; 143 delete DelElem; 144 } 145 else 146 { 147 node *Prev; 148 int Index = 0; 149 while(++Index < Pos && DelElem) 150 { 151 Prev = DelElem; 152 DelElem = DelElem -> next; 153 } 154 Prev -> next = DelElem -> next; 155 e = DelElem -> Num; 156 delete DelElem; 157 } 158 Len--; 159 return false; 160 } 161 //ListTraverse(L, visit())//依次對L中的每個數據元素調用函數visit(),一旦visit()失敗,則操作失敗 162 void PrintList() 163 { 164 if(ListEmpty()) 165 { 166 printf("The List is empty!\n"); 167 return ; 168 } 169 node *Cur = pHead; 170 int Index = 0; 171 while(++Index < Len && Cur) 172 { 173 printf("%d ",Cur -> Num); 174 Cur = Cur -> next; 175 } 176 printf("%d\n",Cur -> Num); 177 } 178 bool ElemPrio(node a, node b) 179 { 180 return a.Num < b.Num; 181 } 182 void MergeList(MyList Lb) //把Lb插入L中 183 { 184 int aElem, bElem, aIndex = 0; 185 while(aIndex < Len && (!Lb.ListEmpty())) 186 { 187 GetElem(++aIndex, aElem); 188 Lb.GetElem(1, bElem); 189 if(aElem > bElem) 190 { 191 Lb.ListDelete(1, bElem); 192 ListInsert(aIndex, bElem); 193 } 194 } 195 while(!Lb.ListEmpty()) 196 { 197 Lb.ListDelete(1, bElem); 198 ListInsert(Len + 1, bElem); 199 } 200 } 201 }; 202 203 void Read(MyList &L) 204 { 205 int n, i, Elem; 206 L.InitList(); 207 printf("Please input a number n.\n"); 208 scanf("%d", &n); 209 printf("Please input n non-decreasing numbers.\n"); 210 for(i = 1; i <= n; i++) 211 { 212 scanf("%d", &Elem); 213 L.ListInsert(i, Elem); 214 } 215 } 216 217 int main() 218 { 219 MyList La, Lb; 220 Read(La); 221 Read(Lb); 222 La.MergeList(Lb); 223 La.PrintList(); 224 return 0; 225 }