数据结构-队列
Posted newbase
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构-队列相关的知识,希望对你有一定的参考价值。
数据结构
队列&栈
队列---先入先出(FIFO)的数据结构
队列是典型的 FIFO 数据结构。插入(insert)操作也称作入队(enqueue),新元素始终被添加在队列的末尾
(tail)。 删除(delete)操作也被称为出队(dequeue)。 你只能移除第一个元素
(head)。
用数组实现循环队列(取模)
下面我用golang来实现循环队列,但需要注意,程序中tailIndex
所指引的是最后一个插入的元素(即真实的队尾元素),而非插入元素的下一个位置(许多其他实现采用,详见B站视频)。这有很大区别,此时 队尾与队首的关系为:
tailIndex = (headIndex+count?1) % capacity
新增元素:
tailIndex = (tailIndex+1) % capacity
---> tailIndex = (headIndex+count) % capacity
删除元素:
headIndex = (headIndex+1) % capacity
下面图表示队列容量为5,长度为3实现时head与tail所指位置:
程序利用了取模运算,共定义了四个属性:数组;队首索引;队列长度;队列容量
/*
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
*/
type MyCircularQueue struct {
queue []int // 一个固定大小的数组,用于保存循环队列的元素
headIndex int // 一个整数,保存队首 head 的索引
count int // 循环队列当前的长度,即循环队列中的元素数量。
// tailIndex = (headIndex+count?1) % capacity , 因此不需要队尾属性
capacity int // 循环队列的容量,即队列中最多可以容纳的元素数量。
}
/** Initialize your data structure here. Set the size of the queue to be k. */
func Constructor(k int) MyCircularQueue {
return MyCircularQueue{
queue: make([]int, k, k),
headIndex: 0,
count: 0,
capacity: k,
}
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
// 向循环队列插入一个元素。如果成功插入则返回真
func (m *MyCircularQueue) EnQueue(value int) bool {
if m.IsFull() {
return false
}
// 既定公式,令队尾+1 = value
/*
这里需要注意,是令队尾的下一个元素 = value
即: m.queue[(m.headIndex+m.count-1+1)%m.capacity] = value
*/
m.queue[(m.headIndex+m.count)%m.capacity] = value
m.count++
return true
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
// 从循环队列中删除一个元素。如果成功删除则返回真
func (m *MyCircularQueue) DeQueue() bool {
if m.IsEmpty() {
return false
}
m.headIndex = (m.headIndex + 1) % m.capacity
m.count--
return true
}
/** Get the front item from the queue. */
// 从队首获取元素。如果队列为空,返回 -1
func (m *MyCircularQueue) Front() int {
if m.IsEmpty() {
return -1
}
return m.queue[m.headIndex]
}
/** Get the last item from the queue. */
// 从队尾获取元素。如果队列为空,则返回 -1
func (m *MyCircularQueue) Rear() int {
if m.IsEmpty() {
return -1
}
return m.queue[(m.headIndex+m.count-1)%m.capacity]
}
/** Checks whether the circular queue is empty or not. */
// 检查循环队列是否为空
func (m *MyCircularQueue) IsEmpty() bool {
return m.count == 0
}
/** Checks whether the circular queue is full or not. */
// 检查循环队列是否已满
func (m *MyCircularQueue) IsFull() bool {
return m.count == m.capacity
}
/**
* Your MyCircularQueue object will be instantiated and called as such:
* obj := Constructor(k);
* param_1 := obj.EnQueue(value);
* param_2 := obj.DeQueue();
* param_3 := obj.Front();
* param_4 := obj.Rear();
* param_5 := obj.IsEmpty();
* param_6 := obj.IsFull();
*/
用数组实现循环队列(size)
这种方法略微取巧,不需要取模运算,但是实际运算速度也差不太多,还不容易理解.....
判空:head == tail
判满:tail + 1 == head,数组越界时需要转换
有两点需要额外注意:
- head或tail的数组越界转换问题:在移动过程中,若是越界则需要转换到有效下标值处
- 访问队尾元素时的越界问题:队尾元素的下标是tail- 1,因此若是tail=0,则需要进行有效下标转换
程序共定义了四个属性:数组;队首索引;队尾索引;队列长度(多加一个用于给tail指针站位)
直接上代码:
/*
MyCircularQueue(k): 构造器,设置队列长度为 k 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
*/
// head为序列元素的上标,即队首,tail为序列的下标,即队尾,size为数组长度
type MyCircularQueue struct {
data []int
head int
tail int
size int
}
/** Initialize your data structure here. Set the size of the queue to be k. */
func Constructor(k int) MyCircularQueue {
return MyCircularQueue{
data: make([]int, k + 1, k + 1), // 确定data长度,容量
head: 0,
tail: 0,
size: k + 1, // 多加一个用于给tail指针站位
}
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
// 向循环队列插入一个元素。如果成功插入则返回真
func (m *MyCircularQueue) EnQueue(value int) bool {
// 如果序列已经满了,返回false
if m.IsFull() {
return false
}
// 向数组起始位置插入value,下标+1
m.data[m.tail] = value
m.tail++
// 如果下标达到数组最后一个元素位置(等于size长度,这个位置是为tail准备的,不存储元素)下标归0 ---> 开始循环
if m.tail == m.size {
m.tail = 0
}
return true
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
// 从循环队列中删除一个元素。如果成功删除则返回真。
func (m *MyCircularQueue) DeQueue() bool {
// 如果队列是空的,返回false
if m.IsEmpty() {
return false
}
// 序列 head+1,如果到头了就归0 ---> 循环 <原理同上>
m.head++
if m.head == m.size {
m.head = 0
}
return true
}
/** Get the front item from the queue. */
// 从队首获取元素。如果队列为空,返回 -1
func (m *MyCircularQueue) Front() int {
if m.IsEmpty() {
return -1
}
return m.data[m.head]
}
/** Get the last item from the queue. */
// 获取队尾元素。如果队列为空,返回 -1
func (m *MyCircularQueue) Rear() int {
if m.IsEmpty() {
return -1
}
// 边界问题,tail独占一个空间,因此要取的index为tail前一个位置
lastIndex := m.tail - 1
if lastIndex < 0 {
lastIndex = m.size - 1
}
return m.data[lastIndex]
}
/** Checks whether the circular queue is empty or not. */
// 检查队列是否为空
func (m *MyCircularQueue) IsEmpty() bool {
// 只要相等,队列即为空
return m.head == m.tail
}
/** Checks whether the circular queue is full or not. */
// 检查队列是否已满
func (m *MyCircularQueue) IsFull() bool {
// tail + 1 == head 时队列已满
next := m.tail + 1
// 越界问题处理
if next == m.size {
next = 0
}
return next == m.head
}
/**
* Your MyCircularQueue object will be instantiated and called as such:
* obj := Constructor(k);
* param_1 := obj.EnQueue(value);
* param_2 := obj.DeQueue();
* param_3 := obj.Front();
* param_4 := obj.Rear();
* param_5 := obj.IsEmpty();
* param_6 := obj.IsFull();
*/
队列和广度优先搜索(BFS)
待定吧,慢慢来
以上是关于数据结构-队列的主要内容,如果未能解决你的问题,请参考以下文章