JavaScript数据结构(单向链表,双向链表)

链表以及数组的缺点

数组:

  1. 需要申请一段连续的内存空间

  2. 大小固定,当数组不满足容量需求时,需要扩容(js不需要)(申请一个更大的数组,将原数组的元素复制过去)

链表的优势:

  1. 可以存储多个元素

  2. 不同于数组。链表中的元素在内存里面不用是连续的空间,可以实现灵活的内存动态管理

  3. 链表的每个元素由一个存储元素本身的节点一个指向下一个元素的引用组成(指针)

  4. 链表不必在创建时就确定大小,并且大小可以无限地延申下去

  5. 链表在插入和删除数据时,时间复杂度可以达到O(1),相对数组效率高很多

链表的缺点:

  1. 当他要访问任何一个元素的时候,必须要从头开始访问(因为他是一个一个连接的)

  2. 不能通过下标直接访问元素,需要从头一个个访问,直到找到对应的元素

     

    链表结构的封装

链表常见的操作

属性 head(第一个节点) node节点(数据data+引用next)

  • append(ele) 在链表尾部添加一个新的项

  • insert(position,element) 向列表的特定位置插入一个新的项

  • removeAt(position)通过链表的位置信息删除一项

  • remove(ele)通过元素信息,从列表中移除一项

  • update(position) 修改某个位置的元素

  • get(position):获取对应位置的元素

  • indexOf(ele) 返回元素在列表中的索引,如果列表中没有元素返回-1

其他

//判空 
isEmpty()
//返回元素的个数
size()
//输出字符串
toString()

append()

// 1.添加方法
            LinkedList.prototype.append = function (data) {
                var newnode = new Node(data)
                // 判断是不是第一个节点
                if (this.length == 0) {
                    this.head = newnode
                } else {
                    // 找到最后一个节点 给他的next赋值上新节点
                    // current 用来作为一个游标的 从头往后滑
                    var current = this.head
                    // 如果当前游标指的节点的next不为空就 把下一个节点赋值给游标 直到找到空next 
                    while (current.next) {
                        current = current.next
                    }
                    //退出循环后说明游标已经指向最后一个节点
                    // 将新节点赋值给最后一个指针
                    current.next = newnode
                }
                // 链表节点加一
                this.length += 1;
            }

insert()

情况1:放在开头的地方

情况2:放在中间(两种方式)

  1. 声明要插入位置的pre节点和next节点

  2. 只用找到当前位置的前一个节点,previous

    • while(index++<position-1){ previous=previous.next}

    • 将该节点保存的下一个节点等于新节点的下一个节点,前一个节点的下一个节点保存为新节点

      previous.next=newNode.next
      previous.next=newNode

这里先写get(index) 先找到 prevNode 比较好 直接 newNode.next = prevNode.next; prevNode.next = newNode

情况3:放在后面

 // 封装链表类
        function LinkedList() {
            // 封装一个内部的节点类 放数据和指针
            function Node(data) {
                this.data = data
                //next指向未知
                this.next = null
            }
            //头部属性
            this.head = null
            // 记录当前的长度
            this.length = 0

            // 1.添加方法
            LinkedList.prototype.append = function (data) {
                var newnode = new Node(data)
                // 判断是不是第一个节点
                if (this.length == 0) {
                    this.head = newnode
                } else {
                    // 找到最后一个节点 给他的next赋值上新节点
                    // current 用来作为一个游标的 从头往后滑
                    var current = this.head
                    // 如果当前游标指的节点的next不为空就 把下一个节点赋值给游标 直到找到空next 
                    while (current.next) {
                        current = current.next
                    }
                    //退出循环后说明游标已经指向最后一个节点
                    // 将新节点赋值给最后一个指针
                    current.next = newnode
                }
                // 链表节点加一
                this.length += 1;
            }

            //toString
            LinkedList.prototype.toString = function () {
                var str = ''
                //定义一个游标,这个游标从头到尾指一遍
                var current = this.head
                // 当游标有节点就循环
                while (current) {
                    // 拼接每个节点的数据
                    str += current.data + ' '
                    // 当指完上一个节点之后,游标要往后一个节点移动
                    current = current.next
                }
                return str
            }

            //insert方法
            LinkedList.prototype.insert = function (position, data) {
                //需要判断插入的位置是不是越界 位置不可是负数和超过链表的长度
                if (position < 0 || position > this.length) return false
                //2.新建一个节点
                var newNode = new Node()
                newNode.data = data
                //根据插入的情况判断
                // 插入为节点的第一个
                // 插入为链表的中间
                //插入到最后
                if (position == 0) {
                    newNode.next = this.head
                    this.head = newNode
                } else {
                    var index = 0
                    var current = this.head
                    var previous = null
                    while (index < position) {
                        previous = current
                        current = current.next
                        index++
                    }
                    newNode.next = current
                    previous.next = newNode
                }
                //链表长度加一
                this.length += 1
                return true
            }
            //get()根据位置信息获取对应的元素
            LinkedList.prototype.get = function (position) {
                //1.越界判断
                if (position < 0 || position >= this.length) return null

                // 2.获取对应的data
                var current = this.head
                var index = 0
                while (index < position) {
                    current = current.next
                    index++
                }
                return current.data
            }
            // indexOf返回再列表中的索引,如果列表中没有该元素就返回-1
            LinkedList.prototype.indexOf = function (ele) {
                var index = 0
                var current = this.head
                //这里不能等于this.length 因为下标是从0开始的
                while (index < this.length) {

                    if (ele === current.data) {
                        return index
                        break
                    }
                    //往后走一步
                    current = current.next
                    index++
                }
                return -1
            }
            //update(position) 修改某个位置的元素
            LinkedList.prototype.update = function (position, ele) {
                if (position < 0 || position >= this.length) return false
                var index = 0;
                var current = this.head
                while (index++ < position) {
                    current = current.next
                }
                // 将position位置的元素修改成新的ele
                current.data = ele
                return true
            }
            // removeAt(position) 从特定列表的特定位置移除一项
            LinkedList.prototype.removeAt = function (position) {
                if (position < 0 || position >= this.length) return null
                var current = this.head
                //判断是否删除的是第一个节点
                if (position == 0) {
                    this.head = this.head.next
                } else {
                    var index = 0
                    var previous = null
                    while (index++ < position) {
                        previous = current
                        current = current.next;
                    }
                    previous.next = current.next

                }
                this.length -= 1
                return current.data
            }

            //remove(ele) 从特定列表移除一项
            LinkedList.prototype.remove = function (ele) {
                // var index = 0
                // var current = this.head
                // var previous = null
                // while (current) {
                //     if (ele === current.data) {
                //         if (index == 0) {
                //             this.head = this.head.next
                //             break
                //         }
                //         previous.next = current.next
                //         break
                //     }
                //     previous = current
                //     current = current.next
                //     index++
                // }
                // this.length -= 1
                // return true
                var position = this.indexOf(ele)
                return this.removeAt(position)
            }

            //isEmpty
            LinkedList.prototype.isEmpty = function () {
                return this.length == 0
            }
            //size()
            LinkedList.prototype.size = function () {
                return this.length
            }

        }

双向链表

  • 单向链表,只能从头到尾或者从尾到头

  • 双向链表,每一个节点上面有两个引用,既可以从头到尾,也可以从尾到头

双向链表的缺点:

  • 插入和删除节点要处理四个引用

  • 占用的内存空间更大

双向链表的特点

  • 使用head指向头部,使用tail指向尾部

  • 每个节点都由三部分组成,prev(指向前一个节点)/item(保存的元素)/next(指向后一个节点)

  • 双向链表的第一个节点的prev是null

  • 双向链表的最后一个节点的next是null

常见操作

 实现代码


        function DoublyLinkedList() {
            function Node(data) {
                this.data = data
                //next指向未知
                this.next = null
                this.pre = null
            }
            //属性
            this.head = null
            // 尾部
            this.tail = null
            this.length = 0

            DoublyLinkedList.prototype.append = function (data) {
                // 这里记得要加new
                var newNode = new Node(data)
                console.log(newNode)
                if (this.head === null) {
                    this.head = newNode
                    this.tail = newNode
                    console.log(this.head)
                    console.log(this.tail)
                } else {
                    this.tail.next = newNode
                    newNode.pre = this.tail
                    this.tail = newNode
                }
                return this.length += 1
            }


            // 2.将链表转换成字符串

            DoublyLinkedList.prototype.toString = function () {
                return this.backwardString()
            }
            // 向前遍历
            DoublyLinkedList.prototype.forwardString = function () {
                var current = this.tail
                var str = ''
                while (current) {
                    str += current.data + ' '
                    current = current.pre
                }
                return str
            }
            // 向后遍历
            DoublyLinkedList.prototype.backwardString = function () {
                var current = this.head
                var str = ''
                while (current) {
                    str += current.data + ' '
                    current = current.next
                }
                return str
            }


            //插入方法 insert 向列表插入特定位置插入一个新的项
            DoublyLinkedList.prototype.insert = function (position, data) {
                // 越界判断
                if (position < 0 || position > this.length) return false

                //2.根据data创建新节点
                var newNode = new Node(data)
                // 插入的情况
                // 判断是否为空
                if (this.length == 0) {
                    this.head = newNode
                    this.tail = newNode
                } else {
                    //从第一个节点插 就是由节点的情况下
                    if (position == 0) {
                        //原来的第一个节点pre指向新的节点
                        this.head.pre = newNode
                        newNode.next = this.head
                        this.head = newNode
                    } else if (position == this.length) { //从最后一个添加新节点
                        this.tail.next = newNode
                        newNode.pre = this.tail
                        this.tail = newNode
                    } else {//插入到中间的位置
                        var index = 0
                        var current = this.head
                        while (index++ < position) {
                            current = current.next
                        }

                        //先把新节点的前后指针给赋值
                        newNode.next = current
                        newNode.pre = current.pre
                        // 前一个节点的next指向新节点 前一个节点由之前的节点的pre得到
                        current.pre.next = newNode
                        //再把后一个节点的pre改成新节点
                        current.pre = newNode



                    }
                    this.length += 1

                    return true

                }

            }

            //get方法元素 根据位置返回元素
            DoublyLinkedList.prototype.get = function (position) {
                // 1.越界判断
                if (position < 0 || position >= this.length) return null
                var index
                var current
                //使用二分查找提高效率
                if (this.length / 2 < position) {
                    index = 0
                    current = this.head
                    while (index++ < position) {
                        current = current.next
                    }

                } else {
                    index = this.length - 1
                    current = this.tail
                    while (index-- > position) {
                        current = current.pre
                    }

                }

                return current.data
            }
            //根据元素返回下标,没有返回-1
            DoublyLinkedList.prototype.indexOf = function (data) {
                var index = 0
                var current = this.head
                while (current) {
                    if (data === current.data) {
                        return index
                    }
                    current = current.next
                    index++
                }
                return -1
            }
            //更新方法
            DoublyLinkedList.prototype.update = function (position, ele) {
                // 越界判断
                if (position < 0 || position > this.length) return false

                var current

                // 二分查找法
                if (position / 2 < position) {
                    index = 0
                    current = this.head
                    while (index++ < position) {
                        current = current.next
                    }
                    current.data = ele

                } else {
                    index = this.length - 1
                    current = this.tail
                    while (index-- > position) {
                        current = current.pre
                    }
                    current.data = ele
                }
                return true
            }
            // 从列表移除一项
            DoublyLinkedList.prototype.removeAt = function (position) {
                // 越界判断
                if (position < 0 || position >= this.length) return null
                var index
                var current = this.head
                // 判断是否只有一个节点
                if (this.length == 1) {
                    this.head = null
                    this.tail = null
                } else {
                    if (position == 0) {
                        this.head.next.pre = null
                        this.head = this.head.next
                        // this.head.pre = null
                    } else if (position == this.length - 1) {
                        this.tail.pre.next = null
                        this.tail = this.tail.pre
                    } else {
                        var index = 0
                        while (index++ < position) {
                            current = current.next
                        }
                        current.pre.next = current.next
                        current.next.pre = current.pre

                    }
                }

                // if (position / 2 < position) {
                //     //移除第一个
                //     if (position == 0) {
                //         this.head.next.pre = null
                //         this.head = this.head.next
                //         // this.head.pre = null
                //     } else {
                //         // index = 0
                //         current = this.head
                //         while (index++ < position) {
                //             current = current.next
                //         }
                //         current.next.pre = current.pre
                //         current.pre.next = current.next

                //     }
                // } else {
                //     //移除最后一个
                //     if (position = this.length - 1) {
                //         this.tail.pre.next = null
                //         this.tail = this.tail.pre
                //     } else {
                //         index = this.length - 1
                //         current = this.tail
                //         while (index-- > position) {
                //             current = current.pre
                //         }
                //         current.next.pre = current.pre
                //         current.pre.next = current.next
                //     }

                // }
                this.length--
                return current.data
            }
            //remove移除列表中的一项
            DoublyLinkedList.prototype.remove = function (ele) {
                var index = this.indexOf(ele)
                return this.removeAt(index)

            }

            // isEmpty
            DoublyLinkedList.prototype.isEmpty = function () {
                return this.length == 0
            }
            //size()
            DoublyLinkedList.prototype.size = function () {
                return this.length
            }

            //获取链表的第一个元素
            DoublyLinkedList.prototype.getHead = function () {
                return this.head.data
            }
            //获取连接的最后一个元素
            DoublyLinkedList.prototype.getTail = function () {
                return this.tail.data
            }

        }
        // 测试代码
        var list = new DoublyLinkedList()
        list.append('abc')
        list.append('cba')
        list.append('nba')
        //2.测试转成字符串的方法
        // alert(list)
        // alert(list.backwardString())
        // alert(list.forwardString())
        // 3.测试insert方法
        list.insert(0, 'aaa')
        list.insert(2, '99')
        // alert(list)
        // alert(list.get(2))
        // alert(list.indexOf('99'))
        list.update(2, '55')
        // alert(list)
        // list.removeAt(0)
        alert(list)
        alert(list.remove('cba'))
        alert(list.remove('nba'))
        alert(list.remove('55'))
        alert(list)
        alert(list.size())
        alert(list.getHead())
        alert(list.isEmpty())

​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值