题目链接:
力扣https://leetcode.cn/problems/merge-two-sorted-lists/description/
解题思路:
1. 初学者对于此题的解答最容易想到就是将第一个链表中的结点与第二个链表中的结点逐一进行比
较后进行多次“随机插入” 操作,但是该种算法容易想到,但是用代码实现起来又会很困难
2. 所以我们想到用 “ 归并 ” 去求解。
所谓归并就是每次都要从头比较第一个链表和第二个链表的头结点,然后取最小的结点尾插到新的
链表(因为题目中要求是升序)
当其中一个链表被遍历完毕,即用来进行遍历的指针指向空,那么归并就结束
而且假如第一个链表被遍历完毕(即没有结点),但是第二个链表不一定就只有一个结点,可能剩
余有多个结点
【注意】:
有哨兵位时不需要判断指针是否为 NULL,而没有哨兵位时则需要判断
参考代码1(不带哨兵位):
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
//不要忘了要先判断题目中所给的俩个单链表是否其中有一个为空链表,如果有,则无需进行归并排序
//如果有哨兵位则不需要去进行判断
if (list1 == NULL)
{
return list2;
}
if (list2 == NULL)
{
return list1;
}
struct ListNode *head,*tail;
head = NULL;//设立head指针的目的:存储归并得到的链表的第一个结点的地址,但是必须先设为空指针,因为不知道俩个单链表的第一个数据谁更小
//即不知道归并后得到的新链表的第一个结点是来自list1还是list2
tail = NULL;//设立该指针的目的是为了在归并结束后无需再次遍历剩余没有进行尾插的结点
while (list1 && list2)//注意这里要使用&&,而不是||,因为这是要执行下去的条件
{
if (list1->val < list2->val)
//因为题目要求最后得到一个升序的链表,所以此时应该将数值小的进行尾插
//要时刻检查俩个链表是否达到归并结束的条件
//因为这是list1小于list2的情况,所以在这里要检查list1是否达到归并结束的条件
{
if(tail == NULL)//归并一开始就要执行该代码,而不执行其else部分
{
head = tail = list1;
}
else
{
tail->next = list1;//因为程序一开始要先执行head与tail的初始化,而执行完初始化后程序执行list1=list1->next;代码
tail = tail->next;
}
list1 = list1->next;//对list1进行迭代更新
}
else
{
//if (list2 == NULL)
if (tail == NULL)//是对tail是否为NULL进行判断,而不是对list2
{
head = tail = list2;
}
else
{
tail->next = list2;
tail = tail->next;
}
list2 = list2->next;
}
}
//归并结束后要检查俩个链表哪一个为空链表,并且要将不是空链表的剩余结点与前面归并后得到的新链表连接起来
if (list1 != NULL)
{
tail->next = list1;
}
if (list2 != NULL)
{
tail->next = list2;
}
return head;
}
参考代码2:带哨兵位
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
//如果有哨兵位则不需要去进行判断
//if (list1 == NULL)
//{
// return list2;
//}
//if (list2 == NULL)
//{
// return list1;
// }
/***************************************************************************************************************************/
//用带有哨兵位的方法去写,因为有哨兵位的存在,所以题目中所给的单链表一定不为空,所以无需对题目所给的链表进行判断
//因为即使题目给出一个空链表,因为有哨兵位(头结点)的存在,所以其单链表长度不为0,此时为1.
struct ListNode *head,*tail;
head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
tail->next = NULL;//对哨兵位指针域的初始化
while (list1 && list2)//注意这里要使用&&,而不是||,因为这是要执行下去的条件
{
if (list1->val < list2->val)
//因为题目要求最后得到一个升序的链表,所以此时应该将数值小的进行尾插
//要时刻检查俩个链表是否达到归并结束的条件
//因为这是list1小于list2的情况,所以在这里要检查list1是否达到归并结束的条件
{
//if(tail == NULL)//归并一开始就要执行该代码,而不执行其else部分
//{
// head = tail = list1;
//}
//else
// {
// tail->next = list1;//因为程序一开始要先执行head与tail的初始化,而执行完初始化后程序执行list1=list1->next;代码
// tail = tail->next;
//}
//list1 = list1->next;//对list1进行迭代更新
//因为有哨兵位的存在,所以无需担心空链表的存在
tail->next = list1;
tail = tail->next;
list1 = list1->next;
}
else
{
//if (tail == NULL)
//{
// head = tail = list2;
//}
//else
//{
// tail->next = list2;
// tail = tail->next;
//}
//list2 = list2->next;
tail->next = list2;
tail = tail->next;
list2 = list2->next;
}
}
//归并结束后要检查俩个链表哪一个为空链表,并且要将不是空链表的剩余结点与前面归并后得到的新链表连接起来
if (list1 != NULL)
{
tail->next = list1;
}
if (list2 != NULL)
{
tail->next = list2;
}
//归并结束后得到新的单链表也存在头结点(哨兵位),即此时的head,所以要想返回不含有头结点的新的单链表
//head需要被free,但在free前要保存头结点(哨兵位)的下一个结点的地址,即head->next
struct ListNode *list = head->next;
free(head);
return list;
}