【数据结构与算法】--JavaScript 链表(二)

简介: 【数据结构与算法】--JavaScript 链表(二)

三、双向链表


双向链表的特点就是添加了指向上一个节点的指针(prev),比较单链表来说,稍微复杂一些,也更强大,这里把上面的单链表修改一下。


function initList() {
    class Node {
        constructor(item) {
            this.element = item
            this.next = null
            this.prev = null
        }
    }
    class List {
        constructor() {
            this.head = null
            this.size = 0
            this.last = null
        }
        /**
        * 链表查找元素
        * @param index 查找的位置
        */
        find(index) {
            let current = this.head
            for (let i = 0; i < index; i++) {
                current = current.next
            }
            return current
        }
        /**
        * 链表尾部插入元素
        * @param element 插入的元素
        */
        push(element) {
            let newNode = new Node(element)
            if (this.size === 0) {
                this.head = newNode
                this.head.next = null
                this.last = this.head
            } else {
                this.last.next = newNode
                newNode.next = null
                newNode.prev = this.last
                this.last = newNode
            }
            this.size += 1
        }
        /**
        * 链表尾部删除元素
        * @param element 删除的位置
        */
        pop() {
            if (this.size === 0)
                return
            if (this.size === 1) {
                this.head = null
                this.last = null
            } else {
                this.last.prev.next = null
                this.last = this.last.prev
            }
            this.size -= 1
        }
        /**
        * 链表头部删除元素
        */
        shift() {
            if (this.size === 0)
                return
            if (this.size === 1) {
                this.head = null
                this.last = null
            } else {
                this.head = this.head.next
                this.head.prev = null
            }
            this.size -= 1
        }
        /**
        * 链表头部插入元素
        * @param element 插入的元素
        */
        unshift(element) {
            let newNode = new Node(element)
            if (this.size === 0) {
                this.head = newNode
                this.head.next = null
                this.last = this.head
            } else {
                this.head.prev = newNode
                newNode.next = this.head
                this.head = newNode
            }
            this.size += 1
        }
        /**
        * 链表插入元素
        * @param element 插入的位置, index 插入的位置
        */
        insert(element, index) {
            if (index < 0 || index > this.size) {
                console.error('超出链表节点范围')
                return
            }
            let newNode = new Node(element)
            if (this.size === 0) {
                // 空链表
                this.head = newNode
                this.head.next = null
                this.last = this.head
            } else if (index === 0) {
                // 插入头部
                this.head.pre = newNode
                newNode.next = this.head
                this.head = newNode
            } else if (index == this.size - 1) {
                //插入尾部
                newNode.next = null
                newNode.prev = this.last
                this.last.next = newNode
                this.last = newNode
            } else {
                // 中间插入
                let prevNode = this.find(index - 1)
                newNode.next = prevNode.next
                prevNode.next = newNode
                newNode.prev = prevNode
                newNode.next.prev = newNode
            }
            this.size += 1
        }
        /*
        *链表编辑元素
        * @param element 编辑的元素,index 元素位置
        */
        edit(element, index) {
            let current = this.find(index)
            current.element = element
        }
        /*
        *链表删除元素
        * @param index 删除元素位置
        */
        delete(index) {
            let current = this.find(index)
            if (index === 0) {
                // 删除头节点
                this.head = this.head.next
                this.prev = null
            } else if (index === ((this.size) - 1)) {
                // 删除尾节点
                let preNode = this.find(index - 1)
                preNode.next = null
            } else {
                // 删除中间节点
                let preNode = this.find(index - 1)
                let nextNode = preNode.next.next
                let removeNode = preNode.next
                preNode.next = nextNode
                nextNode.prev = preNode
            }
            this.size -= 1
        }
        /*
        *链表使首尾成环
        */
        cycle() {
            this.last.next = this.head
            this.head.prev = this.last
        }
    }
    return new List()
}
let list = new initList()


三、循环链表


循环链表可以是单链表也可以是双向链表,它的特点是最后一个节点的 next 指针指向的是 head 节点 而不是 null,上面代码已经提供了 cycle 方法来实现。


四、判断链表有环


主要有这些方法:

  1. 遍历链表,使每一个节点与之前节点比较,若有重复则为有环链表
  2. 定义一个 Map 对象,遍历链表到每一个节点,若 Map 中没有次节点 ID,则将节点 ID 为 key, 存入 Map ,每个节点判断一次,如果某个节点的 ID存在,证明链表成环
  3. 双指针法,举个例子来说,两个人在操场跑步,速度不同时,总会在某些时刻相遇,就是因为跑到是圆的(环),利用这个原理,定义一个循环和两个指向头节点的指针,一个每次移动一个节点,一个移动两个节点,如果是成环的链表,某个时刻必然会遇到同一个节点。

五、链表在前端开发中的应用


  1. 链表的特性表明其擅长修改,不擅查找,所以对于需要大量修改的操作,可以考虑用链表实现,但是前端往往处理的数据量不会大,所以这种场景的实际意义不是很大,个人感觉在框架的底层优化上,使用较多,业务开发中,数组够用。
  2. 链表因为是随机存储的,所以比较省内存,但是对动态语言 JavaScript 解释器来说,有自动的垃圾回收机制来管理内存,所以链表的这个优势就不明显了。
  3. 链表特殊的结构,感觉适合做轮播图(双向循环链表)、双向导航列表等
目录
相关文章
|
2天前
|
存储
数据结构第二课 -----线性表之单向链表
数据结构第二课 -----线性表之单向链表
|
22小时前
|
前端开发 JavaScript 算法
JavaScript 中实现常见数据结构:栈、队列与树
JavaScript 中实现常见数据结构:栈、队列与树
|
1天前
|
存储
数据结构链表详解(不仅顺序表可以,我链表也可以)
数据结构链表详解(不仅顺序表可以,我链表也可以)
10 0
|
2天前
|
存储 算法 Java
数据结构与算法 数组和链表
数据结构与算法 数组和链表
11 0
|
2天前
|
存储 Java
深入浅出数据结构之链表
深入浅出数据结构之链表
|
2天前
|
C++
数据结构(双链表
数据结构(双链表
10 1
|
2天前
|
存储 缓存
[数据结构]~双向+循环链表从(0~1)
[数据结构]~双向+循环链表从(0~1)
|
2天前
|
存储
数据结构第三课 -----线性表之双向链表
数据结构第三课 -----线性表之双向链表
|
2天前
|
存储 Java
数据结构奇妙旅程之顺序表和链表
数据结构奇妙旅程之顺序表和链表
|
2天前
|
存储 C语言
数据结构基础:双链表结构、实现
数据结构基础:双链表结构、实现