資料結構-線性表- 01 “兩個有序連結串列序列的合併” 問題
阿新 • • 發佈:2018-11-10
題目要求:
本題要求實現一個函式,將兩個連結串列表示的遞增整數序列合併為一個非遞減的整數序列。
函式介面定義:
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
分析:
動態連結串列對比:
為了實現將兩個連結串列按照一定順序動態拼接到一起,需要不斷的比較兩個連結串列節點資料域中元素大小,然後用一個新的連結串列去接資料。
實現過程和程式碼如下:
// // main.cpp // Node // // Created by Hao Wang on 2018/11/7. // Copyright © 2018年 Hao Wang. All rights reserved. // #include <iostream> #include <stdlib.h> using namespace std; 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); free(L1); free(L2); return 0; } /* 你的程式碼將被嵌在這裡 */ List Read() { int n; cout << "Input a integer number n"<<endl; cin >>n; List L=(List)malloc(sizeof(PtrToNode)); //申請一個頭結點,注意:使用完之後要free掉這部分堆空間,防止記憶體洩漏 L->Next = NULL; //頭指標為空 if( n!= 0) //當n不是0時,迴圈讀入資料並寫入連結串列 { List r=L; //r是一箇中間變數的節點 for(int i=0;i<n;i++) { List p=(List)malloc(sizeof(struct Node)); //同上,free() cin>>p->Data; //尾插法 r->Next = p; r = p; } r->Next = NULL; } return L; //開闢的空間需要釋放 } void Print( List L ) //Print 函式 { List p=L->Next; if(p) { List r; r = L; while(r->Next) { r = r->Next; cout<<r->Data<<endl; } } else { cout<<"NULL"<<endl; } } List Merge(List L1, List L2) //Merge函式,執行主操作,即合併兩個連結串列 { List temp_1, temp_2, NewList, work; temp_1 = L1 -> Next; /*這裡因為L1是一個帶頭結點的連結串列,頭結點裡沒有任何資料, 只有指向下一個節點的指標,所以讓工作指標直接指向那個有意義的節點*/ temp_2 = L2 -> Next; NewList = (List)malloc(sizeof(struct Node)); NewList -> Next = NULL; //為儲存結果的連結串列申請一個空的頭指標 work = NewList; //將NewList定義為操作連結串列 while(temp_1 != NULL && temp_2 != NULL) // 讀取連結串列的資料,直至連結串列結尾 { if(temp_1 -> Data <= temp_2 -> Data) //比較兩個連結串列節點的資料域數值大小,取小值至工作連結串列 { work -> Next = temp_1; work = work -> Next; temp_1 = temp_1 -> Next; //work連結串列指向下一個節點,迴圈操作 } else { work -> Next = temp_2; work = work -> Next; temp_2 = temp_2 -> Next; //同上 } } //退出迴圈則必須有一個連結串列為空,那麼直接把不為空的那個連結串列整體移動到新連結串列中即可。 /*Case 1 : 當list1先跑完迴圈,觸發temp_1 == NULL ,則此時list2的節點數一定是大於等於list1,因此, 工作連結串列的next指標就可以指向list2的節點,即將剩餘(list2可能剩餘0)的連結串列整體連結在工作z連結串列的後面*/ work -> Next = temp_1 ? temp_1 : temp_2; //temp_1為空,work->Next = temp_2 L1 -> Next = NULL; //題目要求空連結串列輸出NULL,由於L1和L2連結串列已經被搬空了,故應當置NULL L2 -> Next = NULL; return NewList; }
執行結果:
總結:
1.每個連結串列都有一個頭節點,該頭節點資料無意義,只有指向下一節點的指標Next有效。
2.List L=...malloc...代表了一個連結串列,L為指向頭節點的指標。注意釋放掉開闢的記憶體空間。
3.通常對連結串列的操作通過新建立一個指標p=L來操作連結串列的增刪查詢(頭指標代表一個連結串列,其值不能改變)。