剑指 Offer II 024. 反转链表

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

描述

给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。


示例:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
输入:head = [1,2]
输出:[2,1]
输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

解题思路

修改数值法

创建一个数组vals,用于存放链表中节点的值。遍历链表,把每一个节点的值依次添加到数组中,通过push方法即可。然后再次遍历链表,不断把vals中最后一个元素pop出来并赋值给当前节点。也就是说,这里的vals可以看作是一个栈,由于入栈的顺序和出栈的顺序正好是相反的,因此每个元素出栈的时候依次赋值给正向遍历的链表,就可以实现反转链表的目的。这种方法的时间复杂度和空间复杂度为 O ( n ) O(n) O(n),但是相当简单。

var reverseList = function(head) {
    let vals = []
    let p = head
    while(p !== null){
        vals.push(p.val)
        p = p.next
    }
    p = head
    while(p !== null){
        p.val = vals.pop()
        p = p.next
    }
    return head
};

头插法

这个比较好理解了,把原始链表的头节点不断弹出,放到另一个链表的尾部。这种思想跟第一种解法很类似。只需要用一个指针一直指向新的链表的头节点即可。由于只用了三个新的变量,因此空间复杂度为 O ( 1 ) O(1) O(1)

var reverseList = function(head) {
	// 定义新链表的头指针
    let newHead = null
    // p一直指向原始链表的即将被弹出的首节点
    let p = head
    while(p !== null){
    	// 保存原始链表弹出第一个节点后的首节点
        const temp = p.next
        p.next = newHead
        newHead = p
        p = temp
    }
    return newHead
};

递归法

  • 定义一个函数reserveNode,每次需要传的参数为当前节点cur和它的上一个节点pre
  • 先在函数中不断递归调用自身,传入的参数分别为cur.nextcur,就是当前节点的下一个节点和当前节点。当然,我们并不能保证cur是否为空(链表的最后一个节点的nextnull)。因此,需要判断cur是否为空。
var reserveNode = function(cur, pre){
 	if(cur === null) return
 	console.log(cur,pre)
	reserveNode(cur.next, cur)
}
// 题目中所给的主函数
var reverseList = function(head) {
	//因为head的上一个节点并不存在,所以第二个参数传为null
	return reserveNode(head, null)
};

执行上面的代码,我们可以看到控制台会输出


[1,2,3,4,5] null
[2,3,4,5] [1,2,3,4,5]
[3,4,5] [2,3,4,5]
[4,5] [3,4,5]
[5] [4,5]

其实等价于(每个中括号代表一个链表节点)

[1] null
[2] [1]
[3] [2]
[4] [3]
[5] [4]

也就是说,我们成功得到了每个节点和它上一个节点的组合。那么接下来我们需要把每个组合依次链接。如[5].next = [4] [4].next = [3] ...以此类推。只需要将cur.next = pre便可以完成。emmm,要是还不明白,自己用一个实例过一遍就能通啦。由于使用递归函数,所以空间复杂度依旧是 O ( n ) O(n) O(n)

var reverseList = function(head) {
    var reserveNode = function(cur, pre){
    	// cur 指向的是[5]的时候,返回[5]
        if(cur === null) return pre
        let temp = reserveNode(cur.next, cur)
        // 将当前节点的 next 指向它原来的上一个节点
        cur.next = pre
        // 返回当前节点的 next 指向的节点
        return temp
    }
    return reserveNode(head, null)
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值