个人算法成长之路五!!!定期更新一些刷题过程中个人的思路以及理解。有兴趣的朋友们可以互动交流哈~
题目:
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]
解题思路:
方法一:迭代
很多人叫他穿针引线法,其实我认为本身就是一种迭代,所以就暂且叫他迭代法吧。 步骤: 1.记录left前面一个节点和right后面一个节点; 2.在left和right处将需要反转的链表截取; 3.截取后反转链表; 4.pre.next指向反转后的链表的head,反转后的链表的结尾的next指向right.next
var reverseBetween = function(head, left, right) {
const newNode = new ListNode(0,head);
let pre = newNode;
//left
//从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
for(let i = 0; i < left - 1; i++) {
pre = pre.next;
}
//right
//从 pre 再走 right - left + 1 步,来到 right 节点
let rightNode = pre;
for(let i = 0; i < right-left+1; i++) {
rightNode = rightNode.next;
}
//切断出子链表
let leftNode = pre.next;
let curr = rightNode.next; //right右侧下一个节点
//切断链接
rightNode.next = null;
pre.next = null;
// 第 4 步:同第 206 题,反转链表的子区间
reverseList(leftNode);
pre.next = rightNode;
leftNode.next = curr;
return newNode.next
};
var reverseList = function(head) {
let prev = null;
let curr = head;
while(curr) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev
}
这种方法连续遍历两次,如果链表比较长的话,很显然,会有影响。
方法二: 头插法
这个方法的思路就是,在需要反转的区间里,每遍历到一个节点,让这个新节点来到反转部分的起始位置。如图所示
- curr:指向待反转区域的第一个节点 left;
- next:永远指向 curr 的下一个节点,循环过程中,curr 变化以后 next 会变化;
- pre:永远指向待反转区域的第一个节点 left 的前一个节点,在循环过程中不变。
具体步骤就是:
- 先将 curr 的下一个节点记录为 next;
- 执行操作 ①:把 curr 的下一个节点指向 next 的下一个节点;
- 执行操作 ②:把 next 的下一个节点指向 pre 的下一个节点;
- 执行操作 ③:把 pre 的下一个节点指向 next。
var reverseBetween = function(head, left, right) {
// 设置 dummyNode 是这一类问题的一般做法
const dummy_node = new ListNode(-1);
dummy_node.next = head;
let pre = dummy_node;
for (let i = 0; i < left - 1; ++i) {
pre = pre.next;
}
let cur = pre.next;
for (let i = 0; i < right - left; ++i) {
const next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next = next;
}
return dummy_node.next;
};