前面介绍的栈是后进先出(LIFO)的一种线性表,今天介绍的队列是先进先出(FIFO)的一种线性表。
栈只允许在栈顶进行插入或删除操作;而队列只允许在队头删除元素(出队列),在队尾插入元素(入队列)。
队列有队头指针指向队头,队尾指针指向队尾。
不管是栈或是队列,都既可以用链表实现,也可以用顺序表实现,但是栈一般采取顺序存储,而队列我们常用链表实现,简称链队列。
为啥队列的顺序存储结构不受青睐呢?下面我们来介绍一下队列的顺序存储结构。
假设现在队列里有n个元素,我们插入只能在队尾,时间复杂度为O(1)。删除只能在队头,如果不移动队头指针,删除操作需要将剩余元素都向前移一个位置,时间复杂度为O(n);如果采取删一个就将头指针往后移一个位置的操作,仿佛解决了这个问题,但是不要忘记我们这是顺序存储结构,空间申请多少就是多少,不能动态增加!这里还要注意一点,此处的尾指针是指向下一个可存放元素的空位置,所以按前面所说,一直删一直删直到尾指针已经指到队列外去了,再想插入元素就会溢出,因为尾指针指的地方已经不属于队列了,然而由于头指针前面还有空位,所以这种现象叫“假溢出”。
怎么解决“假溢出”呢?说白了,就是后面满了的话,就再从头开始呗,也就是要一个头尾相接的循环。
循环队列就好像一个环形存储空间,它依然是队列的顺序存储结构,所以容量固定,并且队头与队尾指针随着元素出入队列发生变化。如何实现循环呢?取模运算。
下面来介绍一下循环队列的相关操作。
1、定义
typedef struct
{
ElemType *base;//存放内存分配基地址
int front;
int rear;
}
2、初始化
q->base=(ElemType *)malloc(MAXSIZE*sizeof(ElemType));
q->front=q->rear=0;//空队列时头,尾指针指向同一处
3、入队
判断队列是否已满:(q->rear+1)%MAXSIZE==q->front
未满则:q->base[q->rear]=e; q->rear=(q->rear+1)%MAXSIZE;
4、出队
判断队列是否为空:q->front==q->rear;
未空则:*e=q->base[q->front]; q->front=(q->front+1)%MAXSIZE;
BY ZJQ