Bağlantılı listedeki bir döngü nasıl algılanır?


434

Diyelim ki Java'da bağlantılı bir liste yapınız var. Düğümlerden oluşur:

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

ve her bir düğüm, bir sonraki için boş olan son Düğüm hariç, bir sonraki düğüme işaret eder. Listenin bir döngü içerebilme olasılığı olduğunu varsayalım; yani son Düğüm, bir boş değere sahip olmak yerine, listedeki düğümlerden birine başvuruda bulunuyor.

Yazmanın en iyi yolu nedir

boolean hasLoop(Node first)

trueverilen Düğüm bir döngüye sahip bir listenin ilki ise hangisi dönecektir false? Nasıl sabit bir alan ve makul bir zaman gerektirecek şekilde yazabilirsiniz?

Döngü içeren bir listenin neye benzediğini gösteren bir resim:

alternatif metin


50
Vay be ... Bu işveren için çalışmak finite amount of space and a reasonable amount of time?
isterim

10
@SLaks - döngü ilk düğüme geri döngü gerektirmez. Yarısına kadar geri dönebilir.
jjujuma

109
Aşağıdaki cevaplar okunmaya değer, ancak bunun gibi mülakat soruları korkunç. Cevabı biliyorsunuz (yani Floyd'un algoritmasında bir varyant gördünüz) ya da bilmiyorsunuz ve akıl yürütme veya tasarım yeteneğinizi test etmek için hiçbir şey yapmıyor.
GaryF

3
Adil olmak gerekirse, "algoritmaları bilmek" in çoğu böyledir - araştırma düzeyinde bir şey yapmazsanız!
Larry

12
@GaryF Yine de cevabı bilmediklerinde ne yapacaklarını bilmek açıklayıcı olurdu. Örneğin hangi adımları atacaklardı, kiminle çalışacaklar, algoritmik bilgi eksikliğinin üstesinden gelmek için ne yapacaklardı?
Chris Knight

Yanıtlar:


538

Floyd'un kaplumbağa ve tavşan algoritması olarak da bilinen döngü bulma algoritmasını kullanabilirsiniz .

Fikir, listeye iki referans vermek ve bunları farklı hızlarda taşımaktır . Birini 1düğüm ve diğerini 2düğümlerle ilerletin .

  • Bağlantılı listenin bir döngüsü varsa, kesinlikle karşılarlar.
  • Diğer iki referanstan biri (ya da onların next) olur null.

Algoritmayı uygulayan Java işlevi:

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
Ayrıca tekrar fast.nextaramadan önce bir null kontrol yapmak gerekir next:if(fast.next!=null)fast=fast.next.next;
cmptrgeekken

12
hızlıya yavaş atlamayı önlemek için (yavaş == hızlı) değil: (yavaş == hızlı || yavaş.next == hızlı) kontrol etmelisiniz
Oleg Razgulyaev

13
yanılmışım: hızlı yavaş atlayamaz, çünkü bir sonraki adımda yavaşça atlamak hızlı aynı pozisyonda olmalıdır :)
Oleg Razgulyaev

4
Listede yalnızca bir düğüm yoksa slow == null denetimi gereksizdir. Ayrıca Node.next adresine yapılan bir çağrıdan da kurtulabilirsiniz. İşte döngünün daha basit ve daha hızlı bir sürümü: pastie.org/927591
Kay Sarraute

22
Referanslarınızı gerçekten belirtmelisiniz. Bu algoritma Robert Floyd tarafından 60'larda icat edildi, Floyd'un döngü bulma algoritması olarak bilinir. Kaplumbağa ve Tavşan Algoritması.
joshperry

127

Burada, garip uzunluk listelerini doğru şekilde işleyen ve netliği artıran Hızlı / Yavaş çözümünün iyileştirilmesi.

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
Güzel ve özlü. Bu kod yavaş == hızlı || (fast.next! = null && slow = fast.next); :)
arachnode.net

11
@ arachnode.net Bu bir optimizasyon değil. slow == fast.nextO slowzaman bir fastsonraki yinelemede eşit olacaksa ; her yineleme için ek bir test pahasına yalnızca bir yineleme kaydeder.
Jason C

@ ana01 , aynı referans yolunu izlediği için daha slowönce boş olamaz fast(bu durumda tüm bahisler kapalıysa, listede aynı anda değişiklik yapmadığınız sürece).
Dave

Meraktan, bu tek sayılar için nasıl çalışır? Tavşan hala tek uzunluktaki bağlantılı listelerde kaplumbağa geçemez mi?
theGreenCabbage

1
@theGreenCabbage Tavşan her döngü tekrarı kaplumbağadan 1 adım ileride olur. Tavşan 3 adımda geride kalırsa, bir sonraki yineleme iki atlama alır ve kaplumbağa bir hop alır ve şimdi tavşan 2 adım geride kalır. Bir sonraki yinelemeden sonra tavşan 1 hopla geride kaldı ve sonra tam olarak yakalandı. Eğer kaplumbağa bir tane alırken tavşan 3 atlama aldıysa, her seferinde 2 artacağı için atlayabilir, ancak her bir yinelemenin sadece 1 kazanması nedeniyle geçemez.
Dave L.

52

Floyd'un algoritmasından daha iyi

Richard Brent alternatif bir döngü algılama algoritması tanımladı , tavşan ve kaplumbağaya (Floyd'un döngüsü) benzeyen , ancak buradaki yavaş düğüm hareket etmiyor, ancak daha sonra sabit düğümdeki hızlı düğüm konumuna "ışınlanıyor" aralıkları.

Açıklama burada bulunabilir: http://www.siafoo.net/algorithm/11 Brent algoritmasının Floyd'un döngü algoritmasından% 24 ila 36 daha hızlı olduğunu iddia ediyor. O (n) zaman karmaşıklığı, O (1) uzay karmaşıklığı.

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;
}

Bu cevap harika!
valin077

1
Cevabınızı gerçekten beğendim, bloguma ekledi - k2code.blogspot.in/2010/04/… .
kinshuk4

Neden kontrol etmeniz gerekiyor slow.next != null? Görebildiğim kadarıyla slowhep geride ya da eşittir fast.
TWiStErRob

Bunu uzun zaman önce algoritmalar öğrenmeye başladığımda yaptım. Kodu düzenledi. Teşekkürler :)
Ashok Bijoy Debnath

50

Listeyi geçici olarak değiştirdiğim kadar güzel değil, Kaplumbağa ve Tavşan için alternatif bir çözüm:

Fikir, listeyi yürümek ve gittikçe tersine çevirmektir. Ardından, daha önce ziyaret edilmiş bir düğüme ilk ulaştığınızda, bir sonraki işaretçisi "geriye" işaret edecek ve yinelemenin firstsona erdiği yere tekrar ilerlemesine neden olacaktır .

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;

Test kodu:

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);
}

Döngü ilk önce başka bir düğüme işaret ettiğinde tersi doğru çalışır mı? İlk bağlantılı liste bu şekildeyse 1-> 2-> 3-> 4-> 5-> 2 (5'den 2'ye kadar bir döngü ile), tersine çevrilmiş liste 1-> 2 <-3 <-4 gibi görünür <-5? Ve bunun tersi ise, yeniden yapılan son liste berbat mı olacak?
Zenil

1
@Zenil: Bu yüzden son düğümün listenin ortasını gösterdiği son test çantasını yazdım. Yeniden yapılanma işe yaramazsa, bu test başarısız olur. Örneğiniz hakkında: 1-> 2-> 3-> 5-> 2 geri dönüşü 1-> 2-> 5-> 4-> 3-> 2 olacaktır, çünkü döngü sadece listenin sonunda bir kez durur döngünün sonuna (kolayca tespit edemediğimiz) ulaşıldığında değil, ulaşıldı.
meriton

28

Kaplumbağa ve tavşan

Pollard'ın rho algoritmasına bir göz atın . Aynı sorun değil, ama belki de mantığı ondan anlayacak ve bağlantılı listelere uygulayacaksınız.

(tembelseniz, döngü algılamasını kontrol edebilirsiniz. - kaplumbağa ve tavşan hakkındaki bölümü kontrol edin.)

Bu sadece doğrusal zaman ve 2 ekstra işaretçi gerektirir.

Java dilinde:

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;
}

(Solüsyonlarının çoğu ikisi için kontrol etmiyoruz nextve next.nextiçin boş değerlere Ayrıca kaplumbağa arkasında daima olduğundan, size boş olup olmadığını kontrol etmek gerekmez -.. Tavşan zaten bunu yaptı)


13

Kullanıcı unicornaddict yukarıda güzel bir algoritma var, ama maalesef garip uzunluk> = 3 döngüsel olmayan listeler için bir hata içerir. Sorun, fastlistenin sonundan hemen önce "sıkışmış" alabilirsinizslow , o kadar yakalar ve bir döngü (yanlış) tespit edildi.

İşte düzeltilmiş algoritma.

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

Bu bağlamda, her yerde metin materyallerine yükler vardır. Sadece konsepti anlamamda bana yardımcı olan diyagramatik bir sunum yapmak istedim.

Hızlı ve yavaş p noktasında buluştuğunda,

Hızlı gidilen mesafe = a + b + c + b = a + 2b + c

Yavaş hareket mesafesi = a + b

Çünkü hızlı yavaştan 2 kat daha hızlıdır. Yani a + 2b + c = 2 (a + b) , sonra a = c elde ederiz .

Bu nedenle, başka bir yavaş işaretçi baştan başa tekrar çalıştığında , aynı zamanda hızlı işaretçi p'den q'ya koşar , böylece q noktasında bir araya gelirler.

resim açıklamasını buraya girin

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
Bir resim binlerce kelimeden daha değerli. Düzgün ve basit bir açıklama için teşekkürler!
Calios

1
İnternette en iyi açıklama. Sadece bu hızlı ve yavaş işaretçi doğrusal zaman sonra yakınsama kanıtlıyor eklemek
VarunPandey

eğer adöngü uzunluğundan daha büyüktür sonra hızlı çoklu döngü yapacak ve formül distance (fast) = a + b + b + cdeğişecektir a + (b+c) * k + bekstra parametreyi tanıtan kBPPP sayıları sayının hızla biri tarafından yapılan bu.
Ben

9

Algoritma

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;
}

karmaşa

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

Uzay karmaşıklığı O (2n) nasıldır?
Programcı345

@ user3543449 haklısın, sadece nsabit olmalı
Khaled.K

1
Bu aslında her bir ArrayList'in O (n) kontrolünü içerdiği ve bunlardan O (n) olduğu için zaman olduğundan ~ O (n ^ 2) 'dir. Doğrusal zaman için bunun yerine bir HashSet kullanın.
Dave L.

3
Bu döngüleri için ancak öğelerini kullanarak yinelenen değerler için test etmez equalsve hashCode. Aynı şey değil. Ve nullson elementin dereferences'ı . Ve soru, düğümleri a LinkedList.
Lii

2
@Lii bu sahte bir kod, bu bir Java kodu değil, bu yüzden onu Algoritma
başladım

8

Aşağıdakiler en iyi yöntem olmayabilir - O (n ^ 2). Bununla birlikte, işi bitirmeye hizmet etmelidir (sonunda).

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 () işlevini yapmak için listede kaç öğe olduğunu nasıl bilebilirsiniz?
Jethro Larson

@JethroLarson: Bağlantılı bir listedeki son düğüm bilinen bir adrese işaret ediyor (birçok uygulamada bu NULL). Bilinen adrese ulaşıldığında for döngüsünü sonlandırın.
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;
}

Cehaletimi affet (Java ve programlama konusunda hala yeniyim), ama yukarıdakiler neden işe yaramasın ki?

Sanırım bu sürekli uzay sorununu çözmüyor ... ama en azından oraya makul bir zamanda var, değil mi? Yalnızca bağlantılı listenin alanını artı n öğeli bir kümenin alanını alır (burada n, bağlantılı listedeki öğelerin sayısı veya bir döngüye ulaşana kadar öğe sayısıdır). Ve zamanla, en kötü durum analizi, sanırım O (nlog (n)) önerir. İnclude () için SortedSet aramaları log (n) (javadoc'u kontrol edin, ancak TreeSet'in altında yatan yapının TreeMap olduğundan eminim ki bu da kırmızı-siyah bir ağaçtır) ve en kötü durumda (döngüler, veya sonda döngü), n-up yapmak zorunda kalacak.


2
Evet, bir dizi Set içeren bir çözüm iyi çalışır, ancak listenin boyutuyla orantılı alan gerektirir.
jjujuma

3

Sınıfı gömmemize izin verilirse Node, sorunu aşağıda uyguladığım şekilde çözerdim. hasLoop()O (n) zamanda çalışır ve sadece boşluğunu alır counter. Bu uygun bir çözüm gibi mi görünüyor? Yoksa gömmeden yapmanın bir yolu var Nodemı? (Açıkçası, gerçek bir uygulamada RemoveNode(Node n), vb. Gibi daha fazla yöntem olacaktır .)

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

Bunu sabit O (1) zamanda bile yapabilirsiniz (çok hızlı veya verimli olmasa da): Bilgisayarınızın belleğinin tutabileceği sınırlı sayıda düğüm var, örneğin N kayıtları. N'den fazla kayıt geçirirseniz, bir döngünüz vardır.


Bu O (1) değildir, bu algoritmanın büyük O gösterimlerinde anlamlı bir zaman karmaşıklığı yoktur. Büyük-O-notasyonu sadece performansı hakkında size söyler limiti giriş boyutu sonsuza gider. Senin algoritma var varsayımına dayanmaktadır Yani eğer vardır bazı büyük N, N adetten fazla elemana sahip hiçbir listeler, liste boyutu olarak çalışma zamanı sınırı sonsuzluk tanımlanmamış yaklaşır. Dolayısıyla, karmaşıklık "O (herhangi bir şey)" değildir.
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 bağlantılı listesinde bir döngü algılamak için yukarıdaki işlevi kullanın.


2
Yukarıdaki cevabımın hemen hemen aynısı, ama bir sorunu var. Tek uzunluk listelerine (döngüsüz) sahip listeler için bir NullPointerException kurar. Örneğin, head.next boşsa, sec.next.next bir NPE atar.
Dave L.

1

Bağlantılı bir listedeki bir döngüyü tespit etmek en basit yollardan biriyle yapılabilir, bu da sıralama tabanlı bir yaklaşım kullanarak hashmap veya O (NlogN) kullanarak O (N) karmaşıklığına yol açar.

Listeyi baştan başa doğru hareket ettirdikçe, sıralı bir adres listesi oluşturun. Yeni bir adres eklediğinizde, adresin zaten sıralanmış listede O (logN) karmaşıklığı olup olmadığını kontrol edin.


Bu apporach karmaşıklığı O (N log N)
fgp

0

Bunu sabit bir süre veya alan kaplamanın herhangi bir yolunu göremiyorum, her ikisi de listenin boyutuyla artacak.

(Henüz bir IdentityHashSet olmadığı göz önüne alındığında) bir IdentityHashMap kullanırım ve her düğümü haritaya kaydederim. Bir düğüm kaydedilmeden önce üzerinde ihtiva anahtarını çağırırsınız. Düğüm zaten varsa bir döngünüz vardır.

ItentityHashMap .equals yerine == kullanır, böylece nesnenin aynı içeriğe sahip olması yerine bellekte nerede olduğunu kontrol edersiniz.


3
Listenin en sonunda bir döngü olabileceğinden, sabit bir süre alması kesinlikle imkansızdır, bu nedenle tüm listenin ziyaret edilmesi gerekir. Ancak Hızlı / Yavaş algoritma, sabit miktarda bellek kullanan bir çözüm gösterir.
Dave L.

Asimptotik davranışından bahsetmiyor mu, yani n listenin uzunluğu olan doğrusal O (n) 'dir. Sabit O (1)
Mark Robson

0

Bu konuyu ele almak için çok geç ve yeni olabilirim. Ama hala..

Düğümün adresi ve işaret eden "sonraki" düğüm neden bir tabloda saklanamıyor?

Bu şekilde tablolaştırabilseydik

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)

Dolayısıyla bir döngü oluşur.


Çözümünüz "sabit alan" gereksinimini karşılamıyor.
Arnaud

0

İşte benim çalıştırılabilir kodum.

Ne yaptım bağlantıları takip üç geçici düğüm (boşluk karmaşıklığı O(1)) kullanarak bağlı liste geri almaktır .

Bunu yapmakla ilgili ilginç gerçek, bağlantılı listedeki döngüyü algılamaya yardımcı olmaktır, çünkü ilerledikçe, başlangıç ​​noktasına (kök düğüm) geri dönmeyi beklemezsiniz ve geçici düğümlerden biri, siz sürece sürece null değerine gitmelidir. Kök düğümü işaret ettiği bir döngü var.

Bu algoritmanın zaman karmaşıklığı O(n)ve uzay karmaşıklığıdır O(1).

Bağlantılı listenin sınıf düğümü:

public class LinkedNode{
    public LinkedNode next;
}

Son düğümün ikinci düğüme işaret ettiği üç düğümden oluşan basit bir test senaryosuna sahip ana kod:

    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;
    }

Son düğümün ikinci düğüme işaret ettiği üç düğümden oluşan basit bir test örneği:

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

Bu kod optimize edilmiştir ve en iyi yanıt olarak seçilen koddan daha hızlı sonuç verecektir. cevap 'yöntemi. aşağıdaki kuru çalışma bakmak ve ne söylemeye çalışacağımı fark edeceksiniz. sonra aşağıdaki verilen yöntem ile soruna bakmak ve no ölçmek. cevabı bulmak için atılan adımlar.

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

İşte kod:

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
}

Bunun her durumda doğru sonucu verdiğinden emin misiniz? Bu algoritmayı 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 3 -> ... listesinde çalıştırırsanız, 4 tane kafa olarak döneceğine inanıyorum. 3.
Sunreef

Soru sadece bir döngü olup olmadığını bulmaktır.Bu durumda, evet, soru kesinlikle iyi çalışacak ve vaka için istenen boolean sonucunu alacaktır.Lopun başladığı yerden tam düğüm istiyorsanız, o zaman bir sonuç üretmek söz konusu olduğunda, bu daha hızlı bir sonuç üretecektir.
Sarthak Mehra

Soruyu doğru bir şekilde okumadınız: boolean hasLoop(Node first)Verilen Düğüm bir döngü içeren listenin ilkini alırsa doğru, aksi halde yanlış döndürecek en iyi yazma yolu nedir ?
Sunreef

İşte listeniz için kuru çalışma. İlk değer geri işaretçi ve ikinci bölüm ileri işaretçi anlamına gelir. (1,1) - (1,3) - (2,3) - (2,5) - (3,5) - (3,7) - (4,7) - (4,4).
Sarthak Mehra

Aslında, şu an farkındayım ki soruyu anlamanın iki yolu var (ya da en azından iki farklı yorum görüyorum). Sadece bir döngü olup olmadığını arıyorsanız algoritmanız doğrudur. Ama sorunun döngünün nereden başladığını sorduğunu düşündüm.
Sunreef

0

İşte benim java çözüm

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

Floyd'un kaplumbağa algoritmasını yukarıdaki cevaplarda da önerildiği gibi kullanabilirsiniz.

Bu algoritma, tek bağlantılı bir listenin kapalı bir döngüye sahip olup olmadığını kontrol edebilir. Bu, farklı hızda hareket edecek iki işaretçi içeren bir listeyi yineleyerek elde edilebilir. Bu şekilde, bir döngü varsa, iki işaretçi gelecekte bir noktada buluşacaktır.

Lütfen java dilinde yukarıda belirtilen algoritmanın bir uygulamasına sahip bir kod snippet'i eklediğim bağlantılı listeler veri yapısında blog yayınımı kontrol etmekten çekinmeyin .

Saygılarımızla,

Andreas (@xnorcode) Instagram Profilini Görüntüle


0

Döngüyü tespit etmek için çözüm.

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

// bağlantılı liste bul döngü işlevi

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

Bu yaklaşımın alan yükü vardır, ancak daha basit bir uygulaması vardır:

Döngü, düğümleri bir Haritaya kaydederek tanımlanabilir. Ve düğümü koymadan önce; düğümün zaten mevcut olup olmadığını kontrol edin. Haritada düğüm zaten varsa Bağlantılı Liste'nin döngü olduğu anlamına gelir.

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;  
  }  

Bu , soruda verilen sabit alan kısıtlaması miktarını karşılamıyor !
dedek

boşluk yükü olduğunu kabul edin; bu sorunu çözmek için başka bir yaklaşım. Açık yaklaşım kaplumbağa ve sert algoritmadır.
rai.skumar

@downvoter, sebebini de açıklamanız yararlı olacaktır.
rai.skumar

-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;

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.