C++ unordered_set插入触发Segmentation fault:11栈溢出排查
错误触发根因
崩溃核心原因是手写递归二分查找函数findind存在逻辑缺陷,触发无限递归导致栈溢出,具体问题点:
- 中点索引计算错误:代码中
int mid = (high - low)/2计算的是搜索区间长度的一半,并非区间内的中点索引,正确中点索引应为low + (high - low)/2,从根源上导致索引定位错误。 - 递归无收敛逻辑:当搜索区间缩小到相邻两个元素时(如high=3、low=1),错误的mid计算会让mid值固定不变,且调整high/low的逻辑没有排除已经比较过的mid位置,递归永远无法触达匹配返回分支,无限压栈最终耗尽栈内存,触发段错误(退出码139),对应LeetCode平台AddressSanitizer报出的栈溢出错误。
- 其他潜在问题:
findind的vector参数为值传递,每次递归都会完整拷贝一份数组,栈内存消耗速度翻倍;初始调用findind时传入的high参数是数组长度N,超过数组最大合法索引N-1,本身存在数组越界访问风险。
对应你观察到的异常现象:哈希集合us只有执行insert语句时才会存入遍历过的元素,把insert替换为打印语句后,us始终为空,永远走不到调用findind的else分支,自然不会触发无限递归导致的崩溃。
以第一个复现用例numbers = {-1,0,1,4,5,7}、target=8为例:遍历到最后一个元素7时,target-7=1已经在之前的循环中被插入us,此时触发findind调用,递归过程中会进入high=3、low=1的状态,此后mid永远计算为1,每次判断目标值1大于s[1]=0,就重复将low设为1,永远无法退出递归,直接触发栈溢出。
修复方案
- 修正二分查找函数的所有逻辑缺陷:
- 将vector参数改为const引用传递,避免递归时无意义的数组拷贝
- 修正中点索引的计算方式
- 增加区间无效的终止判断,调整边界收敛逻辑:比较完mid位置元素后,下一轮搜索直接排除mid点,确保区间每次递归都会缩小,从根本上避免死循环
- 修正初始调用findind时的high参数,传入数组最后一个合法索引
N-1
修正后的二分查找代码如下:
int findind(const vector<int>& s, int tar, int high, int low ){ // 搜索区间为空,终止递归 if (low > high) { return -1; } // 正确计算区间中点索引 int mid = low + (high - low) / 2; if(s[mid] == tar){ return mid; } else if (tar < s[mid]){ // 目标值更小,搜索左半区间,排除已比较的mid点 return findind(s, tar, mid - 1, low); } else{ // 目标值更大,搜索右半区间,排除已比较的mid点 return findind(s, tar, high, mid + 1); } }
同时将main函数中调用findind的语句修正为:
id2 = findind(numbers, target - numbers[i], N-1, 0);
- 可选优化:本题给定数组为严格升序排列,最优解法是使用左右双指针从两端向中间夹逼,不需要哈希集合也不需要二分查找,时间复杂度O(n)、空间复杂度O(1),代码更简洁不易出错。
内容的提问来源于stack exchange,提问作者A Hasnain Khan
相关产品推荐
相关产品推荐

