关闭

视频编程作业-两个有序列表的合并

标签: 链表链表的基本操作C数据结构列表合并
354人阅读 评论(0) 收藏 举报
分类:

算是写了快要一天才写出来的一个题!!有些崩溃啊~

题目描述

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->Data和L2->Data,然后往L上接。
不想重写一些链表的操作,就想着拿之前写的链表的基本操作的代码过来用吧,然后问题就暴露出来了。当时写“按位插入”操作的时候的确是实现了,却没有深入的理解,用到这个题目上,不理解好根本就是用不上的了。(这个题目不需要按位插入,我只是偷懒没有重写新的插入操作(直接链表尾部插入)。)
然后我就开始去好好想,好好理解了。最后也理解了~这里得到一个启示啊,遇到问题一定要赶紧解决,否则总有会因为当时没能解决问题而无法解决手下的问题这种情况,这时候就尴尬了。
之后呢,在编码的过程中,发现了题目里面的几个关键词,这非常重要:L1和L2是给定的带头结点的单链表、应直接使用原序列中的结点尤其是第二点,尤为重要!
第一点注意一下就OK了,第二点让我想了一个上午。
第二点的意思其实“是要求你把原链表上的结点一个一个摘下来,挂到新链表上。”–姥姥原话。
先看看下面两个函数:

List Test(List L){
    List temp,LL;
    temp = L;
    LL = (List)malloc(sizeof(struct Node));
    L = L->Next;
    LL->Next = L;
    while( L ){
        temp->Next = L->Next;
        L = L->Next;
    }
    return LL;
}

void Test2(List L){
    while( L ){
        L = L->Next;
    }
}

这两个函数中,第一个可以改变传进去的实参L,即L在经过这个函数之后会变成NULL;而第二个却不能,即L在经过这个函数之后是不变的。
能看懂吗?(注:只是讨论实参L的前后变化)
虽然这两个函数都是我自己写的,但是事后我仔细想后,又有一些问题困惑了我,所以我先讲一下这两个函数对实参L的作用。

要说这两个函数,先说一下变量的存储。其实呢,在我的理解里面,指针是这样的一个东西:这里写图片描述
这个东西这么理解:L:0x2000表示指针L这个变量名存放的地址,而0x6666才是指针变量里面的内容,这个内容存在0x5000这个内存单元中,也就是指针L是指向0x5000这个地址单元的,而这个单元恰好也是个指针,指向地址为0x6666的这个内存地址单元。 也可以这么理解:x=5;这个式子的表示图是这样的:这里写图片描述这个式子中x就像指针L,它们都只是变量名,只是对应地址单元的一个Nickname而已,这个x变量名存在0x6000这个地址单元中,x的值5存在0x8888这个单元。

好了,下面开始讲这两个函数:

首先,抓住理解这两个函数区别的关键:在C语言中,调用某个函数,传递过去的实参的副本,即参数是按值传递的。


第二个函数是经典的 p = p->next 型,意思不用解释了。那么,按照上面对变量存储的解释,将指针L当作实参传递过去的话,传过取得应该是0x6666这个值,在这里,L只是0x6666这个值的一个Nickname。因为L = L->Next 这个语句没有改变L这个链表任何一个 Next 域里面的内容,所以对实参L是不会有任何改变的;


再来看第一个函数。第一个函数就不同了,它通过temp变量多次修改了头结点的Next域的值,也就是对应于上面的变量存储0x6666这个值。比如说把这个值改成了0x5555,是不是就改变了头结点所指向的内存单元了?^_^ 后面的结点就不用说,跟着就连不上了。所以最后头结点指向了实参L的最后一个结点的Next域–NULL。


AC代码:

其中Print()和Read()是我自己写的,不过估计后台写的也差不多

#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();

    Print(L1);
    Print(L2);

    printf("\n");

    L = Merge(L1, L2);  

    Print(L);
    Print(L1);
    Print(L2);

    return 0;
}

/* Read */
List Read(){
    List L = (List)malloc(sizeof(struct Node));
    L->Next = NULL; //L为头结点 
    List tmp = L;
    ElementType x;
    while(scanf("%d",&x),x!=-1){
        List TempL = (List)malloc(sizeof(struct Node));
        TempL->Data = x;
        TempL->Next = NULL;
        tmp->Next = TempL; 
        tmp = tmp->Next;
    }
    return L;
}

/* Print */
void Print( List L ){
    List Temp = L->Next;
    if( L->Next == NULL ){
        printf("NULL\n");
        return;
    }else{
        while( Temp ){
            printf("%d ",Temp->Data);
            Temp = Temp->Next;
        }
        printf("\n");
    }
}

/* 你的代码将被嵌在这里 */
List Merge(List L1, List L2){
    if( L1->Next == NULL){
        List temp,L;
        L = (List)malloc(sizeof(struct Node));
        temp = L2;
        L2 = L2->Next;
        L->Next = L2;
        while( L2 ){
            temp->Next = L2->Next;
            L2 = L2->Next;
        } 
        return L;
    }else if( L2->Next == NULL){
        List temp,L;
        L = (List)malloc(sizeof(struct Node));
        temp = L1;
        L1 = L1->Next;
        L->Next = L1;
        while( L1 ){
            temp->Next = L1->Next;
            L1 = L1->Next;
        } 
        return L;
    } else{
        List LL = (List)malloc(sizeof(struct Node));
        List LLL = LL;
        List temp1,temp2; 
        temp1 = L1; temp2 = L2;
        L1 = L1->Next; L2 = L2->Next; 
        while( L1 && L2){
            if(L1->Data < L2->Data){
                LL->Next = L1;
                LL = LL->Next;
                temp1->Next = L1->Next;
                L1 = L1->Next;
            }else{
                LL->Next = L2;
                LL = LL->Next;
                temp2->Next = L2->Next;
                L2 = L2->Next;
            }
        }
        while( L1 ){
            LL->Next = L1;
            LL = LL->Next;
            temp1->Next = L1->Next;
            L1 = L1->Next;
        }
        while(L2){
            LL->Next = L2;
            LL = LL->Next;
            temp2->Next = L2->Next;
            L2 = L2->Next;
        }
        return LLL;
    }
} 

代码比较繁琐,Merge()函数几乎每个if/else都写了很多重复的代码,代码优化的时候提出来就好,我这么写只是为了加深那种把结点“摘下来”的思想。


THE END.

1
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

两个有序单链表合并成一个有序单链表的java实现

两个有序单链表合并成一个有序单链表的java实现 -- 仅作为备注, 便于自己回顾.
  • kslinabc
  • kslinabc
  • 2016-10-20 20:01
  • 1649

Java合并两个有序序列算法实现

Java合并两个有序序列算法实现问题描述输入:序列A<a0,a1,a2,...aq,aq+1,aq+2,...,ar>A<a_0,a_1,a_2,...a_q,a_{q+1},a_{q+2},...,a_{r}>,其中a0<a1<...<aq,aq+1<...
  • baidu_22502417
  • baidu_22502417
  • 2015-06-25 16:27
  • 667

《数据结构》链式有序表的合并

链式有序表的合并 算法思想: 设立三个指针,pa,pb,pc,其中pa和pb分别指向La表和LB表当前待比较结点,而pc指向LC白哦当前最后一个结点。 指针的初始值:pa和pb分别指向LA和LB表第一个结点,pc指向空表LC的头结点;然后比较指针a和pb所指向的元素的值,依次从表LA和LB“摘...
  • sungaochao
  • sungaochao
  • 2016-03-09 13:07
  • 1463

C语言实现合并两个有序(从小到大)顺序表为一个顺序表

#include<stdio.h> #include<malloc.h> typedef int ElemType; typedef int status; # define LIST_INIT_SIZE 100 # define OK 1 # define OVERFLO...
  • zzldm
  • zzldm
  • 2017-05-11 17:18
  • 1355

算法题:合并两个有序的链表

题目:已知有两个有序的单链表,其头指针分别为head1和head2,实现将这两个链表合并的函数: Node* ListMerge(Node *head1,Node *head2) 这个算法很像我们排序算法中的归并排序,只能说“很像”,因为思想是一样的,但是这个与归并...
  • JXH_123
  • JXH_123
  • 2014-08-04 16:50
  • 16450

合并两个有序链表,让结果ren仍然有序

转载自: http://www.cnblogs.com/heyonggang/p/3405179.html   题目:输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。例如下图中的链表1和链表2,则合并之后的升序链表如链表3...
  • tangaowen
  • tangaowen
  • 2016-06-06 21:52
  • 985

排序单链表、 并两个有序链表, 合并后依然有序

链表排序,链表的合并
  • My_heart_
  • My_heart_
  • 2016-06-02 22:26
  • 812

如何将两个有序链表合并成一个有序链表。

有两个带头节点的有序链表,如何把他们合成一个有序链表?(假设链表带头节点) 我们通过取出其中一条链表的头节点作为合成后的有序链表的头节点(当然,这里也可以自己重新生成一个节点来作为头节点,但是使用原先的可以节省开销)。 取出后,这既是新链表的head,也是它的tail,因为新链表现在只有这么一个...
  • nitibu
  • nitibu
  • 2015-08-11 17:22
  • 1727

合并两个有序数组为一个有序数组

二、问题: 两个有序数组,合并成一个有序数组,假设第一个数组空间足够容纳两个数组。 分析: 考虑到a数组很大,可以直接在a数组上进行合并,但是要讲究效率。如果单纯从前往后合并,那么效率会非常低,因为a数组后面的数字需要不停的移动。换一种思路,我们采用从后往前合并,首先计算出总长度,设置一个指针...
  • suibianshen2012
  • suibianshen2012
  • 2016-07-06 21:49
  • 1155

【剑指offer】链表相关-合并两个有序链表&递归写法17

#include #include #include //合并两个有序链表 struct ListNode { int m_nValue; ListNode * m_pNext; }; ListNode * CreateLink(int a[],int k) { ListNode * He...
  • panpan639944806
  • panpan639944806
  • 2014-03-04 23:21
  • 1396
    个人资料
    • 访问:35947次
    • 积分:1696
    • 等级:
    • 排名:千里之外
    • 原创:136篇
    • 转载:4篇
    • 译文:0篇
    • 评论:13条
    最新评论