C语言合并两个带头节点升序排列链表

合并链表,顾名思义,就是将两个按顺序存放数据的链表中的数据合并为用一个链表存储,比如在处理多项式的加减法时就需要将两个多项式的数据进行合并。合并方式有很多种:如果按照存储方式的不同,可以将两个链表的数据分别提取出来生成一个新的链表来存储原先两个链表的数据,还可以将其中一个链表的数据依次插入到另外一个链表的相应位置当中去。在遇到相同数据时可以采取只留下一个数据的方式和两个数据均保留的方式。这些不同点需要到具体的问题中具体分析,但是只是在细节上有一些差别,大体的思路都是一样的,本文主要介绍将一个链表插入到另一个链表的相应位置,插入完成后销毁链表二,且遇到相同数据采用均保留的方式。

我们还是先抛开链表的操作本身,先对两组不同的数据进行合并操作,知道两组按升序排列的数据该如何合并成一组数据,我们来分析这样几组数据(将第二组的数据插入到第一组中):

1   5   9   13   15

2   7   12

对于这组数据我们可以很轻易的说出应该把“2”插入到“1”的前面,应该把“7”插入到“5”的前面... ... 那么我们又是如何得到这个结论的呢,接下来我们来用语言细致的描述一下我们刚才是如何得到这样的结论的:“首先,遍历第一组数据,找到第一个比要插入的数据大的数据的前一个数据“i”,再将要插入的数据插入到“i”的后面”。

上述描述解决了插入数据的一般性情况,包括我们用“比该数据大”这样的话说明了遇到相同数据时该如何处理,但是,问题依然存在,我们来观察下面一组数据:

2   4   7   9

1   3   6   8

我们要将第二组数据插入到第一组数据中,按照之前的说法,找到第一个不比待插入数据小的数据的前一个数据“i”,但是我们发现第一组数据中第一个数据就符合我们的要求,那么它自然没有前一个数据。这里我们给出一个方案:在进行插入工作前,先对两个链表的首元素进行比较,如果链表一的首元素是小于第二个链表的首元素的,那么开始合并工作,如果链表一的首元素不小于第二个链表的首元素,我们先交换两个链表的头结点,使得链表一变成链表二。但是这种方式仅适用于我们当前的情况,因为我们要实现的是将链表二的元素加入到链表一中,之后销毁链表二,这就意味着我们最后只要得到一个链表就可以,只要使得传递过来的链表一在执行完我们的合并链表程序后能够变成合并后的结果链表就好了,因此我们可能要更改传递过来的链表的头结点的值。

根据上面的说法,我们就要在开始进行合并之前先对两个链表的首元素进行比较,如果链表二的首元素比链表一的首元素大,则交换两个链表的控制头,再执行后续的合并工作。

解决了上述问题,还差最后一种情况,我们来观察下面一组数据:

1   3   4   6   7

2   6   9   10  13

我们可以观察到,将第二组数据依次向第一组数据中插入,到了“9”的时候,第一组数据就没有可以插入的位置了,而应该在尾部追加,于是,我们就不能执行之前的操作了,而应该执行另外一项操作,我们具体实现是这样的:

如果我们在第一组数据中找不到比待插入数据大的数据,我们就将待插入数据的后面的数据连同待插入数据本身,追加到第一组数据的末尾。

根据如上分析,我们需要执行的有四种对链表的操作:1、在指定链中找到第一个比目标数据的大的节点的前驱节点; 2、将指定元素插入到指定链表的指定位置; 3、将链表中的制定位置后的所有元素追加到指定链表的末尾; 4、交换两个链表的头结点。

下面我们就分布解决这四种操作(注:A_LINE是我们自己定义的为了方便起见的一个结构体,只有一个int类型的num和一个A_LINE *类型的next成员不具有任何意义,仅仅是为了完成合并链表):

1、在指定链中找到第一个比目标数据的大的节点的前驱节点:

A_LINE *getFirstLocalBiggerThanEle(A_LINE head, A_LINE ele) {
 A_LINE *p;
 A_LINE *q;

 for (p = head.next; p->next != NULL && ele.num >= p->num; p = p->next)
 q = p;
 if (p->next == NULL) {
 return NOT_FOUND;
 }
 return q;
}

我们用A_LINE *p来遍历链表一,用A_LINE *q来实时保存当前节点的前驱节点,链表没有遍历完而且当前元素依然小于等于目标元素,则循环继续。当循环结束后,判断循环结束的原因,如果是因为链表遍历完了而结束的循环,则说明整个链表里都没有比目标节点大的节点,返回NOT_FOUND;若是因为找到了比目标元素大的元素而结束的,那么返回该元素的前驱节点的地址“q”。

2、将指定元素插入到指定链表的指定位置:

void insertEleToLine(A_LINE *local, A_LINE ele) {
 A_LINE *newEle;

 newEle = (A_LINE *)calloc(1, sizeof(A_LINE));
 newEle->next = local->next;
 local->next = newEle;
 newEle->num = ele.num;
}

在第一步中我们已经可以得到需要插入的位置的首地址了,下一步我们就要完成插入了,这对于学过链表的人并不是什么难事,本文不做为重点讲解。

3、将一段数据追加到指定链表的末尾:

void appendLineToLineEnd(A_LINE *line, A_LINE targetLine) {
 A_LINE *tarP;

 for (tarP = targetLine.next; tarP->next != NULL; tarP = tarP->next)
 ;
 tarP->next = line;
}

这里给出的A_LINE *line代表着待插入链表的首元素的首地址,targetLine代表要插入到的链表的头结点。首先先定位目标链表的末节点的首地址,再将待插入链表的末节点的next成员更改为待插入链表的首元素的首地址。

但这里存在着问题,这里的赋值相当于直接把链表二的一部分节点直接放到链表一的末尾,并不是复制出来一份再追加到链表一的末尾,这样就使得链表二的那一部分节点又属于链表一又属于链表二,而链表二在最后是要被释放的,那么它的后几个节点也会被释放。为了防止这样的情况发生,我们必须将链表二中的要插入到链表一中的那一部分节点的前驱节点的next成员赋值为NULL,这样在就相当于将那一部分节点从链表二中删除,但是这样并不会引起内存泄漏,这些节点被连接到了链表一的末尾,因此在释放链表一的时候这些节点依然会被释放。

4、交换两个链表的头结点:

void exchangeLine(A_LINE *head1, A_LINE *head2) {
 A_LINE temp;

 temp = *head1;
 *head1 = *head2;
 *head2 = temp;
}

5、合并链表完整代码:

void margeLine(A_LINE *targetLine, A_LINE *resourceLine) {
 A_LINE *tarP = targetLine->next;
 A_LINE *srcP = resourceLine->next;
 A_LINE *srcPreP;
 A_LINE *local;

 if (tarP->num >= srcP->num) {
 exchangeLine(targetLine, resourceLine);
 }

 for (srcP = srcPreP = resourceLine->next; srcP != NULL; srcP = srcP->next) {
 local = getFirstLocalBiggerThanEle(*targetLine, *srcP);

 if (local != NOT_FOUND) {
 insertEleToLine(local, *srcP);
 }
 else {
 appendLineToLineEnd(srcPreP->next, *targetLine);
 srcPreP->next = NULL;
 destoryLine(resourceLine);
 return;
 }
 srcPreP = srcP;
 }
 destoryLine(resourceLine);
}

最后我们给出整体的可测试的代码:

#include<stdio.h>
#include<malloc.h>

#define NOT_FOUND NULL

typedef struct A_LINE {
 int num;
 struct A_LINE *next;
} A_LINE;

void margeLine(A_LINE *targetLine, A_LINE *resourceLine);
A_LINE *getFirstLocalBiggerThanEle(A_LINE head, A_LINE ele);
void insertEleToLine(A_LINE *local, A_LINE ele);
void destoryLine(A_LINE *head);
void initLine(A_LINE *head);
void showLine(A_LINE head);
void exchangeLine(A_LINE *head1, A_LINE *head2);
void appendLineToLineEnd(A_LINE *line, A_LINE targetLine);

void appendLineToLineEnd(A_LINE *line, A_LINE targetLine) {
 A_LINE *tarP;

 for (tarP = targetLine.next; tarP->next != NULL; tarP = tarP->next)
 ;
 tarP->next = line;
}

void exchangeLine(A_LINE *head1, A_LINE *head2) {
 A_LINE temp;

 temp = *head1;
 *head1 = *head2;
 *head2 = temp;
}

void showLine(A_LINE head) {
 A_LINE *p;

 for (p = head.next; p != NULL; p = p->next) {
 printf("%d ", p->num);
 }
}

void initLine(A_LINE *head) {
 int num;
 A_LINE *p = NULL;

 printf("请输入一个数(-1表示结束):");
 scanf_s("%d", &num);
 while (num != -1) {
 if (head->next == NULL) {
 head->next = (A_LINE *)calloc(1, sizeof(A_LINE));
 (head->next)->num = num;
 p = head->next;
 }
 else {
 p->next = (A_LINE *)calloc(1, sizeof(A_LINE));
 (p->next)->num = num;
 p = p->next;
 }
 printf("请输入一个数(-1表示结束)");
 scanf_s("%d", &num);
 }
}

void destoryLine(A_LINE *head) {
 A_LINE *p = head->next;

 while (NULL != head->next) {
 p = head->next;
 head->next = p->next;
 free(p);
 }
}

void insertEleToLine(A_LINE *local, A_LINE ele) {
 A_LINE *newEle;

 newEle = (A_LINE *)calloc(1, sizeof(A_LINE));
 newEle->next = local->next;
 local->next = newEle;
 newEle->num = ele.num;
}

A_LINE *getFirstLocalBiggerThanEle(A_LINE head, A_LINE ele) {
 A_LINE *p = NULL;
 A_LINE *q = NULL;

 for (p = head.next; p != NULL && ele.num >= p->num; p = p->next)
 q = p;
 if (p == NULL) {
 return NOT_FOUND;
 }
 return q;
}

void margeLine(A_LINE *targetLine, A_LINE *resourceLine) {
 A_LINE *tarP = targetLine->next;
 A_LINE *srcP = resourceLine->next;
 A_LINE *srcPreP;
 A_LINE *local;

 if (tarP->num >= srcP->num) {
 exchangeLine(targetLine, resourceLine);
 }

 for (srcP = srcPreP = resourceLine->next; srcP != NULL; srcP = srcP->next) {
 local = getFirstLocalBiggerThanEle(*targetLine, *srcP);

 if (local != NOT_FOUND) {
 insertEleToLine(local, *srcP);
 }
 else {
 appendLineToLineEnd(srcPreP->next, *targetLine);
 srcPreP->next = NULL;
 destoryLine(resourceLine);
 return;
 }
 srcPreP = srcP;
 }
 destoryLine(resourceLine);
}

void main(void) {
 A_LINE head1 = {0};
 A_LINE head2 = {0};

 printf("录入第一个链表:\n");
 initLine(&head1);
 printf("录入第二个链表\n");
 initLine(&head2);

 margeLine(&head1, &head2);

 printf("链表一:\n");
 showLine(head1);
 printf("链表二:\n");
 showLine(head2);

  destoryLine(&head1);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C语言合并排序及实例代码

    归并排序也称合并排序,其算法思想是将待排序序列分为两部分,依次对分得的两个部分再次使用归并排序,之后再对其进行合并.仅从算法思想上了解归并排序会觉得很抽象,接下来就以对序列A[0], A[l]-, A[n-1]进行升序排列来进行讲解,在此采用自顶向下的实现方法. 操作步骤如下: (1)将所要进行的排序序列分为左右两个部分,如果要进行排序的序列的起始元素下标为first,最后一个元素的下标为last,那么左右两部分之间的临界点下标mid=(first+last)/2,这两部分分别是A[first

  • c语言合并两个已排序数组的示例(c语言数组排序)

    问题:将两个已排序数组合并成一个排序数组 这里先不考虑大数据量的情况(在数据量很大时不知大家有什么好的思路或方法?),只做简单数组的处理. 简单代码如下: 说明:之所以把merge函数定义成返回数组长度,是因为后续会有重复数据合并功能的merge版本,考虑到接口一致性. 复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#include <string.h> int merge(int* ar1, int len1, int

  • 合并排序(C语言实现)

    其基本模式如下: 分解:把一个问题分解成与原问题相似的子问题 解决:递归的解各个子问题 合并:合并子问题的结果得到了原问题的解. 现在就用递归算法,采用上面的分治思想来解合并排序. 合并排序(非降序) 分解:把合并排序分解成与两个子问题 伪代码: 复制代码 代码如下: MERGE_SORT(A, begin, end) if begin < end then mid<- int((begin + end)/2) MERGE_SORT(A, begin, mid) MERGE_SORT(A, m

  • C语言合并两个带头节点升序排列链表

    合并链表,顾名思义,就是将两个按顺序存放数据的链表中的数据合并为用一个链表存储,比如在处理多项式的加减法时就需要将两个多项式的数据进行合并.合并方式有很多种:如果按照存储方式的不同,可以将两个链表的数据分别提取出来生成一个新的链表来存储原先两个链表的数据,还可以将其中一个链表的数据依次插入到另外一个链表的相应位置当中去.在遇到相同数据时可以采取只留下一个数据的方式和两个数据均保留的方式.这些不同点需要到具体的问题中具体分析,但是只是在细节上有一些差别,大体的思路都是一样的,本文主要介绍将一个链表

  • c语言实现两个单链表的交叉合并方式

    如下所示: #include<stdio.h> #include<stdlib.h> #include<iostream> using namespace std; struct Node { int data; Node *next; }; //初始化 Node *init() { Node *head=new Node; head->next=NULL; return head; } //头插法创建节点 void insetList(Node *head,in

  • C/C++合并两个升序链表的方式

    目录 合并两个升序链表 算法的思想 代码实现+注释 合并K个升序链表(递归方法) 归并的思想 先来看合并两个有序链表的代码 我们再来看合并K个链表的递归方法 合并两个升序链表 算法的思想 1.需要合并的两个链表La,Lb,合并之后的链表Lc(用La的头节点). 2.定义两个辅助指针Pa,Pb分别是链表La,Lb的复制指针. 3.从首元节点开始比较,当两个链表都没有到达链表尾部的时候,依次取其中较小的数据进行链接到Lc的最后 4.如果两个元素的值相同,取La链的,把Lb链表的元素删除(确保新链表没

  • go语言题解LeetCode88合并两个有序数组示例

    目录 题目描述 思路分析 AC 代码 题目描述 原题链接 : 88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目. 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列. 注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中.为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素

  • R语言实现用cbind合并两列数据

    我有两个数据文件,分别只有一列,这两列数据行数一行,我想把这两列合并到一个数据文件中,方便使用. 我的两个数据文件分别是1.txt,2.txt,保存后的文件名是3.txt. // 代码如下 gow1<-read.table("1.txt",header = FALSE) gow2<-read.table("2.txt",header = FALSE) View(gow1) View(gow2) gow<-cbind(gow1,gow2) View(

  • 带你了解如何用C++合并两个有序链表

    目录 将两个升序链表合并为一个新的 升序 链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 思路 代码 链表Listnode详细介绍 总结 将两个升序链表合并为一个新的 升序 链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例 1: 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 示例 2: 输入:l1 = [], l2 = [] 输出:[] 示例 3: 输入:l1 = [], l2 = [0] 输出:[0] 思路 可以简

  • python实现合并两个有序列表的示例代码

    题目描述 将两个升序链表合并为一个新的升序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. LeetCode原题地址:https://leetcode-cn.com/problems/merge-two-sorted-lists/ 测试用例 示例1 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 示例2 输入:l1 = [], l2 = [] 输出:[] 示例3 输入:l1 = [], l2 = [0] 输出:[0] 代码详解 因为Lee

  • 详解C语言如何实现双向带头循环链表

    目录 一.双向循环链表与顺序表的区别 二.List.h 三.List.c 1.带头双向循环链表的初始化 2.带头双向循环链表的销毁 3.带头双向循环链表的打印 4.动态开辟一个节点 5.带头双向循环链表的判空 6.带头双向循环链表的尾插.尾删 7.带头双向循环链表的头插.头删 8.带头双向循环链表的长度 9.带头双向循环链表的查找.任意位置插入.删除 一.双向循环链表与顺序表的区别 不同点 顺序表 双向带头循环链表 在内存中的存储方式 连续存储 不一定连续 随机访问 支持随机访问 不支持随访问

  • 逆转交替合并两个链表的解析与实现

    逆转交替合并两个链表,即从一个链表的尾指针指向另一个链表的尾指针,依次逆转交替进行合并.下面就通过实例来详细的介绍该逆转交替合并两个链表的思路与实现代码. 一.问题描述 链表A和B A: 1->2->3->4 B: a->b->c->d 请逆转交替合并两个链表,示例结果如下: 4->d->3->c->2->b->1->a 节点类型定义如下: classNode { public Node next; ... } 二.源代码: 传

随机推荐