《数据结构与算法分析——C语言描述》ADT实现(NO.02) : 队列(Queue)
第三个结构——队列(Queue)
队列与上次的栈相反,是一种先进先出(FIFO)的线性表。写入时只暴露尾部,读取时只暴露头部。
本次只实现了数组形式的队列。原因是链表形式的队列极为简单,只需要实现简单的删除首结点和尾部插入两种操作,在此便不再具体实现。
而对于数组形式的队列,内存单元固定,又不具备像栈一样一端固定的特性,为使数组中的空间可被重复使用,需使得队列的头部随着Dequeue的操作而移动。如果每次Dequeue都将整个队列的内容前移一个单元,将是一个O(n)复杂度的操作,对于删除操作而言开销过大。因此,我们不对元素本身进行移动,而是通过两个标识来指示队列的头(Front)和尾(Rear),随着Dequeue操作,向后移动Front。这样一来,前面的空间空余出来,当尾部达到数组尽头时,回过头使用数组头部的空间。也就是形成一个循环数组。
此时,我们通过Front和Rear之间的大小关系即可得知队列的长度,是否为满,是否为空。
特别需要注意的是,一旦形成循环,我们设想一下当Front确定之后,共可以形成多少种不同长度的队列?
显然,Rear可以取的值的数目与数组的长度(设为m)相等。也就是说,相应的队列长度可能为0, 1, 2, 3, ..., m-1. 这就是问题所在,一个长度为m的数组,形成循环结构后所能表示的队列的最大长度是m-1.
我们不妨思考一下原因。事实上,假如我们使用长度为m的数组表示最大长度为m的队列,如果:
(i)Front指示第一个元素的位置,Rear指示最后一个元素的位置。那么我们无法表示空队列的情况。
(ii)Front指示首元素之前的位置(类似于链表中的头结点,不保存实际数据),当Rear与Front重合时表示空队列,那么将无法表示长度为m的队列。因为不论Front处于什么位置、不论队列长度是多少,总有一个位置是不保存数据的。Rear指示尾元素之后的位置与上述情况相同,故不再单列为(iii)。
因此,要创建最大长度为n的队列,需要申请(n+1) * sizeof( ElementType ) 的数组空间。这般说下来看似废话,好像很容易可以想到,但事实上对队列不甚熟悉时,编写队列的实现很有可能忽略这一点。
至于具体将空单元放在首元素之前(Front指示)还是尾元素之后(Rear指示),则没有太大的影响。下面的实现中,笔者采用了前一种方案。
下面给出代码
// Queue.h #include <stdio.h>
#include <stdlib.h> struct QueueRecord;
typedef struct QueueRecord *Queue; int IsEmpty(Queue Q);
int IsFull(Queue Q);
Queue CreateQueue(int MaxElements);
void DisposeQueue(Queue Q);
void MakeEmpty(Queue Q);
void Enqueue(ElementType X, Queue Q);
ElementType Front(Queue Q);
void Dequeue(Queue Q);
ElementType FrontAndDequeue(Queue Q);
// Queue.c
#include "Queue.h"
struct QueueRecord{
int Capacity;
int Front;
int Rear;
int Size;
ElementType *Array;
};
int IsEmpty(Queue Q)
{
return Q->Size == 0;
}
int IsFull(Queue Q)
{
return Q->Size == Q->Capacity;
}
Queue CreateQueue(int MaxElements)
{
Queue ret;
if((ret = (Queue)malloc(sizeof(struct QueueRecord))) == NULL)
{
printf("Error! Out of memory! \n");
return NULL;
}
if((ret->Array = (ElementType*)malloc(sizeof(ElementType) * (1 + MaxElements))) == NULL)
{
printf("Error! Out of memory! \n");
free(ret);
return NULL;
}
ret->Capacity = MaxElements;
ret->Size = 0;
ret->Front = ret->Rear = 0;
return ret;
}
void DisposeQueue(Queue Q)
{
if(Q)
{
free(Q->Array);
free(Q);
}
}
void MakeEmpty(Queue Q)
{
Q->Rear = Q->Front;
Q->Size = 0;
}
void Enqueue(ElementType X, Queue Q)
{
int t;
if(IsFull(Q))
{
printf("Error! The queue is full! \n");
return;
}
t = (Q->Rear + 1) % (Q->Capacity + 1);
Q->Array[t] = X;
Q->Rear = t;
Q->Size += 1;
}
ElementType Front(Queue Q)
{
if (IsEmpty(Q))
{
printf("Error! The queue is empty! \n");
return 0;
}
return (Q->Array)[Q->Front];
}
void Dequeue(Queue Q)
{
if (IsEmpty(Q))
{
printf("Error! The queue is empty! \n");
return;
}
Q->Front = (Q->Front + 1) % (Q->Capacity + 1);
Q->Size -= 1;
}
ElementType FrontAndDequeue(Queue Q)
{
ElementType ret;
if (IsEmpty(Q))
{
printf("Error! The queue is empty! \n");
return 0;
}
ret = (Q->Array)[Q->Front];
Q->Front = (Q->Front + 1) % (Q->Capacity + 1);
Q->Size -= 1;
return ret;
}
《数据结构与算法分析——C语言描述》ADT实现(NO.02) : 队列(Queue)的更多相关文章
- 《数据结构与算法分析——C语言描述》ADT实现(NO.00) : 链表(Linked-List)
开始学习数据结构,使用的教材是机械工业出版社的<数据结构与算法分析——C语言描述>,计划将书中的ADT用C语言实现一遍,记录于此.下面是第一个最简单的结构——链表. 链表(Linked-L ...
- 数据结构与算法分析——C语言描述 第三章的单链表
数据结构与算法分析--C语言描述 第三章的单链表 很基础的东西.走一遍流程.有人说学编程最简单最笨的方法就是把书上的代码敲一遍.这个我是头文件是照抄的..c源文件自己实现. list.h typede ...
- 最小正子序列(序列之和最小,同时满足和值要最小)(数据结构与算法分析——C语言描述第二章习题2.12第二问)
#include "stdio.h" #include "stdlib.h" #define random(x) (rand()%x) void creat_a ...
- C语言学习书籍推荐《数据结构与算法分析:C语言描述(原书第2版)》下载
维斯 (作者), 冯舜玺 (译者) <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行 ...
- 《数据结构与算法分析-Java语言描述》 分享下载
书籍信息 书名:<数据结构与算法分析-Java语言描述> 原作名:Data Structures and Algorithm Analysis in Java 作者: 韦斯 (Mark A ...
- 《数据结构与算法分析——C语言描述》ADT实现(NO.03) : 二叉搜索树/二叉查找树(Binary Search Tree)
二叉搜索树(Binary Search Tree),又名二叉查找树.二叉排序树,是一种简单的二叉树.它的特点是每一个结点的左(右)子树各结点的元素一定小于(大于)该结点的元素.将该树用于查找时,由于二 ...
- 《数据结构与算法分析——C语言描述》ADT实现(NO.05) : 散列(Hash)
散列(Hash)是一种以常数复杂度实现查找功能的数据结构.它将一个关键词Key,通过某种映射(哈希函数)转化成索引值直接定位到相应位置. 实现散列有两个关键,一是哈希函数的选择,二是冲突的处理. 对于 ...
- 《数据结构与算法分析——C语言描述》ADT实现(NO.04) : AVL树(AVL-Tree)
上次我们已经实现了普通的二叉查找树.利用二叉查找树,可以用O(logN)高度的树状结构存储和查找数据,提高了存储和查找的效率. 然而,考虑一种极端情形:依次插入1,2,3,4,5,6,7,8,9九个元 ...
- 《数据结构与算法分析——C语言描述》ADT实现(NO.01) : 栈(Stack)
这次的数据结构是一种特殊的线性表:栈(Stack) 栈的特点是后入先出(LIFO),可见的只有栈顶的一个元素. 栈在程序中的地位非常重要,其中最重要的应用就是函数的调用.每次函数调用时都会创建该函数的 ...
随机推荐
- 关于类中的参数类型和return返回值
基础有些忘了,现在重新巩固一下 先定义一个Person类 class Person(): def __init__(self,name,age,height): self.name=name, sel ...
- C++——Struct 和 Union区别
1.在存储多个成员信息时,编译器会自动给struct第个成员分配存储空间,struct 可以存储多个成员信息,而Union每个成员会用同一个存储空间,只能存储最后一个成员的信息. 2.都是由多个不同的 ...
- Codeforces 1169B Pairs
题目链接:http://codeforces.com/contest/1169/problem/B 题意:给你 m 对数 ,问你能不能在 1 − n 之间找到俩个不相等的 x 和 y 使得 对于前面每 ...
- keepalive基础知识
一.LVS负载均衡集群的缺点 二.Keepalived介绍 三.Keepalived的功能 四.Keepalived工作原理 五.Keepalived组件框架 六.Keepalived的安装 6.1 ...
- 时间同步服务器NTP搭建
NTP服务器 NTP(Network Time Protocol)[网络时间协议],它是用来同步网络中各个计算机的时间的协议,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒, ...
- [USACO10FEB]购买巧克力Chocolate Buying
题目描述 Bessie and the herd love chocolate so Farmer John is buying them some. The Bovine Chocolate Sto ...
- 前端 Java Python等资源合集大放送
如果需要学习视频,欢迎关注[JAVA乐园]微信公众号,回复[领取资源]菜单一键领取以下所有200G干货资源,获取更多有用技术干货.文档资料.所有文档会持续更新,欢迎关注一起成长! 1.vue全套 Vu ...
- (Struts2学习系列五)Struts2默认action
当我们访问项目下一个不存在的Action的时候,页面就会报错,404找不到资源,这样对用户来说是非常不友好的,所以我们设置一个默认的Action,当找不到对应Action的时候,就会跳转到默认Acti ...
- JS window对象 History 对象 history对象记录了用户曾经浏览过的页面(URL),并可以实现浏览器前进与后退相似导航的功能。语法: window.history.[属性|方法]
History 对象 history对象记录了用户曾经浏览过的页面(URL),并可以实现浏览器前进与后退相似导航的功能. 注意:从窗口被打开的那一刻开始记录,每个浏览器窗口.每个标签页乃至每个框架,都 ...
- shell脚本输出空心等腰三角形
第一种:(for循环) #!/bin/bash#空心等腰三角形arr=9 #定义金字塔的层数for ((i=1;i<=$arr;i++))do for ((j=1;j<=$(($arr-$ ...