迭代
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if (left == right){
return head;
}
ListNode prev = null;
ListNode cur = head;
/**
* 先找到left的位置
*/
for (int i = 1; i < left; i++) {
ListNode next = cur.next;
prev = cur;
cur = next;
}
/**
* 记录下left左边的第一个节点和right右边的第一个节点,为了最后还原链表
* 循环次数n等于节点数
*/
int n = right - left + 1;
ListNode lastNode = null;
ListNode firstNode = null;
ListNode replaceFirst = null;
while (n > 0){
/**
* 当left == 1时,表明头节点最后会改变,而firstNode会一直等于null
* 此时需要单独记录下第一个节点,为了最后将其链接上原链表
*/
if (n == right - left + 1){
if (prev != null) {
firstNode = prev;
}
else {
replaceFirst = cur;
}
}
if (n == 1){
lastNode = cur.next;
}
/**
* 对区间内的链表进行反转
*/
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
n--;
}
/**
* 与原链表相链接
* 如果firstNode不存在,说明原区间最后一个节点是新的头节点,此时不能用firstNode.next,而要用之前备用的replaceFirst
*/
if (firstNode != null) {
firstNode.next.next = lastNode;
firstNode.next = prev;
}
else {
head = prev;
replaceFirst.next = lastNode;
}
return head;
}
}
/**
* 时间复杂度 O(n)
* 空间复杂度 O(1)
*/
优化1——简化判断流程(两次遍历)
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
/**
* 涉及到链表问题,一般头节点会变化,因此都采用虚拟头节点,且赋值一下以免定义为空左指针
*/
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
/**
* 先找到left节点
*/
ListNode prev = dummyHead;
for (int i = 1; i < left; i++) {
prev = prev.next;
}
/**
* 从left开始,遍历链表找到right节点
*/
ListNode rightNode = prev.next;
for (int i = 0; i < right - left; i++) {
rightNode= rightNode.next;
}
/**
* 分别记录left和right节点以及他们的前节点
*/
ListNode leftNode = prev.next;
ListNode cur = rightNode.next;
/**
* 将[left, right]区间独立出来进行链表反转
*/
prev.next = null;
rightNode.next = null;
reverse(leftNode);
/**
* 和原链表接上
* right是新的头节点,left是尾节点
*/
prev.next = rightNode;
leftNode.next = cur;
return dummyHead.next;
}
/**
* 反转链表
* 不用返回头节点,因为之前保存了
*/
public void reverse(ListNode head){
ListNode prev = null;
ListNode cur = head;
while (cur != null){
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
}
}
/**
* 时间复杂度 O(n)
* 空间复杂度 O(1)
*/
优化2——一次遍历
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
/**
* 一次遍历,在遍历[left, right]区间时就让其反转,始终让当前节点成为新的区间头节点,这样就完成了反转
* 涉及到链表问题,一般头节点会变化,因此都采用虚拟头节点,且赋值一下以免定义为空左指针
*/
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
/**
* 先找到left节点
*/
ListNode prev = dummyHead;
for (int i = 1; i < left; i++) {
prev = prev.next;
}
/**
* 从left开始,遍历[left, right]区间
* 将每一个遍历到的节点变成区间的头节点
*/
ListNode cur = prev.next;
ListNode next;
/**
* prev始终指向left节点的前一个节点,这样的话其指向谁谁就是区间的头节点
* 遍历到某个节点时,先记录下其下一个节点为next,然后断开链接,指向next的下一个节点,而next指向当前的区间头节点就是prev.next
* 最后prev指向next,next成为新的区间头节点
*/
for (int i = 0; i < right - left; i++) {
next = cur.next;
cur.next = next.next;
next.next = prev.next;
prev.next = next;
}
return dummyHead.next;
}
}
/**
* 时间复杂度 O(n)
* 空间复杂度 O(1)
*/
https://leetcode-cn.com/problems/reverse-linked-list-ii/