携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
本次刷题日记的第 93 篇,力扣题为:641. 设计循环双端队列
一、题目描述:
咱今天来做一个设计类的题目,641. 设计循环双端队列
\
二、这道题考察了什么思想?你的思路是什么?
还记得之前我们写过设计循环队列的题目 【刷题日记】622. 设计循环队列,本次我们来设计循环双端队列,实际上这俩题咱们的实现思路差不多,不过我们还是来巩固一下链表的使用吧
-
题目要求我们设计一个双端队列,双端队列,咱们需要实现如下功能
- 构造基本双端队列
- 将数据从队列头插入
- 从队列尾部插入数据
- 删除队头
- 删除队尾
- 获取队头元素
- 获取队尾元素
- 判断队列是否为空
- 判断队列是否满
分析
对于一个队列来说,我们使用链表来实现循环双端队列,先来简单快速的温习一下链表的基本知识
咱们的链表是由一个节点一个节点的串起来的,串起来之后就是是一个链表了
咱们的一个节点有指针域和值域,例如下面这样,这是一个简单的节点
| 前向指针**(指针域)** | 节点本身的值**(值域)** | 后向指针**(指针域)** |
|---|
那么多个节点连接起来就是这个样子的
这个时候,如果是要加上头指针和指针,那就是这样的
咱们这个时候,要是想获取队列头那就就是直接读取 head 指向的节点值即可,同理,要是想读取队列尾部的值,那么就直接读取 tail 指向的节点值即可
那么对于判断一个队列是空,或者是满,咱们直接判断队列的 size 和 cap 的关系就可以了
判断队列为空,即判断 size 是否为 0 即可,判断队列是否为满,则判断 size 和 cap 是否相等即可
对于从队头插入数据和队尾插入数据,我们可以这样来插入
- 新建一个节点,节点的 next 指针指向 head
- head 的 prev 指向新的节点
- head 指针指向新节点的地址上
- size++
从尾部插入一个节点,原理也是一样的,去操作 tail 指针就可了
最后,我们来看一下如何移除链表头和移除链表尾呢?
咱们删除队列头的话,我们可以这样来删除:
- Head 指针指针 head 的下一个节点
- 此时 head 指针的 prev 指针指向空,即 nil
- size--
同样的道理,删除尾巴指针的节点,我们也是这样的做法,只用去操作 tail 指针对应的节点就可以了
至此,咱们就可以按照上面的思路,进行编码的实现代码就可以,来撸代码就可以了
三、编码
根据上述逻辑和分析,我们就可以翻译成如下代码
- 在插入数据,删除数据的时候,注意队列为空,队列为满的时候,我们需要特殊处理一下
编码如下:
type node struct {
prev, next *node
val int
}
type MyCircularDeque struct {
head, tail *node
capacity, size int
}
func Constructor(k int) MyCircularDeque {
return MyCircularDeque{capacity: k}
}
func (q *MyCircularDeque) InsertFront(value int) bool {
if q.IsFull() {
return false
}
node := &node{val: value}
if q.IsEmpty() {
q.head = node
q.tail = node
} else {
node.next = q.head
q.head.prev = node
q.head = node
}
q.size++
return true
}
func (q *MyCircularDeque) InsertLast(value int) bool {
if q.IsFull() {
return false
}
node := &node{val: value}
if q.IsEmpty() {
q.head = node
q.tail = node
} else {
q.tail.next = node
node.prev = q.tail
q.tail = node
}
q.size++
return true
}
func (q *MyCircularDeque) DeleteFront() bool {
if q.IsEmpty() {
return false
}
q.head = q.head.next
if q.head != nil {
q.head.prev = nil
}
q.size--
return true
}
func (q *MyCircularDeque) DeleteLast() bool {
if q.IsEmpty() {
return false
}
q.tail = q.tail.prev
if q.tail != nil {
q.tail.next = nil
}
q.size--
return true
}
func (q MyCircularDeque) GetFront() int {
if q.IsEmpty() {
return -1
}
return q.head.val
}
func (q MyCircularDeque) GetRear() int {
if q.IsEmpty() {
return -1
}
return q.tail.val
}
func (q MyCircularDeque) IsEmpty() bool {
return q.size == 0
}
func (q MyCircularDeque) IsFull() bool {
return q.size == q.capacity
}
四、总结:
对于时间复杂度的话,咱们的这种设计方式比较明确,我们是使用的链表的方式来实现的,因此时间复杂度对于类中每一个接口都是 O(1) ,空间复杂度是 O(k) ,因为咱们的循环双端队列的最大长度为 k
原题地址:641. 设计循环双端队列
今天就到这里,学习所得,若有偏差,还请斧正
欢迎点赞,关注,收藏
朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力
好了,本次就到这里
技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。
我是阿兵云原生,欢迎点赞关注收藏,下次见~