多個有序鏈表的合並


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

后記: 

雖然解決了多個有序鏈表的合並問題,但在解決一道實際OJ題時還是碰到了比較尷尬的問題,題目描述如下:

02-線性結構1 兩個有序鏈表序列的合並(15 分)

本題要求實現一個函數,將兩個鏈表表示的遞增整數序列合並為一個非遞減的整數序列。

函數接口定義:

List Merge( List L1, List L2 );

其中List結構定義如下:

typedef struct Node *PtrToNode;
struct Node {
    ElementType Data; /* 存儲結點數據 */
    PtrToNode   Next; /* 指向下一個結點的指針 */
};
typedef PtrToNode List; /* 定義單鏈表類型 */

L1L2是給定的帶頭結點的單鏈表,其結點存儲的數據是遞增有序的;函數Merge要將L1L2合並為一個非遞減的整數序列。應直接使用原序列中的結點,返回歸並后的鏈表頭指針。

裁判測試程序樣例:

#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
 
該題和前面的差別在於: L1L2是給定的帶頭結點的單鏈表,最終要返回歸並后的鏈表頭指針
而且輸出結果必須是, 原始鏈表要置空
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;
}
View Code

版本一 版本二對比如下:

 

測試的完整程序:

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

 

 
 
 
 
 
 
 
 
 

 


免責聲明!

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



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