链表

链表

链表翻转

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

关键思路

反转一个链表,只需要改变指向就行了

1.首先定义一个当前节点cur,指向头节点head,再定义一个pre指针,指向nullptr,这个初始化很重要

2.之后就可开始反转了,在翻转的时候,我们会将当前cur节点指向pre,但是一旦完成后就与之后的节点断开了,所以我们需要一个临时节点temp预先来保存cur->next

3.之后就是循环遍历了,我们先不确定条件,循环的时候做什么?应该是先进行保存之后的节点,然后将当前节点cur的指向指为pre,自此完成了一个节点翻转,需要进行下一个节点,这时应该先将pre节点变成cur节点,而cur节点应该是预先保留的临时节点

4.哪结束条件呢?应该cur不为空,如果cur为空,说明临时节点也就是某个节点的next下一个节点为空,这时候只要返回pre就行了

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur != nullptr){
            ListNode* temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

采用递归的方式实现

依旧沿用之前双指针的思路实现。

class Solution {
public:
    ListNode* reverse(ListNode* cur, ListNode* pre){
        if(cur == nullptr) return pre; 
        ListNode* temp = cur->next; // 记录后一个节点
        cur->next = pre; //将当前的指向指为前一个节点
        return reverse(temp,cur); // 开始处理后一个节点后当前节点的指向
    }

    ListNode* reverseList(ListNode* head) {
        return reverse(head, nullptr); // 和之前的初始化逻辑一样
    }
    
};

两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

这道题,引入虚拟头节点会容易很多。

image-20260304124116289

通过引入虚拟头节点来保证元素的交换,首先需要明确,如果我们想交换两个相邻节点,那么需要在这两个节点之前,比如上述例子,cur是在1、2两个节点之前的,我要把cur->next指向2,2中的next指向1(原来的cur中的指向cur->next),1在指向3;然后更新cur,下一次cur应该需要向后移两个节点,cur=cur->next->next

首先,我们需要解决循环条件的问题,如果链表元素是偶数,那么cur->next,cur->next->next都不为空的情况,那么就要交换,如果是0呢?也是满足条件的,cur->next为空,退出循环;那如果元素是奇数呢?奇数会出现的情况是这样的, cur->next != nullptr,但是cur->next->next = nullptr,这样不交换就行了,没问题。

接下来就开始交换流程了,我们需要一些临时的变量来保存节点,因为在进行交换的时候链接会丢失;那需要保存哪些节点呢?上面的例子告诉我们,我们需要保存1(temp1)、3(temp2)这两个节点,因为我们的链会从cur处断开,cur指向next的next也就是2cur = temp1->next,现在节点情况是 cur --- 2,之后是cur --- 2 --- 1,也就是cur->next->next = temp1,最后是1指3,原先保存的节点temp1->next = temp2,这个temp2就是开始保存下来的2的指向。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyhead = new ListNode(0,head);
        ListNode* cur = dummyhead;
        while(cur->next != nullptr && cur->next->next != nullptr){
            ListNode* temp1 = cur->next;
            ListNode* temp2 = cur->next->next->next;
            cur->next = temp1->next;
            temp1->next->next = temp1;
            // cur->next->next = temp1;
            temp1->next = temp2;
            cur = cur->next->next;
        }
        return dummyhead->next;
    }
};

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

蛮有意思的一道题,首先要明确,删除一个节点,我们需要跑到这个节点的前一个节点。

推荐使用虚拟头节点,这样方便处理删除实际头节点的逻辑。

我们定义一个快慢指针,fast、slow,让快指针先跑n次,然后两个指针一起跑到结尾,这时候,slow所处的位置就是需要删除节点的前面了。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyhead = new ListNode(0,head);
        ListNode* start = dummyhead, *end = dummyhead;
        // 两个指针都处在虚拟头节点的位置
        for(int i = 0; i < n; ++i){ // 快指针开跑
            end = end->next;
        }
        while(end->next){
            // 之后一起跑,直到快指针指向nullptr,说明快指针已经跑到最后一个节点了
            end = end->next;
            start = start->next;
        }
        ListNode* temp = start->next; // 删除的节点为慢指针后面的一个节点
        start->next = start->next->next;
        delete temp;
        return dummyhead->next;
    }
};

环形链表II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

image-20260304150005967

这道题需要一点数学计算,我们通过定义快慢指针,快指针每次移动2个节点,慢指针每次移动1个节点,如果两个指针相遇,说明有环。我们将从头节点到环入口的距离定义为x,环中相遇定义为y,相遇点到入口为z

image-20260304150334409

可得出公式:(n表示转的圈数)
$$
(x+y) ×2 = x+y+(y+z)×n\
x=n(y+z)-y
$$
慢指针一定是滞后于快指针的,那么如果在环内相遇,那么相遇点这个位置很关键,我们根据公式得到了x=n(y+z)-y这样的式子,也就是说从头节点到入口节点的距离 等于 圈的节点数的公倍数(也许走了1圈或n圈) - 相遇位置到节点位置 y 的距离;也就是如果在这个相遇点和头节点分别开始以一个步长开走,一定会在入口处相遇,只是在环中可能走过多个圈,没问题,公式就是这个意思。

ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != nullptr && fast->next != nullptr){
            fast = fast->next->next;
            slow = slow->next;
            while(slow == fast){
                ListNode* index1 = slow;
                ListNode* index2 = head;
                while(index1 != index2){
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return nullptr;
    }
posted @ 2026-03-04 15:25  wenyiGamecpp  阅读(26)  评论(0)    收藏  举报