情况:
我有'页面'如果有链接到其他页面,这些页面将存储在数据库中,以便存储1我需要先存储其链接页面(对于外键)。
所以我需要做的是找到从所选页面链接到根目录的所有页面。
示例:
A - 链接到 - B& C& d
B - 链接到 - C& ˚F
C - 链接到 - E& ˚F
D - 链接到 - B& ç
电子链接 - F
F - 链接到 - 没有链接
此示例中的正确嵌套顺序为:
F - E - C - B - D - A
请注意,我通常会有近30页的链接到处都是
问题:
我已经有了这个有效的代码,但从每个页面获取链接需要一段时间(平均800毫秒),所以我想尽可能少地检查页面的链接。
代码示例:(不是生产代码)
static class Program
{
public static Dictionary<int, int[]> Dict = new Dictionary<int, int[]>();
private static int hitcount = 0;
static void Main(string[] args)
{
//Example data
Stopwatch sw = new Stopwatch();
sw.Start();
Dict.Add( 1, new int[]{2, 3, 4});
Dict.Add(2, new int[] {3, 6 });
Dict.Add(3, new int[] { 5, 6 });
Dict.Add(4, new int[] { 2, 3 });
Dict.Add(5, new int[] { 6 });
Dict.Add(6, new int[] {});
var links = GetAllLinks( 1 );
foreach ( var link in links )
{
Console.WriteLine(link.ToString());
}
sw.Stop();
Console.WriteLine("MS:" + sw.ElapsedMilliseconds + " - " + hitcount);
Console.ReadKey();
}
private static List<int> GetLinksFromKey(int key)
{
//This usually takes avg 800ms so sleep here
//This is the BottleNeck, the more often its called longer it will take
Thread.Sleep( 800 );
hitcount++;
return Dict[key].ToList();
}
private static List<int> GetAllLinks(int key)
{
var allPages = new List<int>();
var pages = new List<int>();
pages.Add(key);
while (true)
{
var i = 0;
var newP = new List<int>();
newP.AddRange(pages);
foreach (var page in pages.Distinct())
{
if (allPages.Contains(page))
{
continue;
}
newP.AddRange(GetLinksFromKey(page));
i++;
allPages.Add(page);
}
pages = new List<int>(newP);
if (i == 0)
{
break;
}
}
return SortLinks(new List<int>(allPages.Distinct()));
}
private static List<int> SortLinks(List<int> pagesToSort)
{
var sortedPages = new List<int>();
var hasReference = new List<int>();
while (sortedPages.Count != pagesToSort.Count)
{
foreach (var page in pagesToSort)
{
if (sortedPages.Contains(page))
{
continue;
}
var links = GetLinksFromKey(page);
if (new List<int>(links.Distinct()).RemoveListFromList(sortedPages).Count == 0)
{
sortedPages.Add(page);
}
else
{
hasReference.Add(page);
}
if (hasReference.Distinct().Count() == pagesToSort.Distinct().Count())
{
Console.WriteLine("There are circular references, can't find the root.");
return sortedPages;
}
}
}
return sortedPages;
}
private static List<int> RemoveListFromList(this List<int> mainList, List<int> removeList)
{
foreach (var item in removeList)
{
if (mainList.Contains(item))
{
mainList.Remove(item);
}
}
return mainList;
}
}
我把这段代码作为我的嵌套情况的一个例子,我做了它,所以它适用于字典而不是页面。使用字典非常快,但我知道瓶颈是哪种方法,所以如果有人有一个解决方案,我使用它,那将是伟大的。
问题:
反正有没有提高效率?因为我觉得我做错了。
如果没有更快的方法,我也很乐意听到。
如果你可以制作hitcount&lt; 20和你有相同的输出(6 - 5 - 3 - 2 - 4 - 1)你已经改进了它
答案 0 :(得分:1)
递归应该是:
public static Dictionary<int, int[]> Dict = new Dictionary<int, int[]>();
private static int hitcount = 0;
static void Main(string[] args)
{
//Example data
Stopwatch sw = new Stopwatch();
sw.Start();
Dict.Add(1, new int[] { 2, 3, 4 });
Dict.Add(2, new int[] { 3, 6 });
Dict.Add(3, new int[] { 5, 6 });
Dict.Add(4, new int[] { 2, 3 });
Dict.Add(5, new int[] { 6 });
Dict.Add(6, new int[] { });
var links = GetAllLinks(1);
foreach (var link in links)
{
Console.WriteLine(link.ToString());
}
sw.Stop();
Console.WriteLine("MS:" + sw.ElapsedMilliseconds + " - " + hitcount);
Console.ReadKey();
}
private static List<int> GetLinksFromKey(int key)
{
//This usually takes avg 800ms so sleep here
//This is the BottleNeck, the more often its called longer it will take
Thread.Sleep(800);
hitcount++;
return Dict[key].ToList();
}
private static List<int> GetAllLinks(int key)
{
var alreadyDone = new HashSet<int>();
var workingOn = new HashSet<int>();
var pages = new List<int>();
RecursiveGetAllLinks(pages, alreadyDone, workingOn, key);
return pages;
}
private static void RecursiveGetAllLinks(List<int> pages, HashSet<int> alreadyDone, HashSet<int> workingOn, int key)
{
if (!workingOn.Add(key))
{
throw new Exception("Cyclic recursion for " + key);
}
var links = GetLinksFromKey(key);
foreach (int link in links)
{
if (alreadyDone.Contains(link))
{
continue;
}
RecursiveGetAllLinks(pages, alreadyDone, workingOn, link);
}
alreadyDone.Add(key);
pages.Add(key);
workingOn.Remove(key);
}
我有两个HashSet<>
:一个用于我已经完全解析的页面(包含所有链接),另一个用于我正在解析的页面(用于检查递归)。此时我在递归的情况下使用Exception()
,但是应该可以在任何地方return false
并且在递归的情况下在每个地方检查此false
值。
对于O(N)
的调用,这段代码为GetLinksFromKey()
,因此,如果总共有6页,则应该正好加载N次。
请注意,此代码是递归的...递归是一把双刃剑...通常情况下,StackOverflowException
不应该出现这样的问题(你不会有10000深链接)