• 队列和双端队列


    本篇博客的所有代码都整理到自己的github库中,以便大家查看及学习。

    本篇博客主要是从以下几点展开

    1. 队列数据结构
    2. 双端数列数据结构
    3. 向队列和双端队列增加元素
    4. 从队列和双端队列中删除元素
    5. 经典算法

    1. 队列数据结构

    队列遵循先进先出(FIFO)原则的一组有序的项。队列在尾部添加元素,在头部移除元素,最先添加的元素一定是放在队列的末尾。

    队列

    队列的例子在生活中很常见,在排队买票,排队上车等等

    2. 创建队列

    接下来我们通过创建自己的类表示队列,然后声明以下方法

    • enqueue(elements): 向尾部追加元素(追加新的项)
    • dequeue(): 移除队列中的第一项,并返回移除的元素
    • peek() : 返回队列中的第一项(并不会对该项有任何操作)
    • isEmpty() : 判断队列是否为空
    • size() : 返回队列中包含的元素的个数
    • clear() : 清空队列
    • toString() : 转为字符串的方法
    class Queue{
        constructor(){
            this.items = {} // 根据上篇文章《栈》的理解,这次我们也是用数组的方式来放队列中的各元素
            this.count = 0 // 队列中各项元素的个数
            this.lowestCount = 0 // 用来帮助我们追踪第一个元素的变量
        }
        enqueue(elem){
            this.items[this.count] = elem //将队列中的最后一个位置设置为追加进来的元素
            this.count++
        }
        dequeue(){
            if (this.isEmpty()) {
                return ''
            }
            const result = this.items[this.lowestCount]
            delete this.items[this.lowestCount]
            this.lowestCount++
            return result
        }
        peek(){
            if (this.isEmpty()) {
                return ''
            }
            return this.items[this.lowestCount] 
        }
        isEmpty(){
            return this.size() === 0
        }
        size(){
            return this.count - this.lowestCount
        }
        clear(){
            this.items = {}
            this.count = 0
            this.lowestCount = 0
        }
        toString(){
            if (this.isEmpty()) {
                return ''
            }
            let result = this.items[this.lowestCount]
            
            for (const k in this.items) {
                if (this.items[k] && this.items[k] !== this.items[this.lowestCount]) {
                    result = `${result},${this.items[k]}` 
                }
            }
            return result
        }
    
    }
    
    var queue = new Queue()
    queue.enqueue(1)
    queue.enqueue(2)
    queue.enqueue(3)
    queue.enqueue(4)
    console.log(queue) // Queue { items: [ 1, 2, 3, 4 ], count: 4, lowestCount: 0 }
    console.log(queue.size()) // 4
    console.log(queue.isEmpty()) // false
    console.log(queue.dequeue()) // 1
    console.log(queue) // Queue { items: [ <1 empty item>, 2, 3, 4 ], count: 4, lowestCount: 1 }
    console.log(queue.size())
    console.log(queue.peek()) // 2
    console.log(queue) // Queue { items: [ <1 empty item>, 2, 3, 4 ], count: 4, lowestCount: 1 }
    console.log(queue.toString()) // 2,3,4
    

    3. 双端队列数据结构

    双端队列(deque,或称 double-end queue) 是一种允许我们同时从前端和后端添加和移除元素的特殊队列。
    最常见的生活中的例子就是大家排队买票,当第一个人买完票后,可能会再次返回问售票员一些问题,这个时候他就不用去排队,而排在队尾的人可能就因为赶时间,提前离开队伍。

    双端队列最常见的一个应用就是存储一系列撤销操作

    4. 创建 Deque 类

    和之前一样,我们也是在构造函数中声明一些双端队列内部的属性及方法,isEmpty,clear,size,toString
    接下来定义一些对外的方法

    • addFront(element) : 向双端队列前面插入新的元素
    • addBack(element) : 向双端队列后面追加新的元素
    • removeFront() : 移除双端队列前面的元素,并返回移除的元素
    • removeBack() : 移除双端队列后面的元素,并返回移除的元素
    • peekFront() : 返回双端队列中的第一项元素
    • peekBack() : 返回双端队列中的最后一项元素
    class Deque{
        constructor(){
            this.items = {}
            this.count = 0
            this.lowestCount = 0
        }
        isEmpty(){
            return this.count === 0
        }
        clear(){
            this.items = {}
            this.count = 0
            this.lowestCount = 0
        }
        size(){
            return this.count - this.lowestCount
        }
        addFront(elem){
            
            if (this.isEmpty()) {
                this.addBack(elem)
            } else if (this.lowestCount > 0) {
                this.lowestCount --
                this.items[this.lowestCount] = elem
            } else {
                for (let i = this.count; i > 0; i--) {
                  this.items[i] = this.items[i - 1]
                }
                this.count ++
                this.lowestCount = 0  
                this.items[0] = elem
            }
        }
        addBack(elem){
            this.items[this.count] = elem
            this.count++
        }
        removeFront(){
            const result = this.items[this.lowestCount]
            this.items[this.lowestCount] = undefined
            this.lowestCount ++
            return result
        }
        removeBack(){
            const result = this.items[this.count]
            this.count --
            return result
        }
        peekFront(){
            return this.items[this.lowestCount]
        }
        peekBack(){
            return this.items[this.count]
        }
        toString(){
            if (this.isEmpty()) {
                return ''
            }
            let result = this.items[this.lowestCount]
            for (const k in this.items) {
                if (this.items[k] !== result && this.items[k]) {
                    result = `${result},${this.items[k]}`
                }
            }
            return result
        }
    }
    
    var deque = new Deque()
    deque.addBack(1)
    deque.addFront(0)
    deque.addBack('Alice')
    deque.addFront('JD')
    console.log(deque) //Deque {items: { '0': 'JD', '1': 0, '2': 1, '3': 'Alice' },count: 4,lowestCount: 0 }
    console.log(deque.removeFront()) // JD
    console.log(deque) //Deque {items: { '0': undefined, '1': 0, '2': 1, '3': 'Alice' },count: 4, lowestCount: 1}
    
    console.log(deque.size()) // 3
    console.log(deque.toString()) // 0,1,Alice
    

    5. 经典算法

    1. 击鼓传花

    在思考这个题目的时候我们先了解一下循环队列。而循环队列最常见的例子就是击鼓传花。击鼓传花就是一群孩子们围成圈,将花传递给旁边的人,当在某一时刻,传花停止时,花在谁的手里谁就会被淘汰。重复这个过程,直到剩下最后一个孩子

    function hotPotato(elementList,num){
        const queue = new Queue()
        const eliminatedList = [] // 淘汰的元素
    
        for (let i = 0; i < elementList.length; i++) {
            queue.enqueue(elementList[i])
        }
    
        while (queue.size() > 1) {
            for (let i = 0; i < num; i++) {
                queue.enqueue(queue.dequeue())
            }
            eliminatedList.push(queue.dequeue())
        }
        return {
            eliminated:eliminatedList,
            winner:queue.dequeue()
        }
    }
    
    var names = ['John','Jack','Calmia','Alice','Carl']
    const result = hotPotato(names,7)
    // console.log(result) // { eliminated: [ 'Calmia', 'Jack', 'Carl', 'Alice' ], winner: 'John' }
    result.eliminated.forEach(name => {
        console.log(`${name}在游戏中被淘汰`)
    })
    console.log(`胜利者是:${result.winner}`)
    // Calmia在游戏中被淘汰
    // Jack在游戏中被淘汰
    // Carl在游戏中被淘汰
    // Alice在游戏中被淘汰
    // 胜利者是:John
    

    上述的代码不好理解,这里画一幅图,便于理解;图中只表示了第一次循环的状态

    xuxieban.png

    2. 回文检查器

    最简单的方式就是将字符串反向排列后,检查它和原字符串是否一样,若相同,则为一个回文字符串

    function palindromeChecker(string) {
        if (!string) { // 判空
            return false 
        }
        let deque = new Deque()
        const str = string.toLowerCase() // 转为了小写
        const tempStr = str.split(' ').join() // 去除字符串中的空格
        let isEqual = true 
        let firstChar; let lastChar 
    
        // 将字符串中的第一个字符串放到最后
        for (let i = 0; i < tempStr.length; i++) { 
            deque.addBack(tempStr.charAt(i))
        }
        // 比较每次双端队列移除的项是否相等,相等则继续循环,直到size为1,不想等则直接退出循环
        while (deque.size() > 1 && isEqual) {
            firstChar = deque.removeFront()
            lastChar = deque.removeBack()
            if (firstChar !== lastChar) {
                isEqual = false     
            }
        }
        return isEqual
    }
    
  • 相关阅读:
    初涉数组
    声明
    简述java程序中的main方法
    概述java语言
    1.3 linux基础(三)
    linux基础之-screen命令
    1.2 linux基础(二)
    1.1 Linux基础(一)
    实验7-1-13 装箱问题
    实验7-1-12 组个最小数
  • 原文地址:https://www.cnblogs.com/bllx/p/15949239.html
Copyright © 2020-2023  润新知