链表是动态的数据结构,它的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。

现实中,有一些链表的例子。

第一个就是寻宝的游戏。你有一条线索,这条线索是指向寻找下一条线索的地点的指针。你顺着这条链接去下一个地点,得到另一条指向下一处的线索。得到列表中间的线索的唯一办法,就是从起点(第一条线索)顺着列表寻找。

第二个例子是火车。一列火车是由一些车厢组成的。每节车厢都是相互连接。你很容易分离一节车皮,改变它的位置,添加或移除它。每节车厢都是列表的元素,车厢间的连接就是指针。

链表的实现

链表有多种不同的类型,单链表,双向链表,循环链表等。

我们先实现单链表。单链表是一种链式存取的数据结构。

linkednode.js文件,里面包含了链表中节点的项


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

linkedlist.js文件,链表各种方法的实现


import { Node } from './linkednode' /** * 链表类 */ export default class LinkedList { constructor() { this.length = 0 // 存储链表中列表项的数量 this.head = null // 存储链表中第一个节点的引用 } /** * 向列表尾部添加一个新的项 * @param {*} element 需要添加至链表中的节点项 */ append(element) { let node = new Node(element) // 将新项创建为符合链表结构的列表项 if (this.head === null) { // 链表中的元素为空 this.head = node } else { let current = this.head // 将第一个节点的引用赋值给当前项 // 循环列表,直到找到最后一项 while (current.next) { current = current.next } // 找到最后一项,将其next赋为node,建立链接 current.next = node } this.length++ // 更新链表的长度 } /** * 向列表的特定位置插入一个新的项 * @param {*} position 插入链表中的位置 * @param {*} element 需要添加的节点 */ insert(position, element) { let node = new Node(element) // 检查越界 if (position > -1 && position <= this.length) { let current = this.head let previous let index = 0 // 在第一个位子添加 if (position === 0) { node.next = current this.head = node } else { // 需要找到特定的位子,将想要插入的元素放在previous节点和current节点之间 while (index++ < position) { // previous 将是对想要插入新元素的位置之前一个元素的引用 previous = current // current 对想要插入新元素的位置之后一个元素的引用 current = current.next } node.next = current previous.next = node } this.length++ return true } else { return false } } /** * 移除指定位置的节点元素,并返回移除的项 * 如果超出了链表的范围,则返回null * @param {Int32Array} position 链表中的位置 */ removeAt(position) { // 检查越界 if (position > -1 && position < this.length) { let current = this.head let previous // 先前一个节点 let index = 0 // 当前节点的位子 // 移除第一项 if (position === 0) { this.head = current.next } else { while (index++ < position) { previous = current current = current.next } // 将previous 与 current 的下一项链接起来:跳过current,从而移除它 // 当前元素会丢弃在计算机内存中,等待GC清除 previous.next = current.next } this.length-- // 链表元素数量减1 return current.element // 返回移除的项 } else { return null // 如果超出了链表的范围,则返回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 } /** * 获取链表的表头节点 * head变量是LinkedList类的私有变量。 * 但是,我们需要实现在类的外部循环访问列表。 * 此时,就需要提供获取类的第一个元素的方法 */ getHead() { return this.head } /** * 由于列表项使用了Node类,需要重写toString方法,让其只输出元素的值。 */ toString() { let current = this.head let string = '' while (current) { string += current.element + (current.next ? '-->' : '') current = current.next } return string } }

查看源代码,使用JEST,进行单元测试

链表的应用思考

我们先比较下JavaScript中链表与数组的区别:

  1. 链表中的元素是不连续的,而数组中的元素是连续的。

  2. 链表添加或移除元素的时候不需要移动其他元素,而数组需要。

  3. 链表需要指针,而数组不需要。

  4. 链表需要从表头开始迭代列表直到找到所需要的元素。数组则可以直接访问任何位置的任何元素。

  5. 两者都具有动态性。关于数组动态性。数组在js中是一个可以修改的对象,添加移除元素,它会动态的变化,但是成本很高,需要移动元素。在大多数语言中(比如C和Java),数组的大小是固定的,想添加元素就要创建一个全新的数组,不能简单地往其中添加所需的元素。

要存储多个元素,数组可能是最常用的数据结构。但是,如果要存储数据,并且会有移除或者添加大量数据时候,链表比数组更实用。

更多实例,请查看源码

js数据结构与算法--单链表的实现与应用思考的更多相关文章

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

    链表结构:链表中每个元素由一个存储元素本身的节点和一个指向下一元素的引用组成.如下所示(手画的,比较丑,懒得用工具画了,嘻嘻) 1.append方法,向链表末尾插入一个节点 2.insert(posi ...

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

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

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

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

  4. 数据结构:DHUOJ 单链表ADT模板应用算法设计:长整数加法运算(使用单链表存储计算结果)

    单链表ADT模板应用算法设计:长整数加法运算(使用单链表存储计算结果) 时间限制: 1S类别: DS:线性表->线性表应用 题目描述: 输入范例: -5345646757684654765867 ...

  5. 数据结构&算法-单链表

    1.引言 工作一年了,感觉越来越懒散,把很多基础性的东西都慢慢遗忘了,最近想趁着还没忘完,回顾一下,整理了点笔记,分享一下. 如有错的地方,欢迎大家怒喷. 2.学习 我们就从最简单的链表开始吧. 链表 ...

  6. Java数据结构和算法之链表

    三.链表 链结点 在链表中,每个数据项都被包含在‘点“中,一个点是某个类的对象,这个类可认叫做LINK.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.每个LINK对象中 ...

  7. Java数据结构和算法(四)--链表

    日常开发中,数组和集合使用的很多,而数组的无序插入和删除效率都是偏低的,这点在学习ArrayList源码的时候就知道了,因为需要把要 插入索引后面的所以元素全部后移一位. 而本文会详细讲解链表,可以解 ...

  8. 数据结构——Java实现单链表

    一.分析 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.链表中的数据是以结点来表示的,每个结点由元素和指针构成.在Java中,我们可以将单链表定义成一个类,单链表的基 ...

  9. js数据结构与算法--双向链表的实现

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

随机推荐

  1. 【dp】友好城市

    题目一: [题目描述] Palmia国有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市.北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相同. 每对友好城市都 ...

  2. [CQOI2016]伪光滑数

    题目描述 若一个大于1的整数M的质因数分解有k项,其最大的质因子为Ak,并且满足Ak^K<=N,Ak<128,我们就称整数M为N-伪 光滑数.现在给出N,求所有整数中,第K大的N-伪光滑数 ...

  3. 【学习笔记】python

    1.  len( s )  返回对象(字符.列表.元祖等)的长度或项目个数. >>>str = "runoob" >>> len(str) # ...

  4. CF271D 【Good Substrings】

    定义哈希函数 \(H(c)=\sum_{i = 1} ^ m c_i*b^{m-i}\) \(H(C,K+1)=H(C,K)*b+C_{K+1}\)(K个坏字母) 用long long #includ ...

  5. mysql 在visual studio中的配置

    视图-->其他窗口-->服务器资源管理器-->数据连接-->右键添加连接 servername:localhost username:root password:123456 ...

  6. python验证卡普耶卡(D.R.Kaprekar)6174猜想

    1955年,卡普耶卡(D.R.Kaprekar)对4位数字进行了研究,发现一个规律: 对任意各位数字不相同的4位数,使用各位数字能组成的最大数减去能组成的最小数,对得到的差重复这个操作,最终会得到61 ...

  7. Linux动态链接库的生成和使用

    目录 1. 编写C程序 2. 编译动态链接库 3. 使用共享库 4. 执行程序 5. 参考资料 1. 编写C程序 比如编写myfunc.c文件,里面包含两个函数,一个是say_hello,另一个是ca ...

  8. 常用数据库:MongoDB

    下载地址:https://www.mongodb.com/download-center/community 安装及配置指南:https://docs.mongodb.com/manual/insta ...

  9. Linux-ubuntu16.04安装 mysql5.7-PHP7.0+Swoole

    步骤1 – 安装MySQL 在 Ubuntu 16.04 中,默认情况下,只有最新版本的 MySQL 包含在 APT 软件包存储库中.只需更新服务器上的包索引并安装默认包 apt-get. sudo ...

  10. 二值化神经网络(BNN)基础学习(一)

    目录 1.简介 2.优点 3.基本原理 3.1 权重和激活值二值化[3] 3.2 乘法优化 3.3 权重和激活值更新 4.结论[3] 参考资料 1.简介 ​ 二值化神经网络,在浮点型(权重值和激活函数 ...