数据结构(队列)的代码实现和测试(1)——顺序队列实现,以及关于虚函数的理解

一、队列的实现有两种方式

        顺序队列

        链式队列

由于链式队列需要用到链表模板,因此,在代码实现链表后再实现链式队列

顺序队列实际上是由 数组 来承担主要的存储任务。每在队尾放进一个新元素后,rear向后移动一位,每从队首提取一个元素之后,front会向后移动一位。因此,不可避免的会遇到数组的长度不够用的问题。

        这里的解决方法是,将线性数组围成一个环,从而实现空间的重复利用。

        这里将数组连成环有一个很重要的思想——取余思想

这个思想的精妙之处在于,他没有用物理方式像链表一样把尾指针指向头节点,而是创造性的将这个  “把直棍掰弯”  的物理问题转化成为了一个数学计算问题。

        注意下面的代码中,每次向后移动rear时都会用到以下代码。

rear = (rear + 1) % maxSize;

        这使得rear即使到了数组尾部的索引时,取余%操作会将他重新放到数组的头部进行存储。

这个思想还可以用到同样类似的问题中,通过简单的数学计算达成目的。

二、注意点:判断队列是否满

另外,在这个代码中还有一个重点

        如何判断队列是否是满的

因为引入了将数组转化为循环结构,因此判断队列是否满不能直接用 rear == maxsize 来判断,因为有可能数组的前端位置还有空间。

于是,我们用以下代码来判断队列是否满

(rear + 2) % maxSize) == front

这里 rear +2有两个原因

        1.数组是从0开始索引的,因此要+1

        2.我们为队列申请的存储空间比原本的值要大1,因此再+1

取余之后判断是否和front相等,这里只有一种情况相等,就是当数组满的时候。

三、注意点子类的length函数被设置为虚函数

	virtual int length() const {		//length 函数,返回队列实际使用长度
		return ((rear + maxSize) - front + 1) % maxSize;
	}

这是子类中的length函数,Shaffer将他声明成了虚函数。

这里关于虚函数,有一点理解:

·       如果父类是虚函数(不是纯虚函数),子类是实函数,那么 父类->该方法,则调用的是子类的函数

        如果父类是  实函数,子类是 实函数,那么  父类->该方法 直接调用父类函数

        总结:如果该方法是virtual ,那么他会向子类中找非virtual 的函数去调用

例:

#include <iostream>
class x {
	public:
		virtual void print() {
			std:: cout << "x" << std:: endl;
		}
};

class y: public x {
	public:
		void print() {
			std::cout << "y" << std:: endl;
		}
};

int main() {
	x *X = new y;
	X->print();
	return 0;
}

在上述代码中,父类的print方法是虚函数,子类的是实函数,我们执行父类的print,输出结果为

y

class x {
	public:
		void print() {
			std:: cout << "x" << std:: endl;
		}
};

但我们将父类的print方法改为实函数,输出结果为

x

由此可见

        程序会先向子类中找非virtual方法调用,如果没有,则调用此类的方法

        如果父子都为virtual,那么实际是哪类,就用哪类的方法

四、队列的代码实现

Queue.h文件

#pragma once
#include<bits/stdc++.h>

  //断言函数,如果不符合条件a,则打印字符串b

template<typename E>class Queue
{
private:
	void operator =(const Queue&) {};
	Queue(const Queue&) {};
	
public:
	
	Queue() {};								//类模板的成员函数通常定义在头文件中
	virtual ~Queue() {};					//析构函数
	virtual void Assert(bool a, std::string b);//断言函数 如果不符合条件a,则打印字符串b
	virtual void clear() = 0;				//重新初始化队列,用户负责回收队列使用的储存空间

	virtual void enqueue(const E&) = 0;		//将一个元素进行如队列操作,放进队列的尾部
	
	virtual E dequeue() = 0;				//返回并删除队头的元素
	
	virtual const E& frontValue() = 0;//取队头的元素但是不删除,第二个const表示不修改所属对象的状态
	
	virtual int length() const = 0;			//返回队列的长度
};


template<typename E>class QueueA: public Queue<E>//顺序队列的实现AQueue
{
private:
	int maxSize;						//队列的最大空间
	int front;							//队列头部元素的索引
	int rear;							//队列尾部元素索引
	E* listArrey;						//存储队列元素的数组
	
public:
	
	QueueA(int size = 10) {				//构造函数,默认大小为10
		maxSize = size + 1;				//最大空间比定义的多1
		rear = 0;						//让队尾的索引指向队尾
		front = 1;						//对首的索引指向队首
		listArrey = new E[maxSize];		//初始化存储空间
	}
	
	~QueueA() {							//析构函数
		delete[] listArrey;
	}
	void clear() { rear = 0; front = 1; }//重置队列

	void enqueue(const E& it);			//将#it放进队列尾部
	
	virtual int length() const {		//length 函数,返回队列实际使用长度
		return ((rear + maxSize) - front + 1) % maxSize;
	}

	E dequeue();						//返回并删除队首元素

	const E& frontValue();			//提取队首元素但是不删除

	void print();//打印队列中的元素
};


template<typename E>
inline void QueueA<E>::enqueue(const E& it)
{
	Queue<E>::Assert(((rear + 2) % maxSize) != front, "Queue is full");
	//当前面的条件式((rear + 2) % maxSize)== front时,队列是满的
	rear = (rear + 1) % maxSize;		//rear向后移动一位,这里考虑了循环队列的情况,即如果rear在数组的末尾,则从头开始存储
	listArrey[rear] = it;				//将it值存在队尾的位置
}

template<typename E>
inline E QueueA<E>::dequeue()
{
	Queue<E>::Assert(length() != 0, "Queue is empty");//如果函数的长度为0
	E it = listArrey[front];				//读取队首元素
	front = (front + 1) % maxSize;			//队首索引向后移动一位
	return it;
}

template<typename E>
inline const E& QueueA<E>::frontValue() 
{
	Queue<E>::Assert(length() != 0, "Queue is empty");
	return listArrey[front];
}

template<typename E>
inline void QueueA<E>::print()
{
	Queue<E>::Assert(length() != 0, "Queue is empty");
	for (int i = front; i <= rear; i++)
	{
		std::cout << listArrey[i] << " ";
	}
	std::cout << std::endl;
}

template<typename E>
inline void Queue<E>::Assert(bool a, std::string b)
{
	if (!a)
	{
		std::cout << b << std::endl;
	}
}

测试文件

test.cpp

#include "Queue.h"

int main() {
	QueueA<int> a(20);		//初始化一个20长度的队列
	a.enqueue(1);
	a.enqueue(2);
	a.enqueue(3);
	a.enqueue(4);
	a.enqueue(5);
	a.enqueue(6);
	a.enqueue(7);
	a.enqueue(8);
	a.enqueue(9);
	a.enqueue(10);
	a.enqueue(11);
	a.print();
	
	std::cout << a.frontValue() << std::endl; //测试frontvalue函数
	a.print();								
	
	std::cout << a.dequeue() << std::endl;	//测试dequeue函数
	a.print();

	a.clear();		//测试clear函数
	a.print();		//若clear成功,则控制台应该输出断言函数的字符串“Queue is empty”

	return 0;
}

运行结果

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值