队列的顺序存储与链式存储

队列就是我们日常生活中的排队,队列也是一种线性表,与堆栈相反,队列的入列必须在队尾,出列必须在队头。与一般的线性表不同,队列的操作只能在两端,一端插入一端删除,先进先出。(堆栈只能在一端进行操作,所以先进后出)。

队列的存储方式这里讲两种,首先第一种是用数组的方式实现队列的顺序存储。用一个一维数组来存储队列的数据,对队列执行操作时,插入和删除分别是对数组头和数组尾进行操作,所以还要有两个变量来指示数组的头和尾。所以队列的结构可以这样定义:

结构体里定义一个一维数组Data[ ]和两个变量HeadEndHeadEnd分别表示数组头下标和数组尾的下标。初始化队列时,一开始数组是空的,HeadEnd都是下标,可以把它们都设为-1

队列的顺序存储方式思路是这样的:首先第一个数据读进来后,End1,再读入数据就继续往后加,End一直表示的是数组中最后一个数据的下标。而当队列中删除一个元素时,Head就加1Head指向的位置有点不同,一开始数组中加入元素后,第一个数据的下标是0,但Head还是-1,所以其实往后操作时,Head指向的一直都是第一个数据的下标的前一位。

在队列的不断操作中(插入和删除)后因为数组的原因,我们会遇到下面这种情况,从而出现两个问题:


第一个问题:假设有一个能存放8个元素的数组,数组的最后一个位置被存放数据了,这时如果我们再要读入数据,是可以的,因为数组的前面有空位置可以存放,End做为最新插入的数据的下标,要怎么才能让它从数组的最后一位过渡到数组的第一位0呢?

第二个问题:当数组的所有位置都使用过一遍后,又从头开始存放数据时(此时End在前了,Head反而在后),如果此时再存入一个数据,End就和Head相等了,这会出现什么问题?一开始数组初始化时,HeadEnd都是等于-1HeadEnd相等时我们会把它当作是队列为空的情况的条件。在数组被使用完一轮后,从头开始存放数据时,End数组尾变成了数组头,追上了Head,再回去看一下上图,当End再加1后,EndHead又相等了,但它们都不会等于-1。那么当EndHead相等时,我们怎么知道队列是空的还是满的?

 第一个问题先留着(记住!!),先看看第二个问题如何解决?其实解决方法有很多,比如:

1、定义一个变量count来记录当前数据的个数,存入数据时,count就加一,删除数据时,count就减一,然后根据count的值如果为0就表示队列空,如果等于数组的大小减一就表示队列满。(当HeadEnd相等时,你就去查看这个count,根据count的大小来判断队列的空还是满。)

2、定义一个变量tag,一开始初始化为0。当存入一个数据时,tag的值变为1;当删除一个数据时,tag的值变为0;最后根据tag1还是0来判断队列是空还是满。(当HeadEnd相等时,就去查看tag,因为只有插入操作时tag才会等于1,如果做到Head等于End这一步时是插入操作,那么队列就是满了。相反同理,如果Head等于End这一步是删除操作后得到的,那么队列就是空了。)

3、第三个方法比较推荐,就是对于大小为n的数组,我们不把它使用满,而是使用n-1就够了。这种方法HeadEnd相等就只会出现在一开始队列空的时候了。

下面来展开第三种方法的实现,就是队列只使用n-1的数组空间。队列的结构体和初始化还是一样的,我们先来看下入队列的操作:

24行的注释里说明了为什么第25行要用取余的方式,为了解决上面提到的第一个问题,“数组的位置被使用完一轮后,如何让End从数组尾过渡回到数组头?”,我们再看一下这副图

数组的大小MaxSize等于8End指向了最后一个数据的下标等于7,当(End+1%MaxSize后,End就等于0了。这样就可以让End从数组尾过渡到数组头了,同时也可以作为判断队列是否满的条件。(因为使用数组的n-1个空间,EndHead在队列满时会相差1,所以当(End+1%MaxSize等于Head时,就说明队列满了。)。判断完特殊情况后,就开始插入数据,如果数组没满,我们就把End加一(第29行),然后把数据插入相应的数组位置里。

接着到出队列,很简单就和入队列差不多,只不过是对Head操作:

37行一开始还是特殊情况的判断,判断队列是否为空,这时判断HeadEnd是否相等就可以了。如果队列不为空,就让Head加一,然后返回相应的数组位置的元素。

上面讲完第一种方式用一维数组来顺序存储队列,接下来看第二种:队列的链式存储,就是用线性表的方式了。

先来看下每个数据的数据结构:

结点的每个区块都有一个Data数据,和一个Next指针来和其他区块串联起来。接着再定义一个结构,里面两个Node型的指针,HeadEnd分别指向线性表的头结点和尾结点。

接下来看入队列的操作:

22行,队列一开始是空的,所以Head等于NULL,让PtrQ->End指向第一个数据区块,然后让Head指向End,链接起来。第一次数据存储完后,下一次就直接操作End就可以了,就跟普通链表存储数据一样的操作。

接着到出队列:

因为出列就是要把那个数据区块free掉,所以先定义一个指针HeadCell,指向要删除的那个Head(第39行)。接着再做一个特殊情况的判断:(第41行)如果队列中只有一个数据,当你free掉之后,也就是删除掉数据之后,队列应该要为空了,但此时End仍然指向那个数据块,所以其实并没有删除掉。当队列只有一个数据块时,我们要把HeadEnd都指向NULL,最后再freeHeadCell。如果队列中不止一个元素,那么就让PtrQ->Head=PtrQ->Head->Next,然后把Head->的数据Data保存起来做return,最后freeHeadCell就可以了。

猜你喜欢

转载自blog.csdn.net/justinzengtm/article/details/79794992