代码算法训练营day4 | 24两两交换链表中的节点、19删除链表的倒数第N个节点、 面试题 02.07. 链表相交、142环形链表II

4 篇文章 0 订阅

24 两两交换链表中的节点

题目链接
状态:指针换来换去头麻了
文档:programmercarl.com

思路:
思路图解

实现中的问题:
循环的终止条件没弄明白,看了文档后算是自己理解了点。如果链表的元素个数是奇数:比如只有下标0,那么就是cur->next->next != NULL。如果链表的元素是偶数:比如没有元素,那么就是cur->next != NULL。
并且,两个的位置不能反,若先判断cur->next->next != nullptr的话,如果出现cur->next == nullptr,那上面这个语句就会出现空指针报错。这两个终止条件意思是既要满足奇数的也要满足偶数的。
循环终止条件图解
并且一定要同时满足这两个条件。否则可能会出现空指针异常。就像节点3,要保护节点3是一个有效值,也就是cur->next->next->next是一个有效值。

代码:

/**
 * 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) {
        //指针要指向要交换的两个元素之前的位置
        //决定一下终止条件 奇数:cur->next->next == NULL
        //偶数:cur->next == NULL
        //因为指针指向会变,所以要先保存一下节点1、节点3的值
        
        //设置虚拟节点
        ListNode* dummy_node = new ListNode(0);
        dummy_node->next = head; //设置虚拟头节点

        //将临时指针指向虚拟头节点,这样才能控制要交换的那两个节点
        ListNode* cur = dummy_node;  
        //设置临时节点存储断链的节点
        ListNode* temp1; //存储节点1的位置
        ListNode* temp2; //存储节点3的位置
        while(cur->next != nullptr && cur->next->next != nullptr)
        {
            temp1 = cur->next;
            temp2 = cur->next->next->next;
            cur->next = cur->next->next;
            cur->next->next = temp1;
            temp1->next = temp2;

            //cur向后移动两个
            cur = cur->next->next;
        }
        //返回头节点
        return dummy_node->next;
    }
};

总结: 首先要保护的就是前链断掉的节点,其次再看哪个节点前后都断了,就保护它。


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

题目链接
状态:磕磕绊绊
文档:programmercarl.com

思路:
既然想删除节点,必不可少的就是遍历节点,创建一个遍历指针cur。
要注意的是这里的第n个,n是从1开始并不是从0开始的。
因为是倒数,所以我需要先计算出来链表的元素个数,这样才知道倒数第n个到底是正数的第几个元素。

注意:

delete cur; 

用完指针一定要置空,不然变成野指针会很危险。

cur = NULL;

代码:

/**
 * 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* removeNthFromEnd(ListNode* head, int n) {
        //创建一个虚拟头节点
        ListNode* dummy_Head = new ListNode(0);
        dummy_Head->next = head;
        //临时保存cur指向的目标节点的前一个节点
        ListNode* temp;
        //创建一个临时指针,遍历元素
        ListNode* cur = dummy_Head;
        //计算链表元素个数
        int size = 0;
        
        while(cur->next != NULL)
        {
            size++;
            cur = cur->next;
        }
        cout<<"元素个数为:"<<size<<endl;

        cur = dummy_Head;
        for(int i = 0;i<=size-n;i++)
        {
            temp = cur;
            cur = cur->next;
        }
        //删除节点
        temp->next = cur->next;
        delete cur;
        cur = NULL;

        return dummy_Head->next;
    }
};

面试题 02.07. 链表相交

题目链接
状态:很头疼 没太弄懂
文档:programmercarl.com

疑点:
示例
在这个示例中,我很疑惑为什么相交的点不是1,而是8.所以我去测试用例中测试了一下。
第一种 交点是8
在这里插入图片描述
第二种,交点是1
在这里插入图片描述
两者结果的不同 取决于 skipA和skipB的值有所变动。
所以在实现的时候就正常实现,不用管那么多了。

思路:
要注意的一点是,这里的相同指的是指针相同,而不是元素的数值相同。指针相同的意思就是内存地址是相同的,我在看了很多博客之后,推荐
推荐阅读
用双指针 pA 、pB 循环两个链表,链表 A 循环结束就循环链表 B,链表 B 循环结束就循环链表 A;当 pA == pB 时就是交点,因为两个指针移动的步数一样;
思路图解

代码:

/**
 * 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 == NULL || headB == NULL) return NULL;
        ListNode* pA = headA;
        ListNode* pB = headB;
        while(pA!=pB)
        {
            //pA不为空的时候指向下一个 为空的时候指向b的头节点
            pA = pA == NULL?headB:pA->next; 
            pB = pB == NULL?headA:pB->next;
        }
        return pA;
    }
};

142 环形链表II

题目链接
状态:没做出来
文档:programmercarl.com

看完文档后的理解:
Q:为什么会想起使用双指针呢?A:因为这道题在数学中实际上是同向相遇问题。
可以用双指针来判断到底有没有环。如果没有环的话,快慢指针永远都不会相遇;如果有环的话,快慢指针会相遇的。
Q:那什么情况下才会相遇?A:快指针每次走2个节点,而慢指针每次走1个节点,这样快指针比慢指针每次多走一个节点,那么快慢指针终究会遇上的。如果快指针每次比慢指针走多走2个节点,那么可能就会跳过慢指针从而无法相遇。
Q:为什么慢指针进入环的第一圈就被快指针给遇到了呢?
A:看图解

整体思路如下:
整体思路
代码:

/**
 * 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 !=NULL && fast->next!=NULL)
        {
            fast = fast->next->next;
            slow = slow->next;
            //相遇了,找到了环(在环的某处相遇了)
            if(fast == slow)
            {
                ListNode* index1 = fast;  //记录相遇节点
                ListNode* index2 = head;  //记录头节点
                //在相遇前 不停往前走 寻找入口
                //因为是共享一个节点 所以不应该说值相等 该说指针相等
                while(index1 != index2)
                {
                    //head到入口的距离 = 相遇点到入口的距离
                    index1 = index1->next;
                    index2 = index2->next;
                }
                //返回环的入口
                return index1;
            }
        }
        return NULL;
    }
};

总结:数学逻辑性真的很重要呜呜呜

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值