代码随想录算法训练营Day4 | 24. 两两交换链表中的节、19.删除链表倒数第N个节点、 链表相交、142.环形链表II

文章介绍了如何在链表中进行两两交换节点,删除倒数第N个节点,查找链表相交点以及检测环形链表的入口。涉及技术包括虚拟头结点的使用、双指针法和环形链表的判断方法。

24 两两交换链表中的节点

  • 题意

    • 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
    • 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
    • 示例:

  • 思路

    • 操作1,2节点需要1前面一个节点即dummyhead
    • 弄清楚顺序:三部曲
      • 初始时,cur指向虚拟头结点,然后进行如下三步:

    • 在开始步骤之前需要保留节点cur->next和cur->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) {
        ListNode* dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode* cur = dummyhead;
        while (cur->next != NULL && cur->next->next != NULL) {//cur->next是偶数情况的末尾,cur->next->next是奇数情况的末尾;且只能cur->next放在前面,防止cur->next->next空指针的情况
            ListNode* temp = cur->next;//保留1号节点
            ListNode* temp1 = cur->next->next->next;//保留3号节点
            cur->next = cur->next->next;//cur指向2
            cur->next->next = temp;//2指向原1号节点
            temp->next = temp1;//原1号节点指向3
            cur = cur->next->next;//cur指针向后变动两位,开始新的循环
        }//循环类推,以cur为标准变动
        return dummyhead->next;//返回起始点
        delete dummyhead;
    }
};

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

  • 题意

    • 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。进阶:你能尝试使用一趟扫描实现吗?
    • 示例 :
      • 输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 示例 2:
      • 输入:head = [1], n = 1 输出:[] 示例 3:
      • 输入:head = [1,2], n = 1 输出:[1]
  • 思路

    • 双指针的经典应用:如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
    • 定义fast指针和slow指针,初始值为虚拟头结点;
    • 理解,fast比slow的差值为n,即fast在重点NULL处,slow与fast的差值为n,即为倒数第n个;
    • 实际的执行过程中需要让fast多走一步,同步移动slow则少走一步,这样移动的过程才能让slow指向删除节点的上一个节点便于做删除操作;
/**
 * 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* dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode* fast = dummyhead;
        ListNode* slow = dummyhead;
        n++;        //fast多移动一格,即slow少移动一格,slow指向被删除节点的上一个节点,便于删除
        while (n-- && fast != nullptr) {
            fast = fast->next;
        }
        while (fast != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }//fast,slow同步移动
        ListNode* temp = slow->next;
        slow->next = slow->next->next;
        delete temp;
        return dummyhead->next;
    }
};

 面试题 02.07 链表相交

  • 题意

    • 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
      • 图示两个链表在节点 c1 开始相交:

      • 注意,函数返回结果后,链表必须 保持其原始结构 。
      • 题目数据 保证 整个链式结构中不存在环。
    • 示例:

      • 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
      • 输出:Intersected at '8'
      • 解释:相交节点的值为 8
      • (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
   template<typename T>
   void Swap(T& a, T& b)
   {
       T temp;
       temp = a;
       a = b;
       b = temp;
   }
   ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
       //先找到两个链表的长度
       int lenA = 0, lenB = 0;
       ListNode* curA = headA;
       ListNode* curB = headB;
       while (curA != NULL) {
           lenA++;
           curA = curA->next;
       }
       while (curB != NULL) {
           lenB++;
           curB = curB->next;
       }
       //找到链表A,B的长度之后,让curA和curB回到初始值;
       curA = headA;
       curB = headB;
       //让大的减去小的;即A始终比B长
       if (lenB > lenA) {
           swap(lenA, lenB);
           swap(curA, curB);
       }
       //计算两者的差,让A与B在同一起跑线
       int gap = lenA - lenB;
       while (gap--) {
           curA = curA->next;
       }
       //相交的定义是节点相等即地址相等而不是值相等
       while (curB != NULL) {
           if (curA == curB) return curB;
           else {
               curA = curA->next;
               curB = curB->next;
           }
       }
       //经过了while循环依然没找到相交的点,则返回 NULL
       return NULL;
   }
};

 142  环形链表II

/**
 * 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;
        //fast前进两步所以是直接等于NULL或者next等于NULL还需要注意顺序
        while (fast != NULL && fast->next != NULL) {
            fast = fast->next->next;
            slow = slow->next;
            //fast等于slow说明存在环节点,现在的目标是找到环节点的位置
            if (fast == slow) {
                ListNode* index1 = fast;//fast和slow相遇的位置
                ListNode* index2 = head;//头位置
                //index1和index2前进,相遇点为环节点位置
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2;
            }
        }
        return NULL;//如果整个循环过后,fast于slow没有相遇,说明没有环节点
    }
};

总结&&碎碎念

链表练到后面果然是熟悉了不少,例如,设置虚拟头结点,删除一个节点一定要找到被删除节点的前一个节点,节点之间跳转要注意跳转保存跳转节点的下一个节点的位置防止丢失等核心要点还是领会了的,当然也有一些比较有创意的题例如环形列表||,一开始真的是毫无想法啊,就像是遇到了新概念题型一样,上来很懵逼,但是看完视频切中“x=z”这样的要点之后,代码也并不是很难写,逐渐体会到了算法的“巧妙”,前面还有很多有意思的东西,加把劲学习新东西也要及时复习啊,共勉~(PS:冬天起床真的困难,不是赖床,是单纯醒不了~哎,我怎么一点都不着急呢,急急急~) 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值