浅谈数据结构之链队列(六)

  前面我们讲了队列的顺序存储结构,现在我们来看看队列的链式存储结构。队列的链式存储其实就是线性表的单链表结构,只不过它是尾进头出而已,通常我们把它简称为链队列。为了操作上的方便,我们将队头指针front指向链队列的头结点,而队尾指针rear则指向终端结点。注意:当队列为空时,指针front和rear都指向头结点。

  在这里,我们再介绍一下循环队列。循环队列是为了避免数组插入与删除数据时需要移动数据而引入的,我们一般把队列的这种头尾相接的顺序存储结构称为循环队列。对于循环队列和链队列相比较来说,循环队列使得队头和队尾在数组中循环变化,解决了移动数据时所需要的时间损耗,因此它的时间复杂度为0(1);而链队列事先并不需要预估队列的长度,也没有存储元素个数和空间浪费的问题,所以在空间上,链队列更加灵活,方便。

  链队列的入队操作其实就是在链表尾插入结点,而出队操作则是将头结点的后继结点出队,同时将头结点的后继改为它后面的结点;若链表除头结点外只剩下一个元素,则需将指针rear指向头结点,具体操作源程序代码如下所示:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <math.h>
  4
  5 #define OK 1
  6 #define ERROR 0
  7 #define TRUE 1
  8 #define FALSE 0
  9
 10 #define MAXSIZE 20            /* 存储空间初始分配量 */
 11
 12 typedef int Status;
 13 typedef int QElemType;        /* QElemType类型根据实际情况而定,这里假设为int */
 14
 15 typedef struct QNode          /* 结点结构 */
 16 {
 17    QElemType data;
 18    struct QNode *next;
 19 }QNode,*QueuePtr;
 20
 21 typedef struct                /* 队列的链表结构 */
 22 {
 23    QueuePtr front,rear;       /* 队头、队尾指针 */
 24 }LinkQueue;
 25
 26 Status visit(QElemType c)
 27 {
 28     printf("%d ",c);
 29     return OK;
 30 }
 31
 32 /* 构造一个空队列Q */
 33 Status InitQueue(LinkQueue *Q)
 34 {
 35     Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode));
 36     if(!Q->front)
 37         exit(OVERFLOW);
 38     Q->front->next=NULL;
 39     return OK;
 40 }
 41
 42 /* 将Q清为空队列 */
 43 Status ClearQueue(LinkQueue *Q)
 44 {
 45     QueuePtr p,q;
 46     Q->rear=Q->front;
 47     p=Q->front->next;
 48     Q->front->next=NULL;
 49     while(p)
 50     {
 51         q=p;
 52         p=p->next;
 53         free(q);
 54     }
 55     return OK;
 56 }
 57
 58 /* 求队列的长度 */
 59 int QueueLength(LinkQueue Q)
 60 {
 61     int i=0;
 62     QueuePtr p;
 63     p=Q.front;
 64     while(Q.rear!=p)
 65     {
 66         i++;
 67         p=p->next;
 68     }
 69     return i;
 70 }
 71
 72 /* 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR */
 73 Status GetHead(LinkQueue Q,QElemType *e)
 74 {
 75     QueuePtr p;
 76     if(Q.front==Q.rear)
 77         return ERROR;
 78     p=Q.front->next;
 79     *e=p->data;
 80     return OK;
 81 }
 82
 83
 84 /* 插入元素e为Q的新的队尾元素 */
 85 Status EnQueue(LinkQueue *Q,QElemType e)
 86 {
 87     QueuePtr s=(QueuePtr)malloc(sizeof(QNode));
 88     if(!s)                        /* 存储分配失败 */
 89         exit(OVERFLOW);
 90     s->data=e;
 91     s->next=NULL;
 92     Q->rear->next=s;              /* 把拥有元素e的新结点s赋值给原队尾结点的后继,见图中① */
 93     Q->rear=s;                    /* 把当前的s设置为队尾结点,rear指向s,见图中② */
 94     return OK;
 95 }
 96
 97 /* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */
 98 Status DeQueue(LinkQueue *Q,QElemType *e)
 99 {
100     QueuePtr p;
101     if(Q->front==Q->rear)
102         return ERROR;
103     p=Q->front->next;             /* 将欲删除的队头结点暂存给p,见图中① */
104     *e=p->data;                   /* 将欲删除的队头结点的值赋值给e */
105     Q->front->next=p->next;       /* 将原队头结点的后继p->next赋值给头结点后继,见图中② */
106     if(Q->rear==p)                /* 若队头就是队尾,则删除后将rear指向头结点,见图中③ */
107         Q->rear=Q->front;
108     free(p);
109     return OK;
110 }
111
112 /* 从队头到队尾依次对队列Q中每个元素输出 */
113 Status QueueTraverse(LinkQueue Q)
114 {
115     QueuePtr p;
116     p=Q.front->next;
117     while(p)
118     {
119         visit(p->data);
120         p=p->next;
121     }
122     printf("\n");
123     return OK;
124 }
125
126 int main()
127 {
128     int i;
129     QElemType e;
130     LinkQueue q;
131
132     i=InitQueue(&q);
133     printf("1.初始化后队列后,队列的长度:Q.length=%d\n",QueueLength(q));
134
135     EnQueue(&q,-5);
136     EnQueue(&q,3);
137     EnQueue(&q,8);
138     EnQueue(&q,10);
139     EnQueue(&q,16);
140     EnQueue(&q,36);
141     printf("2.插入6个元素后,队列的长度:Q.length=%d\n",QueueLength(q));
142     printf("3.队列的元素输出依次为:");
143     QueueTraverse(q);
144
145     i=GetHead(q,&e);
146     if(i==OK)
147          printf("4.此时队列的队头元素是:%d\n",e);
148
149     DeQueue(&q,&e);
150     i=GetHead(q,&e);
151     if(i==OK)
152         printf("5.删除队列的队头元素后,新的队头元素是:%d\n",e);
153
154     DeQueue(&q,&e);
155     printf("6.继续删除队头元素后,队列的长度:Q.length=%d\n",QueueLength(q));
156     printf("7.队列的元素输出依次为:");
157     QueueTraverse(q);
158
159     ClearQueue(&q);
160     printf("8.清空队列后,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next);
161
162     return 0;
163 }
时间: 2025-01-07 10:46:56

浅谈数据结构之链队列(六)的相关文章

浅谈数据结构之链栈(四)

栈的链式存储结构,我们一般简称为"链栈".由于单链表有头指针,而栈顶指针也是必须要有的,所以我们通常把栈顶放在单链表的头部,有了栈顶在头部,单链表中比较常用的头结点就失去了意义.通常对于链栈来说,是不需要头结点的,也基本不存在栈满的情况,除非内存已经没有使用的空间了.但对于空栈来说,链表原定义是头指针指向"空",那么链栈的"空"其实就是"top=NULL"的时候. 链栈的进栈push和出栈pop操作都很简单,没有任何循环操作,

浅谈数据结构-二叉树

浅谈数据结构-二叉树 二叉树是树的特殊一种,具有如下特点:1.每个结点最多有两颗子树,结点的度最大为2.2.左子树和右子树是有顺序的,次序不能颠倒.3.即使某结点只有一个子树,也要区分左右子树. 一.特殊的二叉树及特点 1.斜树 所有的结点都只有左子树(左斜树),或者只有右子树(右斜树).这就是斜树,应用较少 2.满二叉树 所有的分支结点都存在左子树和右子树,并且所有的叶子结点都在同一层上,这样就是满二叉树.就是完美圆满的意思,关键在于树的平衡. 根据满二叉树的定义,得到其特点为: 叶子只能出现

浅谈数据结构-树

树是一种数据结构,其中一个元素可以有两个或者多个数据元素,具有一对多的特点,用树结构来存储文件. 树的概念 结点的度:子结点的个数.例如结点1中有3个子结点,结点1的度是3. 树的度:树的度等于所有结点度中度最高的值.结点最高的度为3,树的度为3. 叶子结点:度为0的结点,即没有子结点的结点.例如:上图中3,5,6,7,9,10. 分支结点:除了叶子结点以外的结点,即度不为0的结点.例如:上面树的分支结点为1,2,4,8. 内部结点:除了根结点以及叶子结点或在分支结点的基础之上在去掉根结点.例如

浅谈数据结构系列 栈和队列

计算机程序离不开算法和数据结构,在数据结构算法应用中,栈和队列应用你比较广泛,因为两者在数据存放和读取方面效率比较高,本章节重点讲解两者的基本概念和实现. 基本概念 栈:是一种先进后出,后进先出的数据结构,本质上是线性表,只是限制仅允许在表的一段进行插入和删除工作.此端为栈顶,这是在栈中应用很关键的概念.所有数据的处理都是在栈顶进行的,进栈时,栈中元素增加,栈顶上移一位,出栈时栈顶下移一位.应用中比如:洗碗,每次洗干净的碗放在上面-进栈,取碗,从顶上取出一个-出栈:装子弹-进栈,开枪-出栈. 队

浅谈数据结构之线性表顺序存储(一)

 首先,数据结构是由某一数据元素集合及该集合中所有数据元素之间的关系组成.具体来说,数据结构应当包含三方面的内容:(1).数据的逻辑结构:(2).数据的存储结构:(3).对数据所施加的操作.而数据的存储结构形式有两种:顺序存储与链式存储.在这里,先谈一谈线性表的顺序存储. 线性表:零个或多个数据元素的有限序列.第一,它是一个序列,也就是说,元素之间是有顺序的:第二,它是有限的,即元素个数是有限的.而线性表的顺序存储结构,说白了,就是在内存中找块地,通过占位的形式把一定的内存空间给占了,然后把相同

Java学习笔记——浅谈数据结构与Java集合框架(第一篇、List)

横看成岭侧成峰,远近高低各不同.不识庐山真面目,只缘身在此山中. --苏轼 这一块儿学的是云里雾里,咱们先从简单的入手.逐渐的拨开迷雾见太阳.本次先做List集合的三个实现类的学习笔记 List特点:有序,元素可重复.其实它的本质就是一个线性表(下面会说到) 先上图,Java集合有Collection体系和Map体系: 然后简单介绍一下数据结构和算法: 数据结构就是数据和数据之间的关系,好比分子结构,晶体结构.碳原子按照一定的方式组合在一起形成碳分子,碳分子再按照一定方式形成晶体. 算法是对解题

浅谈数据结构(一) 线性表 Lists

一.vector和list 线性结构中,比较重要的有 vector和list,这两个都是C++的标准模板库(C++ Standard Template Library)中的库文件. 访问操作,查找和删除 vector可以提供下标访问,即v[i]的方式,所以索引方便.然而如果要插入数据,尤其是在下标小的地方插入,需要把其后面所有的数据全部都往后移动一位,因此代价非常高.同样的,删除数据也是这样. list在C++中指的是双向链表,由于是指针访问,因此元素的插入和删除代价较小,只要改变几个指针即可.

浅谈栈和队列

### 栈 栈模型 栈(stack)是限制对元素的插入(push)和删除(pop)只能在一个位置上进行的表,该位置是表的末端,叫做栈的栈顶(top). 栈的基本操作只有两种,压入栈(push)和弹出栈顶(pop),且只能作用于栈顶.(只有栈顶元素是可访问的 你可以把栈结构理解成一个底部封闭,顶部打开的桶.最先进去的元素一定是最后才能取出,最晚进去的元素一定是最先取出. 因此栈又叫做LIFO(后进先出,Last In First Out)表. 栈的优势 栈的操作是常数时间的,而且是以非常快的常数时

浅谈数据结构之图的邻接表深度和广度优先遍历(九)

邻接矩阵是一种不错的图存储结构,但是我们发现,对于边数相对较少的图,这种结构是存在对存储空间的极大浪费的.我们知道,顺序存储结构存在预先分配内存可能造成空间浪费的问题,于是引出了链式存储的结构.同样的,我们也可以考虑对边或弧使用链式存储的方式来避免空间浪费的问题.因此,对于图的存储结构,我们同样引入了一种数组与链表相组合的存储方法,我们一般称之为邻接表. 邻接表的处理方法是这样的:(1).图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过数组可以较容易的读取顶点的信息,更加方便:另