两种算法的Big-O分析
Big-O analysis of two algorithms
我为leetcode问题17创建了两个解决方案,其中它要求您从电话号码组合中生成所有可能的文本字符串,例如"3"
导致["d","e","f"]
。
我的第一个解决方案使用递归算法来生成字符串,如下所示:
class Solution {
public:
void fill_LUT(vector<string>& lut) {
lut.clear();
lut.push_back(" ");
lut.push_back("");
lut.push_back("abc");
lut.push_back("def");
lut.push_back("ghi");
lut.push_back("jkl");
lut.push_back("mno");
lut.push_back("pqrs");
lut.push_back("tuv");
lut.push_back("wxyz");
}
void generate_strings(int index, string& digits, vector<string>& lut, vector<string>& r, string& work) {
if(index >= digits.size()) {
r.push_back(work);
return;
}
char idx = digits[index] - '0';
for(char c : lut[idx]) {
work.push_back(c);
generate_strings(index+1, digits, lut, r, work);
work.pop_back();
}
}
vector<string> letterCombinations(string digits) {
vector<string> r;
vector<string> lut;
fill_LUT(lut);
if(digits.size() <= 0)
return r;
string work;
generate_strings(0, digits, lut, r, work);
return r;
}
};
我对big-O有点生疏,但在我看来,递归调用的空间复杂度为O(n)
,即其最大深度,缓冲字符串为O(n)
,结果字符串为O(n*c^n)
。这加起来会是O(n+n*c^n)
吗?
对于时间的复杂性,我有点困惑。每一级递归执行c
推送+弹出+递归调用乘以下一级的操作数,所以听起来像c^1 + c^2 + ... + c^n
。此外,还有n
长度字符串的c^n
重复如何将其合并为一个漂亮的big-O表示
第二种解决方案将结果数视为混合基数,并将其转换为字符串,就像您可能执行int到hex字符串转换一样:
class Solution {
public:
void fill_LUT(vector<string>& lut) {
lut.clear();
lut.push_back(" ");
lut.push_back("");
lut.push_back("abc");
lut.push_back("def");
lut.push_back("ghi");
lut.push_back("jkl");
lut.push_back("mno");
lut.push_back("pqrs");
lut.push_back("tuv");
lut.push_back("wxyz");
}
vector<string> letterCombinations(string digits) {
vector<string> r;
vector<string> lut;
fill_LUT(lut);
if(digits.size() <= 0)
return r;
unsigned total = 1;
for(int i = 0; i < digits.size(); i++) {
digits[i] = digits[i]-'0';
auto m = lut[digits[i]].size();
if(m > 0) total *= m;
}
for(int i = 0; i < total; i++) {
int current = i;
r.push_back(string());
string& s = r.back();
for(char c : digits) {
int radix = lut[c].size();
if(radix != 0) {
s.push_back(lut[c][current % radix]);
current = current / radix;
}
}
}
return r;
}
};
在这种情况下,我认为空间复杂度是O(n*c^n)
,类似于第一个解决方案减去缓冲区和递归,第一个for循环的时间复杂度必须是O(n)
,另外还有一个O(n*c^n)
,为每个可能的结果创建一个结果字符串。最后一个大O是O(n+n*c^n)
我的思维过程正确吗
编辑:要对代码进行澄清,请想象一个"234"
的输入字符串。第一个递归解决方案将使用参数(0, "234", lut, r, work)
调用generate_strings
。lut
是一个将数字转换为相应字符的查找表。r
是包含结果字符串的向量。CCD_ 20是执行工作的缓冲器。
然后,第一个递归调用将看到索引0
数字是对应于"abc"
的2
,将a
推送到work
,然后用参数(1, "234", lut, r, work)
调用generate_strings
。一旦呼叫返回,它将把b
推到work
,并冲洗并重复。
当index
等于digits
的大小时,则生成了唯一的字符串,并将该字符串推送到r
上。
对于第二个解决方案,输入字符串首先从ascii表示转换为整数表示。例如,CCD_ 33被转换为CCD_。然后,代码使用这些作为索引来查找查找表中相应字符的数量,并计算结果中的字符串总数。例如,如果输入字符串是"234"
,则2
对应于具有3个字符的abc
。CCD_ 38对应于具有3个字符的CCD_。CCD_ 40对应于具有3个字符的CCD_。可能的字符串总数为3*3*3 = 27
。
然后,代码使用计数器来表示每个可能的字符串。如果i
是15
,它将通过首先找到15 % 3
来评估,CCD_45是0
,对应于第一个数字的第一个字符(a
)。然后将CCD_ 48除以CCD_。5 % 3
是与第二个数字的第三个字符f
相对应的2
。最后将5
除以3
,得到1
。1 % 3
是与第三位的第二个字符h
相对应的1
。因此,与数字15
相对应的字符串是afh
。这是针对每个数字执行的,并且所得到的字符串被存储在r
中。
递归算法:
空间:每个递归级别都是O(1),并且有O(n)个级别。因此,递归是O(n)。结果的空间为O(c^n),其中c=max(lut[i].length)。算法的总空间为O。
时间:设T(n)是长度为n的数字的代价。然后我们有递归公式:T(n;<=cT(n-1)+O(1)。求解这个方程,给出T(n)=O(c^n)。
哈希算法:
空格:如果你需要存储所有结果的空格,那么它仍然是O(c^n)。
时间:O(n+c^n)=O(c^n)。
我喜欢哈希算法,因为如果问题要求您给出特定的字符串结果(假设我们按字母顺序排列),效果会更好。在这种情况下,空间和时间只有O(n)。
这个问题让我想起了一个类似的问题:生成集合{1,2,3,…,n}的所有排列。哈希方法更好,因为通过逐个生成排列并进行处理,我们可以节省大量空间。
- (C++)分析树以计算返回错误值的简单算术表达式
- 函数复杂度分析
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 使用指针重新分析实体
- 如何处理来自核心指南检查器的关于gsl::at的静态分析警告
- 如何计算Big-O表示法中的平均渐近运行时间
- 模板元编程 - 尝试实现维度分析
- 嵌套在循环中的两个循环的 big-O 表示法
- 如何以静态代码分析友好的方式使用 #define 防护?
- 为什么C++不支持对未初始化变量进行智能分析?
- 分析包含 NMEA 句子的日志文件C++
- Pisarze - 来自波兰奥林匹克信息学的数据分析任务
- 在字符数组维度分析中构造字符数组
- 如何分析代码的哪一部分创建了线程?
- 如何执行 cppcheck 交叉翻译单元 (CTU) 静态分析?
- 如何分析在 Windows 上运行C++代码?
- 是否可以仅通过将分配的指针地址存储在C++中来分析内存?
- PVS 工作室分析
- 两种算法的Big-O分析