リンクされたリストでループを検出する方法は?


434

Javaでリンクされたリスト構造があるとします。ノードで構成されています:

class Node {
    Node next;
    // some user data
}

そして、各ノードは次のノードを指しますが、最後のノードは例外で、nextはnullです。リストにループが含まれている可能性があるとしましょう。つまり、最後のノードはnullの代わりに、リストの前にあるノードの1つを参照しています。

書くための最良の方法は何ですか

boolean hasLoop(Node first)

true指定されたノードがループのあるリストの最初である場合、どちらが返されfalseますか?一定量のスペースと妥当な時間を取るように、どのように書くことができますか?

ループのあるリストがどのようなものかを次に示します。

代替テキスト


50
うわー..私はこの雇用主のために働きたいですfinite amount of space and a reasonable amount of time?:)
codaddict

10
@SLaks-ループは最初のノードに戻る必要はありません。途中までループバックできます。
jjujuma 2010

109
以下の回答は読む価値がありますが、このようなインタビューの質問はひどいです。答えを知っている(つまり、フロイドのアルゴリズムのバリアントを見た)か、知らないかのいずれかであり、推論や設計能力をテストするために何もしません。
GaryF 2010

3
公平を期すために、「知るアルゴリズム」のほとんどは、研究レベルのことを行わない限り、このようなものです。
ラリー

12
@GaryFそして、それでも彼らが答えを知らなかったときに彼らが何をするかを知ることは明らかになるでしょう。たとえば、彼らはどのようなステップを踏み、誰と協力し、アルゴリズムの知識の欠如を克服するために何をしますか?
クリスナイト

回答:


538

カメとウサギのアルゴリズムとも呼ばれるフロイドのサイクル検索アルゴリズムを利用できます

アイデアは、リストへの2つの参照を持ち、それらを異なる速度で移動することです。1つは1ノードごとに、もう1つはノードごとに移動します2

  • リンクされたリストにループがある場合、それらは確実に満たされます。
  • それ以外の場合、2つの参照(またはその参照)のどちらかnextがになりnullます。

アルゴリズムを実装するJava関数:

boolean hasLoop(Node first) {

    if(first == null) // list does not exist..so no loop either
        return false;

    Node slow, fast; // create two references.

    slow = fast = first; // make both refer to the start of the list

    while(true) {

        slow = slow.next;          // 1 hop

        if(fast.next != null)
            fast = fast.next.next; // 2 hops
        else
            return false;          // next node null => no loop

        if(slow == null || fast == null) // if either hits null..no loop
            return false;

        if(slow == fast) // if the two ever meet...we must have a loop
            return true;
    }
}

29
また、上のヌル・チェックを行う必要がありfast.next呼び出す前にnext再び:if(fast.next!=null)fast=fast.next.next;
cmptrgeekken

12
(slow == fast)だけでなく:(slow == fast || slow.next == fast)をチェックして、低速から高速へのジャンプを防ぐ必要があります
Oleg Razgulyaev

13
私は間違っていました:次のステップでスローを飛び越えることは、ファストがスローを飛び越すことができないからです:)
Oleg Razgulyaev

4
リストにノードが1つしかない場合を除いて、slow == nullのチェックは冗長です。Node.nextへの1つの呼び出しを取り除くこともできます。:ここでは簡単かつループの高速化バージョンだpastie.org/927591
ケイSarraute

22
あなたは本当にあなたの参照を引用する必要があります。このアルゴリズムは、60年代にロバートフロイドによって発明されました。これは、フロイドのサイクル検出アルゴリズムとも呼ばれます。亀とウサギのアルゴリズム。
joshperry

127

以下は、Fast / Slowソリューションを改良したもので、奇数長のリストを正しく処理し、明瞭さを向上させます。

boolean hasLoop(Node first) {
    Node slow = first;
    Node fast = first;

    while(fast != null && fast.next != null) {
        slow = slow.next;          // 1 hop
        fast = fast.next.next;     // 2 hops 

        if(slow == fast)  // fast caught up to slow, so there is a loop
            return true;
    }
    return false;  // fast reached null, so the list terminates
}

2
素敵で簡潔。このコードは、遅い==速いかどうかをチェックすることで最適化できます。(fast.next!= null && slow = fast.next); :)
arachnode.net

11
@ arachnode.netそれは最適化ではありません。その場合slow == fast.next、次の反復でslow等しくなりfastます。繰り返しごとに追加のテストを実行するという犠牲を払って、最大で1つの繰り返しを保存するだけです。
Jason C

@ ana01 は同じ参照パスをたどっているので、slow以前にnullになることはできませんfast(リストの同時変更がない限り、すべてのベットがオフになります)。
Dave L.

好奇心から、これは奇数に対してどのように機能しますか?まだ奇妙な長さのリンクされたリストでカメを追い越すことはできませんか?
theGreenCabbage 2015

1
@theGreenCabbageループの反復ごとに、ノウサギは亀より1つ先に進みます。したがって、ウサギが3ステップ遅れている場合、次の反復では2ホップ、カメは1ホップかかり、ウサギは2ステップ遅れています。次の反復の後、うさぎは1ホップ遅れており、正確に追いついています。カメが1ホップを取る間にウサギが3ホップかかった場合、毎回2ずつ増加するためスキップできますが、反復ごとに1しか増加しないため、スキップできません。
Dave L.

52

フロイドのアルゴリズムよりも優れている

Richard Brentが代替サイクル検出アルゴリズムについて説明しました。これはウサギとカメ[フロイドのサイクル]によく似ていますが、ここでは低速ノードは移動せず、固定ノードで高速ノードの位置に「テレポート」されます。間隔。

説明はここにあります:http : //www.siafoo.net/algorithm/11 ブレントは、彼のアルゴリズムはフロイドのサイクルアルゴリズムより24〜36%速いと主張しています。O(n)時間の複雑さ、O(1)空間の複雑さ。

public static boolean hasLoop(Node root){
    if(root == null) return false;

    Node slow = root, fast = root;
    int taken = 0, limit = 2;

    while (fast.next != null) {
        fast = fast.next;
        taken++;
        if(slow == fast) return true;

        if(taken == limit){
            taken = 0;
            limit <<= 1;    // equivalent to limit *= 2;
            slow = fast;    // teleporting the turtle (to the hare's position) 
        }
    }
    return false;
}

この答えは素晴らしいです!
valin077 2014年

1
私のブログ-k2code.blogspot.in/2010/04/…に含めて、あなたの答えを本当に気に入りました
kinshuk4 2014年

なぜ確認する必要があるのslow.next != nullですか?私が見る限りでslowは、常にに等しいか、それよりも後ろfastです。
TWiStErRob 2015年

私がアルゴリズムを学び始めたとき、私はこれをずっと前に行いました。コードを編集しました。ありがとう:)
Ashok Bijoy Debnath 2015年

50

TurtleとRabbitの代替ソリューションですが、一時的にリストを変更しているため、それほど良くありません。

アイデアは、リストをたどって、進むにつれてそれを逆にすることです。次に、すでにアクセスされたノードに初めて到達すると、その次のポインターは「後方」を指し、反復がfirst再び終了するところに向かって進みます。

Node prev = null;
Node cur = first;
while (cur != null) {
    Node next = cur.next;
    cur.next = prev;
    prev = cur;
    cur = next;
}
boolean hasCycle = prev == first && first != null && first.next != null;

// reconstruct the list
cur = prev;
prev = null;
while (cur != null) {
    Node next = cur.next;
    cur.next = prev;
    prev = cur;
    cur = next;
}

return hasCycle;

テストコード:

static void assertSameOrder(Node[] nodes) {
    for (int i = 0; i < nodes.length - 1; i++) {
        assert nodes[i].next == nodes[i + 1];
    }
}

public static void main(String[] args) {
    Node[] nodes = new Node[100];
    for (int i = 0; i < nodes.length; i++) {
        nodes[i] = new Node();
    }
    for (int i = 0; i < nodes.length - 1; i++) {
        nodes[i].next = nodes[i + 1];
    }
    Node first = nodes[0];
    Node max = nodes[nodes.length - 1];

    max.next = null;
    assert !hasCycle(first);
    assertSameOrder(nodes);
    max.next = first;
    assert hasCycle(first);
    assertSameOrder(nodes);
    max.next = max;
    assert hasCycle(first);
    assertSameOrder(nodes);
    max.next = nodes[50];
    assert hasCycle(first);
    assertSameOrder(nodes);
}

ループが最初以外のノードを指している場合、逆は正しく機能しますか?最初のリンクされたリストがこのように1-> 2-> 3-> 4-> 5-> 2(5から2のサイクルで)である場合、逆のリストは1-> 2 <-3 <-4のようになります。 <-5?そして、その逆の場合、最終的に再構築されたリストは台無しになりますか?
Zenil 2015年

1
@Zenil:そのため、最後のノードがリストの中央を指す、最後のテストケースを作成しました。再構築が機能しない場合、そのテストは失敗します。あなたの例について:1-> 2-> 3-> 5-> 2の逆は1-> 2-> 5-> 4-> 3-> 2になります、なぜならループはリストの終わりに一度停止するだけだからですループの終わり(簡単に検出できない)に達したときではありません。
メリトン2015年

28

亀とウサギ

Pollardのrhoアルゴリズムご覧ください。それは全く同じ問題ではありませんが、多分あなたはそれからロジックを理解し、リンクされたリストにそれを適用するでしょう。

(あなたが怠惰な場合は、サイクル検出をチェックアウトすることができます -亀とうさぎの部分を確認してください)。

これには、線形時間と2つの追加のポインタのみが必要です。

Javaの場合:

boolean hasLoop( Node first ) {
    if ( first == null ) return false;

    Node turtle = first;
    Node hare = first;

    while ( hare.next != null && hare.next.next != null ) {
         turtle = turtle.next;
         hare = hare.next.next;

         if ( turtle == hare ) return true;
    }

    return false;
}

(ほとんどのソリューションは両方nextnext.nextnullをチェックしません。また、亀は常に遅れているため、nullをチェックする必要はありません。うさぎはすでにそれを実行しました。)


13

ユーザーunicornaddictには上記の優れたアルゴリズムがありますが、残念なことに、ループ長が奇数> = 3の非ループリストのバグが含まれています。問題はfast、リストの最後の直前で「スタック」する可能性があることです。slowし、それに追いつくことがあり、ループが(誤って)検出されました。

これが修正されたアルゴリズムです。

static boolean hasLoop(Node first) {

    if(first == null) // list does not exist..so no loop either.
        return false;

    Node slow, fast; // create two references.

    slow = fast = first; // make both refer to the start of the list.

    while(true) {
        slow = slow.next;          // 1 hop.
        if(fast.next == null)
            fast = null;
        else
            fast = fast.next.next; // 2 hops.

        if(fast == null) // if fast hits null..no loop.
            return false;

        if(slow == fast) // if the two ever meet...we must have a loop.
            return true;
    }
}

10

この文脈では、どこにでもテキスト資料への負荷があります。私は、概念を理解するのに本当に役立つ図式表現を投稿したかっただけです。

高速と低速がポイントpで出会うとき、

高速で移動した距離= a + b + c + b = a + 2b + c

スローの移動距離= a + b

速いのは遅いの2倍速いからです。したがって、a + 2b + c = 2(a + b)の場合、a = cになります。

したがって、別のスローポインターが先頭からqに再び実行されると同時に、高速ポインターがpからqに実行されるため、ポイントqで一緒に出会います。

ここに画像の説明を入力してください

public ListNode detectCycle(ListNode head) {
    if(head == null || head.next==null)
        return null;

    ListNode slow = head;
    ListNode fast = head;

    while (fast!=null && fast.next!=null){
        fast = fast.next.next;
        slow = slow.next;

        /*
        if the 2 pointers meet, then the 
        dist from the meeting pt to start of loop 
        equals
        dist from head to start of loop
        */
        if (fast == slow){ //loop found
            slow = head;
            while(slow != fast){
                slow = slow.next;
                fast = fast.next;
            }
            return slow;
        }            
    }
    return null;
}

2
絵は何千もの言葉に値します。きちんとした簡単な説明をありがとう!
Calios

1
インターネット上で最高の説明。これは、高速と低速のポインタが線形時間後に収束することを証明していると付け加えます
VarunPandey

場合はa、ループの長さよりも大きいそして速く、複数のループ式を行いますdistance (fast) = a + b + b + cに変更されますa + (b+c) * k + b追加のパラメータを導入k速い1によって作られたloppsの数をカウントします。
ベン

9

アルゴリズム

public static boolean hasCycle (LinkedList<Node> list)
{
    HashSet<Node> visited = new HashSet<Node>();

    for (Node n : list)
    {
        visited.add(n);

        if (visited.contains(n.next))
        {
            return true;
        }
    }

    return false;
}

複雑

Time ~ O(n)
Space ~ O(n)

空間の複雑度O(2n)はどうですか?
Programmer345

@ user3543449あなたしている権利は、それだけでする必要がありn、固定
Khaled.K

1
それぞれがArrayListのチェックを含み、O(n)とO(n)があるため、これは実際には時間〜O(n ^ 2)です。線形時間の代わりにHashSetを使用してください。
Dave L.

3
これはサイクルのテストではなく、要素equalsとを使用した重複値のテストですhashCode。同じことではありません。そして、それnullは最後の要素で逆参照します。また、質問では、ノードをに保存することについては何も述べられていませんLinkedList
Lii

2
@Liiこれは疑似コードであり、Javaコードではありません。そのため、私はアルゴリズムで
Khaled.K

8

以下は最適な方法ではない可能性があります-O(n ^ 2)です。しかし、それは(最終的には)仕事を成し遂げるのに役立つはずです。

count_of_elements_so_far = 0;
for (each element in linked list)
{
    search for current element in first <count_of_elements_so_far>
    if found, then you have a loop
    else,count_of_elements_so_far++;
}

for()を実行するためにリストにある要素の数をどのように知ることができますか?
Jethro Larson

@JethroLarson:リンクリストの最後のノードは既知のアドレスを指します(多くの実装では、これはNULLです)。その既知のアドレスに到達したら、forループを終了します。
Sparky、

3
public boolean hasLoop(Node start){   
   TreeSet<Node> set = new TreeSet<Node>();
   Node lookingAt = start;

   while (lookingAt.peek() != null){
       lookingAt = lookingAt.next;

       if (set.contains(lookingAt){
           return false;
        } else {
        set.put(lookingAt);
        }

        return true;
}   
// Inside our Node class:        
public Node peek(){
   return this.next;
}

私の無知を許してください(私はまだJavaとプログラミングにかなり新しいです)が、なぜ上記のことがうまくいかないのでしょうか?

これは一定のスペースの問題を解決しないと思います...しかし、それは少なくとも妥当な時間内に到達します、正しいですか?これは、リンクリストのスペースとn要素のセットのスペースのみを使用します(nはリンクリスト内の要素の数、またはループに到達するまでの要素の数です)。そして、しばらくの間、最悪のケースの分析では、O(nlog(n))が示唆されると思います。contains()のSortedSetルックアップはlog(n)(javadocを確認しますが、TreeSetの基本構造はTreeMapであり、その結果として赤黒ツリーです)、そして最悪の場合(ループなし、または非常に最後にループ)、n回のルックアップを行う必要があります。


2
はい、ある種のSetを使用したソリューションはうまく機能しますが、リストのサイズに比例したスペースが必要です。
jjujuma 2010

3

クラスの埋め込みが許可されている場合はNode、以下で実装したように問題を解決します。hasLoop()O(n)時間で実行され、のスペースのみを使用しますcounter。これは適切な解決策のように見えますか?または、埋め込むことなくそれを行う方法はありNodeますか?(明らかに、実際の実装ではRemoveNode(Node n)、などのより多くのメソッドがあります。)

public class LinkedNodeList {
    Node first;
    Int count;

    LinkedNodeList(){
        first = null;
        count = 0;
    }

    LinkedNodeList(Node n){
        if (n.next != null){
            throw new error("must start with single node!");
        } else {
            first = n;
            count = 1;
        }
    }

    public void addNode(Node n){
        Node lookingAt = first;

        while(lookingAt.next != null){
            lookingAt = lookingAt.next;
        }

        lookingAt.next = n;
        count++;
    }

    public boolean hasLoop(){

        int counter = 0;
        Node lookingAt = first;

        while(lookingAt.next != null){
            counter++;
            if (count < counter){
                return false;
            } else {
               lookingAt = lookingAt.next;
            }
        }

        return true;

    }



    private class Node{
        Node next;
        ....
    }

}

1

あなたはそれを一定のO(1)時間で実行することもできます(それは非常に速くまたは効率的ではありませんが):コンピューターのメモリが保持できるノードの数は限られています(たとえばNレコード)。N個を超えるレコードをトラバースする場合、ループが発生します。


これはO(1)ではありません。このアルゴリズムには、big-O表記で意味のある時間の複雑さはありません。big-O-notationは、入力サイズが無限大になったときの制限内のパフォーマンスについてのみ通知します。したがって、いくつかの大きなNに対してN個を超える要素を持つリストないという仮定に基づいてアルゴリズムを構築する場合、リストサイズが無限に近づくときのランタイムの制限は定義されていません。したがって、複雑さは "O(anything)"ではありません。
fgp

1
 // To detect whether a circular loop exists in a linked list
public boolean findCircularLoop() {
    Node slower, faster;
    slower = head;
    faster = head.next; // start faster one node ahead
    while (true) {

        // if the faster pointer encounters a NULL element
        if (faster == null || faster.next == null)
            return false;
        // if faster pointer ever equals slower or faster's next
        // pointer is ever equal to slower then it's a circular list
        else if (slower == faster || slower == faster.next)
            return true;
        else {
            // advance the pointers
            slower = slower.next;
            faster = faster.next.next;
        }
    }
}

1
boolean hasCycle(Node head) {

    boolean dec = false;
    Node first = head;
    Node sec = head;
    while(first != null && sec != null)
    {
        first = first.next;
        sec = sec.next.next;
        if(first == sec )
        {
            dec = true;
            break;
        }

    }
        return dec;
}

上記の関数を使用して、Javaのリンクリストでループを検出します。


2
上記の私の答えとほぼ同じですが、問題があります。奇数長のリスト(ループなし)のリストに対してNullPointerExceptionをスローします。たとえば、head.nextがnullの場合、sec.next.nextはNPEをスローします。
デイブL.

1

リンクリストのループの検出は、最も簡単な方法の1つで実行できます。これにより、ハッシュマップを使用したO(N)またはソートベースのアプローチを使用したO(NlogN)が複雑になります。

リストを先頭からトラバースしながら、ソートされたアドレスのリストを作成します。新しいアドレスを挿入するときは、アドレスがソート済みリストにすでに存在するかどうかを確認します。これはO(logN)の複雑さを伴います。


このアプローチの複雑さはO(N log N)
fgp

0

これを一定の時間またはスペースで行う方法はわかりません。どちらもリストのサイズが大きくなると増加します。

IdentityHashMap(まだIdentityHashSetがない場合)を使用して、各ノードをマップに格納します。ノードが格納される前に、そのノードでcontainsKeyを呼び出します。ノードがすでに存在する場合は、サイクルがあります。

ItentityHashMapは.equalsの代わりに==を使用するため、オブジェクトが同じ内容であるかどうかではなく、メモリ内のどこにあるかを確認できます。


3
リストの最後でループが発生する可能性があるため、リスト全体にアクセスする必要があるため、一定の時間がかかることは確かに不可能です。ただし、Fast / Slowアルゴリズムは、固定量のメモリを使用するソリューションを示しています。
Dave L.

それはそれの漸近的な振る舞いを参照していませんか、すなわちそれは線形O(n)であり、nはリストの長さです。修正されるのはO(1)
Mark Robson

0

私はひどく遅く、このスレッドを処理するのが初めてかもしれません。それでも..

ノードのアドレスとポイントされた「次の」ノードがテーブルに格納できないのはなぜですか

このように集計できたら

node present: (present node addr) (next node address)

node 1: addr1: 0x100 addr2: 0x200 ( no present node address till this point had 0x200)
node 2: addr2: 0x200 addr3: 0x300 ( no present node address till this point had 0x300)
node 3: addr3: 0x300 addr4: 0x400 ( no present node address till this point had 0x400)
node 4: addr4: 0x400 addr5: 0x500 ( no present node address till this point had 0x500)
node 5: addr5: 0x500 addr6: 0x600 ( no present node address till this point had 0x600)
node 6: addr6: 0x600 addr4: 0x400 ( ONE present node address till this point had 0x400)

したがって、形成されたサイクルがあります。


ソリューションは、「一定量のスペース」要件を満たしていません。
Arnaud、

0

これが私の実行可能なコードです。

私が行ったことはO(1)、リンクを追跡する3つの一時ノード(スペースの複雑さ)を使用して、リンクリストを崇拝することです。

これを行うことの興味深い事実は、リンクリストのサイクルを検出するのに役立つことです。先に進むと、開始点(ルートノード)に戻ることは期待できず、一時ノードの1つがnullになるはずです。ルートノードを指すことを意味するサイクルがあります。

このアルゴリズムの時間の複雑さはでO(n)あり、空間の複雑さはO(1)です。

リンクリストのクラスノードは次のとおりです。

public class LinkedNode{
    public LinkedNode next;
}

以下は、最後のノードが2番目のノードを指す3つのノードの単純なテストケースを持つメインコードです。

    public static boolean checkLoopInLinkedList(LinkedNode root){

        if (root == null || root.next == null) return false;

        LinkedNode current1 = root, current2 = root.next, current3 = root.next.next;
        root.next = null;
        current2.next = current1;

        while(current3 != null){
            if(current3 == root) return true;

            current1 = current2;
            current2 = current3;
            current3 = current3.next;

            current2.next = current1;
        }
        return false;
    }

以下は、最後のノードが2番目のノードを指す3つのノードの単純なテストケースです。

public class questions{
    public static void main(String [] args){

        LinkedNode n1 = new LinkedNode();
        LinkedNode n2 = new LinkedNode();
        LinkedNode n3 = new LinkedNode();
        n1.next = n2;
        n2.next = n3;
        n3.next = n2;

        System.out.print(checkLoopInLinkedList(n1));
    }
}

0

このコードは最適化されており、最良の回答として選択したものよりも速く結果を生成します。このコードは、次の場合に発生する、前方および後方ノードポインターを追跡する非常に長いプロセスに進むのを防ぎます。答え」の方法。以下の予行演習を見ると、私が言っていることがわかるはずです。次に、以下の所定の方法で問題を確認し、Noを測定します。答えを見つけるために取られたステップの。

1-> 2-> 9-> 3 ^ -------- ^

これがコードです:

boolean loop(node *head)
{
 node *back=head;
 node *front=head;

 while(front && front->next)
 {
  front=front->next->next;
  if(back==front)
  return true;
  else
  back=back->next;
 }
return false
}

これがすべての状況で正しい結果を生み出すことを確信していますか?リストでこのアルゴリズムを実行すると、1-> 2-> 3-> 4-> 5-> 6-> 7-> 3-> ...、4がヘッドとして返されますが、 3.
サンリーフ2016年

問題は、ループが存在するかどうかを見つけることだけです。この場合、はい、問題は完全に機能し、目的のブール結果を取得します。ループが開始した場所から正確なノードが必要な場合は、コードにさらに何かを追加する必要があります。しかし、結果の生成に関する限り、これはより速い結論を生成します。
Sarthak Mehra

あなたは質問を適切に読みませんでした:boolean hasLoop(Node first)与えられたノードがループのあるリストの最初である場合にtrueを返し、そうでない場合にfalseを返す最良の記述方法は何ですか?
サンリーフ2016年

これがリストの予行演習です。最初の値はバックポインターを意味し、2番目の値はフォワードポインターを意味します。(1,1)-(1,3)-(2,3)-(2,5)-(3,5) -(3,7)-(4,7)-(4,4)。
Sarthak Mehra

実際、質問を理解するには2つの方法がある(または、少なくとも2つの異なる解釈を見ている)ことに気づきました。ループがあるかどうかを検索しているだけなら、アルゴリズムは正しいです。しかし、問題はループがどこから始まっているのかということだと思いました。
サンリーフ2016年

0

これがJavaでの私の解決策です

boolean detectLoop(Node head){
    Node fastRunner = head;
    Node slowRunner = head;
    while(fastRunner != null && slowRunner !=null && fastRunner.next != null){
        fastRunner = fastRunner.next.next;
        slowRunner = slowRunner.next;
        if(fastRunner == slowRunner){
            return true;
        }
    }
    return false;
}

0

上記の回答で提案されているように、フロイドの亀のアルゴリズムも使用できます。

このアルゴリズムは、単一リンクリストに閉サイクルがあるかどうかをチェックできます。これは、異なる速度で移動する2つのポインタを含むリストを反復することによって実現できます。このように、サイクルがある場合、2つのポインタは将来のある時点で出会います。

上記のアルゴリズムをJava言語で実装したコードスニペットも含めて、リンクリストのデータ構造に関する私のブログ投稿を確認してください。

よろしく、

アンドレアス(@xnorcode)


0

これは、サイクルを検出するためのソリューションです。

public boolean hasCycle(ListNode head) {
            ListNode slow =head;
            ListNode fast =head;

            while(fast!=null && fast.next!=null){
                slow = slow.next; // slow pointer only one hop
                fast = fast.next.next; // fast pointer two hops 

                if(slow == fast)    return true; // retrun true if fast meet slow pointer
            }

            return false; // return false if fast pointer stop at end 
        }

0

//リンクリスト検索ループ関数

int findLoop(struct Node* head)
{
    struct Node* slow = head, *fast = head;
    while(slow && fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
            return 1;
    }
 return 0;
}

-1

このアプローチにはスペースのオーバーヘッドがありますが、実装はより単純です。

ループは、ノードをマップに格納することで識別できます。そしてノードを置く前; ノードがすでに存在するかどうかを確認してください。ノードがマップにすでに存在する場合、リンクリストにループがあることを意味します。

public boolean loopDetector(Node<E> first) {  
       Node<E> t = first;  
       Map<Node<E>, Node<E>> map = new IdentityHashMap<Node<E>, Node<E>>();  
       while (t != null) {  
            if (map.containsKey(t)) {  
                 System.out.println(" duplicate Node is --" + t  
                           + " having value :" + t.data);  

                 return true;  
            } else {  
                 map.put(t, t);  
            }  
            t = t.next;  
       }  
       return false;  
  }  

これは、質問で与えられた一定のスペース制限を満たしていません!
dedek 2014年

スペースのオーバーヘッドがあることに同意します。これは、この問題を解決するための別のアプローチです。明らかなアプローチは、亀と馬のアルゴリズムです。
rai.skumar 2014年

@downvoter、理由も説明していただければ助かります。
rai.skumar 2017

-2
public boolean isCircular() {

    if (head == null)
        return false;

    Node temp1 = head;
    Node temp2 = head;

    try {
        while (temp2.next != null) {

            temp2 = temp2.next.next.next;
            temp1 = temp1.next;

            if (temp1 == temp2 || temp1 == temp2.next) 
                return true;    

        }
    } catch (NullPointerException ex) {
        return false;

    }

    return false;

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.