Reverse a linked list from position m to n. Do it in-place and in one-pass.
For example:
Given 1->2->3->4->5->NULL, m = 2 and n = 4,
return 1->4->3->2->5->NULL.
题中让在一条链表中将给定位置中间的节点进行反转。可以使用两个栈node1和node2,node1存储位置m之前的节点,然后将m和n之前的节点存放在node2中。然后将node1中的节点依次取出,使用头插法插入到新链表来保证和之前链表顺序一致,因为m位置之前并不需要转换位置。node1取完之后将node2的依次取出链接到新链表,这样的话m和n之间的位置就进行了反转,然后再处理剩下的节点(也就是n位置之后的),依次添加到新链表之后即可。
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(!head || m==n) {
return head;
}
ListNode* dummpy = new ListNode(0);
dummpy->next = head;
ListNode* temp = dummpy;
ListNode* result = new ListNode(0);
ListNode* tail = result;
stack<ListNode*> node1;
stack<ListNode*> node2;
int num = n - m + 1;//需要加1,此时需要包含后面的那个节点
m--;
while(m) {//将m之前的都存放在node1栈中
temp = temp->next;
node1.push(temp);
m--;
}
while(num&&temp) {
temp = temp->next;
node2.push(temp);
num--;
}
if(temp) {
temp = temp->next;//移动到变换后的一个节点
}
while(!node1.empty()) {//前面的顺序不变,所以使用头插法
node1.top()->next = result->next;
result->next = node1.top();
if(tail == result) {
tail = node1.top();
}
node1.pop();
}
while(!node2.empty()) {
tail->next = node2.top();
tail = tail->next;
node2.pop();
}
while(temp) {
tail->next = temp;
tail = temp;
temp = temp->next;
}
tail->next = nullptr;
return result->next;
}
};
也可以使用两个指针,第一个指针先移动到m位置,然后记录下此刻的位置,然后计算出此刻共需要反转多少个元素,从第一个指针所指的m位置开始遍历,反转一定的元素之后再遍历下面的元素。这个方法就是需要记录各个关键地方的位置。
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(!head || m==n) {
return head;
}
ListNode* dummpy = new ListNode(0);
dummpy->next = head;
ListNode* start = dummpy;//指向需要变换的开头
ListNode* tail = nullptr;//指向需要变换的结尾
ListNode* temp = nullptr;
ListNode* p1,*p2;
int num = n - m;
m--;
while(m) {//运行到需要变换位置的前一个节点
start = start->next;
m--;
}
p1 = start;//记录中断的位置
p2 = start->next;//记录转换后的最后一个位置(也就是m位置)
start = start->next;//此刻的位置才是需要开始变换的位置
temp = start->next;//指向下一个节点
start->next = nullptr;
while(temp && num) {//将m和n之间进行反转链接
tail = temp->next;
temp->next = start;
start = temp;
temp = tail;
num--;
}
p1->next = start;//与前半部分对接
while(temp) {//需要用temp而不是tail,因为tail可能为空(如果没有进while(temp&&num)tail就是空)
p2->next = temp;
p2 = temp;
temp = temp->next;
}
return dummpy->next;
}
};
还有一种极为简洁的写法,思路也是使用指针。与上述方法不同的是:上述方法将n和m中间的节点相当于是完全隔离出来进行反转,然后再次进行对接,也就是说将原链表断开了,单独处理n和m之间。下面这种方式是保证原链表不断的情况下进行n和m之间的反转,代码写的可以说是非常漂亮了。
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(m == n)
return head;
n = n - m;
ListNode* pre = new ListNode(0);
ListNode* pre_stable = pre;
pre->next = head;
while(--m > 0)
pre = pre->next;
ListNode* p = pre->next;
while(n-- > 0)
{
ListNode* q = p->next;
p->next = p->next->next;
q->next = pre->next;
pre->next = q;
}
return pre_stable->next;
}
};