[leetcode] Reverse Linked List II 链表反转
2016-07-16 21:31
477 查看
Leetcode上关于链表反转有2题,分别是https://leetcode.com/problems/reverse-linked-list/ 和 https://leetcode.com/problems/reverse-linked-list-ii/
第一题是反转整个链表,第二题是反转链表中位于[m,n]之间的部分。
首先简单说下原地反转整个链表的两种方法:
法一:【递归法】假设从i+1开始到结束的链表L(i+1)已反转,则我们只需将第i个元素置于L(i+1)尾部,即可完成从i开始到结束的链表L(i)的反转,递归完成从第1个元素到结尾的链表L(1)的反转即可。代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL || head->next == NULL){
return head;
}
ListNode* p = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return p;
}
};
法二:【迭代法】从前往后扫描链表,用pre指针指向当前元素的前一个元素,每次将当前元素的next指针指向pre,扫描至最后即可完成整个链表的反转。时间复杂度为O(n),空间复杂度为O(1)。代码如下:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL || head->next == NULL){
return head;
}
ListNode* p = head->next;
ListNode* pre = head;
head->next = NULL;
while(p){
ListNode* tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
}
return pre;
}
};
反转链表的一部分,如[m,n]之间的部分
其实知道了如何反转整个链表,我们完全可以按照上述法二的思路反转链表[m,n]之间的部分,这里需要指针特别记录一下m-1和n+1的位置,便于将反转后部分正确插入。时间复杂度为O(n),空间复杂度为O(1),代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(head == NULL || head->next == NULL || m == n) return head;
ListNode* p = head->next;
ListNode* pre = head, *p1 = head;
int pos = 1;
ListNode* nh = new ListNode(0);
while(p){
++ pos;
if(pos <= m || pos > n){
if(pos == m){
nh = pre;
p1 = p;
}
pre = pre->next;
p = p->next;
}else{
ListNode* tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
if(pos == n){
nh->next = pre;
p1->next = p;
}
}
}
if(m == 1){
return nh->next;
}
return head;
}
};
看了discussion之后发现了另外一种方法,即插入法,特别巧妙。例如,我们要反转1->2->3->4->5的位于[2,4]之间的元素,我们可以依次将3和4向前插入,插入3之后变为1->3->2->4->5,再插入4变为1->4->3->2->5。代码如下:
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(head == NULL || head->next == NULL || m == n) return head;
ListNode* p = head;
int pos = 1;
ListNode* nh = new ListNode(0);
nh->next = head;
ListNode* pre = nh;
while(p && pos < m){
++ pos;
pre = p;
p = p->next;
}
// 插入法
for(int i=0; i<n-m; ++i){
ListNode* tmp = p->next;
p->next = tmp->next;
tmp->next = pre->next;
pre->next = tmp;
}
return nh->next;
}
};
第一题是反转整个链表,第二题是反转链表中位于[m,n]之间的部分。
首先简单说下原地反转整个链表的两种方法:
法一:【递归法】假设从i+1开始到结束的链表L(i+1)已反转,则我们只需将第i个元素置于L(i+1)尾部,即可完成从i开始到结束的链表L(i)的反转,递归完成从第1个元素到结尾的链表L(1)的反转即可。代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL || head->next == NULL){
return head;
}
ListNode* p = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return p;
}
};
法二:【迭代法】从前往后扫描链表,用pre指针指向当前元素的前一个元素,每次将当前元素的next指针指向pre,扫描至最后即可完成整个链表的反转。时间复杂度为O(n),空间复杂度为O(1)。代码如下:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL || head->next == NULL){
return head;
}
ListNode* p = head->next;
ListNode* pre = head;
head->next = NULL;
while(p){
ListNode* tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
}
return pre;
}
};
反转链表的一部分,如[m,n]之间的部分
其实知道了如何反转整个链表,我们完全可以按照上述法二的思路反转链表[m,n]之间的部分,这里需要指针特别记录一下m-1和n+1的位置,便于将反转后部分正确插入。时间复杂度为O(n),空间复杂度为O(1),代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(head == NULL || head->next == NULL || m == n) return head;
ListNode* p = head->next;
ListNode* pre = head, *p1 = head;
int pos = 1;
ListNode* nh = new ListNode(0);
while(p){
++ pos;
if(pos <= m || pos > n){
if(pos == m){
nh = pre;
p1 = p;
}
pre = pre->next;
p = p->next;
}else{
ListNode* tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
if(pos == n){
nh->next = pre;
p1->next = p;
}
}
}
if(m == 1){
return nh->next;
}
return head;
}
};
看了discussion之后发现了另外一种方法,即插入法,特别巧妙。例如,我们要反转1->2->3->4->5的位于[2,4]之间的元素,我们可以依次将3和4向前插入,插入3之后变为1->3->2->4->5,再插入4变为1->4->3->2->5。代码如下:
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(head == NULL || head->next == NULL || m == n) return head;
ListNode* p = head;
int pos = 1;
ListNode* nh = new ListNode(0);
nh->next = head;
ListNode* pre = nh;
while(p && pos < m){
++ pos;
pre = p;
p = p->next;
}
// 插入法
for(int i=0; i<n-m; ++i){
ListNode* tmp = p->next;
p->next = tmp->next;
tmp->next = pre->next;
pre->next = tmp;
}
return nh->next;
}
};
相关文章推荐
- [C/C++]反转链表
- C#实现基于链表的内存记事本实例
- C#模拟链表数据结构的实例解析
- C语言实现带头结点的链表的创建、查找、插入、删除操作
- C++利用静态成员或类模板构建链表的方法讲解
- C++实现简单的学生管理系统
- Linux内核链表实现过程
- C++链表倒序实现方法
- C#通过链表实现队列的方法
- Node.js环境下JavaScript实现单链表与双链表结构
- C#实现的简单链表类实例
- 找出链表倒数第n个节点元素的二个方法
- Java数据结构之简单链表的定义与实现方法示例
- Java模拟有序链表数据结构的示例
- C语言单循环链表的表示与实现实例详解
- C++实现的链表类实例
- PHP小教程之实现链表
- PHP中模拟链表和链表的基本操作示例
- C语言双向链表的表示与实现实例详解
- js链表操作(实例讲解)