1. 题目

2. 解答

2.1. 方法一

将链表每个节点向右移动 1 个位置,其实就是让链表最后一个结点指向第一个结点。

因此,向右移动 k 个位置就重复上述过程 k 次即可。

然后,我们注意到,若链表有 n 个结点,则移动 n 次后就还是原链表。

原始链表 1->2->3->NULL
向右旋转 1 步: 3->1->2->NULL
向右旋转 2 步: 2->3->1->NULL
向右旋转 3 步: 1->2->3->NULL

实际上,若链表有 n 个结点,我们只需要移动 n % k 次即可。

确定链表有多少个结点,我们则可以用快慢指针法。

  • 偶数个结点时,结点个数等于 i * 2。

  • 奇数个结点时,结点个数等于 i * 2 + 1。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) { if (head == NULL || head->next == NULL) return head; int node_num = 0; // 链表的结点个数
ListNode* slow = head;
ListNode* fast = head; // 利用快慢指针确定链表的结点总个数
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
node_num++;
} if (fast) // 奇数个结点
{
node_num = node_num * 2 + 1;
}
else // 偶数个结点
{
node_num = node_num * 2;
} // 若链表有 K 个结点,向右移动 K 个位置就还是原链表
// 因此我们只需要移动 K % node_num 次即可
if (k % node_num == 0) return head;
else k = k % node_num; ListNode *temp = head;
ListNode *last_node = NULL; for (int i = 0; i < k; i++)
{
// 向右移动 1 个位置就是让最后一个结点指向第一个结点 // 先找到倒数第二个结点
while (temp->next->next)
{
temp = temp->next;
} // 最后一个结点指向第一个结点,倒数第二个节点指向 NULL
last_node = temp->next;
temp->next = NULL;
last_node->next = head; head = last_node;
temp = head;
} return head;
}
};

2.2. 方法二

上面的算法中每次将链表结点向右移动 1 个位置的时候,我们都要遍历一次链表,效率较低。

针对链表 1->2->3->4->5->NULL,如果要将链表每个结点向右移动 2 个位置,那倒数第 2 个结点就是旋转后新链表的起始位置;如果要将链表每个结点向右移动 3 个位置,那倒数第 3 个结点就是旋转后新链表的起始位置;而如果要将链表每个结点向右移动 33 个位置,那倒数第 3 个结点就是旋转后新链表的起始位置。

更一般的情况下,若链表长度为 n,移动次数为 k,旋转后链表的起始位置就是原链表倒数第 k % n 个结点

在上面的例子中,假设新链表的起始位置是结点 4,那链表就被分成了两部分。1->2->3 和 4->5->NULL,我们要做的就是把 1->2->3 拼接在 4->5->NULL 后即可。

实现思路是这样的:

我们先通过快慢指针确定链表长度 n,然后找到旋转后链表起始位置的上一个结点,记为 new_head_last,然后从起始位置 new_head 开始向后遍历,当到达 NULL 时,将 head 指向的头结点拼接在 new_head 后面,然后再将新链表的最后一个结点也即 new_head_last 后面置为 NULL 即可。

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) { if (head == NULL || head->next == NULL) return head; int node_num = 0; // 链表的结点个数
int mid_num = 0;
ListNode* slow = head;
ListNode* fast = head; // 利用快慢指针确定链表的结点总个数
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
mid_num++;
} if (fast) // 奇数个结点
{
node_num = mid_num * 2 + 1;
}
else // 偶数个结点
{
node_num = mid_num * 2;
} // 若链表有 K 个结点,向右移动 K 个位置就还是原链表
// 因此我们只需要移动 K % node_num 次即可
if (k % node_num == 0) return head;
else k = k % node_num; int which_node_is_new = 0; // 旋转后的头结点是第几个节点
ListNode* new_head = NULL;
ListNode* new_head_last = NULL; // 查找旋转后头结点的上一个结点
// 此时 slow 指针指向中间结点,分为在 slow 前还是在 slow 后
which_node_is_new = node_num - k;
if (which_node_is_new > node_num / 2)
{
new_head_last = slow;
for (int i = 0; i < which_node_is_new - mid_num - 1; i++)
{
new_head_last = new_head_last->next;
}
}
else
{
new_head_last = head;
for (int i = 1; i < which_node_is_new; i++)
{
new_head_last = new_head_last->next;
}
} new_head = new_head_last->next;
ListNode* temp = new_head; while (temp->next)
{
temp = temp->next;
} temp->next = head;
new_head_last->next = NULL; return new_head;
}
};

获取更多精彩,请关注「seniusen」!

LeetCode 61——旋转链表的更多相关文章

  1. Java实现 LeetCode 61 旋转链表

    61. 旋转链表 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5->NULL, k = ...

  2. leetcode 61. 旋转链表

    题目描述: 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5->NULL, k = 2 输 ...

  3. LeetCode 61——旋转链表(JAVA)

    给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5->NULL, k = 2 输出: 4-& ...

  4. LeetCode 61. 旋转链表(Rotate List)

    题目描述 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5->NULL, k = 2 输出 ...

  5. LeetCode:旋转链表【61】

    LeetCode:旋转链表[61] 题目描述 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5- ...

  6. leetcode网解题心得——61. 旋转链表

    目录 leetcode网解题心得--61. 旋转链表 1.题目描述 2.算法分析: 3.用自然语言描述该算法 4.java语言实现 5.C语言实现 leetcode网解题心得--61. 旋转链表 1. ...

  7. 【LeetCode】61. 旋转链表

    61. 旋转链表 知识点:链表: 题目描述 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置. 示例 输入:head = [1,2,3,4,5], k = 2 输出:[4 ...

  8. 61. 旋转链表-leetcode

    给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5->NULL, k = 2 输出: 4-& ...

  9. leetcode刷题-61旋转链表

    题目 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5->NULL, k = 2输出: 4 ...

随机推荐

  1. HDU 1060 Leftmost Digit(求N^N的第一位数字 log10的巧妙使用)

    Leftmost Digit Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)To ...

  2. 轻量ORM-SqlRepoEx (二)初始化SqlRepoEx

    一.SqlRepoEx引用 暂时没放至nuget上,可以直接到https://github.com/AzThinker/SqlRepoEx下载源码,编译引用. (一).静态引用 1.需引用以下dll在 ...

  3. C# http post、get请求

    public string HttpPostJson(string Url, string paramData, Dictionary<string, string> headerDic ...

  4. Python基础—12-面向对象(02)

    面向对象 面向对象三大特点 封装:既可对数据结构进行封装,又可对处理数据的方法进行封装 继承:强调的父子类的关系 多态:不同对象调用相同的方法,会有不同的响应 类的继承 相关概念 继承:父类的属性和方 ...

  5. SpringBoot学习17:springboot热部署配置

    spring为开发者提供了一个名为spring-boot-devtools的模块来使Spring Boot应用支持热部署,提高开发者的开发效率,无需手动重启Spring Boot应用. devtool ...

  6. php第三节(运算符)

    <?php //算术运算符 + - * / % //++ 前加加 先做加运算后座赋值运算 后加加 先做赋值运算后座加运算 //-- 前减减 先做加运算后座赋值运算 后减减 先做赋值运算后座加运算 ...

  7. BZOJ2844: albus就是要第一个出场(线性基)

    Time Limit: 6 Sec  Memory Limit: 128 MBSubmit: 2054  Solved: 850[Submit][Status][Discuss] Descriptio ...

  8. BZOJ1607: [Usaco2008 Dec]Patting Heads 轻拍牛头(模拟 调和级数)

    Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 3031  Solved: 1596[Submit][Status][Discuss] Descriptio ...

  9. Linux外在设备的使用

    参考高峻峰 著 循序渐进Linux(第二版) 软盘在linux下对应的设备文件为/dev/fdx.主设备号fd是软盘驱动器(floppydisk)的缩写,次设备号x是软盘驱动器的相应编号.例如,/de ...

  10. 【cisco探索之路】

    CISCO探索之路 show&debug&clear 1:show show version:显示版本信息show running-config:显示当前的配置show interfa ...