一、栈
栈(stack)是限定仅在表尾进行插入和删除操作的线性表,我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈;栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构。
理解栈的定义时我们需要注意:首先它是一个线性表,也就是说,栈元素具有线性关系,即前驱后继关系,只不过它是一种特殊的线性表而已,定义中说是在线性表的表尾进行插入和删除操作,这里的表尾是指栈顶,而不是栈底。它的特殊之处就在于限制了这个线性表的插入和删除位置,它始终只在栈顶进行,这也就使得:栈底是固定,最先进栈的元素只能在栈底,每当从栈内弹出一个数据,栈的当前容量就-1。
栈的插入操作,叫做进栈,也称为压栈、入栈;栈的删除操作,叫做出栈,也有叫做弹栈;栈对线性表的插入和删除的位置进行了限制,并没有对元素进出的时间进行限制,也就是说,在不是所有元素都进栈的情况下,事先进去的元素也可以出栈,只要保证栈顶元素出栈就可以。
清空栈:就是将栈中的元素全部作废,但找本身的物理空间并不会发生改变(不是销毁);
销毁栈:是要释放掉该栈所占据的物理内存空间;
既然栈是线性表的特例,那么栈也会有线性表的特点,分为栈的顺序存储结构和栈的链式存储结构;利用顺序存储方式的栈称为顺序栈,类似于顺序表的定义,栈中的数据元素用一个预设的足够长度的一堆数组实现:datatype data[MAXSIZE],栈底位置可以设置在数组的任意一个端点,而栈顶随着插入和删除而变化的,用int top来作为栈顶的指针,指明当前栈顶的位置,用于将data和top封装在一个结构中;用链式存储结构实现的栈称为链栈,通过链栈用单链表表示,因此其结点结构与单链表的结点结构相同。
栈的顺序存储结构:
栈的链式存储结构:
二、队列
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表,队列是一种先进先出(First In First Out)的线性表,简称FIFO;允许插入的一端称为队尾,允许删除的一端称为队头;假设队列是q=(a1,a2,……,an),那么,a1就是对头元素,而an就是队尾元素。这样我们就可以删除时,总是从a1开始删除,而插入时,列在最后。这样也比较符合我们通常生活中的习惯,排在第一个的优先出列,最后来的当然就排在队伍最后。
创建一个队列要完成两个任务:一是在内存中创建一个头结点,二是将队列的头指针和尾指针都指向这个生成的头结点,因为此时该队列是一个空队列。
线性表有顺序存储和链式存储两个存储结构,栈是线性表,所以有这两种存储方式,同样,队列作为一种特殊的线性表,也同样存在这两种存储方式;我们把队列的头尾相接的循环的顺序存储结构称为循环队列,循环队列它的容量是固定的,并且它的队头和队尾都可以随着元素入出队列而发生改变,这样循环队列逻辑上就好像是一个环形存储空间,但是要注意的是,在实际的内存当中,不可能有真正的环形存储区,我们只是用顺序表模拟出来的逻辑上的循环。队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。
循环队列:
链式存储队列:
对于循环队列和链队列的比较,可以从两个方面来考虑,从时间上,其实它们的基本操作都是常数时间,即都为O(1)的,不过循环队列是事先申请好空间的,使用期间不释放,而对于链队列,每次申请和释放结点也会存在一些时间开销,如果入队出队频繁,则两者还是有细微差异的;对于空间上来说,循环队列必须要有一个固定的长度,所以就有了存储元素个数和空间浪费的问题,而链队列却不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但是也可以接受,所以在空间上,链队列更加的灵活;总的来说,在可以确定队列长度的情况下,建议使用循环队列,如果你无法预估队列的长度时,则使用链队列。