队列 - 从零开始实现by C++
参考链接:数据结构探险—队列篇
数据结构太重要了,不学好是没法进行软件开发的。
C++写数据结构基本套路:一个.h文件写该数据结构类的接口;一个.cpp文件写接口的具体实现;一个main.cpp用于测试。
队列
队列的模型
想象一下现实生活中的队列,排队先入先出,不允许插队,队头先出,队尾进入。(应用:营业厅自动排号机)
队列的编程实现方式
环形队列,数组实现,静态的,事先确定队列容量,人为取余,循环利用数组资源。
普通队列,链表实现,动态的,有点浪费,因为插入删除只在队列头尾进行。
队列的基本元素
要操纵队列,必须要有一个数组(指针),队列长度,队列容量;然后用队列头和队列尾操纵队列。
环形队列中队列头和队列尾到底意味着什么?对尾指向最后一个元素的下一个位置。
队列的基本操作
创建队列,销毁队列,清空队列,队列判空,队列判满,队列长度;元素入队,元素出对,遍历队列
基本操作的实现
创建队列:构造函数实现,函数参数为队列容量;此步需要完成的任务:形成数组实体,即分配内存,将一些队列基本元素初始化。
销毁队列:析构函数实现,释放内存,指针置空。
清空队列:指针不动,基本元素初始化
队列判空:通过长度判断
队列判满:通过长度和容量判断
队列长度:直接返回
元素入队:判断能否插入,能则插入,更新队列基本信息(技巧)
元素出对:判断是否有东西可以删除,更新队列基本信息
遍历队列:循环,从头到尾,遍历(输出)
大部分的操作都是针对队列基本元素的,会利用和改变它。(所以用C++其实是简化了,只需考虑类内数据元素)
写一个大型的数据结构很难,很容易就会出现某些细小的错误,如果无法控制这些细小的错误,那等程序大起来之后,就很难很难找错和调试了。
C++实现步骤
- 首先,把上面的流程走一遍,做到心中有数。
- 设计队列的接口(建议写在一个单独文档里,以便随时查阅),先不谈具体实现;(照着抄很容易,关键要会默写)
- 实现,核心功能,创建,插入,删除,遍历显示,调试成功
- 补充其他核心功能
代码要写好,还真是太难了,首先要正确的命名,其次大小写还不能搞错,在使用的时候还要一一对应。
函数的返回值、函数名、参数等等,都不允许有错误。(很难一次性写好,在实现时慢慢修正)
初次亲手敲代码的体会:队列的代码量确实很少,接口20多行;实现80多行,测试20多行,根本就不算多;但是写起来还是感觉很难,之前敲书本上的单个例题,感觉还没什么难度;队列元素太多,基本的5个元素,操作太多,9个基本操作,操作之间部分依赖;我对C++的语法也不算太熟悉,有些卡顿;东西太多,太杂,名称太长,导致我眼花缭乱。
课程笔记
这是之前C++的进阶课,数据结构:一群数据以及数据之间的关系(集合+关系),数据结构是前人经验的总结,所以你学习借鉴就好了,不要钻牛角尖。
队列是先入先出的数学模型,队列中很少用到位序,因为只能对首尾进行操作,载入元素默认实在队尾,删除元素默认是在队头。
队列分为普通队列和环形队列
普通队列:分两种情况,一是队头离开,后面全部前移;二是队头离开,不移,往后继续排,这两种结果都不好
环形队列:充分利用资源,也不牺牲效率;队头队尾重叠只有两种情况,要么为空,要么为满。
队列的C和C++实现方式是大为不同的
在C++中,队列被写成了类,每一个具体队列都是该类的实例;而C语言中,只是简单定义了个结构体,C语言函数的参数中一定要含有队列的地址,而C++因为写在了类中,则不用传参数。
标准C++代码
#pragma once
//MyQueue.h
//环形队列C++实现
class MyQueue
{
public:
MyQueue(int queueCapacity); //InitQueue(&Q)创建队列
virtual ~MyQueue(); //DestroyQueue(&Q)销毁队列
void ClearQueue(); //ClearQueue(&Q)清空队列
bool QueueEmpty() const; //QueueEmpty(&Q)判空队列
int QueueLength() const; //QueueLength(Q)队列长度
bool QueueFull() const; bool EnQueue(int element); //EnQueue(&Q, element)新元素入队
bool DeQueue(int &element); //DeQueue(&Q, &element)首元素出队
void QueueTraverse(); //QueueTraverse(Q, visit())遍历队列
private:
int *m_pQueue; //队列数组数组
int m_iQueueLen; //队列元素个数
int m_iQueueCapacity; //队列数组容量 int m_iHead;
int m_iTail;
};
//MyQueue.cpp
#include"MyQueue.h"
#include<iostream>
using namespace std; MyQueue::MyQueue(int queueCapacity)
{
m_iQueueCapacity = queueCapacity;
m_iHead = 0;
m_iTail = 0;
m_iQueueLen = 0;
m_pQueue = new int[m_iQueueCapacity];
} MyQueue::~MyQueue()
{
delete[]m_pQueue;
m_pQueue = nullptr;
} void MyQueue::ClearQueue()
{
m_iHead = 0;
m_iTail = 0;
m_iQueueLen = 0;
} bool MyQueue::QueueEmpty() const
{
if (m_iQueueLen == 0)
return true;
else
return false;
} bool MyQueue::QueueFull() const
{
if (m_iQueueLen == m_iQueueCapacity)
return true;
else
return false;
} int MyQueue::QueueLength() const
{
return m_iQueueLen;
} bool MyQueue::EnQueue(int element)
{
if (QueueFull())
return false;
else
{
m_pQueue[m_iTail] = element;
m_iTail++;
m_iTail = m_iTail % m_iQueueCapacity;
m_iQueueLen++;
return true;
}
} bool MyQueue::DeQueue(int &element)
{
if (QueueEmpty())
return false;
else
{
element = m_pQueue[m_iHead];
m_iHead++;
m_iHead = m_iHead % m_iQueueCapacity;
m_iQueueLen--;
return true;
}
} void MyQueue::QueueTraverse()
{
for (int i = m_iHead; i < m_iQueueLen + m_iHead; i++)
{
cout << m_pQueue[i%m_iQueueCapacity] << endl;
}
}
//demo.cpp
#include<iostream>
#include"MyQueue.h"
using namespace std; int main()
{
MyQueue *p = new MyQueue(4);
p->EnQueue(1);
p->EnQueue(0);
p->EnQueue(11);
p->QueueTraverse();
p->ClearQueue();
cout << "after clear: " << endl;
p->QueueTraverse();
if (p->QueueEmpty())
cout << "Empty queue" << endl;
else
cout << "Not Empty" << endl;
p->EnQueue(5);
p->EnQueue(15);
p->EnQueue(25);
cout << p->QueueLength() << endl;
int tmp;
p->DeQueue(tmp);
cout << "tmp is " << tmp << endl;
cout << p->QueueLength() << endl;
return 0;
}
实际应用
节点的数据可以是任意类型的数据,ElemType为Customer
//Customer.h
#pragma once
#include <string>
using namespace std; class Customer
{
public:
Customer(string name = "", int age = 0);
void printInfo() const;
private:
string m_strName;
int m_iAge;
};
//Customer.cpp
#include<iostream>
#include"Customer.h"
using namespace std; Customer::Customer(string name, int age)
{
m_strName = name;
m_iAge = age;
} void Customer::printInfo() const
{
cout << "Name: " << m_strName << endl;
cout << "Age: " << m_iAge << endl;
cout << "=================" << endl;
}
//demo.cpp
#include<iostream>
#include"MyQueue.h"
using namespace std; int main()
{
MyQueue *p = new MyQueue(4);
Customer c1("zhangsan", 20);
Customer c2("lisi", 30);
Customer c3("wangwu", 24); p->EnQueue(c1);
p->EnQueue(c2);
p->EnQueue(c3); p->QueueTraverse(); cout << endl;
Customer c4;
p->DeQueue(c4);
cout << "this is c4: " << endl;
c4.printInfo(); cout << "this is QueueTraverse: " << endl;
p->QueueTraverse();
return 0;
}
使用C++模板写一个队列
比较C和C++实现的不同
队列 - 从零开始实现by C++的更多相关文章
- 从零开始写STL—栈和队列
从零开始写STL-栈和队列 适配器模式 意图:将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 主要解决:主要解决在软件系统中,常常要将 ...
- 从零开始写STL-容器-双端队列
从零开始写STL-容器-双端队列 什么是双端队列?在介绍vector源码,我们发现在vector前端插入元素往往会引起大量元素的重新分配,双端队列(deque)就是为了解决这一问题,双端队列中在首端和 ...
- 【MQ】java 从零开始实现消息队列 mq-02-如何实现生产者调用消费者?
前景回顾 上一节我们学习了如何实现基于 netty 客服端和服务端的启动. [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]java 从零开始实现消息队列 mq-02-如何实现生产者调用 ...
- 从零开始实现lmax-Disruptor队列(二)多消费者、消费者组间消费依赖原理解析
MyDisruptor V2版本介绍 在v1版本的MyDisruptor实现单生产者.单消费者功能后.按照计划,v2版本的MyDisruptor需要支持多消费者和允许设置消费者组间的依赖关系. 由于该 ...
- 从零开始实现lmax-Disruptor队列(三)多线程消费者WorkerPool原理解析
MyDisruptor V3版本介绍 在v2版本的MyDisruptor实现多消费者.消费者组间依赖功能后.按照计划,v3版本的MyDisruptor需要支持多线程消费者的功能. 由于该文属于系列博客 ...
- 从零开始实现lmax-Disruptor队列(四)多线程生产者MultiProducerSequencer原理解析
MyDisruptor V4版本介绍 在v3版本的MyDisruptor实现多线程消费者后.按照计划,v4版本的MyDisruptor需要支持线程安全的多线程生产者功能. 由于该文属于系列博客的一部分 ...
- 从零开始实现lmax-Disruptor队列(五)Disruptor DSL风格API原理解析
MyDisruptor V5版本介绍 在v4版本的MyDisruptor实现多线程生产者后.按照计划,v5版本的MyDisruptor需要支持更便于用户使用的DSL风格的API. 由于该文属于系列博客 ...
- 从零开始实现lmax-Disruptor队列(六)Disruptor 解决伪共享、消费者优雅停止实现原理解析
MyDisruptor V6版本介绍 在v5版本的MyDisruptor实现DSL风格的API后.按照计划,v6版本的MyDisruptor作为最后一个版本,需要对MyDisruptor进行最终的一些 ...
- 从零开始实现lmax-Disruptor队列(一)RingBuffer与单生产者、单消费者工作原理解析
1.lmax-Disruptor队列介绍 disruptor是英国著名的金融交易所lmax旗下技术团队开发的一款java实现的高性能内存队列框架 其发明disruptor的主要目的是为了改进传统的内存 ...
随机推荐
- winform中利用反射实现泛型数据访问对象基类(3)
继续完善了几点代码 满足没有主键的情况下使用 并且完善实体字段反射设置value时的类型转换 /// <summary> /// DAO基类 实体名必须要与数据表字段名一致 /// < ...
- Intellij IDEA svn的使用记录
这里的忽略一直灰色的,可以进入 这里的版本控制里进行忽略选择 或者 这里进行添加 这里有三个选择 按照顺序 1.忽略指定的文件 2.忽略文件夹下所有文件 3.忽略符合匹配规则的文件 到Commit C ...
- Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理
Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理 JDK动态代理的实现及原理 作者:二青 邮箱:xtfggef@gmail.com 微博:http://weibo.com/xtfg ...
- 工欲善其事必先利其器系列之:在VS里面折叠js代码
之前用vs写js的时候经常因为js代码过长而且不能像cs文件里面的方法一样进行折叠而抓狂,直到在扩展库发现了这款插件有了它就可以把代码进行折叠了 插件地址
- VC++检测当前网络状态
VC获得本机网络连接状态 转载:http://www.cppblog.com/wrhwww/archive/2010/12/02/135230.html //本机网络连接类型(成功) #define ...
- git版本控制器
Git 是一个开源的分布式版本控制系统 Google用于android 源代码的管理就是Git, 它支持离线工作, 本地提交可以稍后提交到服务器上. 众多的开源项目都使用 Git 作为版本控制系统 ...
- C# this.invoke()作用 多线程操作UI
Invoke()的作用是:在应用程序的主线程上执行指定的委托.一般应用:在辅助线程中修改UI线程( 主线程 )中对象的属性时,调用this.Invoke(); //测试的窗体 public class ...
- 对 strcpy_s 若干测试
今天发现如果strcpy这函数,目标buffer太小,会有意想不到的崩溃.而且不容易调试.以后尽量要用strcpy_s了. strcpy_s是strcpy的更安全的版本 1.当目标字符串参数是一个字符 ...
- 【翻译】Anatomy of a Program in Memory—剖析内存中的一个程序(进程的虚拟存储器映像布局详解)
[翻译]Anatomy of a Program in Memory—剖析内存中的一个程序(进程的虚拟存储器映像布局详解) . . .
- Linux安装配置JDK
如果想看Windows下的安装,请访问此链接: http://www.cnblogs.com/yoyotl/p/5101321.html 1. 去官网下载Linux版本的jdk安装包,(我下载的是ta ...