代码随想录算法训练营:4/60

非科班学习算法day4 | LeetCode24: 两两交换链表中的节点,Leetcode19: 删除链表的倒数第N个节点,面试题02.07 链表相交 ,Leetcode142:环形链表||

目录

介绍

一、基础概念补充:

二、LeetCode题目

1.LeetCode24: 两两交换链表中的节点

题目解析

2.Leetcode19:删除链表的倒数第N个节点 

题目解析

3.面试题02.07.链表相交

题目解析

4.Leetcode142:环形链表||

题目解析

总结


介绍

包含LC的四道题目,还有相应概念的补充。

相关图解和更多版本:

代码随想录 (programmercarl.com)https://programmercarl.com/#%E6%9C%AC%E7%AB%99%E8%83%8C%E6%99%AF


一、基础概念补充:

        链表相关知识请见day3!

二、LeetCode题目

1.LeetCode24: 两两交换链表中的节点

题目链接:

题目解析

        模拟过程,利用虚拟头节点,设置遍历指针;

        和删除节点需要注意的事项一样,需要使用的后续节点提前保存。

 c++代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) 
    {
        //创建虚拟节点
        ListNode * dummyhead = new ListNode(0);

        //将虚拟头节点插到头部
        dummyhead->next = head;

        //设置遍历指针
        ListNode * cur = dummyhead;

        //

        while(cur && cur->next &&cur->next->next)
        {
            ListNode* temp1 = cur->next;
            ListNode* temp2 = cur->next->next->next;

            cur->next = cur->next->next;
            cur->next->next = temp1;
            cur->next->next->next = temp2;

            cur = cur->next->next;
        }
        ListNode * result = dummyhead->next;
        delete dummyhead;
        return result;
    }
};

注意点1:最后在返回的时候需要删除虚拟头节点,所以这里多了一步保存头节点信息,然后删除虚拟头节点,最后返回保存信息。

注意点2:关于遍历条件,我一开始写的是cur && cur->next,现在举个例子,如果希望能够交换两个值,那么这两个值必须存在,而我们cur指针一开始指向都是在需要交换的两个数前面。所以才会又现在的条件cur && cur->next->next。

注意点3:这道题没有必要采用两个指针来忽悠自己.(o-o)

2.Leetcode19:删除链表的倒数第N个节点 

题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

题目解析

     这一次采用最常规的方式,遍历两次,第一次获取链表长度;第二次遍历链表到指定位置进行删除操作。

 C++代码如下: 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    int getCount(ListNode* head)
    {
        int count = 0;
        while(head)
        {
            ++count;
            head = head->next;
        }
        return count;
    }

    
    ListNode* removeNthFromEnd(ListNode* head, int n) 
    {
        //设置虚拟头节点
        ListNode* dummdyhead = new ListNode(0);

        //插入虚拟头节点
        dummdyhead->next = head;

        //设置检索指针
        ListNode * cur = dummdyhead;

        //获取链表长度
        int count = getCount(head);

        for(int i = 1; i < count - n + 1; ++i)
        {
            cur = cur->next;
        }
        cur->next = cur->next->next;
        ListNode* result = dummdyhead->next;
        delete dummdyhead;
        return result;
    }
};

注意点1:首先获取数组长度和插入虚拟头节点这两个操作并不陌生,这里补充一个便捷写法

ListNode* dummyhead = new ListNode(0, head);

 注意点2:我自己在做的时候关于第二次遍历反复出错,这里在计算长度的时候一定要注意是从虚拟头节点(i=0)还是头节点(i = 1)来进行。

3.面试题02.07.链表相交

题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)

题目解析

        首先这道题可以直到我们需要两个指针分别从headA和headB来解决遍历的,那么问题是,怎么确定headA和headB处于同一位置。

        借助LeetCode的Krahets的思路:先假设两个链表存在公共的尾部,长度为c;链表A的长度为a,链表B的长度为b,公共的节点node。

        当指针A遍历结束A链表,开始遍历B链表,到达node,此时一共需要步数:a+(b-c)

        当指针B遍历结束B链表,开始遍历A链表,到达node,此时一共需要步数:b+(a-c)

        这时不难发现两者相等。

C++代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        if(headA == nullptr || headB == nullptr)
        {
            return nullptr;
        }
        //设置指针1
        ListNode * cur1 = headA;

        //设置指针2
        ListNode * cur2 = headB;

        while(cur1 != cur2)
        {
            if(cur1 == nullptr)
            {
                cur1 = headB;
            }
            else
            {
                cur1 = cur1->next;
            }
            if(cur2 ==nullptr)
            {
                cur2 = headA;
            }
            else
            {
                cur2 = cur2->next; 
            }
            
        }
        return cur1;

    }
};

注意点1:对于返回值也有两种情况,首先,有公共节点,直接返回node,也就是现在指针的位置;没有公共节点的,两个遍历结束,也可以根据要求直接返回指针。

4.Leetcode142:环形链表||

 题目链接:142. 环形链表 II - 力扣(LeetCode)

题目解析

        有两个问题需要解决,第一个是如何确定有没有环?

        第二个是如何确定环的入口?

        针对第一个问题,我们可以采用快慢双指针,就像是小学数学中的追及问题,将快指针的步长设置为2,慢指针的步长设置为1,那么在移动指针的过程中,如果存在环,那么快慢指针一定在圆环上重合。

        针对第二个问题,这里参考代码随想录 (programmercarl.com)中的思路,可以确定快慢指针重合之后的数量关系。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) 
    {
        //快指针
        ListNode* fast = head;

        //慢指针
        ListNode* slow = head;

        //追及问题
        while(fast != nullptr)
        {
            slow = slow->next;
            if(fast->next == nullptr)
            {
                return nullptr;
            }
            fast = fast->next->next;
            if(fast == slow)
            {
                ListNode* ptr = head;
                while(ptr != slow)
                {
                    ptr = ptr->next;
                    slow = slow->next;
                }
            }
            return ptr;
        }
        return nullptr;
    }
};

注意点1:其实在解决了如何确定有没有环的问题之后,转化为代码的困难集中到了边界条件如何设置。首先来看追及问题的边界条件是指针遍历结束,这时链表不存在环,返回值自然也就是空,又因为快指针先遍历结束,所以条件为快指针不指向空。 

注意点2:针对于快指针,因为它的步长是2,所以也要检查当前位置的后一个指针是不是空。不检查后两个的原因是,我们已经在外层设置了循环的条件是快指针的指向为空停止。

总结


打卡第四天,对我来说很有难度,耽搁到了今天,所以坚持!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值