队列:顺序队列和循环队列

和栈的先进后出不同,队列的形式是先进先出,队列的想法来自于生活中排队的策略, 顾客在付款结账的时候,按照到来的先后顺序排队结账。先来的顾客先结账,后来的顾客后结账。

队列有两种实现形式:1 顺序表实现 2 循环顺序表 

首先来看下顺序表的实现,在python中,队列的实现用list来写十分的方便。实现方式如下:

class line_queue():

def __init__(self):

self._elem=[]

def push(self,elem):

self._elem.append(elem)

def pop(self):

elem=self._elem.pop(0)

return elem

def queue_length(self):

return len(self._elem)

和栈唯一的区别是,这里pop是pop(0),也就是首先进队列的数据出列。这个实现很简单,但是有一个问题,每次有元素出队列的时候,元素都必须要进行前移。这就带来了一个问题,它的操作复杂度为O(n),而不是O(1)。只有从尾部弹出元素也就是先进后出的时候复杂度为O(1).

那么如何才能满足O(1)的出列复杂度呢。我们可以考虑记住队头和队尾的位置。每次出队的时候直接将队头位置的元素弹出就可以了。具体的实现可以参考下图

下面来看下代码的实现:

class line_queue_update():

def __init__(self):

self._elem=[]

self.head=self.rear=0

def push(self,elem):

self._elem.append(elem)

self.rear+=1

def pop(self):

elem=self._elem[self.head]

self.head+=1

return elem

def queue_length(self):

return len(self._elem)

def get_elem(self):

print self._elem

if __name__=="__main__":

q=line_queue_update()

for i in range(10):

q.push(i)

print ‘The length is %d‘ % q.queue_length()

q.pop()

q.pop()

q.push(90)

q.push(100)

q.push(200)

print ‘The length is %d‘ % q.queue_length()

运行结果如下:

/usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

The length is 10

The length is 13

这个方法的实现出队列的复杂度就是O(1)。但是我们同时也注意到另外一个问题,那就是尽管我们曾经pop元素。但是整个list的长度在不断的增加。这是为什么呢,是因为队头/对尾位置变量的值将随着操作移动。从操作效率来看,每个操作都在O(1)时间完成。但另一方面,表中元素序列随着操作向表尾方向移动,这就导致了在表的前端留下来了越来越多的空间,这些空间就是那些已经出队的元素曾经占据的空间。在c语言中,表元素存储的大小是固定的,经过反复的入队和出队操作。一定会在某次入队时出现队尾溢出也就是表满的情况。对于python而言,由于list是自动增长的,随着操作进行,表前端留下了越来越大的空区,而且这片空区永远也不会用到,完全浪费了。

前面介绍了顺序队列的两种实现方式,第一种时间效率无法满足,空间效率满足。 第二种时间效率满足,空间效率无法满足。两种策略都有各自的缺点,那么是否有一种方式既能满足时间效率也能满足空间效率呢。这就需要用到队列的第二种实现方式:循环顺序表

循环顺序表是在顺序表的第二种方式进行的变种。前面介绍过,通过head,rear记录表头和表尾元素的方式可以做到时间效率为O(1)但是会导致大量的空间浪费。但是如果我们将顺序表看做一种环形结构。那么空间浪费的问题就可以解决了,结构如下所示。

那么在这个结构里面,数据就保存在q.front到q.rear的位置里面。两个变量的差就是队列里面的元素个数。这种结构下元素始终在一个环里进行轮转。空位置可以被新插入的元素占据。

那么在环形结构中,需要注意以下3点:

1当队列为空或者为满的时候,q.front=q.rear。由于q.front=q.rear对应队空和队满两种情况,因此为了区分,可以通过队列长度来判断,当q.front=q.rear且q.len=q.max的时候认为为队满,否则为队空。

2 出队的时候q.front=(q.front+1)%q.len

3 入队的时候q.rear=(q.rear+1)%q.len

第2点和第3点的实现方式和顺序表不一样,在更新队头和队尾位置的时候需要进行求余操作,这是因为循环队列的空间大小固定,队头和队尾的位置是相对增长的,不是绝对增长的。下面来看下具体的实现代码

class squeue():

def __init__(self,init_len=8):

self._len=init_len

self._elem=[0]*init_len

self._head=0

self._rear=0

self._num=0

def is_empty(self):

return self._num == 0

def is_full(self):

return self._num == self._len

def dequeue(self):

if self.is_empty():

raise BaseException(‘queue is empty‘)

e=self._elem[self._head]

self._head=(self._head+1) % self._len

self._num-=1

return e

def enqueue(self,elem):

if self.is_full():

raise BaseException(‘queue is full‘)

self._elem[self._rear] = elem

self._rear=(self._rear+1) % self._len

self._num+=1

def get_elem(self):

print self._elem

if __name__=="__main__":

q=squeue()

for i in range(8):

q.enqueue(i)

q.get_elem()

q.dequeue()

q.enqueue(100)

q.get_elem()

首先插入8个元素,然后出队一个元素

/usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

[0, 1, 2, 3, 4, 5, 6, 7]

[100, 1, 2, 3, 4, 5, 6, 7]

当继续插入元素的时候,q.enqueue(100)会提示队列已满

/usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

Traceback (most recent call last):

File "/home/zhf/py_prj/data_struct/chapter5.py", line 196, in <module>

q.enqueue(100)

File "/home/zhf/py_prj/data_struct/chapter5.py", line 173, in enqueue

raise BaseException(‘queue is full‘)

BaseException: queue is full

我们可以添加一个扩充队列的操作,防止队列满导致插入失败

def expand(self):

old_len=self._len

self._len*=2

new_elems=[0]*self._len

for i in range(old_len):

new_elems[i] = self._elem[(self._head+i-1) % old_len]

self._elem,self._head,self._rear=new_elems,0,self._head+self._num

if __name__=="__main__":

q=squeue()

for i in range(8):

q.enqueue(i)

q.get_elem()

q.dequeue()

q.enqueue(100)

q.get_elem()

q.enqueue(200)

q.get_elem()

运行结果如下:

/usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

[0, 1, 2, 3, 4, 5, 6, 7]

[100, 1, 2, 3, 4, 5, 6, 7]

[100, 1, 2, 3, 4, 5, 6, 7, 0, 200, 0, 0, 0, 0, 0, 0]

原文地址:https://www.cnblogs.com/zhanghongfeng/p/8469904.html

时间: 2024-10-08 10:09:00

队列:顺序队列和循环队列的相关文章

数据结构之队列(三)——循环队列

循环队列采用顺序存储的方式(数组),基本思想是通过一个数组存储数据,两个下标front和rear分别指向队头和队尾 由于假溢出现象:采用循环队列,又由于循环队列无法判断队列是空还是满,所以采用损失一个元素为空的代价来分别队列为空还是为满 与链队列不同的是: 循环队列的队头指针(下标)不是指向什么头结点,而是直接指向当前队头的元素 循环队列的队尾指针(下标)不是指向最后一个元素,而是指向最后一个元素的下一个下标 当循环队列为空的时候,队尾和队头下标均指向啊a[0] 循环队列的定义 一个数组存放数据

看数据结构写代码(16)顺序队列的实现(循环队列)

循环队列的基本结构如下: front 属性 表示 队头,rear 属性表示 队尾. 在队空时 :q.rear 和 q.front 都为0 ,其余时刻q.rear 指向 队尾的后继节点,q.front指向 队头. 当在队尾插入元素时,q.rear + 1 ,在删除 队头元素时 ,q.front + 1,这样的操作 会造成 "假溢出"问题. 图(d) 就是一种 假溢出 问题,q.rear 指向 空间的边界,再插入 会造成 溢出,但是 实际上 空间 并未满. 为了解决假溢出,将 队列 看成

顺序结构的循环队列

//循环队列 #include <stdio.h> #include<stdlib.h> #define MAXSIZE 5 #define status int struct Queue { int* base; int front; int real; }Q; status initQueue(Queue &Q) { Q.base=(int *)malloc(MAXSIZE*sizeof(int)); if(!Q.base) { printf(" 内存分配失败

队列( FIFO ) 循环队列

队列( FIFO ) 数组实现 #include <stdio.h> #define N 64 int main() { int i, n, front, rear, q[2*N]; scanf("%d", &n); for(i = 0; i < n; i++) q[i] = i + 1; front = 0; rear = n; while(rear – front > 0) { printf("%d ", q[front]); f

队列(FIFO)—循环队列、队列的链式存储

1 队列的定义 队列是只允许在一端(队尾)进行插入操作,而在另一端(队头)进行删除操作的线性表. 2 队列的特点 1)先进先出是队列最大的特点,是应用中非常常见的模型,例如排队: 2)队列也属于线性表,线性表的特性队列都拥有. 3 循环队列的实现及关键点 3.1 关键点 1)队列为空的条件:队头指针等于队尾指针,即head == tial: 2)队列中保留一个元素空间,当队列满时,尾指针和头指针之间还剩一个元素空间.队列为满的条件:(tial + 1) % quenceSize == head:

数据结构-队列的顺序存储(循环队列)

1 #include "stdio.h" 2 #include "stdlib.h" 3 4 #define OK 1 5 #define ERROR 0 6 #define OVERFLOW -1 7 #define MAXQSIZE 100 8 typedef int QElemType; 9 typedef int Status; 10 11 typedef struct 12 { 13 QElemType *base; 14 int front; 15 in

javascript的队列,优先队列,循环队列

按书上的来弄的.慢慢理解了. function Queue() { var items = []; this.enqueue = function(element){ items.push(element); } this.dequeue = function(){ return items.shift(); } this.front = function(){ return items[0]; } this.isEmpty = function(){ return items.length =

数据结构Java实现07----队列:顺序队列&amp;顺序循环队列、链式队列、顺序优先队列

数据结构Java实现07----队列:顺序队列&顺序循环队列.链式队列.顺序优先队列 一.队列的概念: 队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作. 队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头.队列的插入操作通常称作入队列,队列的删除操作通常称作出队列. 下图是一个依次向队列中插入数据元素a0,a1,...,an-

数据结构--顺序循环队列和链式队列

第一部分:顺序循环队列的实现 1 //循环队列的实现 2 #define OK 1 3 #define MAXSIZE_Q 10 4 //#define OVERFLOW -2 5 #define ERROR 0 6 7 typedef int Status; 8 typedef int QElemtype; 9 10 typedef struct{ 11 QElemtype *base; //初始化的动态分配存储空间 12 int front; //队头指针 13 int rear; //队尾

循环队列——队列的顺序表示和实现

參考书目:<数据结构(C语言版)>,严蔚敏 怎样将<数据结构>中的知识应用到如今的工作中呢(单片机C编程.数字信号处理算法),希望在这里可以得到各位的指点.这个程序是我自己用循环队列实现了一个简单的应用模型(得益于一位童鞋的启示).这里高手如云,希望可以得到很多其它的指点啊! common.h #ifndef _COMMON_H_ #define _COMMON_H_ #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR