数据结构-链表篇

178 阅读2分钟

链表

数组<队列>是比较简单的线性存储,数组的大小初始化是固定,从起点和中间插入元素成本很高,因为需要不断的移动元素。 链表存储有序的数据集合,不同于数组的是,链表存储的数据在内存中并不要求是连续的,链表中每个节点存储了当前节点值,以及指向下一个节点的指针

链表可细分为

  • 链表结构实现
    • 添加、删除元素
    • 插入元素
    • 获取链表长度、输出链表文本
  • 双向链表
  • 循环链表
  • 排序链表
  • 通过链表实现栈

链表结构实现

实现一个链表类,包含删除、添加元素、链表长度等


  class Node {
    constructor(value) {
      this.value = value
      this.next = null
    }
  }

  class LinkedList {
    count = 0

    head = null

    push(element) {
      const node = new Node(element)
      if (this.isEmpty()) {
        this.head = node
      } else {
        let current = this.head
        while (current.next) {
          current = current.next
        }

        current.next = node
      }

      this.count++
    }

    insert(element, index = 0) {
      if (index < 0 || index > this.count) {
        return false
      }

      let prious
      let current = this.head
      const node = new Node(element)
      if (index === 0) {
        if (!this.head) {
          this.head = node
        } else {
          node.next = current
          this.head = node
        }
      } else {
        for (let i = 0; i < index; i++) {
          prious = current
          current = current.next
        }

        prious.next = node
        node.next = current
      }

      this.count++
      return true
    }

    removeElement(element) {
      const index = this.getIndexBy(element)

      return index > -1 ? this.removeElementBy(index) : false
    }

    removeElementBy(index) {
      if (!this.head || index < 0 || index >= this.count) {
        return false
      }

      if (index === 0) {
        this.head = this.head.next
      } else {
        let previous
        let current = this.head
        for (let i = 0; i < index; i++) {
          previous = current
          current = current.next
        }

        previous.next = current.next
      }

      this.count--
      return true
    }

    getIndexBy(element) {
      if (!this.head || !element) {
        return undefined
      }

      let current = this.head
      for (let i = 0; current; i++) {
        if (current.value === element) {
          return i
        }

        current = current.next
      }

      // for 循环找到的直接退出函数体
      return -1
    }

    getElementBy(index) {
      if (!this.head || index < 0 || index >= this.count) {
        return undefined
      }

      let current = this.head
      // 控制次数
      for (let i = 0; i < index; i++) {
        current = current.next
      }

      return current
    }

    size() {
      return this.count
    }

    isEmpty() {
      return this.count === 0
    }

    print() {
      if (!this.count === 0) {
        console.log('')
        return
      }

      let str = ''
      let current = this.head
      while (current) {
        str = `${str}=>${current.value}`
        current = current.next
      }
    }
  }

双向链表

相比较单链表,当前元素只能指向下一个元素,双向链接区别在于,不仅可以指向下一个元素,还可以指向上一个元素


  class Node {
    constructor(value) {
      this.value = value
      this.next = null
      this.prev = null
    }
  }

  class DoubleLinkedList {
    count = 0

    head = null

    tail = null // 记录尾指针,向后插入不用一次次遍历了

    push(element) {
      const node = new Node(element)
      if (this.isEmpty()) {
        this.head = node
        this.tail = node
      } else {
        let current = this.tail;

          current.next = node;
          node.prev = current;
          this.tail = node;
      }

      this.count++
    }

    insert(element, index = 0) {
      if (index < 0 || index > this.count) {
        return false
      }

      let current = this.head
      const node = new Node(element)
      if (index === 0) {
        if (!this.head) {
          this.head = node
          this.tail = node
        } else {
          current.prev = node
          node.next = current
          this.head = node
        }
      } else if (index === this.count) {
        this.tail.next = node
        node.prev = this.tail
        this.tail = node
      } else {
        const previous = this.getElementBy(index - 1)
        node.prev = previous
        node.next = previous.next
        previous.next.prev = node
        previous.next = node
      }

      this.count++
      return true
    }

    removeElement(element) {
      const index = this.getIndexBy(element)

      return index > -1 ? this.removeElementBy(index) : false
    }

    removeElementBy(index) {
      if (!this.head || index < 0 || index >= this.count) {
        return false
      }

      let current = this.head

      if (index === 0) {
        this.head = current.next
        if (this.count === 1) {
          this.tail = null
        } else {
          this.head.prev = null
        }
      } else if (index === this.count - 1) {
        current = this.tail.prev
        this.tail = current
        this.tail.next = null
      } else {
        const previous = this.getElementBy(index)
        previous.prev.next = previous.next
        previous.next.prev = previous.prev
      }

      this.count--
      return true
    }

    getIndexBy(element) {
      if (!this.head || !element) {
        return undefined
      }

      let current = this.head
      for (let i = 0; current; i++) {
        if (current.value === element) {
          return i
        }

        current = current.next
      }

      // for 循环找到的直接退出函数体
      return -1
    }

    getElementBy(index) {
      if (!this.head || index < 0 || index >= this.count) {
        return undefined
      }

      let current = this.head
      // 控制次数
      for (let i = 0; i < index; i++) {
        current = current.next
      }

      return current
    }

    size() {
      return this.count
    }

    isEmpty() {
      return this.count === 0
    }

    print() {
      if (!this.count === 0) {
        console.log('')
        return
      }

      let str = ''
      let current = this.head
      while (current) {
        str = `${str}=>(current: ${current.value}, prev: ${current.prev && current.prev.value}, next:${
          current.next && current.next.value
        })\n`
        current = current.next
      }

      console.log(str)

      return str
    }
  }

循环链表

循环链表和普通链表最大的区别是,循环链表最后的tail指针,指向了header


  class CycleLinkedList {
    count = 0

    head = null

    push(element) {
      const node = new Node(element)
      if (this.isEmpty()) {
        this.head = node
        node.next = this.head
      } else {
        const last = this.getElementBy(this.count - 1)
        last.next = node
        node.next = this.head
      }

      this.count++
    }

    insert(element, index = 0) {
      if (index < 0 || index > this.count) {
        return false
      }

      let current = this.head
      const node = new Node(element)
      if (index === 0) {
        if (!this.head) {
          this.head = node
          node.next = this.head
        } else {
          const last = this.getElementBy(this.count - 1)
          last.next = node
          node.next = this.head
          this.head = node
        }
      } else if (index === this.count) {
        const last = this.getElementBy(this.count - 1)
        last.next = node
        node.next = current
      } else {
        const previous = this.getElementBy(index - 1)
        node.next = previous.next
        previous.next = node
      }

      this.count++
      return true
    }

    removeElement(element) {
      const index = this.getIndexBy(element)

      return index > -1 ? this.removeElementBy(index) : false
    }

    removeElementBy(index) {
      if (!this.head || index < 0 || index >= this.count) {
        return false
      }

      let current = this.head

      if (index === 0) {
        if (this.count !== 1) {
          const last = this.getElementBy(this.count - 1)
          this.head = current.next
          last.next = this.head
        } else {
          this.head = null
        }
      } else {
        const previous = this.getElementBy(index - 1)
        current = previous.next
        previous.next = current.next
      }

      this.count--
      return true
    }

    getIndexBy(element) {
      if (!this.head || !element) {
        return undefined
      }

      let current = this.head
      for (let i = 0; current; i++) {
        if (current.value === element) {
          return i
        }

        current = current.next
      }

      // for 循环找到的直接退出函数体
      return -1
    }

    getElementBy(index) {
      if (!this.head || index < 0 || index >= this.count) {
        return undefined
      }

      let current = this.head
      // 控制次数
      for (let i = 0; i < index; i++) {
        current = current.next
      }

      return current
    }

    size() {
      return this.count
    }

    isEmpty() {
      return this.count === 0
    }

    print() {
      if (!this.count === 0) {
        console.log('')
        return
      }

      let str = ''
      let current = this.head
      let i = 0
      while (current && i < this.count) {
        str = `${str}=>(current: ${current.value}, next:${current.next && current.next.value})\n`
        current = current.next
        i++
      }

      console.log(str)

      return str
    }
  }

双向循环链表

链表尾元素指向链表的头部节点, 每个链表有next和prev指针,分别指向链表的下个节点和上个节点

有序链表

按照链表的元素大小排列依次排列


 const compare = {
    BIGGER: 1,
    SMALL: -1,
    EQUAL: 0,
  }

  class Node {
    constructor(value) {
      this.value = value
      this.next = null
    }
  }

  class OrderlyinkedList {
    count = 0

    head = null

    push(element, index = 0) {
      if ((index < 0 || index > this.count) && typeof element !== 'number') {
        return false
      }

      let current = this.head
      const node = new Node(element)
      if (this.isEmpty()) {
        return this.insert(element, 0)
      } else {
        // 不断移动索引, index 和 current每次循环开始前都一一对应了
        for (var i = 0; i < this.count; i++) {
          const result = element - current.value
          if (result === compare.EQUAL || result === compare.SMALL) {
            break
          }

          current = current.next
        }

        return this.insert(element, i)
      }
    }

    insert(element, index = 0) {
      if (index < 0 || index > this.count) {
        return false
      }

      let prious
      let current = this.head
      const node = new Node(element)
      if (index === 0) {
        if (!this.head) {
          this.head = node
        } else {
          node.next = current
          this.head = node
        }
      } else {
        for (let i = 0; i < index; i++) {
          prious = current
          current = current.next
        }

        prious.next = node
        node.next = current
      }

      this.count++
      return true
    }

    removeElement(element) {
      const index = this.getIndexBy(element)

      return index > -1 ? this.removeElementBy(index) : false
    }

    removeElementBy(index) {
      if (!this.head || index < 0 || index >= this.count) {
        return false
      }

      let current = this.head

      if (index === 0) {
        if (this.count !== 1) {
          const last = this.getElementBy(this.count - 1)
          this.head = current.next
          last.next = this.head
        } else {
          this.head = null
        }
      } else {
        const previous = this.getElementBy(index - 1)
        current = previous.next
        previous.next = current.next
      }

      this.count--
      return true
    }

    getIndexBy(element) {
      if (!this.head || !element) {
        return undefined
      }

      let current = this.head
      for (let i = 0; current; i++) {
        if (current.value === element) {
          return i
        }

        current = current.next
      }

      // for 循环找到的直接退出函数体
      return -1
    }

    getElementBy(index) {
      if (!this.head || index < 0 || index >= this.count) {
        return undefined
      }

      let current = this.head
      // 控制次数
      for (let i = 0; i < index; i++) {
        current = current.next
      }

      return current
    }

    size() {
      return this.count
    }

    isEmpty() {
      return this.count === 0
    }

    print() {
      if (!this.count === 0) {
        console.log('')
        return
      }

      let str = ''
      let current = this.head
      while (current) {
        str = `${str}=>${current.value}`
        current = current.next
      }

      console.log(str)

      return str
    }
  }