<数据结构基础学习>(三)Part 2 队列
一.队列 Queue
队列也是一种线性结构
相比数组,队列对应的操作是数组的子集
只能从一端(队尾)添加元素,只能从另一端(队首)取出元素。
(排队)
队列是一种先进先出的数据结构(先到先得)FIFO(First In First Out)
二.数组队列的实现(基于动态数组)
Interface Queue<E> 接口设置5个方法
void enqueue(E e) 入队 O(1) 均摊
E dequeue() 出队 O(n) (出队后会将后续的元素向前挪一个单位,所以在出队操作上性能比较差)
E getFront() 查看队首元素(只对队首的元素感兴趣) O(1)
int getSize() 队列中的元素个数 O(1)
boolean isEmpty() 队列是否为空 O(1)
public interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E e);
E dequeue();
E getFront();
}
ArrayQueue<E>实现接口Interface Queue<E>
public class ArrayQueue<E> implements Queue<E>
1.基本的构造方法
private Array<E> array;
public ArrayQueue(int capacity){
array = new Array<>(capacity);
} public ArrayQueue(){
array = new Array<>();
} @Override
public int getSize() {
return array.getSize();
} @Override
public boolean isEmpty() {
return array.isEmpty();
} public int getCapacity(){
return array.getCapacity();
}
2.入队
@Override
public void enqueue(E e) {
array.addLast(e);
}
3.出队
@Override
public E dequeue() {
return array.removeFirst();
}
4.查看队首元素
@Override
public E getFront() {
return array.getFirst();
}
5.重写toString()方法,使输出更具有可读性
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Queue:");
res.append("front [");
for( int i = 0 ; i < array.getSize() ; i ++){
res.append(array.get(i));
if(i != array.getSize() - 1)
res.append(", ");
}
res.append("]");
return res.toString();
}
数组队列的问题:出队时间复杂度为O(n)级别(底层实现过程:取出队首元素,后续的元素要依次向前挪一位)
三.循环队列
初始时,front=tail=0,front指向队列第一个元素,tail指向队尾最后一个元素的下一位。只有队列中没有元素的情况,front才会与tail相等。
Eg:
1.基本情况
入队操作(对tail进行维护,即tail++,front不动)
a入队,front=0 tail++即tail=1
front [a]
b入队,front=0 tail++即tail=2
front [a, b]
c入队,front=0 tail++即tail=3
front [a, b, c]
出队操作(对front进行维护,即front++,tail不动)
a出队,tail=3 front++即front=1
front [b, c]
b出队,tail=3 front++即front=2
front[c]
c出队,tail=3 front++即front=3
数组为空,此时front == tail
2.循环(数组看为一个环)
假设capacity=8,0 1 2 3 4 5 位置均有元素 front=0,tail=6
先进行两次出队操作,2 3 4 5 位置有元素 front=2,tail=6
在进行两次入队操作,2 3 4 5 6 7位置有元素,此时size=capacity=8,即已将队列的空间加满,但队首前面部分有进行出队操作后空出来的2个单位的空间
再进行入队操作时,进行循环,该元素就加到0位置的元素,此时 front=2,tail=1,此时0 2 3 4 5 6 7位置有元素,1位置无元素。
若再进行入队操作,front=tail=2,与上边front==tail对列为空的定义冲突,所以再进行入队操作就需要扩容,1位置上始终无元素,即capacity中有意识地浪费一个空间。
所以,有当(tail+1)% capacity = front表示队列已满,需要扩容,但实际capacity浪费了一个空间无元素。
所以总结如下:
当front == tail时队列为空。
当(tail+1)% capacity == front时表示队列已满,需要扩容,但实际capacity浪费了一个空间。
四.循环队列的实现
LoopQueue<E>
void enqueue(E e) O(1) 均摊
E dequeue() O(1) 均摊
E getFront() O(1)
int getSize() O(1)
boolean isEmpty() O(1)
创建一个LoopQueue类实现Queue接口
public class LoopQueue<E> implements Queue<E>
1.基本的构造方法
需要注意的几点:
a.除了初始化数组以外,还要初始化front和tail作为首元素和末尾元素的下一位的指向,size实际可以由front与size得到。
b.当用户初始化输入capacity时,需要在构造函数中对capacity进行+1的操作,因为实际capacity中浪费了一个空间。
c.同理,getCapacity()方法就需要得到的data.length进行-1的操作。
d.对于判断队列是否为空的方法判断标准则为 front == tail。
private E[] data;
private int front, tail;
private int size; public LoopQueue(int capacity){
data = (E[])new Object[capacity + 1]; //浪费了一个单位 需要+1
front = 0;
tail = 0;
size = 0;
} public LoopQueue(){
this(10);
} public int getCapavity(){
return data.length - 1; //浪费了一个单位的空间,实际空间需要-1
} @Override
public boolean isEmpty() {
return front == tail;
} @Override
public int getSize() {
return size;
}
2.resize()方法
在进行入队和出队时,有时候需要进行必要的resize()操作来改变循环队列的capacity
需要注意:
a.初始化newData时,容量为newCapacity+1
b.两种遍历方式
c.front =0;
tail = size;
private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity + 1];
for(int i = 0 ; i < size ; i ++){
newData[i] = data[ (i + front) % data.length ];
}
// for(int i = front ; i != tail ; i = (i+1) % data.length){
// newData[i] = data[i];
// }
data = newData;
front = 0;
tail = size;
}
3.入队
需要注意:
a.首先判断队列是否满,满的话进行扩容,扩容时不可使用data.length,因为data.length比getCapacity()方法多1
b.tail = (tail + 1) % data.length 而不是 tail ++;
c.维护size,进行size++
@Override
public void enqueue(E e) {
if((tail + 1) % data.length == front) {
resize(2 * getCapacity()); //没有使用data.length,是因为data.length比getCapacity多1
} data[tail] = e;
tail = (tail + 1) % data.length;
size ++;
}
4.出队
需要注意:
a.首先判断队列是否为空
b.先取出data[front]给res,再使data[front] = null,避免浪费空间
c.移动front front = (front + 1) % data.length,而不是front ++
d.维护size,进行 size --
e.进行判断缩容
@Override
public E dequeue() {
if(isEmpty()){
throw new IllegalArgumentException("Cannot dequeue from an empty queue");
}
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size --;
if(size == getCapacity() / 4 && getCapacity() / 2 != 0){
resize(getCapacity() / 2);
}
return ret; }
5.查看队首元素
@Override
public E getFront() {
if(isEmpty()){
throw new IllegalArgumentException("Queue is empty");
}
return data[front];
}
6.重写toString()方法,使输出更具有可读性
需要注意:
a.输出capacity为getCapacity(),而不是data.length
b.两种遍历方式
public String toString() {
StringBuilder res = new StringBuilder();
res.append(String.format("Queue: size = %d , capacity = %d%n", size, getCapacity())); //使用getCapacity()而不是data.length
res.append("front [");
for(int i = front ; i != tail; i = (i + 1) % data.length){
res.append(data[i]);
if( (i + 1)% data.length != tail)
res.append(", ");
}
// for(int i = 0 ; i < size ; i ++){
// res.append(data[( i + front ) % data.length]);
// if((i + front) % data.length != tail - 1 )
// res.append(", ");
// }
res.append("] tail");
return res.toString();
}
总结:
循环队列复杂主要复杂在三点:
a.getCapacity()方法得到的为data.length-1
b.将队列看为环进行循环,front和tail的变化,尤其是tail的变化
c.在方法中对整个队列的遍历需要考虑循环因素的影响
但是出队的复杂度为O(1)使循环队列在出队时有很好的性能。
对于循环队列的逻辑和代码还需要好好理解,尤其是遍历部分。
<数据结构基础学习>(三)Part 2 队列的更多相关文章
- C语言数据结构基础学习笔记——栈和队列
之前我们学过了普通的线性表,接下来我们来了解一下两种特殊的线性表——栈和队列. 栈是只允许在一端进行插入或删除的线性表. 栈的顺序存储结构也叫作顺序栈,对于栈顶指针top,当栈为空栈时,top=-1: ...
- Python入门基础学习 三
Python入门基础学习 三 数据类型 Python区分整型和浮点型依靠的是小数点,有小数点就是浮点型. e记法:e就是10的意思,是一种科学的计数法,15000=1.5e4 布尔类型是一种特殊的整形 ...
- Python基础学习三
Python基础学习三 1.列表与元组 len()函数:可以获取列表的元素个数. append()函数:用于在列表的最后添加元素. sort()函数:用于排序元素 insert()函数:用于在指定位置 ...
- 算法与数据结构基础 - 堆(Heap)和优先级队列(Priority queue)
堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值: 图片来源:这里 堆可以用数组存储,插入.删除会触发节点shift_down.shif ...
- Django基础学习三_路由系统
今天主要来学习一下Django的路由系统,视频中只学了一些皮毛,但是也做下总结,主要分为静态路由.动态路由.二级路由 一.先来看下静态路由 1.需要在project中的urls文件中做配置,然后将匹配 ...
- <数据结构基础学习>(三)Part 1 栈
一.栈 Stack 栈也是一种线性的数据结构 相比数组,栈相对应的操作是数组的子集. 只能从一端添加元素,也只能从一端取出元素.这一端成为栈顶. 1,2,3依次入栈得到的顺序为 3,2,1,栈顶为3, ...
- <数据结构基础学习>(四)链表 Part 1
一.链表基础 动态数组.栈.队列底层都是依托静态数组实现的,靠resize来解决固定容量问题. 链表是真正的动态数据结构,是一种最简单的一种动态数据结构. 更深入的理解引用(或者指针). 更深入的理解 ...
- Java基础学习(三)—面向对象(上)
一.理解面向对象 面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节:这种思想是将数据作为第一位,而方法或者说是 ...
- salesforce lightning零基础学习(三) 表达式的!(绑定表达式)与 #(非绑定表达式)
在salesforce的classic中,我们使用{!expresion}在前台页面展示信息,在lightning中,上一篇我们也提及了,如果展示attribute的值,可以使用{!v.expresi ...
随机推荐
- 动态BGP和静态BGP的含义与区别
1.在华为云上选购虚拟机时,会让用户选择动态BGP还是静态BGP, 全动态BGP可根据设定的寻路协议第一时间自动优化网络结构,以保持客户使用的网络持续稳定.高效. 静态BGP中的网络结构发生变化,运营 ...
- Android的WebView调试工具(无需Fan墙,可同时调试多个设备,永不过期)
缘起 前端开发离不开Chrome的开发者工具,尤其是调试Android WebView时.然而,如果使用chrome://Inspect的方法,国内的开发者会惊奇地发现“空白啊”!为此,我发布过这个离 ...
- mssql sqlserver 指定特定值排在表前面
转自:http://www.maomao365.com/?p=7141 摘要: 下文讲述sql脚本编写中,将 特定值排在最前面的方法分享, 实验环境:sqlserver 2008 R2 例:将数据表中 ...
- 定时任务 Cron表达式
Cron表达式由6~7项组成,中间用空格分开.从左到右依次是: 秒.分.时.日.月.周几.年(可省略) Cron表达式的值可以是数字,也可以是以下符号: "*":所有值都匹配 &q ...
- 做移动端电子签名发现canvas的 一些坑
做移动端收集电子签名项目的时候发现了一些坑: 1. 移动端的手指按下.移动.抬起事件跟PC端的鼠标按下.移动.弹起事件是不一样的 2. canvas它的属性宽高和样式宽高是不一样的,通过CSS来设置c ...
- Oracle——DQL、DML、DDL、DCL
1.DQL:数据查询语言 基本结构:由select.from.where组成 子句组成的查询块: SELECT <字段名表> FROM <表或视图名> WHE ...
- mysql解决select * from 表名 (where + 约束条件为空)
mysql解决select * from 表名 (where + 约束条件为空),示例如下: SELECT * from tableName WHERE name is NULL; 从 tableNa ...
- WPF中窗体最大化问题处理
遇到的问题信息 问题:当WindowStyle=None时,窗口最大化,不显示任务栏 -- 即窗体是全屏效果. 解决中遇到的问题列表[主要涉及到任务栏发生改变后的一些问题处理]: 最大化时,任务栏被遮 ...
- centos下 telnet访问百度
先在命令行输入以下命令: telnet www.baidu.com 80 点击确认之后出现如下界面 然后接着输入以下两行命令 GET /index.html HTTP/1.1 Host: www.ba ...
- (九)Delete an Index
Now let’s delete the index that we just created and then list all the indexes again: 现在让我们删除刚刚创建的索引, ...