C语言合并两个有序链表
题目描述:
题目描述: 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
题目链接:https://leetcode.cn/problems/merge-two-sorted-lists/description/
解题思路:
看到这个题目描述我们目前有两种想法:
第一种是:直接在原链表上操作,题目已经告诉我们两链表是升序链表,所以我们就可以比较方便对链表中的所有数据进行有序的遍历比较,比较以后我们可以直接对原链表进行操作,根据比较结果,将数值小一点的节点在链表的指定位置插入,将所有链表按顺序连接起来;
第二种是:创建一个新的链表,对原来两升序链表中的所有数据进行遍历,将小一点的那个数据节点尾插到新链表中,继续向后遍历
解法图示:
思路一:
思路二:
示例代码:
思路一代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode LN;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
//解法一:直接对两链表遍历,将所有节点按顺序连接起来
if(list1==NULL)//list1为空时
{
return list2;
}
if(list2==NULL)//list2为空时
{
return list1;
}
LN* n1=NULL,*n2=NULL,*cur1=list1,*cur2=list2;//用n1指向cur1前一个结点,n2指向cur2后一个节点
while(cur1&&cur2)
{
if((cur1->val)<=(cur2->val))//当cur1节点中数据更小时或相等时,直接将cur1和n1后移一位
{
n1=cur1;
cur1=cur1->next;
}
else
{
if(n1==NULL)//当cur1和cur2第一次比较时cur2中数据就小一点,此时就将cur2中的第一个节点作为头节点
{
n1=cur2;
n2=cur2->next;
cur2->next=cur1;
cur2=n2;
list1=n1;//注意此时是头插,所以将list1移动到新的头处
}
else//一般情况,将cur2中每个节点遍历插到cur1中
{
n2=cur2->next;
n1->next=cur2;
cur2->next=cur1;
n1=cur2;
cur2=n2;
}
}
}
if(cur1==NULL)//当cur1先被遍历完时
{
n1->next=cur2;
}
return list1;
}
思路二代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode LN;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
//解法二:创建一个新的链表,将list1,list2中节点依次按顺序放入
LN* newhead=NULL,*newtail=NULL,*cur1=list1,*cur2=list2;//newhead指向新链表的头节点,newtail指向新链表的尾节点
if(list1==NULL)//list1为空时
{
return list2;
}
if(list2==NULL)//list2为空时
{
return list1;
}
while(cur1&&cur2)
{
if(cur1->val<=cur2->val)//当cur1中数据更小时,有两种情况
{
if(newhead==NULL)//创建的新链表中一个节点都没有,由这个cur1的节点来当头节点
{
newhead=newtail=cur1;
cur1=cur1->next;
}
else//一般情况,直接尾插节点
{
newtail->next=cur1;
newtail=cur1;
cur1=cur1->next;
}
}
else
{
if(newhead==NULL)//创建的新链表中一个节点都没有,由这个cur2的节点来当头节点
{
newhead=newtail=cur2;
cur2=cur2->next;
}
else//一般情况,直接尾插节点
{
newtail->next=cur2;
newtail=cur2;
cur2=cur2->next;
}
}
}
if(cur1==NULL)//出来后cur1先为空
{
newtail->next=cur2;
}
if(cur2==NULL)//cur2先为空
{
newtail->next=cur1;
}
return newhead;
}
改进补充:
看到上面写出来的代码我们发现一个问题,就是在中间一部分重复的代码较多而且比较繁琐,比如:
为解决这点我们可以对思路二做些改进,可以定义一个带头的链表,哨兵位,不存储任何有效的数据。这样我们每次插入数据时就都可以看作是尾插了,不需要再去判断链表的头节点是否为空。
示例代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode LN;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
//解法三:头节点为哨兵位(思路二的基础上的改进)
LN* newhead=NULL,*newtail=NULL,*cur1=list1,*cur2=list2;//newhead指向新链表的头节点,newtail指向新链表的尾节点
if(list1==NULL)//list1为空时
{
return list2;
}
if(list2==NULL)//list2为空时
{
return list1;
}
newhead=newtail=(LN*)malloc(sizeof(LN));
while(cur1&&cur2)
{
if(cur1->val<=cur2->val)//当cur1中数据更小时,有两种情况
{
// if(newhead==NULL)//创建的新链表中一个节点都没有,由这个cur1的节点来当头节点
// {
// newhead=newtail=cur1;
// cur1=cur1->next;
// }
// else//一般情况,直接尾插节点
// {
// newtail->next=cur1;
// newtail=cur1;
// cur1=cur1->next;
// }
//直接尾插
newtail->next=cur1;
newtail=cur1;
cur1=cur1->next;
}
else
{
// if(newhead==NULL)//创建的新链表中一个节点都没有,由这个cur2的节点来当头节点
// {
// newhead=newtail=cur2;
// cur2=cur2->next;
// }
// else//一般情况,直接尾插节点
// {
// newtail->next=cur2;
// newtail=cur2;
// cur2=cur2->next;
// }
//直接尾插
newtail->next=cur2;
newtail=cur2;
cur2=cur2->next;
}
}
if(cur1==NULL)//出来后cur1先为空
{
newtail->next=cur2;
}
if(cur2==NULL)//cur2先为空
{
newtail->next=cur1;
}
//释放掉动态开辟的内存
LN* retHead=newhead->next;
free(newhead);
newhead=NULL;
return retHead;
}