【发布时间】:2020-01-19 20:19:58
【问题描述】:
我在执行某项任务时遇到问题。这不是家庭作业或其他任何事情,而是现在的个人问题。我想知道是否有解决方案...
关键是要实现一个函数的预期 O(n) 最坏情况时间复杂度,它需要 2 个字符串数组作为输入(让我们调用第一个数组 A,第二个数组 @ 987654322@) 并且应该返回一个整数数组,其中每个元素表示数组 A 中相应元素的索引。
所以,函数应该是这样的:
private static int[] GetExistingStrings(string[] A, string[] B) { ... }
- 数组
A包含所有可能的名称 - 数组
B包含应排除的名称(即,如果存储在B数组中的某些名称也在A数组中,则它们的索引不应包含在输出 int[] 数组中;它是该数组也可能包含一些随机字符串,这些字符串不一定存在于A数组中,甚至可能为空。
例如,如果我们有这些数组:
string[] A = { "one", "two", "three", "four" }; // 0, 1, 2, 3
string[] B = { "two", "three" }; // Indices of "two" and "three" not taken into account
函数应该返回:
int[] result = { 0, 3 }; // Indices of "one" and "four"
一开始,我尝试了一种显而易见且简单的方法(使用嵌套的 for 循环):
private static int[] GetExistingStrings(string[] A, string[] B)
{
LinkedList<int> aIndices = new LinkedList<int>();
for (int n = 0; n < A.Length; n++)
{
bool isExcluded = false;
for (int m = 0; m < B.Length; m++)
{
if (A[n].Equals(B[m]))
{
isExcluded = true;
break;
}
}
if (!isExcluded)
{
aIndices.AddLast(i);
}
}
int[] resultArray = new int[aIndices.Count];
aIndices.CopyTo(resultArray, 0);
return resultArray;
}
我使用 LinkedList 是因为我们不可能知道输出的数组大小应该是多少,还因为向该列表添加新节点是一个常量 O(1) 操作。当然,这里的问题是这个函数(我假设)是 O(n*M) 时间复杂度。所以,我们需要另辟蹊径……
我的第二种方法是:
private static int[] GetExistingStrings(string[] A, string[] B)
{
int n = A.Length;
int m = B.Length;
if (m == 0)
{
return GetDefaultOutputArray(n);
}
HashSet<string> bSet = new HashSet<string>(B);
LinkedList<int> aIndices = new LinkedList<int>();
for (int i = 0; i < n; i++)
{
if (!bSet.Contains(A[i]))
{
aIndices.AddLast(i);
}
}
if (aIndices.Count > 0)
{
int[] result = new int[aIndices.Count];
aIndices.CopyTo(result, 0);
return result;
}
return GetDefaultOutputArray(n);
}
// Just an utility function that returns a default array
// with length "arrayLength", where first element is 0, next one is 1 and so on...
private static int[] GetDefaultOutputArray(int arrayLength)
{
int[] array = new int[arrayLength];
for (int i = 0; i < arrayLength; i++)
{
array[i] = i;
}
return array;
}
这里的想法是将B 数组的所有元素添加到一个HashSet,然后使用它的方法Contains() 在for 循环中检查是否相等。但是我不能完全计算这个函数的时间复杂度......我确定for循环中的代码将执行n次。但是最让我烦恼的是 HashSet 初始化 - 这里应该考虑到它吗?它如何影响时间复杂度?这个函数是 O(n) 吗?还是 O(n+m) 因为 HashSet 初始化?
有什么办法可以解决这个任务并实现O(n)?
【问题讨论】:
-
而不是链表,使用预先设定容量的链表。列表的构造函数允许您指定容量。像 A.Count 这样的东西应该是一个很好的上限。在真正遇到问题之前不要使用链表。与一个好的旧名单相比,他们的表现非常糟糕。最坏的情况是在最后添加一些东西,需要遍历整个现有列表(找到你需要添加的元素)。
-
"有什么办法可以解决这个任务并达到 O(n)?"。不,你能得到的最好结果可能是 O(m*log(n))。
-
在什么情况下了解复杂性很重要?您是否正在运行这样的代码数百万/数十亿次并且遇到性能问题,或者您是否正在尝试预先优化某些内容?
-
HashSet 对于查找的摊销为 O(1),因此不考虑整体复杂性。 AddLast() 是 O(1)。你有一个 for 循环,所以它是 O(n)。填充哈希集并复制到数组也是 O(n),所以它保持 O(n)。代码不是最优的,但不是你问的。
-
@ErikPhilips 我试图理解这个时间复杂度(Big-O Notation),特别是对于有 2 个变量作为输入的函数。我记得我看到了一个类似于我的任务,其中一个条件是实现
O(n)最坏情况下的时间复杂度,用于具有 2 个字符串数组(作为输入)的函数,并且在一个函数中,您需要简单地检查两个数组的每个元素为了平等。我不相信O(n)在这里是可能的,所以出于好奇,我不得不问。
标签: c# arrays algorithm time-complexity big-o