栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

基础数据结构

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基础数据结构

一、单调栈

        栈是一种特殊线性表,其特点是后进先出或先进后出

        在代码实现中,可以用数组+变量手写栈,也可以调用STL库中的栈容器

栈是维护数据局部关系的一种数据结构,单调栈则是对这一属性的极致应用

简单来说,单调栈就是栈中的元素满足单调关系(单调上升,单调下降,单调不上升,单调不下降)

举个例子,假设要构造个单调上升的栈,那么先压入第一个数据后,如果下一个数据大于栈顶元素,就将这个数据压入栈中;如果下一个数据小于等于栈顶元素,那么就不断弹出栈顶元素,然后接着判断该元素与新的栈顶元素的关系(开一个while循环,条件为栈非空且该元素小于等于栈顶元素,循环体为不断弹出栈顶元素) 

在做题时,要根据题目描述判断如何构造单调栈,同时要注意栈中元素之间的关系(累加等),同时也可以利用单调关系遍历栈中的元素

例题
乱头发节 题目描述

贤正的某 N 头奶牛 (1 <= N <= 80,000) 正在过乱头发节!由于每头牛都 意识到自己凌乱不堪的发型, 宁智贤希望统计出能够看到其他牛的头发的牛的数量。

每一头牛 i有一个高度 h[i] (1 <= h[i] <= 1,000,000,000)而且面向东方排成 一排(在我们的图中是向右)。因此,第i头牛可以看到她前面的那些牛的头, (即i+1, i+2,等等),只要那些牛的高度严格小于她的高度。

例如这个例子:

                                     

牛#1 可以看到牛 #2, 3, 4的发型 ;牛#2 不能看到任何牛的发型; 牛#3 可以看到牛#4的发型;  牛#4 不能看到任何牛的发型 ;牛#5 可以看到牛#6的发型 ;牛#6 不能看到任何牛的发型!

让 c[i] 表示第i头牛可以看到发型的牛的数量;请输出 c[1] 至 c[N]的和。 如上面的这个例子,正确解是3 + 0 + 1 + 0 + 1 + 0 = 5。

输入格式
  • Line 1: 牛的数量 N。

  • Lines 2..N+1: 第 i+1 是一个整数,表示第i头牛的高度。

输出格式

Line 1: 一个整数表示c[1] 至 c[N]的和。

样例数据

input

6
10
3
7
4
12
2

output

5
数据规模与约定

时间限制:1texts

空间限制:256textsMB

        每只牛都是向右侧看,它所看到的牛必须严格小于它的高度,并且牛与牛之间的高度关系是局部关系,所以考虑维护一个单调下降的单调栈

#include
using namespace std;
int n;
long long a[90000];
long long ans;
stack st;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		while(!st.empty()&&a[i]>=st.top())
		{
			st.pop();
		}
		st.push(a[i]);
		ans+=st.size()-1;
	}
	cout<



二、单调队列

        队列也是一种特殊的线性表,它和栈不同,它是允许在一段进行插入,在另一端进行删除的线性表。允许插入的一端称为队尾,通常用一个队尾指针tail指向队尾元素,即tail总是指向最后被插入的元素;允许删除的一端称为队首,通常也用一个队首指针head指向排头元素的前面。

        队列的特点是先进先出,主要维护数据的整体顺序,一旦数据进入队列,其出队顺序就固定下来,即进队序列一旦是确定的,出队序列也是唯一确定的,这一点和栈是不同的。

        队列可以有单向操作的queue,也有可双端操作的deque(这两种STL库中都提供),双端的deque的两端都可以进行插入删除操作,函数调用也和queue大同小异,只是需要点明插入或删除的位置

        下面给出两种队列常用的基础函数,如果有兴趣学习更多可以自行搜索

单端队列:
queue que;
que.push(a);//插入操作
que.pop();//删除操作
que.front();//取出队首元素
que.empty();//判断队列是否为空
que.size();//返回队列元素个数

/
双端队列:
deque que;
que.push_front(a);//队首插入元素
que.push_back(a);//队尾插入元素
que.pop_front();//删除队首元素
que.pop_back();//删除队尾元素
que.empty();//判断队列是否为空
que.size();//返回队列元素个数

        同单调栈一样,单调队列也在维护数据间的单调关系,但其维护的更侧重于整体的关系,如单调栈维护两两数据间的大小关系,而单调队列则在寻找一段区间内的极值时常用 。

 例题 题目描述

给你一个长度为 N 的数组,一个长为 K 的滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,如下表:

                         

你的任务是找出窗口在各位置时的 max value,min value.

输入格式

第 1 行 n,k,

第 2 行为长度为 n 的数组

输出格式

2 行,

第 1 行每个位置的 min value,

第 2 行每个位置的 max value

样例数据

input

8 3 
1 3 -1 -3 5 3 6 7

output

-1 -3 -3 -3 3 3 
3 3 5 5 6 7
数据规模与约定

20%: n<=500;  

50%: n<=100000;

100%: n<=1000000;

时间限制:1texts

空间限制:256textMB

        窗口是可以滑动的,并且有一定的长度限制,那么我们就可以把它看作一个有长度限制的队列

        在本题中,我们要寻找窗口内的最大值和最小值,那么我们就可以构造两个单调栈:当求最小值时,构造一个单调上升的队列,那么该窗口内的最小值就始终是队列的队首元素;当求最大值时,构造一个单调下降的队列,那么该窗口内的最大值就始终是队列的队首元素。

        然后在考虑如何维护区间的长度限制:当队首元素的下标与队尾元素的下标相差超过该长度限制时,就将队首元素弹出,以保持区间的长度限制

        那么在这道题的操作中,我们发现:在构造单调队列时,我们需要在队尾同时弹出和插入元素,在维护区间长度时,我们需要从队首弹出元素,所以这道题我们运用双端队列deque进行操作。

#include
using namespace std;
struct node
{
	int num,pla;
}window[1010000];
int n,k;
deque que1,que2;
void init()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>window[i].num;
		window[i].pla=i;
	}
}
void minn()//求最小值
{
	que2.push_back(window[1]);
	for(int i=2;i<=n-1;i++)
	{
		while(!que2.empty()&&window[i].num=k) cout<que1.back().num)//构造单调下降队列
		{
			que1.pop_back();
		}
		que1.push_back(window[i]);
		if(que1.front().pla=k) cout<que1.back().num)//最后一个答案后没有空格,单独处理一次
		{
			que1.pop_back();
		}
		que1.push_back(window[n]);
		if(que1.front().pla 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1012170.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号