【JS刷算法】链表 -- 简单系列

232 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

算法是程序员绕不开的话题,而链表又是算法中首当其冲的部分,为了一举拿下链表知识,今天是链表专题的简单系列~

1. 反转链表

LeetCode206:反转链表

题目:

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

image.png

解析:

一共三个指针,pre,cur,next,循环链表时指针依次向前走一步,循环的截止条件:cur指针在最后一个节点时做最后一次循环,即cur为null时不再循环

var reverseList = function(head) {
    let pre = null
    let cur = head
    while(cur) {
        let next = cur.next
        cur.next = pre
        pre = cur
        cur = next
    }
    return pre
};

时间复杂度: O(n),因为要循环整个链表

空间复杂度: O(1),因为只新建了三个指针占用存储

2. 合并两个有序链表

LeetCode21:合并两个有序链表

题目:

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

image.png

解析

新建一个头指针pos,循环两个链表,两个链表的当前节点分别为l1、l2,pos.next指向l1、l2中较小的节点,循环截止条件:当l1.next === null 或者 l2.next === null时。

var mergeTwoLists = function(l1, l2) {
    let pos = new ListNode(1, 1)
    let cur = pos
    while(l1 && l2) {
        if(l1.val < l2.val) {
            cur.next = l1
            cur = l1
            l1 = l1.next
        }else {
            cur.next = l2
            cur = l2
            l2 = l2.next
        }
    }
    l1 === null ? cur.next = l2 : cur.next = l1
    return pos.next 
};

时间复杂度: O(n),因为每次循环只取l1或者l2中的一个节点,所以循环的次数为O(m+n)

空间复杂度: O(1),因为只新建了一个指针占用存储

3. 回文链表

LeetCode234:回文链表

题目:

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。其实就是判断链表是否对称

image.png

解析:

  1. 用快慢指针找到中间节点,当快指针为null时,慢指针在中间节点,节点个数为奇数和偶数时的情况如下: 4a68c85bf2dbbd444068c7117112f02.jpg

2.把slow指针后面的部分反转(用到第一题的解法)

3.反转后,遍历前后两部分head1、head2,节点值一直相同,则是回文链表。循环截止条件:head2为null时,不再遍历

4.如有需要则再恢复链表

var isPalindrome = function(head) {
    // 快慢指针找出中间节点
    let slow = head
    let fast = head
    while(fast.next && fast.next.next) {
        slow = slow.next
        fast = fast.next.next
    }
    
    // 反转后半部分
    let pre = null
    let cur = slow.next
    while(cur) {
        let next = cur.next
        cur.next = pre
        pre = cur
        cur = next
    }

    let head1 = head
    let head2 = pre
    // 比较得出结果
    while(head2) {
        if(head1.val === head2.val) {
            head1 = head1.next
            head2 = head2.next
        } else {
            return false
        }
    }
    return true
};

时间复杂度: O(n),快慢指针部分O(n/2),反转链表部分O(n/2),比较部分O(n/2)

空间复杂度: O(1),因为新建指针个数也是常数量级

4. 环形链表

LeetCode141:环形链表

题目

给定一个链表,判断链表中是否有环。

image.png

解析

方法1:标记节点

判断链表中是否有环,可以通过判断节点是否被访问第二次,如果节点被第二次访问,则存在环。所以我们给访问过的节点加上标记。

var hasCycle = function(head) {
    while(head) {
        if(head.flag) {
            return true
        } else {
            head.flag = true
            head = head.next
        }
    }
    return false
};

时间复杂度: O(n),因为要循环整个链表

空间复杂度: O(n),因为在每个节点都开辟了新的空间存储flag

方法2:快慢指针

快慢指针法,如果有环,快慢指针就一定会相遇,就想环形跑道,速度慢的人一定会和速度快的在某个地方相遇。

var hasCycle = function(head) {
    if(!head) return false
    let slow = head
    let fast = head
    while(fast && fast.next) {
        slow = slow.next
        fast = fast.next.next
        if(slow === fast) {
            return true
        }
    }
    return false
};

时间复杂度: O(n),因为要循环整个链表

空间复杂度: O(1),因为新建指针个数也是常数量级

以上就是链表系列的简单部分,后续会持续更新~

原创文章,希望对你有帮助,也为社区贡献自己的一份力量!喜欢请点赞哦~

作者:前端小小梦

主页:了解更多,点击个人主页