美文网首页
数据结构与算法JavaScript描述(4) —— 链表(Lin

数据结构与算法JavaScript描述(4) —— 链表(Lin

作者: fehysunny | 来源:发表于2018-01-03 09:28 被阅读22次

    单向链表

    链表是由一组节点组成的集合。每个节点都使用一个对象的引用指向它的后继。指向另一 个节点的引用叫做链。

    实现:

    class Node {
        constructor(val) {
            this.val = val      // 节点数据
            this.next = null        // 指向下一个节点的链接
        }
    }
    
    class LinkedList {
    
        constructor() {
            this.head = new Node('head')
            this.length = 0         // 链表的长度,默认为0,不算头节点
        }
    
        get isEmpty() {
            return !this.length
        }
    
        // 遍历链表,查找指定索引节点
        find(val) {
            let curNode = this.head
            while(curNode.val !== val) {
                curNode = curNode.next
            }
            return curNode
        }
    
        // 查找指定节点的索引
        findIndex(val) {
            let curNode = this.head
            let index = -1
            while(curNode.val !== val) {
                curNode = curNode.next
                index++
            }
            if (index == this.length) return -1
            return index
        }
    
        // 在任意位置插入节点
        insert(position, val) {
            if (position < 0 || position > this.length) return false
            const newNode = new Node(val)
            let curNode = this.head
            let index = 0
            // 找到插入点的节点
            while(index < position) {
                curNode = curNode.next
                index++
            }
            newNode.next = curNode.next
            curNode.next = newNode
            this.length++
            return true
        }
    
        // 在链表最后插入一个新元素
        append(val) {
            const newNode = new Node(val)
            let curNode = this.head
            while(curNode.next !== null) {
                curNode = curNode.next
            }
            curNode.next = newNode
            this.length++
        }
    
        // 删除指定位置的节点
        removeAt(position) {
            if (position < 0 || position > this.length) return null
            let curNode = this.head
            let index = 0
            let prevNode = null
            // 找到待删除节点
            while(index <= position) {
                prevNode = curNode
                curNode = curNode.next
                index++
            }
            prevNode.next = curNode.next
            this.length--
            return curNode.val
        }
    
        // 删除节点
        remove(val) {
            // 找到待删除节点前面的节点,然后修改它的next属性,使其不再指向待删除节点,而是指向待删除节点的下一个节点
            const index = this.findIndex(val)
            return this.removeAt(index)
        }
    
        toString() {
            let curNode = this.head
            let str = ''
            while(curNode.next !== null) {
                str += curNode.next.val + ', '
                curNode = curNode.next
            }
            return str.slice(0, -2)
        }
    }
    

    测试:

    // test
    const cities = new LinkedList()
    cities.insert(0, 'BeiJing')
    cities.insert(1, 'ShangHai')
    cities.insert(0, 'ChongQing')
    console.log(cities.toString())      // ChongQing, BeiJing, ShangHai
    cities.remove('BeiJing')
    console.log(cities.toString())      // ChongQing, ShangHai
    console.log(cities.length)              // 2
    cities.removeAt(1)
    console.log(cities.toString())      // ChongQing
    cities.append('GuangZhou')
    console.log(cities.toString())      // ChongQing, GuangZhou
    

    双向链表

    与普通列表类似,只是多了一个指向前驱节点的链接。

    实现:

    class Node {
        constructor(val) {
            this.val = val      // 节点数据
            this.next = null        // 指向下一个节点的链接
            this.prev = null    // 指向前一个节点
        }
    }
    
    class DbLinkedList {
    
        constructor() {
            this.head = new Node('head')
            this.current = this.head
            this.length = 0         // 链表的长度,默认为0,不算头节点
        }
    
        get isEmpty() {
            return !this.length
        }
    
        // 遍历链表,查找指定索引节点
        find(val) {
            let curNode = this.head
            while(curNode.val !== val) {
                curNode = curNode.next
            }
            return curNode
        }
    
        // 查找最后的节点
        findLast() {
            let curNode = this.head
            while(curNode.next !== null) {
                curNode = curNode.next
            }
            return curNode
        }
    
        // 查找指定节点的索引
        findIndex(val) {
            let curNode = this.head
            let index = -1
            while(curNode.val !== val) {
                curNode = curNode.next
                index++
            }
            if (index == this.length) return -1
            return index
        }
    
        // 在任意位置插入节点
        insert(position, val) {
            if (position < 0 || position > this.length) return false
            const newNode = new Node(val)
            let curNode = this.head
            let index = 0
            // 找到插入点的节点
            while(index < position) {
                curNode = curNode.next
                index++
            }
            newNode.next = curNode.next
            newNode.prev = curNode
            if (curNode.next && curNode.next.prev) {
                curNode.next.prev = newNode
            }
            curNode.next = newNode
            this.length++
            return true
        }
    
        // 在链表最后插入一个新元素
        append(val) {
            const newNode = new Node(val)
            let curNode = this.head
            while(curNode.next !== null) {
                curNode = curNode.next
            }
            curNode.next = newNode
            newNode.prev = curNode
            this.length++
        }
    
        // 删除指定位置的节点
        removeAt(position) {
            if (position < 0 || position > this.length) return null
            let curNode = this.head
            let index = 0
            let prevNode = null
            // 找到待删除节点
            while(index <= position) {
                prevNode = curNode
                curNode = curNode.next
                index++
            }
            prevNode.next = curNode.next
            curNode.next.prev = prevNode
            curNode.prev = null
            curNode.next = null
            this.length--
            return curNode.val
        }
    
        // 删除节点
        remove(val) {
            // 找到待删除节点前面的节点,然后修改它的next属性,使其不再指向待删除节点,而是指向待删除节点的下一个节点
            const index = this.findIndex(val)
            return this.removeAt(index)
        }
    
        toString() {
            let curNode = this.head
            let str = ''
            while(curNode.next !== null) {
                str += curNode.next.val + ', '
                curNode = curNode.next
            }
            return str.slice(0, -2)
        }
    
        display() {
            console.log(this.toString())
        }
    
        displayReverse() {
            const data = this.toString().split(', ').reverse().join(', ')
            console.log(data)
        }
    
        // 在链表中向前移动 n 个节点
        advance(n) {
            while(n > 0 && this.current.next !== null) {
                this.current = this.current.next
                n--
            }
        }
    
        // 在双向链表中后退 n 个节点
        back(n) {
            while(n > 0 && this.current.val !== 'head') {
                this.current = this.current.prev
                n--
            }
        }
    
        // 只显示当前节点
        show() {
            console.log(this.current)
        }
    }
    

    测试:

    const cities = new DbLinkedList()
    cities.insert(0, 'BeiJing')
    cities.insert(1, 'ShangHai')
    cities.insert(0, 'ChongQing')
    cities.append('GuangZhou')
    cities.append('TianJin')
    cities.append('Taiwan')
    cities.display()                            // ChongQing, BeiJing, ShangHai, GuangZhou, TianJin, Taiwan
    cities.remove('BeiJing')
    cities.display()                            // ChongQing, ShangHai, GuangZhou, TianJin, Taiwan
    cities.removeAt(1)
    cities.display()                            // ChongQing, GuangZhou, TianJin, Taiwan
    cities.displayReverse()             // Taiwan, TianJin, GuangZhou, ChongQing
    cities.show()
    cities.advance(2)
    cities.show()                                   // Node {val: "GuangZhou", next: Node, prev: Node}
    cities.back(1)
    cities.show()                                   // Node {val: "ChongQing", next: Node, prev: Node}
    

    循环链表

    循环链表和单向链表相似,节点类型都是一样的。唯一的区别是,在创建循环链表时,让其头节点的 next 属性指向它本身,即: head.next = head

    实现:

    class Node {
        constructor(val) {
            this.val = val      // 节点数据
            this.next = null        // 指向下一个节点的链接
        }
    }
    
    class CircularLinkedList {
        constructor() {
            this.head = new Node('head')
            this.length = 0         // 链表的长度,默认为0,不算头节点
            this.current = this.head
        }
    
        get isEmpty() {
            return !this.length
        }
    
        // 遍历链表,查找指定索引节点
        find(val) {
            let curNode = this.head
            while(curNode.val !== val && curNode.next.val !== 'head') {
                curNode = curNode.next
            }
            return curNode
        }
    
        // 查找指定节点的索引
        findIndex(val) {
            let curNode = this.head
            let index = -1
            while(curNode.val !== val && curNode.next.val !== 'head') {
                curNode = curNode.next
                index++
            }
            if (index == this.length) return -1
            return index
        }
    
        // 查找最后的节点
        findLast() {
            let curNode = this.head
            while(curNode.next !== null && curNode.next.val !== 'head') {
                curNode = curNode.next
            }
            return curNode
        }
    
        // 在任意位置插入节点
        insert(position, val) {
            if (position < 0 || position > this.length) return false
            const newNode = new Node(val)
            let curNode = this.head
            let index = 0
            // 找到插入点的节点
            while(index < position) {
                curNode = curNode.next
                index++
            }
            newNode.next = curNode.next
            curNode.next = newNode
            // 链表的尾节点指向头节点
            const lastNode = this.findLast()
            lastNode.next = this.head
            this.length++
            return true
        }
    
        // 在链表最后插入一个新元素
        append(val) {
            const newNode = new Node(val)
            let curNode = this.head
            while(curNode.next !== null && curNode.next.val !== 'head') {
                curNode = curNode.next
            }
            curNode.next = newNode
            // 链表的尾节点指向头节点
            newNode.next = this.head
            this.length++
        }
    
        // 删除指定位置的节点
        removeAt(position) {
            if (position < 0 || position > this.length) return null
            let curNode = this.head
            let index = 0
            let prevNode = null
            // 找到待删除节点
            while(index <= position) {
                prevNode = curNode
                curNode = curNode.next
                index++
            }
            prevNode.next = curNode.next
            this.length--
            return curNode.val
        }
    
        // 删除节点
        remove(val) {
            // 找到待删除节点前面的节点,然后修改它的next属性,使其不再指向待删除节点,而是指向待删除节点的下一个节点
            const index = this.findIndex(val)
            return this.removeAt(index)
        }
    
        // 在链表中向前移动 n 个节点
        advance(n) {
            while(n > 0) {
            this.current = this.current.next
            if (this.current.val === 'head') {
                this.current = this.current.next
            }
            n--
        }
        }
    
        // 只显示当前节点
        show() {
            console.log(this.current)
        }
    
        display() {
            console.log(this.toString())
        }
    
        displayReverse() {
            const data = this.toString().split(', ').reverse().join(', ')
            console.log(data)
        }
    
        toString() {
            let curNode = this.head
            let str = ''
            while(curNode.next !== null && curNode.next.val !== 'head') {
                str += curNode.next.val + ', '
                curNode = curNode.next
            }
            return str.slice(0, -2)
        }
    }
    

    测试:

    const cities = new CircularLinkedList()
    cities.insert(0, 'BeiJing')
    cities.insert(1, 'ShangHai')
    cities.insert(0, 'ChongQing')
    cities.append('GuangZhou')
    cities.append('TianJin')
    cities.append('Taiwan')
    cities.display()                            // ChongQing, BeiJing, ShangHai, GuangZhou, TianJin, Taiwan
    cities.remove('BeiJing')
    cities.display()                            // ChongQing, ShangHai, GuangZhou, TianJin, Taiwan
    cities.removeAt(1)
    cities.display()                            // ChongQing, GuangZhou, TianJin, Taiwan
    cities.displayReverse()             // Taiwan, TianJin, GuangZhou, ChongQing
    cities.advance(2)
    cities.show()                                   // Node {val: "GuangZhou", next: Node}
    

    示例:约瑟夫环问题

    传说在公元 1 世纪的犹太战争中,犹太历史学家弗拉维奥·约瑟夫斯和他的 40 个同胞 被罗马士兵包围。犹太士兵决定宁可自杀也不做俘虏,于是商量出了一个自杀方案。他 们围成一个圈,从一个人开始,数到第三个人时将第三个人杀死,然后再数,直到杀光 所有人。约瑟夫和另外一个人决定不参加这个疯狂的游戏,他们快速地计算出了两个位 置,站在那里得以幸存。写一段程序将 n 个人围成一圈,并且第 m 个人会被杀掉,计算 一圈人中哪两个人最后会存活。使用循环链表解决该问题。

    /**
     * @param { Number } n  // 总人数
     * @param { Number } m  // 间隔人数
     * @return { String }
     */
    function game(n, m) {
        var links = new CircularLinkedList()
        links.insert(0, 1)
        var sign = 2
        // 生成循环链表
        while(sign <= n) {
            links.insert(sign - 1, sign)
            sign++
        }
        // 循环遍历,直到剩余的人数少于间隔数
        while(n >= m) {
            links.advance(m)
            links.remove(links.current.val)
            n--
        }
        links.display()
    }
    
    // test
    game(40, 3)        // 13, 28
    

    相关文章

      网友评论

          本文标题:数据结构与算法JavaScript描述(4) —— 链表(Lin

          本文链接:https://www.haomeiwen.com/subject/ipocnxtx.html