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

链表内指定区间反转

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

题解一:迭代翻转
题解思路 : 建立一个空白节点指向头节点,然后翻转[m,n]内的节点。
参数分析: p:指向m前一个节点,q指向第n个节点。p1,p2用于翻转.
图示:
图片说明
复杂度分析
时间复杂度:O(N),最多遍历整个链表
空间复杂度:O(1),只使用了常数个变量
实现如下:

class Solution {
public:
    /**
     *
     * @param head ListNode类
     * @param m int整型
     * @param n int整型
     * @return ListNode类
     */

    ListNode* reverseBetween(ListNode* head, int m, int n) {
        // write code here
          if(head==NULL||head->next==NULL||m==n) return head;
          struct ListNode* p;
          struct ListNode* q;
          struct ListNode* l;
          l = (struct ListNode*)malloc(sizeof(struct ListNode));  //创建空白节点
          l->next = head;  // 空白节点指向头节点
          p = q = l;
          for(int i =1;i<m;i++)
              p = p->next;  // p指向m前一个节点
          for(int i = 1;i<=n;i++)
              q = q->next; //q 指向第n个节点
          struct ListNode* p1 = p->next;  // p1 是第m个节点
          struct ListNode* p2 = p1->next;
          p->next = q; // m前一个节点要指向q
          p1->next = q->next;  
          while(p2!=q){  //反转节点
              p = p2->next;
              p2->next = p1;
              p1 = p2;
              p2 = p;
          }
          p2->next = p1;
          return l->next;
    }
};

题解二:递归
题解思路:利用反转链表的思想,使用递归
图示:反转链表的递归
图片说明

递归分析:
递归边界:n=1 tmp保存子问题的部分的next用于拼接
递归进行: (m-1,n-1)表示反转开始的位置在子问题中的节点开始位置
图示:
图片说明

复杂度分析:
时间复杂度:O(N)
空间复杂度:O(N),最坏递归栈深度
实现如下:

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;
    }
};
牛客网编程题题解 文章被收录于专栏

本专栏记录在牛客网上AC的每一题,写下题解。 未来2年完成2000编程题的题解。 2021.12.29更新,最进准备毕设,断更了,会尽快做完毕设,继续做这一件事情

全部评论
您的代码中存在几个问题,主要涉及到C++和链表操作的混合使用,以及一些逻辑上的小错误。首先,您使用了malloc来分配内存,这是C语言中的做法,而在C++中通常推荐使用new操作符或者智能指针(如std::unique_ptr)。其次,您在反转链表节点时的逻辑处理有误,导致可能的链表断裂或无限循环。 下面是修改后的C++代码,去除了malloc的使用,并修正了反转链表部分的逻辑: cpp #include <iostream> struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(nullptr) {} }; class Solution { public: ListNode* reverseBetween(ListNode* head, int m, int n) { if (head == nullptr || head->next == nullptr || m == n) return head; ListNode* dummy = new ListNode(0); // 创建哑节点 dummy->next = head; ListNode* prev = dummy; ListNode* curr = head; // 将prev移动到第m-1个节点 for (int i = 1; i < m; ++i) { prev = prev->next; curr = curr->next; } ListNode* start = curr; // 第m个节点 ListNode* thenext = nullptr; // 反转第m到第n个节点 for (int i = m; i <= n; ++i) { thenext = curr->next; curr->next = prev->next; prev->next = curr; curr = thenext; } // 将start的下一个节点连接到第n+1个节点 start->next = curr; ListNode* newHead = dummy->next; delete dummy; // 释放哑节点内存 return newHead; } }; // 用于测试的代码 int main() { // 示例链表 1->2->3->4->5 ListNode* head = new ListNode(1); head->next = new ListNode(2); head->next->next = new ListNode(3); head->next->next->next = new ListNode(4); head->next->next->next->next = new ListNode(5); Solution sol; ListNode* reversedHead = sol.reverseBetween(head, 2, 4); // 打印反转后的链表 while (reversedHead != nullptr) { std::cout << reversedHead->val << " "; ListNode* temp = reversedHead; reversedHead = reversedHead->next; delete temp; // 释放链表节点内存 } return 0; } 注意: 我添加了一个哑节点(dummy node)来简化边界条件的处理。 使用new操作符创建节点,并在主函数中释放了所有动态分配的内存。 修正了反转链表的逻辑,确保在反转后正确地连接前后的链表部分。 添加了测试代码来验证函数的功能。</iostream>
1 回复 分享
发布于 2024-08-11 23:16 广东
在您的代码中,您尝试实现一个函数来反转链表中从第 m 个节点到第 n 个节点之间的部分。不过,有几个问题和潜在的错误需要修复。首先,您的 reverse 函数设计得有些复杂,并且 tmp 成员变量的使用方式可能不是最优的,因为它在递归调用之间共享,这可能导致不可预测的行为。 此外,您的 reverseBetween 函数在递归调用时正确地处理了 m 和 n 的减少,但在 reverse 函数中,您应该直接处理反转逻辑,而不是依赖于一个全局的 tmp 变量。 下面是一个改进后的版本,这个版本不使用全局的 tmp 变量,而是直接在递归中处理反转和拼接: cpp class Solution { public: ListNode* reverseBetween(ListNode* head, int m, int n) { // 处理边界情况:如果链表为空或m大于n,则直接返回原链表 if (head == nullptr || m > n) return head; // 如果m等于1,即从头节点开始反转,需要特殊处理 if (m == 1) { return reverseN(head, n); } // 递归处理子链表,并更新head的下一个节点 head->next = reverseBetween(head->next, m - 1, n - 1); return head; } private: // 反转链表的前n个节点,并返回新的头节点 ListNode* reverseN(ListNode* head, int n) { if (n == 1) { // 递归基:当n为1时,返回头节点,此时它已经是反转后的最后一个节点 return head; } // 递归反转接下来的n-1个节点,并获取新的头节点 ListNode* newHead = reverseN(head->next, n - 1); // 反转当前节点和下一个节点的指向 head->next->next = head; head->next = nullptr; // 切断与后续节点的连接 // 返回新的头节点 return newHead; } }; 在这个版本中,我添加了一个私有函数 reverseN,它负责反转链表的前 n 个节点,并返回新的头节点。这样,reverseBetween 函数就可以通过递归调用 reverseN 来处理从第 m 个节点到第 n 个节点的反转,而无需使用全局变量。 此外,我还添加了对空链表和 m > n 的边界情况的处理,以确保函数的健壮性。
点赞 回复 分享
发布于 2024-08-11 23:21 广东

相关推荐

头发暂时没有的KFC总裁:找廉价劳动力罢了
点赞 评论 收藏
分享
评论
21
8
分享

创作者周榜

更多
牛客网
牛客企业服务