返回

Javascript算法实现:反转链表 旋转链表 链表相交 数据流中的第K大元素 翻转二叉树

前端

揭秘链表和二叉树的五大经典算法

在计算机科学中,数据结构和算法是至关重要的基石。其中,链表和二叉树是两种广泛使用的非线性数据结构,广泛应用于各种应用中。本文将深入探讨与链表和二叉树相关的五大经典算法,它们是程序员必备的技能。

1. 反转链表

链表是一种线性数据结构,由一组节点组成,每个节点包含一个值和指向下一个节点的指针。反转链表是将其节点的顺序从头到尾颠倒的过程。反转链表的一个简单方法是使用三个指针:前一个指针指向当前节点的前一个节点,当前指针指向当前节点,下一个指针指向当前节点的下一个节点。然后,将当前节点的下一个指针指向前一个节点,并将前一个节点指向当前节点,最后将当前节点移动到下一个节点。重复这个过程,直到遍历完整个链表。

function reverseList(head) {
  let prev = null;
  let curr = head;
  while (curr) {
    const next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
  }
  return prev;
}

2. 旋转链表

旋转链表是将链表向右旋转一定位置的过程。给定一个链表和一个旋转位置,我们可以将链表分为两部分:第一部分是需要旋转的部分,第二部分是剩下的部分。然后,将第二部分连接到第一部分的尾部,并断开第一部分的尾部与第二部分的连接。最后,将第二部分的头节点作为旋转后的链表的头节点返回。

function rotateRight(head, k) {
  if (!head || !head.next || k === 0) {
    return head;
  }
  let len = 1;
  let curr = head;
  while (curr.next) {
    curr = curr.next;
    len++;
  }
  k %= len;
  if (k === 0) {
    return head;
  }
  let slow = head;
  let fast = head;
  while (k--) {
    fast = fast.next;
  }
  while (fast.next) {
    slow = slow.next;
    fast = fast.next;
  }
  const newHead = slow.next;
  slow.next = null;
  fast.next = head;
  return newHead;
}

3. 链表相交

链表相交问题是指判断两个链表是否相交,以及相交的话返回相交的节点。我们可以使用两个指针,一个指针指向第一个链表的头节点,另一个指针指向第二个链表的头节点。然后,两个指针同时遍历两个链表,当其中一个指针到达链表的尾部时,就重新指向另一个链表的头节点。重复这个过程,直到两个指针相遇或者两个指针都到达链表的尾部。如果两个指针相遇,则表明两个链表相交,返回相交结点的值。如果两个指针都到达链表的尾部,则表明两个链表不相交,返回-1。

function getIntersectionNode(headA, headB) {
  let pA = headA;
  let pB = headB;
  while (pA !== pB) {
    pA = pA ? pA.next : headB;
    pB = pB ? pB.next : headA;
  }
  return pA;
}

4. 数据流中的第K大元素

数据流中的第K大元素问题是指,给定一个数据流,要求随时返回数据流中第K大的元素。我们可以使用一个小顶堆来保存数据流中的元素。小顶堆是一种数据结构,它可以快速地找到堆中的最小元素。当数据流中的元素超过K个时,将堆中的最小元素弹出,然后将新元素插入堆中。这样,堆中始终保存着数据流中最大的K个元素,并且堆顶的元素就是数据流中的第K大元素。

class KthLargest {
  constructor(k) {
    this.k = k;
    this.heap = [];
  }

  add(val) {
    this.heap.push(val);
    this.heap.sort((a, b) => a - b);
    if (this.heap.length > this.k) {
      this.heap.shift();
    }
  }

  getKthLargest() {
    return this.heap[0];
  }
}

5. 翻转二叉树

翻转二叉树是指将二叉树的左右子树交换。我们可以使用递归来遍历二叉树。在遍历每个节点时,将它的左右子树交换,然后递归地遍历它的左右子树。这样,整棵二叉树就会被翻转。

function invertTree(root) {
  if (!root) {
    return null;
  }
  const left = invertTree(root.left);
  const right = invertTree(root.right);
  root.left = right;
  root.right = left;
  return root;
}

常见问题解答

1. 链表和二叉树有什么区别?

链表是一种线性数据结构,由一组节点组成,每个节点包含一个值和指向下一个节点的指针。二叉树是一种树形数据结构,由一个根节点组成,每个节点最多有两个子节点。

2. 反转链表和旋转链表有什么区别?

反转链表是将链表的顺序从头到尾颠倒,而旋转链表是将链表向右旋转一定位置。

3. 链表相交问题如何处理环形链表?

对于环形链表,可以使用快慢指针法来检测相交。快指针每次移动两步,慢指针每次移动一步。如果快指针和慢指针相遇,则表明链表存在环。

4. 数据流中的第K大元素问题如何处理无序数据?

对于无序数据,可以使用快速选择算法来找到第K大元素。快速选择算法是一种基于分治的排序算法,它可以快速地找到数组中第K大元素。

5. 翻转二叉树会影响二叉树的遍历顺序吗?

翻转二叉树不会影响二叉树的遍历顺序。例如,对于前序遍历,翻转二叉树后,遍历顺序仍然是根节点、左子树、右子树。