61. Rotate List**

61. Rotate List**

https://leetcode.com/problems/rotate-list/

题目描述

Given a linked list, rotate the list to the right by k places, where k is non-negative.

Example 1:

Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL

Example 2:

Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL

C++ 实现 1

先介绍我最新的思路吧, 然后再看看以前的做法.

下面代码的思路是:

  1. 先翻转整个链表
  2. 翻转前 k 个节点, 再翻转后 num - k 个节点
  3. 拼接两个子链表

大致思路如上, 但是有一些细节需要考虑, 下面依次介绍:

  1. 首先, k 有可能非常大, 如果我们知道了链表的节点个数 num, 那么只需要处理 k %= num 的情况. 为了统计链表的节点个数, 这一步在翻转这个链表时完成, 所以在 reverse 函数中增加了 int &num 这个参数.
  2. 我们只需要考虑 k %= num 的情况, 如果此时 k == 0, 那么直接返回原始链表即可, 但由于在第一步中已经将链表翻转了, 所以要注意翻转回去.
  3. 之后进入到第 2 个步骤, 原本是打算先翻转前 k 个节点, 再翻转后面的 num - k 个节点, 但是会发现这有个困难就是无法有效的拼接两个链表, 如果要拼接两个链表, 需要先找到前一个子链表的最后一个节点, 当然这样做也是可以的, 但感觉不太优雅.
  4. 因此, 换种方式是, 先翻转后面的 num - k 个节点, 当翻转完之后, 效果如下:
  1. 之后第三步, 需要先翻转 5->4, 并让 5 指向 1. 这个思路其实和 reverse 函数中的逻辑是一致的.
    但有个细节需要注意, head 最多移动到 4, 相当于 3 是 head 的终点, 需要用 end = p->next 先将 3 保存到 end 中, 而不能在 while 循环中直接使用 head != p->next; 这是因为节点 4 最后会发生变化, 相应的 p 也会发生变化, 导致最后 p->next 不再指向 3. 下面用图简单描述一下第三步:
class Solution {
private:
    ListNode* reverse(ListNode *head, int &num) {
        ListNode *prev = nullptr;
        while (head) {
            auto tmp = head->next;
            head->next = prev;
            prev = head;
            head = tmp;
            num += 1;
        }
        return prev;
    }
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (!head || !head->next) return head;
        int num = 0;
      	// 第一步
        head = reverse(head, num);
        k = k % num;
        if (k == 0) return reverse(head, num);
        auto p = head;
        while (--k) p = p->next;
        // 第二步
        auto rlist = p->next;
        rlist = reverse(rlist, num);
        // 第三步
        auto end = p->next;
        while (head != end) {
            auto tmp = head->next;
            head->next = rlist;
            rlist = head;
            head = tmp;
        }
        return rlist;
    }
};

C++ 实现 2

两年前的代码:

思路: 使用递归可以非常简单的解决这道题目, 如果要将链表旋转 k 次, 那么可以在旋转 k - 1 次的基础上, 将链表的最后一个节点放在链表的头部, 这样就得到了第 k 次的链表. 但是有个优化必须注意, 否则会报超时, 毕竟 k 可能很大, 我们观察到, 如果 k 能整除链表中节点的个数, 那么旋转后的链表将是原链表本身.

class Solution {
private:
    ListNode* rotateRight(ListNode *head, int n, int k) {
      	// 如果 k 能整除 n, 那么直接返回链表本身
        if ((k % n) == 0) return head;
		
      	// 否则只要在第 k-1 次旋转得到的链表的基础上, 做一些简单的操作即可.
      	// 注意 while 循环中判断 ptr->next->next 是否存在, 这样是为了让
      	// ptr 最终的值是倒数第二个节点.
        head = rotateRight(head, n, k - 1);
        auto ptr = head;
        while (ptr->next && ptr->next->next)
            ptr = ptr->next;
        auto tmp = ptr->next;
        ptr->next = nullptr;
        tmp->next = head;
        head = tmp;
        return head;

    }
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (!head || !head->next) return head;
      	// 用 n 统计链表中节点的个数
        int n = 0;
        auto ptr = head;
        while (ptr) {
            ptr = ptr->next;
            ++ n;
        }
        return rotateRight(head, n, k);
    }
};

C++ 实现 3

来自 LeetCode 讨论区:

My clean C++ code, quite standard (find tail and reconnect the list)

这种做法的思路是, 首先求出链表的大小, 并找到最后一个节点 tail, 然后将链表弄成环状 tail->next = head. 之后根据 k %= len, 判断何处断开链表. (如果 if 语句中没有使用 k %= len, 那么 for 循环中的判断条件 i < len - k 实际上是 i < len - k % len.

相当于先成环, 再断开. 最后 tail 要移动 len - k 步, 直观一点去想, 如果指针能向后移动, 那么向后移动 k 步最方便; 如果不能向后移动, 那么就能向前移动 len - k 步.

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(!head) return head;
        
        int len=1; // number of nodes
        ListNode *newH, *tail;
        newH=tail=head;
        
        while(tail->next)  // get the number of nodes in the list
        {
            tail = tail->next;
            len++;
        }
        tail->next = head; // circle the link

        if(k %= len) 
        {
            for(auto i=0; i<len-k; i++) tail = tail->next; // the tail node is the (len-k)-th node (1st node is head)
        }
        newH = tail->next; 
        tail->next = NULL;
        return newH;
    }
};
发布了455 篇原创文章 · 获赞 8 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Eric_1993/article/details/105004378