目录
题目链接:合并两个有序链表
方法一:无头结点的方法
思路:依次比较链表中的节点,每次取小的节点,尾插到新的链表后面。
问题一:找到较小的节点后是不是就要找到尾节点进行尾插,难道每次尾插完都找尾节点吗?效率肯定很低,所以可以用一个指针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才行,下面是更简洁的代码:
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开始
#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;
}
最后更新下有头结点的C++和Java代码
C++代码:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if (list1 == nullptr) return list2;
if (list2 == nullptr) return list1;
ListNode* head = new ListNode();
ListNode* tail = head;
while(list1 && list2){
if (list1->val < list2->val){
tail->next = list1;
tail = tail->next;
list1 = list1->next;
} else{
tail->next = list2;
tail = tail->next;
list2 = list2->next;
}
}
if (list1) tail->next = list1;
if (list2) tail->next = list2;
ListNode* newHead = head->next;
return newHead;
}
};
Java代码:
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if (list1 == null) return list2;
if (list2 == null) return list1;
ListNode head = new ListNode();
ListNode tail = head;
while(list1 != null && list2 != null){
if (list1.val < list2.val){
tail.next = list1;
tail = tail.next;
list1 = list1.next;
} else{
tail.next = list2;
tail = tail.next;
list2 = list2.next;
}
}
if (list1 != null) tail.next = list1;
if (list2 != null) tail.next = list2;
ListNode newHead = head.next;
return newHead;
}
}