LeetCode题解链表篇(TS版)

203.移除链表元素

题意:删除链表中等于给定值 val 的所有节点。

示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]

示例 2: 输入:head = [], val = 1 输出:[]

示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]

虚拟头节点

iShot_2023-05-11_11.59.24

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function removeElements(head: ListNode | null, val: number): ListNode | null {
    const dummyHead = new ListNode();
    dummyHead.next = head;
    let temp = dummyHead;
    while (temp.next) {
        if (temp.next.val === val) {
            temp.next = temp.next.next
        } else {
            temp = temp.next;
        }
    }
    return dummyHead.next;
};

707.设计链表

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

虚拟头节点

时间复杂度:初始化O(1),get消耗O(index),addAtHead消耗O(1),addAtTail消耗O(1),addAtIndex消耗O(index)。

空间复杂度:所有函数单词调用的空间复杂度均为O(1),总体空间复杂度O(n),其中n为addAtHead,addAtTail和addAtIndex的调用次数之和。

1663892191-cHQUiq-LeetCode707插入头结点
class N {
    next: N;
    val: number;

    constructor(val?: number) {
        this.val = val;
    }
}

class MyLinkedList {
    dummyNode: N = new N(0);
    size = 0;

    constructor() {

    }

    get(index: number): number {
        if (index < 0 || index >= this.size) return -1;
        let temp = this.dummyNode;
        for (let i = 0; i <= index; i++) {
            temp = temp.next;
        }
        return temp.val;
    }

    addAtHead(val: number): void {
        this.addAtIndex(0, val);
    }

    addAtTail(val: number): void {
        this.addAtIndex(this.size, val);
    }

    addAtIndex(index: number, val: number): void {
        if (index > this.size) return;
        index = Math.max(0, index);
        this.size++;
        let pred = this.dummyNode;
        for (let i = 0; i < index; i++) {
            pred = pred.next;
        }
        const n = new N(val);
        n.next = pred.next;
        pred.next = n;
    }

    deleteAtIndex(index: number): void {
        if (index < 0 || index >= this.size) return;
        this.size--;
        let pred = this.dummyNode;
        for (let i = 0; i < index; i++) {
            pred = pred.next;
        }
        pred.next = pred.next.next;
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * var obj = new MyLinkedList()
 * var param_1 = obj.get(index)
 * obj.addAtHead(val)
 * obj.addAtTail(val)
 * obj.addAtIndex(index,val)
 * obj.deleteAtIndex(index)
 */

206.反转链表

题意:反转一个单链表。

img

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

双指针

时间复杂度: O ( n ) O(n) O(n),其中n是链表的长度。需要遍历链表一次。

空间复杂度: O ( 1 ) O(1) O(1)

iShot_2023-05-12_17.05.49

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function reverseList(head: ListNode | null): ListNode | null {
    let prev: ListNode = null;
    let cur: ListNode = head;
    while (cur) {
        const temp: ListNode = cur.next;
        cur.next = prev;
        prev = cur;
        cur = temp;
    }
    return prev;
};

递归

时间复杂度: O ( n ) O(n) O(n),其中n是链表的长度。需要对链表的每个节点进行反转操作。
空间复杂度: O ( n ) O(n) O(n),其中几是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为n层。

8951bc3b8b7eb4da2a46063c1bb96932e7a69910c0a93d973bd8aa5517e59fc8
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function reverseList(head: ListNode | null): ListNode | null {
    if (!head || !head.next) return head;
    const ret = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return ret;
};

24.两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

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

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

输入:head = []
输出:[]

输入:head = [1]
输出:[1]

迭代

  • 时间复杂度: O ( n ) O(n) O(n),其中 n是链表的节点数量。需要对每个节点进行更新指针的操作。
  • 空间复杂度: O ( 1 ) O(1) O(1)

iShot_2023-05-15_10.03.01

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function swapPairs(head: ListNode | null): ListNode | null {
    if (!head || !head.next) return head;
    const dummyNode = new ListNode();
    dummyNode.next = head;
    let temp = dummyNode;
    while (temp.next && temp.next.next) {
        let node1 = temp.next;
        let node2 = temp.next.next;
        temp.next = node2;
        node1.next = node2.next;
        node2.next = node1;
        temp = node1;
    }
    return dummyNode.next;
};

递归

  • 时间复杂度: O ( n ) O(n) O(n),其中n是链表的节点数量。需要对每个节点进行更新指针的操作。
  • 空间复杂度: O ( n ) O(n) O(n),其中n是链表的节点数量。空间复杂度主要取决于递归调用的栈空间。

iShot_2023-05-15_10.19.47

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function swapPairs(head: ListNode | null): ListNode | null {
    if (head === null || head.next === null) return head;
    const newHead = head.next;
    head.next = swapPairs(newHead.next);
    newHead.next = head;
    return newHead;
};

19.删除链表的倒数第N个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

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

输入:head = [1], n = 1
输出:[]

输入:head = [1,2], n = 1
输出:[1]

  • 时间复杂度: O ( L ) O(L) O(L),其中 L L L 是链表的长度。
  • 空间复杂度: O ( L ) O(L) O(L),其中 L L L 是链表的长度。主要为栈的开销。
iShot_2023-05-15_11.03.27
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    if (!head) return head;
    const dummyNode = new ListNode(0);
    dummyNode.next = head;
    const stack: ListNode[] = [];
    let cur = dummyNode;
    let prev: ListNode = null;
    while (cur.next) {
        stack.unshift(cur);
        cur = cur.next;
    }
    while (n > 0) {
        n--;
        prev = stack.shift();
    }
    prev.next = prev.next.next;
    return dummyNode.next;
};

双指针

  • 时间复杂度: O ( L ) O(L) O(L),其中 L L L 是链表的长度。
  • 空间复杂度: O ( 1 ) O(1) O(1)
p3
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    if (!head) return head;
    const dummyNode = new ListNode(0);
    dummyNode.next = head;
    let first: ListNode = head;
    let second: ListNode = dummyNode;
    for (let i = 0; i < n; i++) {
        first = first.next;
    }
    while(first) {
        first = first.next;
        second = second.next;
    }
    second.next = second.next.next;
    return dummyNode.next;
};

160.链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

双指针

  • 时间复杂度: O ( m + n ) O(m+n) O(m+n),其中 m m m n n n 是分别是链表 h e a d A headA headA h e a d B headB headB 的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次。
  • 空间复杂度: O ( 1 ) O(1) O(1)
iShot_2023-05-15_11.51.45
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) return null;
        ListNode pA = headA;
        ListNode pB = headB;
        while (pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}

142.环形链表Ⅱ

题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

image-20230516101038026 image-20230516101130412

哈希表

思路:遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。

  • 时间复杂度: O ( N ) O(N) O(N),其中 N N N 为链表中节点的数目。我们恰好需要访问链表中的每一个节点。
  • 空间复杂度: O ( N ) O(N) O(N),其中 N N N 为链表中节点的数目。我们需要将链表中的每个节点都保存在哈希表当中。
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function detectCycle(head: ListNode | null): ListNode | null {
    if (!head) return head;
    const set = new Set();
    let temp = head;
    while (temp) {
        if (set.has(temp)) {
            return temp;
        }
        set.add(temp);
        temp = temp.next;
    }
    return null;
};

快慢双指针

  • 时间复杂度: O ( N ) O(N) O(N),其中 N N N 为链表中节点的数目。在最初判断快慢指针是否相遇时, s l o w slow slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O ( N ) + O ( N ) = O ( N ) O(N)+O(N) =O(N) O(N)+O(N)=O(N)
  • 空间复杂度: O ( 1 ) O(1) O(1)。我们只使用了 s l o u , f a s t slou,fast slou,fast 两个指针。
iShot_2023-05-16_10.15.12
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */

function detectCycle(head: ListNode | null): ListNode | null {
    if (!head) return null;
    let slow = head, fast = head;
    while (true) {
        if (!fast || !fast.next) return null;
        slow = slow.next;
        fast = fast.next.next;
        if (slow === fast) break;
    }
    fast = head;
    while (slow !== fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return fast;
};
  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值