返回

链表+6道前端算法面试高频题解|刷题打卡

前端

在上一篇专栏《数组回炉重造+6道前端算法面试高频题解|刷题打卡》中,我们回顾了数组并进行了刷题打卡。为了继续深入理解数据结构,这次我们来将目光对准链表,进行对比学习。

链表和数组最主要的区别在于其底层存储结构不同。数组要求存储在一块连续的内存中,就像一排整齐的房子,每一间房子都紧挨着。而链表则不同,它是通过指针将一组零散的内存块串联起来,就像一条火车,每一节车厢都可以单独移动。

这种不同的存储结构导致了两者在内存管理和数据存储方面各有优势。数组在内存管理上更简单,因为只需要分配一块连续的内存空间即可。但是,如果数组需要动态地添加或删除元素,那么就需要对整个数组进行重新分配,这是一个非常耗时的操作。而链表则不同,由于它是通过指针连接的,因此在添加或删除元素时,只需要修改指针即可,不需要对整个链表进行重新分配。

在数据存储方面,数组可以快速地访问任何元素,因为每个元素都存储在连续的内存空间中。但是,链表在插入和删除元素时具有优势,因为不需要移动其他元素。

为了加深对链表的理解,我们一起来解决6道前端算法面试高频题,看看链表是如何在实际应用中发挥作用的。

1. 单链表反转

给定一个单链表,将链表反转并返回新的头结点。

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

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
const reverseList = (head) => {
  let prev = null;
  let current = head;
  while (current) {
    const next = current.next;
    current.next = prev;
    prev = current;
    current = next;
  }
  return prev;
};

2. 检测链表是否有环

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

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

/**
 * @param {ListNode} head
 * @return {boolean}
 */
const hasCycle = (head) => {
  let slow = head;
  let fast = head;
  while (fast && fast.next) {
    slow = slow.next;
    fast = fast.next.next;
    if (slow === fast) {
      return true;
    }
  }
  return false;
};

3. 合并两个有序链表

给定两个有序链表,将两个链表合并为一个新的有序链表。

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

/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
const mergeTwoLists = (l1, l2) => {
  if (l1 === null) {
    return l2;
  }
  if (l2 === null) {
    return l1;
  }
  if (l1.val < l2.val) {
    l1.next = mergeTwoLists(l1.next, l2);
    return l1;
  } else {
    l2.next = mergeTwoLists(l1, l2.next);
    return l2;
  }
};

4. 删除链表中的节点

给定一个链表和一个节点值,删除链表中值为该值的节点。

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

/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
const deleteNode = (head, val) => {
  if (head === null) {
    return null;
  }
  if (head.val === val) {
    return head.next;
  }
  let current = head;
  while (current.next) {
    if (current.next.val === val) {
      current.next = current.next.next;
      break;
    }
    current = current.next;
  }
  return head;
};

5. 求链表的中间节点

给定一个链表,找到链表的中间节点。如果链表的长度为偶数,则返回中间两个节点的第一个。

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

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
const middleNode = (head) => {
  let slow = head;
  let fast = head;
  while (fast && fast.next) {
    slow = slow.next;
    fast = fast.next.next;
  }
  return slow;
};

6. 判断两个链表是否相交

给定两个链表,判断这两个链表是否相交。如果相交,返回相交的节点。

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

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
const getIntersectionNode = (headA, headB) => {
  let a = headA;
  let b = headB;
  while (a !== b) {
    a = a === null ? headB : a.next;
    b = b === null ? headA : b.next;
  }
  return a;
};

通过这6道题解,我们对链表有了更深入的了解,同时也掌握了链表的一些基本操作。相信在未来的前端开发面试中,这些知识将发挥重要的作用。