powerfuly

导航

算法day3

今天主要是哈希,像什么哈希表啊,哈希函数啊,还有哈希的底层含义,有点难倒也是说,有几个难点和不懂的地方确实难以理解,只能就着代码一点一点的看了,今天写的代码附下:
// A3 · 哈希表:去重、计数、流表聚合
//
// 对应 mini-ids 的核心场景:把包聚合成流(五元组 -> 包数/字节数)。
// 这正是 W4/W6 要写的流表雏形,只不过这里的输入是"已经解析好的包记录"。
// 运行:./build/a3_exercises

include

include

include

include

include

include <unordered_map>

include <unordered_set>

include

include

include "checks.hpp"

// ---------------------------------------------------------------------------
// 题 1:数组里有没有重复元素?用哈希做 O(n)(不要写双重循环)
bool have_duplicate(const std::vector& v) {
if (v.empty()) {
return false;
}
std::unordered_map<int, int> seed;
for (int i : v) {
if (seed.count(i) ){
return true;
}
seed[i]++;
}
return false;
}

// ---------------------------------------------------------------------------
// 题 2:Top-K 高频单词(次数相同按字典序)。用 unordered_map 计数。
std::vectorstd::string top_k_words_fast(const std::vectorstd::string& words, std::size_t k) {
if (words.empty()) {
return {};
}
std::vectorstd::string res = {};
std::unordered_map<std::string, int> seed;
for (auto& word : words) {
seed[word]++;
}
std::vector<std::pairstd::string,int>vec(seed.begin(),seed.end());
std::sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) {
if (a.second != b.second)return a.second > b.second;
return a.first < b.first;
});
for (size_t i = 0; i < k && i < vec.size(); ++i) {
res.push_back(vec[i].first);
}
return res;
}

// ---------------------------------------------------------------------------
// 下面两个类型是"已经解析好的包记录"和"流的统计结果"(已给出,不用改)
struct PacketRecord {
uint32_t src_ip = 0;
uint32_t dst_ip = 0;
uint16_t src_port = 0;
uint16_t dst_port = 0;
uint8_t proto = 0; // 6 = TCP, 17 = UDP
std::size_t bytes = 0;
};

struct FlowStats {
std::size_t packets = 0;
std::size_t bytes = 0;
};

// ---------------------------------------------------------------------------
// 题 3【项目连接】:流的五元组作为哈希表的键
// C++20 的 = default 会自动生成"逐字段比较"的 ==,不用手写
struct FlowKey {
uint32_t src_ip = 0;
uint32_t dst_ip = 0;
uint16_t src_port = 0;
uint16_t dst_port = 0;
uint8_t proto = 0;

bool operator==(const FlowKey& other) const = default;
};

// [TODO] 实现哈希函数:把 5 个字段"混"成一个 size_t
// 起步提示:h = h * 31 + field;更均匀的混合见参考答案
struct FlowKeyHash {
std::size_t operator()(const FlowKey& k) const {
size_t h = 0;
h = h * 31 + k.src_ip;
h = h * 31 + k.dst_ip;
h = h * 31 + k.src_port;
h = h * 31 + k.dst_port;
h = h * 31 + k.proto;
return h;
}
};

// ---------------------------------------------------------------------------
// 题 4【项目连接】:把包列表聚合成流表
std::unordered_map<FlowKey, FlowStats, FlowKeyHash> aggregate_flows(
const std::vector& packets) {
std::unordered_map<FlowKey, FlowStats, FlowKeyHash> res;
res.reserve(packets.size()); // 预分配桶,减少中途 rehash

for (const PacketRecord& p : packets) {
// ① 从包记录取出五元组,构造键(顺序必须和 FlowKey 字段一致)
const FlowKey key{p.src_ip, p.dst_ip, p.src_port, p.dst_port, p.proto};

// ② 拿到"这条流"的统计对象的引用:
// 已存在 -> 返回已有对象;不存在 -> 插入 FlowStats{}(packets=0, bytes=0)再返回引用
FlowStats& st = res[key];

// ③ 累加
st.packets += 1;
st.bytes += p.bytes;
}

return res;
}

// ===========================================================================
int main() {
std::cout << "A3 · 哈希表与流表\n";

CHECK(have_duplicate({1, 2, 3, 2}));
CHECK(!have_duplicate({1, 2, 3, 4}));
CHECK(!have_duplicate({}));

const auto top = top_k_words_fast({"a", "b", "a", "c", "a", "b"}, 2);
CHECK(top.size() == 2);
CHECK(top.size() == 2 && top[0] == "a");
CHECK(top.size() == 2 && top[1] == "b");
CHECK(top_k_words_fast({"x"}, 5).size() == 1);
CHECK(top_k_words_fast({}, 3).empty());

// 三条流:A 有 3 个包(600 字节)、B 一个 50 字节、C 一个 40 字节
const std::vector packets = {
{0x0a000001, 0x0a000002, 1234, 80, 6, 100}, // 流 A
{0x0a000001, 0x0a000002, 1234, 80, 6, 200}, // 流 A(同一条流)
{0x0a000001, 0x0a000002, 1234, 80, 6, 300}, // 流 A
{0x0a000003, 0x0a000002, 5000, 443, 6, 50}, // 流 B
{0x0a000004, 0x0a000002, 123, 53, 17, 40}, // 流 C(UDP)
};
const auto flows = aggregate_flows(packets);
CHECK(flows.size() == 3);

const FlowKey key_a{0x0a000001, 0x0a000002, 1234, 80, 6};
const auto it = flows.find(key_a);
CHECK(it != flows.end());
if (it != flows.end()) {
CHECK(it->second.packets == 3);
CHECK(it->second.bytes == 600);
}

// 两个只差一个字段的键,必须落到不同的桶里(哈希不能太粗糙)
const FlowKey key_b{0x0a000001, 0x0a000002, 1234, 80, 17};
CHECK(FlowKeyHash{}(key_a) != FlowKeyHash{}(key_b));

return report("A3");
}

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