力扣题目——143. 重排链表

注:本文的代码实现使用的是 JS(JavaScript),为前端中想使用JS练习算法和数据结构的小伙伴提供解题思路。

描述

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L 0 → L 1 → … → L n − 1 → L n L_0 → L_1 → … → L_{n-1} → L_n L0L1Ln1Ln
请将其重新排列后变为:

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} → … L0LnL1Ln1L2Ln2

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。


示例:

力扣题目——143. 重排链表_第1张图片

输入: 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. 反转链表

你可能感兴趣的:(基于js的算法题解,链表,数据结构,算法)