例题描述
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
- 输入:
1->2->4
,1->3->4
- 输出:
1->1->2->3->4->4
结点结构体定义
struct ListNode {
int val;
struct ListNode *next;
};
解题思路
- 首先判断极端条件:
l1
链表如果为空链表,则不需要进行合并,直接返回另一个链表l2
。l2
链表如果为空链表,直接返回l1
链表。 - 创建一个新的头指针
pHead
作为新链表的头指针,初始化为NULL
。再创建一个pTail
尾指针用来进行尾部插入,初始化为NULL
。 - 创建一个
p1
指针指向第一个链表的结点,创建另一个p2
指针指向第二个链表的结点,通过这两个指针进行操作。
- 进行新链表的首结点选择
< 如果 >:p1
指针指向的结点数值小于(或等于,不强制归属于此,可置于大于逻辑)p2
指针指向的结点val
值,说明p1
结点的第一个结点值更小,将它置为新链表的首结点,使pHead
和pTail
都指向p1
这个结点,然后使得p1
指针指向它的下一个结点。
< 如果 >:p2
指针指向的结点数值大于p1
指针指向的结点val
值,同理使pHead
和pTail
都指向p2
这个结点,然后使得p2
指针指向它的下一个结点。
【题目中此时都为1
,p1
指向结点值不大于p2
指向的结点值,所以将p1
指定为新链表的首结点】
更新旧连表指针
【此时新链表的首结点设置完毕,现在有更新后的两个旧链表,下面逻辑进行处理】 - 进行链表遍历,循环条件为
p1
指针与p2
指针都不为空,但凡有任一指针为空,说明到达了这个链表的尾部,无法使用循环继续链接,要采用别的代码逻辑处理。 - 循环逻辑中,判定两链表结点值的大小
< 如果 >:p1
指向的l1
链表中的结点值小于等于p2
指向的l2
链表中的结点值,那么就将此结点尾插至新链表的尾部,然后将结点的指针更新,使它变成被插入指针的next
结点。
< 否则 >(大于):同理将p2
指向的结点尾插到新链表的尾部,更新结点指向。
- 循环退出条件,任一链表到达结尾。此时我们进行判定:如果是
l1
链表到达结尾,那么说明l2
链表有可能没有结束、或者也结束了,我们将这种情况统一处理:将此时的l2
链表链接到新链表的尾部。如果是l2
链表同理。
【题目中此时l1
链表到达了尾部,而l2
链表还没有到达尾部】
【所以选择将p2
指针之后的结点全部"链接"在pTail
指向的结点之后】
- 最终返回新链表的头指针
pHead
即可。
代码实现
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
if(l1 == NULL){
return l2;
}
if(l2 == NULL){
return l1;
}
struct ListNode *pHead = NULL;
struct ListNode *pTail = NULL;
struct ListNode *p1 = l1;
struct ListNode *p2 = l2;
if(p1->val <= p2->val){
pHead = pTail = p1;
p1 = p1->next;
}
else{
pHead = pTail = p2;
p2 = p2->next;
}
while(p1 != NULL && p2 != NULL){
if(p1->val <= p2->val){
pTail->next = p1;
p1 = p1->next;
}
else{
pTail->next = p2;
p2 = p2->next;
}
pTail = pTail->next;
}
if(p1 == NULL){
pTail->next = p2;
}
if(p2 == NULL){
pTail->next = p1;
}
return pHead;
}
小结
这段程序要保证健壮性,同时可以处理很多“意外情况”,比如我们一开始对新链表头结点的选择时,就要求对两个链表结点值进行比较,从而谨慎的选出较小者作为新链表的头结点,如果不进行这步操作直接执行后面的逻辑,程序就会运行错误。
最后一步链接操作,保证了一个链表的完整性,最后指向了空(NULL
),最后的时候其实要考虑循环退出条件,进而处理不同的情况,我们将未到达结尾的链表结点链接在新链表之后就相当于“兵来将挡水来土掩”,可以处理多种情况,效果显著。