JavaScript数据结构之链表

链表相较于数组的优缺点

1. 链表在 插入、删除、移动数据效率比数组要高,数组插入、移动、删除数据需要改变没有数据的索引,而链表则只需要更改指针即可

2. 在查询方面,数组要优于链表,数组存储的数据是连续的,可以直接通过位置信息读取到数据,而链表则需要通过遍历的方式找到对应的指针

3. 灵活性方面,传统的数组是需要固定长度的,而链表则可以是任意长度

备注:JavaScript中会根据实际情况,将数组转换为 快数组或者慢数组(掘金的文章:https://juejin.cn/post/6844903943638794248#heading-0

链表图解:

 

 

链表实现(实现了循环链表、双向链表、单项链表):

// 创建链表
class Node {
    constructor(element) {
        this.element = element
        this.next = null
        // 实现双向链表指针
        this.prev = null
    }
}
class SingleList {
    constructor() {
        this.size = 0
        this.head = null
        this.tail = this.head
    }

    // 在尾部添加元素
    append(element) {
        const newNode = new Node(element)
        // 实现循环链表(其实就是将最后一个元素的指针指向 head)
        // newNode.next = this.head
        if (!this.head) {
            this.head = newNode
        } else {
            this.tail.next = newNode
            newNode.prev = this.tail
        }

        this.tail = newNode
        this.size++
    }
  // 对 size 进行缓存,获取长度的时候直接返回即可
    getLength() {
        return this.size
    }

    find(element) {
        let _elm = this.head
        let i = 0
        // while(_elm) {} 因为实现了循环链表,_elm不可能为空
        while (i < this.size) {
            if (_elm.element === element) {
                return _elm
            }

            _elm = _elm.next
            i++
        }

        return null
    }
    // 删除指定元素
    remove(element) { 
        const _delItem = this.find(element)

        if(!_delItem) {
            return false
        }
    // 通过改变指针选项实现
        const _oldNext = _delItem.next
        const prev = _delItem.prev
        prev.next = _oldNext

        return true
    }
  // 或者最后一个元素,this.tail 已经进行缓存,故直接返回即可
    findLast() { return this.tail }

    insert(newItem, posItem) {
        if (posItem) {
            const pos = this.find(posItem)
            if (!pos) {
                return false
            }
       // 通过改变指针指向实现插入元素
            const oldNext = pos.next
            const newNext = new Node(newItem)
            newNext.prev = pos
            newNext.next = oldNext
            pos.next = newNext
        } else {
            this.append(newItem)
        }
    }
}

  

链表拓展算法:

1. 链表反转

function reverse(head) {
    let _newHead = null

    while(head) {
        const tmp = head.next
        head.next = _newHead 
        _newHead = head
        head = tmp       
    }

    return _newHead
}

// 模拟一个链表格式
const head = {"element":1,"next":{"element":2,"next":{"element":3,"next":null}}}
reverse(head)
// 分析一波:
// 第一次while(head = {"element":1,"next":{"element":2,"next":{"element":3,"next":null}}}):
//     const tmp = head.next // tmp -> {"element":2,"next":{"element":3,"next":null}}
//     head.next = _newHead // head -> {"element":1,"next": null }
//     _newHead = head // _newHead -> {"element":1,"next": null }
//     head = tmp // head -> {"element":2,"next":{"element":3,"next":null}}

// 第二次while(head = {"element":2,"next":{"element":3,"next":null}}):
//     const tmp = head.next // tmp -> {"element":3,"next":null}
//     head.next = _newHead // head -> {"element":2,"next": {"element":1,"next": null }}
//     _newHead = head // _newHead -> {"element":2,"next": {"element":1,"next": null }}
//     head = tmp // head -> {"element":3,"next":null}

// 第三次while(head = {"element":3,"next":null}):
//     const tmp = head.next // tmp -> null
//     head.next = _newHead // head -> {"element":3,"next":{"element":2,"next": {"element":1,"next": null }}}
//     _newHead = head // _newHead -> {"element":3,"next":{"element":2,"next": {"element":1,"next": null }}}
//     head = tmp // head -> null

  

2. 判断是否为回文链表

备注:这里使用 快慢指针 的方式实现 时间复杂度O(n),空间复杂度O(1),也可以先将链表转为数组实现,时间复杂度O(n) 空间复杂度O(n)

// 判断是否是 回文链表
function isPalindrome(head) {
    if(!head || !head.next) {
        return true
    }
    let fast = head;
    let slow = head;
    let prev;
    while (fast && fast.next) {
        prev = slow;
        slow = slow.next;
        fast = fast.next.next;
    }
    // 断开链表
    prev.next = null

    // 翻转后半段链表
    let head2 = null
    while(slow) {
        const tmp = slow.next
        slow.next = head2
        head2 = slow
        slow = tmp
    }
    
    // 对比
    while(head && head2) {
        if(head.element !== head2.element) {
            return false
        }
        head = head.next
        head2 = head2.next
    }

    return true
}

  

 

posted @ 2021-11-30 17:41  C+V-Engineer  阅读(112)  评论(0编辑  收藏  举报