给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L 0 → L 1 → … → L n − 1 → L n L_0 → L_1 → … → L_{n-1} → L_n L0→L1→…→Ln−1→Ln
请将其重新排列后变为:
L 0 → L n → L 1 → L n − 1 → L 2 → L n − 2 → … L_0 → L_n → L_1 → L_{n-1} → L_2 → L_{n-2} → … L0→Ln→L1→Ln−1→L2→Ln−2→…
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
输入: head = [1,2,3,4]
输出: [1,4,2,3]
这道题的要求是根据下标来进行节点的链接,我们可以把每个都保存到数组中,然后按照题目中所给的规律进行链表的重链接即可。
在重链接之前,我们需要将每个节点与其他节点分割开并保存到数组nodes
中,即代码中的while循环部分。
然后就是找规律了,只需要观察下标0, n, 1, n-1, 2, n-2 ……
可以看出,每两个相邻的节点的下标之和为n
,需要注意的是,n
是最后一个元素的下标,若nodes
的长度为len
,则n
对应着len-1
。也就说是说,相邻两个元素的下标之和应该为len -1
。
var reorderList = function(head) {
let nodes = []
let p = head
while(p !== null){
// 临时变量,存放当前节点的下一个节点
const temp = p.next
// 将当前节点与下一个节点分隔开
p.next = null
// 将当前节点存放到数组中
nodes.push(p)
//更新当前节点
p = temp
}
const len = nodes.length
// 我们只需要将 nodes 后半部分的元素插入到前半部分即可,因此只需要遍历 len 大小的一半
for(let i = 0, limit = Math.floor(len / 2); i < limit; i++){
// 将当前节点与对应的节点链接
nodes[i].next = nodes[len - 1 - i]
// 防止处于 nodes 中间的那个节点自身形成闭环
if(len - 1 - i !== i + 1)
nodes[len - 1 - i].next = nodes[i+1]
}
return head
};
我最开始的构想是把这个链表分成前后两个部分,然后对后半部分的进行逆序,再依次将元素插入到前半部分中。这种方法思路很直接,不用找规律,空间复杂度 O ( 1 ) O(1) O(1),但是写起来代码较多。
var reorderList = function(head) {
//将原始链表从中间拆分,使用了快慢指针的方法
// 前一部分链表的头节点为 head, 后一部分的头节点为 slow
let fast = head
let slow = head
let slow_pre = null
while(fast !== null){
slow_pre = slow
slow = slow.next
if(fast.next === null) break
fast = fast.next.next
}
//将前面一个链表的最后一个节点的 next 置 null,防止和后面的链表纠缠在一起
slow_pre.next = null
//链表逆序
slow = reverseList(slow)
let p = head
while(p !== null && slow !== null){
// 保存当前 slow 的下一个节点
const temp = slow.next
// 将被弹出的头节点插入到对应的位置
slow.next = p.next
p.next = slow
// 将 slow 继续指向新的头节点
slow = temp
// 因为插入了一个节点,所以需要使用两次 next
p = p.next.next
}
return head
};
// 链表逆序函数
var reverseList = head => {
let p = head
let newHead = null
while(p !== null){
const temp = p.next
p.next = newHead
newHead = p
p = temp
}
return newHead
}
链表逆序部分的思路可以参考这篇文章 剑指 Offer II 024. 反转链表