ทำไมเราไม่ใช้การจัดเรียงอย่างรวดเร็วในรายการที่เชื่อมโยง?


16

อัลกอริทึมการเรียงลำดับด่วนสามารถแบ่งออกเป็นขั้นตอนต่อไปนี้

  1. ระบุเดือย

  2. แบ่งพาร์ติชันรายการที่เชื่อมโยงตาม pivot

  3. แบ่งรายการที่เชื่อมโยงแบบวนซ้ำเป็น 2 ส่วน

ตอนนี้ถ้าผมมักจะเลือกองค์ประกอบสุดท้ายเป็นเดือยแล้วระบุองค์ประกอบหมุน (ขั้นตอนที่ 1) ใช้เวลาเวลาO(n)

หลังจากระบุองค์ประกอบ pivot เราสามารถจัดเก็บข้อมูลและเปรียบเทียบกับองค์ประกอบอื่น ๆ ทั้งหมดเพื่อระบุจุดพาร์ติชันที่ถูกต้อง (ขั้นตอนที่ 2) เปรียบเทียบแต่ละคนจะใช้เวลาเวลาที่เราเก็บข้อมูลเดือยและแต่ละแลกเปลี่ยนใช้เวลาO ( 1 )เวลา ดังนั้นโดยรวมจะใช้เวลาO ( n )สำหรับองค์ประกอบnO(1)O(1)O(n)n

ดังนั้นความสัมพันธ์ที่เกิดซ้ำคือ:

ซึ่งเป็น O ( n log n )ซึ่งเหมือนกับในการเรียงแบบผสานกับรายการที่เชื่อมโยงT(n)=2T(n/2)+nO(nlogn)

เหตุใดจึงต้องจัดเรียงผสานมากกว่าการจัดเรียงด่วนสำหรับรายการที่เชื่อมโยง


ไม่จำเป็นต้องเลือกองค์ประกอบสุดท้ายเป็นเดือยแทนตัวแรก
TheCppZoo

คำตอบ:


19

รูปแบบการเข้าถึงหน่วยความจำใน Quicksort เป็นแบบสุ่มนอกจากนี้ยังมีการใช้งานนอกสถานที่ด้วยดังนั้นจึงใช้การสลับหลายครั้งหากเซลล์เพื่อให้ได้ผลลัพธ์ตามสั่ง
ในเวลาเดียวกันการจัดเรียงผสานเป็นภายนอกจะต้องใช้อาร์เรย์เพิ่มเติมเพื่อส่งกลับผลการสั่งซื้อ ในอาร์เรย์หมายถึงพื้นที่ว่างเพิ่มเติมในกรณีที่มีการเชื่อมโยงรายการคุณสามารถดึงค่าออกมาและเริ่มรวมโหนด การเข้าถึงเป็นลำดับต่อเนื่องมากขึ้นในธรรมชาติ

ด้วยเหตุนี้ quicksort จึงไม่ใช่ตัวเลือกที่เป็นธรรมชาติสำหรับรายการที่เชื่อมโยงในขณะที่การผสานการเรียงจะใช้ประโยชน์อย่างมาก

สัญกรณ์รถม้าสี่ล้ออาจ (มากหรือน้อยเพราะ Quicksort ยังคงเป็น ) เห็นด้วย แต่ค่าคงที่สูงกว่ามากO(n2)

ในกรณีเฉลี่ยอัลกอริธึมทั้งสองอยู่ในดังนั้นกรณี asymptotic เหมือนกัน แต่การตั้งค่านั้นเข้มงวดเนื่องจากค่าคงที่ซ่อนอยู่และบางครั้งความเสถียรคือปัญหา (quicksort ไม่เสถียรแน่นอนO(nlogn)


แต่ความซับซ้อนของเวลาโดยเฉลี่ยเหมือนกันใช่ไหม การใช้การเรียงลำดับแบบด่วนรวมถึงการเรียงลำดับแบบผสานสำหรับรายการที่เชื่อมโยง
Zephyr

10
@ Zephyr คุณต้องจำไว้ว่าสัญกรณ์ความซับซ้อนลดลงปัจจัยคงที่ ใช่การเรียงลำดับอย่างรวดเร็วในรายการที่เชื่อมโยงและการรวมในรายการที่เชื่อมโยงเป็นระดับความซับซ้อนเดียวกัน แต่ค่าคงที่เหล่านั้นที่คุณไม่เห็นจะทำให้การรวมกลุ่มเร็วขึ้นอย่างสม่ำเสมอ
ทำเครื่องหมาย

@ Zephyr โดยทั่วไปมันเป็นความแตกต่างของผลลัพธ์ทางทฤษฎีและเชิงประจักษ์ สังเกตุอย่างรวดเร็วจะเร็วกว่า
ferit

1
นอกจากนี้การเลือกเดือยที่ดีก็เป็นเรื่องยากสำหรับรายการที่เชื่อมโยง หากคุณใช้องค์ประกอบสุดท้ายเช่น OP แนะนำนั่นหมายความว่ากรณีที่เลวร้ายที่สุด ( ) เป็นรายการหรือรายการย่อยที่เรียงลำดับแล้ว และกรณีที่เลวร้ายที่สุดนั้นค่อนข้างน่าจะปรากฏในทางปฏิบัติ O(n2)
เฮมเมอร์

3
Quicksort ไม่เคยเกิดขึ้นนั่นเป็นความเข้าใจผิดที่พบบ่อย มันต้องการพื้นที่เพิ่มเติมยิ่งไปกว่านั้นรูปแบบการเข้าถึงหน่วยความจำแบบ "สุ่ม" ก็ไม่แม่นยำเช่นกันมันขึ้นอยู่กับการเลือกของเดือยอย่างมากดังที่อธิบายไว้ในคำตอบอื่น O(logn)
Konrad Rudolph

5

คุณสามารถเรียงลำดับรายการที่เชื่อมโยงอย่างรวดเร็ว แต่คุณจะถูก จำกัด อย่างมากในแง่ของการเลือกเดือยการ จำกัด ให้คุณหมุนไปทางด้านหน้าของรายการซึ่งไม่ดีสำหรับอินพุตที่เรียงเกือบจะยกเว้นว่าคุณต้องการวนซ้ำแต่ละเซ็กเมนต์ หนึ่งครั้งสำหรับพาร์ติชัน) และคุณจะต้องเก็บสแต็กของขอบเขตพาร์ติชันสำหรับรายการที่คุณยังต้องเรียงลำดับ สแต็คที่สามารถเติบโตได้ถึงเมื่อเลือกเดือยจะไม่ดีพร้อมกับความซับซ้อนเวลาเพิ่มขึ้นเพื่อO ( n 2 )O(n)O(n2)

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;

นี่เป็นอัลกอริทึมที่เคอร์เนล linux ใช้สำหรับเรียงลำดับรายการที่ลิงก์ แม้ว่าจะมีการเพิ่มประสิทธิภาพพิเศษบางอย่างเช่นการละเว้นpreviousตัวชี้ในระหว่างการดำเนินการผสานทั้งหมด แต่สุดท้าย


-2

คุณสามารถเขียน merge sort พาร์ทิชันเรียงลำดับการจัดเรียงต้นไม้และเปรียบเทียบผล
มันค่อนข้างง่ายที่จะเขียนต้นไม้เรียงลำดับถ้าคุณให้บางพื้นที่พิเศษ
สำหรับต้นไม้การจัดเรียงโหนดของรายการที่เชื่อมโยงแต่ละคนจะต้องมีสองตัวชี้แม้ว่าเราเรียงลำดับโดยลำพังเชื่อมโยงรายชื่อ
ในรายการที่เชื่อมโยง ฉันชอบที่จะแทรกและลบแทนการสลับ
พาร์ติชัน Hoare สามารถทำได้เฉพาะรายการที่ลิงก์ซ้ำกัน

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 บรรทัดไม่ได้ทำอะไรเพื่ออธิบายว่าทำไมการเรียงแบบผสานจึงเป็นที่ต้องการมากกว่าการเรียงแบบด่วนสำหรับรายการที่ลิงก์
David Richerby

ในพาร์ติชันการเรียงลำดับการเลือกเดือยถูก จำกัด ไว้ที่องค์ประกอบแรกหรือสุดท้าย (สุดท้ายถ้าเราเก็บตัวชี้ไปที่โหนดหาง) มิฉะนั้นการเลือกเดือยคือช้าพาร์ติชัน Hoare เป็นไปได้เฉพาะรายการที่เชื่อมโยงสองเท่าการแลกเปลี่ยนควรแทนที่ด้วยการแทรก ต้นไม้มี compexity เดียวกับ quicksort ถ้าเราไม่สนใจปัจจัยคงที่ แต่มันง่ายกว่าที่จะหลีกเลี่ยงกรณีที่แย่ที่สุดในการเรียงลำดับแบบต้นไม้สำหรับการเรียงแบบผสานนั้นมีตัวอักษรไม่กี่ตัวในคอม
เม้นท์

-2

Quicksort
บางทีฉันจะแสดงขั้นตอนสำหรับ quicksort

หากรายการมีมากกว่าหนึ่งโหนด

  1. การเลือก Pivot
  2. รายการพาร์ติชันออกเป็นสามรายการย่อย
    แรกรายการย่อยประกอบด้วยโหนดที่มีคีย์น้อยกว่า pivot key
    รายการย่อยที่สองประกอบด้วยโหนดที่มีคีย์เท่ากับคีย์ pivot
    รายการย่อยที่สามประกอบด้วยโหนดที่มีคีย์มากกว่ากุญแจสำคัญ
  3. เรียกซ้ำสำหรับรายการย่อยที่มีโหนดไม่เท่ากับโหนด pivot
  4. เชื่อมรายการย่อยที่เรียงลำดับไว้เป็นหนึ่งรายการ

โฆษณา 1.
ถ้าเราต้องการเลือกสาระสำคัญทางเลือกมี จำกัด
เราสามารถเลือกหัวโหนดหรือโหนดหาง
รายการของเราต้องมี poiner ต่อ node ถ้าเราต้องการให้ pivot ของเรา
เข้าถึงได้เร็วมิฉะนั้นเราต้องค้นหา node

โฆษณา 2
เราสามารถใช้การดำเนินงานคิวสำหรับขั้นตอนนี้
กำปั้นเรา dequeue โหนดจากรายการที่เชื่อมโยงเดิม
เปรียบเทียบคีย์กับ pivot key และ enqueue node กับรายการย่อยที่ถูกต้อง
Sublists ถูกสร้างขึ้นจากโหนดที่มีอยู่และไม่จำเป็นต้อง
จัดสรรหน่วยความจำสำหรับโหนดใหม่

ตัวชี้ไปยังโหนดท้ายจะมีประโยชน์เนื่องจากการดำเนินการคิว
และการต่อข้อมูลทำงานเร็วขึ้นโดยมีตัวชี้นี้อยู่


ฉันกลัวว่าฉันจะไม่เห็นว่าสิ่งนี้ตอบคำถามได้อย่างไร
John L.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.