【TS数据结构】链表

链表

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素都由一个存储匀速本身的节点和一个指向下一个元素的引用组成。

链表中的一些方法

  • push(element):向链表尾部添加一个新元素
  • insert(element, position):在链表的指定位置插入一个新的元素
  • getElementAt(index):返回链表中特点位置的元素。如果链表中不存在这样的元素,则返回undefined
  • remove(element):从链表中移除一个元素
  • indexOf(element):返回元素在链表中的索引。如果链表中没有该元素则返回-1
  • removeAt(position):从链表的特定位置移除一个元素
  • isEmpty():返回链表是否为空
  • size():返回链表内包含的元素的个数
  • toString():返回表示这个那个链表的字符串。

一般链表

import { defaultEqualFunction } from '../util/defaultEqualFunction'

export default class LinkedList<T> {
  private count: number
  private head: Node<T>
  private equalFn: Function

  constructor(equalFn = defaultEqualFunction) {
    this.count = 0
    this.head = undefined
    this.equalFn = equalFn
  }

  [Symbol.iterator]() {
    let current = this.head
    return {
      next() {
        if (current) {
          let res = current
          current = current.next
          return { done: false, value: res.element }
        } else {
          return { done: true, value: undefined }
        }
      },
    }
  }

  /**
   * 向链表尾部添加一个新元素
   * @param element
   */
  push(element: T) {
    let node = new Node(element)
    if (!this.head) {
      this.head = node
    } else {
      let current = this.head
      while (current.next) {
        current = current.next
      }
      current.next = node
    }
    this.count++
  }

  /**
   * 在链表的指定位置插入一个新的元素
   * @param element
   * @param position
   */
  insert(element: T, position: number) {
    position = position > this.count ? this.count : position
    let newborn: Node<T>
    switch (true) {
      case position < 0:
        throw new Error('invalid position')
      case position === 0:
        newborn = new Node<T>(element, this.head)
        this.head = newborn
        this.count++
      case position === this.count:
        this.push(element)
        break
      default:
        let previous = this.getNodeAt(position - 1),
          current = previous.next
        newborn = new Node(element, current)
        previous.next = newborn
        this.count++
    }
  }

  /**
   * 获取第一个元素
   * @return T
   */
  getHead(): T {
    return this.head.element
  }

  /**
   * 返回链表中特点位置的元素。如果链表中不存在这样的元素,则返回undefined
   * @param index
   */
  getElementAt(index: number): T {
    if (index < 0 || index >= this.count) return undefined
    let current = this.head
    for (let i = 0; i < this.count && current; i++) {
      current = current.next
    }
    return current.element
  }

  /**
   * 从链表中移除一个元素
   * @param element
   */
  remove(element: T) {
    let current = this.head,
      previous: Node<T>
    while (current) {
      previous = current
      current = current.next
      if (this.equalFn(current.element, element)) break
    }
    previous.next = current.next
    this.count--
  }

  /**
   * 返回元素在链表中的索引。如果链表中没有该元素则返回-1
   * @param element
   */
  indexOf(element: T) {
    let current = this.head
    for (let i = 0; i < this.count && current; i++) {
      if (this.equalFn(current.element, element)) {
        return i
      }
      current = current.next
    }
    return -1
  }

  /**
   * 返回元素在链表中的索引。如果链表中没有该元素则返回-1
   * @param index
   * @return 移除的元素
   */
  removeAt(index: number): T {
    if (index < 0 || index >= this.count) return undefined
    let current = this.head
    if (index === 0) {
      this.head = current.next
    } else {
      let previous = this.getNodeAt(index - 1)
      current = previous.next
      // 将previous和current的下一项连接起来,跳过current
      previous.next = current.next
    }
    this.count--
    return current.element
  }

  /**
   * 返回链表是否为空
   * @return boolean
   */
  isEmpty() {
    return this.size() === 0
  }

  /**
   * 返回链表内包含的元素的个数
   * @return size
   */
  size(): number {
    return this.count
  }

  /**
   * 返回表示这个那个链表的字符串
   * @return string
   */
  toString() {
    return [...this].join(' -> ')
  }

  /**
   *  返回目标元素Node
   * @param index 元素的下标
   * @returns
   */
  private getNodeAt(index: number): Node<T> {
    if (index < 0 || index >= this.count) return undefined
    let current = this.head
    for (let i = 0; i < index && current; i++) {
      current = current.next
    }
    return current
  }
}

export class Node<T> {
  public element: T
  public next: Node<T> | undefined
  constructor(element: T, next: Node<T> = undefined) {
    this.element = element
    this.next = next
  }
}

从链表中移除元素

从链表中移除元素

双向链表

链表有多种不同的类型,双向链表就是链表的一种变形。双向链表和一般链表的区别在于,在链表中一个节点只有链向下一个节点的链接;而在双向链表中,链接是双向的:一个链向下一个节点,一个链向前一个元素。

实现双向链表

import LinkedList, { Node } from './linked-list'
import { defaultEqualFunction } from '../util/defaultEqualFunction'

export default class DoublyLinkedList<T> extends LinkedList<T> {
  protected tail: DoublyNode<T>
  protected head: DoublyNode<T>
  constructor(equalFn = defaultEqualFunction) {
    super(equalFn)
    this.tail = undefined
    this.head = undefined
  }

  /**
   * 在任意位置插入元素
   * @param element 待插入的元素
   * @param position 任意位置
   * @returns 是否插入成功
   */
  insert(element: T, position: number): boolean {
    if (position < 0) throw new Error('Invalid position')
    position = position > this.count ? this.count : position
    const newborn = new DoublyNode(element)
    let current = this.head
    switch (true) {
      case !current:
        // 当链表为空时,直接将this.head设置为新生节点
        this.head = newborn
        this.tail = newborn
        break
      case position === 0:
        // 当添加的位置是第一个时
        current.prev = newborn
        newborn.next = current
        this.head = newborn
        break
      case position === this.count:
        current = this.tail
        current.next = newborn
        newborn.prev = current
        this.tail = newborn
        break
      default:
        // 在中间添加元素
        let previous = this.getNodeAt(position - 1) as DoublyNode<T>
        current = previous.next
        previous.next = newborn
        newborn.prev = previous
        newborn.next = current
        current.prev = newborn
    }
    // 链表数量++
    this.count++
    return true
  }

  removeAt(index: number): T {
    if (index < 0 || index >= this.count) throw new Error('invalid index')
    let current = this.head
    if (index === 0) {
      current = current
      this.head = current.next
      this.head.prev = undefined
    } else if (index === this.count - 1) {
      current = this.tail
      this.tail = current.prev
      this.tail.next = undefined
    } else {
      let previous = this.getNodeAt(index - 1)
      current = previous.next
      previous.next = current.next
      current.next.prev = previous
    }
    this.count--
    return current.element
  }

  push(element: T) {
    if (!this.head) {
      let newborn = new DoublyNode(element)
      this.head = newborn
      this.tail = newborn
    } else {
      let newborn = new DoublyNode(element, undefined, this.tail)
      this.tail.next = newborn
      this.tail = newborn
    }
    this.count++
  }

  /**
   *  返回目标元素Node
   * @param index 元素的下标
   * @returns
   */
  protected getNodeAt(index: number): DoublyNode<T> {
    if (index < 0 || index >= this.count) return undefined
    let current = this.count / 2 < index ? this.tail : this.head
    if (index > this.count / 2) {
      for (let i = this.count - 1; i > index && current; i--) {
        current = current.prev
      }
    } else {
      for (let i = 0; i < index && current; i++) {
        current = current.next
      }
    }
    return current
  }

  toString(): string {
    return [...this].join(' <-> ')
  }
}

export class DoublyNode<T> extends Node<T> {
  public prev: DoublyNode<T>
  public next: DoublyNode<T>
  constructor(
    element: T,
    next: DoublyNode<T> = undefined,
    prev: DoublyNode<T> = undefined
  ) {
    super(element, next)
    this.prev = prev
  }
}

双向链表的大部分逻辑和一般链表是相同的,所以我们让它继承自一般链表LinkedList只需要修改insertpushremoveAt方法即可。

循环链表

循环链表可以向链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间的唯一区别在于,最后一个元素指向下一个元素的指针不是undefined,而是指向第一个元素head。

循环链表

如果是双向循环链表,那么第一个节点head的prev指向的就是最后一个节点tail,相反tail的next指向的就是head。

实现单向循环链表

import { defaultEqualFunction } from './../util/defaultEqualFunction'
import LinkedList, { Node } from './linked-list'
export default class CircularLinkedList<T> extends LinkedList<T> {
  constructor(equalFn = defaultEqualFunction) {
    super(equalFn)
  }

  [Symbol.iterator]() {
    let current = this.head
    let _self = this
    let i = 0
    return {
      next() {
        if (i++ < _self.count) {
          let res = current
          current = current.next
          return { done: false, value: res.element }
        } else {
          return { done: true, value: undefined }
        }
      },
    }
  }

  /**
   * 在指定的位置插入元素
   * @param element 待插入的元素
   * @param position 插入元素的位置
   */
  insert(element: T, position: number) {
    if (position < 0) throw new Error('invalid positive')
    position = position > this.count ? this.count : position
    let current = this.head
    const newborn = new Node(element)
    switch (position) {
      case 0:
        if (this.head) {
          newborn.next = this.head
          current = this.getNodeAt(this.count - 1)
          this.head = newborn
          current.next = newborn
        } else {
          this.head = newborn
        }
        break
      default:
        const previous = this.getNodeAt(position - 1)
        current = previous.next
        newborn.next = current
        previous.next = newborn
    }
    this.count++
  }

  /**
   * 在链表末尾添加元素
   * @param element 待添加的元素
   */
  push(element: T) {
    this.insert(element, this.count)
  }

  /**
   * 移除任意位置的元素
   * @param position 位置
   */
  removeAt(position: number): T {
    if (position < 0 || position >= this.count)
      throw new Error('Invalid position')
    let current = this.head
    if (position === 0) {
      if (this.size() === 1) {
        this.head = undefined
      } else {
        const lastNode = this.getNodeAt(this.count - 1)
        lastNode.next = current.next
        this.head = current.next
      }
    } else {
      const previous = this.getNodeAt(position - 1)
      current = previous.next
      previous.next = current.next
    }
    this.count--
    return current.element
  }
}

仓库地址:https://github.com/FarajMujey/dsa.ts

posted @ 2021-09-08 16:02  FarajMujey  阅读(811)  评论(0)    收藏  举报