链表-循环链表

循环链表可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间唯一的区别在于

最后一个元素指向下一个元素的指针(tail.next)不是引用undefined,而是指向第一个元素(head).

  • 单链表: this.tail.next = this.head;
  • 双向链表: this.tail.next = this.head; this.head.prev = this.tail;

这里简化演示就单向链表的方式来操作即可;

链表初始化

// 节点类
class Node {
  constructor(element) {
    this.element = element; 
    this.next = undefined; 
  }
}


// 链表类
class CicularLinkedList {
  constructor() {
    this.count = 0; 
    this.head = null; 
  }

  size() {
    return this.count
  }

  toString() {
    if (this.head == null) return undefined
    let str = `${this.head.element}`
    let current = this.head.next 
    for (let i = 1; i < this.count; i++) {
      str = `${str}, ${current.element}`
      current = current.next 
    }
  }

  getElementAt(index) {
    if (index < 0 || index > this.count) return undefined
    let node = this.head; 
    for (let i = 0; i < index; i++) {
      node = node.next;
    }
    return node;
  }

  indexOf(element) {
    let current = this.head;
    for (let i = 0; i < this.count; i++) {
      if (current.element == element) return i;
      current = current.next; 
    }
    return -1;
  }
  
}


在任意位置插入元素

  • 头结点插入
    • 空链表, 自循环
    • 非空链表, 记得通过 getElement() 方法将 tail 元素指向 head
  • 中间或尾部插入, 调整 previous, current 即可
// 在任意位置插入元素
  insert(index, element) {
    if (index < 0 || index > this.count) return false
    const node = new Node(element)
    let current = this.head 

    if (index == 0) {
      // 空链表插入元素, 则自循环呗
      if (this.head == null) {
        this.head = node
        node.next = this.head 
      } else {
        // 记得要将最后一个元素的 next 指向头结点元素
        node.next = current
        this.head = node 

        const tail = this.getElementAt(this.size())
        tail.next = this.head 
      }
    } else {
      // 从中间或者尾部插入, 调整 previous, current  指针即可
      const previous = this.getElementAt(index - 1)
      current = previous.next 

      previous.next = node 
      node.next = current
    }
    // 记得更新链表长度
    this.count++
    return true
  }

和普通链表一样的, 唯一要考虑的就是要将尾节点的 next 连接到头节点

在任意位置删除元素

  • 删除头节点
    • 空链表, 返回 undefined
    • 链表仅一个元素, 让 head ->null 即可
    • 链表有多个元素, 让 head -> 原第二; tail -> 原第二 (现第一)
  • 中间或尾部删, 调整 previous , current 即可
  // 从任意位置删除元素
  removeAt(index) {
    // 越界和空链表检查
    if (index < 0 || index > this.count) return undefined
    if (this.head == null) return undefined

    let current = this.head 
    if (index == 0) {
      if (this.count == 1) {
        // 删除头节点, 且当只有一个元素时, 让 head -> null 即可
        this.head = undefined
      } else {
        // 删除头结点, 后面原来还有多个元素, head -> 原第二, tail -> 原第二(现第一 )
        current = this.head 
        this.head = this.head.next 

        const tail = this.getElementAt(this.count)
        tail.next = this.head.next 
      }
    } else {
      // 删除中间或者尾部元素, 不用常更新 tail
      const previous = this.getElementAt(index - 1)
      current = previous.next 

      previous.next = current.next 
    }
    // 记得更新链表长度
    this.count--
    return current.element
  }

  // 删除元素
  remove(element) {
    const index = this.indexOf(element)
    return this.removeAt(index)
  }

}

循环链表-完整实现

// 节点类
class Node {
  constructor(element) {
    this.element = element
    this.next = undefined
  }
}


// 链表类
class CicularLinkedList {
  constructor() {
    this.count = 0
    this.head = null
  }

  size() {
    return this.count
  }

  toString() {
    if (this.head == null) return undefined
    let str = `${this.head.element}`
    let current = this.head.next 
    for (let i = 1; i < this.count; i++) {
      str = `${str}, ${current.element}`
      current = current.next 
    }
    return str 
  }

  getElementAt(index) {
    if (index < 0 || index > this.count) return undefined
    let node = this.head; 
    for (let i = 0; i < index; i++) {
      node = node.next
    }
    return node
  }

  indexOf(element) {
    let current = this.head;
    for (let i = 0; i < this.count; i++) {
      if (current.element == element) return i
      current = current.next
    }
    return -1
  }

  // 在任意位置插入元素
  insert(index, element) {
    if (index < 0 || index > this.count) return false
    const node = new Node(element)
    let current = this.head 

    if (index == 0) {
      // 空链表插入元素, 则自循环呗
      if (this.head == null) {
        this.head = node
        node.next = this.head 
      } else {
        // 记得要将最后一个元素的 next 指向头结点元素
        node.next = current
        this.head = node 

        const tail = this.getElementAt(this.size())
        tail.next = this.head 
      }
    } else {
      // 从中间或者尾部插入, 调整 previous, current  指针即可
      const previous = this.getElementAt(index - 1)
      current = previous.next 

      previous.next = node 
      node.next = current
    }
    // 记得更新链表长度
    this.count++
    return true
  }

  // 从任意位置删除元素
  removeAt(index) {
    // 越界和空链表检查
    if (index < 0 || index > this.count) return undefined
    if (this.head == null) return undefined

    let current = this.head 
    if (index == 0) {
      if (this.count == 1) {
        // 删除头节点, 且当只有一个元素时, 让 head -> null 即可
        this.head = undefined
      } else {
        // 删除头结点, 后面原来还有多个元素, head -> 原第二, tail -> 原第二(现第一 )
        current = this.head 
        this.head = this.head.next 

        const tail = this.getElementAt(this.count)
        tail.next = this.head.next 
      }
    } else {
      // 删除中间或者尾部元素, 不用常更新 tail
      const previous = this.getElementAt(index - 1)
      current = previous.next 

      previous.next = current.next 
    }
    // 记得更新链表长度
    this.count--
    return current.element
  }

  // 删除元素
  remove(element) {
    const index = this.indexOf(element)
    return this.removeAt(index)
  }

}


// test 
const list = new CicularLinkedList()

list.insert(0, '111')
console.log('链表是: ', list.toString());

list.insert(1, '222')
console.log('链表是: ', list.toString());

list.insert(2, '333')
console.log('链表是: ', list.toString());

list.insert(list.size(), '444')
console.log('链表是: ', list.toString());

list.insert(0, '555')
console.log('链表是: ', list.toString());
console.log('链表长度是: ', list.size());

console.log('尾元素的 next 是: ', list.getElementAt(list.size() - 1).next.element);

console.log('删除头部元素: ', list.removeAt(0));
console.log('链表是: ', list.toString());
console.log('链表长度是: ', list.size());

console.log('删除位置 1 的元素: ', list.removeAt(1));
console.log('链表是: ', list.toString());
console.log('链表长度是: ', list.size());

console.log('删除尾部部元素: ', list.removeAt(list.size() - 1 ));
console.log('链表是: ', list.toString());
console.log('链表长度是: ', list.size());

测试结果如下:

PS E:\Front_Mook\projects\shop-admin> node test.js
链表是:  111
链表是:  111, 222
链表是:  111, 222, 333
链表是:  111, 222, 333, 444
链表是:  555, 111, 222, 333, 444
链表长度是:  5
尾元素的 next 是:  555
删除头部元素:  555
链表是:  111, 222, 333, 444
链表长度是:  4
删除位置 1 的元素:  222
链表是:  111, 333, 444
链表长度是:  3
删除尾部部元素:  444
链表是:  111, 333
链表长度是:  2

至次, 循环链表也基本理解差不多了, 还是比较容易实现的, 通过形象化的想象出来.

posted @ 2024-06-08 19:15  致于数据科学家的小陈  阅读(6)  评论(0编辑  收藏  举报