数据结构与算法JavaScript实现:链表,双向链表,循环链表,约瑟夫环

链表 (LinkList)

1. 链表的特点

2. 实现链表

  1. 条件设定:

    • 链表的节点都应通过 Node 类实例化得到
    • 链表实例化时,自动生成头指针, new Node('head'), 其 next 属性默认为 null
    • 头指针点不计入链表的总长度
    • 链表最后一个节点的 next 属性永远指向 null
  2. 代码

// 节点类
// 每个节点包含节点的值和节点下一个节点的引用(`next`属性)
// 刚初始化的节点默认指向 null
class Node {
  constructor(value) {
    this.value = value
    this.next = null
  }
}
/**
 * 链表类
 */
class LinkedList {
  constructor() {
    this.head = new Node('head')
    this.length = 0
  }

  // 向链表的尾部插入一个元素
  append(...values) {
    values.forEach(v => {
      const tailNode = this.findTailNode()
      tailNode.next = new Node(v)
      this.length++
    })
  }

  // 查找链表末尾元素
  findTailNode() {
    let tail = this.head
    while (tail.next) {
      tail = tail.next
    }
    return tail
  }

  // 在某个节点后插入一个节点
  // 在值为 value 的节点后边新添加一个元素 newValue
  insertAfter(newValue, value) {
    const newNode = new Node(newValue);
    const nodeBefore = this.findByValue(value)
    newNode.next = nodeBefore.next
    nodeBefore.next = newNode
    this.length++
  }

  // 在某个节点前插入一个节点
  // 在值为 value 的节点前边边新添加一个元素 newValue
  insertBefore(newValue, value) {
    const prevNode = this.findPreviosByValue(value)
    const currentNode = this.findByValue(value)
    const newNode = new Node(newValue)
    newNode.next = currentNode
    prevNode.next = newNode
    this.length++

  }

  // 通过值去查找节点, 
  // 查找到了, 返回元素
  // 找不到的话返回 null
  findByValue(value) {
    let currentNode = this.head
    while (currentNode && currentNode.value !== value) {
      currentNode = currentNode.next
    }
    return currentNode
  }
  // 通过值删除链表节点
  removeByValue(value) {
    const prevNode = this.findPreviosByValue(value)
    let currentNode = this.findByValue(value)
    if (!prevNode || !currentNode) return false;
    prevNode.next = currentNode.next
    currentNode = null
    this.length--
    return true
  }
  // // 通过值查找节点索引
  indexOf(value) {
    // 头结点不计入索引, 从头结点后第一个节点开始记录
    let currentNode = this.head.next,
      i = 0
    for (; i < this.length; i++) {
      if (currentNode.value === value) {
        return i
      } else {
        currentNode = currentNode.next
      }
    }
    return -1

  }
  // 是否有包含某个值的节点
  includes(value) {
    return this.indexOf(value) > -1
  }
  // 通过索引查找节点
  findByIndex(index) {
    const isOutBoundary = index < -1 || index >= this.length
    if (isOutBoundary) return null
    let currentNode = this.head.next
    for (let i = 0; i < this.length; i++) {
      if (index === i) {
        return currentNode
      } else {
        currentNode = currentNode.next
      }
    }
  }
  // 查找当前索引的上一个元素
  findPreviosByIndex(index) {
    const isOutBoundary = index <= 0 || index > this.length
    if (isOutBoundary) return null
    let currentNode = this.head.next
    for (let i = 0; i < this.length; i++) {
      if (index === i + 1) {
        return currentNode
      } else {
        currentNode = currentNode.next
      }
    }
    return null


  }
  // 通过索引删除元素
  removeByIndex(index) {
    const isOutBoundary = index < 0 || index > this.length - 1
    if (isOutBoundary) return null
    const prevNode = this.findPreviosByIndex(index)
    const currentNode = this.findByIndex(index)
    prevNode.next = currentNode.next
    this.length--
    return currentNode
  }

  // 根据自定义条件删除
  // 传入一个 fn(node) 返回 true 则删除
  // fn(node) 返回 false 不删除
  removeByCondition(fn) {
    let currentNode = this.head.next
    while (currentNode) {
      if (fn.call(null, currentNode)) {
        this.removeByValue(currentNode.value)
      }
      currentNode = currentNode.next
    }
  }
  // 查找当前节点的上一个节点
  // 删除当前节点的时候要将上个节点的 next 指向当前节点的 next 属性指向的节点
  findPreviosByValue(value) {
    let prevNode = this.head
    while (prevNode.next && prevNode.next.value !== value) {
      prevNode = prevNode.next
    }
    const isFound = prevNode.next && prevNode.next.value === value
    return isFound ? prevNode : null
  }

  // 通过索引更新节点值
  updateByIndex(index, newValue) {
    const currentNode = this.findByIndex(index)
    if (currentNode) {
      currentNode.value = newValue
      return true
    }
    return false
  }
  // 通过值更新节点值
  updateByValue(oldValue, newValue) {
    const currentNode = this.findByValue(oldValue)
    if (currentNode) {
      currentNode.value = newValue
      return true
    }
    return false
  }

  // 打印链表到控制台
  print() {
    console.log(JSON.stringify(this, null, 2))
  }
}

3. 双向链表

3.1 双向链表特点:

  1. 每个元素比但链表多了一个 prev 指针, 指向前一个节点

3.2 双向链表的实现

本来可以通过继承单链表, 只重写几个方法就可以了, 后来想了想, 以后使用双向链表还要引入单项链表, 所以就直接分离了。

// 双向链表节点,比普通链表多了 prev 属性
class DoublyNode {
  constructor(value) {
    this.prev = null
    this.next = null
    this.value = value
  }
}

// 双向链表节点,比普通链表多了 prev 属性
class DoublyLinkedList {
  constructor() {
    super()
    this.head = new DoublyNode(`head`)
    this.length = 0
  }
  // // 通过值查找节点索引
  indexOf(value) {
    // 头结点不计入索引, 从头结点后第一个节点开始记录
    let currentNode = this.head.next,
      i = 0
    for (; i < this.length; i++) {
      if (currentNode.value === value) {
        return i
      } else {
        currentNode = currentNode.next
      }
    }
    return -1

  }
  // 是否有包含某个值的节点
  includes(value) {
    return this.indexOf(value) > -1
  }
  // 通过索引查找节点
  findByIndex(index) {
    const isOutBoundary = index < -1 || index >= this.length
    if (isOutBoundary) return null
    let currentNode = this.head.next
    for (let i = 0; i < this.length; i++) {
      if (index === i) {
        return currentNode
      } else {
        currentNode = currentNode.next
      }
    }
  }
  // 查找当前索引的上一个元素
  findPreviosByIndex(index) {
    const isOutBoundary = index <= 0 || index > this.length
    if (isOutBoundary) return null
    let currentNode = this.head.next
    for (let i = 0; i < this.length; i++) {
      if (index === i + 1) {
        return currentNode
      } else {
        currentNode = currentNode.next
      }
    }
    return null


  }

  // 查找链表末尾元素
  findTailNode() {
    let tail = this.head
    while (tail.next) {
      tail = tail.next
    }
    return tail
  }
  // 通过值去查找节点, 
  // 查找到了, 返回元素
  // 找不到的话返回 null
  findByValue(value) {
    let currentNode = this.head
    while (currentNode && currentNode.value !== value) {
      currentNode = currentNode.next
    }
    return currentNode
  }

  // 越界检查
  isOutBoundary(index) {
    return index < 0 || index >= this.length
  }
  // 在当前索引添加一个元素
  insert(index, value) {
    if (this.isOutBoundary(index)) return false;
    const node = new DoublyNode(value)
    const currentNode = this.findByIndex(index)
    // 在第一个位置增加
    if (index === 0) {
      node.next = currentNode
      currentNode.prev = node
      this.head.next = node
      node.prev = this.head
    } else {
      node.prev = currentNode.prev
      node.next = currentNode
      currentNode.prev.next = node
    }
    this.length++
    return true
  }



  // 以下方法需要全部重写,属于对链表的增删改, 链表的查询方法不需要重写

  // OK 向链表尾部增加元素
  append(...values) {
    values.forEach(v => {
      const tailNode = this.findTailNode()
      const node = new DoublyNode(v)
      tailNode.next = node
      node.prev = tailNode
      this.length++
    })
  }

  // 在某个节点后插入一个节点
  // OK 在值为 value 的节点后边新添加一个元素 newValue
  insertAfter(value, newValue) {
    const newNode = new Node(newValue);
    const currentNode = this.findByValue(value)
    newNode.next = currentNode.next
    currentNode.next.prev = newNode
    newNode.prev = currentNode
    currentNode.next = newNode
    this.length++
  }

  // 在某个节点前插入一个节点
  // ok 在值为 value 的节点前边边新添加一个元素 newValue
  insertBefore(value, newValue) {
    const currentNode = this.findByValue(value)
    const newNode = new Node(newValue)
    newNode.next = currentNode
    newNode.prev = currentNode.prev
    currentNode.prev.next = newNode
    currentNode.prev = newNode
    this.length++

  }


  // OK 通过值删除链表节点
  removeByValue(value) {
    const prevNode = this.findPreviosByValue(value)
    let currentNode = this.findByValue(value)
    if (!prevNode || !currentNode) return false;
    prevNode.next = currentNode.next
    currentNode.next.prev = prevNode
    this.length--
    return true
  }
  // OK 通过索引删除元素
  removeByIndex(index) {
    const isOutBoundary = index < 0 || index > this.length - 1
    if (isOutBoundary) return null
    const prevNode = this.findPreviosByIndex(index)
    const currentNode = this.findByIndex(index)
    prevNode.next = currentNode.next
    currentNode.next.prev = prevNode
    this.length--
    return currentNode
  }

  // 查找当前节点的上一个节点
  // 删除当前节点的时候要将上个节点的 next 指向当前节点的 next 属性指向的节点
  findPreviosByValue(value) {
    let prevNode = this.head
    while (prevNode.next && prevNode.next.value !== value) {
      prevNode = prevNode.next
    }
    const isFound = prevNode.next && prev.next.value === value
    return isFound ? prevNode : null
  }

  // 通过索引更新节点值
  updateByIndex(index, newValue) {
    const currentNode = this.findByIndex(index)
    if (currentNode) {
      currentNode.value = newValue
      return true
    }
    return false
  }
  // 通过值更新节点值
  updateByValue(oldValue, newValue) {
    const currentNode = this.findByValue(oldValue)
    if (currentNode) {
      currentNode.value = newValue
      return true
    }
    return false
  }

}

4. 循环链表

5. API

1. 属性

2. 方法

接口名称说明参数
append向链表插入节点, 节点的值为 value(value : any)
单元格单元格单元格
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值