位图 、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;
}
相关推荐
劲夫学编程33 分钟前
leetcode:杨辉三角
算法·leetcode·职场和发展
毕竟秋山澪35 分钟前
孤岛的总面积(Dfs C#
算法·深度优先
浮生如梦_3 小时前
Halcon基于laws纹理特征的SVM分类
图像处理·人工智能·算法·支持向量机·计算机视觉·分类·视觉检测
励志成为嵌入式工程师5 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉5 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer5 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
wheeldown6 小时前
【数据结构】选择排序
数据结构·算法·排序算法
观音山保我别报错7 小时前
C语言扫雷小游戏
c语言·开发语言·算法
TangKenny8 小时前
计算网络信号
java·算法·华为
景鹤8 小时前
【算法】递归+深搜:814.二叉树剪枝
算法