位图 、Max Sum、滑动窗口

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

判断字符是否唯一

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

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

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

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

位图标记:

0表示没有出现过

1表示出现过

遇到出现过的直接返回false

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

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

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

复制代码
(n>>x) & 1

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

复制代码
n = n | (1 << x)

代码:

复制代码
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代码:

复制代码
#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代码:

复制代码
#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;
}
相关推荐
生锈的键盘6 分钟前
推荐算法实践:movielens数据集
算法
董董灿是个攻城狮7 分钟前
Transformer 通关秘籍9:词向量的数值实际上是特征
算法
代码AC不AC16 分钟前
【数据结构】队列
c语言·数据结构·学习·队列·深度讲解
林泽毅16 分钟前
SwanLab x EasyR1:多模态LLM强化学习后训练组合拳,让模型进化更高效
算法·llm·强化学习
小林熬夜学编程18 分钟前
【高并发内存池】第八弹---脱离new的定长内存池与多线程malloc测试
c语言·开发语言·数据结构·c++·算法·哈希算法
刚入门的大一新生24 分钟前
归并排序延伸-非递归版本
算法·排序算法
独好紫罗兰29 分钟前
洛谷题单3-P1980 [NOIP 2013 普及组] 计数问题-python-流程图重构
开发语言·python·算法
独好紫罗兰34 分钟前
洛谷题单3-P1009 [NOIP 1998 普及组] 阶乘之和-python-流程图重构
开发语言·python·算法
曦月逸霜1 小时前
蓝桥杯高频考点——高精度(含C++源码)
c++·算法·蓝桥杯
ゞ 正在缓冲99%…1 小时前
leetcode152.乘积最大子数组
数据结构·算法·leetcode