记录一下在leetcode上刷的滑动窗口的几道题,方便后边复习。
解析都放在代码里注释。
leetcode 15:
/*
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
*/
/*
用排序加双指针,这个题主要是去重
*/
#include
#include
#include
using std::cout;
using std::cin;
using std::vector;
using std::endl;
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
auto n = nums.size();
vector<vector<int>> res;
if (n < 3)
{
return {};
}
std::sort(nums.begin(),nums.end()); //先排序
for (int i = 0; i < n - 2; i++)
{
if (nums[i] > 0)
{
break;
}
if (i > 0 && nums[i - 1] == nums[i])
{
continue;
}
int L = i + 1; //两个指针一个指左边一个右边
int R = n - 1;
while (L < R)
{
if (nums[i] + nums[L] + nums[R] == 0)
{
res.push_back({ nums[i],nums[L],nums[R] });
while (nums[L + 1] == nums[L] && L < R)
{
L = L + 1;
}
while (nums[R - 1] == nums[R] && L < R)
{
R = R - 1;
}
L = L + 1;
R = R - 1;
}
else if(nums[i] + nums[L] + nums[R] > 0) //大于0的话就要减少 让右边的减小
{
R = R - 1;
}
else
{
L = L + 1;
}
}
}
return res;
}
};
leetcode 1423:
/*
几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。
每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。
你的点数就是你拿到手中的所有卡牌的点数之和。
给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。
示例 1:
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
#include
#include
using namespace std;
class Solution {
public:
int maxScore(vector<int>& cardPoints, int k) {
int n = cardPoints.size();
int max = 0;
//如果k要是大于等于n的话 就全加起来
if (k >= n)
{
for (int i = 0; i < n; i++)
{
max = max + cardPoints[i];
}
return max;
}
int ans;
int sum=0;
vector<int> vec(k,0);
//初始化滑动窗口
for (int i = 0; i < k; i++)
{
vec[i] = cardPoints[i];
sum = sum + cardPoints[i];
}
ans = sum;
//依次滑动
for (int i = 1; i <= k; i++)
{
sum = sum + (cardPoints[n - i] -vec[k-i]);
if (sum > ans)
{
ans = sum;
}
}
return ans;
}
};
leetcode 1052:
/*
今天,书店老板有一家店打算试营业 customers.length 分钟。每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开。
在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气,那么 grumpy[i] = 1,否则 grumpy[i] = 0。 当书店老板生气时,那一分钟的顾客就会不满意,不生气则他们是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 X 分钟不生气,但却只能使用一次。
请你返回这一天营业下来,最多有多少客户能够感到满意的数量。
示例:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], X = 3
输出:16
解释:
书店老板在最后 3 分钟保持冷静。
感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.
*/
#include
#include
#include
using namespace std;
class Solution {
public:
int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int X) {
int n = customers.size();
int i;
int ans=0;
for (i = 0; i < n; i++)
{
if (grumpy[i] == 0)
{
ans = ans + customers[i];
}
}
//发动技能的时候滑动窗口,窗口里面放下标
deque<int> vec(X,0);
for (i = 0; i < X; i++)
{
vec[i] =i;
if (grumpy[i] == 1)
{
ans = ans + customers[i];
}
}
int max = ans;
for (int k = 1; k < n - X + 1; k++)
{
if (grumpy[vec[0]] == 1)
{
max = max - customers[vec[0]];
}
vec.pop_front();
vec.push_back(X+k-1);
if (grumpy[X + k - 1] == 1)
{
max = max + customers[X + k - 1];
}
if (max > ans)
{
ans = max;
}
}
return ans;
}
};
leetcode 295:
/*
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
*/
/*
解析:
用两个堆,最大堆和最小堆,最大堆让小的数字一半在最大堆,最小堆让大数字的一半去最小堆。
因为最大堆是从大到小,最小堆是从小到大,所以呢,中位数就是堆顶。所以要做的就是平衡两个堆的数目
如果k是奇数 k = 2*n+1 则允许最大堆持有n+1个元素
当k 是偶数的时候,则两个堆都是n
*/
#include
#include
#include
using namespace std;
class MedianFinder {
priority_queue<int> lo; // 最大堆
priority_queue<int, vector<int>, greater<int>> hi; // 最小堆
public:
void addNum(int num)
{
lo.push(num);
hi.push(lo.top()); // 平衡
lo.pop();
if (lo.size() < hi.size()) {
lo.push(hi.top());
hi.pop();
}
}
// 返回中值
double findMedian()
{
return lo.size() > hi.size() ? (double)lo.top() : (lo.top() + hi.top()) * 0.5;
}
};
leetcode 480:
/*
中位数是有序序列最中间的那个数。如果序列的大小是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。
例如:
[2,3,4],中位数是 3
[2,3],中位数是 (2 + 3) / 2 = 2.5
给你一个数组 nums,有一个大小为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。
示例:
给出 nums = [1,3,-1,-3,5,3,6,7],以及 k = 3。
窗口位置 中位数 //排序的中位数 所以啊 理解错了
--------------- -----
[1 3 -1] -3 5 3 6 7 1
1 [3 -1 -3] 5 3 6 7 -1
1 3 [-1 -3 5] 3 6 7 -1
1 3 -1 [-3 5 3] 6 7 3
1 3 -1 -3 [5 3 6] 7 5
1 3 -1 -3 5 [3 6 7] 6
因此,返回该滑动窗口的中位数数组 [1,-1,-1,3,5,6]。
*/
/*
在295的基础上加些东西,也是用两个堆,但是加一个哈希表,里面添加那些被移出去的元素
一旦这些被移除去的元素在堆顶,那么就在堆顶删除(不是堆顶不好删除,所以等到在堆顶)
在删除,因为在堆里也不影响找中位数,还是堆顶那个。
但是注意保持平衡的时候,要算有效的数字,那么被移出窗口但是还没有从堆里删除的元素不能算进去
*/
#include
#include
#include
#include
#include
using namespace std;
class Solution {
public:
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
vector<double> res;
double ans;
unordered_map<int, int> hash_table; //哈希表保存被移出窗口的元素
priority_queue<int> max; //最大堆,里面保存小的那一半的数字
priority_queue<int, vector<int>, greater<int>> min; //最小堆,里面保存大的那一半的元素
int i = 0;
int out_num, in_num, balance;
//初始化两个堆
while (i < k)
{
max.push(nums[i]);
i++;
}
for (int j = 0; j < k / 2; j++)
{
min.push(max.top());
max.pop();
}
while (1)
{
if (k % 2 == 0) //如果k是偶数
{
ans = ((double)max.top() + (double)min.top()) / 2;
}
else
{
ans = max.top(); //因为我们是允许max里比min里多一个的
}
res.push_back(ans);
if (i >= n) //证明窗口滑完了
{
break;
}
out_num = nums[i-k]; //要被移出去的元素
in_num = nums[i++]; //要进来的元素
balance = 0;
//如果那个要移出去的元素是max里面的,那么balance就-1,反之+1
//因为我们规定max里面的元素比min里面的少 balance<0
balance += (out_num <= max.top() ? -1 : 1);
//移出去的元素放进哈希表里
hash_table[out_num]++;
//有出就有进
if (!max.empty() && in_num <= max.top()) //比max.top()小 那就是进入max里
{
balance++;
max.push(in_num);
}
else
{
balance--;
min.push(in_num);
}
//看是不是还平衡
if (balance < 0) //max里面可用的元素少
{
max.push(min.top());
min.pop();
balance++;
}
if (balance > 0)
{
min.push(max.top());
max.pop();
balance--;
}
//如果堆顶元素是被移出去的,要清理
while (hash_table[max.top()])
{
hash_table[max.top()]--;
max.pop();
}
while (!min.empty() && hash_table[min.top()])
{
hash_table[min.top()]--;
min.pop();
}
}
return res;
}
};