XingYun blog
  • JS基础

    • 图解js原型链
    • JS Event Loop
    • 对象的底层数据结构
    • 让你的JavaScript代码简单又高效
    • 函数参数按值传递
    • 判断数据类型
    • 浮点数精度问题和解决办法
    • 常用方法snippet
    • 实现Promise
    • 防抖和节流
    • 巧用sort排序
  • CSS && HTML

    • CSS也需要性能优化
    • class命名规范
    • em、px、rem、vh、vw 区别
    • CSS揭秘阅读笔记
  • 浏览器

    • 浏览器是如何渲染页面的
    • 重排和重绘
    • BOM浏览器对象模型
    • DOM事件
    • 浏览器存储
  • 数据结构

    • JS实现链表
    • JS实现栈与栈应用
    • JS实现常见排序
    • 哈夫曼编码
    • MD5算法
  • vue原理浅析

    • Vue虚拟dom与Diff算法
    • 前端打包文件的缓存机制
    • vue数组为什么不是响应式
    • v-for为什么不能用index做key
  • 前端工程化

    • 浏览器是如何渲染页面的
    • 前端打包需要gzip压缩吗
    • 前端打包文件的缓存机制
    • webpack loader和plugin
  • 轮子&&组件库

    • 实现水波浪进度球
  • 文字转语音mp3文件
  • 文件上传前后端实现
  • moment.js给定时间获取自然月、周的时间轴
  • 实现文件上传功能
  • 批量下载照片
  • leaflet改变坐标原点
  • 网络

    • 有了MAC地址 为什么还需要IP地址
    • 为什么IP地址老是变
    • 我们为什么需要IPV6
    • TCP与UDP
  • 计算机组成原理

    • ASCII、Unicode、UTF-8和UTF-16
  • VSCode

    • VSCode图片预览插件 Image preview
    • rsync:linux间的高效传输工具

XingYun

冲!
  • JS基础

    • 图解js原型链
    • JS Event Loop
    • 对象的底层数据结构
    • 让你的JavaScript代码简单又高效
    • 函数参数按值传递
    • 判断数据类型
    • 浮点数精度问题和解决办法
    • 常用方法snippet
    • 实现Promise
    • 防抖和节流
    • 巧用sort排序
  • CSS && HTML

    • CSS也需要性能优化
    • class命名规范
    • em、px、rem、vh、vw 区别
    • CSS揭秘阅读笔记
  • 浏览器

    • 浏览器是如何渲染页面的
    • 重排和重绘
    • BOM浏览器对象模型
    • DOM事件
    • 浏览器存储
  • 数据结构

    • JS实现链表
    • JS实现栈与栈应用
    • JS实现常见排序
    • 哈夫曼编码
    • MD5算法
  • vue原理浅析

    • Vue虚拟dom与Diff算法
    • 前端打包文件的缓存机制
    • vue数组为什么不是响应式
    • v-for为什么不能用index做key
  • 前端工程化

    • 浏览器是如何渲染页面的
    • 前端打包需要gzip压缩吗
    • 前端打包文件的缓存机制
    • webpack loader和plugin
  • 轮子&&组件库

    • 实现水波浪进度球
  • 文字转语音mp3文件
  • 文件上传前后端实现
  • moment.js给定时间获取自然月、周的时间轴
  • 实现文件上传功能
  • 批量下载照片
  • leaflet改变坐标原点
  • 网络

    • 有了MAC地址 为什么还需要IP地址
    • 为什么IP地址老是变
    • 我们为什么需要IPV6
    • TCP与UDP
  • 计算机组成原理

    • ASCII、Unicode、UTF-8和UTF-16
  • VSCode

    • VSCode图片预览插件 Image preview
    • rsync:linux间的高效传输工具
  • 算法图解阅读笔记
  • MD5算法
  • JS实现常见排序
  • 邓俊辉算法与数据结构
  • JS实现队列
  • JS实现树的几种遍历方式
  • 二叉树相关
  • 哈希算法
  • 进制转换与js实现
  • JS实现链表
    • JS实现栈与栈应用
    • 算法与数据结构
    XingYun
    2022-01-19
    目录

    JS实现链表

    链表数据结构特性

    优点

    • 插入与删除效率高(O(1))

    缺点

    • 查询或随机读取某个元素时效率低,比如在需要读取链表的最后一个元素时,你不能直接读取,因为你不知道 它所处的地址,只能从第一个开始一个个查询 (0(n))

    链表的实现

    # 一. 单链表的基础操作

    对单链表的操作有:

    find(item) // 在单链表中寻找item元素
    insert(element, item) // 向单链表中插入元素
    remove(item) // 在单链表中删除一个节点
    append(element) // 在单链表的尾部添加元素
    findLast() // 获取单链表的最后一个节点
    display() // 单链表的遍历显示
    isEmpty() // 判断单链表是否为空
    show() // 显示当前节点
    getLength() // 获取单链表的长度
    advance(n, currNode) // 从当前节点向前移动n个位置
    clear() // 清空单链表
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    只要实现上述的这些方法,一个基本的单链表结构就实现了。

    # 二. 节点

    链表中的节点类型描述如下:

    class Node {
      constructor(data) {
        this.data = data // 节点的数据域
        this.prev = null // 节点的指针域
        this.next = null // 节点的指针域
      }
    }
    
    1
    2
    3
    4
    5
    6
    7

    当然,JS 中并没有指针,节点的指针只是借用的 C 语言中的概念。
    之所以用 prev 和 next 两个指针域是为了实现双向链表,在实现单链表时,prev 指针并没有用到。

    # 三. 单链表类

    将单链表的基础操作方法放在单链表的类中,就形成了单链表数据结构的大概框架了。

    class SingleList {
      constructor() {
        this.size = 0 // 单链表的长度
        this.head = new Node('head') // 表头节点
        this.currNode = '' // 当前节点的指向
      }
    
      find(item) {} // 在单链表中寻找item元素
      insert(item, element) {} // 向单链表中插入元素
      remove(item) {} // 在单链表中删除一个节点
      append(element) {} // 在单链表的尾部添加元素
      findLast() {} // 获取单链表的最后一个节点
      isEmpty() {} // 判断单链表是否为空
      show() {} // 显示当前节点
      getLength() {} // 获取单链表的长度
      advance(n, currNode) {} // 从当前节点向前移动n个位置
      display() {} // 单链表的遍历显示
      clear() {} // 清空单链表
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    剩下的问题就拆分为了实现一个个操作方法了

    # 四. 实现主要方法

    我们就从最基础的 添加节点->查找节点->删除节点->修改节点

    1.找出链表中最后一个节点

    findLast() {
        let last = this.head
        while (last.next) {
          last = last.next
        }
        return last
      }
    
    1
    2
    3
    4
    5
    6
    7

    为啥要先实现这个方法?

    因为你得找到最后一个节点才方便 append 节点

    2.在链表的尾部添加节点

     append(item) {
        let last = this.findLast()
        const node = new Node(item)
        last.next = node
        node.prev = last
      }
    
    1
    2
    3
    4
    5
    6

    3.查找节点

    find(item) {
        const _find = (item, node) => {
          if (!node) return null
          if (node.data === item) {
            return node
          } else {
            return _find(item, node.next) // 不是当前节点 递归查找 直到找到节点或者查找到链表最后一个节点返回null
          }
        }
        return _find(item, this.head)
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    4.在单链表中删除一个节点

    remove(item) {
        if (item === 'head') return console.log('不能删除根元素')
        let node = this.find(item)
        if (node) {
          if (node.next) {
            // 要删除的节点有子节点 将子节点连接到当前节点
            let next = node.next
            next.prev = node.prev
            node.prev.next = next
          } else {
            // 要删除的节点为最后一个节点
            node.prev.next = null
          }
        } else {
          console.log('没有这个节点')
        }
      }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    5.单链表的遍历显示

    
     display() {
        let res = ''
        let current = this.head
        if (current) res += current.data + ' -> '
        while (current.next) {
          current = current.next
          res += current.data + ' -> '
        }
        return res
      }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    。。。 剩下的方法待实现 。。。

    # 五、单链表完整代码

    class Node {
      constructor(data) {
        this.data = data // 节点的数据域
        this.prev = null // 节点的指针域
        this.next = null // 节点的指针域
      }
    }
    
    class SingleList {
      constructor() {
        this.size = 0 // 单链表的长度
        this.head = new Node('head') // 表头节点
        this.currNode = '' // 当前节点的指向
      }
    
      insert(item, element) {} // 向单链表中插入元素
      isEmpty() {} // 判断单链表是否为空
      show() {} // 显示当前节点
      getLength() {} // 获取单链表的长度
      advance(n, currNode) {} // 从当前节点向前移动n个位置
      clear() {} // 清空单链表
    
      // 在单链表的尾部添加元素
      append(item) {
        let last = this.findLast()
        const node = new Node(item)
        last.next = node
        node.prev = last
      }
    
      // 获取单链表的最后一个节点
      findLast() {
        let last = this.head
        while (last.next) {
          last = last.next
        }
        return last
      }
    
      // 在单链表中寻找item元素
      find(item) {
        const _find = (item, node) => {
          if (!node) return null
          if (node.data === item) {
            return node
          } else {
            return _find(item, node.next)
          }
        }
        return _find(item, this.head)
      }
    
      // 单链表的遍历显示
      display() {
        let res = ''
        let current = this.head
        if (current) res += current.data + ' -> '
        while (current.next) {
          current = current.next
          res += current.data + ' -> '
        }
        return res
      }
    
      // 在单链表中删除一个节点
      remove(item) {
        if (item === 'head') return console.log('不能删除根元素')
        let node = this.find(item)
        if (node) {
          if (node.next) {
            // 要删除的节点有子节点 将子节点连接到当前节点
            let next = node.next
            next.prev = node.prev
            node.prev.next = next
          } else {
            // 要删除的节点为最后一个节点
            node.prev.next = null
          }
        } else {
          console.log('没有这个节点')
        }
      }
    }
    
    let singleList = new SingleList()
    singleList.append('item1')
    singleList.append('item2')
    singleList.append('item3')
    
    // console.log(singleList.find('item2'))
    // console.log(singleList.display())
    
    singleList.remove('item2')
    console.log(singleList.display())
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94

    # 双向链表

    两个节点链接允许在任一方向上遍历列表。

    #数据结构
    上次更新: 2023/04/05, 09:41:10
    进制转换与js实现
    JS实现栈与栈应用

    ← 进制转换与js实现 JS实现栈与栈应用→

    最近更新
    01
    JavaScript-test
    07-20
    02
    二维码的原理
    07-20
    03
    利用ChatGPT优化代码
    07-20
    更多文章>
    Theme by Vdoing | Copyright © 2021-2023 XingYun | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式