44


32

リストをたどるのに2つの変数だけを使用して、リンクされたリストがそれ自体でループするかどうかを見つけるためのアルゴリズムを知っている人はいますか。 リンクされたオブジェクトのリストがあるとしたら、どのタイプのオブジェクトでもかまいません。 リンクリストの先頭へのポインタが1つの変数に含まれており、リストを走査するために使用できる変数は1つだけです。

だから私の計画は、ポインタが同じかどうかを確認するためにポインタ値を比較することです。 リストは有限サイズですが、巨大になる可能性があります。 両方の変数を先頭に設定してから、リストが他の変数と等しいかどうかを常に確認しながら他の変数を使用してリストを走査することはできますが、ループにヒットした場合は絶対に抜けません。 私はそれがリストを行き来しそしてポインタ値を比較する異なった速度と関係があると思っています。 何かご意見は?

7 Answer


46


+ Floyd’s Cycle-Finding Algorithm`を使用することをお勧めします_a_ + Tortoise and the Hare Algorithm + `。 それはO(n)の複雑さを持ち、私はそれがあなたの要求に合うと思います。

コード例:

function boolean hasLoop(Node startNode){
  Node slowNode = Node fastNode1 = Node fastNode2 = startNode;
  while (slowNode && fastNode1 = fastNode2.next() && fastNode2 = fastNode1.next()){
    if (slowNode == fastNode1 || slowNode == fastNode2) return true;
    slowNode = slowNode.next();
  }
  return false;
}

ウィキペディアに関するさらに詳しい情報:http://en.wikipedia.org/wiki/Floyd%27s_cycle-finding_algorithm#Tortoise_and_hare[Floydのサイクル発見アルゴリズム]。


17


あなたは Turtle and Rabbitアルゴリズムを使うことができます。

ウィキペディアにも説明があり、彼らはそれを " フロイドのサイクル発見アルゴリズム"または "亀と野ウサギ"と呼びます


9


もちろんです。 1つの解決策は、実際には両方のポインタを使用してリストを移動することです。1つは他のポインタの2倍の速度で移動することです。

リスト内の任意の場所を指す「遅い」および「速い」ポインタで始めます。 トラバーサルループを実行します。 「速い」ポインタがいつでも遅いポインタと一致するようになった場合は、循環リンクリストがあります。

int * head = list.GetHead(); if(head!= null){int * fastPtr = head; int * slowPtr = head;

bool isCircular = true;

do {if(fastPtr-> Next == null || fastPtr-> Next-> Next == null)//リストの終わりが見つかりました{isCircular = false;}ブレーク; }

fastPtr = fastPtr-> Next-> Next; slowPtr = slowPtr->次へ; while(fastPtr!= slowPtr);

// 'isCircular'フラグを使用して、ここで必要なことをすべて実行してください}


3


私はこれを自分で解決しようとしましたが、別の(効率が悪いが最適な)解決策を見つけました。

このアイデアは、単一リンクリストを線形時間で逆にすることに基づいています。 これは、リストを反復処理する際に各ステップで2回スワップすることで実行できます。 qが前の要素(最初はnull)でpが現在の場合、swap(q、p→ next)swap(p、q)はリンクを逆にして同時に2つのポインタを進めます。 スワップは、3番目のメモリ位置を使用しなくても済むように、XORを使用して実行できます。

リストにサイクルがある場合は、反復中のある時点で、ポインタがすでに変更されているノードに到着します。 どのノードであるかはわかりませんが、繰り返しを続け、一部の要素を2回交換すると、リストの先頭に戻ります。

リストを2回反転すると、リストの結果は変わりません。リストの最初の部分に到達したかどうかに基づいて、リストにサイクルがあるかどうかを判断できます。


2


int isListCircular(ListNode * head){if(head == NULL)の場合、0が返されます。 ListNode * fast = head、* slow = head。しながら(速い


1


boolean findCircular(Node * head){Node *が遅い、*速い;遅い=頭;速い=頭 - >次。 while(true){if(!faster ||!faster-> next)はfalseを返します。そうでなければ(速い==遅い||速い - >次の==遅い)trueを返します。そうでなければ、= fast-> next-> nextです。 }}


0


この問題を次のステップに進めることは、サイクルを識別することです(つまり、サイクルが存在するということだけではなく、それがリストの正確な場所にあるということです)。 TortoiseとHareのアルゴリズムも同じように使用できますが、常にリストの先頭を追跡する必要があります。 このアルゴリズムの実例は こちらにあります。