लिंक की गई सूची में एक लूप का पता कैसे लगाएं?


434

कहते हैं कि आपके पास जावा में एक लिंक सूची संरचना है। यह नोड्स से बना है:

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

और प्रत्येक नोड अंतिम नोड को छोड़कर अगले नोड को इंगित करता है, जिसमें अगले के लिए शून्य है। कहो कि एक संभावना है कि सूची में एक लूप हो सकता है - यानी अंतिम नोड, एक अशक्त होने के बजाय, सूची में एक नोड का संदर्भ है जो इसके पहले आया था।

लिखने का सबसे अच्छा तरीका क्या है

boolean hasLoop(Node first)

जो वापसी होगी trueअगर दिए गए नोड एक पाश के साथ एक सूची के पहले है, और falseनहीं तो? आप ऐसा कैसे लिख सकते हैं कि यह एक निरंतर स्थान और समय की उचित मात्रा लेता है?

यहाँ लूप वाली सूची क्या दिखती है:

वैकल्पिक शब्द


50
वाह..मैं इस नियोक्ता के लिए काम करना पसंद करूंगा finite amount of space and a reasonable amount of time?:)
कोडादिक

10
@ एसएलएस - लूप पहले नोड में वापस आवश्यक लूप नहीं है। यह आधे रास्ते तक वापस जा सकता है।
जुजुमा

109
नीचे दिए गए जवाब पढ़ने लायक हैं, लेकिन इस तरह से साक्षात्कार के सवाल भयानक हैं। आप या तो जवाब जानते हैं (यानी आपने फ्लॉयड के एल्गोरिदम पर एक संस्करण देखा है) या आप नहीं करते हैं, और यह आपके तर्क या डिजाइन की क्षमता का परीक्षण करने के लिए कुछ भी नहीं करता है।
गैरीएफ

3
निष्पक्ष होने के लिए, "जानने वाले एल्गोरिदम" में से अधिकांश इस तरह है - जब तक आप अनुसंधान-स्तरीय चीजें नहीं कर रहे हैं!
लैरी

12
@ गैरीएफ और फिर भी यह जानना होगा कि जब वे जवाब नहीं जानते थे तो वे क्या करेंगे। उदा। वे क्या कदम उठाएंगे, वे किसके साथ काम करेंगे, एल्गोरिथम ज्ञान की कमी को दूर करने के लिए वे क्या करेंगे?
क्रिस नाइट

जवाबों:


538

आप फ्लोयड के चक्र-खोज एल्गोरिथ्म का उपयोग कर सकते हैं , जिसे कछुआ और हरे एल्गोरिदम के रूप में भी जाना जाता है

विचार सूची के दो संदर्भ हैं और उन्हें अलग-अलग गति से स्थानांतरित करना है1नोड द्वारा एक को आगे बढ़ाएं और दूसरे को 2नोड द्वारा ।

  • यदि लिंक की गई सूची में एक लूप है, तो वे निश्चित रूप से मिलेंगे।
  • दो संदर्भों में से या तो (या उनके next) बन जाएगा null

एल्गोरिथ्म को लागू करने वाला जावा फ़ंक्शन:

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
आपको न केवल (धीमी == तेज़) जाँच करनी चाहिए, बल्कि: (धीमी == तेज़ = धीमी || धीमी.प्राण == तेज़) धीमी गति से तेज़ कूदने से रोकने के लिए
ओलेग रज़ुल्येव

13
मैं गलत था: उपवास धीमी गति से नहीं कूद सकता, क्योंकि अगले कदम पर धीमी गति से कूदने के लिए धीमी गति से समान स्थिति होनी चाहिए :)
ओलेग रज़ुल्येव

4
जब तक सूची में केवल एक नोड नहीं है, तब तक धीमी == नल की जांच बेमानी है। आप Node.next पर एक कॉल से भी छुटकारा पा सकते हैं। यहाँ पाश का एक सरल और तेज़ संस्करण है: pastie.org/927591
Kay Sarraute

22
आपको वास्तव में अपने संदर्भों का हवाला देना चाहिए। इस एल्गोरिथ्म का आविष्कार रॉबर्ट फ्लॉयड ने 60 के दशक में किया था, इसे फ्लॉयड के चक्र-खोज एल्गोरिथ्म, उर्फ ​​के रूप में जाना जाता है। कछुआ और हरे एल्गोरिथ्म।
joshperry

127

यहां फास्ट / स्लो समाधान का शोधन किया गया है, जो विषम लंबाई सूचियों को सही ढंग से संभालता है और स्पष्टता में सुधार करता है।

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; यह केवल प्रत्येक पुनरावृत्ति के लिए एक अतिरिक्त परीक्षण की कीमत पर एक पुनरावृत्ति को बचाता है।
जेसन सी

@ ana01 संदर्भों के समान पथ का अनुसरण करने slowसे पहले अशक्त नहीं हो fastसकता है (जब तक कि आपके पास उस सूची का समवर्ती संशोधन नहीं है जिसमें सभी दांव बंद हैं)।
डेव एल।

जिज्ञासा से बाहर यह विषम संख्या के लिए कैसे काम करता है? क्या अब भी कछुए को विषम लंबाई से जुड़ी सूचियों से नहीं निकाला जा सकता है?
द ग्रेनकैबेज सेप

1
@TheGreenCabbage पाश के प्रत्येक पुनरावृत्ति को कछुए से 1 कदम आगे मिलता है। इसलिए अगर हरेक 3 चरणों से पीछे है, तो अगले पुनरावृत्ति में दो हॉप लगते हैं और कछुआ एक हॉप लेता है, और अब हरेक 2 चरणों से पीछे है। अगले पुनरावृत्ति के बाद हर 1 हॉप से ​​पीछे है, और फिर इसे बिल्कुल पकड़ा जाता है। अगर कछुए ने एक को लिया तो हरेक को 3 बार लग गए, तो वह इसे छोड़ सकता है, क्योंकि यह हर बार 2 से बढ़ रहा होगा, लेकिन चूंकि यह केवल 1 पुनरावृत्ति से प्राप्त होता है, इसलिए यह अतीत को छोड़ नहीं सकता है।
डेव एल।

52

फ्लोयड के एल्गोरिथ्म से बेहतर है

रिचर्ड ब्रेंट ने एक वैकल्पिक चक्र का पता लगाने वाले एल्गोरिदम का वर्णन किया , जो कि हरे और कछुए की तरह है [फ्लोयड का चक्र] इसके अलावा, यहां धीमा नोड नहीं चलता है, लेकिन बाद में निश्चित नोड पर तेज नोड की स्थिति के लिए "टेलीपोर्ट" किया जाता है। अंतराल।

विवरण यहाँ उपलब्ध है: 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;
}

यह जवाब कमाल है!
वेलिन ० val

1
वास्तव में आपके उत्तर को पसंद किया, इसे मेरे ब्लॉग पर शामिल किया - k2code.blogspot.in/2010/04/…
किंशुक ४

आपको जांच करने की आवश्यकता क्यों है slow.next != null? जहाँ तक मैं देख सकता हूँ slowहमेशा पीछे या बराबर होता है fast
ट्वीस्ट्रीमोब

मैंने यह बहुत पहले किया था, जब मैंने एल्गोरिदम सीखना शुरू किया था। कोड को संपादित किया। धन्यवाद :)
अशोक बिजॉय देबनाथ

50

कछुए और खरगोश के लिए एक वैकल्पिक समाधान, बहुत अच्छा नहीं है, क्योंकि मैं अस्थायी रूप से सूची को बदलता हूं:

विचार सूची को चलना है, और जैसे ही आप जाते हैं, इसे उल्टा करना है। फिर, जब आप पहली बार एक नोड पर पहुंचते हैं जो पहले से ही दौरा किया गया है, तो इसका अगला पॉइंटर "पीछे की ओर" इंगित करेगा, जिससे पुनरावृत्ति 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- 1- 2-> 3-> 4-> 5-> 2 (5 से 2 के चक्र के साथ), तो उलट सूची 1-> 2 <-3 <-4 की तरह दिखती है <-5? और अगर रिवर्स है, तो अंतिम खंगाला गया सूची खराब हो जाएगी?
ज़ेनिल

1
@Zenil: यही कारण है कि मैंने उस अंतिम टेस्टकेस को लिखा है, जहां अंतिम नोड सूची के मध्य में इंगित करता है। यदि पुनर्निर्माण काम नहीं करेगा, तो वह परीक्षा विफल हो जाएगी। अपने उदाहरण के बारे में: 1-> 2-> 3-> 5-> 2 का उलटा 1-> 2-> 5-> 4-> 3-> 2 होगा, क्योंकि लूप केवल सूची के अंत में रुक जाता है पहुँच गया है, तब नहीं जब लूप का अंत (जिसे हम आसानी से पता नहीं लगा सकते) तक पहुँच चुके हैं।
मेरिटॉन

28

कछुआ और खरगोश

पोलार्ड के आरएच एल्गोरिथ्म पर एक नज़र डालें । यह काफी समान समस्या नहीं है, लेकिन हो सकता है कि आप इससे तर्क को समझें, और इसे लिंक की गई सूचियों के लिए लागू करें।

(यदि आप आलसी हैं, तो आप केवल चक्र का पता लगा सकते हैं - कछुए और हरे के बारे में भाग की जाँच करें।)

इसके लिए केवल रैखिक समय और 2 अतिरिक्त बिंदुओं की आवश्यकता होती है।

जावा में:

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

(अधिकांश समाधान दोनों के लिए nextऔर next.nextनल के लिए जाँच नहीं करते हैं । इसके अलावा, चूंकि कछुआ हमेशा पीछे रहता है, आपको इसे नल के लिए जाँचने की ज़रूरत नहीं है - खरगोश ने पहले से ही ऐसा किया है।)


13

उपयोगकर्ता के यूनिकॉर्नडिक्ट में ऊपर एक अच्छा एल्गोरिथ्म है, लेकिन दुर्भाग्य से इसमें विषम लंबाई की गैर-लूपि सूची के लिए एक बग होता है> = 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

इस संदर्भ में, हर जगह पाठ्य सामग्री के लिए भार हैं। मैं सिर्फ एक आरेखात्मक अभ्यावेदन पोस्ट करना चाहता था जिसने मुझे अवधारणा को समझने में मदद की।

जब बिंदु पी पर तेज और धीमी गति से मिलते हैं,

तेजी से यात्रा की गई दूरी = a + b + c + b = a + 2b + c

धीमी गति से यात्रा की दूरी = ए + बी

चूंकि व्रत धीमी गति से 2 गुना तेज है। तो a + 2b + c = 2 (a + b) तो हमें a = c मिलता है

इसलिए जब एक और धीमा सूचक सिर से q तक फिर से चलता है , उसी समय, तेज सूचक पी से 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
एक चित्र हजारों शब्दों से अधिक मूल्य का होता है। स्वच्छ और सरल स्पष्टीकरण के लिए धन्यवाद!
कैलिओस

1
इंटरनेट पर सबसे अच्छा स्पष्टीकरण। बस इतना जोड़ना होगा कि यह साबित करता है कि तेजी और धीमी गति से सूचक रेखीय समय के बाद अभिसरण होता है
वरुणपांडे

यदि aलूप की लंबाई से बड़ा है तो तेजी से कई लूप बनेंगे और फॉर्मूला अतिरिक्त पैरामीटर distance (fast) = a + b + b + cको a + (b+c) * k + bशुरू करने के लिए बदल जाएगा kजो कि तेजी से एक से बने लूप्स की संख्या की गणना करता है।
बेन

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)

अंतरिक्ष जटिलता ओ (2 एन) कैसे है?
प्रोग्रामर

@ user3543449 आप रहे हों तो सही है, यह किया जा सिर्फ चाहिए n, फिक्स्ड
Khaled.K

1
यह वास्तव में समय है ~ O (n ^ 2) क्योंकि प्रत्येक में ArrayList के लिए जाँच में O (n) होता है और उनमें से O (n) होते हैं। रैखिक समय के बजाय एक हाशसेट का उपयोग करें।
डेव एल।

3
यह चक्रों के लिए परीक्षण नहीं करता है लेकिन तत्वों का उपयोग करके डुप्लिकेट मानों के लिए equalsऔर hashCode। यह एक ही बात नहीं है। और यह nullअंतिम तत्व पर dereferences है । और सवाल कुछ भी नहीं कहा कि नोड्स को स्टोर करने के बारे में LinkedList
Lii

2
@Lii यह एक छद्म कोड है, यह एक जावा कोड नहीं है, यही कारण है कि मैंने इसे एलगोरिदम के
खालेद

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

आप कैसे जानेंगे कि सूची () के लिए कितने तत्व हैं?
जेथ्रो लार्सन

@ जेथरो लार्सन: किसी लिंक की गई सूची में अंतिम नोड एक ज्ञात पते की ओर इशारा करता है (कई कार्यान्वयनों में, यह पूर्ण है)। उस ज्ञात पते पर पहुंचने पर फॉर-लूप को समाप्त करें।
स्पार्कली

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

मुझे मेरी अज्ञानता माफ कर दो (मैं अभी भी जावा और प्रोग्रामिंग के लिए काफी नया हूं), लेकिन उपरोक्त कार्य क्यों नहीं होगा?

मुझे लगता है कि यह निरंतर स्थान के मुद्दे को हल नहीं करता है ... लेकिन यह कम से कम एक उचित समय में मिलता है, सही है? यह केवल लिंक की गई सूची का स्थान लेगा, साथ ही n तत्वों के साथ एक सेट का स्थान (जहाँ n लिंक की गई सूची में तत्वों की संख्या है, या तत्वों की संख्या जब तक यह एक लूप तक नहीं पहुंचता है)। और समय के लिए, सबसे खराब स्थिति का विश्लेषण, मुझे लगता है, सुझाव होगा कि ओ (nlog (n))। इसमें सॉर्ट किए गए लुक-अप शामिल हैं () लॉग हैं (n) (javadoc की जांच करें, लेकिन मुझे पूरा यकीन है कि ट्रीसेट का अंतर्निहित ढांचा ट्रीपाउप है, जिसके बदले में लाल-काला पेड़ है), और सबसे खराब स्थिति में (लूप नहीं) या बहुत अंत में लूप), इसे n लुक-अप करना होगा।


2
हां, कुछ प्रकार के सेट के साथ एक समाधान ठीक काम करता है, लेकिन सूची के आकार के लिए आनुपातिक स्थान की आवश्यकता होती है।
जुजुमा

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 रिकॉर्ड्स। यदि आप एन रिकॉर्ड्स से अधिक पार करते हैं, तो आपके पास एक लूप है।


यह ओ (1) नहीं है, इस एल्गोरिथ्म में बिग-ओ नोटेशन में कोई सार्थक समय जटिलता नहीं है। बिग-ओ-नोटेशन केवल सीमा में प्रदर्शन के बारे में बताता है क्योंकि इनपुट आकार अनंत तक जाता है। तो अपने एल्गोरिथ्म धारणा है कि बनाता है, तो कर रहे हैं कुछ बड़े एन के लिए एन से अधिक तत्वों वाले कोई सूची, सूची के आकार के रूप में क्रम की सीमा के करीब पहुंचती अनंत अपरिभाषित है। इसलिए, जटिलता "ओ (कुछ भी)" नहीं है।
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;
}

जावा में लिंकलिस्ट में एक लूप का पता लगाने के लिए उपरोक्त फ़ंक्शन का उपयोग करें।


2
लगभग मेरे उत्तर के समान ही है, लेकिन एक मुद्दा है। यह विषम लंबाई सूचियों (बिना छोरों) वाली सूचियों के लिए NullPointerException को फेंक देगा। उदाहरण के लिए, अगर head.next शून्य है, तो sec.next.next एक NPE फेंक देगा।
डेव एल।

1

किसी लिंक की गई सूची में एक लूप का पता लगाना सबसे सरल तरीकों में से एक में किया जा सकता है, जिसके परिणामस्वरूप हेमप या ओ (एनओएलएन) का उपयोग करके सॉर्ट आधारित दृष्टिकोण का उपयोग करके ओ (एन) जटिलता होती है।

जब आप सूची को सिर से शुरू करते हैं, तो पतों की क्रमबद्ध सूची बनाएं। जब आप एक नया पता सम्मिलित करते हैं, तो जांच लें कि क्या पता पहले से ही सॉर्ट की गई सूची में है, जिसमें O (logN) जटिलता है।


इस एपोरच की जटिलता हे (एन लॉग एन)
एफजीपी

0

मैं इसे समय या स्थान की एक निश्चित राशि लेने का कोई तरीका नहीं देख सकता, दोनों सूची के आकार के साथ बढ़ेंगे।

मैं एक IdentityHashMap का उपयोग करूँगा (यह देखते हुए कि अभी तक IdentityHashSet नहीं है) और प्रत्येक नोड को मानचित्र में संग्रहीत करता है। इससे पहले कि कोई नोड संग्रहीत किया जाता है आप उस पर होता है। यदि नोड पहले से मौजूद है तो आपके पास एक चक्र है।

ItentityHashMap। = का उपयोग करता है। इसके बजाय। के समान है ताकि आप जाँच कर रहे हैं कि ऑब्जेक्ट मेमोरी में कहां है बजाय इसके कि इसमें समान सामग्री है।


3
निश्चित रूप से समय की एक निश्चित राशि लेना असंभव है, क्योंकि सूची के बहुत अंत में एक लूप हो सकता है, इसलिए पूरी सूची का दौरा किया जाना चाहिए। हालाँकि, Fast / Slow algorithm स्मृति की निश्चित मात्रा का उपयोग करके किसी समाधान को प्रदर्शित करता है।
डेव एल।

क्या यह विषमतापूर्ण व्यवहार का उल्लेख नहीं है, अर्थात यह रैखिक हे (n) है जहां n सूची की लंबाई है। फिक्स्ड होगा ओ (1)
मार्क रॉबसन

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)

इसलिए एक चक्र बनता है।


आपका समाधान "अंतरिक्ष की निरंतर मात्रा" आवश्यकता को पारित नहीं करता है।
अरनौद

0

यहाँ मेरा रन करने योग्य कोड है।

मैंने जो भी किया है वह तीन अस्थायी नोड्स (स्पेस जटिलता O(1)) का उपयोग करके लिंक की गई सूची को पुन: प्रस्तुत करना है जो लिंक का ट्रैक रखते हैं।

इसे करने के बारे में दिलचस्प तथ्य यह है कि लिंक की गई सूची में चक्र का पता लगाने में मदद करने के लिए क्योंकि आप आगे बढ़ते हैं, आप शुरुआती बिंदु (रूट नोड) पर वापस जाने की उम्मीद नहीं करते हैं और अस्थायी नोड्स में से एक को शून्य तक जाना चाहिए जब तक कि आप नहीं एक चक्र है जिसका अर्थ है कि यह रूट नोड को इंगित करता है।

इस एल्गोरिथ्म का समय जटिलता है O(n)और अंतरिक्ष जटिलता है O(1)

यहाँ लिंक की गई सूची के लिए वर्ग नोड है:

public class LinkedNode{
    public LinkedNode next;
}

यहाँ तीन नोड्स के सरल परीक्षण के मामले में मुख्य कोड है जो अंतिम नोड को दूसरे नोड की ओर इशारा करता है:

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

यहां तीन नोड्स का एक सरल परीक्षण मामला है जो अंतिम नोड को दूसरे नोड की ओर इशारा करता है:

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

यह कोड अनुकूलित है और सबसे अच्छे उत्तर के रूप में चुने गए के साथ तेजी से परिणाम देगा। यह कोड आगे और पिछड़े नोड पॉइंटर का पीछा करने की एक बहुत लंबी प्रक्रिया में जाने से बचाता है जो निम्न मामले में होगा यदि हम 'सर्वश्रेष्ठ का पालन करते हैं। उत्तर 'विधि। निम्नलिखित के सूखे रन के माध्यम से देखें और आप महसूस करेंगे कि मैं क्या कहने की कोशिश कर रहा हूं। फिर नीचे दी गई विधि के माध्यम से समस्या को देखें और न को मापें। जवाब खोजने के लिए उठाए गए कदम।

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.
सुनहरी

सवाल सिर्फ यह है कि क्या कोई लूप मौजूद है या नहीं। इस मामले में, हाँ, सवाल पूरी तरह से ठीक काम करेगा और मामले के लिए वांछित बूलियन परिणाम प्राप्त करेगा। यदि आप सटीक नोड चाहते हैं जहां से लूप शुरू हुआ, तो हम करेंगे किसी कोड में कुछ और जोड़ने की आवश्यकता है। लेकिन जहाँ तक एक परिणाम का संबंध है, यह एक तेजी से निष्कर्ष निकालता है।
सार्थक मेहरा

आपने इस प्रश्न को ठीक से नहीं पढ़ा: लेखन का सबसे अच्छा तरीका क्या है boolean hasLoop(Node first)जो कि सही होगा यदि दिए गए नोड में लूप के साथ एक सूची सबसे पहले है, और गलत है?
Sunreef

यहाँ आपकी सूची के लिए ड्राई रन है। फ़्री वैल्यू का अर्थ है बैक पॉइंटर और दूसरे भाग का मतलब है फॉरवर्ड पॉइंटर। (1,1) - (1,3) - (2,3) - (2,5) - (3,5) - (3,7) - (4,7) - (4,4)।
सार्थक मेहरा

दरअसल, मुझे अब एहसास हुआ कि सवाल को समझने के दो तरीके हैं (या कम से कम मुझे दो अलग-अलग व्याख्याएं दिखती हैं)। यदि आप खोज रहे हैं कि कहीं लूप है तो आपका एल्गोरिथ्म सही है। लेकिन मैंने सोचा कि सवाल पूछ रहा था कि लूप कहां शुरू हो रहा था।
Sunreef

0

यहाँ जावा में मेरा समाधान है

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

आप फ्लोयड के कछुआ एल्गोरिथ्म का उपयोग कर सकते हैं जैसा कि ऊपर दिए गए उत्तर में भी दिया गया है।

यह एल्गोरिथ्म जांच सकता है कि क्या एक एकल लिंक की गई सूची में एक बंद चक्र है। यह दो बिंदुओं के साथ एक सूची को पुनरावृत्त करके प्राप्त किया जा सकता है जो अलग-अलग गति में आगे बढ़ेगा। इस तरह, यदि कोई चक्र है, तो दोनों बिंदु भविष्य में किसी बिंदु पर मिलेंगे।

कृपया लिंक किए गए सूचियों डेटा संरचना पर अपने ब्लॉग पोस्ट की जांच करने के लिए स्वतंत्र महसूस करें , जहां मैंने जावा भाषा में उपर्युक्त एल्गोरिदम के कार्यान्वयन के साथ एक कोड स्निपेट भी शामिल किया है।

सादर,

एंड्रियास (@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;  
  }  

यह प्रश्न में दी गई अंतरिक्ष प्रतिबंध की निरंतर मात्रा को पूरा नहीं करता है !
देदेक

सहमत हूँ कि इसके पास अंतरिक्ष ओवरहेड है; यह इस समस्या को हल करने का एक और तरीका है। स्पष्ट दृष्टिकोण कछुआ और हर्ज़ एल्गोरिथ्म है।
राय.कुमार

@downvoter, यदि आप कारण भी बता सकते हैं तो यह उपयोगी होगा।
राय.कुमार

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

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.