powerfuly

导航

算法day2

今天写了双指针算法,可以用于快慢指针求最大和滑动窗口问题,代码如下,时常复习:
`// A2 · 数组、双指针、滑动窗口
//
// 对应 mini-ids 的场景:按时间窗口统计流量(每 10 秒多少包、哪个窗口突然暴涨)
// 核心思想:窗口在数组上"滑动"时,不要每次重新算一遍,而是"进一个、出一个"。
// 运行:./build/a2_exercises

include

include

include

include

include <unordered_map>

include

include "checks.hpp"

// ---------------------------------------------------------------------------
// 题 1:已排序数组原地去重,返回去重后的长度。
// 要求 O(n) 时间、O(1) 额外空间(不许再开一个数组)
// 例:{1,1,2,2,3,4,4} -> 返回 4,且 v 的前 4 个元素变成 {1,2,3,4}
// 提示:双指针 —— 一个读指针 i 扫描,一个写指针 w 记录"下一个要写的位置"
std::size_t dedup_in_place(std::vector& v) {
if (v.empty()) {
return 0;
}
size_t slow = 0;
for (size_t fast = 1; fast < v.size(); ++fast) {
if (v[fast] != v[slow]) {
++slow;
v[slow] = v[fast];
}
}
return (slow + 1);
}

// ---------------------------------------------------------------------------
// 题 2:固定长度窗口:长度为 k 的连续子数组的最大和。
// 要求 O(n)(不要写成对每个窗口再求和的 O(n*k))
// k == 0 或 k > v.size() 时返回 0。
long long max_fixed_window_sum(const std::vector& v, std::size_t k) {
if (k == 0 || k > v.size() || v.empty()) {
return 0;
}
long long res = 0;
for (size_t i = 0; i < k; i++) {
res += v[i];
}
size_t slow = 0;
long long maxl = res;
for (size_t fast = slow + k; fast < v.size(); ++fast) {
res = res - v[slow] + v[fast];
if (maxl < res) {
maxl = res;
}
slow++;
}
return maxl;
}

// ---------------------------------------------------------------------------
// 题 3:最长无重复字符子串的长度(变长滑动窗口 + 哈希表)
// 例:"abcabcbb" -> 3("abc");"bbbbb" -> 1;"" -> 0
std::size_t longest_unique_substring(const std::string& s) {
if (s.empty()) {
return 0;
}
std::unordered_map<char, size_t> hax = {};
size_t res = 0;
size_t left = 0;
for (size_t i = 0; i < s.size(); ++i) {
char ch = s[i];//拿到下标
if (hax.count(ch)&&hax[ch]>=left) {//count函数判断是否存在ch
left = hax[ch] + 1;
}
hax[ch] = i;
res = std::max(res, i - left + 1);
}
return res;
}

// ---------------------------------------------------------------------------
// 题 4【项目连接】:给定按时间升序排列的包时间戳(单位:秒),
// 求任意左闭右开窗口 [t, t + window) 内最多能装多少个包。
// 例:{0.0, 0.5, 0.9, 1.0, 1.5, 2.0},window = 1.0 -> 3
// (窗口 [0.0,1.0) 装 0.0/0.5/0.9;[0.5,1.5) 装 0.5/0.9/1.0;[0.9,1.9) 装 0.9/1.0/1.5)
// 提示:这是"变长滑动窗口"——右指针一路前进,左指针追着 window 的边界跑。
std::size_t max_packets_in_window(std::vector timestamps, double window) {
if (timestamps.empty() || window <= 0) {
return 0;
}
size_t res = 0;
size_t left = 0;
for (size_t right = 0; right < timestamps.size(); ++right) {
while (timestamps[right] >= timestamps[left] + window) {
left++;
}
res = std::max(res, right - left + 1);
}
return res;
}

// ===========================================================================
int main() {
std::cout << "A2 · 数组与滑动窗口\n";

std::vector v1{1, 1, 2, 2, 3, 4, 4};
CHECK(dedup_in_place(v1) == 4);
CHECK(v1.size() >= 4 && v1[0] == 1 && v1[1] == 2 && v1[2] == 3 && v1[3] == 4);

std::vector v2{5};
CHECK(dedup_in_place(v2) == 1);

std::vector v3;
CHECK(dedup_in_place(v3) == 0);

CHECK(max_fixed_window_sum({1, 2, 3, 4, 5}, 2) == 9); // 4+5
CHECK(max_fixed_window_sum({5, 1, 1, 1}, 2) == 6); // 5+1
CHECK(max_fixed_window_sum({1, 2, 3}, 0) == 0);
CHECK(max_fixed_window_sum({1, 2, 3}, 5) == 0);
CHECK(max_fixed_window_sum({-3, -1, -2}, 2) == -3); // 全负数也要对

CHECK(longest_unique_substring("abcabcbb") == 3);
CHECK(longest_unique_substring("bbbbb") == 1);
CHECK(longest_unique_substring("pwwkew") == 3);
CHECK(longest_unique_substring("") == 0);

CHECK(max_packets_in_window({0.0, 0.5, 0.9, 1.0, 1.5, 2.0}, 1.0) == 3);
CHECK(max_packets_in_window({1.0, 1.1, 1.2}, 1.0) == 3);
CHECK(max_packets_in_window({1.0, 1.5}, 10.0) == 2);
CHECK(max_packets_in_window({}, 1.0) == 0);

return report("A2");
}`

posted on 2026-10-18 15:14  powerfuly  阅读(2)  评论(0)    收藏  举报