[數據結構]線性表合並


一、問題描述

線性表合並是程序設計語言編譯中的一個最基本的問題,現在有兩個線性表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 }
View Code

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 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM