JavaScript实现双向链表结构

双向链表的结点

        与单链表不同的是多了prev指针,如图所示:

封装双向链表的结点

JavaScipt没有直接提供该数据结构,需要自己封装

class DoublyNode{
    constructor(data){
        this.prev = null
        this.data = data
        this.next = null
    }
}
export default DoublyNode

什么是双向链表

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

如图所示:

双向链表的特点 

        结点除了存储数据外,还有两个指针分别指向前一个结点地址(前驱指针prev)和下一个结点地址(后继指针next)。

封装双向链表 

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

import DoublyNode from './DoublyNode.mjs'
class DoublyLinkedList {
    //记录链表长度
    #length = 0
    //链表头指针-
    #head = null
    //链表尾指针
    #tail = null
    //在链尾添加数据结点
    push(data) {
        //创建好结点
        const node = new DoublyNode(data)
        //链表为空时
        if (this.#head === null) {
            //头尾指针都指向新插入的结点
            this.#head = node
            this.#tail = node
        } else {
            //链表此时不为空,直接在链尾添加数据
            //将node连接在tail上
            this.#tail.next = node
            //node上的prev需要连接到之前的链尾结点
            node.prev = this.#tail
            //把node作为新的链尾
            this.#tail = node
        }
        //结点增加,链表长度增加
        this.#length++
    }
    //指定位置插入数据
    insert(data, index) {
        //判断index是否存在
        if (index >= 0 && index <= this.#length) {
            //创建node结点
            const node = new DoublyNode(data)
            //用于存放node,暂时指向head(中途插入需要用到)
            let current = this.#head
            //在链头插入
            if (index === 0) {
                //链表内没有任何结点
                if (this.#head === null) {
                    this.#head = node
                    this.#tail = node
                } else {
                    //链表中存在结点
                    //将node从链头插入
                    node.next = this.#head
                    //原来链头结点的prev要指向node
                    this.#head.prev = node
                    //将node作为新链头
                    this.#head = node
                }
            } else if (/*在链尾插入结点*/index === this.#length) {
                //将node连接在链尾
                this.#tail.next = node
                //将node的prev连接到之前的链尾结点
                node.prev = this.#tail
                //把node作为新链尾
                this.#tail = node
            } else {
                //在链表中插入
                //先找到需要插入结点的上一结点
                const previous = this.getNodeAt(index - 1)
                //再找到需要插入结点的下一结点
                current = previous.next
                //插入node结点
                //将node上的next指向current
                node.next = current
                //current的prev指向node
                current.prev = node
                //previous的next指向node
                previous.next = node
                //node的prev指向previous
                node.prev = previous
            }
            //链表增长,长度增加
            this.#length++
            return true
        }
        return false
    }
    //找到指定位置的结点
    getNodeAt(index) {
        if (index >= 0 && index < this.#length) {
            let node = this.#head
            for (let i = 0; i < index; i++) {
                node = node.next
            }
            return node
        }
        return
    }
    //删除指定位置的结点
    removeAt(index) {
        //先判断index是否存在
        if (index >= 0 && index < this.#length) {
            //保存需要删除的结点,暂时指向head
            let current = this.#head
            if (index === 0/*删除链头的结点*/) {
                //将原有链头的下一结点作为新链头(current保护了需要删除的结点)
                this.#head = current.next
                if (this.size() === 0) {
                    return
                }else if (this.#length === 1/*链表只有一个结点*/) {
                    //将tail指向null
                    this.#tail = null
                } else/*此时的链表有多个结点*/ {
                    //由于head已经后移,此时只需要将新链头的prev赋空即可
                    this.#head.prev = null
                }
            } else if (index === this.#length - 1/*删除链尾的结点*/) {
                //用临时变量保护即将被删除的结点
                current = this.#tail
                //将倒数第二个结点作为新的链尾
                this.#tail = current.prev
                //倒数第二个结点的next赋值为空
                this.#tail.next = null
            } else/*删除链表中的结点*/ {
                //找到需要删除结点的上一结点
                const previous = this.getNodeAt(index - 1)
                //保护将被删除的结点
                current = previous.next
                //删除结点(跳过)
                previous.next = current.next
                //被删除结点的下一结点的prev需要跳过一个结点
                current.next.prev = previous
            }
            this.#length--
            return current.data
        }
        return
    }
    //查找链头结点
    getHead() {
        return this.#head
    }
    //查找链尾结点
    getTail() {
        return this.#tail
    }
    //清空链表
    clear() {
        this.#length = 0
        this.#tail = null
        this.#head = null
    }
    //删除指定的数据
    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 (JSON.stringify(data) === JSON.stringify(current.data)) {
                return i
            }
            current = current.next
        }
        return -1
    }
    //转换为字符串
    toString() {
        let string = ''
        let current = this.#head
        while (current) {
            string += current.data + (current.next ? ',' : '')
            current = current.next
        }
        return string
    }
    //返回链表长度
    size() {
        return this.#length
    }
    //判断链表是否为空
    isEmpty() {
        return this.size() === 0
    }
}
export default DoublyLinkedList

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

根号三;

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

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

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

打赏作者

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

抵扣说明:

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

余额充值