加油 第 4、5 练
206. 反转链表
题目描述:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
方法一:递归
var reverseList = function(head) {
if (head == null || head.next == null) {
return head;
}
//递归的传入下一个节点 - 到最后一个节点返回
const newHead = reverseList(head.next);
/**
* 以1,2,3,4,5为例
* 第一轮head为5,head.next为空,返回5
* 第二轮head为4,head.next为5,执行head.next.next=head也就是5.next=4,
把当前节点的子节点的子节点指向当前节点
此时链表为1->2->3->4<->5,由于4与5互相指向,所以此处要断开4.next=null
此时链表为1->2->3->4<-5
返回节点5
* 第三轮,head为3,head.next为4,执行head.next.next=head也就是4.next=3,
此时链表为1->2->3<->4<-5,由于3与4互相指向,所以此处要断开3.next=null
此时链表为1->2->3<-4<-5
返回节点5
* 第四轮,head为2,head.next为3,执行head.next.next=head也就是3.next=2,
此时链表为1->2<->3<-4<-5,由于2与3互相指向,所以此处要断开2.next=null
此时链表为1->2<-3<-4<-5
返回节点5
* 第五轮,head为1,head.next为2,执行head.next.next=head也就是2.next=1,
此时链表为1<->2<-3<-4<-5,由于1与2互相指向,所以此处要断开1.next=null
此时链表为1<-2<-3<-4<-5
返回节点5
* 出栈,最终头节点5->4->3-2->1
*/
head.next.next = head;
// 当链表递归反转之后,新的头节点是last,
//而之前的head变成了最后一个节点,链表的末尾要指向 null
head.next = null;
return newHead;
};
//思考: new ListNode
var ans = null;
for ( let x = head; x != null; x = x.next) {
ans = new ListNode(x.val,ans);
}
return ans;
//类似[].reverse()
var reverse = function(pre, head) {
if(!head) return pre;
const temp = head.next;
head.next = pre;
pre = head
return reverse(pre, temp);
}
return reverse(null, head);
// 递归2
var reverse = function(head) {
if(!head || !head.next) return head;
// 从后往前翻
const pre = reverse(head.next);
head.next = pre.next;
pre.next = head;
return head;
}
var reverseList = function(head) {
let cur = head;
while(cur && cur.next) {
cur = cur.next;
}
reverse(head);
return cur;
};
方法二:指针
- 定义三个指针“前指针pre”、“当前指针cur”、“后指针next”
- 指针每次移动反向修改当前节点cur链表指针指向prev
- 修改节点完成后,更新prev和cur指针
- 最后返回prev,即可获得整条反向链表
var reverseList = function(head) {
let prev = null,cur = head,next = head;
while (cur) {
next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
//ES6解构赋值,curr.next和prev反向
//[curr.next, prev, curr] = [prev, curr, curr.next];
}
return prev;
};
92. 反转链表 II
题目描述:给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表。
方法一
切断left到right的链表,反转之(反转方法同上),再反转连接。
(切断连接,区间反转,反向连接)
var reverseBetween = function(head, left, right) {
const _Node = new ListNode(-1);
_Node.next = head//虚拟头节点
let pre = _Node
for (let i = 0; i < left - 1; i++) {
//pre遍历到left的前一个节点
pre = pre.next
}
let rightNode = pre
for (let i = 0; i < right - left + 1; i++) {
//rightNode遍历到right的位置
rightNode = rightNode.next
}
let leftNode = pre.next //保存leftNode
let curr = rightNode.next //保存rightNode.next
//切断left到right链,独立出区间
pre.next = null
rightNode.next = null
var reverseList = function(head) {
let prev = null,curr = head;
while (curr) {
[curr.next, prev, curr] = [prev, curr, curr.next];
}
};
reverseList(leftNode);
// 反向连接
pre.next = rightNode;
leftNode.next = curr;
return _Node.next;
};
方法二:
遍历到left的前一个节点,由该节点开始反转到right区间内的链表(无需切断再连接)
var reverseBetween = function(head, left, right) {
const _node = new ListNode(-1);
_node.next = head;
// 遍历到开始反转的前一个节点(left的前一个节点)
let pre = _node;
for (let i = 0; i < left - 1; ++i) {
pre = pre.next;
}
// left到right区间反转
let curr = pre.next;
for (let i = 0; i < right - left; ++i) {
const next = curr.next;
curr.next = next.next;
next.next = pre.next;
pre.next = next;
}
return _node.next;
};