1, 先將問題簡化,合並兩個有序鏈表
首先分析合並兩個鏈表的過程。我們的分析從合並兩個鏈表的頭結點開始。鏈表1的頭結點的值小於鏈表2的頭結點的值,因此鏈表1的頭結點將是合並后鏈表的頭結點。如下圖所示。
參考:http://www.cnblogs.com/jason2013/articles/4341153.html
使用遞歸方法,一步步生成頭結點,代碼如下
遞歸的要訣是子問題要和父問題完全一樣,只是規模變小(每次調用,更小的參數值),
1 List merge(List head1, List head2){ 2 List mergeHead = NULL; 3 if (head1 == NULL) { 4 return head2; 5 } 6 if (head2 == NULL){ 7 return head1; 8 } 9 10 if (head1->item < head2->item){ 11 mergeHead = head1; 12 mergeHead->next = merge(head1->next, head2); 13 }else{ 14 mergeHead = head2; 15 mergeHead->next = merge(head1, head2->next); 16 } 17 return mergeHead; 18 }
以上圖為例,該函數調用結束后:
mergeHead: 1->2->3->4->5->6
原始鏈表1 : 1->2->3->4->5->6
原始鏈表2 : 2->3->4->5->6
2, 當有多個鏈表時,考慮分治法每兩個鏈表進行合並.
確定父子函數原型,要想使用遞歸,子問題和父問題必須完全一樣(即返回值,參數類型完全一致)
父問題:多個鏈表
子問題:n/2,...,2個鏈表,1個鏈表
遞歸函數原型List mergeList(int l, int r)
父子問題都是返回一個合並后鏈表,
使用l,r 兩個變量控制問題規模,指定鏈表個數(快速排序,歸並排序都喜歡用這樣的兩個參數)
將多個鏈表存放在全局變量vector<List> lists中,簡化遞歸函數.
第9行代碼復用前面提到的兩個有序鏈表合並
1 List mergeList(int l, int r){ 2 List u, v; 3 int m = (l + r) / 2; 4 if (l == r) { 5 return lists[l]; 6 } 7 u = mergeList(l, m); 8 v = mergeList(m + 1, r); 9 return merge(u, v); 10 }
3, main 函數
1 int main(void) 2 { 3 int size = 8; 4 int num = 5; 5 ListFactory(size, num); 6 for (int i = 0; i < size; i++){ 7 print(lists[i]); 8 } 9 cout << endl; 10 link t = mergeList(0, size-1); 11 print(t); 12 return 0; 13 }
效果
1->9->17->25->33-> 2->10->18->26->34-> 3->11->19->27->35-> 4->12->20->28->36-> 5->13->21->29->37-> 6->14->22->30->38-> 7->15->23->31->39-> 8->16->24->32->40-> 1->2->3->4->5->6->7->8->9->10->11->12->13->14->15->16->17->18->19->20->21->22->23->24->25->26->27->28->29->30->31->32->33->34->35->36->37->38->39->40->
完整程序

1 #include<iostream> 2 #include<string> 3 #include<vector> 4 using std::cin; 5 using std::cout; 6 using std::endl; 7 using std::string; 8 using std::vector; 9 typedef struct node* link; 10 struct node{ 11 int item; 12 link next; 13 }; 14 typedef link List; 15 vector<List> lists; 16 void print(List list){ 17 while (list != NULL){ 18 cout << list->item<< "->"; 19 list = list->next; 20 } 21 cout << endl; 22 } 23 24 vector<List> ListFactory(int num, int size){ 25 for (int k = 1; k <= num; k++){ 26 link t = (link)malloc(sizeof *t); 27 t->item = k; 28 t->next = t; 29 link x = t; 30 for (int m = k + num; m <= num*size; m = m+num){ 31 x = (x->next = (link)malloc(sizeof *x)); 32 x->item = m; 33 x->next = t; 34 } 35 x->next = NULL; 36 lists.push_back(t); 37 } 38 return lists; 39 } 40 41 List merge(List head1, List head2){ 42 List mergeHead = NULL; 43 if (head1 == NULL) { 44 return head2; 45 } 46 if (head2 == NULL){ 47 return head1; 48 } 49 50 if (head1->item < head2->item){ 51 mergeHead = head1; 52 mergeHead->next = merge(head1->next, head2); 53 }else{ 54 mergeHead = head2; 55 mergeHead->next = merge(head1, head2->next); 56 } 57 return mergeHead; 58 } 59 60 List mergeList(int l, int r){ 61 List u, v; 62 int m = (l + r) / 2; 63 if (l == r) { 64 return lists[l]; 65 } 66 u = mergeList(l, m); 67 v = mergeList(m + 1, r); 68 return merge(u, v); 69 } 70 71 int main(void) 72 { 73 int size = 8; 74 int num = 5; 75 ListFactory(size, num); 76 for (int i = 0; i < size; i++){ 77 print(lists[i]); 78 } 79 cout << endl; 80 link t = mergeList(0, size-1); 81 print(t); 82 return 0; 83 }
后記:
雖然解決了多個有序鏈表的合並問題,但在解決一道實際OJ題時還是碰到了比較尷尬的問題,題目描述如下:
02-線性結構1 兩個有序鏈表序列的合並(15 分)
本題要求實現一個函數,將兩個鏈表表示的遞增整數序列合並為一個非遞減的整數序列。
函數接口定義:
List Merge( List L1, List L2 );
其中List
結構定義如下:
typedef struct Node *PtrToNode;
struct Node {
ElementType Data; /* 存儲結點數據 */
PtrToNode Next; /* 指向下一個結點的指針 */
};
typedef PtrToNode List; /* 定義單鏈表類型 */
L1
和L2
是給定的帶頭結點的單鏈表,其結點存儲的數據是遞增有序的;函數Merge
要將L1
和L2
合並為一個非遞減的整數序列。應直接使用原序列中的結點,返回歸並后的鏈表頭指針。
裁判測試程序樣例:
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;
List Read(); /* 細節在此不表 */
void Print( List L ); /* 細節在此不表;空鏈表將輸出NULL */
List Merge( List L1, List L2 );
int main()
{
List L1, L2, L;
L1 = Read();
L2 = Read();
L = Merge(L1, L2);
Print(L);
Print(L1);
Print(L2);
return 0;
}
/* 你的代碼將被嵌在這里 */
輸入樣例:
3
1 3 5
5
2 4 6 8 10
輸出樣例:
1 2 3 4 5 6 8 10
NULL
NULL
L1
和L2
是給定的帶頭結點的單鏈表,最終要返回歸並后的鏈表頭指針。
1 2 3 4 5 6 8 10
NULL
NULL
List Merge(List L1, List L2){ /*struct node head; List mergeHead = &head;*/ //錯誤寫法,函數執行完畢,分配的棧空間會釋放,引發空指針. List mergeHead = (List)malloc(sizeof *mergeHead);// 接點作為返回值,要使用malloc分配到堆空間 List merge = mergeHead; if (L1->Next == NULL){ return L2; } if (L1->Next == NULL){ return L1; } List first1 = L1->Next; List first2 = L2->Next; while (first1 != NULL && first2 != NULL){ if (first1->Data < first2->Data){ merge->Next = first1; first1 = first1->Next; }else{ merge->Next = first2; first2 = first2->Next; } merge = merge->Next; } if (first1 == NULL){ //由於鏈表均為有序,其中一個為空,剩下的數據就無需比較了 merge->Next = first2; } if (first2 == NULL){ merge->Next = first1; } L1->Next = NULL; L2->Next = NULL; return mergeHead; }
結尾加上
L1->Next = NULL; L2->Next = NULL;
基本上可以滿足題目的輸出,但如果並不加呢?
輸出是這樣的
原始鏈表:
1->3->5->7-> 2->4->6->8->
合並后: 1->2->3->4->5->6->7->8->
再次打印原始鏈表: 1->2->3->4->5->6->7->8-> 2->3->4->5->6->7->8->
很明顯的看出,這個Merge是有副作用的,改變了原始鏈表,這不好吧...
版本二:為什么會改變,主要是相加時,沒有新建結點,而是復用了原來的鏈表,把原始鏈表給改了
稍加修改就好

List Merge(List L1, List L2){ /*struct node head; List mergeHead = &head;*/ //錯誤寫法,函數執行完畢,分配的棧空間會釋放,引發空指針. List mergeHead = (List)malloc(sizeof *mergeHead);// 接點作為返回值,要使用malloc分配到堆空間 List merge = mergeHead; if (L1->Next == NULL){ return L2; } if (L1->Next == NULL){ return L1; } List first1 = L1->Next; List first2 = L2->Next; while (first1 != NULL && first2 != NULL){ List temp = (List)malloc(sizeof *temp);// 每次循環創建新結點 if (first1->Data < first2->Data){ temp->Data = first1->Data; merge->Next = temp; first1 = first1->Next; } else{ temp->Data = first2->Data; merge->Next = temp; first2 = first2->Next; } merge = merge->Next; } if (first1 == NULL){ //由於鏈表均為有序,其中一個為空,剩下的數據就無需比較了 merge->Next = first2; } if (first2 == NULL){ merge->Next = first1; } //L1->Next = NULL; //L2->Next = NULL; return mergeHead; }
版本一 版本二對比如下:
測試的完整程序:

#include<iostream> #include<string> #include<vector> using std::cin; using std::cout; using std::endl; using std::string; using std::vector; typedef struct node* link; struct node{ int Data; link Next; }; typedef link List; vector<List> lists; void print(List listHead){ while (listHead->Next != NULL){ listHead = listHead->Next; cout << listHead->Data << "->"; } cout << endl; } List Merge(List L1, List L2){ /*struct node head; List mergeHead = &head;*/ //錯誤寫法,函數執行完畢,分配的棧空間會釋放,引發空指針. List mergeHead = (List)malloc(sizeof *mergeHead);// 接點作為返回值,要使用malloc分配到堆空間 List merge = mergeHead; if (L1->Next == NULL){ return L2; } if (L1->Next == NULL){ return L1; } List first1 = L1->Next; List first2 = L2->Next; while (first1 != NULL && first2 != NULL){ if (first1->Data < first2->Data){ merge->Next = first1; first1 = first1->Next; }else{ merge->Next = first2; first2 = first2->Next; } merge = merge->Next; } if (first1 == NULL){ //由於鏈表均為有序,其中一個為空,剩下的數據就無需比較了 merge->Next = first2; } if (first2 == NULL){ merge->Next = first1; } L1->Next = NULL; L2->Next = NULL; return mergeHead; } int main(void) { link x1, x2,t1,t2; struct node head1, head2; t1 = &head1;//系統隨機分配的內存空間做為頭結點 t1->Next = t1; x1 = t1; for (int i = 1; i <= 7; i = i + 2){ x1 = (x1->Next = (link)malloc(sizeof *x1)); x1->Data = i; x1->Next = t1; } x1->Next = NULL; print(t1); t2 = &head2; t2->Next = t2; x2 = t2; for (int i = 2; i <= 8; i = i + 2){ x2 = (x2->Next) = (link)malloc(sizeof *x2); x2->Data = i; x2->Next = t2; } x2->Next = NULL; print(t2); link t3 = Merge(t1, t2); print(t3); print(t1); print(t2); return 0; }