题目
对于单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
解题思路
栈
利用栈来把要反转的结点入栈,最后按序出栈就是翻转的顺序,前提是要保存反转链表的前一个和后一个节点。
代码如下:
import java.util.Stack;
public class Stack_ {
public ListNode reverseBetween(ListNode head, int m, int n){
//没有考虑从第一个节点就要倒序的情况
Stack<ListNode> stack = new Stack<>();
if(m == n){
return head;
}
if(m >1){
ListNode pre = head;
ListNode first = head.next;
int step = 1;
while(step < m-1){
pre = first;
first = first.next;
step++;
}
for(int i=0;i<=n-m;i++){
stack.push(first);
first = first.next;
}
while(!stack.isEmpty()){
ListNode tmp = stack.peek();
stack.pop();
pre.next = tmp;
pre = tmp;
}
pre.next = first;画个图,一眼就看出来了
}else{//从第一个开始
ListNode first = head;
for(int i=0;i<=n-m;i++){
stack.push(first);
first = first.next;
}
head = stack.peek();
stack.pop();
ListNode pre = head;
while(!stack.isEmpty()){
ListNode tmp = stack.peek();
stack.pop();
pre.next = tmp;
pre = tmp;
}
pre.next = first;画个图,一眼就看出来了
}
return head;
}
要注意的是,从第一个位置开始反转要单独考虑,而翻转到最后不需要,因为最后一个节点所指向的下一个节点是不需要改变的,即使是空,也不需要处理。而从头开始反转,就不需要寻找pre节点,只要找到反转范围外的第一个节点first,把前面的都反转之后,再把反转之后的尾接到first上就行了。
头插法
这种方法放弃了头结点的数据域,所以变量名叫dummyhead(哑节点),对于要反转的部分,当做一个新的链表从头插入,最后得到反转之后的链表。
public ListNode reverseBetween(ListNode head, int m, int n) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode g = dummyHead;
ListNode p = dummyHead.next;
int step = 0;
while (step < m - 1) {
g = g.next; p = p.next;
step ++;
}
//pre节点
for (int i = 0; i < n - m; i++) {
ListNode removed = p.next;
p.next = p.next.next;
removed.next = g.next;
g.next = removed;
}
return dummyHead.next;
}
寻找反转头的前一个节点,例如反转2到4,则m等于2,step循环只会执行一次。因为第一个节点是哑结点,相当于是0号节点,执行一次到达一号节点,正好是反转头的前一个节点。
int step = 0;
while (step < m - 1) {
g = g.next; p = p.next;
step ++;
}
首先分清,g是不需要反转的,p是需要反转的第一个。
for (int i = 0; i < n - m; i++) {
ListNode removed = p.next;
p.next = p.next.next;
removed.next = g.next;
g.next = removed;
}
这段非常类似于冒泡,相邻两节点相对位置交换,见下图
总结
栈在反转链表中的应用,以及头插法/交换临节点在反转链表中的应用。