リンクリストでクイックソートを使用しないのはなぜですか?


16

クイックソートアルゴリズムは、次のステップに分けることができます

  1. ピボットを識別します。

  2. ピボットに基づいてリンクリストをパーティション分割します。

  3. リンクリストを再帰的に2つの部分に分割します。

ここで、最後の要素を常にピボットとして選択すると、ピボット要素(最初のステップ)の識別に時間かかります。O(n)

ピボット要素を特定したら、そのデータを保存し、他のすべての要素と比較して、正しいパーティションポイントを特定します(2番目のステップ)。ピボットデータを保存するため、各比較には時間かかり、各スワップにはO1 時間かかります。したがって、n個の要素に対して合計でOn 時間かかります。O(1)O(1)O(n)n

したがって、再帰関係は次のとおりです。

On log n )です。これは、リンクリストを使用したマージソートと同じです。T(n)=2T(n/2)+nO(nlogn)

それでは、なぜリンクリストのクイックソートよりもマージソートが優先されるのでしょうか。


最初ではなく最後の要素をピボットとして選択する必要はありません
-TheCppZoo

回答:


19

Quicksortのメモリアクセスパターンはランダムであり、すぐに使用できる実装もインプレースであるため、セルが順序付けられた結果を達成するために多くのスワップを使用します。
マージソートは外部であると同時に、順序付けられた結果を返すために追加の配列が必要です。配列では、追加のスペースオーバーヘッドを意味します。リンクリストの場合、値を引き出してノードのマージを開始することができます。アクセスは、本質的に連続的です。

このため、クイックソートはリンクされたリストの自然な選択ではありませんが、マージソートは非常に有利です。

O(n2)

O(nlogn)


しかし、平均的な時間の複雑さは同じですよね?リンクリストのクイックソートとマージソートの使用。
ゼファー

10
@Zephyr、複雑な表記法は一定の要因を落とすことを覚えておく必要があります。はい、リンクリストのクイックソートとリンクリストのマージソートは同じ複雑度クラスですが、表示されていない定数によりマージソートが均一に高速になります。
マーク

@Zephyr基本的には、理論的結果と経験的結果の違いです。経験的に、クイックソートはより高速です。
フェリット

1
O(n2)

3
O(logn)

5

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

あなたは、マージソート、パーティションのソート、木ソートを書いて、その結果を比較することができ
ますが、いくつかの余分なスペースができた場合それは非常に簡単書き込み木ソートにある
ソートツリーのリンクされたリストの各ノードは、我々は、ソート単独リストをリンクされても、二つのポインタを持っている必要があります
リンクリストで私は挿入するとスワップするのではなく、削除することを好む
ホーアパーティションを二重にリンクされたリストに対してのみ行うことができます

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行のコードは、リンクリストのクイックソートよりもマージソートが優先される理由を説明するものではありません。
デビッドリチャービー

パーティションソートでは、ピボットの選択は最初または最後の要素に限定されます(テールノードへのポインターを保持する場合は最後)、そうでない場合はピボットの選択が遅くなりますHoareパーティションは二重リンクリストでのみ可能です我々は一定の係数を無視するが、一種のコメントのいくつかの文字にあり、ソートマージのツリーの中で最悪の事態を回避することが容易になる場合ツリーはクイックソートと同じcompexityを持っている
マリウシュ

-2

クイックソートクイックソートの
手順を示します

リストに複数のノードが含まれる場合

  1. ピボット選択
  2. リストを3つのサブリストに分割します。
    最初のサブリストにはピボットキーより小さいキーを持つノードが含まれます
    2番目のサブリストにはピボットキーに等しい
    キーを持つノードが含まれます3番目のサブリストにはピボットキーより大きいキーを持つノードが含まれます
  3. ピボットノードと等しくないノードを含むサブリストの再帰呼び出し
  4. ソート済みサブリストを1つのソート済みリストに連結します

広告1.
ピボットを高速に選択する場合、選択肢は限られ
ます。ヘッドノードまたはテールノードを選択できます。
ピボット
に高速でアクセスできるようにするには、リストにノードへのポイナーが必要です。

広告2.
私たちは、このステップのためのキュー操作を使用することができ
拳我々は、元のリンクリストからデキューノードが
正しいサブリストにピボットキーとエンキューノードとそのキーの比較
既存のノードから作成されたサブリストをし、する必要はありません
新しいノードのためのメモリを割り当てるには

キュー操作
と連結はこのポインターの存在により速く実行されるため、テールノードへのポインターが便利です。


これがどのように質問に答えているのか見当がつかなかったのではないでしょうか。
Apass.Jack
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.