链表经典算法OJ题目(2)
1.寻找链表的中间节点
题目链接:876. 链表的中间结点 - 力扣(LeetCode)

我们来直接介绍一个思路:快慢指针
快慢指针是指我们创建创建2个指针,一个为快指针,一个为慢指针,且快指针一次走的步数是慢指针的两倍。两个指针同时往后走,当快指针为空或者快指针->next为空时,此时,慢指针的位置恰好是中间节点。
1.1 节点的个数为奇数



上图为了方便理解,画了两条链表。其实两个指针是在一条链表里面实现的。
节点个数为奇数时,当fast指针走到节点末尾时,slow指针指针的位置恰好是节点的中间位置。
1.2 节点的个数为偶数



当节点的个数为偶数时,当fast指针最后为空指针时,slow指针的位置恰好是节点的中间节点。
代码实现
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
ListNode*slow=head;
ListNode*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
注意事项:循环条件不能写成while(fast->next&&fast),因为当链表的节点个数为偶数个时,fast指针最终会为空指针,则fast->next就会报错,如果按照我上面那样写,当fast为空时,循环条件的表达式就会短路,不会执行后面的fast->next。
2.合并两个有序链表
做题链接:21. 合并两个有序链表 - 力扣(LeetCode)

暴力解题思路:直接创建一个新链表,两个链表之间各个节点的数据依次进行比较,数据小的插入新链表。












如上面的过程图所示,数据小对应的节点被拿出来插入新的节点,接着让对应的节点向后走到下一个节点,直到有一条链表走完。
代码实现
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if(list1==NULL)
{
return list2;
}
if(list2==NULL)
{
return list1;
}
//创建新链表
ListNode*newhead,*newtail;
newhead=newtail=NULL;
//遍历原链表
ListNode*l1=list1;
ListNode*l2=list2;
while(l1&&l2)
{
if(l1->val<l2->val)
{
if(newhead==NULL)
{
newhead=newtail=l1;
}
else
{
newtail->next=l1;
newtail=l1;
}
l1=l1->next;
}
else
{
if(newhead==NULL)
{
newhead=newtail=l2;
}
else
{
newtail->next=l2;
newtail=l2;
}
l2=l2->next;
}
}
if(l1)
{
newtail->next=l1;
}
if(l2)
{
newtail->next=l2;
}
return newhead;
}

当写完代码我们发现,如上图,有两段一样的代码,这就造成了代码的拥挤,造成有这两段带码的原因:因为新建的头节点和尾节点存在空指针的情况,需要就行判空。
解决方法:建立哨兵位,让哨兵位作为新的头节点,并且哨兵位不存放任何数据。

优化代码
#include<stdlib.h>
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if(list1==NULL)
{
return list2;
}
if(list2==NULL)
{
return list1;
}
//建立哨兵位
ListNode*newhead,*newtail;
newhead=newtail=(ListNode*)malloc(sizeof(ListNode));
//遍历原链表
ListNode*l1=list1;
ListNode*l2=list2;
while(l1&&l2)
{
if(l1->val<l2->val)
{
newtail->next=l1;
newtail=l1;
l1=l1->next;
}
else
{
newtail->next=l2;
newtail=l2;
l2=l2->next;
}
}
if(l1)
{
newtail->next=l1;
}
if(l2)
{
newtail->next=l2;
}
ListNode*next=newhead->next;
free(newhead);
newhead=NULL;
return next;
}
注意事项:使用malloc函数,最后要记得销毁掉,不然可能会造成内存溢出。
我们要返回的节点是哨兵位的下一个节点,因为返回前要将哨兵位销毁掉,所以我们要提前将哨兵位的下一个节点存储起来。