我想找到一个checks a linked-list with n elements for consistency
的算法。链表使用dummy head
(也称为哨兵节点)。算法需要run in O(n) time
并且允许use O(1) extra space
除了迭代列表所需的空间。列表size
的{{1}}。另外,它是is unknown
。
如果列表项指向前一个列表项,则列表计为不一致。 首先,我考虑存储第一个元素,然后在将当前元素与第一个元素进行比较时迭代列表。
答案 0 :(得分:3)
列表是否提供了一个Size属性,告诉你它包含多少元素(n)而不遍历它?
如果确实如此,那么满足所有大O要求的简单解决方案就是尝试遍历列表,随时计算元素。如果计数超过列表中预期的元素数,则它有一个循环。
伪代码看起来像这样:
bool isConsistent (List list)
{
bool consistent = true;
Node node = list.Sentinel.Next;
int count = 0;
while (node != list.Sentinel && consistent)
{
count++;
if (count > list.Size)
consistent = false;
node = node.Next;
}
return consistent;
}
在O(n)中完成并使用O(1)存储。
答案 1 :(得分:2)
Floyd's "Tortoise and Hare" algorithm执行您需要的操作,只需要进行一些小修改就可以使用虚拟头/尾(sentinel)节点。
以下是我编写伪代码的方法:
bool IsConsistent (List list)
{
Node tortoise = list.Sentinel.Next;
Node hare = tortoise.Next;
while (tortoise != list.Sentinel && hare != list.Sentinel)
{
if (tortoise == hare)
return false;
tortoise = tortoise.Next;
hare = hare.Next.Next;
}
return true;
}
答案 2 :(得分:1)
如果链表中的每个项目都没有Visited属性,则需要一些RAM。 如果您有Visited属性,则在运行算法之前首先需要清除它。这可能不符合您的大O要求。
目前尚不清楚“前一个列表项目中的点数”是什么意思。是通过引用(对象)还是相同的值/属性值集(struct)?我假设参考。可以很容易地修改下面的代码来处理结构。
static void Main(string[] args)
{
var list = BuildALinkedListFromSomeData();
var isConsitent = IsConsistent(list);
}
static bool IsConsistent(LinkedList<Item> list)
{
var visited = new List<LinkedListNode<Item>>()
var runner = list.First;
while(runner != null)
{
if (visited.Contains(runner))
return false;
visited.Add(runner);
runner = runner.Next;
}
return true;
}
使用已经使用存储空间的现有数字VisitCounter的O(n)解决方案(无需额外存储):
static bool IsConsistent(LinkedList<Item> list)
{
var runner = list.First;
if (runner == null)
return false; // Assume consistent if empty
var consistent = true;
var runId = runner.Value.VisitCount;
while (runner != null)
{
// Does the traversed item match the current run id?
if(runner.Value.VisitCount > runId)
{
// No, Flag list as inconsistent. It must have been visited previously during this run
consistent = false;
// Reset the visit count (so that list is ok for next run)
runner.Value.VisitCount = runId;
}
// Increase visit count
runner.Value.VisitCount++;
// Visit next item in list
runner = runner.Next;
}
return consistent;
}
这会更改列表中项目的内容,但不会更改列表本身。如果您不允许更改列表中项目的内容,那么当然这也不是解决方案。好吧,第二个想法,这根本不是一个可能的解决方案。当不一致时,您的列表是循环的,最后一个算法永远不会完成:)
然后,您必须从列表中的每个访问项目向后遍历列表,这将破坏您的O(n + 1)要求。
结论:如果可以获得计数,那么就不可能执行任务。见格雷厄姆的回答
答案 3 :(得分:1)
以下是我对第二个问题的解决方案。
IsConsistent(LinkedList<Item> list) :N
slow = List.Sentinel.next :Element
fast = slow.next :Element
isConsistent = true :boolean
while(fast != list.Sentinel && fast.next != list.Sentinel && isConsistent) do
if(slow == fast)
isConsistent = false
else
slow:= slow.next
fast:= fast.next.next
od
if(isConsistent)
return 0
else
position = 0 : N
slow:= list.Sentinel
while(slow != fast) do
slow:= slow.next
fast:= fast.next
position:= position + 1
od
return position
答案 4 :(得分:0)
基本上,指向前一项意味着在列表中有一个循环。在这种情况下,循环检查似乎是合适的。
答案 5 :(得分:0)
起初我没有想过使用list.Sentinel。现在我有了一个新主意。
IsConsistent(LinkedList<Item> list) :boolean
slow = List.Sentinel.next :Element
fast = slow.next :Element
while(slow != list.Sentinel && fast != list.Sentinel) do
if(slow == fast) return false
else
slow:= slow.next
fast:= fast.next.next
od
return true