首页 > 其他分享 >队列 - 双端队列实现

队列 - 双端队列实现

时间:2024-04-11 21:22:08浏览次数:12  
标签:count deque obj 队列 双端 元素 实现 frontIndex

之前实现的单端队列, 只能从队列的尾部进, 头部出.

但现在我们来实现一种从两端都可进行出队入队的结构, 即双端队列 deque.

在计算机中, 双端队列最常用的一个场景是存储一系列的撤销操作. 当然用户点击了某个操作, 则此操作会被存在一个双端队列中, 类似栈里.

当用户点击撤销操作时, 该操作会被从双端队列弹出. 一顿猛如虎的操作后, 最先进行的操作会先进行出队. 由于双端队列 同时遵循先进先出和后进先出 原则, 可知它是一种 队列+栈 结合的数据结构.

以数组来模拟队列直观演示一下,

当单端时是这样的, 假设左边是队首, 右边是队尾, 是单向流动的.

// 初始队列
var queue = [ ]

// 元素 1, 2, 3 依次从队尾入队
[1, 2, 3]

// 队首元素出队
[2, 3]

当双端队列时候, 队尾和队首都可进行新增和删除元素, 还是以数组来模拟, 左边是队首, 右边是对尾.

// 初始队列
var deque = []

// 元素1, 2 从队首或者队尾入对
[1, 2]

// 元素3 从队首入队
[3, 1, 2]

// 元素4 从队尾入队
[3, 1, 2, 4]

// 队首元素出队一次
[1, 2, 4]

// 队尾元素出队一次
[1, 2]

创建队列

同之前一样, 底层用 js对象来实现, 或者直接在原来的基础改造即可.

class Deque {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 队列长度
  size() {
    return this.count - this.frontIndex
  }
  // 队列是否为空
  isEmpty() {
    this.count - this.frontIndex == 0
  }
  // 清空队列
  clear() {
    this.obj = {}
    this.frontIndex = 0
    this.count = 0
  }
  // 查看全队列
  toString() {
    if (this.isEmpty()) return this.obj

    let objString = `${this.obj[this.frontIndex]}`
    for (let i = this.frontIndex + 1; i < this.count; i++) {
      objString = `${objString}, ${this.obj[i]}`
    }
    return objString
  }
}

双端队列除了以上通用方法外, 还运行在两端添加和移除元素, 因此还有又如下方法:

  • addFront () 在双端队列前端添加元素
  • addBack () 在双端队列后端添加元素, 类似单端的 enqueue
  • removeFront () 从双端队列前端移除第一个元素, 类似单端的 dequeue
  • removeBack () 从双端队列后端移除第一个元素, 类似单端的 pop
  • peekFront () 从双端队列前端返回第一个元素, 类似单端的 peek
  • peekBack () 从双端队列后端返回第一个元素

队尾添加元素

class Deque {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 队尾添加元素
  addBack(item) {
    this.obj[this.count] = item
    this.count++
  }
}

队尾删除元素

class Deque {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 队尾删除元素
  removeBack() {
    if (this.isEmpty()) return undefined

    this.count -= 1
    let backItem = this.obj[this.count]
    
    delete this.obj[this.count]
    return backItem
  }
}

队首添加元素

class Deque {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 队首添加元素
  addFront(item) {
    // 当为空队列时, 则从队尾加
    if (this.isEmpty()) {
      this.addBack(item)
    } else if (this.frontIndex > 0) {
      // 当队首元素已有过出队时, 此时 frontIndex > 0, 此时进行 减1替换即可
      this.frontIndex -= 1
      this.obj[this.frontIndex] = item
    } else {
      // 当队首元素未有过出队时, 此时 frontIndex = 0, 后面元素依次后挪
      for (let i = this.count; i > 0; i--) {
        this.obj[i] = this.obj[i-1]
      }
      // 移完后将队首元素的值和索引都更新, 队列长度加1
      this.obj[0] = item 
      this.frontIndex = 0
      this.count += 1
    }
  }
}

队首删除元素

class Deque {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 队尾删除元素
  removeBack() {
    if (this.isEmpty()) return undefined

    this.count -= 1
    let backItem = this.obj[this.count]
    
    delete this.obj[this.count]
    return backItem
  }
}

于是, 这样一个双端都可以进行出队, 入队的结构就做好啦!

测试验证

然后整体来测试一波.

class Deque {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 队列长度
  size() {
    return this.count - this.frontIndex
  }
  // 队列是否为空
  isEmpty() {
    if (this.count - this.frontIndex == 0) return true;
  }
  // 清空队列
  clear() {
    this.obj = {}
    this.frontIndex = 0
    this.count = 0
  }
  // 查看全队列
  toString() {
    if (this.isEmpty()) return this.obj;

    let objString = `${this.obj[this.frontIndex]}`
    for (let i = this.frontIndex + 1; i < this.count; i++) {
      objString = `${objString}, ${this.obj[i]}`
    }
    return objString
  }
  // 队尾添加元素
  addBack(item) {
    this.obj[this.count] = item
    this.count++
  }
  // 队尾删除元素
  removeBack() {
    if (this.isEmpty()) return undefined

    this.count -= 1
    let backItem = this.obj[this.count]
    delete this.obj[this.count]
    return backItem
  }
  // 队首添加元素
  addFront(item) {
    // 当为空队列时, 则从队尾加
    if (this.isEmpty()) {
      this.addBack(item)
    } else if (this.frontIndex > 0) {
      // 当队首元素已有过出队时, 此时 frontIndex > 0, 此时进行 减1替换即可
      this.frontIndex -= 1
      this.obj[this.frontIndex] = item
    } else {
      // 当队首元素未有过出队时, 此时 frontIndex = 0, 后面元素依次后挪
      for (let i = this.count; i > 0; i--) {
        this.obj[i] = this.obj[i-1]
      }
      // 移完后将队首元素的值和索引都更新, 队列长度加1
      this.obj[0] = item 
      this.frontIndex = 0
      this.count += 1
    }
  }
  // 队首删除元素
  removeFront() {
    if (this.isEmpty()) return undefined
    
    let frontItem = this.obj[this.frontIndex]
    delete this.obj[this.frontIndex]

    this.frontIndex += 1
    return frontItem
  }
}


// test 

// 初始队列
let deque = new Deque()
console.log('新建空队列此时为: ', deque.toString());

let item0 = deque.removeBack()
console.log('此时队列为: ', deque.toString());


console.log('元素1, 2 分别从队首, 对尾入队');
deque.addFront(1)
deque.addBack(2)
console.log('此时队列为: ', deque.toString());

console.log('元素3 从队首入队');
deque.addFront(3)
console.log('此时队列为: ', deque.toString());

console.log('元素4 从队尾入队');
deque.addBack(4)
console.log('此时队列为: ', deque.toString());


let item1 = deque.removeFront()
console.log('队首元素出队一次, 删除的元素是: ', item1);
console.log('此时队列为: ', deque.toString());

let item2 = deque.removeBack()
console.log('队尾元素出队一次, 删除的元素是: ', item2);
console.log('此时队列为: ', deque.toString());

PS F:\algorithms> node .\deque.js

新建空队列此时为:  {}
此时队列为:  {}

元素1, 2 分别从队首, 对尾入队
此时队列为:  1, 2

元素3 从队首入队
此时队列为:  3, 1, 2

元素4 从队尾入队
此时队列为:  3, 1, 2, 4

队首元素出队一次, 删除的元素是:  3
此时队列为:  1, 2, 4

队尾元素出队一次, 删除的元素是:  4
此时队列为:  1, 2

只要逻辑理解了, 然后实现就是慢慢思考即可, 还是比较有趣的.

标签:count,deque,obj,队列,双端,元素,实现,frontIndex
From: https://www.cnblogs.com/chenjieyouge/p/18130056

相关文章

  • 接口实现-删除文章(2024-4-11)
    代码量:500时间:5h博客量:1今天写了Android的前端页面,和页面功能的基本实现,剩下最难的接口调用方面了下面是跟的项目的一个简单接口//controller@DeleteMappingpublicResultdelete(Integerid){articleService.delete(id);returnResult.success();......
  • 说说你对栈、队列的理解?应用场景?
    一、栈栈(stack)又名堆栈,它是一种运算受限的线性表,限定仅在表尾进行插入和删除操作的线性表表尾这一端被称为栈顶,相反地另一端被称为栈底,向栈顶插入元素被称为进栈、入栈、压栈,从栈顶删除元素又称作出栈所以其按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶......
  • linux中通过systemctl建立服务并实现开机启动
    目录centos7下,systemctl可以理解为systemd的一个工具建立Unitfile配置文件加载配置启动服务停止服务设置服务开机启动关闭服务开机启动更多命令查看服务产生的日志centos7下,systemctl可以理解为systemd的一个工具建立Unitfile配置文件systemctl是通过Unit管理单元的形式来添......
  • 使用L7·蚂蚁地理空间数据可视化实现灯杆查看
     1<template>2<divv-if="showMapDialog">3<el-dialogtitle="查看灯杆分布":visible="visible"@close="closeDia">4<divid="map"></div>5</el-dialo......
  • el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能,利用@sort-cha
        写这篇博客的原因是前段时间做了一个数据列可变的表格,同时需要实现在网页中更新了数据列之后,能够对表格进行排序的需求。如果想要直接了解实现el-table的动态数据动态排序(列数据是通过计算获得,并且可以在页面中修改,在此基础上实现数据变化后实时更新)。请直接跳到......
  • github-webhook+docker实现项目可持续自动化部署
    目录一、项目手动部署二、项目自动部署自动构建部署流程docker概念补充使用nginx+pm2+github-webhook+docker实现项目自动部署注:docker也能实现pm2的守护进程功能(持续启动项目),所以使用了docker就不需要使用pm2了但是需要注意的是使用node启动的webhook服务器不......
  • C#的AOP(最经典实现)
    (适用于.NET/.NETCore/.NETFramework)【目录】0.前言1.第一个AOP程序2.Aspect横切面编程3.一个横切面程序拦截多个主程序4.多个横切面程序拦截一个主程序5.AOP的泛型处理(扩充)6.AOP的异步处理(扩充)7.优势总结8.展望0.前言AOP(AspectOrientedProgramming)是“面向横切面编程”,主......
  • 木棒(c++实现)
    题目乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整......
  • consul:啥?我被优化没了?AgileConfig+Yarp替代Ocelot+Consul实现服务发现和自动网关配置
    现在软件就业环境不景气,各行各业都忙着裁员优化。作为一个小开发,咱也不能光等着别人来优化咱,也得想办法优化下自己。就拿手头上的工作来说吧,我发现我的微服务应用里,既有AgileConfig这个配置中心组件,又有一个Consul服务发现组件。本来吧他俩也没啥事,各干个的。但是,我在操作AgileCo......
  • golang实现R6900路由器外网IP更新通知程序
    程序一分钟执行一次,检测路由器外网IP地址变更则自动发送邮件,使用网易126smtp协议发送邮件,邮箱地址及授权码请自行替换,getIp函数中的grep根据自己的网卡信息调试替换R6900路由器的交叉编译语句:CGO_ENABLED=0GOOS=linuxGOARCH=armGOARM=5gobuildxxxx.go1234567......