我正在解决LeetCode上的问题,但没人能解释我的问题。
给出一个任意的赎金票据字符串和另一个包含所有杂志字母的字符串,编写一个函数,如果可以从杂志中构造赎金票据,则该函数将返回true;否则,它将返回false。
杂志字符串中的每个字母在赎金记录中只能使用一次。
注意: 您可能会假设两个字符串都只包含小写字母。
canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
if(ransomNote.size() > magazine.size()) return false;
unordered_map<char, int> m;
for(int i = 0; i < magazine.size(); i++)
m[magazine[i]]++;
for(int i = 0; i < ransomNote.size(); i++)
{
if(m[ransomNote[i]] <= 0) return false;
m[ransomNote[i]]--;
}
return true;
}
};
bool canConstruct(string ransomNote, string magazine) {
int lettersLeft = ransomNote.size(); // Remaining # of letters to be found in magazine
int arr[26] = {0};
for (int j = 0; j < ransomNote.size(); j++) {
arr[ransomNote[j] - 'a']++; // letter - 'a' gives a value of 0 - 25 for each lower case letter a-z
}
int i = 0;
while (i < magazine.size() && lettersLeft > 0) {
if (arr[magazine[i] - 'a'] > 0) {
arr[magazine[i] - 'a']--;
lettersLeft--;
}
i++;
}
if (lettersLeft == 0) {
return true;
} else {
return false;
}
}
这两种方法都具有相同的复杂度,并使用相同的结构来解决问题,但是我不明白为什么一个方法要比另一方法花费几乎两倍的时间。查询向量的时间为O(1),但对于unordered_map而言相同。向其中一个添加条目/键的情况与此相同。
请,有人可以解释为什么运行时间变化如此之大吗?
答案 0 :(得分:6)
首先要注意的是,尽管查询unordered_map
的平均时间是恒定的,但最坏的情况不是O(1)
。如您所见,here实际上升至O(N)
的顺序,N
表示容器的大小。
第二,由于vector
分配内存的顺序部分,因此即使在最坏的情况下,访问该内存的效率也很高,实际上 是恒定的。 (即简单的指针算法,而不是计算更复杂的哈希函数的结果)可能还会涉及到各种级别的顺序内存缓存(即,取决于您的代码运行的平台),这可能与使用vector
的代码相比,使用unordered_map
的代码执行速度更快。
本质上,就复杂性而言,vector
的最坏情况性能比unordered_map
的最有效。最重要的是,大多数硬件系统都提供了诸如缓存之类的功能,这些功能使vector
的使用更具优势。 (即O(1)
操作中较小的常量因子)
答案 1 :(得分:3)
第二种方法使用纯C数组,其中访问元素是简单的指针取消引用。但是unordered_map
并非如此。有两点需要注意:
unordered_map
实际上是引擎盖下的哈希表,而C ++标准间接要求it to be implemented using open addressing,它是比简单数组访问复杂得多的算法。由于这些原因,即使运行时复杂度相同,也难怪数组版本会比unordered_map
更好。这是另一个示例,其中两个运行时复杂度相同的代码的执行效果不同。
仅当您拥有大量键(此处与固定26相对)时,您才会看到unordered_map
的好处。