前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >位图 、Max Sum、滑动窗口

位图 、Max Sum、滑动窗口

作者头像
咬咬
发布2024-07-20 12:55:56
650
发布2024-07-20 12:55:56
举报
文章被收录于专栏:学习笔记

这篇博客主要总结一下,这两天刷的算法题:

判断字符是否唯一

题目意思很简单不再解读,拿到这道题,其实不难看出哈希表可以直接秒了,注意这是一道面试题,在oj上可以哈希表秒了,如果面试官要求不使用数据结构呢,所以今天带大家用位图的方法来做一下这道题。

注意题目只有小写字母,所以只有26个字母,这里可以先用鸽巢原理优化一下,当给定数组大小大于26时可以直接返回false。

使用位图前首先建立字母和下标的映射:

这个映射建立很简单只需让 字符 - 'a' 即可得出。

位图标记:

0表示没有出现过

1表示出现过

遇到出现过的直接返回false

遇到没出现过的修改它对应的位图为1

这里需要用到两个二级结论:

 1.确定数n二进制第x位是0还是1

代码语言:javascript
复制
(n>>x) & 1

2.将数n二进制的第x位改成1 

代码语言:javascript
复制
n = n | (1 << x)

代码:

代码语言:javascript
复制
class Solution {
public:
    bool isUnique(string astr) {
        //利用鸽巢原理优化
        if(astr.size()>26) return false;
        int bitmap = 0;
        for(auto ch : astr)
        {
            int in = ch - 'a';//字母与下标形成映射
            if(((bitmap>>in) & 1) == 1) return false;//判断是否存在
            //代码走到这,说明不存在,修改位图
            bitmap = bitmap | (1<<in);
        }
        return true;
    }
};

Max Sum

题目为英文,我用AI翻译了。

思路:

这道题的关键思路是如何判断目前区间有没有结束,结束的意思也就是有没有走下去的必要。

我们用一个sum存数组开始位置到结束位置的和,如果sum现在已经是一个负数了,负数加上任何一个数都会比那个数小,此时end已经完全没有继续走下去的必要了,因为我们完全可以从这个数开始重新对sum开始求和,这个sum肯定比之前的sum要大

同理,如果sum是正数,end还可以继续往下走。

每次走的时候注意更新max和开始结束位置即可

AC代码:

代码语言:javascript
复制
#include<iostream>
#include<vector>
using namespace std;
int main()
{
	int T = 0;
	cin >> T;
	int count = 1;
	while (T--)
	{
		int n = 0;
		cin >> n;
		vector<int> num(n);
		for (int i = 0;i < n;i++) cin >> num[i];
		int max = num[0];
		int sum = num[0];
		int start = 0, end = 0;//当前的开始和结束位置
		int max_start = 0, max_end = 0;//最大子序列的开始和结束位置
		for (int i = 1;i < n;i++)
		{
			if (sum >= 0)//如果前面序列的和为正数
			{
				sum = sum + num[i];//将下一个数加入sum
				end++;//终点往后移
			}
			else//前面序列和为负数,这个区间走完了
			{
				sum = num[i];//从i位置重新开始一个区间
				start = end = i;
			}
			if (sum > max)//更新max
			{
				max = sum;
				max_start = start;
				max_end = end;
			}
		}
		cout << "Case " << count++ << ":" << endl;
		cout << max << " " << max_start + 1 << " " << max_end + 1;
	}
	return 0;
}

滑动窗口 / 单调队列

这道题我的第一思路是用滑动窗口,每次进窗口用变量更新最大值和最小值,但是难点就在出窗口,出窗口时如果出的是最大值和最小值,最大值和最小值该如何更新是个难点。

然后我想到用大堆和小堆来更新最大值和最小值,但是大堆和小堆只能移除堆顶元素,如果出的既不是最大值也不是最小值就出不了窗口。

最终我终于发现了这道题的妙处,当我们出的元素既不是最大值也不是最小值时,其实不必出窗口因为他们既不是最大值也不是最小值,在窗口里对我们的结果毫无影响,只要当最大值最小值距离窗口右边的位置大于k时,这时再用while出窗口,不仅最大值和最小值能出窗口,还会通过循环将之前那些既不是最大值也不是最小值的元素一并带出

AC代码:

代码语言:javascript
复制
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
struct num//定义该结构体目的为同时存储下标
{
	int i,val;
	bool operator > (const num x) const { return (val > x.val) || (val == x.val && i > x.i); }
	bool operator < (const num x) const { return (val < x.val) || (val == x.val && i < x.i); }
};
int main(){
	priority_queue<num> q1;//大堆
	priority_queue<num,vector<num>,greater<num>>q2;//小堆
	int n, k;
	cin >> n >> k;
	vector<int> arr(n);
	for (int i = 0;i < n;i++)cin >> arr[i];
	vector<int> ret1(n);
	vector<int> ret2(n);
	int m = 0;
	for (int left = 0, right = 0;right < n;right++)
	{
		num in;
		in.i = right;
		in.val = arr[right];
		q1.push(in);
		q2.push(in);//进窗口
		while ((right - q1.top().i + 1) > k)q1.pop();//当最大值或者最小值出窗口是才会真正意义上出窗口
		while ((right - q2.top().i + 1) > k)q2.pop();
		if (right >= k - 1)//存结果
		{
			ret1[m] = q1.top().val;
			ret2[m] = q2.top().val;
			m++;
		}
	}
	for (int i = 0;i < m;i++)cout << ret2[i] << " ";
	cout << endl;
	for (int i = 0;i < m;i++)cout << ret1[i] << " ";
	return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-07-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 判断字符是否唯一
    •  1.确定数n二进制第x位是0还是1
      • 2.将数n二进制的第x位改成1 
      • Max Sum
      • 滑动窗口 / 单调队列
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档