Leetcode-92. 反转链表 II
题目
给你单链表的头指针
head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:
输入:head = [5], left = 1, right = 1
输出:[5]
提示:
- 链表中节点数目为
n 1 <= n <= 500-500 <= Node.val <= 5001 <= left <= right <= n
进阶: 你可以使用一趟扫描完成反转吗?
我的思路
设置一个栈,将要反转的节点放入其中,设置两个数组,将反转节点前面的节点和后面的节点分别放入其中,再用一个新链表连接这些节点。但是不知道为什么遇到下面的测试用例会出错:
[1, 2, 3]
1
1
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
//设置一个栈,将要反转的节点放入其中,设置两个数组,将反转节点前面的节点和后面的节点分别放入其中,再用一个新链表连接这些节点。
public ListNode reverseBetween(ListNode head, int left, int right) {
//left和right是位置而不是节点值
Stack<ListNode> reverseNodes = new Stack<ListNode>();
Set<ListNode> frontNodes = new HashSet<ListNode>();
Set<ListNode> backNodes = new HashSet<ListNode>();
int index = 1;
ListNode reverseList = new ListNode(0);
ListNode tempNode = head;
ListNode preNode = head;
while(tempNode != null){
if(index < left){
frontNodes.add(tempNode);
}
else if(index >= left && index <= right){
reverseNodes.push(tempNode);
}
else{
backNodes.add(tempNode);
}
tempNode = tempNode.next;
++index;
}
tempNode = reverseList;
for(ListNode node : frontNodes){
tempNode.next = node;
tempNode = tempNode.next;
}
while(!reverseNodes.isEmpty()){
tempNode.next = reverseNodes.pop();
tempNode = tempNode.next;
}
for(ListNode node : backNodes){
tempNode.next = node;
tempNode = tempNode.next;
}
//最后一个节点要记得设置next指针为null,否则会产生环
tempNode.next = null;
return reverseList.next;
}
}
输出结果:

PS:笔试时能够AC即可,即可以用空间复杂度高的方法解题,但是面试时如果用额外空间解题的话就避开了要考察的知识点,所以还是用时间复杂度低点的方法。当然要是想不出以能解题为优先考虑。
双指针-头插法
找到left的前一个节点,用一个指针记录下他,再用一个指针记录left位置的节点,然后将left到right的节点头插到left节点前面。难点在于怎么找到left前面的节点,left节点和right节点,因为需要这些节点做连接。另外一个难点是怎么进行头插连接。还要注意设置一个哨兵头节点,因为head节点也可能被反转。
我的错误代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
//找到left的前一个节点,用一个指针记录下他,再用一个指针记录left位置的节点,然后将left到right的节点头插到left节点前面
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode beforeLeft = null;
ListNode leftNode = null;
ListNode tempNode = head;
int index = 1;
//这个循环刚开始设置时也有问题,没有得到第一个要反转节点的前面的节点
for(int i = 1; i < left-1; ++i){
tempNode = tempNode.next;
}
beforeLeft = tempNode;
leftNode = tempNode.next;
tempNode = tempNode.next.next;
ListNode currNode = tempNode;
for(int i = 0; i < right-left; ++i){
currNode = currNode.next;
//不能这样连接,因为leftNode的值没有更新,每次新的节点都会连接到同一个leftNode,导致前面的节点没有成功连接,从而丢失了前面的节点
tempNode.next = leftNode;
tempNode = currNode;
}
//出来后,currNode是要反转的最后一个节点后面的节点,但是要进行连接需要最后一个要反转的节点,所以上面的循环设计就是有问题的,应该保证出来后能得到最后一个要反转的节点。
currNode = currNode.next;
//因为循环设置有问题,导致下面这条语句执行后会永远丢失要反转的节点
beforeLeft.next = tempNode;
leftNode.next = currNode;
return head;
}
}
正确代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
//找到left的前一个节点,用一个指针记录下他,再用一个指针记录left位置的节点,然后将left到right的节点头插到left节点前面。
public ListNode reverseBetween(ListNode head, int left, int right) {
//关键:设置哨兵头节点,以免head节点被反转后无法返回正确的链表
ListNode reverseList = new ListNode(0);
reverseList.next = head;
/**
以下设置错误,应该初始将beforeLeft设置为reverseList,这样才符合当left位置为head时, left前面还有一个节点
ListNode beforeLeft = reverseList.next;
ListNode leftNode = reverseList.next.next;
*/
//利用for循环找到对应的节点,注意初始时leftNode和beforeLeft的值的设置
ListNode beforeLeft = reverseList;
ListNode leftNode = reverseList.next;
for(int i = 0; i < left-1; ++i){
beforeLeft = beforeLeft.next;
leftNode = leftNode.next;
}
//哨兵节点的好处就是如果要从head开始反转,也会不断更新reverseList.next的值
ListNode tempNode = null;
ListNode currNode = null;
for(int i = 0; i < right-left; ++i){
tempNode = leftNode.next;
//关键:注意更新leftNode.next的值
leftNode.next = leftNode.next.next;
tempNode.next = beforeLeft.next;
beforeLeft.next = tempNode;
}
return reverseList.next;
}
}
截断反转法
截断要反转部分的链表,反转完后再连接回去。由于head节点可能改变,需要设置哨兵节点避免复制的分类讨论。需要记录left和right节点,以及left前面一个节点和right后面一个节点。注意利用for循环找节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
//截断要反转部分的链表,反转完后再连接回去。由于head节点可能改变,需要设置哨兵节点避免复制的分类讨论。
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode reverseList = new ListNode(0);
ListNode preLeft = null;
ListNode leftNode = null;
ListNode rightNode = null;
ListNode succRight = null;
reverseList.next = head;
//初始将left的前一个结点指向哨兵结点,遍历left-1次找到left前一个结点
preLeft = reverseList;
for(int i = 0; i < left-1; ++i){
preLeft = preLeft.next;
}
leftNode = preLeft.next;
//寻找right结点和right后一个结点
rightNode = leftNode;
for(int i = 0; i < right-left; ++i){
rightNode = rightNode.next;
}
succRight = rightNode.next;
//截断链表
preLeft.next = null;
rightNode.next = null;
//反转链表
reverseLinkedList(leftNode);
//连接反转后的链表
preLeft.next = rightNode;
leftNode.next = succRight;
return reverseList.next;
}
public ListNode reverseLinkedList(ListNode head){
ListNode reverseList = new ListNode(0);
reverseList.next = head;
//初始时left节点前面一个指针为null没错
ListNode preNode = reverseList;
ListNode tempNode = reverseList.next;
ListNode currNode = null;
while(tempNode != null){
currNode = tempNode.next;
tempNode.next = preNode;
preNode = tempNode;
tempNode = currNode;
}
return reverseList.next;
}
}
学到和回忆了
//声明一个栈对象,并向内压入三个元素
Stack stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
//判断是否为空栈
System.out.println(stack.isEmpty());//输出:false
//使用peek()方法查询栈顶元素,使用pop()方法取出栈顶元素
System.out.println(stack.peek());//输出:3
System.out.println(stack.pop());//输出:3
System.out.println(stack.peek());//输出:2
System.out.println(stack.pop());//输出:2
System.out.println(stack.peek());//输出:1
System.out.println(stack.pop());//输出:1
System.out.println(stack.isEmpty());//输出:true
浙公网安备 33010602011771号