Javascript 实现单向链表和双向链表

链表原理:链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

单链表的特点:1、只能单方向的遍历(一般从头到尾)2、上一个节点只有一个指向下一个节点的指针,如图:
在这里插入图片描述
p,q是指向A节点和节点的两个指针。

单项链表的缺点:从上一个节点到下一个节点很容易,但回去很难

代码实现:

//封装链表类

function LinkedList() {
    //内部类: 节点类
    function Node(data) {
        this.data = data
        this.next = null
    }

    this.head = null //头指针 指向链表的第一个元素
    this.length = 0  //初始链表的长度

    //1、追加方法
    LinkedList.prototype.append = function (data) {
        let newNode = new Node(data)
        //判断添加的是否为第一个节点
        if (this.length === 0) {
            this.head = newNode
        } else {
            let current = this.head
            while (current.next) { //寻找最后的节点并添加新节点
                current = current.next
            }
            current.next = newNode
        }

        this.length += 1 //追加后 长度加1
    }

    //2、toString 方法
    LinkedList.prototype.toString = function () {

        let current = this.head
        let resultString = ''
        while(current) {
            resultString += current.data + ' '
            current = current.next
        }
        return resultString
    }

    //3、insert方法 根据确切的位置插入节点
    LinkedList.prototype.insert = function (data,position) {
        //对position进行越界判断
        if (position < 0 || position > this.length) return false

        let newNode = new Node(data)
        // 判断插入的是否是第一个
        if (position === 0) {
            newNode.next = this.head
            this.head = newNode
        } else {
            let index = 0
            let current = this.head //当前节点
            let previous = null  //当前节点的上一个节点
            while (index < position) { //找到插入的位置
                previous = current
                current = current.next
                index += 1
            }
            newNode.next = current
            previous.next = newNode
        }
        this.length += 1
        return true
    }

    //4、get方法  获取确切位置的数据
    LinkedList.prototype.get = function (position) {
        //越界判断
        if (position < 0 || position >= this.length) return false

        let current = this.head
        let index = 0
        while (index < position) {
            current = current.next
            index += 1
        }
        return current.data
    }

    //5、indexOf方法 返回查找元素的索引
    LinkedList.prototype.indexOf = function (data) {

        let current = this.head
        let index = 0

        while (current) {
            if (current.data === data) {
                return index
            }
            current = current.next
            index += 1
        }

        return -1 //没找到
    }

    //6、update方法 修改确定位置的数据
    LinkedList.prototype.update = function (position,data) {
        if (position < 0 || position >= this.length) return false

        let current = this.head
        let index = 0

        while (index < position) {
            current = current.next
            index += 1
        }
        current.data = data
        return true
    }

    //7、removeNode方法 删除确切位置的节点
    LinkedList.prototype.removeNode = function (position) {

        if (position < 0 || position >= this.length) return null
        let current = this.head
        //判断是否删除第一个节点
        if (position === 0) {
            this.head = this.head.next
        } else {
            let index = 0

            let previous = null
            while (index < position) {
                previous = current
                current = current.next
                index += 1
            }
            previous.next = current.next
        }
        this.length -= 1
        return current.data
    }

    //8、remove方法 根据数据删除节点
    LinkedList.prototype.remove = function (data) {
        let index = this.indexOf(data)
        return this.removeNode(index)
    }

    //9、是否为空
    LinkedList.prototype.isEmpty = function () {
        return this.length === 0
    }

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

//以下是测试链表功能的代码
const ll = new LinkedList()

ll.append('a')
ll.append('b')
ll.append('c')
ll.append('d')
ll.append('r')
ll.append('f')

console.log(ll.toString())

ll.insert('w',2)
console.log(ll.toString())

console.log(ll.get(2))

console.log(ll.indexOf('b'))

console.log(ll.update(1,'m'))
console.log(ll.toString())

console.log(ll.removeNode(1))
console.log(ll.toString())

console.log(ll.remove('w'))
console.log(ll.toString())

console.log(ll.isEmpty())
console.log(ll.size())

测试的结果

a b c d r f
a b w c d r f
w
1
true
a m w c d r f
m
a w c d r f
w
a c d r f
false
5

双向链表特点:1、每个数据结点中都有两个指针,分别指向直接后继和直接前驱 2、有头指针和尾指针分别指向第一个节点和最后一个节点 3,可以从前往后遍历也可以从后往前遍历
在这里插入图片描述

代码实现:


//封装双向链表类
function DoubleLinkedList() {
    this.head = null //指向第一个节点的指针
    this.tail = null //指向最后一个节点的指针
    this.length = 0 //长度

    //内部类: 节点类
    function Node(data) {
        this.prev = null //指向上一个节点的指针
        this.data =  data
        this.next = null //指向下一个节点的指针
    }

    //1、append()方法 追加
    DoubleLinkedList.prototype.append = function (data) {
        let newNode = new Node(data) //创建新节点
        //判断添加的是否为第一个节点
        if (this.length === 0) { //为第一个节点
            this.head = newNode //第一个节点 头指针和尾指针指向同一个节点
            this.tail = newNode
        } else { //不是第一个
            newNode.prev = this.tail //新节点的prev指向tail
            this.tail.next = newNode //双向连接
            this.tail = newNode //尾指针更新为新加入的节点 始终指向最后一个节点
        }
        this.length += 1
    }

    //2、toString() 方法
    DoubleLinkedList.prototype.toString = function () {

        return this.backwardString()
    }

    //3、forwardString() 从后往前遍历
    DoubleLinkedList.prototype.forwardString = function () {
        let current = this.tail

        let resultString = ''
        while(current) {
            resultString += current.data + ' '
            current =  current.prev
        }
        return resultString
    }

    //4、backwardString() //从前往后遍历
    DoubleLinkedList.prototype.backwardString = function () {
        let current = this.head

        let resultString = ''
        while(current) {
            resultString += current.data + ' '
            current =  current.next
        }
        return resultString
    }

    //5、insert() 在确切位置插入节点
    DoubleLinkedList.prototype.insert = function (position,data) {
        //越界判断
        if (position < 0 || position > this.length) {
            return false
        }
        //创建新节点
        let newNode = new Node(data)
        //判断列表是否为空
        if (this.length === 0) {
            this.head = newNode
            this.tail = newNode
        } else {
            //判断插入的是否为第一个节点
            if (position === 0) {
                this.head.prev = newNode
                newNode.next = this.head
                this.head = newNode
            } else if (position === this.length) { //判断插入的是否为最后一个节点
                newNode.prev = this.tail
                this.tail.next = newNode
                this.tail = newNode
            } else {
                let current = this.head
                let index = 0

                while (index < position) {
                    current = current.next
                    index += 1
                }
               //修改指针
                newNode.prev = current.prev
                newNode.next = current
                current.prev.next = newNode
                current.prev = newNode

            }
        }
        this.length += 1
        return true
    }

    //6、get()方法 获取确切位置的数据
    DoubleLinkedList.prototype.get = function (position) {
        //越界判断
        if (position < 0 || position >= this.length)  {
            return false
        }
        //this.length / 2 > position 从后遍历
        //this.length / 2 < position 从前遍历
        if ( this.length / 2 <= position ) {
            let current = this.head
            let index = 0

            while (index < position) {
                current = current.next
                index +=1
            }
            return current.data
        } else {
            let current = this.tail
            let index = this.length - 1

            while (index > position) {
                current = current.prev
                index -= 1
            }
            return current.data
        }
    }

    //7、indexOf()方法 获取元素的索引
    DoubleLinkedList.prototype.indexOf = function (data) {
        let current = this.head
        let index = 0

        while (current) {
            if (current.data === data) {
                return index
            }
            current = current.next
            index += 1
        }
        return -1
    }

    //8、update()方法 跟新数据
    DoubleLinkedList.prototype.update = function (position,data) {
        //越界判断
        if ( position < 0 || position >= this.length )  {
            return false
        }
        //this.length / 2 > position 从后遍历
        //this.length / 2 < position 从前遍历
        if ( this.length / 2 <= position ) {
            let current = this.head
            let index = 0

            while (index < position) {
                current = current.next
                index +=1
            }
           current.data = data
        } else {
            let current = this.tail
            let index = this.length - 1

            while (index > position) {
                current = current.prev
                index -= 1
            }
           current.data = data
        }
        return true
    }

    //9、removeNode() 删除确切位置的节点
    DoubleLinkedList.prototype.removeNode = function (position) {
        //越界判断
        if ( position < 0 || position >= this.length )  {
            return null
        }
        let current = this.head
        //判断链表是否只有一个节点
        if (this.length === 1) {
            this.head = null
            this.tail = null
        } else {
            //删除的是否为第一个节点
            if (position === 0) {
                this.head.next.prev = null
                this.head = this.head.next
            } else if (position === this.length -1) { //删除是否为最后一个节点
                current = this.tail
                this.tail.prev.next = null
                this.tail = this.tail.prev
            } else {
                //this.length / 2 > position 从后遍历
                //this.length / 2 < position 从前遍历
                if ( this.length / 2 <= position ) {
                    let index = 0

                    while (index < position) {
                        current = current.next
                        index +=1
                    }
                    current.next.prev = current.prev
                    current.prev.next = current.next
                } else {
                    current = this.tail
                    let index = this.length - 1

                    while (index > position) {
                        current = current.prev
                        index -= 1
                    }
                    current.next.prev = current.prev
                    current.prev.next = current.next
                }

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

    //10、remove()方法 根据元素删除节点
    DoubleLinkedList.prototype.remove = function (data) {
        let index = this.indexOf(data)
        return this.removeNode(index)
    }

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

    //size()
    DoubleLinkedList.prototype.size = function () {
        return this.length
    }

    //获取第一个元素
    DoubleLinkedList.prototype.getFirst = function () {
        return this.head.data
    }

    //获取随后一个元素
    DoubleLinkedList.prototype.getLast = function () {
        return this.tail.data
    }

}

测试以上功能的代码:

const dl = new DoubleLinkedList()

dl.append('a')
dl.append('b')
dl.append('c')
dl.append('d')
dl.append('e')
dl.append('f')
dl.append('g')


console.log(dl.toString())
console.log(dl.backwardString())
console.log(dl.forwardString())

dl.insert(0,'x')
dl.insert(2,'Y')
dl.insert('4','z')

console.log(dl.backwardString())
console.log(dl.get(8))

console.log(dl.indexOf('d'))

console.log(dl.update(9,'o'))
console.log(dl.backwardString())

console.log(dl.removeNode(6))
console.log(dl.backwardString())

console.log(dl.remove('z'))
console.log(dl.backwardString())

console.log(dl.isEmpty())
console.log(dl.size())

console.log(dl.getFirst())
console.log(dl.getLast())

测试结果:

a b c d e f g
a b c d e f g
g f e d c b a
x a Y b z c d e f g
f
6
true
x a Y b z c d e f o
d
x a Y b z c e f o
z
x a Y b c e f o
false
8
x
o

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值