算法面试常考leetcode模版总结--dfs、回溯、单调栈模板、二分法、排序(快速排序,归并排序)总结

本文总结了算法面试中常见的dfs、回溯、单调栈模板,以及单源最短路径Dijkstra算法。同时详细介绍了二分法在查找相同值时的应用,并探讨了原地修改的快速排序和归并排序方法,对它们的时间和空间复杂度进行了分析。

模板

dfs

回溯法

三个核心要素:路径、选择列表、结束条件

result = []
路径 = []
def backtrack(ind):
	if ind 最后一个:
		res.append(路径)
		return 
	for 选择 in 选择列表:
		做选择
		backtrack(ind+1)
		撤销选择

单调栈

遇到比栈顶大的元素,直接append,
遇到比栈顶小的元素,找到合适的位置,替换。

单源最短路径Digkstra

节点分成两类:(1)已经确定距离的节点 (2)没有确定的节点
每次从未确定的节点中选择一个与起点距离最点的点,然后更新从起点到其他所有未确定节点的距离。

二分法

二分法查找相同值取最右边的插入位置

def bisect_right(a, x, lo=0, hi=None):
    """Return the index where to insert item x in list a, assuming a is sorted.

    The return value i is such that all e in a[:i] have e <= x, and all e in
    a[i:] have e > x.  So if x already appears in the list, a.insert(x) will
    insert just after the rightmost x already there.

    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = 
<think>好的,用户需要一种用C++实现的算法,以O(n)时间复杂度去除字符串中的重复字母。首先,我得理解问题要求。去除重复字母,同时保持原有顺序,并且时间复杂度线性。这让我想到可能需要使用哈希表结合的方法。 然后,我回忆常见的去重算法,比如暴力法,但那是O()的时间复杂度,不符合要求。另一个思路是贪心算法,每次选择最小的字符,但这可能无法保留顺序。所以可能需要优化贪心,结合结构。 接着,想到使用来维护结果,同时记录每个字符的出现次数。遍历字符串时,递减字符计数,如果当前字符已经在中,跳过。否则,循环检查顶字符是否大于当前字符,且后面还会出现,这样就可以弹出顶,保证最小字典序。不过用户的需求可能只是去重,不涉及字典序,但通常这类问题会要求最小字典序,比如LeetCode 316题。 然后,检查是否时间复杂度是O(n)。每个字符入一次,最多出一次,所以是O(n)的时间。空间上,使用几个数组记录状态,应该是O(1)因为字符集固定,比如小写字母是26个。 接下来,参考引用中的信息。用户提供的引用提到KMP算法的时间复杂度O(n),以及空间优化方法,这可能对处理字符串有帮助。不过具体到这个问题,可能需要不同的方法。 然后,编写代码的大致步骤:统计每个字符的出现次数,维护一个,记录字符是否已入。遍历字符串,每次处理字符时,减少计数。如果字符已在中,跳过。否则,循环弹出顶,当顶字符大于当前字符且后续还有该字符时。然后将当前字符入,标记为已存在。 需要注意处理字符的存在状态,比如使用布尔数组。最后,中的字符组成结果字符串。 测试案例,比如输入"bcabc",输出"abc";输入"cbacdcbc",输出"acdb"。 现在,考虑用户是否需要保留原有顺序或者最小字典序。题目描述是“去除重复字母”,通常是指每个字符只出现一次,且保持原有顺序的最小字典序,所以上述方法适用。 最后,确保代码正确,时间复杂度分析准确。这样就能满足用户的需求了。</think>### 解答:去除字符串重复字母的O(n)算法 要实现时间复杂度为$O(n)$的字符串去重算法,可以采用**+贪心策略**,结合哈希表记录字符出现频率。以下是详细实现步骤: --- #### 1. 算法思路 1. **统计字符频率**:使用数组`count[256]`记录每个字符剩余的出现次数。 2. **维护单调栈**:用保存最终结果,保证字符顺序最小字典序。 3. **标记已存在字符**:用布尔数组`inStack[256]`记录字符是否已加入。 4. **贪心移除顶**:若当前字符比顶字符小,且顶字符后续还会出现,则弹出顶。 --- #### 2. 代码实现 ```cpp #include <string> #include <vector> using namespace std; string removeDuplicateLetters(string s) { vector<int> count(256, 0); vector<bool> inStack(256, false); vector<char> stack; // 统计字符出现次数 for (char c : s) { count[c]++; } for (char c : s) { count[c]--; // 当前字符剩余次数减1 if (inStack[c]) continue; // 已存在则跳过 // 弹出顶比当前字符大且后续仍存在的字符 while (!stack.empty() && stack.back() > c && count[stack.back()] > 0) { inStack[stack.back()] = false; stack.pop_back(); } stack.push_back(c); inStack[c] = true; } return string(stack.begin(), stack.end()); } ``` --- #### 3. 复杂度分析 - **时间复杂度**:$O(n)$,每个字符最多入一次。 - **空间复杂度**:$O(1)$,辅助数组`count``inStack`大小固定为256,与输入规模无关。 --- #### 4. 示例测试 - 输入:`"bcabc"` → 输出:`"abc"` - 输入:`"cbacdcbc"` → 输出:`"acdb"` --- ### 相关问题 1. **如何保证去重后的字符串是字典序最小的?** 通过贪心策略结构,当遇到更小的字符且顶字符后续仍存在时,优先选择更小字符[^1]。 2. **如何处理非小写字母的情况?** 将辅助数组`count``inStack`的大小扩展为256即可支持ASCII字符集。 3. **该算法与暴力去重方法有何优势?** 暴力法需要双重循环,时间复杂度为$O(n^2)$,而此方法通过空间换时间实现线性复杂度[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值