如何基于Item对象的id与指向关系正确填充自定义Graph并解决段错误
段错误根因
当前崩溃的核心是邻接表的索引逻辑和实际节点ID的取值完全不匹配:
- 原Graph类的实现逻辑中,
adj是一个固定长度的原生数组,构造时传入的参数决定了数组长度,合法访问下标范围是0 ~ 长度-1 - 你构造Graph时传入的长度是
itemsFiltered.size()也就是10,意味着数组只分配了10个元素的内存空间,仅支持下标0到9的访问 - 但实际节点的ID是10000、10001这类万级离散值,调用
addEdge(10000, 10001)时,代码直接拿10000当数组下标访问adj[10000],远远超出了数组的合法内存边界,必然触发段错误。 - 后续BFS逻辑中
visited数组也是同样的问题,即使跳过addEdge的崩溃,执行BFS时用大ID当下标访问visited数组,依然会越界。
最小改造成本的修正方案
如果暂时不想重构Graph的数组实现逻辑,核心要补上ID到数组下标的映射转换步骤,不能直接把原始ID当数组下标用:
- 遍历所有Item实例,收集全部出现的节点ID(包括Item的id字段、pointingTo指向的目标ID,避免漏掉类似10004这种没有作为Item存在的节点)
- 对收集到的所有唯一ID,分配从0开始的连续内部下标,建立双向映射关系:
idToIndex[原始ID] = 内部数组下标,indexToId[内部下标] = 原始ID - 构造Graph时,传入的总节点数V等于实际收集到的所有唯一节点的数量,而非itemsFiltered的长度
- 调用addEdge时,把原始的id、pointingTo值通过映射转成内部下标,再传入addEdge方法做数组访问
- 执行BFS时,先把入口的原始节点ID转成内部下标,遍历完成后再把所有内部下标转回原始ID存入结果列表
当前场景下更合理的图实现
由于你的节点ID是跨度很大的离散长整型值,完全不适合用固定长度数组做邻接表存储,更适配的实现是把基于原生数组的邻接表替换为基于哈希结构的动态邻接表,从根源上消除越界问题:
- 把原有的
std::list<long> *adj原生数组,替换为std::unordered_map<long, std::list<long>>,以原始节点ID作为哈希表的key,完全不需要做ID到下标的转换 - Graph构造函数不再需要提前指定总节点数,依赖哈希表的动态扩容能力适配任意数量的节点
- addEdge方法直接以原始ID为key,把邻接节点值push到对应的列表中,不存在数组越界风险
- BFS逻辑中原有的固定长度visited数组,替换为
std::unordered_set<long>存储已访问的节点,同样不需要提前分配固定长度的内存。
修正后的核心代码片段参考:
#include <unordered_map> #include <unordered_set> #include <list> class Graph { // 动态邻接表,替代原有原生数组 std::unordered_map<long, std::list<long>> adj; public: // 无需提前传入总节点数 Graph() = default; // 直接使用原始ID做邻接表索引 void addEdge(long v, long w) { adj[v].push_back(w); } // BFS实现适配哈希结构 void BFS(long s, std::list<long>& listOfIDs) { // 用哈希集合替代原有的固定长度visited数组 std::unordered_set<long> visited; std::list<long> queue; visited.insert(s); queue.push_back(s); while(!queue.empty()) { s = queue.front(); listOfIDs.push_back(s); queue.pop_front(); // 无邻接表的节点直接跳过,不存在越界访问 if(adj.find(s) != adj.end()) { for(auto neighbor : adj[s]) { if(visited.find(neighbor) == visited.end()) { visited.insert(neighbor); queue.push_back(neighbor); } } } } } };
这种实现的优势:
- 完全适配当前离散、大跨度的ID取值,不需要额外维护映射表,代码逻辑更简洁
- 从机制上消除了固定数组越界的可能,不会触发段错误
- 针对小体量的节点集合,内存利用率远高于按最大ID值分配数组的实现方式,不会因为ID是万级就浪费数万长度的数组内存。
额外提示:原BFS方法的
listOfIDs参数是值传递,就算逻辑跑通,遍历得到的节点列表也不会回传给外部,需要改成引用传递才能正常拿到结果。
内容的提问来源于stack exchange,提问作者harunB10
相关产品推荐
相关产品推荐

