1.第三个题目:链表的中间节点
这个显然是需要使用分类情况进行考虑,也就是分成奇数个节点和偶数个节点进行考虑;
在这个地方我们直接使用快慢指针的方法对于这个题目进行求解:
快慢指针在我们的链表类型的题目里面其实是经常被使用到的,本质就是我们的这个快指针和慢指针走的这个速度是不一样的;
下面的这个是奇数个节点对应的这个情况
下面的这个就是我们的这个指针的第一次移动的位置结束时候的情况:
下一次结束的时候,就是下面的这个情况:这个时候我们的fast已经是达到了这个最后一个节点的这个位置,因此这个时候我们的这个移动指针的过程应该就是结束了的;
接下来我们处理一下这个偶数个节点的这个情况:在这个第一次结束的时候,具体的指向的情况如下所示
第二次结束的时候,如下所示
这个时候我们的fast后面还是右节点的,因此我们的这个指针可以继续移动:这个时候哦们的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;
}
2.第四个题目:合并两个有序链表
下面的这个就是我们的思路:通过对于两个链表的元素的遍历使用的比较,这个时候把小的这个元素放到我们的这个新的链表的开始的这个位置即可;然后不断地进行尾插的操作;
先把这个基本的框架搭建一下,然后后面去进行这个完善的过程:
while循环的条件就是我们的这两个有序链表都是没有遍历结束的,这个时候,就一直在这个循环里面,根据这个大小判断这个插入的选择;
循环结束之后,l1不是空的,我们就返回这个l2里面的这个剩余的元素,但是上面的写法不对,因为这个需要拆插入到我们的这个尾结点的后面,下面的这个是正确的;
下面的这个是具体代码,while里面的插入,需要判断我们的这个新链表有没有节点没有的话,这个插入的节点就是头结点,也是尾结点,如果本来就存在节点,这个时候就是尾插,这个newtail需要向后移动一下即可;
typedef struct ListNode listnode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
listnode* l1=list1;
listnode* l2=list2;
if(list1==NULL)
{
return list2;
}
if(list2==NULL)
{
return list1;
}
listnode* newhead,*newtail;
newhead=newtail=NULL;
while(l1&&l2)
{
if(l1->val<l2->val)
{
if(newhead==NULL)
{
newhead=newtail=l1;
}
else{
newtail->next=l1;
newtail=newtail->next;
}
l1=l1->next;
}
else
{
if(newhead==NULL)
{
newhead=newtail=l2;
}
else{
newtail->next=l2;
newtail=newtail->next;
}
l2=l2->next;
}
}
if(l2)
{
newtail->next=l2;
}
if(l1)
{
newtail->next=l1;
}
return newhead;
}