单链表 链表的快慢指针 经典例题

这周末爽刷力扣,写个博客回顾一下。

 之前很不适应力扣写函数的模式,不过好像写两道就习惯了。

707 设计链表 力扣

没什么可说的基础题,要注意的是链表是有头节点的。

------------------------------------------------------------------------

141 环形链表 力扣

这题就是快慢指针的应用了,当链表有环的时候,快指针一定可以追上慢指针。无环时,快指针会先到达链表尾部。

----------------------------------------------------------------------------

142 环形链表 力扣

也是快慢指针,由于要返回链表开始入环的第一个节点,所以稍微复杂一点,我们设未成环的节点长度为a,环节点的长度为b,这样当快指针追上慢指针时,我们设慢指针走了s,这样就是2s=s+nb,n是大于等于1的整数。于是就有s=nb,又因为a+nb还是入环的第一个节点,也就是说慢指针再走a就到达了入环的第一个节点,所以我们可以再加一个指针或者直接把快指针重新指向头节点,速度置为一,这样当他们再次相遇的时候一定位于入环的第一个节点。

------------------------------------------------------------------------------

160 相交链表 力扣

这题就是双指针,分别指向两个链表的头节点,当他们走到空指针时,分别置为另一个链表的头节点,当它们相等时返回其中一个即可。

这个可以想象为两个人,他们的速度的相同,并且由于链表相交,他们的起点可以视为相同,那么他们相遇的终点一定位于链表的相交点。想不通的话反过来想一想。如果链表不相交,那么他们最后相等的时候一定同时等于空指针。

(btw,颇有感触,当两个人走过完全相同的一段路,那么他们算不算、会不会相遇呢,如果他们还没有相遇,那么是不是注定不会相遇呢。)

-----------------------------------------------------------------------------

19 删除链表的倒数第N个节点  力扣

这题没什么难度,可以自己写一个函数用于获取链表的长度,然后对于头节点不好处理的情况,可以再头节点前面加上一个哑节点(只有指针域),这样的话对于这个新链表,原来的头节点就变成了一个普通的节点了。

-----------------------------------------------------------------------------------

2022/9/25

该睡觉了,就先写这么多了,明天接着写。

-----------------------------------------------------------------------------------------------------

206 反转链表 力扣

这题的话画图比较明白,感觉链表的题画图的话思路很清晰。

定义三个指针 cur,pre,end 名字也没什么特殊的意义。

cur指向当前待反转的节点

pre指向cur的下一个节点,用于保存。

end指向反转后的链表的头节点。

那么

while(cur!=NULL)
{
    pre=cur->next;  //保存原始链表
    cur->next=end;  //断开连接,建立新的连接
    end=cur;   //移位操作,让end指向反转后链表的新的头节点
    cur=pre;  //cur指向下一个待反转的节点
}

最后返回end指针即可。

-----------------------------------------------------------------------------

203 移除链表元素 力扣

同样的思路,为了把头节点变得普通,新建了一个哑节点。

在遍历链表的时候,当遇到要删除的点时,应当删除,但不需要移动,应当是出现下一个不需要删除的数的时候,移动指针。可以避免出现连续需要删除节点出错的问题。

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode *cur=(struct ListNode *)malloc(sizeof(struct ListNode));
    cur->next=head;
    struct ListNode *temp=cur;
    while(cur->next)
    {
        if(cur->next->val != val)
        {
            cur=cur->next;
        }
        else
        {
            cur->next =cur->next->next;
        }
    }
    return temp->next;
}

-----------------------------------------------------------------------------

328 奇偶链表 力扣

这题就是通过遍历链表,把链表分开为奇数链表和偶数链表,然后让奇数链表的尾节点连上偶数链表的头节点即可。

struct ListNode* oddEvenList(struct ListNode* head){
    if(head == NULL || head->next == NULL) 
        return head;
    struct ListNode *odd =head;
    struct ListNode *even=head->next;
    struct ListNode *evenhead=even;
    while(even != NULL && even->next != NULL)//由于偶节点一定在奇数节点后面所以循环条件是这样
    {
        even=odd->next;
        odd->next = even->next;
        even->next =odd->next->next;
        odd=odd->next;
        even=even->next;
    }
    odd->next=evenhead;
    return head;
}

------------------------------------------------------------------------------

234 回文链表 力扣

这题我觉得好难,我觉得回文都挺难的。

由于是回文,那么我们会想到,用递归遍历到最后一个节点再回溯来实现反向遍历的思路。

但是还是有别的地方不好想。

struct ListNode *pre;

bool dfs(struct ListNode *cur)
{
    if(cur != NULL)
    {
        if(!dfs(cur->next))//这一步是很巧妙的,既是递归又是出口,出口指的是一旦出现return false的情况那么就会一层层的向上return false,而第一次出现false的情况只能是出现了不匹配的情况                                               
        {
            return false;
        }
        if(cur->val != pre->val)
        {
            return false;
        }
        pre=pre->next;//比较下一个
    }
    return true;
}

bool isPalindrome(struct ListNode* head){
    pre=head;
    return dfs(head);
}

其实这种递归的做法还是有浪费的地方,也就是链表前半部分会重复比较一次。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值