हम लिंक की गई सूची में त्वरित सॉर्ट का उपयोग क्यों नहीं करते हैं?


16

त्वरित सॉर्ट एल्गोरिथ्म को निम्नलिखित चरणों में विभाजित किया जा सकता है

  1. धुरी को पहचानें।

  2. धुरी के आधार पर लिंक की गई सूची को विभाजित करें।

  3. लिंक की गई सूची को 2 भागों में पुन: विभाजित करें।

अब, यदि मैं हमेशा अंतिम तत्व को धुरी के रूप में चुनता हूं, तो धुरी तत्व (प्रथम चरण) की पहचान करने में समय लगता है।O(n)

धुरी तत्व की पहचान करने के बाद, हम उसके डेटा को स्टोर कर सकते हैं और सही विभाजन बिंदु (दूसरा चरण) की पहचान करने के लिए अन्य सभी तत्वों के साथ तुलना कर सकते हैं। प्रत्येक तुलना में समय लगेगा क्योंकि हम पिवट डेटा संग्रहीत करते हैं और प्रत्येक स्वैप समय लेता है । तो कुल में यह तत्वों के लिए समय लेता है ।O(1)O(1)O(n)n

तो पुनरावृत्ति संबंध है:

T(n)=2T(n/2)+n जो O(nlogn) जो कि एक लिंक्ड सूची के साथ मर्ज सॉर्ट के समान है।

तो लिंक्ड सूचियों के लिए त्वरित सॉर्ट पर मर्ज सॉर्ट को क्यों पसंद किया जाता है?


पहले के बजाय धुरी के रूप में अंतिम तत्व को चुनने की कोई आवश्यकता नहीं है
TheCppZoo

जवाबों:


19

क्विकॉर्ट में मेमोरी एक्सेस पैटर्न यादृच्छिक है, साथ ही आउट-ऑफ-द-बॉक्स कार्यान्वयन इन-प्लेस है, इसलिए यह कई स्वैप का उपयोग करता है यदि कोशिकाओं को आदेशित परिणाम प्राप्त करना है।
एक ही समय में मर्ज सॉर्ट बाहरी है, इसे ऑर्डर किए गए परिणाम को वापस करने के लिए अतिरिक्त सरणी की आवश्यकता होती है। सरणी में इसका अर्थ है कि अतिरिक्त स्थान ओवरहेड, यदि लिंक की गई सूची में, मूल्य को बाहर निकालना और नोड्स को मर्ज करना शुरू करना संभव है। पहुंच प्रकृति में अधिक अनुक्रमिक है।

इस वजह से, लिंक्ड सूची के लिए क्विकॉर्ट प्राकृतिक विकल्प नहीं है, जबकि मर्ज सॉर्ट में बहुत फायदा होता है।

Landau संकेतन (अधिक या कम, क्योंकि Quicksort अभी भी ) सहमत है, लेकिन निरंतरता कहीं अधिक है।O(n2)

औसत मामले में दोनों एल्गोरिदम इसलिए एसिम्प्टोटिक केस समान होता है, लेकिन पहले से छिपी होने के कारण वरीयता सख्ती से होती है और कभी-कभी स्थिरता मुद्दा होती है (Quicksort स्वाभाविक रूप से अस्थिर है, विलय स्थिर है) ।O(nlogn)


लेकिन औसत समय जटिलता समान है? लिंक्ड सूची के लिए त्वरित सॉर्ट और साथ ही मर्ज सॉर्ट का उपयोग करना।
ज़ेफियर

10
@Zephyr, आपको यह याद रखने की आवश्यकता है कि जटिलता अंकन लगातार कारकों को गिराता है। हां, एक लिंक की गई सूची में क्विकॉर्ट और एक लिंक की गई सूची पर मर्जेसट एक ही जटिलता वर्ग हैं, लेकिन उन स्थिरांक को आप विलय को समान रूप से तेज नहीं देख रहे हैं।
मार्क

@ ज़ेफियर मूल रूप से यह सैद्धांतिक और आनुभविक परिणामों का अंतर है। जाहिर है, quicksort तेज है।
फेरिट

1
इसके अलावा, एक अच्छी धुरी का चयन एक लिंक की गई सूची के लिए मुश्किल है। यदि आप अंतिम तत्व लेते हैं, जैसे कि ओपी सुझाव देता है, तो इसका मतलब है कि सबसे खराब स्थिति ( ) पहले से ही सॉर्ट की गई सूची या सबलिस्ट है। और यह सबसे बुरा मामला व्यवहार में दिखाई देने की काफी संभावना है। O(n2)
स्टिग हेमर

3
क्विकॉर्ट कभी जगह पर नहीं होता, यह एक आम गलत धारणा है। इसके लिए अतिरिक्त स्थान की आवश्यकता होती है। इसके अलावा, "यादृच्छिक" मेमोरी एक्सेस पैटर्न भी बहुत सटीक नहीं है: यह धुरी की पसंद पर महत्वपूर्ण रूप से निर्भर करता है, जैसा कि अन्य उत्तर में बताया गया है। O(logn)
कोनराड रूडोल्फ

5

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

लिंक की गई सूचियों पर मर्ज को केवल अतिरिक्त स्थान का उपयोग करके निष्पादित किया जा सकता है यदि आप विभाजन के अनुसार नीचे-अप दृष्टिकोण लेते हैं जहां विभाजन की सीमाएं हैं और तदनुसार विलय कर रहे हैं।O(1)

हालाँकि बिंदुओं के एक एकल 64 तत्व सरणी को जोड़ने से आप उस अतिरिक्त पुनरावृत्ति से बच सकते हैं और O ( 1 ) अतिरिक्त अतिरिक्त स्थान में तत्वों तक की क्रमबद्ध सूची बना सकते हैं।264O(1)

head = list.head;
head_array = array of 64 nulls

while head is not null
    current = head;
    head = head.next;
    current.next = null;
    for(i from 0 to 64)
        if head_array[i] is null
            head_array[i] = current;
            break from for loop;
        end if
        current = merge_lists(current, array[i]);
        head_array[i] = null;
     end for
end while

current = null;
for(i from 0 to 64)
    if head_array[i] is not null
        if current is not null
            current = merge_lists(current, head_array[i]);
        else
            current = head_array[i];
        end if
     end if
 end for

 list.head = current;

यह एल्गोरिथ्म है कि लिनक्स कर्नेल अपनी जुड़ी हुई सूचियों को छांटने के लिए उपयोग करता है। हालांकि कुछ अतिरिक्त अनुकूलन के साथ जैसे कि previousसूचक को अनदेखा करना लेकिन अंतिम मर्ज ऑपरेशन।


-2

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

program untitled;


type TData = longint;
     PNode = ^TNode;
     TNode = record
                data:TData;
                prev:PNode;
                next:PNode;
             end;

procedure ListInit(var head:PNode);
begin
  head := NIL;
end;

function ListIsEmpty(head:PNode):boolean;
begin
  ListIsEmpty := head = NIL;
end;

function ListSearch(var head:PNode;k:TData):PNode;
var x:PNode;
begin
  x := head;
  while (x <> NIL)and(x^.data <> k)do
     x := x^.next;
  ListSearch := x;
end;

procedure ListInsert(var head:PNode;k:TData);
var x:PNode;
begin
  new(x);
  x^.data := k;
  x^.next := head;
  if head <> NIL then
     head^.prev := x;
   head := x;
   x^.prev := NIL;
end;

procedure ListDelete(var head:PNode;k:TData);
var x:PNode;
begin
   x := ListSearch(head,k);
   if x <> NIL then
   begin
     if x^.prev <> NIL then
        x^.prev^.next := x^.next
      else 
        head := x^.next;
     if x^.next <> NIL then
        x^.next^.prev := x^.prev;
     dispose(x);
   end;
end;

procedure ListPrint(head:PNode);
var x:PNode;
    counter:longint;
begin
  x := head;
  counter := 0;
  while x <> NIL do
  begin
    write(x^.data,' -> ');
    x := x^.next;
    counter := counter + 1;
  end;
  writeln('NIL');
  writeln('Liczba elementow listy : ',counter);
end;

procedure BSTinsert(x:PNode;var t:PNode);
begin
  if t = NIL then
    t := x
  else
    if t^.data = x^.data then
            BSTinsert(x,t^.prev)
        else if t^.data < x^.data then
            BSTinsert(x,t^.next)
        else
            BSTinsert(x,t^.prev);
end;

procedure BSTtoDLL(t:PNode;var L:PNode);
begin
   if t <> NIL then
   begin
     BSTtoDLL(t^.next,L);
     ListInsert(L,t^.data);
     BSTtoDLL(t^.prev,L);
   end;
end;

procedure BSTdispose(t:PNode);
begin
   if t <> NIL then
   begin
    BSTdispose(t^.prev);
    BSTdispose(t^.next);
    dispose(t);
   end; 
end;

procedure BSTsort(var L:PNode);
var T,S:PNode;
    x,xs:PNode;
begin
  T := NIL;
  S := NIL;
  x := L;
  while x <> NIL do
  begin
    xs := x^.next;
    x^.prev := NIL;
    x^.next := NIL;
    BSTinsert(x,t);
    x := xs;
  end;
  BSTtoDLL(T,S);
  BSTdispose(T);
  L := S;
end;

var i : byte;
    head:PNode;
    k:TData;
BEGIN
  ListInit(head);
  repeat
     writeln('0. Wyjscie');
     writeln('1. Wstaw element na poczatek listy');
     writeln('2. Usun element listy');
     writeln('3. Posortuj elementy drzewem binarnym');
     writeln('4. Wypisz elementy  listy');
     readln(i);
     case i of
     0:
     begin
       while not ListIsEmpty(head) do
            ListDelete(head,head^.data);
     end;
     1:
     begin
       writeln('Podaj element jaki chcesz wstawic');
       readln(k);
       ListInsert(head,k);
     end;
     2:
     begin
       writeln('Podaj element jaki chcesz usunac');
       readln(k);
       ListDelete(head,k);
     end;
     3:
     begin
       BSTsort(head);
     end;
     4:
     begin
        ListPrint(head);    
     end
     else
        writeln('Brak operacji podaj inny numer');
     end;
  until i = 0;  
END.

इस कोड को कुछ सुधारों की आवश्यकता है
सबसे पहले हमें अतिरिक्त भंडारण को पुनरावृत्ति की जरूरतों तक सीमित करना चाहिए
फिर हमें पुनरावृत्ति को पुनरावृत्ति से बदलने की कोशिश करनी चाहिए
यदि हम एल्गोरिथ्म में सुधार करना चाहते हैं तो हमें स्वयं संतुलन वृक्ष का उपयोग करना चाहिए


आपके विस्तृत योगदान के लिए धन्यवाद, लेकिन यह कोडिंग साइट नहीं है। कोड की 200 लाइनें यह समझाने के लिए कुछ भी नहीं करती हैं कि लिंक किए गए सूचियों के लिए त्वरित सॉर्ट पर मर्ज सॉर्ट को क्यों पसंद किया जाता है।
डेविड रिचेर्बी

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

-2

क्विकसॉर्ट
शायद मैं क्विकसॉर्ट के लिए कदम दिखाऊंगा

यदि सूची में एक से अधिक नोड हैं

  1. धुरी चयन
  2. तीन उप-सूचियों में विभाजन सूची
    पहले sublist धुरी कुंजी से कम कुंजी के साथ नोड्स शामिल
    दूसरे sublist कुंजी धुरी कुंजी के बराबर के साथ नोड्स शामिल
    तीसरे sublist धुरी कुंजी से अधिक कुंजी के साथ नोड्स शामिल
  3. उप-कलाकारों के लिए पुनरावर्ती कॉल जिसमें नोड्स पिवट नोड के बराबर नहीं होते हैं
  4. एक क्रमबद्ध सूची में उपविजेताओं को क्रमबद्ध करें

विज्ञापन 1.
यदि हम पिवट का चयन तेजी से करना चाहते हैं तो विकल्प सीमित है।
हम हेड नोड या टेल नोड चुन सकते हैं।
हमारी सूची में नोड के लिए सूचक होना चाहिए, यदि हम चाहते हैं कि हमारी धुरी
जल्दी से सुलभ हो अन्यथा हमें नोड की खोज करनी होगी

विज्ञापन 2.
हम इस कदम के लिए कतार संचालन का उपयोग कर सकते हैं । हम
मूल लिंक से नोड को हटाते
हैं, इसकी कुंजी की तुलना धुरी की चाबी से करते हैं और नोड को सही सब लिस्ट में
डालते हैं, उपविजेता मौजूदा नोड से बनाए जाते हैं और
नए नोड्स के लिए मेमोरी आवंटित करने की कोई आवश्यकता नहीं है।

टेल नोड को इंगित करना उपयोगी होगा क्योंकि
इस सूचक की उपस्थिति के साथ कतार संचालन और संघनन तेजी से चलता है


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