2025-09-23 学习日记

发布于 2025-09-23 12:00 1331 字 7 min read 手写 每个字都是我自己敲的。

2025-12-24 学习日记2025-12-17 学习日记2025-12-14 学习日记2025-12-09 学习日记2025-12-08 学习日记2025-12-07 学习日记2025-12-06 学习日记2025-12-05 学习日记2025-12-04 学习日记2025-12-03 学习日记2025-12-02 学习日记2025-12-01 学习日记2025-11-30 学习日记2025-11-29 学习日记2025-11-28 学习日记2025-11-27 学习日记2025-11-26 学习日记2025-11-25 学习日记2025-11-24 学习日记2025-11-23 学习日记2025-11-22 学习日记2025-11-21 学习日记2025-11-20 学习日记2025-11-19 学习日记2025-11-18 学习日记2025-11-17 学习日记2025-11-16 学习日记2025-11-15 学习日记2025-11-14 学习日记2025-11-13 学习日记2025-11-12 学习日记2025-11-11 学习日记2025-11-10 学习日记2025-11-09 学习日记2025-11-08 学习日记2025-11-07 学习日记2025-11-06 学习日记2025-11-05 学习日记2025-11-04 学习日记2025-11-03 学习日记2025-11-02 学习日记2025-11-01 学习日记2025-10-31 学习日记2025-10-30 学习日记2025-10-29 学习日记2025-10-28 学习日记2025-10-27 学习日记2025-10-26 学习日记2025-10-25 学习日记2025-10-24 学习日记2025-10-23 学习日记2025-10-22 学习日记2025-10-21 学习日记2025-10-20 学习日记2025-10-19 学习日记2025-10-18 学习日记2025-10-17 学习日记2025-10-16 学习日记2025-10-15 学习日记2025-10-14 学习日记2025-10-13 学习日记2025-10-11 学习日记2025-10-10 学习日记2025-10-09 学习日记2025-10-03 学习日记2025-10-02 学习日记2025-10-01 学习日记2025-09-30 学习日记2025-09-29 学习日记2025-09-28 学习日记2025-09-27 学习日记2025-09-26 学习日记2025-09-25 学习日记2025-09-24 学习日记2025-09-23 学习日记2025-09-22 学习日记2025-09-21 学习日记2025-09-20 学习日记2025-09-18 学习日记2025-09-17 学习日记2025-09-16 学习日记2025-09-15 学习日记2025-09-14 学习日记2025-09-13 学习日记2025-09-12 学习日记2025-09-11 学习日记2025-09-10 学习日记2025-09-09 学习日记2025-09-08 学习日记2025-09-04 学习日记2025-09-03 学习日记2025-09-02 学习日记2025-09-01 学习日记2025-08-31 学习日记2025-08-29 学习日记2025-08-28 学习日记2025-08-27 学习日记2025-08-26 学习日记2025-08-25 学习日记2025-08-24 周记2025-08-23 学习日记2025-08-22 学习日记2025-08-21 学习日记2025-08-20 学习日记2025-08-19 学习日记2025-08-18 学习日记2025-08-17 周记2025-08-16 学习日记2025-08-15 学习日记2025-08-14 学习日记2025-08-13 学习日记2025-08-12 学习日记2025-08-11 学习日记
今日学习记录 1. 设计路由器leetcode.3508 关键词: 问题分析: 这个题有点难,以下是几个难点分析 怎么去重三个值 题目中的这个装时间数组的队列似乎无法正常工作(如果直接使用队列) 核心思路 首先就是这个三个数据同时存在的去重,我们都知道去重直接使用 set 就行,可是对于这个三个值的对象 tuple,set...

今日学习记录

1. 设计路由器leetcode.3508

关键词:设计 问题分析:

  1. 这个题有点难,以下是几个难点分析
    • 怎么去重三个值
    • 题目中的这个装时间数组的队列似乎无法正常工作(如果直接使用队列)

核心思路

  1. 首先就是这个三个数据同时存在的去重,我们都知道去重直接使用 set 就行,可是对于这个三个值的对象 tuple,set 是无法获取这个对象的哈希值,如果不使用的话就要一个个存储然后检查是否相同,巨耗时间必超时,之前学过利用 long 的双整数二进制大小来捆绑两个值进行储存,但是这里是三个值也用不了,所以要快速查找去重,只能重写哈希值计算函数

c++ 17,最最通用平衡最好

使用了0x9e3779b9这个约等于黄金比例数(32 位),目的是增加随机性和分散性

struct TupleHash{

    template<typename T>

    static void hash_combine(size_t& seed,const T& v){

        seed ^= hash<T>{}(v)+0x9e3779b9+(seed<<6)+(seed>>2);

    }

    size_t operator()(const tuple<int,int,int>&t)const{

        auto &[a,b,c] = t;

        size_t seed = 0;

        hash_combine(seed,a);

        hash_combine(seed,b);

        hash_combine(seed,c);

        return seed;

    }

};

c++11 以上除了 auto 不能用之外没区别

追求极致性能

使用的是FNV这个非加密哈希函数 核心思想是:使用质数乘法来混合每个字节 在 32 位中:

  • 初始值为 2166136261U
  • 质数: 16777619U 在 64 位数中:
  • 初始值为 14695981039346656037ULL
  • 质数: 1099511628211ULL
struct TupleHash{

    size_t operator()(const tuple<int,int,int>&t)const{

        const auto &a = get<0>(t);

        const auto &b = get<1>(t);

        const auto &c = get<2>(t);

  

        size_t seed = 2166136261U;

        seed = (seed^a)*16777619U;

        seed = (seed^b)*16777619U;

        seed = (seed^c)*16777619U;

        return seed;

    }

};

追求极致质量

使用 splitmix64 来进行操作,性能上虽然有损失,但是哈希冲突概率极低

	struct EnhancedTupleSplitMix64Hash {
		//定义黄金比例常数为初始种子
	    static constexpr uint64_t prime1 = 0x9e3779b97f4a7c15ULL;  // 64位黄金比例
	    //混合质数
	    static constexpr uint64_t prime2 = 0xbf58476d1ce4e5b9ULL;  // SplitMix64 常数1
	    static constexpr uint64_t prime3 = 0x94d049bb133111ebULL;  // SplitMix64 常数2
	    static uint64_t splitmix64(uint64_t x) {
	        x = (x ^ (x >> 30)) * prime2;
	        x = (x ^ (x >> 27)) * prime3;
	        return x ^ (x >> 31);
	    }
	    static uint64_t hash_int(int value) {
	        return splitmix64(static_cast<uint64_t>(value) ^ prime1);
	    }
	    size_t operator()(const std::tuple<int, int, int>& t) const {
	        auto& [a, b, c] = t;
	        uint64_t h = hash_int(a);
	        h = (h ^ (h >> 27)) * prime3 + hash_int(b);
	        h = (h ^ (h >> 27)) * prime3 + hash_int(c);
	        // 最终混合
	        h = splitmix64(h);
	        return static_cast<size_t>(h);
	    }
	};
  1. 还有就是这个存储时间的数组,因为题目本质上是需要(先进先出)队列实现的,但是要查询,还要存储一个时间的数组,用队列明显是实现不了的,所以使用数组来模拟队列的出队的操作,首先就是查询,就是 map,key 为地址,value 为时间的数组,但是怎么模拟队列呢,我们要定义一个常数指向队头,用来模拟出队的操作,每次出队的时候这个队头向右移,那么进一步优化,我们把这个值与时间数组绑在一起,就有了 pair<时间数组,队头>

**最终代码:

	struct TupleHash{
	
	    template<typename T>
	
	    static void hash_combine(size_t& seed,const T& v){
	
	        seed ^= hash<T>{}(v)+0x9e3779b9+(seed<<6)+(seed>>2);
	
	    }
	
	    size_t operator()(const tuple<int,int,int>&t)const{
	
	        auto &[a,b,c] = t;
	
	        size_t seed = 0;
	
	        hash_combine(seed,a);
	
	        hash_combine(seed,b);
	
	        hash_combine(seed,c);
	
	        return seed;
	
	    }
	
	};
	
	class Router {
	
	    int len = 0;//限制最大数量
	
	    queue<tuple<int,int,int>> q;
	
	    unordered_set<tuple<int,int,int>,TupleHash> st;
	
	    //地址值,地址值对应的所有时间
	
	    unordered_map<int,pair<vector<int>,int>> mp;
	
	public:
	
	    //此处直接设置大小变量就行
	
	    //初见为队列
	
	    Router(int memoryLimit) {
	
	        len = memoryLimit;
	
	    }
	
	    //用数组来装
	
	    //但是有一个在队列中查找重复的操作,无论是那种队列结构都不适合查找
	
	    //map要来储存所有的数据,不能用于单个去重
	
	    //使用set的去重来检查是否重复
	
	    bool addPacket(int source, int destination, int timestamp) {
	
	        auto pak = tuple(source,destination,timestamp);
	
	        if(!st.insert(pak).second) return false;
	
	        q.push(pak);
	
	        if(q.size()>len) forwardPacket();
	
	        mp[destination].first.push_back(timestamp);
	
	        return true;
	
	    }
	
	    //要出队的话,有很多东西要注意
	
	    vector<int> forwardPacket() {
	
	        if(q.empty()) return {};
	
	        auto it = q.front();
	
	        q.pop();
	
	        st.erase(it);
	
	        auto [source,destination,timestamp] = it;
	
	        mp[destination].second++;
	
	        return {source,destination,timestamp};
	
	    }
	
	    //这里要查找时间范围的上界和下界,使用两个二分查找
	
	    //但是队列中的时间数据要单调,单调队列吗
	
	    //在查找时间的前提是地址相同,所以以地址为主头
	
	    int getCount(int destination, int startTime, int endTime) {
	
	        auto &[arr,head] = mp[destination];
	
	        auto left = ranges::lower_bound(arr.begin()+head,arr.end(),startTime);
	
	        auto right = ranges::upper_bound(arr.begin()+head,arr.end(),endTime);
	
	        return right-left;
	
	    }
	
	};

学到的点:

  1. 黄金比例数
  2. FNV
  3. splitmix64

执笔 手写

随机文章

加载中...