数据结构-线性表- 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来操作链表的增删查找(头指针代表一个链表,其值不能改变)。

猜你喜欢

转载自blog.csdn.net/hhaowang/article/details/83821392