循环队列和策略模式、工厂模式

本文介绍了如何结合策略模式和工厂模式优化环形队列的实现,展示了使用数组和链表两种方式,并分析了它们的时间复杂度,强调了在处理大数据量和高效操作时二维数据结构的重要性。
摘要由CSDN通过智能技术生成

        上节说到,循环队列: 它是队列的一个特殊,队头和队尾在逻辑上是连通的,环形队列同时也是非常高效的一种队列实现。在很多场景都非常有用。

        本节将结合策略模式、工厂模式介绍环形队列的实现使用。

策略模式:处理一个问题时有多种算法,这些算法的输入输出参数一致,那么可以使用策略模式封装,并通过绑定不同的策略对象来执行算法,而这些对象的创建,您可以直接手动new出来,也可以设计“工厂模式”将创建对象,与使用对象分离开来,达到解耦的目的,但是,除非您的系统中要创建的对象很多,否则慎用“工厂模式;

工厂模式:当实现多态时,如果要创建派生类对象并赋值给抽象类指针,那么,我们需要知道派生类类名,这样,创建对象时,对象所属类(派生类)的名字就会硬编码到程序中的各个地方,导致要更换派生类对象的时候,需要在创建对象的地方修改其名称。如果使用工厂模式,只需要添加一个抽象工厂的派生类就可以扩展可用的派生类集合,因此,实现了解耦,且遵循开闭原则(对已有代码只读,可扩展更多的派生类来使用。)注意:不要在派生类数目已经确定的场景中使用”工厂模式“,否则,就是鸡肋。

定义抽象类和测试函数

        抽象类描述了环形队列的接口和一些公共实现;接口都以纯虚函数实现;测试函数作为其友元函数,可以访问抽象类的成员方法,完成对接口功能测试。

circular_buffer.hpp

#include<iostream>

//entry
template<typename T_data>
struct queue_entry
{
	T_data _data;
	queue_entry(const T_data& data):_data(data){}
};
//node
template<typename T_data>
struct queue_node
{
	queue_entry<T_data> _entry;
	queue_node* _next;
	queue_node(const queue_entry<T_data>& entry,queue_node* next) :_entry(entry),_next(next) {}
};
//抽象队列定义
template<typename T_data>
class abstract_queue
{
public:
	abstract_queue(size_t capacity);
	virtual ~abstract_queue();
	size_t size() { return _size; }
	size_t capacity() { return _capacity; }
	bool empty() { return _size == 0; }
	virtual void clear() = 0;//不再是直接销毁队列,而是将队列中元素全部改为无效。
	virtual bool enqueue(const queue_entry<T_data>& entry) = 0;
	virtual bool dequeue() = 0;
	virtual queue_entry<T_data>* front() = 0;
	virtual queue_entry<T_data>* rear() = 0;
private:
	virtual void print() = 0;
	friend void test_queue();
protected:
	size_t _size, _capacity;
};

template<typename T_data>
inline abstract_queue<T_data>::abstract_queue(size_t capacity):
	_size(0),_capacity(capacity)
{
	std::cout << "abstract_queue" << std::endl;
}

template<typename T_data>
inline abstract_queue<T_data>::~abstract_queue()
{
	std::cout << "~abstract_queue" << std::endl;
}
extern void test_queue();

二、测试函数

        在测试函数中,抽象类指针分别绑定策略对象circular_queue_array和circular_queue_link的实例,然后调用抽象类中定义的接口。

circular_buffer.cpp

#include "circular_queue_array.hpp"
#include "circular_queue_link.hpp"
//测试
void test_queue()
{
	abstract_queue<int>* queue = new circular_queue_array<int>(12);
	for (size_t i = 0; i < 12; i++)
	{
		queue_entry<int> entry(i);
		queue->enqueue(entry);
	}
	std::cout << "\nqueue_array size after enqueue " << queue->size() 
		<< ",front:" << queue->front()->_data 
		<< ",rear:" << queue->rear()->_data << std::endl;
	queue->print();
	size_t i;
	for (i = 0; i < 4; i++)
	{
		queue->dequeue();
	}
	std::cout << "\nqueue_array size after dequeue " << queue->size()
		<< ",front:" << queue->front()->_data
		<< ",rear:" << queue->rear()->_data << std::endl;
	queue->print();
	for (; i > 0; i--)
	{
		queue_entry<int> entry(i);//4,3,2,1
		queue->enqueue(entry);
	}
	std::cout << "\nqueue_array size after enqueue " << queue->size()
		<< ",front:" << queue->front()->_data
		<< ",rear:" << queue->rear()->_data << std::endl;
	queue->print();
	queue->clear();
	std::cout << "\nqueue_array size after clear " << queue->size()  << std::endl;
	queue->print();
	delete queue;

	std::cout << "\n";
	//circular_queue_link<int> link_queue;
//abstract_queue<int>* queue = &link_queue;//复习:绑定了编译期对象,对象销毁可以知道对象类型。
//queue->dequeue();
//复习:绑定了运行期对象,编译的时候无法知道基类指针指向对象类型,因此,delete时调用的是基类类型虚函数,
// 若基类使用虚析构函数,会结合虚表调用指向对象的析构函数,才能正确析构掉对象。
	abstract_queue<int>* queue_link = new circular_queue_link<int>(12);

	for (size_t i = 0; i < 12; i++)
	{
		queue_entry<int> entry(i);
		queue_link->enqueue(entry);
	}
	std::cout << "\nqueue_link size after enqueue " << queue_link->size() << ",front:" << queue_link->front()->_data << ",rear:" << queue_link->rear()->_data << std::endl;
	queue_link->print();
	for (i = 0; i < 3; i++)
	{
		queue_link->dequeue();
	}
	std::cout << "\nqueue_link size after dequeue " << queue_link->size() << ",front:" << queue_link->front()->_data << ",rear:" << queue_link->rear()->_data << std::endl;
	queue_link->print();
	for (; i > 0; i--)
	{
		queue_entry<int> entry(i);
		queue_link->enqueue(entry);
	}
	std::cout << "\nqueue_link size after enqueue " << queue_link->size() << ",front:" << queue_link->front()->_data << ",rear:" << queue_link->rear()->_data << std::endl;
	queue_link->print();

	queue_link->clear();
	std::cout << "\nqueue_link size after clear " << queue_link->size() << std::endl;
	queue_link->print();
	delete queue_link;
}

三、派生出的策略对象

        继承抽象队列,以数组形式实现环形队列,并且预先分配队列空间,当有空闲位置enqueue会成功,没有就抛出溢出异常,您需要捕获这个异常,否则有可能导致使用者的数据遭到覆盖。注意,在派生类构造函数体中,使用基类protected或者public成员时,若基类时模板,那么需要用this引用,或者以基类的作用域限定。

circular_queue_array.hpp

#pragma once
#include"circular_buffer.hpp"

template<typename T_data>
class circular_queue_array:public abstract_queue<T_data>
{
public:
	static constexpr size_t MAX_CAPACITY = 128;
public:
	circular_queue_array(size_t capacity = MAX_CAPACITY);
	virtual ~circular_queue_array();
	virtual void clear() override;
	virtual bool enqueue(const queue_entry<T_data>& entry)override;
	virtual bool dequeue()override;
	queue_entry<T_data>* front()override;
	queue_entry<T_data>* rear()override;
private:
	virtual void print() override;
private:
	int64_t _front, _rear;
	std::shared_ptr<queue_entry<T_data>>* _array;
};

template<typename T_data>
inline circular_queue_array<T_data>::circular_queue_array(size_t capacity):
	abstract_queue<T_data>(capacity),_front(-1),_rear(-1)
{
	//基类是模板,需要用this来访问基类protected或者public成员。
	if (this->_capacity > MAX_CAPACITY)
	{
		this->_capacity = MAX_CAPACITY;
	}
	_array = new std::shared_ptr<queue_entry<T_data>>[this->_capacity];
	std::cout << "circular_queue_array\n";
}

template<typename T_data>
inline circular_queue_array<T_data>::~circular_queue_array()
{
	std::cout << "~circular_queue_array\n";
	delete[] _array;
}

template<typename T_data>
inline void circular_queue_array<T_data>::clear()
{
	this->_size = 0;
}

template<typename T_data>
inline bool circular_queue_array<T_data>::enqueue(const queue_entry<T_data>& entry)
{
	if (this->_size == this->_capacity)
	{
		//溢出异常,你不捕获,就崩给你看
		throw std::runtime_error("the circular_buf is full warning!!!");
		return false;
	}
	if (_rear == this->_capacity - 1)
	{
		_rear = 0;//数组末尾
	}
	else
	{
		_rear++;
	}
	_array[_rear] = std::make_shared<queue_entry<T_data>>(entry);
	
	if (this->_size == 0)//首元素,队列空,_front无效
	{
		_front = _rear ;//_front指向首元素位置
	}
	this->_size++;
	return true;
}

template<typename T_data>
inline bool circular_queue_array<T_data>::dequeue()
{
	if (this->_size > 0)
	{
		if (_front == this->_capacity - 1)//最后一个下标
		{
			_front = 0;
		}
		else
		{
			_front++;
		}
		this->_size--;
	}
	return true;
}

template<typename T_data>
inline queue_entry<T_data>* circular_queue_array<T_data>::front()
{
	if (this->_size > 0)
	{
		std::shared_ptr<queue_entry<T_data>>& ptr_entry = _array[_front];
		return ptr_entry.get();
	}
	return nullptr;
}

template<typename T_data>
inline queue_entry<T_data>* circular_queue_array<T_data>::rear()
{
	if (this->_size > 0)
	{
		std::shared_ptr<queue_entry<T_data>>& ptr_entry = _array[_rear];
		return ptr_entry.get();
	}
	return nullptr;
}

template<typename T_data>
inline void circular_queue_array<T_data>::print()
{
	int64_t idx = _front;
	size_t count = this->_size;
	while (count>0)//_front,_rear有效
	{
		std::cout << _array[idx]->_data << ",";
		//下一个位置
		if (idx == this->_capacity - 1)
		{
			idx = 0;
		}
		else
		{
			idx++;
		}
		count--;
	}
}

继承抽象队列,以链表形式实现环形队列。链表容量刚开始是0,当增长到设置容量后,容量不在增长,当有空闲位置enqueue会成功,没有就抛出溢出异常,您需要捕获这个异常,否则有可能导致使用者的数据遭到覆盖。注意,在派生类构造函数体中,使用基类protected或者public成员时,若基类时模板,那么需要用this引用,或者以基类的作用域限定。

circular_queue_link.hpp

#pragma once
#include "circular_buffer.hpp"

template<typename T_data>
class circular_queue_link : public abstract_queue<T_data>
{
public:
	static size_t constexpr MAX_CAPACITY = 1024 * 10;
public:
	circular_queue_link(size_t capacity = MAX_CAPACITY);
	virtual ~circular_queue_link();
	virtual void clear() override;
	virtual bool enqueue(const queue_entry<T_data>& entry) override;
	virtual bool dequeue() override;
	queue_entry<T_data>* front() override;
	queue_entry<T_data>* rear() override;
private:
	virtual void print() override;
private:
	queue_node<T_data>* _front, * _rear;
	size_t _cur_capacity;
};
//容量初始化0
template<typename T_data>
inline circular_queue_link<T_data>::circular_queue_link(size_t capacity):
	abstract_queue<T_data>(capacity),_cur_capacity(0), _front(nullptr),_rear(nullptr)
{
	std::cout << "circular_queue_link\n";
}

template<typename T_data>
inline circular_queue_link<T_data>::~circular_queue_link()
{
	if (this->_cur_capacity > 0)
	{
		while (_front)
		{
			queue_node<T_data>* next = _front->_next;
			this->_cur_capacity--;
			if (next == _front)
			{
				delete next;
				break;
			}
			else
			{
				_front->_next = next->_next;
				delete next;
			}
		}
	}
	std::cout << "~circular_queue_link\n";
}

template<typename T_data>
inline void circular_queue_link<T_data>::clear()
{
	this->_size = 0;
}

template<typename T_data>
inline bool circular_queue_link<T_data>::enqueue(const queue_entry<T_data>& entry)
{
	//当前容量一直增加直到等于设置的容量
	if (_cur_capacity < this->_capacity)
	{
		queue_node<T_data>* node = new queue_node<T_data>(entry, nullptr);
		if (_rear == nullptr)
		{
			node->_next = node;
			_front = node;//首元素
		}
		else
		{
			node->_next = _rear->_next;
			_rear->_next = node;
		}
		_rear = node;
		_cur_capacity++;
	}
	else
	{//容量稳定后
		if (this->_size == _cur_capacity)
		{
			//溢出异常,你不捕获,就崩给你看
			throw std::runtime_error("the circular_buf is full warning!!!");
			return false;
		}
		//插入位置
		_rear = _rear->_next;
		_rear->_entry = entry;
		if (this->_size == 0)
		{
			_front = _rear;//首元素
		}
	}
	this->_size++;
	return true;
}

template<typename T_data>
inline bool circular_queue_link<T_data>::dequeue()
{
	if (this->_size > 0)
	{
		_front = _front->_next;
		this->_size--;
		return true;
	}
	return false;
}

template<typename T_data>
inline queue_entry<T_data>* circular_queue_link<T_data>::front()
{
	if (this->_size > 0)
	{
		return &_front->_entry;
	}
	return nullptr;
}

template<typename T_data>
inline queue_entry<T_data>* circular_queue_link<T_data>::rear()
{
	if (this->_size > 0)
	{
		return &_rear->_entry;
	}
	return nullptr;
}

template<typename T_data>
inline void circular_queue_link<T_data>::print()
{
	queue_node<T_data>* p_node = _front;
	while (this->_size > 0)//_front,_rear有效
	{
		std::cout << p_node->_entry._data << ",";
		if (p_node  == _rear)
		{
			break;
		}
		//打印下一个节点
		p_node = p_node->_next;
	}
}

四、执行测试

#include <iostream>
#include "circular_buffer.hpp"

int main()
{
    test_queue();
}

五、结果

        虽然线性结构及算法使用简单,方便,但对于搜索,数组、链表的时间复杂度达到O(N),而栈、队列不支持搜索;插入时间复杂度方面,数组、链表也都是O(N),而数组插入最坏情况下,精确来说步骤在2N,但差别还是在同一数量级O(N),而栈、队列的插入效率极高,在O(1),但不支持随机插入。

        因此,线性数据结构的缺憾导致我们必须研究一些二维的数据结构,比如二维表(树、图),来满足对大数据量输入时,能够高效,并随即插入删除查找。 

  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值