LeetCode 374 使用循环代替递归的二分查找从而避免栈溢出的错误

题意是这样的:


大致的意思就是会给出一个n,然后在[1, n]中间挑一个数,题目会给一个方法guess(int num)来帮助判断你猜的数是否正确,如果你猜的数和题目挑的数一样,返回0,如果题目挑的数更小,返回-1,如果题目挑的数更大,返回1。

一开始拿到题目感觉这就是一个二分查找,所以就用二分查找的方法做了,用了递归

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        return myGuessNumber(1, n);
    }
    
    private int myGuessNumber(int begin, int end) {
        if (begin == end) {
            return begin;
        }
        int middle = (begin + end) / 2;
        int result = guess(middle);
        if (result == 0) {
            return middle;
        } else if (result < 0) {
            return myGuessNumber(begin, middle - 1);
        } else {
            return myGuessNumber(middle + 1, end);
        }
    }
}
测试了一下没有问题,就提交了,结果报了栈溢出



这下就比较尴尬了,还想着是不是要换方法,但是数据量这么大的话,还是要用二分查找会比较快,要不然可能会超时。想了半天,原来二分查找还可以用循环来实现

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        return myGuessNumberLoop(1, n);
    }
    
    private int myGuessNumberLoop(int begin, int end) {
        while (begin < end) {
            int middle = (begin + end) / 2;
            int result = guess(middle);
            if (result == 0) {
                return middle;
            }
            if (result < 0) {
                end = middle - 1;
            } else {
                begin = middle + 1;
            }
        }
        return begin;
    }
}
提交后报了另外一个错误,超时


感觉已经没招了,只能去网上搜资源了,这时候看到另外一种写法,尝试了一下,其实只是修改了一行

int middle = (begin + end) / 2;
替换为

int middle = begin + (end - begin) / 2;
然后就过了。。。

只能根据答案猜原因了,这句话是放在循环中执行的,当数据比较大的时候,begin + end 可能会溢出,也有可能是比较大的数的除法比比较小的数的除法要耗时,所以导致上面一种写法会超时但是下面的写法就不会超时。

看了一下网站给出的答案,网站一共给出了三种做法,第一种是直接遍历,肯定超时。第二种是循环的二分查找,第三种是循环的三分查找,这里贴一下代码

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        int low = 1;
        int high = n;
        while (low <= high) {
            int mid1 = low + (high - low) / 3;
            int mid2 = high - (high - low) / 3;
            int res1 = guess(mid1);
            int res2 = guess(mid2);
            if (res1 == 0)
                return mid1;
            if (res2 == 0)
                return mid2;
            else if (res1 < 0)
                high = mid1 - 1;
            else if (res2 > 0)
                low = mid2 + 1;
            else {
                low = mid1 + 1;
                high = mid2 - 1;
            }
        }
        return -1;
    }
}

<think>好的,我现在需要帮用户解决关于蓝桥杯比赛中出现TLE的问题。用户想知道原因和解决方法。首先,我得回忆一下常见的TLE原因,比如算法复杂度高、输入输出效率低、递归深度过大等。蓝桥杯的题目通常对时间限制严格,特别是C/C++组可能更明显,但Python组因为本身速度慢,更容易遇到TLE。 接下来,要分点整理原因。首先算法复杂度方面,比如用了暴力搜索而没有优化,或者没有选择更高效的算法,比如该用动态规划却用了递归。然后输入输出问题,Python中使用input()可能较慢,需要换成sys.stdin.readline。再者,递归深度如果超过默认限制,会导致栈溢出或者超时,需要改循环或者设置递归深度。 优化技巧方面,可以建议剪枝策略,比如在DFS/BFS中提前终止不必要的分支。记忆化技术,比如用缓存减少重复计算。循环优化,比如减少嵌套循环,或者提前break。数据结构的选择,比如用集合查找代替列表遍历。预处理和前缀和的应用,减少重复计算。 另外,需要举例子,比如全排列问题用回溯法不加剪枝会超时,或者动态规划问题用递归导致重复计算。然后给出对应的优化代码示例,比如用lru_cache或者改成迭代方式。 还要注意引用用户提供的参考内容,引用1提到了DFS和BFS的重要性,所以在优化搜索算法时需要强调剪枝和记忆化。引用2提到省赛题解,可能涉及具体题目的优化方法,但用户没有具体题目,所以保持一般性建议。 最后,生成相关问题,比如如何分析代码时间复杂度,蓝桥杯常见优化技巧,调试TLE的方法,Python中加速输入输出的方法等。确保回答结构清晰,分点明确,有实例和代码示例,帮助用户理解。</think>### 蓝桥杯 TLE 问题原因及优化技巧 #### 一、TLE 常见原因分析 1. **算法时间复杂度高** - 暴力搜索未剪枝(如DFS/BFS未限制深度或分支)[^1] - 未选择合适算法(如应使用动态规划却用递归) - 多重循环嵌套导致计算量爆炸,例如$O(n^3)$算法处理$n=10^4$的数据 2. **输入输出效率低** - Python中使用`input()`逐行读取大数据量时速度不足 - 未使用缓冲区优化(如C++未关闭流同步、Python未使用`sys.stdin`) 3. **递归深度过大** - Python默认递归深度限制(约1000层),若未改为迭代或设置`sys.setrecursionlimit()`可能导致栈溢出 #### 二、优化技巧解决方案 1. **算法优化** - **剪枝策略**:在DFS中提前终止无效路径(如全排列问题中跳过重复元素) ```python def dfs(path, used): if len(path) == n: return for i in range(n): if used[i] or (i > 0 and nums[i] == nums[i-1] and not used[i-1]): # 剪枝条件 continue used[i] = True dfs(path + [nums[i]], used) used[i] = False ``` - **记忆化技术**:使用`@lru_cache`缓存重复计算结果(如斐波那契数列) - **循环优化**:减少嵌套层数,提前`break`或`continue` 2. **数据结构优化** - 用集合/字典替代列表查询($O(1)$ vs $O(n)$) - 使用堆(`heapq`)优化贪心算法 - 预处理数据(如计算前缀和$S_i = a_1 + a_2 + \dots + a_i$) 3. **输入输出加速** - Python推荐写法: ```python import sys input = sys.stdin.readline # 读取速度提升3-5倍 n = int(input()) ``` 4. **代码细节优化** - 避免全局变量(Python访问局部变量更快) - 减少函数调用次数(如将计算逻辑内联) - 使用位运算替代算术运算 #### 三、典型场景示例 **问题**:全排列II(LeetCode 47) **原始代码**(未剪枝导致TLE): ```python def permuteUnique(nums): res = [] def backtrack(path): if len(path) == len(nums): res.append(path) return for num in nums: if num not in path: # 错误!无法处理重复元素 backtrack(path + [num]) backtrack([]) return res ``` **优化后**(排序+剪枝): ```python def permuteUnique(nums): nums.sort() res = [] def backtrack(path, used): if len(path) == len(nums): res.append(path.copy()) return for i in range(len(nums)): if used[i] or (i > 0 and nums[i] == nums[i-1] and not used[i-1]): continue # 关键剪枝 used[i] = True backtrack(path + [nums[i]], used) used[i] = False backtrack([], [False]*len(nums)) return res ``` #### 四、调试建议 1. **测试边界数据**:使用$n=10^5$量级的数据验证算法可行性 2. **性能分析工具**:Python可用`cProfile`模块定位耗时函数 3. **复杂度估算**:通过数据规模反推可接受的时间复杂度,例如: $$T(n) \leq \frac{时间限制(秒) \times 10^8}{操作次数/秒}$$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值