首页 > 其他分享 >队列-经典应用案例

队列-经典应用案例

时间:2024-04-21 21:22:23浏览次数:28  
标签:count return item 队列 案例 frontIndex 经典 obj

这里来简单举几个经典的场景如 "击鼓传花", "字符串回文监测" 等来加深对队列这个结构的直观认识.

单端队列-击鼓传花

这里先介绍一种队列的变种叫 循环队列, 即元素从队首出队后, 立即又进行从队尾入队, 类似行程了一个 , 与之对应的一个经典游戏就是 "击鼓传花", 英文叫 hot potato 烫手的山芋.

在这个游戏中,孩子们围成一个圆圈,把花尽快地传递给旁边的人。某一时刻传花停止,这个时候花在谁手里,谁就退出圆圈、结束游戏。重复这个过程,直到只剩一个孩子(胜者).

更为直接的是我司每年都会进行年会抽奖, 用的就是击鼓传花. 将所有人拉进来, 循环滚动, 中间叫停抽奖, 抽到奖的就移除用户池, 没抽中的继续.

这个 "击鼓传花" 的过程我们可以用队列进行模拟, 用队首表示 potato.

// 用数组模拟队列, 左是队首, 右是队尾
var queue = []

// 假设有 a, b, c, d, e 五个玩家, 先全部从队尾入队
[ a, b, c, d, e]

// 先滚动起来不淘汰哈
// 假设随机数字是 3
[d, e, a, b, c]

// 再假设数字是 4
[b, c, d, e, a]

// 可以看到 "循环" 的效果, 
// 现在开始进行淘汰制, 直到最后剩下1人才结束, 一共需要4轮

// 初始入队, 并假设每轮的数字是恒定的 3
[ a, b, c, d, e]

// 第一轮
[d, e, a, b, c] =>  d 淘汰 => [e, a, b, c]

// 第二轮 
[c, e, a, b] => c 淘汰 => [e, a, b]

// 第三轮
[e, a, b] => e 淘汰 =>[a, b]

// 第四轮
[a, b] => b 淘汰 => [a] => over 

最终经过 4轮角逐, a 笑到了最后哦!

发现如果 传递次数固定, 则是可以通过计算出来谁是最终赢家的, 这里我们用代码来实现一下:

class Queue {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 元素入队
  enqueue(item) {
    this.obj[this.count] = item 
    this.count += 1
  }
  // 队列长度
  size() {
    return this.count - this.frontIndex
  }
  // 队列是否为空
  isEmpty() {
    this.count - this.frontIndex == 0
  }
  // 元素出队
  dequeue() {
    if (this.isEmpty()) return undefined 

    let frontItem = this.obj[this.frontIndex]
    delete this.obj[this.frontIndex]

    this.frontIndex += 1 // 更新队首元素索引
    return frontItem
  }
  // 清空队列
  clear() {
    this.obj = {}
    this.frontIndex = 0
    this.count = 0
  }
  // 查看队首元素
  peek() {
    if (this.isEmpty()) return undefined
    return this.obj[this.frontIndex]
  }
  // 查看全队列
  toString() {
    if (this.isEmpty()) return undefined

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

// 击鼓传花, 传递次数固定
function hotPotato(elementsList, num) {
  const queue = new Queue()
  // 元素全部入队
  for (const item of elementsList) {
    queue.enqueue(item)
  }

  // 开始奏乐, 开始舞, 直到剩下最后一个人
  const loserList = []
  while (queue.size() > 1) {
    for (let i = 0; i < num; i++) {
      // 先出队, 再入队, 等循环次数到了之后, 再进行真正 "离队删除"
      let item = queue.dequeue()
      queue.enqueue(item)
    }
    // 一轮到数, 进行真正的删除
    loserList.push(queue.dequeue())
  }
  // 最后剩下的就是赢家
  return {
    winner: queue.dequeue(),
    loserList: loserList
  }
}


const names = ['a', 'b', 'c', 'd', 'e'];
const results = hotPotato(names, 3)

results.loserList.forEach(name => console.log(`${name} 在击鼓传花中被淘汰!`))
console.log('')
console.log('最终的 winner 是: ', results.winner);

PS F:\algorithms> node queue_case.js
d 在击鼓传花中被淘汰!
c 在击鼓传花中被淘汰!
e 在击鼓传花中被淘汰!
b 在击鼓传花中被淘汰!

最终的 winner 是:  a

双端队列 - 回文检查器

回文是正反序列都是一样的词, 比如:

  • madam
  • 上海自来水来自海上, 山西采煤炭煤采西山

应用方面搜了一下说, 字符串算法的一个广阔的应用领域就是基因序列,比如有了回文串的识别算法,我们就可以在存储基因序列的时候进行压缩,节省时间和空间。

但我感觉更多作为一种游戏玩玩, 或者强行给自己代码上强度吧. 判断字符是否回文, 我们可以用双端队列来实现.

原理就是:

  • 元素先入双端队列
  • 循环: 队首 和 队尾 同时移除元素, 判断如果值相等, 则继续, 不相等则说明非回文, 退出程序
  • 是回文的话, 最后双端队列里只会剩 1 或者 0 个元素 (单双数)
// 字符回文监测

// 假设字符是 madam
// 以数组来模拟队列, 假设左边是队尾
var deque = []

// 从依次从队尾入队
[m, a, d, a, m]

// 分别从两端出队
// 第一轮比较: 
m == m => 继续: [a, d, a]

// 第二轮比较: 
a == a => 继续: [d]

剩一个元素, ok 的, 这个是个回文哦!

用代码来实现就相对简单了.

class Deque {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 队列长度
  size() {
    return this.count - this.frontIndex
  }
  // 队列是否为空
  isEmpty() {
    return 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
  }
  // 队尾添加元素
  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 
function callbackCheck(string) {

  if (typeof string !== 'string') return false 
  if (string.length <= 1) return false 

  const deque = new Deque()
  // 先字符入队
  for (const char of string) {
    deque.addBack(char.toLowerCase())
  }

  while (deque.size() > 1) {
    firstChar = deque.removeFront()
    lastChar = deque.removeBack()

    if (firstChar !== lastChar) return false 
  }
  return true 
}



// test 
console.log(callbackCheck(''));
console.log(callbackCheck('a'));
console.log(callbackCheck('madam'));
console.log(callbackCheck('上海自来水来自海上'));
console.log(callbackCheck('hello'));
console.log(callbackCheck('{}'));

PS F:\algorithms> node deque_case.js
false
false
true
true
false
false

我自己用队列还是蛮多的, 主要是对于那种数据下载的任务, 把它整成一个异步任务, 然后用户的数据下载任务排队处理.

标签:count,return,item,队列,案例,frontIndex,经典,obj
From: https://www.cnblogs.com/chenjieyouge/p/18149521

相关文章

  • 【用户案例】数字化转型中的新质生产力:东风日产的RPA实践与启示
    在数字化时代的浪潮中,企业数字化转型已成为不可逆转的趋势。面对工效联动和数字化转型的双重挑战,传统汽车行业急需寻找新的突破点。东风日产,作为一家拥有 1.9万名员工的汽车企业,为我们展示了如何成功实现内部转型东风日产是东风汽车有限公司旗下的重要乘用车板块,致力于以先进......
  • HarmonyOS NEXT应用开发—翻页动效案例
    介绍翻页动效是应用开发中常见的动效场景,常见的有书籍翻页,日历翻页等。本例将介绍如何通过ArkUI提供的显示动画接口animateTo实现翻页的效果。效果图预览使用说明本例通过setInterval函数每秒调用一次翻页动画,实现连续翻页效果。实现思路如图,左右两侧分别代表打开书籍的......
  • FreeRTOS队列
    FreeRTOS队列在实际的应用中,常常会遇到一个任务或者中断服务需要和另外一个任务进行“沟通交流”,这个“沟通交流”的过程其实就是消息传递的过程。在没有操作系统的时候两个应用程序进行消息传递一般使用全局变量的方式,但是如果在使用操作系统的应用中用全局变量来传递消息就会涉......
  • textfsm 案例分享
    由于安全需要,需要定期对接入层交换机配置进行合规检查,避免不规范配置存在的漏洞给公司网络带来安全风险。如下案例是通过textfsm提取交换机接口的配置信息,进一步进行检查准入配置是否开启:1、首先看接口下的配置interfaceGigabitEthernet1/0/7descriptionuser_0001switc......
  • DM 传统行业SQL优化案例
    来OB这么久还没有接触啥金融的SQL,只能发点其他行业的数据库SQL优化案例。......
  • 解决了这次的消息队列堆积事故,我又解锁了新的认知与思考...
    大家好,我是程序员陶朱公。前言前两天解决了一个线上消息队列堆积事故,在这里做一个复盘与总结,希望我解决问题的方式、方法、手段对你将来遇到类似的事情有一定的帮助与启发。背景前两天,有业务方反馈,他们日常处理的工单数据变少了,希望开发同学去排查一下原因。这里简单的画下我......
  • HarmonyOS NEXT应用开发之下拉刷新与上滑加载案例
    介绍本示例介绍使用第三方库的PullToRefresh组件实现列表的下拉刷新数据和上滑加载后续数据。效果图预览使用说明进入页面,下拉列表触发刷新数据事件,等待数据刷新完成。上滑列表到底部,触发加载更多数据事件,等待数据加载完成。实现思路使用第三方库pullToRefresh组件,将列......
  • 优先队列
    priority_queue默认大顶堆小顶堆includeincludeusingnamespacestd;voidmax_k_num(){intsource_data[10]={3,5,8,1,10,2,9,15,13,16};intk=5;//小根堆priority_queue<int,vector,greater>q;for(auton:source_data){if(q.size()==k){......
  • Go语言高并发与微服务实战专题精讲——远程过程调用 RPC——实践案例:Go 语言 RPC 过程
    远程过程调用RPC——实践案例:Go语言RPC过程调用实践 Go语言的官方RPC库/net/rpc为开发者提供了实现远程过程调用的强大功能,使得通过网络访问对象的方法成为可能。这种机制极大地促进了分布式系统的构建,让不同的服务能够轻松地进行相互通信和协作。 在使用Go的RPC库时,服务......
  • HarmonyOS NEXT应用开发之深色跑马灯案例
    介绍本示例介绍了文本宽度过宽时,如何实现文本首尾相接循环滚动并显示在可视区,以及每循环滚动一次之后会停滞一段时间后再滚动。效果图预览使用说明:1.进入页面,检票口文本处,实现文本首尾相接循环滚动,且在同一可视区,滚动完成之后,停滞一段时间后继续滚动。实现思路由于ArkUI中......