1. 程式人生 > >資料結構-線性表- 01 “兩個有序連結串列序列的合併” 問題

資料結構-線性表- 01 “兩個有序連結串列序列的合併” 問題

題目要求:

本題要求實現一個函式,將兩個連結串列表示的遞增整數序列合併為一個非遞減的整數序列。

函式介面定義:

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

 

分析:

動態連結串列對比:

為了實現將兩個連結串列按照一定順序動態拼接到一起,需要不斷的比較兩個連結串列節點資料域中元素大小,然後用一個新的連結串列去接資料。

 

實現過程和程式碼如下:

//
//  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來操作連結串列的增刪查詢(頭指標代表一個連結串列,其值不能改變)。