链表原地反转模式可在不使用额外空间的情况下,反转链表的部分节点。

当你需要反转链表的某些部分时,可使用该模式。

【Leetcode206】反转链表

过程:需要三个指针,使用了迭代的做法;当然还有递归的方法,但不太容易理解

核心代码:

q=p;//指向前一个

p=q;q=r;r=r->next;//三个指针都跳到下一个

注意临界条件,当操作完成时,q和r指向nullptr,而p正好指向最后一个操作的节点,应该返回p即可。

代码中用prev\curr\nextTemp分别代替p\q\r,从用户输入端接收数据(-1为结束输入的标志),创建链表,然后再进行反转。

#include <iostream>
using namespace std;

// 定义链表节点结构
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

// 反转链表的迭代实现
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr;  // 前一个节点
        ListNode* curr = head;    // 当前节点

        while (curr != nullptr) {
            ListNode* nextTemp = curr->next; // 保存下一个节点
            curr->next = prev;              // 反转当前节点的指向
            prev = curr;                   // 移动 prev 到当前节点
            curr = nextTemp;                // 移动 curr 到下一个节点
        }

        return prev; // 返回新的头节点
    }
};

// 打印链表
void printList(ListNode* head) {
    ListNode* current = head;
    while (current != nullptr) {
        cout << current->val << " ";
        current = current->next;
    }
    cout << endl;
}

// 接收输入并创建链表
ListNode* createList() {
    ListNode* head = nullptr; // 链表头节点
    ListNode* tail = nullptr; // 链表尾节点
    int value;

    cout << "请输入链表节点的值(以 -1 结束输入):" << endl;
    while (true) {
        cin >> value;
        if (value == -1) { // 输入 -1 结束
            break;
        }

        // 创建新节点
        ListNode* newNode = new ListNode(value);

        if (head == nullptr) { // 如果链表为空,新节点作为头节点
            head = newNode;
            tail = newNode;
        } else { // 否则将新节点添加到链表尾部
            tail->next = newNode;
            tail = newNode;
        }
    }

    return head; // 返回链表的头节点
}

int main() {
    // 创建链表
    ListNode* head = createList();

    cout << "原始链表: ";
    printList(head);

    // 反转链表
    Solution solution;
    ListNode* reversedHead = solution.reverseList(head);

    cout << "反转后的链表: ";
    printList(reversedHead);

    return 0;
}

递归方法:

ListNode* reverseList(ListNode* head) {
        // 递归终止条件:如果链表为空或者只有一个节点,直接返回该节点
        if (head == nullptr || head->next == nullptr) {
            return head;
        }
        // 递归调用 reverseList 函数,反转从 head->next 开始的后续链表
        ListNode* newHead = reverseList(head->next);
        // 将当前节点的下一个节点的 next 指针指向当前节点,实现局部反转
        head->next->next = head;
        // 将当前节点的 next 指针置为 nullptr,避免形成循环
        head->next = nullptr;
        // 返回反转后链表的头节点
        return newHead;
    }

【Leetcode92】部分连续地方反转

分析:部分链中反转(2、3、4)加上头尾两节点的处理2的指针域和指向4的指针。所以考虑在上个题的基础上,标记变换的连续链表两边节点以及与之相邻的两个节点:(共四个p1->1;p2->2;q1->4;1q2->5)

反转之外的部分:p1->next=q1;p2->next=q2

反转之内(L=p2,r=q1)的跟上个题目一样,改一下遍历开始和结束的位置即可

注意:当 L为 1 时,也就是要反转的区间从链表头节点开始,此时 p1 为空指针,需要特殊处理。建立一个虚拟头节点 dummy,这样就能避免处理 L 为 1 时的边界情况。也就是建立一个空的头结点。

class Solution {
public:
    // 反转从 p2 到 q1 之间的链表
    ListNode* reverseAllList(ListNode* p2, ListNode* q1) {
        ListNode* p = nullptr;  // 前一个节点
        ListNode* curr = p2;    // 当前节点

        while (curr != q1) {
            ListNode* nextTemp = curr->next; // 保存下一个节点
            curr->next = p;              // 反转当前节点的指向
            p = curr;                   // 移动 p 到当前节点
            curr = nextTemp;                // 移动 curr 到下一个节点
        }

        return p; // 返回新的头节点
    }

    // 反转链表中从第 l 个节点到第 r 个节点的部分
    ListNode* linkList(ListNode* head, int l, int r) {
        if (l == r) return head; // 如果 l 和 r 相等,无需反转

        ListNode dummy(0);//建立一个空的头结点
        dummy.next = head;
        ListNode* p1 = &dummy; // 指向第 l-1 个节点
        ListNode* p2 = nullptr; // 指向第 l 个节点
        ListNode* q1 = nullptr; // 指向第 r 个节点
        ListNode* q2 = nullptr; // 指向第 r+1 个节点

        // 找到第 l-1 个节点
        for (int i = 1; i < l; ++i) {
            p1 = p1->next;
        }
        p2 = p1->next;

        // 找到第 r 个节点
        q1 = p2;
        for (int j = l; j < r; ++j) {
            q1 = q1->next;
        }
        q2 = q1->next;

        // 反转从 p2 到 q1 之间的链表
        p1->next = reverseAllList(p2, q2);
        p2->next = q2;

        return dummy.next;
    }
};

【Leetcode24】两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

思考与转化:很像2个元素交换(需要引入一个新变量),不过这次交换的是位置。

初始化temp:

ListNode* temp = new ListNode(0); temp->next = head;

核心变化:

temp->node2;// temp->next = node2

node1->node3;// node1->next = node2->next

node2->node1;// node2->next = node1;

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        // 创建虚拟头节点,方便处理头节点交换的情况
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        // temp 指针用于遍历链表并控制交换操作
        ListNode* temp = dummyHead;

        // 当 temp 后面至少还有两个节点时,进行交换操作
        while (temp->next != nullptr && temp->next->next != nullptr) {
            // 标记要交换的两个节点
            ListNode* node1 = temp->next;
            ListNode* node2 = temp->next->next;

            // 进行节点交换操作
            temp->next = node2;
            node1->next = node2->next;
            node2->next = node1;

            // temp 指针移动到交换后这一对节点的末尾位置,为下一次交换做准备
            temp = node1;
        }

        // 获取交换后的链表头节点
        ListNode* ans = dummyHead->next;
        // 释放虚拟头节点的内存
        delete dummyHead;
        return ans;
    }
};

提醒:遇到链表就画图

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐