JavaScript实现单链表结构

什么是结点

        结点是数据结构中的基本元素,用于存储和表示数据。结点通常包含一个数据项和一个或多个指向其他结点的指针。如图所示:

封装结点 

        JavaScript是没有直接提供这样的数据结构,但是可以通过class封装出这样的数据结构

class Node{
    constructor(data){
        this.data = data
        this.next = null
    }
}
// const node = new Node('4')
// console.log(node);
export default Node

什么是链表 

        链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 简单来说就是把每个结点连接起来(类比与一节一节的火车,确定好火车头,车厢是可以删除或添加的)

链表的特点

        添加,删除数据的效率相比数组要高,查询相比于数组,效率不高只能从链表头或者链表尾遍历查找。

 封装链表

        包含从链尾添加数据,删除指定位置的数据,删除指定数据,查找指定数据的位置,转换链表为字符串,清空,指定位置插入指定数据,查找链头数据,查询链表长度,判断链表是否为空等方法。

 封装如下:(使用模块化语法,注意使用.mjs后缀名)

import Node from "./Node.mjs";
class LinkedList {
    //#表示私有属性
    //记录链表的长度
    #length = 0
    //作为链表头,指向null表示链表为空
    #head = null
    //从链尾添加数据
    addBack(data) {
        //先创建好结点,找到链尾就插入
        const node = new Node(data)
        //链表为空时,头即是尾
        if (this.#head === null) {
            //直接把新结点作为链头
            this.#head = node
        } else {
            //保证链头不丢失,借助临时变量(指向链头)遍历链表
            let temp = this.#head
            //此时的链表不为空,需要遍历到链尾再插入结点
            while (temp.next !== null/*链尾是null*/) {
                //临时变量遍历链表
                temp = temp.next
            }
            //遍历结束找到链尾,插入结点
            temp.next = node
        }
        //插入数据,链表长度增加
        this.#length++
    }
    //找到指定位置的结点
    getNodeAt(index) {
        //先判断该位置是否存在
        if (index >= 0 && index < this.#length) {
            //利用临时变量从链头遍历链表
            let node = this.#head
            //遍历到指定位置的结点
            for (let i = 0; i < index; i++) {
                //变量遍历链表
                node = node.next
            }
            //遍历时,node就是需要寻找的结点
            return node
        }
        return
    }
    //删除指定位置的数据
    removeAt(index) {
        //先判断链表中是否有这个位置
        if (index >= 0 && index < this.#length) {
            //用临时变量存放需要删除的结点,暂时不知道在哪,先存放链头(防止破坏原有的链头)
            let current = this.#head
            //在链头删除数据
            if (index === 0) {
                //将原来链表的第二个结点作为链头
                this.#head = this.#head.next
            } else {
                //此时需要删除的结点在链表中
                //先找到需要删除结点的上一结点
                let previous = this.getNodeAt(index - 1)
                //找到需要删除的结点,使用current存放
                current = previous.next
                //上一结点直接跳过需要删除的结点,直接链上下一结点(删除)
                previous.next = current.next
            }
            //链表删除结点,长度减短
            this.#length--
            //返回删除结点的数据
            return current.data
        }
        return
    }
    //删除指定数据
    remove(data) {
        //先找到该数据的结点所在处
        const index = this.indexOf(data)
        //删除指定位置的数据
        return this.removeAt(index)
    }
    //找到指定数据的结点索引
    indexOf(data) {
        //用临时变量从链头遍历到链尾
        let current = this.#head
        for (let i = 0; i < this.#length; i++) {
            //判断指定数据与结点处的数据是否相同
            if (this.equal(data, current.data)) {
                //返回索引值
                return i
            }
            //变量遍历链表
            current = current.next
        }
        //遍历完说明不存在
        return -1
    }
    //判断数据是否相同
    equal(data1, data2) {
        //转换成字符串进行判断,此方法存在{a:1,b:2}与{b:2,a:1}会被判断为false的问题
        return JSON.stringify(data1) === JSON.stringify(data2)
    }
    //添加指定数据到指定索引
    insert(data, index) {
        //判断index是否有效
        if (index >= 0 && index <= this.#length/*链表长度为0时允许继续添加数据*/) {
            //先创建好结点,找到合适位置就插入
            const node = new Node(data)
            //在链表头添加数据
            if(index === 0){
                if(this.#head === null/*如果链表为空*/){
                    this.#head = node
                }else/*链表不为空*/{
                    //借助临时变量改变链头的结点
                    let temp = this.#head
                    //将node插入到链头
                    node.next = temp
                    //将node作为新的链头
                    this.#head = node
                }
            }else{
                //此时在链表中插入结点
                //先找到需要插入结点的上一结点
                let previous = this.getNodeAt(index - 1)
                //用临时变量存放需要插入结点
                let temp = previous.next
                //在需要插入数据的结点与上一结点之间插入node
                node.next = temp
                previous.next = node
            }
            //结点增加,链表增长
            this.#length++
            return true
        }
        return false
    }
    //返回链表的长度
    size(){
        return this.#length
    }
    //判断链表是否为空
    isEmpty(){
        return this.size() === 0
    }
    //清空链表
    clear(){
        this.#head = null
        this.#length = 0
    }
    //返回链头
    getHead(){
        return this.#head
    }
    //转换为字符串
    toString(){
        //定义字符串用于拼接
        let string = ''
        //用临时变量遍历链表
        let temp = this.#head
        while(temp){
            //拼接字符串
            string += temp.data + ((temp.next ? ' -> ' : ' -> null'));
            temp = temp.next
        }
    }
}
export default LinkedList

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

根号三;

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值