题解 | #链表内指定区间反转#

链表内指定区间反转

http://www.nowcoder.com/practice/b58434e200a648c589ca2063f1faf58c

题目描述
将一个链表m位置到n位置之间的区间反转,要求时间复杂度 O(n),空间复杂度O(1)。
例如:
给出的链表为 1→2→3→4→5→NULL, m=2,n=4
返回1→4→3→2→5→NULL.
注意:
给出的m,n满足以下条件:
1≤m≤n≤链表长度

(参考杭电 De梦的题解)

方法一:直接暴力求解

求解思路
对于反转链表,我们在求解的时候,可以引入一个next节点,然后对链表的局部进行反转。这样暴力求解,即可求出答案。

图片说明

解题代码

import java.util.*;
public class Solution {
       // 参考De梦代码
    public ListNode reverseBetween (ListNode head, int m, int n) {

        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        ListNode pre = dummyNode;
        //pre结点走到m的前一个结点
        for(int i=0;i<m-1;i++){
            pre = pre.next;
        }

        //rightnode走到n的结点
        ListNode rigthNode = pre;
        for(int i=0;i<n-m+1;i++){
            rigthNode = rigthNode.next;
        }

        //取子链表
        ListNode leftNode = pre.next;
        ListNode cur = rigthNode.next;

        //分离子链表
        pre.next=null;
        rigthNode.next=null;

        //反转局部链表,参考De梦的code
        reverseLinkedList(leftNode);

        //拼接链表
        pre.next = rigthNode;
        leftNode.next = cur;
        return dummyNode.next;
    }
    //反转局部链表
    private void reverseLinkedList(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        while(cur!=null){
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
    }
}

复杂度分析
时间复杂度:O(N),N为节点的个数
空间复杂度:O(1),没有使用额外的内存空间

方法二:递归的思想求解

求解思路
对于反转链表,我们使用递归的思想,将大问题转换为小问题,然后进行相应的求解即可。对于递归过程,判断n的数值,当n为1时返回head指针,否则进行递归,并且反转链表,最后进行拼接,返回拼接之后的链表。

图片说明

解题代码

class Solution {
public:
    ListNode* tmp = NULL;
    ListNode* reverseBetween(ListNode* head, int m,int n){
        if( m == 1) return reverse(head, n);
        ListNode* p = reverseBetween(head->next, m-1,n-1); // m,n在子问题中要减1
        head->next = p; //拼接反转之后的部分
        return head;
    }
    ListNode* reverse(ListNode* head,int n)
    {//反转链表,递归的思想,参考贵州大学的漫漫云天自翱翔的思路
        if(n == 1)
        {
            tmp = head->next;
            return head;
        }
        ListNode* new_head = reverse(head->next,n-1);  //子问题,位置要减1
        head->next->next = head; //反转 
        head->next = tmp;  //拼接尾部
        return new_head;
    }
};

复杂度分析
时间复杂度:一层循环,时间复杂度为O(N)
空间复杂度:其最坏递归空间复杂度为O(N).

算法 文章被收录于专栏

算法题的题解以及感受

全部评论
在您的代码中,有几个错误和潜在的问题点需要修正。主要是关于链表反转部分和链表拼接的逻辑。以下是修改后的代码,包括对错误点的修正和注释的添加: java import java.util.*; class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class Solution { // 反转链表中的m到n部分 public ListNode reverseBetween(ListNode head, int m, int n) { if (head == null || head.next == null || m >= n) { return head; } ListNode dummyNode = new ListNode(-1); dummyNode.next = head; ListNode pre = dummyNode; // pre结点走到m的前一个结点 for (int i = 0; i < m - 1; i++) { pre = pre.next; } ListNode leftNode = pre.next; // m位置的节点 ListNode rigthNode = leftNode; // rigthNode走到n的下一个结点 for (int i = 0; i < n - m + 1; i++) { rigthNode = rigthNode.next; } // 反转m到n之间的链表 reverseLinkedList(leftNode, rigthNode); // 拼接链表 pre.next = rigthNode.prev; // 因为在reverseLinkedList中我们使用了prev来记录前一个节点 leftNode.next = rigthNode.next; return dummyNode.next; } // 反转链表中的一部分 private void reverseLinkedList(ListNode start, ListNode end) { ListNode prev = null; ListNode current = start; while (current != end) { ListNode next = current.next; current.next = prev; prev = current; current = next; } // 如果需要,可以在这里给start(现在的尾节点)添加一个prev指针,指向反转前的上一个节点 // 但在这个问题中,我们只需要保证反转后正确连接即可 } } 注意: 原始代码中rigthNode拼写错误,已更正为rigthNode(虽然更标准的可能是rightNode)。 我修改了reverseLinkedList方法,使其接受两个参数:start(开始反转的节点)和end(反转结束后的下一个节点)。这样可以更清晰地控制反转的范围,并且避免了对链表进行不必要的断开和重新连接。 您的原始代码试图将pre.next和rigthNode.next设为null,这在反转链表后是不必要的,并且会导致链表在m到n之间的部分与原始链表断开。 在reverseLinkedList方法中,我没有添加prev指针到ListNode类中,因为这会改变原始链表的结构。如果确实需要,可以在ListNode类中添加prev字段,但在这个问题中,我们不需要这样做。 我添加了对m >= n的检查,因为当m大于等于n时,没有需要反转的节点。 如果head或head.next为null,则直接返回head,因为没有节点需要反转。
1 回复 分享
发布于 2024-08-11 23:09 广东
在您的代码中,有几个关键的问题需要解决,以确保 reverseBetween 函数能够正确地反转链表中的指定部分。首先,您的 reverse 函数设计得很好,用于递归地反转链表的前 n 个节点。但是,reverseBetween 函数中存在一些逻辑错误,特别是在处理递归调用和结果拼接时。 以下是修改后的代码,包括必要的注释来解释每个步骤: cpp class Solution { public: ListNode* reverseBetween(ListNode* head, int m, int n) { // 如果链表为空或m大于n,则直接返回原链表 if (!head || m > n) return head; // 如果m等于1,即需要反转的部分从链表头部开始 if (m == 1) { return reverseN(head, n); } // 递归处理子链表,注意m和n在子问题中都要减1 head->next = reverseBetween(head->next, m - 1, n - 1); return head; } private: ListNode* reverseN(ListNode* head, int n) { // 基本情况:如果n为1,则不需要反转,直接返回头节点 if (n == 1) { // 这里不需要tmp变量,因为我们可以直接返回head return head; } // 递归反转接下来的n-1个节点,并获取新的头节点 ListNode* newHead = reverseN(head->next, n - 1); // 反转当前节点和下一个节点的指向 head->next->next = head; // 将当前节点的next指向nullptr(或原链表的第n+1个节点,但在这里我们不需要) head->next = nullptr; // 返回新的头节点 return newHead; } }; 主要修改点: 函数命名:将 reverse 改为 reverseN,以更清晰地表明该函数的作用是反转链表的前 n 个节点。 移除 tmp 变量:在 reverseN 函数中,我们不需要 tmp 变量来存储下一个节点,因为我们可以直接通过 head->next 访问它,并在反转后将其设置为 nullptr(或更准确地,设置为原链表的第 n+1 个节点,但在这个递归实现中我们不需要这样做)。 处理递归的基本情况:在 reverseN 中,当 n 等于 1 时,我们直接返回头节点,因为不需要反转。 递归调用和结果拼接:在 reverseBetween 中,如果 m 不等于 1,我们递归地调用 reverseBetween 来处理子链表,并将结果链接回原链表的其余部分。注意,这里我们不需要修改 head 节点的值,只是修改 head->next 的指向。 这样修改后的代码应该能够正确地反转链表中的指定部分。
点赞 回复 分享
发布于 2024-08-11 23:13 广东

相关推荐

能干的三文鱼刷了100道题:公司可能有弄嵌入式需要会画pcb的需求,而且pcb能快速直观看出一个人某方面的实力。看看是否有面试资格。问你问题也能ai出来,pcb这东西能作假概率不高
点赞 评论 收藏
分享
评论
31
6
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务