目录
方法一:无头结点的方法
方法二:有头结点的方法
题述:
已给函数头:
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
已给出链表的结构体定义:
struct ListNode
{
struct ListNode* next;
int val;
};已知l1、l2分别指向两个链表
要求:
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。(完善mergeTwoLists函数)
示例:
方法一:无头结点的方法
思路:1、依次比较链表中的节点,每次取小的节点,尾插到新的链表后面。
问题一:找到较小的节点后,那是不是就要找到尾节点进行尾插,难道每次尾插完都找尾节点吗?这样效率肯定很低,所以我们可以再用一个指针tail来存尾节点。再创建一个指针head用来表示另一个没有头结点的新链表。 后续的尾插我们就可以尾插到head链表中
问题二:这个单链表是否有头结点,有头结点是一种情况,没有头结点就是另一种情况,相比而言有头节点的方式更简单,oj题一般都是默认链表无头结点的,但是我们可以创建头结点,所以这就可以推出两种方法
代码如下:
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
//如果其中一个为空,就返回另一个
if (l1 == NULL)
{
//如果刚开始就有一个为空
//那就直接返回另一个
//如果两个都是NULL,那正好返回NULL就好了
return l2;
}
if (l2 == NULL)
{
return l1;
}
struct ListNode* head = NULL, * tail = NULL;
while (l1 && l2)
{//循环走下去的条件就是l1和l2均不为NULL
//如果其中一个为空了,就说明走完了
if (l1 ->val < l2->val)
{
if (head == NULL)
{
//第一次尾插的话,我们是在新链表无头结点的情况下
// 后续的插入才会在有节点的情况,
// 而这两种情况代码不同,所以要分类讨论
//所以直接使头和尾被赋值为l1
//tail主要是用来找尾节点的
head = tail = l1;
}
else
{
//进入else,说明新创的链表中已经有节点,所以
//直接插入在后面的节点
tail->next = l1;//尾插节点
tail = l1;//tail用来保存尾节点
}
l1 = l1->next;//每次尾插完一个数据,l1会往后走
}
else
{
if (head == NULL)
{
//第一次尾插
head = tail = l2;
}
else
{
tail->next = l2;
tail = l2;
}
l2 = l2->next;
}
}
if (l1)
{//如果l1不为空,那么就是l2走完了
//此时直接把l1剩余部分尾插到新链表中即可
tail->next = l1;
}
if (12)
{
tail->next = l2;
}
return head;
}
上段代码的缺点就是while循环中对于l1和l2有几处重复部分,所以可以在上段代码的基础上再修改一下,思路一样,head原来为NULL,那我就先找一个最小的让head指向这个节点就行,就不用在while循环中l1和l2先判断if才行,下面是更简洁的代码:文章来源:https://www.toymoban.com/news/detail-724451.html
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
//如果其中一个为空,就返回另一个
if (l1 == NULL)
{
//如果刚开始就有一个为空
//那就直接返回另一个
//如果两个都是NULL,那正好返回NULL就好了
return l2;
}
if (l2 == NULL)
{
return l1;
}
struct ListNode* head = NULL, * tail = NULL;
//先取两个链表中一个小的去做第一个
//能做第一个,因为两个链表都是升序的
if (l1->val < l2->val)
{
head = tail = l1;
l1 = l1->next;
}
if (l1->val > l2->val)
{
head = tail = l2;
l2 = l2->next;
}
while (l1 && l2)
{//循环走下去的条件就是l1和l2均不为NULL
//如果其中一个为空了,就说明走完了
if (l1->val < l2->val)
{
tail->next = l1;//尾插节点
tail = l1;//tail用来保存尾节点
l1 = l1->next;//每次尾插完一个数据,l1会往后走
}
else
{
tail->next = l2;
tail = l2;
l2 = l2->next;
}
}
if (l1)
{//如果l1不为空,那么就是l2走完了
//此时直接把l1剩余部分尾插到新链表中即可
tail->next = l1;
}
if (l2)
{
tail->next = l2;
}
return head;
}
方法二:有头结点的方法
原来我们题目给的是没有头结点的,但是我们可以自己创建一个头结点,他不存储数据,他是为了使后面找元素方便,值得注意的是这个head和tail都要动态内存开辟一个,最后在用完之后还需要释放,并且最后不能直接return head,要return head->next,因为题目本来就没给你头结点,你就不能返回你自己创建的头,本质上应该从head->next开始文章来源地址https://www.toymoban.com/news/detail-724451.html
#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
struct ListNode* next;
int val;
};
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
//如果其中一个为空,就返回另一个
if (l1 == NULL)
{
//如果刚开始就有一个为空
//那就直接返回另一个
//如果两个都是NULL,那正好返回NULL就好了
return l2;
}
if (l2 == NULL)
{
return l1;
}
struct ListNode* head = NULL, * tail = NULL;
//哨兵位的头结点,head和tail刚开始均指向头结点
head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
while (l1 && l2)
{//循环走下去的条件就是l1和l2均不为NULL
//如果其中一个为空了,就说明走完了
if (l1->val < l2->val)
{
tail->next = l1;//尾插节点
tail = l1;//tail用来保存尾节点
l1 = l1->next;//每次尾插完一个数据,l1会往后走
}
else
{
tail->next = l2;
tail = l2;
l2 = l2->next;
}
}
if (l1)
{//如果l1不为空,那么就是l2走完了
//此时直接把l1剩余部分尾插到新链表中即可
tail->next = l1;
}
if (l2)
{
tail->next = l2;
}
struct ListNode* list = head->next;
free(head);
return list;
}
到了这里,关于【数据结构】--oj_合并两个有序链表(详解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!