• 【代码随想录】算法训练营 第四天 第二章 链表 Part 2


    24. 两两交换链表中的节点

    思路

    为了使得头结点的处理不用特殊化,所以依然设置一个虚拟头结点dummy,每次要交换的时候都要判断接下来的两个结点是否为空结点,即nullptr,若非空即可交换;

    交换的时候,用一个指针保存第一个结点,用另一个指针保存两个结点后的一个结点,为什么不保存第二个用于交换的结点呢?因为在后面重新连接结点时,前一个结点必须连接第三个结点,所以得先存着,不然交换后就找不到了。

    代码

    1. class Solution {
    2. public:
    3. ListNode* swapPairs(ListNode* head) {
    4. ListNode* dummy = new ListNode(0);
    5. dummy->next = head;
    6. ListNode* cur = dummy;
    7. while (cur->next != nullptr && cur->next->next != nullptr) {
    8. ListNode* tmp1 = cur->next; // 暂存要交换的第一个结点
    9. ListNode* tmp2 = cur->next->next->next; // 暂存交换结点的后一个结点
    10. cur->next = cur->next->next;
    11. cur->next->next = tmp1;
    12. cur->next->next->next = tmp2; // 从第一个开始,三个找下家
    13. cur = cur->next->next;
    14. }
    15. return dummy->next;
    16. }
    17. };

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

    题目

    思路

    使用双指针法,一快一慢,快的在前面跑,离慢的要有n+1个结点,这样的话,如果快指针指到空了,就可以删除慢指针指向结点的下一个结点;

    这里依然需要设置虚拟头结点,而且快指针要先跑n个结点后,再跑到下一个结点,前n个结点要保证快指针指向的结点非空,因为题目都要求倒数第n个了,所以至少也要有n个非空结点,接下来再往下跑一个的话就不用保证了,空也可以。

    代码

    1. class Solution {
    2. public:
    3. ListNode* removeNthFromEnd(ListNode* head, int n) {
    4. ListNode* dummy = new ListNode(0);
    5. dummy->next = head;
    6. ListNode* fast = dummy;
    7. ListNode* slow = dummy;
    8. while (n-- && fast != nullptr) {
    9. fast = fast->next;
    10. }
    11. fast = fast->next;
    12. while (fast != nullptr) {
    13. fast = fast->next;
    14. slow = slow->next;
    15. }
    16. ListNode* tmp = slow->next;
    17. slow->next = slow->next->next;
    18. delete tmp;
    19. return dummy->next;
    20. }
    21. };

    面试题 02.07. 链表相交

    题目

    思路

    这道题也用不上算法,因为两个链表要么存在最后几个结点全部相同,要不就不存在相交,所以可以先将两个链表中长的一条缩短到短的那条的长度,那样就可以对剩下的相同长度的结点进行一一比较了,只要遇到相同的结点,就直接返回这个结点的指针即可,因为这道题中不会出现其他特殊情况。

    代码

    1. class Solution {
    2. public:
    3. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    4. ListNode* curA = headA;
    5. ListNode* curB = headB;
    6. int lenA = 0, lenB = 0;
    7. while (curA != nullptr) {
    8. lenA++;
    9. curA = curA->next;
    10. }
    11. while (curB != nullptr) {
    12. lenB++;
    13. curB = curB->next;
    14. }
    15. curA = headA;
    16. curB = headB;
    17. if (lenB > lenA) {
    18. swap (lenB, lenA);
    19. swap (curB, curA);
    20. }
    21. int gap = lenA - lenB;
    22. while (gap--) {
    23. curA = curA->next;
    24. }
    25. while (curA != nullptr) {
    26. if (curA == curB) {
    27. return curA;
    28. }
    29. curA = curA->next;
    30. curB = curB->next;
    31. }
    32. return NULL;
    33. }
    34. };

    142. 环形链表 ll

    题目

    思路

    画图模拟即可,但是还是要结合数学推导,推导详见代码随想录,这里我来概括一下主要思路;

    首先,我们要判断一个链表是否存在闭环,可以设置一个快指针和一个慢指针,快指针每次向前移动两个结点,慢指针移动一个结点,如果存在闭环的话,那么快慢指针一定会相遇,因为快指针和慢指针之间有相对速度,虽然一开始快指针移动较快,但是它也会先进入闭环,快指针总会在闭环中追上后来的慢指针,经由数学推导得知,闭环开始的地方与头结点的距离和闭环开始的地方与相遇处的距离相等,这样就可以用代码实现了。

    代码

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * ListNode *next;
    6. * ListNode(int x) : val(x), next(NULL) {}
    7. * };
    8. */
    9. class Solution {
    10. public:
    11. ListNode *detectCycle(ListNode *head) {
    12. ListNode* fast = head;
    13. ListNode* slow = head;
    14. while (fast != NULL && fast->next != NULL) {
    15. slow = slow->next;
    16. fast = fast->next->next;
    17. if (slow == fast) {
    18. ListNode* index1 = fast;
    19. ListNode* index2 = head;
    20. while (index1 != index2) {
    21. index1 = index1->next;
    22. index2 = index2->next;
    23. }
    24. return index2;
    25. }
    26. }
    27. return NULL;
    28. }
    29. };

  • 相关阅读:
    debug - 用Procmon记录目标程序启动后的操作
    авито/avito ru 代表什么意思?投放广告的时候需要加入关键词中吗?
    可变性隔离
    【前端基础小案例】HTML+CSS打造精美选项卡菜单效果
    9月7日作业
    能掌握未来3个发财趋势的人
    防火墙部署模式 -- 单臂路由模式
    常用docker镜像启动了解
    Docker命令 常用中间件运维部署,方便构建自己服务
    OpenCV + Kotlin 实现 USB 摄像头(相机)实时画面、拍照
  • 原文地址:https://blog.csdn.net/Summerison/article/details/133874768