在 C# 中获得嵌套 for 循环的性能命中
getting a performance hit for nested for loop in C#
我有一个字符串数组,一共(100k)。我需要检查之前是否出现过相同的字符串,如果出现,我所要做的就是 return 该字符串。我已经使用嵌套 for 循环编写了代码,但不幸的是我的性能很差。正确处理 (string[100K]) 函数需要 1.9 分钟,而我希望它能在几秒钟内完成。
我关心的不是逻辑。我关心的是 LOOP。如何提高循环效率。
public string StartMatchingProcess(string[]inputArray)
{
string[] stringArray = inputArray;
string result = string.Empty;
for (long i = 0; i < stringArray.Length; i++)
{
for (long j = 0; j <= i; j++)
{
if(i == j) break;
if (IsPrefix(stringArray[i], stringArray[j]))
{
return stringArray[i];
}
}
}
Console.WriteLine("GOOD SET");
return result;
}
public bool IsPrefix(string string1,string string2)
{
if (AreString1ValuesValid(string1, string2))
{
if (string1 == string2.Substring(0, string1.Length))
{
Console.WriteLine("BAD SET");
Console.WriteLine(string1);
return true;
}
}
else if (AreString2ValuesValid(string1, string2))
{
if (string2 == string1.Substring(0, string2.Length))
{
Console.WriteLine("BAD SET");
Console.WriteLine(string1);
return true;
}
}
return false;
}
public bool AreString1ValuesValid(string string1, string string2)
=> string1.Length <= string2.Length;
public bool AreString2ValuesValid(string string1, string string2)
=> string2.Length <= string1.Length;
如果您只是想确定您的数组是否有重复的字符串值,请考虑使用 LINQ 来计算出现的次数。
string[] arrayTest = new string[] { "hello", "hello", "world"};
string myValue = "hello";
var stringCount = arrayTest.Where(n => n == myValue).Count();
if (stringCount > 1) return myValue;
在上面,我们检查"hello"是否不止一次在数组中,如果是,我们return它。
排序初始数组,你可以检查neighbors只:
public string StartMatchingProcess(string[] inputArray) {
if (null == inputArray)
throw new ArgumentNullException(nameof(inputArray));
string[] sorted = inputArray.OrderBy(item => item).ToArray();
for (int i = 1; i < sorted.Length; ++i) {
string prior = sorted[i - 1];
string current = sorted[i];
if (current.StartsWith(prior))
return prior;
}
return "";
}
因此,您将有 O(n * log(n))
时间复杂度与 O(n**2)
(初始解决方案)
为这个任务使用嵌套循环真的是个坏主意,因为你的答案 O(n*n)
很复杂,需要为 100k 数组调用 Substring()
10.000.000.000 次。
字符串有特定的结构。例如,您可以使用 Trie
:
public string StartMatchingProcess(string[] inputArray)
{
var trie = new Trie();
foreach(var w in inputArray)
trie.AddWord(w);
foreach(var w in inputArray)
if(trie.HasPrefix(w) || trie.HasWord(w)
return w;
return string.Empty;
}
这是一个使用 linq 的完整解决方案。
public class Node
{
public char letter { get; }
public int Index { get; set; }
public List<Node> ChildList { get; set; } = new List<Node>();
public Node(char item, int index)
{
Index = index;
letter = item;
}
}
public class NoPrefixSet
{
public Dictionary<char, Node> ParentNode { get; set; } = new Dictionary<char, Node>();
public string GenerateNodes(string[] inputArray)
{
for (int i = 0; i < inputArray.Length; i++)
{
if (IsWordPrefix(inputArray[i]))
{
Console.WriteLine("BAD SET");
Console.WriteLine(inputArray[i]);
return inputArray[i];
}
}
Console.WriteLine("Good Set");
return "Good Set";
}
private void InsertNodeInParent(char item)
=> ParentNode.Add(item, new Node(item, 0));
private bool IsWordPrefix(string word)
{
//Check parent
Node parentNode = null;
bool hasNotInserted = false;
int similarCounter = 0;
if (!ParentNode.Any(a => a.Key == word[0]))
{
InsertNodeInParent(word[0]);
}
parentNode = ParentNode.Where(a => a.Key == word[0]).FirstOrDefault().Value;
for (int letterIndex = 0; letterIndex < word.Length; letterIndex++)
{
if (!parentNode.ChildList.Any(a => a.letter == word[letterIndex]))
{
parentNode.ChildList.Add(new Node(word[letterIndex], letterIndex));
}
else
{
if (!parentNode.ChildList.Where(a => a.letter == word[letterIndex]).First().ChildList.Any() || word.Length == letterIndex+1)
{
if (similarCounter == letterIndex)
return hasNotInserted = true;
}
similarCounter++;
}
parentNode = parentNode.ChildList.Where(a => a.letter == word[letterIndex] && a.Index == letterIndex).First();
}
return hasNotInserted;
}
public void ReadInput()
{
long data = Convert.ToInt64(Console.ReadLine());
string[] stringArray = new string[data];
for (long i = 0; i < data; i++)
{
stringArray[i] = Console.ReadLine();
}
GenerateNodes(stringArray);
}
}
我有一个字符串数组,一共(100k)。我需要检查之前是否出现过相同的字符串,如果出现,我所要做的就是 return 该字符串。我已经使用嵌套 for 循环编写了代码,但不幸的是我的性能很差。正确处理 (string[100K]) 函数需要 1.9 分钟,而我希望它能在几秒钟内完成。
我关心的不是逻辑。我关心的是 LOOP。如何提高循环效率。
public string StartMatchingProcess(string[]inputArray)
{
string[] stringArray = inputArray;
string result = string.Empty;
for (long i = 0; i < stringArray.Length; i++)
{
for (long j = 0; j <= i; j++)
{
if(i == j) break;
if (IsPrefix(stringArray[i], stringArray[j]))
{
return stringArray[i];
}
}
}
Console.WriteLine("GOOD SET");
return result;
}
public bool IsPrefix(string string1,string string2)
{
if (AreString1ValuesValid(string1, string2))
{
if (string1 == string2.Substring(0, string1.Length))
{
Console.WriteLine("BAD SET");
Console.WriteLine(string1);
return true;
}
}
else if (AreString2ValuesValid(string1, string2))
{
if (string2 == string1.Substring(0, string2.Length))
{
Console.WriteLine("BAD SET");
Console.WriteLine(string1);
return true;
}
}
return false;
}
public bool AreString1ValuesValid(string string1, string string2)
=> string1.Length <= string2.Length;
public bool AreString2ValuesValid(string string1, string string2)
=> string2.Length <= string1.Length;
如果您只是想确定您的数组是否有重复的字符串值,请考虑使用 LINQ 来计算出现的次数。
string[] arrayTest = new string[] { "hello", "hello", "world"};
string myValue = "hello";
var stringCount = arrayTest.Where(n => n == myValue).Count();
if (stringCount > 1) return myValue;
在上面,我们检查"hello"是否不止一次在数组中,如果是,我们return它。
排序初始数组,你可以检查neighbors只:
public string StartMatchingProcess(string[] inputArray) {
if (null == inputArray)
throw new ArgumentNullException(nameof(inputArray));
string[] sorted = inputArray.OrderBy(item => item).ToArray();
for (int i = 1; i < sorted.Length; ++i) {
string prior = sorted[i - 1];
string current = sorted[i];
if (current.StartsWith(prior))
return prior;
}
return "";
}
因此,您将有 O(n * log(n))
时间复杂度与 O(n**2)
(初始解决方案)
为这个任务使用嵌套循环真的是个坏主意,因为你的答案 O(n*n)
很复杂,需要为 100k 数组调用 Substring()
10.000.000.000 次。
字符串有特定的结构。例如,您可以使用 Trie
:
public string StartMatchingProcess(string[] inputArray)
{
var trie = new Trie();
foreach(var w in inputArray)
trie.AddWord(w);
foreach(var w in inputArray)
if(trie.HasPrefix(w) || trie.HasWord(w)
return w;
return string.Empty;
}
这是一个使用 linq 的完整解决方案。
public class Node
{
public char letter { get; }
public int Index { get; set; }
public List<Node> ChildList { get; set; } = new List<Node>();
public Node(char item, int index)
{
Index = index;
letter = item;
}
}
public class NoPrefixSet
{
public Dictionary<char, Node> ParentNode { get; set; } = new Dictionary<char, Node>();
public string GenerateNodes(string[] inputArray)
{
for (int i = 0; i < inputArray.Length; i++)
{
if (IsWordPrefix(inputArray[i]))
{
Console.WriteLine("BAD SET");
Console.WriteLine(inputArray[i]);
return inputArray[i];
}
}
Console.WriteLine("Good Set");
return "Good Set";
}
private void InsertNodeInParent(char item)
=> ParentNode.Add(item, new Node(item, 0));
private bool IsWordPrefix(string word)
{
//Check parent
Node parentNode = null;
bool hasNotInserted = false;
int similarCounter = 0;
if (!ParentNode.Any(a => a.Key == word[0]))
{
InsertNodeInParent(word[0]);
}
parentNode = ParentNode.Where(a => a.Key == word[0]).FirstOrDefault().Value;
for (int letterIndex = 0; letterIndex < word.Length; letterIndex++)
{
if (!parentNode.ChildList.Any(a => a.letter == word[letterIndex]))
{
parentNode.ChildList.Add(new Node(word[letterIndex], letterIndex));
}
else
{
if (!parentNode.ChildList.Where(a => a.letter == word[letterIndex]).First().ChildList.Any() || word.Length == letterIndex+1)
{
if (similarCounter == letterIndex)
return hasNotInserted = true;
}
similarCounter++;
}
parentNode = parentNode.ChildList.Where(a => a.letter == word[letterIndex] && a.Index == letterIndex).First();
}
return hasNotInserted;
}
public void ReadInput()
{
long data = Convert.ToInt64(Console.ReadLine());
string[] stringArray = new string[data];
for (long i = 0; i < data; i++)
{
stringArray[i] = Console.ReadLine();
}
GenerateNodes(stringArray);
}
}