c++--stack,queue,priority_queue_10-7 分别对 stack<int>(3)

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

	const T& top()
	{
		return _con.back();
	}

	bool empty()
	{
		return _con.empty();
	}

	size_t size()
	{
		return _con.size();
	}
private:
	Container _con;
};

}


### 二、queue-队列


队列也是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。


![](https://img-blog.csdnimg.cn/dd3c23c0ac0d4d9c97baad612d42e710.png)




---


#### 2.1queue的使用


队列的接口其实与栈的接口基本一样,而且使用方法也是一样。




|  |  |
| --- | --- |
| 函数声明 | 接口说明 |
| queue() | 构造空的队列 |
| empty() | 检测队列是否为空,是返回true,否则返回false |
| size() | 返回队列中有效元素的个数 |
| front() | 返回队头元素的引用 |
| back() | 返回队尾元素的引用 |
| push() | 在队尾将元素val入队列 |
| pop() | 将队头元素出队列 |



void test_queue()
{
queue q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);

while (!q.empty())
{
	cout << q.front() << " ";
	q.pop();
}
cout << endl;

}


#### 2.2queue的模拟实现



#pragma once
#include
#include

namespace qhx
{

template<class T, class Container = deque<T>>
class queue
{
public:
	void push(const T& x)
	{
		_con.push_back(x);
	}

	void pop()
	{
		_con.pop_front();
	}

	const T& front()
	{
		return _con.front();
	}

	const T& back()
	{
		return _con.back();
	}

	bool empty()
	{
		return _con.empty();
	}

	size_t size()
	{
		return _con.size();
	}
private:
	Container _con;
};

}


queue的模拟实现也是非常简单的,都是复用其他人代码。栈和队列的实现中,我们发现都有class Container = deque<T>。


在queue和stack中都有这样一段话。



> 
> **Queue:**
> 
> 
> queues are a type of **container adaptor**, specifically designed to operate in a FIFO context (first-in first-out), where elements are inserted into one end of the container and extracted from the other.  
> **翻译:**
> 
> 
> 队列是一种**容器适配器**,专门设计用于在 FIFO 上下文(先进先出)中运行,其中元素插入容器的一端并从另一端提取。
> 
> 
> 
> **Stacks:**
> 
> 
> Stacks are a type of **container adaptor**, specifically designed to operate in a LIFO context (last-in first-out), where elements are inserted and extracted only from one end of the container.
> 
> 
> **翻译:**
> 
> 
> 堆栈是一种容器适配器,专门设计用于在后进先出(后进先出)环境中操作,其中元素仅从容器的一端插入和提取。
> 
> 
> 


class Container = deque<T>在这里就是容器适配器。


#### 2.3容器适配器


适配器是一种**设计模式**(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个**类的接口转换**成客户希望的**另外一个接口**。


设计模式的使用将提高软件系统的开发效率和软件质量,节省开发成本。有助于初学者深入理解面向对象思想,设计模式使设计方案更加灵活,方便后期维护修改。


在stack,queue中 deque<T>接口转换到Container中。deque是什么呢?我们不是说stack可以用vector,queue用list吗,怎么这里用的deque。


### 三、deque


在容器适配器为什么会选择deque,那么就必须得从vector,list的优缺点说起




---


**3.1vector,list的优缺点**


**vector:**



> 
> stack可以随机访问,但是头部中部插入删除效率低,并且还需要扩容
> 
> 
> 


**list:**



> 
> 虽然queue在任何地方插入删除效率高,但是不支持随机访问,CPU高速缓存命中率低
> 
> 
> 


对于deque就完美兼容vector,list的优点。所以对于接口选择就是deque。


#### **3.2deque的原理介绍**


**[deque文档](https://bbs.csdn.net/topics/618545628)**


deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和 删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。


![](https://img-blog.csdnimg.cn/edd275adf67a481fa0d1b4d246d7abfe.png)


这个是deque一段的buffer数组,所以deque并不是真正连续的空间,它是由一段一段这样的buffer数组链接而成,一段一段的buffer数组被放在**中控**,这个中控就是一个指针数组,实际上deque类似于一个动态的二维数组, 如图:![](https://img-blog.csdnimg.cn/51e44bbbf622425eb4d3e608549c5ed5.png)


这里的缓冲区就是buffer数组,用于存放数据。map就是中控器,就是存放指针。当map空间不够后,会再开辟一个中控-map。


####  3.3deque--插入


**插入操作--头插**![](https://img-blog.csdnimg.cn/ffbeb4a881a84ea69a2058dd925db857.png)


**插入操作--尾插** 


![](https://img-blog.csdnimg.cn/f89275f607584af3b07efb5cc6ccbfd5.png)


**查找:**即相当于二维数组一样,先找map中的地址(第一层),然后在找buffer(第二层)


**缺点:**


那么我们发现它下标访问有一定的消耗,没有vector快。当我们中间插入时候,它的中间插入的时候需要挪动数据,与list相比也是有消耗的。


deque不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到 某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作 为stack和queue的底层数据结构。


我们通过发现deque其实是没有想象中那样完美的,它与**vector和list相比是不够极致的**。vector是吕布,list是诸葛亮,那么deque就是魏延。所以更多的时候我们更需要极致。


deque的底层实现是比较复杂的,不仅仅是上诉简单两句的问题。


![](https://img-blog.csdnimg.cn/9f621516e6324b478fa0274e866b08d0.png)


根据上图,对于deque的维护是通过两个迭代器,start和finsh。因为daque是作为stack和queue的底层默认容器,一般来说deque是不需要进行中间插入的,那么start和finsh就很好的处理头插和尾插。它通过frist和last指向头尾,头插通过start的frist,如果满了node链接map新开辟buffer的指针位置。尾插通过finish的last控制。如果top()和back(),即通过start的cur和finish的cur控制。、


#### 3.4deque的接口


[deque文档的接口](https://bbs.csdn.net/topics/618545628)![](https://img-blog.csdnimg.cn/e3462bb923654189a575f4dc2eb84080.png)


![](https://img-blog.csdnimg.cn/8048ad75cd4e46ee9105678068974c54.png)


![](https://img-blog.csdnimg.cn/25cc01c583ee49148a0bd058111edf0a.png)![](https://img-blog.csdnimg.cn/ffbd20c8f6d94f54b7de717ab19d906b.png)


 通过stack,queue的接口与deque的接口对比,发现直接调用deque是非常适合充当stack,queue的默认容器。stack,queue就是直接调用deque的接口。


### 四、priority\_queue-优先级队列


优先队列是一种容器适配器,而它实质就是堆。是否还记得[堆](https://bbs.csdn.net/topics/618545628)是完全二叉树中用数组实现的,因为数组正好满足堆下标随机存取的需求,标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority\_queue类实例化指定容器类,则使用vector。相对deque,vector更加极致。priority\_queue是默认大根堆。




---


#### 4.1priority\_queue的使用


priority\_queue的使用来说也是比较简单的,接口也比较少。




|  |  |
| --- | --- |
| 函数声明 | 接口说明 |
| priority\_queue()/priority\_queue(first, last) | 构造一个空的优先级队列 |
| empty( ) | 检测优先级队列是否为空,是返回true,否则返回 false |
| top( ) | 返回优先级队列中最大(最小元素),即堆顶元素 |
| push(x) | 在优先级队列中插入元素x |
| pop() | 删除优先级队列中最大(最小)元素,即堆顶元素 |


对于priority\_queue的头文件,我们通过手册发现,priority\_queue与queue都是一个[头文件](https://bbs.csdn.net/topics/618545628)。


![](https://img-blog.csdnimg.cn/713c487749ae44a4a07e7ecbcd2f878e.png)


 接口演示:



//默认大根堆
void test()
{
priority_queue p;
p.push(7);
p.push(1);
p.push(9);
p.push(2);
p.push(3);
p.push(4);

while (!p.empty())
{
	cout << p.top() << " ";
	p.pop();
}

}



> 
> 结果:9 7 4 3 2 1
> 
> 
> 


小根堆 --greater



void test()
{
priority_queue<int, vector, greater > p;
p.push(7);
p.push(1);
p.push(9);
p.push(2);
p.push(3);
p.push(4);

while (!p.empty())
{
	cout << p.top() << " ";
	p.pop();
}

}



> 
> 结果:1 2 3 4 7 9
> 
> 
> 


#### 4.2模拟实现



#pragma once

namespace qhx
{
template <class T,class Container = vector>
class priority_queue
{
public:
template
priority_queue(InputIterator first, InputIterator last)
:_con(first,last)
{
//建堆-推荐向下调整建堆,时间复杂度更小
for (size_t i = (_con.size() - 1 - 1) / 2; i >= 0; --i)//
{
adjust_down(i);
}
}

	void adjust_up(size_t child)
	{

		size_t parent = (child - 1) / 2;
		while (child > 0)
		{
			if (_con[parent] < con[child])
			{
				swap(_con[child], _con[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}


	void push(const T& x)
	{
		_con.push_back(x);

		adjust_up(_con.size() - 1);
	}

	void adjust_down(size_t parent)
	{
		size_t michild = parent * 2 + 1;
		while (michild < _con.size())
		{
			if (michild< _con.size() && _con[michild]>_con[michild + 1])
			{
				michild++;
			}

			if ( _con[michild]>] < _con[parent])
			{
				swap(_con[michild], _con[parent]);
				parent = michild;
				michild = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}
		
	}

	void pop()
	{
		swap(_con[0], _con(_con.size(-1)));
		_con.pop_back();

		adjust_down(0);
	}

	const T&top()const
	{
		return _con[0];
	}
		
	const empty()const
	{
		return _con.empty();
	}

	size_t size()const
	{
		return _con.size();
	}

private:
	Container _con;
};

};


如果对向上/向下调整忘记了的,就可以看下面图片回忆。


向上调整 


#### 


 向下调整![](https://img-blog.csdnimg.cn/d844881756da41e4a7fa342d9197acf2.png)


### 五、仿函数/函数对象


仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个[operator](https://bbs.csdn.net/topics/618545628)(),这个类就有了类似函数的行为,就是一个仿函数类了。




---


#### 5.1仿函数的实现


这里是实现的比较大小的仿函数



#include
using namespace std;

//仿函数/函数对象
namespace qhx
{
template
class less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};

template<class T>
class greater
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x>y;
	}
};

};

int main()
{
qhx::less lessFunc;

if (lessFunc(3, 2))
	cout << "yes" << endl;
else
	cout << "no" << endl;

return 0;

}


#### 5.2仿函数的使用


冒泡排序



template

void BubbleSort(T* a, int n)
{
for (int i = 0; i < n; i++)
{
int flag = 0;
for (int j = 1; j < n - i; j++)
{
if (a[j - 1] > a[j])
swap(&a[j - 1], &a[j]);
flag = 1;
}
if (flag == 0)
break;

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

排序

template <class T>

void BubbleSort(T* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int flag = 0;
		for (int j = 1; j < n - i; j++)
		{
			if (a[j - 1] > a[j])
				swap(&a[j - 1], &a[j]);
			flag = 1;
		}
		if (flag == 0)
			break;


[外链图片转存中...(img-plxbYQpG-1715668800148)]
[外链图片转存中...(img-p4NjWgFT-1715668800148)]
[外链图片转存中...(img-qmdiqxVs-1715668800149)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值