For AI agents: the complete documentation index is at llms.txt. Markdown versions of any page are available by appending .md to the URL path.

数据结构与算法之链表

技术DSA

链表是一组节点组成的集合,每个节点都使用一个对象的引用指向它的下一个节点,指向节点的引用叫做链

前言

链表是一组节点组成的集合,每个节点都使用一个对象的引用指向它的下一个节点,指向节点的引用叫做链。

实现

使用 LinkedListNode 类来表示节点,使用 LinkedList 来表示链表

class LinkedListNode {
  element: any
  next: LinkedListNode

  constructor(element: any) {
    this.element = element
    this.next = null
  }
}

class LinkedList {
  // 头节点
  head: LinkedListNode

  constructor() {
    this.head = new LinkedListNode('head')
  }

  // 查找
  find(element: any) {
    let currentNode: LinkedListNode = this.head

    while (currentNode.element !== element) {
      currentNode = currentNode.next
    }
    return currentNode
  }

  // 插入
  insert(element: any, item: any) {
    let newNode = new LinkedListNode(element)
    let currentNode = this.find(item)

    newNode.next = currentNode.next
    currentNode.next = newNode
  }

  // 移除
  remove(element: any) {
    let prevNode = this.findPrevNode(element)

    if (!(prevNode.next === null)) {
      prevNode.next = prevNode.next.next
    }
  }

  //
  findPrevNode(element: any) {
    let currentNode = this.head
    while (
      !(currentNode.next === null) &&
      currentNode.next.element !== element
    ) {
      currentNode = currentNode.next
    }
    return currentNode
  }

  // 显示
  display() {
    let currentNode = this.head

    while (!(currentNode.next === null)) {
      console.log(currentNode.next.element)
      currentNode = currentNode.next
    }
  }
}

测试

const foods = new LinkedList()

foods.insert('eggs', 'head')
foods.insert('apple', 'eggs')
foods.insert('bread', 'apple')
foods.insert('chese', 'bread')
foods.insert('rice', 'chese')
foods.display()
console.log('------------')
foods.remove('bread')
foods.display()

输出

eggs
apple
bread
chese
rice
------------
eggs
apple
chese
rice

双向链表

要实现双向链表首先要为 LinkedListNode 类增加一个 prev 属性

class LinkedListNode {
  element: any
  prev: LinkedListNode
  next: LinkedListNode

  constructor(element: any) {
    this.element = element
    this.prev = null
    this.next = null
  }
}

然后修改 LinkedList 类的 insert, remove 方法

class LinkedList {
  // 头节点
  head: LinkedListNode = new LinkedListNode('head')

  // 查找
  find(element: any) {
    let currentNode = this.head

    while (currentNode.element !== element) {
      currentNode = currentNode.next
    }
    return currentNode
  }

  // 找到最后的节点
  findLast() {
    let currentNode = this.head

    while (currentNode.next !== null) {
      currentNode = currentNode.next
    }
    return currentNode
  }

  // 插入
  insert(element: any, item: any) {
    let newNode = new LinkedListNode(element)
    let currentNode = this.find(item)

    newNode.prev = currentNode
    newNode.next = currentNode.next
    currentNode.next = newNode
  }

  // 移除
  remove(element: any) {
    let currentNode = this.find(element)

    if (currentNode.next !== null) {
      currentNode.prev.next = currentNode.next
      currentNode.next.prev = currentNode.prev
      currentNode.next = null
      currentNode.prev = null
    }
  }

  // 显示
  display() {
    let currentNode = this.head

    while (!(currentNode.next === null)) {
      console.log(currentNode.next.element)
      currentNode = currentNode.next
    }
  }

  // 倒叙显示
  displayReverse() {
    let currentNode = this.findLast()

    while (currentNode.prev !== null) {
      console.log(currentNode.element)
      currentNode = currentNode.prev
    }
  }
}

测试一下

foods.insert('eggs', 'head')
foods.insert('apple', 'eggs')
foods.insert('bread', 'apple')
foods.insert('chese', 'bread')
foods.insert('rice', 'chese')
foods.display()
console.log('------------')
foods.remove('bread')
foods.display()
console.log('------------')
foods.displayReverse()

输出

eggs
apple
bread
chese
rice
------------
eggs
apple
chese
rice
------------
rice
chese
apple
eggs

循环链表

创建循环链表需要让它的头节点的 next 属性指向本身, 然后修改 display 方法

this.head.next = this.head

 // 显示
display() {
  let currentNode = this.head

  while (currentNode.next !== null && currentNode.next.element !== 'head') {
    console.log(currentNode.next.element)
    currentNode = currentNode.next
  }
}

总结

链表是一种高效的数据结构,如果发现数组在使用时很慢,就可以考虑用链表替代它,但是如果需要对数据随机访问,数组任然是更优的选择