双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向直接后继和直接前驱。所以,双向链表中的任意一个节点开始,都可以很方便的访问它的前驱节点和后继节点。

双向链表的实现

linkednode.js ,里面使用了类的继承extends,使用了super函数。


/** * 链表节点,链表中的项,链表中的节点 */ export class Node { constructor(element, next = null) { this.element = element // 链表中节点的值 this.next = next // 指向列表中下一个节点项的指针 } } export class DoublyNode extends Node { constructor(element, next = null, prev = null) { super(element, next) this.prev = prev } }

doublyLinkedList.js 双向链表类,实现了各个功能,功能说明,都在代码注释中


import { DoublyNode } from './linkednode' /** * 双向链表类 */ export class DoublyLinkedList { constructor() { /** * 链表长度 */ this.length = 0 /** * 头指针 */ this.head = null /** * 尾指针 */ this.tail = null } /** * 在链表末尾添加元素 * @param {*} element 需要插入的元素 */ append(element) { let node = new DoublyNode(element) if (!this.head) { this.head = node this.tail = node } else { this.tail.next = node node.prev = this.tail this.tail = node } this.length++ return true } /** * 在任意位置插入元素 * @param {Int32Array} position 指定位子 * @param {*} element 需要插入的元素 */ insert(position, element) { // 检查越界值 if (position >= 0 && position <= this.length) { // 实例化一个双向链表的节点 let node = new DoublyNode(element) // 赋初始值 let current = this.head let previous let index = 0 // 位置索引 if (position === 0) { // 在第一个位子添加 if (!this.head) { // 链表无数据的时候,将head和tail都指向新元素 this.head = node this.tail = node } else { // 链表有数据的时候, head node current node.next = current current.prev = node this.head = node } } else if (position === this.length) { // 添加到最后一项 current node current = this.tail current.next = node node.prev = current this.tail = node } else { // 在列表中间位置添加 // 新链表的节点原型是: previous <---> node <---> current while (index++ < position) { // 位置索引递增到指定点之前,找出前后两个节点 previous = current // 当前节点设置为新链表中要插入的节点的前一个元素。 current = current.next // 当前节点之后的元素设置为新链表中要插入的节点的当前元素 } node.next = current previous.next = node current.prev = node node.prev = previous } this.length++ // 更新列表的长度 return true } else { return false } } /** * 在任意位置插入元素 * 在链表头,在链表尾,在链表前半段,在链表后半段 * @param {Int32Array} position 指定位置 * @param {*} element 需要插入的元素 */ insert_up(position, element) { let node = new DoublyNode(element) let previous let current = this.head if (position > -1 && position <= this.length) { if (position === 0) { if (!this.head) { this.head = node this.tail = node } else { node.next = current current.prev = node this.head = node } } else if (position === this.length) { current = this.tail current.next = node node.prev = current this.tail = node } else if (position < this.length / 2) { // 目标在链表前半段 let index = 0 // 0 1 2 [] 3 4 5 while (index++ < position) { previous = current current = current.next } previous.next = node node.next = current node.prev = previous current.prev = node } else { // 目标在链表的后半段 // 0 1 2 3 4 | 5 6 [] 7 8 9 let index = this.length current = this.tail while (index-- > position) { previous = current.prev current = current } previous.next = node node.next = current node.prev = previous current.prev = node } this.length++ return true } else { // 如果超出范围,直接添加到链表末尾 let current = this.tail current.next = node node.prev = current this.tail = node this.length++ return true } } /** * 从任意位置移除元素,返回移除的元素 * 从头部,从尾部,从链表的前半段,从链表的后半段 * @param {*} position 位置索引 */ removeAt(position) { let current = this.head // 当前项 let previous // 前一项 let index = 0 // 索引 // 越界检查 if (position > -1 && position < this.length) { if (position === 0) { // 第一项 this.head = current.next // 如果是最后一项要删除,将tail置为null,此时head也为null // 如果非最后一项,则将this.head.prev置为null if (this.length === 1) { // 只有一项的情况,更新tail this.tail = null } else { this.head.prev = null // 将首项的prev置空 或者 current.next.prev = null } } else if (position === this.length - 1) { // 最后一项 current = this.tail previous = current.prev this.tail = previous this.tail.next = null } else if (position <= this.length / 2) { // 索引在链表前半段,分开计算,提升性能 while (index++ < position) { previous = current current = current.next } // 将previous与current下一项连起来---跳过current previous.next = current.next current.next.prev = previous } else { // 索引在链表后半段 index = this.length - 1 current = this.tail while (index-- > position) { previous = current current = current.prev } // 将previous与current的上一项连起来--跳过current previous.prev = current.prev current.prev.next = previous } this.length-- return current.element } else { // 超出链表安全长度,链表有数据,则删除末尾元素 if (typeof position === 'number' && this.length > 0) { let current = this.tail this.tail = current.prev this.tail.next = null this.length-- return current.element } else { return null } } } /** * 从列表中移除一项 * 先找出元素的索引项,再根据索引移除元素 * @param {*} element 列表中的元素 */ remove(element) { let index = this.indexOf(element) return this.removeAt(index) } /** * 返回元素在列表中的索引。如果列表中没有该元素则返回-1 * @param {*} element 元素 */ indexOf(element) { let current = this.head let index = 0 // 计算位置数 while (current) { if (element === current.element) { return index } index++ current = current.next } return -1 } /** * 判断是否为空链表 * 空链表返回true,非空(链表长度大于0)返回false */ isEmpty() { return this.size() === 0 } /** * 返回链表包含的元素个数。与数组的length属性类似 */ size() { return this.length } /** * 获取链表的表头节点 */ getHead() { return this.head } /** * 获取链表的尾节点 */ getTail() { return this.tail } /** * 输出元素的值 */ toString() { let current = this.head let string = 'null' while (current) { string += "<--->" + current.element + (current.next ? '' : '<--->null') current = current.next } return string } }

以上方法,经过JEST框架测试通过,欢迎查看源码

思考

双向链表与单项链表的比较:

  1. 双向链表可以双向遍历。从头到尾,或者从尾到头

  2. 双向链表可以访问一个特定节点的下一个或者前一个元素,而单链表只能访问下一个元素。

  3. 双向链表内存占用比单链表的多

链表还有一个双向循环链表,在需要用到的时候,考虑它们各自的不同,选择合适的链表来操作。

js数据结构与算法--双向链表的实现的更多相关文章

  1. JS数据结构与算法--双向链表

    双向链表中链接是双向的:一个链向下一个元素,另一个链向上一个元素,如下图所示: 双向链表结构代码如下: class Node { constructor(element) { this.element ...

  2. JS数据结构与算法——栈

    JS数据结构与算法--栈 1.栈结构概念 栈(Stack)是一种先进后出(LIFO Last in First out)的线性表,先进栈的将会比后进栈的先出栈. 栈的限制是仅允许在一端进行插入和删除运 ...

  3. JS数据结构与算法-概述

    JS数据结构与算法概述 数据结构: 计算机存储, 组织数据的方式, 就像锅碗瓢盆 算法: 一系列解决问题的清晰指令, 就像食谱 两者关系: 程序 = 数据结构 + 算法 邂逅数据结构与算法 什么是数据 ...

  4. JS数据结构及算法(二) 队列

    队列是遵循先进先出的一种数据结构,在尾部添加新元素,并从顶部移除元素. 1.普通队列 function Queue() { this.items = []; } Queue.prototype = { ...

  5. JS数据结构及算法(一) 堆栈

    最近在看<学习JavaScript数据结构与算法>这本书,感觉自己又涨知识了 哈哈... 现在将自己看的做个总结,也是巩固理解. 栈:先进后出,新添加和待删除的元素都保存在栈顶.可以用数组 ...

  6. js数据结构与算法--单链表的实现与应用思考

    链表是动态的数据结构,它的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成. 现实中,有一些链表的例子. 第一个就是寻宝的游戏.你有一条线索,这条线索是指向寻找下一条线 ...

  7. JS数据结构与算法 - 剑指offer二叉树算法题汇总

    ❗❗ 必看经验 在博主刷题期间,基本上是碰到一道二叉树就不会碰到一道就不会,有时候一个下午都在搞一道题,看别人解题思路就算能看懂,自己写就呵呵了.一气之下不刷了,改而先去把二叉树的基础算法给搞搞懂,然 ...

  8. js数据结构与算法存储结构

    数据结构(程序设计=数据结构+算法) 数据结构就是关系,没错,就是数据元素相互之间存在的一种或多种特定关系的集合. 传统上,我们把数据结构分为逻辑结构和物理结构. 逻辑结构:是指数据对象中数据元素之间 ...

  9. JS数据结构与算法-队列结构

    队列结构 一.认识队列 受限的线性结构: 我们已经学习了一种受限的线性结构:栈结构. 并且已经知道这种受限的数据结构对于解决某些特定问题,会有特别的 效果. 下面,我们再来学习另外一个受限的数据结构: ...

随机推荐

  1. django rest framework renderer

    渲染器 REST framework 包含许多内置的渲染器类,允许您使用各种 media type 返回响应.同时也支持自定义渲染器. 视图的渲染器集合始终被定义为类列表.当调用视图时,REST fr ...

  2. kubernetes 1.14安装部署metrics-server插件

    简单介绍: 如果使用kubernetes的自动扩容功能的话,那首先得有一个插件,然后该插件将收集到的信息(cpu.memory..)与自动扩容的设置的值进行比对,自动调整pod数量.关于该插件,在ku ...

  3. php中的md5()的安全问题

    汇总下php中md5()的安全问题 安全问题1: 1.x=任意字符串  md5('x')=0e*** 2.y=任意字符串  md5('y')=0e*** 如果x==y,php会返回true,在有些时候 ...

  4. Python学习day17 迭代器&生成器

    迭代器&生成器 1. 迭代器 1.1 迭代器 迭代:迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果.每一次对过程的重复称为一次"迭代" 迭代器:帮助对某种对象 ...

  5. TensorFlow 辨异 —— tf.placeholder 与 tf.Variable

    https://blog.csdn.net/lanchunhui/article/details/61712830 https://www.cnblogs.com/silence-tommy/p/70 ...

  6. GWAS基因芯片数据预处理:质量控制(quality control)

    一.数据为什么要做质量控制 比起表观学研究,GWAS研究很少有引起偏差的来源,一般来说,一个人的基因型终其一生几乎不会改变的,因此很少存在同时影响表型又影响基因型的变异.但即便这样,我们在做GWAS时 ...

  7. java的数组

    作用:存储相同类型的一组数组,相当于一个容器,存放数据的.对同种数据类型集中存储.管理.便于遍历 数组类型:就是数组中存储的数据的类型 特点:数组中的所有元素必须属于相同的数据类型,数组中所有元素在内 ...

  8. django 4.get接口开发

    根据上一篇文章,有post,那么就有get请求,其余部分不变,就是把post换成get就可以. #views.py from django.http.response import HttpRespo ...

  9. Matrix-tree 定理的一些整理

    \(Matrix-tree\) 定理用来解决一类生成树计数问题,以下前置知识内容均是先基于无向无权图来介绍的.有关代数余子式的部分不是很明白,如果有错误还请指出-- 部分内容参考至:\(Blog\_1 ...

  10. 理解依赖注入,laravel IoC容器

    在看laravel文档的时候,有一个服务容器(IoC)的概念.它是这样介绍的:Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具.依赖注入听上去很花哨,其实质是通过构造函数或者某些情 ...