TypeScript_队列结构-链表
队列
队列(Queue),它是一种受限的线性表,先进先出(FIFO First In First Out)
- 受限之处在于它只允许在队列的前端(front)进行删除操作
- 而在队列的后端(rear)进行插入操作
队列
- 有五份文档需要打印,这些文档会按照次序放入到打印队列中
- 打印机会依次从队列中取出文档,优先放入的文档,优先被取出,并且对该文档进行打印
- 以此类推,直到队列中不再有新的文档
线程队列:
- 在开发中,为了让任务可以并行处理,通常会开启多个线程
- 但是,我们不能让大量的线程同时运行处理任务。(占用过多的资源)
- 这个时候,如果有需要开启线程处理任务的情况,我们就会使用线程队列
- 线程队列会依照次序来启动线程,并且处理对应的任务
当然队列还有很多其他应用,我们后续的很多算法中也会用到队列(比如二叉树的层序遍历)
队列操作
- enqueue(element):向队列尾部添加一个(或多个)新的项
- dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素
- front/peek():返回队列中第一个元素————最先被添加,也将是最先被移除的元素。队列不做任何变动 (不移除元素,只返回元素信息一一与 Stack 类的 peek 方法非常类似)
- isEmpty():如果队列中不包含任何元素,返回 true,否则返回 false
- size():返回队列包含的元素个数,与数组的 length 属性类似
实现队列
队列的实现和栈一样,有两种方案:
- 基于 数组 实现
- 基于 链表 实现
import IQueue from './IQueue'class ArrayQueue<T> implements IQueue<T> {// 内部是通过数组保存private data: T[] = []enqueue(element: T): void {this.data.push(element)}dequeue(): T | undefined {return this.data.shift()}peek(): T | undefined {return this.data[0]}isEmpty(): boolean {return this.data.length === 0}size(): number {return this.data.length}
}export default ArrayQueue
泛型
interface IList<T> {peek(): T | undefinedisEmpty(): booleansize(): number
}
interface IQueue<T> extends IList<T> {enqueue(element: T): voiddequeue(): T | undefined
}
击鼓传花
原游戏规则
- 班级中玩一个游戏,所有学生围成一圈,从某位同学手里开始向旁边的同学传一束花
- 这个时候某个人(比如班长),在击鼓,鼓声停下的一颗,花落在谁手里,谁就出来表演节目
修改游戏规则
- 我们来修改一下这个游戏规则
- 几个朋友一起玩一个游戏,围成一圈,开始数数,数到某个数字的人自动淘汰
- 最后剩下的这个人会获得胜利,请问最后剩下的是原来在哪一个位置上的人
封装一个基于队列的函数
- 参数:所有参与人的姓名,基于的数字口结果:最终剩下的一人的姓名
function hotPotato(names: string[], num: number) {if (names.length === 0) return -1// 1.创建队列结构const queue = new ArrayQueue<string>()// 2.将所有的name入队操作for (const name of names) {queue.enqueue(name)}// 3.淘汰规则while (queue.size() > 1) {for (let i = 1; i < num; i++) {const name = queue.dequeue()if (name) queue.enqueue(name)}queue.dequeue()}return queue.dequeue()
}
约瑟夫环问题
阿桥问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环
- 人们站在一个等待被处决的圈子里
- 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行
- 在跳过指定数量的人之后,处刑下一个人
- 对剩下的人重复该过程,从下一个人开始,朝同一方向跳过相同数量的人,直到只剩下一个人,并被释放
- 在给定数量的情况下,站在第几个位置可以避免被处决?
这个问题是以弗拉维奥·约瑟夫命名的,他是1世纪的一名犹太历史学家口
- 他在自己的日记中写道,他和他的40个战友被罗马军队包围在洞中
- 他们讨论是自杀还是被俘,最终决定自杀,并以抽签的方式决定谁杀掉谁
剑指 Offer 62. 圆圈中最后剩下的数字
0,1,···,n-1 这 n 个数字排成一个圆圈,从数字 0 开始,每次从这个圆圈里删除第 m 个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字
例如,0、1、2、3、4 这 5 个数字组成一个圆圈,从数字 0 开始每次删除第 3 个数字,则删除的前 4 个数字依次是 2、0、4、1,因此最后剩下的数字是 3
import ArrayQueue from './queue'function lastRemaining(n: number, m: number) {// 1.创建队列const queue = new ArrayQueue<number>()// 2.将所有的数字加入队列中for (let i = 0; i < n; i++) {queue.enqueue(i)}// 3.判断队列中是否还有数字while (queue.size() > 1) {for (let i = 1; i < m; i++) {queue.enqueue(queue.dequeue()!)}queue.dequeue()}return queue.dequeue()
}console.log(lastRemaining(5, 3)) // 3
console.log(lastRemaining(10, 17)) // 2
动态规划
function lastRemaining(n: number, m: number) {let position = 0for (let i = 2; i <= n; i++) {position = (position + m) % i}return position
}
链表
链表与数组
链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同
数组有很多缺点:
- 数组的创建通常需要申请一段 连续的内存空间(一整块的内存),并且大小是固定的(大多数编程语言数组都是固定的),所以当当前数组 不能满足容量需求 时,需要 扩容。(一般情况下是申请一个更大的数组,比如 2 倍。然后将原数组中的元素复制过去)
- 而且在 数组开头或中间位置插入数据的成本很高,需要 进行大量元素的位移
- 尽管 JavaScript 的 Array 底层可以帮我们做这些事,但背后的原理依然是这样
要存储多个元素,另外一个选择就是链表
- 但不同于数组,链表中的元素在内存中不必是连续的空间
- 链表的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(有些语言称为指针或者链接)组成
相对于数组,链表有一些优点:
- 内存空间不是必须连续的
- 可以充分利用计算机的内存,实现灵活的内存动态管理口
- 链表不必在创建时就 确定大小,并且大小可以 无限的延伸 下去口
- 链表在 插入和删除 数据时,时间复杂度 可以达到 O(1)
- 相对数组效率高很多
相对于数组,链表有一些缺点:
- 链表访问任何一个位置的元素时,都需要 从头开始访问。(无法跳过第一个元素访问任何一个元素)
- 无法通过下标直接访问元素,需要从头一个个访问,直到找到对应的元素
链表
什么是链表呢?
- 其实上面我们已经简单的提过了链表的结构,我们这里更加详细的分析一下
- 链表类似于火车:有一个火车头,火车头会连接一个节点,节点上有乘客(类似于数据),并且这个节点会连接下一个节点,以此类推


实现链表
- 封装一个 Node 类,用于封装每一个节点上的信息 (包括值和指向下一个节点的引用),它是一个泛型类
- 封装一个 LinkedList 类,用于表示我们的链表结构。(和 Java 中的链表同名,不同 Java 中的这个类是一个双向链表)
- 链表中我们保存两个属性,一个是链表的长度,一个是链表中第一个节点
class Node<T> {value: Tnext: Node<T> | null = nullconstructor(value: T) {this.value = value}
}class LinkedList<T> {private head: Node<T> | null = nullprivate size: number = 0get length() {return this.size}
}
链表操作
- append(element):向链表尾部添加一个新的项
- insert(position,element):向链表的特定位置插入一个新的项
- get(position):获取对应位置的元素indexOf(element): 返回元素在链表中的索引。如果链表中没有该元素则返回 -1
- update(position,element):修改某个位置的元素
- removeAt(position):从链表的特定位置移除一项
- remove(element):从链表中移除一项
- isEmpty():如果链表中不包含任何元素,返回 true,如果链表长度大于 0 则返回 false
- size():返回链表包含的元素个数。与数组的 length 属性类似
向链表尾部追加数据可能有两种情况
- 链表本身为空,新添加的数据时唯一的节点
- 链表不为空,需要向其他节点后面追加节点
class Node<T> {value: Tnext: Node<T> | null = nullconstructor(value: T) {this.value = value}
}class LinkedList<T> {private head: Node<T> | null = nullprivate size: number = 0get length() {return this.size}// 根据position获取到当前的节点(不是节点的value,而是获取节点)private getNode(position: number): Node<T> | null {let index = 0let current = this.headwhile (index++ < position && current) {current = current.next}return current}append(value: T) {// 1.根据value创建一个新及诶单const newNode = new Node(value)// 2.判断this.heade是否为nullif (!this.head) {this.head = newNode} else {let current = this.headwhile (current.next) {current = current.next}current.next = newNode}this.size++}traverse() {const values: T[] = []let current = this.headwhile (current) {values.push(current.value)current = current.next}console.log(values.join('->'))}insert(value: T, position: number): boolean {// 1.越界的判断if (position < 0 || position > this.size) return false// 2.根据value创建新的节点const newNode = new Node(value)// 3.判断是否需要插入头部if (position === 0) {// 新节点next指向头部节点、头部newNode.next = this.headthis.head = newNode} else {const previous = this.getNode(position - 1)newNode.next = previous?.next ?? nullprevious!.next = newNode}this.size++return true}removeAt(position: number): T | null {// 1.越界的判断if (position < 0 || position >= this.size) return null// 2.判断是否是删除第一个节点let current = this.headif (position === 0) {this.head = current?.next ?? null} else {const previous = this.getNode(position - 1)previous!.next = previous?.next?.next ?? null}this.size--return current?.value ?? null}get(position: number): T | null {// 1.越界的判断if (position < 0 || position >= this.size) return null// 2.查找元素,并且返回元素return this.getNode(position)?.value ?? null}update(value: T, position: number): boolean {if (position < 0 || position >= this.size) return falseconst currentNode = this.getNode(position)// 获取对应位置的节点,直接更新即可currentNode!.value = valuereturn true}indexOf(value: T): number {let current = this.headlet index = 0while (current) {if (current.value === value) {return index}current = current.nextindex++}return -1}remove(value: T): T | null {const index = this.indexOf(value)return this.removeAt(index)}isEmpty() {return this.size === 0}
}
设计链表
707. 设计链表
你可以选择使用单链表或者双链表,设计并实现自己的链表
单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用
如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始
实现 MyLinkedList 类:
MyLinkedList()初始化MyLinkedList对象int get(int index)获取链表中下标为index的节点的值。如果下标无效,则返回-1void addAtHead(int val)将一个值为val的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点void addAtTail(int val)将一个值为val的节点追加到链表中作为链表的最后一个元素void addAtIndex(int index, int val)将一个值为val的节点插入到链表中下标为index的节点之前。如果index等于链表的长度,那么该节点会被追加到链表的末尾。如果index比长度更大,该节点将 不会插入 到链表中void deleteAtIndex(int index)如果下标有效,则删除链表中下标为index的节点
抽离接口方法
interface IList<T> {peek(): T | undefinedisEmpty(): booleansize(): number
}interface ILinkedList<T> extends IList<T> {append(value: T): voidtraverse(): voidinsert(value: T, position: number): booleanget(position: number): T | nullupdate(value: T, position: number): booleanindexOf(value: T): numberremove(value: T): T | null
}
删除链表中的节点
237. 删除链表中的节点
有一个单链表的 head,我们想删除它其中的一个节点 node
给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head
链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点
删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:
- 给定节点的值不应该存在于链表中
- 链表中的节点数应该减少 1
node前面的所有值顺序相同node后面的所有值顺序相同
class ListNode {val: numbernext: ListNode | nullconstructor(val?: number, next?: ListNode | null) {this.val = val === undefined ? 0 : valthis.next = next === undefined ? null : next}
}function deleteNode(node: ListNode | null): void {node!.val = node!.next!.valnode!.next = node!.next!.next
}
反转链表
206. 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表
利用栈结构解决
function reverseList(head: ListNode | null): ListNode | null {if (head === null) return nullif (head.next === null) return headconst stack: ListNode[] = []let current: ListNode | null = headwhile (current) {stack.push(current)current = current.next}let newHead: ListNode = stack.pop()!let newHeadCurrent = newHeadwhile (stack.length) {const node = stack.pop()!newHeadCurrent.next = nodenewHeadCurrent = newHeadCurrent.next}// 注意:获取到最后一个节点时,一定要将节点的 next 置为 nullnewHeadCurrent.next = nullreturn newHead
}const node1 = new ListNode(1)
node1.next = new ListNode(2)
node1.next.next = new ListNode(3)
const newHead = reverseList(node1)
let current = newHead
while (current) {console.log(current.val)current = current.next
}
非递归方式
-
让 current 指向下一个节点
- 目的:保留着下一个节点的引用,可以拿到,并且不会销毁
-
改变 head 当前指向的节点,指向 newHead
- 对于第一个节点来说,指向 newHead 就是指向 null
-
让 newHead 指向 head 节点
- 目的是下一次遍历时,第二步操作,可以让下一个节点指向第一个节点
-
让 head 移向下一个节点,指向 current
function reverseList(head: ListNode | null): ListNode | null {if (head === null || head.next === null) return headlet newHead: ListNode | null = nullwhile (head) {let current: ListNode | null = head.nexthead.next = newHeadnewHead = headhead = current}return newHead
}

递归方式
- 第一次进入
const newHead下面的代码是倒数第二个节点,因为倒数第一个节点的 next 为 null
function reverseList(head: ListNode | null): ListNode | null {if (head === null || head.next === null) return headconst newHead = reverseList(head.next)head.next.next = headhead.next = nullreturn newHead
}
相关文章:
TypeScript_队列结构-链表
队列 队列(Queue),它是一种受限的线性表,先进先出(FIFO First In First Out) 受限之处在于它只允许在队列的前端(front)进行删除操作而在队列的后端(rear)进…...
STM32G0 定时器PWM DMA输出驱动WS2812配置 LL库
通过DMA方式输出PWM模拟LED数据信号 优点:不消耗CPU资源 缺点:占用内存较大 STM32CUBEMX配置 定时器配置 定时器通道:TIM3 CH2 分频:0 重装值:79,芯片主频64Mhz,因此PWM输出频率:…...
记录错误:Access denied for user ‘root‘@‘localhost‘ (using password:No) 解决方案
他说我没输入密码,但是我输入了啊??于是,我试了试这儿,password 一改就好了。。。 他原来是是我打的很快,快速生成的。。。。...
python爬虫实战(5)--获取小破站热榜
1. 分析地址 打开小破站热榜首页,查看响应找到如下接口地址 2. 编码 定义请求头 拿到标头 复制粘贴,处理成json 处理请求头代码如下: def format_headers_to_json():f open("data.txt", "r", encoding"utf-8") # 读…...
单目标应用:基于麻雀搜索算法SSA的微电网优化调度MATLAB
一、微网系统运行优化模型 参考文献: [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、麻雀搜索算法简介 麻雀搜索算法 (Sparrow Search Algorithm, SSA) 是一种新型的群智能优化算法,于2020…...
C# easymodbus
库介绍 EasyModbus是用于 .NET 和 Java 平台上的Modbus TCP/UDP/RTU通讯协议库,支持多种编程语言,如C#、VB.NET、Java、C 与更多C#的变体,如Unity、Mono、.NET Core等等。 EasyModbus的Java版本至少需要Java 7,而C#版本兼容 .NE…...
HikariCP源码修改,使其连接池支持Kerberos认证
HikariCP-4.0.3 修改HikariCP源码,使其连接池支持Kerberos认证 修改后的Hikari源码地址:https://github.com/Raray-chuan/HikariCP-4.0.3 Springboot使用hikari连接池并进行Kerberos认证访问Impala的demo地址:https://github.com/Raray-chuan/springboot-kerberos-hikari-im…...
5分钟看明白rust mod use
rust把mod简单的事没说清,一片混乱,似懂非懂. mod语句查找只有一条规则:先找mod名1.rs,没有就我同名文件夹下的mod名1.rs,如果没有,就同名文件夹下的mod名1/mod.rs,再没有就error. 在mod.rs中,pub mod 文件…...
【Java核心知识】ThreadLocal相关知识
ThreadLocal 什么是ThreadLocal ThreadLoacal类可以为每个线程保存一份独有的变量,该变量对于每个线程都是独占的。实现原理为每个Thread类中包含一个ThreadHashMap,key为变量的对应的ThreadLocal对象,value为变量的值。 在日常使用中&…...
《Python基础教程(第三版)》阅读笔记 1
目录 1 快速上手:基础知识2 列表和元组3 字符串4 字典5 条件、循环及其他6 抽象7 再谈抽象8 异常9 魔法方法、特性和迭代器10 开箱即用 本文参考自《Beginning Python: from novice to professional》,中文版为《Python基础教程(第三版&#…...
坦克400 Hi4-T预售价28.5万元起,越野新能源好理解
8月25日,在以“智享蓉城,驭见未来”为主题的成都国际车展上,坦克品牌越野新能源再启新程,首次以全Hi4-T新能源阵容亮相展台,释放坦克品牌加速布局越野新能源的强烈信号。 Hi4-T架构首款落地车型坦克500 Hi4-T上市至今斩…...
我的Vim学习笔记(不定期更新)
2023年9月3日,周日上午 学到了啥就写啥,不定期更新 目录 字体 文件 标签页 分屏 调用系统命令 字体 设置字体大小 :set guifont字体:h字体大小 例如,:set guifontMonospace:h20 查询当前使用的字体和字体大小 :set guifont? 查看…...
spring boot项目生成容器并运行
一个安静的周末,shigen又睡懒觉了,上次说的拖延症的惩罚来了:早晚各100个健腹轮练习,早上的已经完成了。今天的文章来的有点晚,但是依旧保持质量。 springboot项目生成容器并运行 背景 将springboot项目打包成jar包&…...
Vue之html中特殊符号的展示
Vue之html中特殊符号的展示 在html中使用特殊字符时直接展示会报错,需要使用实体名称或者实体编号才能展示。 最常用的字符实体 显示结果 描述 实体名称 实体编号空格 < 小于号 < &…...
数据结构1 -- leetcode练习
三. 练习 3.1 时间复杂度 用函数 f ( n ) f(n) f(n) 表示算法效率与数据规模的关系,假设每次解决问题需要 1 微秒( 1 0 − 6 10^{-6} 10−6 秒),进行估算: 如果 f ( n ) n 2 f(n) n^2 f(n)n2 那么 1 秒能解决多…...
Java设计模式:四、行为型模式-05:备忘录模式
文章目录 一、定义:备忘录模式二、模拟场景:备忘录模式三、改善代码:备忘录模式3.1 工程结构3.2 备忘录模式模型结构图3.3 备忘录模式定义3.3.1 配置信息类3.3.2 备忘录类3.3.3 记录者类3.3.4 管理员类 3.4 单元测试 四、总结:备忘…...
MongoDB实验——MongoDB配置用户的访问控制
MongoDB 配置用户的访问控制 一、 实验原理 理解admin数据库:安装MongoDB时,会自动创建admin数据库,这是一个特殊数据库,提供了普通数据库没有的功能,例如,有些账户角色赋予用户操作多个数据库的权限&…...
golang逃逸技术分析
“ 申请到栈内存好处:函数返回直接释放,不会引起垃圾回收,对性能没有影响。 申请到堆上面的内存才会引起垃圾回收。 func F() { a : make([]int, 0, 20) b : make([]int, 0, 20000) l : 20 c : make([]int, 0, l)} “ a和b代码一样࿰…...
说说你了解的 Nginx
分析&回答 nginx性能数据 高并发连接: 官方称单节点支持5万并发连接数,实际生产环境能够承受2-3万并发。内存消耗少: 在3万并发连接下,开启10个nginx进程仅消耗150M内存 (15M10150M) 1. 正向、反向代理 所谓“代理”,是指在内网边缘 …...
SpringWeb(SpringMVC)
目录 SpringWeb介绍 搭建 SpringWeb SpringWeb介绍 Spring Web是一个基于 Servlet API 构建的原始 web 框架,用于构建基于MVC模式的Web应用程序。在 web 层框架历经 Strust1,WebWork,Strust2 等诸多产品的历代更选 之后,目前业界普…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
