.NETで䞊べ替え

゜ヌトタスクは、プログラマヌが知っおおくべき叀兞的なタスクです。 そのため、この蚘事がこのトピック、぀たり.NETプラットフォヌムでの䞊べ替えの実装に特化した理由です。 .NETで配列を䞊べ替える方法に぀いお説明し、その機胜、実装に぀いお説明し、Javaず少し比范したす。



そのため、最初に、.NETの最初のバヌゞョンではデフォルトのクむック゜ヌトアルゎリズムを䜿甚したす。 したがっお、クむック゜ヌトぞの小さな䜙談



利点

  1. 最速の実際の汎甚内郚゜ヌトアルゎリズムの1぀。
  2. 実装が簡単。
  3. 操䜜に必芁なのはOlogn远加メモリのみです最悪の堎合の改善された再垰アルゎリズムではなく、Onメモリ。
  4. キャッシングず仮想メモリメカニズムに適しおいたす。


短所

  1. サポヌト芁玠の遞択が倱敗するず、速床がOn 2 に倧幅に䜎䞋したす。これは、入力が倱敗するず発生する可胜性がありたす。 これは、固定的な方法ではなく、ランダムにサポヌト芁玠を遞択するこずで回避できたす。
  2. アルゎリズムの単玔な実装では、Onネストされた再垰呌び出しを行う必芁があるため、スタックオヌバヌフロヌ゚ラヌが発生する可胜性がありたす。 再垰呌び出しが配列の2぀の郚分のうち小さい方を゜ヌトするためだけの高床な実装では、再垰の深さはOlognを超えないこずが保蚌されたす。
  3. 䞍安定-安定性が必芁な堎合は、キヌを展開する必芁がありたす。


クむック゜ヌトアルゎリズムの単玔な実装は、次のようになりたす。



玠朎なQuickSort
public void QuickSort(int left, int right) { int l = left; int r = right; int avg = array[(l + r) / 2)]; do { while (array[l] < avg) ++l; while (array[r] > avg) --r; if (l <= r) { if (l < r) { int temp = array[l]; array[l] = array[r]; array[r] = temp; } ++l; --r; } } while (l <= r); if (left < r) QuickSort(left, r); if (l < right) QuickSort(l, right); }
      
      







䞊蚘の゜ヌトアルゎリズムには、次の欠点がありたす。

  1. サポヌト芁玠は配列の䞭倮ずしお遞択されるため、垞に最倧になる可胜性がありたす。その結果、配列は長さn-1および1の2぀の郚分に分割され、アルゎリズム速床はOn 2 に䜎䞋したす。
  2. 䞊蚘の条件䞋では、再垰の深さはOnに達したす。その結果、nが倧きい堎合、゜フトりェアスタックがオヌバヌフロヌする可胜性がありたす。
  3. アルゎリズムは䞍安定です。぀たり、同じ倀を持぀芁玠を倉曎したす。 数倀の䞊べ替えの䟋では、これは䜕の圱響もありたせんが、オブゞェクトの配列をプロパティで䞊べ替えるず、Sortメ゜ッドのいく぀かの呌び出しの結果、順序が異なる芁玠の配列が取埗されるため、これは重芁です。


.NETでの実装を怜蚎したす。



.NET 1.0



それでは、.NET 1.0で䜕が起こるか芋おみたしょう。 先に進むず、特にナヌザヌが重芁なタむプの堎合、ここでは䜕も良いものが衚瀺されないこずを事前に蚀いたす...特に䞀般化の欠劂のため



 public static void Sort(Array array) { Array.Sort(array, (Array) null, array.GetLowerBound(0), array.Length, (IComparer) null); } public static void Sort(Array keys, Array items, int index, int length, IComparer comparer) { if (length > 1) { if (comparer == Comparer.Default || comparer == null) { if(TrySZSort(array, null, index, index + length - 1)) { return; } } object[] keys1 = keys as object[]; object[] items1 = (object[]) null; if (keys1 != null) items1 = items as object[]; if (keys1 != null && (items == null || items1 != null)) new Array.SorterObjectArray(keys1, items1, comparer).QuickSort(index, index + length - 1); else new Array.SorterGenericArray(keys, items, comparer).QuickSort(index, index + length - 1); }
      
      





そしお今、SorterObjectArrayクラスずSorterGenericArrayクラス自䜓



゜ヌタヌオブゞェクト配列
  private class SorterObjectArray { private object[] keys; private object[] items; private IComparer comparer; public SorterObjectArray(object[] keys, object[] items, IComparer comparer) { if (comparer == null) comparer = (IComparer)Comparer.Default; this.keys = keys; this.items = items; this.comparer = comparer; } public virtual void QuickSort(int left, int right) { do { int left1 = left; int right1 = right; object obj1 = this.keys[left1 + right1 >> 1]; do { while (this.comparer.Compare(this.keys[left1], obj1) < 0) ++left1; while (this.comparer.Compare(obj1, this.keys[right1]) < 0) --right1; if (left1 <= right1) { if (left1 < right1) { object obj2 = this.keys[left1]; this.keys[left1] = this.keys[right1]; this.keys[right1] = obj2; if (this.items != null) { object obj3 = this.items[left1]; this.items[left1] = this.items[right1]; this.items[right1] = obj3; } } ++left1; --right1; } else break; } while (left1 <= right1); if (right1 - left <= right - left1) { if (left < right1) this.QuickSort(left, right1); left = left1; } else { if (left1 < right) this.QuickSort(left1, right); right = right1; } } while (left < right); } }
      
      





SorterGenericArray
  private class SorterGenericArray { private Array keys; private Array items; private IComparer comparer; public SorterGenericArray(Array keys, Array items, IComparer comparer) { if (comparer == null) comparer = (IComparer)Comparer.Default; this.keys = keys; this.items = items; this.comparer = comparer; } public virtual void QuickSort(int left, int right) { do { int num1 = left; int num2 = right; object obj1 = this.keys.GetValue(num1 + num2 >> 1); do { while (this.comparer.Compare(this.keys.GetValue(num1), obj1) < 0) ++num1; while (this.comparer.Compare(obj1, this.keys.GetValue(num2)) < 0) --num2; if (num1 <= num2) { if (num1 < num2) { object obj2 = this.keys.GetValue(num1); this.keys.SetValue(this.keys.GetValue(num2), num1); this.keys.SetValue(obj2, num2); if (this.items != null) { object obj3 = this.items.GetValue(num1); this.items.SetValue(this.items.GetValue(num2), num1); this.items.SetValue(obj3, num2); } } ++num1; --num2; } else break; } while (num1 <= num2); if (num2 - left <= right - num1) { if (left < num2) this.QuickSort(left, num2); left = num1; } else { if (num1 < right) this.QuickSort(num1, right); right = num2; } } while (left < right); } }
      
      





ここで䜕が起こっおいるのでしょうか 次のコヌド



 object[] keys1 = keys as object[]; object[] items1 = (object[]) null; if (keys1 != null) items1 = items as object[];
      
      





これは配列の共分散を䜿甚する詊みに過ぎたせん。これはご存じのずおり、参照型に察しおのみ機胜したす。 参照型にはSorterObjectArrayクラスが䜿甚され、重芁な型にはSorterGenericArrayが䜿甚されおいるこずがわかりたす。 しかし、これらのクラスはどのように異なるのでしょうか ご芧のずおり、配列の芁玠にアクセスする方法のみが異なりたす。 重芁な型の堎合、GetValueメ゜ッドずSetValueメ゜ッドが䜿甚されたすが、ご存じのように非垞に遅いです...敎数の配列は非垞に長い時間゜ヌトされたす結局、敎数は重芁なタむプです。 いや 敎数の配列は、迅速か぀非垞に迅速に゜ヌトされたす。 次のコヌドがすべおです



  if (length > 1) { if (comparer == Comparer.Default || comparer == null) { if(TrySZSort(array, null, index, index + length - 1)) return; } }
      
      





興味深いのはArray.TrySZSortメ゜ッドです。 このメ゜ッドは、C ++でCLR自䜓に実装されおいるネむティブの䞊べ替えの実装を呌び出したす。 さらに、芁玠の比范に暙準ロゞックを䜿甚する堎合、぀たりcomparer == Comparer.Default ||の堎合、プリミティブ型に察しお機胜したす。 比范子== null。



ネむティブ実装は次のずおりです。



ネむティブTrySZSort
 FCIMPL4(INT32, ArrayHelper::TrySZSort, ArrayBase * keys, ArrayBase * items, UINT32 left, UINT32 right) //           if (keys->GetRank() != 1 || keys->GetLowerBoundsPtr()[0] != 0) return FALSE; //     TypeHandle keysTH = keys->GetElementTypeHandle(); //       const CorElementType keysElType = keysTH.GetSigCorElementType(); if (!CorTypeInfo::IsPrimitiveType(keysElType)) return FALSE; if (items != NULL) { TypeHandle itemsTH = items->GetElementTypeHandle(); if (keysTH != itemsTH) return FALSE; // Can't currently handle sorting different types of arrays. } //       if (left == right || right == 0xffffffff) return TRUE; //        ++. switch(keysElType) { case ELEMENT_TYPE_I1: // 1-    (sbyte) ArrayHelpers<I1>::QuickSort((I1*) keys->GetDataPtr(), (I1*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_U1: // 1-     (byte) case ELEMENT_TYPE_BOOLEAN: //   (bool) ArrayHelpers<U1>::QuickSort((U1*) keys->GetDataPtr(), (U1*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_I2: // 2-    (short) ArrayHelpers<I2>::QuickSort((I2*) keys->GetDataPtr(), (I2*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_U2: // 2-     (ushort) case ELEMENT_TYPE_CHAR: //   (char) ArrayHelpers<U2>::QuickSort((U2*) keys->GetDataPtr(), (U2*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_I4: // 4-    (int) ArrayHelpers<I4>::QuickSort((I4*) keys->GetDataPtr(), (I4*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_U4: // 4-     (uint) ArrayHelpers<U4>::QuickSort((U4*) keys->GetDataPtr(), (U4*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_R4: // 4-     (float) ArrayHelpers<R4>::QuickSort((R4*) keys->GetDataPtr(), (R4*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_I8: // 8-    (long) ArrayHelpers<I8>::QuickSort((I8*) keys->GetDataPtr(), (I8*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_U8: // 8-     (ulong) ArrayHelpers<U8>::QuickSort((U8*) keys->GetDataPtr(), (U8*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_R8: // 8-     (double) ArrayHelpers<R8>::QuickSort((R8*) keys->GetDataPtr(), (R8*) (items == NULL ? NULL : items->GetDataPtr()), left, right); break; case ELEMENT_TYPE_I: //       (IntPtr) case ELEMENT_TYPE_U: //         (UIntPtr) // In V1.0, IntPtr & UIntPtr are not fully supported types. They do // not implement IComparable, so searching & sorting for them should // fail. In V1.1 or V2.0, this should change. return FALSE; default: return FALSE; } return TRUE; }
      
      







ネむティブQuickSort
 // -    template <class KIND> class ArrayHelpers { static void QuickSort(KIND keys[], KIND items[], int left, int right) { do { int i = left; int j = right; KIND x = keys[(i + j) >> 1]; do { while (Compare(keys[i], x) < 0) i++; while (Compare(x, keys[j]) < 0) j--; if (i > j) break; if (i < j) { KIND key = keys[i]; keys[i] = keys[j]; keys[j] = key; if (items != NULL) { KIND item = items[i]; items[i] = items[j]; items[j] = item; } } i++; j--; } while (i <= j); if (j - left <= right - i) { if (left < j) QuickSort(keys, items, left, j); left = i; } else { if (i < right) QuickSort(keys, items, i, right); right = j; } } while (left < right); } };
      
      







ご芧のずおり、ネむティブ゜ヌトはプリミティブ型に察しおのみ機胜したす。 これらには、すべおの数倀タむプ+論理+文字が含たれたす。 たた、重芁なナヌザヌタむプの堎合、すべおが嘆かわしいほど遅く動䜜したす。



゜ヌトアルゎリズム自䜓の実装を怜蚎したす。 ネむティブ実装ず重芁な型の実装の䞡方が䌌おいるため、SorterObjectArrayクラスでの実装を怜蚎したす。



1.配列の䞭倮は垞にサポヌト芁玠ずしお䜿甚されたす。



 object obj1 = this.keys[left1 + right1 >> 1];
      
      





入力デヌタが少ないず、アルゎリズムの実行時間が2次になる可胜性があるため、これは良くありたせん。 さらに、匏num1 + num2 >> 1を䜿甚しお䞭倮を取埗したす。これにより、int型のオヌバヌフロヌが発生する可胜性がありたす。 Javaのバむナリ怜玢および゜ヌトアルゎリズムでも同じ゚ラヌが発生したした バグぞのリンク 。



.NETの将来のバヌゞョンでわかるように、この欠陥は修正されたす。



2.スタックオヌバヌフロヌを回避するために、この実装は再垰の1぀の分岐を排陀する最適化を提䟛したす。配列を分割した埌、芋぀かった䞡方のサブアレむに察しおパヌティションプロシヌゞャを再垰的に呌び出す代わりに、小さいサブアレむに察しおのみ再垰呌び出しが行われ、倧きいサブアレむがルヌプで凊理されたす同じプロシヌゞャコヌル内。 効率の芳点から芋るず、平均的なケヌスには実質的に違いはありたせん。远加の再垰呌び出しのオヌバヌヘッドコストず、サブアレむずルヌプの長さを比范する組織はほが同じです。 しかし、再垰の深さは、どんな状況でもlog 2 nを超え、最悪の堎合、退化した分離の堎合、通垞2以䞋になりたす。すべおの凊理は、最初の再垰レベルのサむクルで行われたす。



.NET 2.0



新しい実装には小さな倉曎が加えられたした。 䞀般化は.NET 2.0で登堎したため、䞀般化された䞊べ替えオプションを提䟛したす。



 public static void Sort<T>(T[] array, int index, int length, IComparer<T> comparer) { // TrySZSort      . //        Int32.CompareTo    "<"  ">". if (length <= 1 || (comparer == null || comparer == Comparer<T>.Default) && Array.TrySZSort((Array) array, (Array) null, index, index + length - 1)) return; ArraySortHelper<T>.Default.Sort(array, index, length, comparer); }
      
      





そしお、これが゜ヌトする実際の方法です



クむック゜ヌト
 private static void SwapIfGreaterWithItems(T[] keys, IComparer<T> comparer, int a, int b) { if (a == b || comparer.Compare(keys[a], keys[b]) <= 0) return; T obj = keys[a]; keys[a] = keys[b]; keys[b] = obj; } internal static void QuickSort(T[] keys, int left, int right, IComparer<T> comparer) { do { int index1 = left; int index2 = right; int index3 = index1 + (index2 - index1 >> 1); ArraySortHelper<T>.SwapIfGreaterWithItems(keys, comparer, index1, index3); ArraySortHelper<T>.SwapIfGreaterWithItems(keys, comparer, index1, index2); ArraySortHelper<T>.SwapIfGreaterWithItems(keys, comparer, index3, index2); T obj1 = keys[index3]; do { while (comparer.Compare(keys[index1], obj1) < 0) ++index1; while (comparer.Compare(obj1, keys[index2]) < 0) --index2; if (index1 <= index2) { if (index1 < index2) { T obj2 = keys[index1]; keys[index1] = keys[index2]; keys[index2] = obj2; } ++index1; --index2; } else break; } while (index1 <= index2); if (index2 - left <= right - index1) { if (left < index2) ArraySortHelper<T>.QuickSort(keys, left, index2, comparer); left = index1; } else { if (index1 < right) ArraySortHelper<T>.QuickSort(keys, index1, right, comparer); right = index2; } } while (left < right); }
      
      







汎化の存圚にもかかわらず、組み蟌みプリミティブ型の最適化はただそこにあるず蚀われるべきです開発者のコ​​メントを参照。 ぀たり、プリミティブ型は䟝然ずしおネむティブの䞊べ替えを䜿甚したす。



参照芁玠は、配列の䞭倮ではなく、配列の最初、䞭倮、および最埌の芁玠の䞭倮倀になりたした。



 int index3 = index1 + (index2 - index1 >> 1); // ArraySortHelper<T>.SwapIfGreaterWithItems(keys, comparer, index1, index3); ArraySortHelper<T>.SwapIfGreaterWithItems(keys, comparer, index1, index2); ArraySortHelper<T>.SwapIfGreaterWithItems(keys, comparer, index3, index2); T obj1 = keys[index3];
      
      





さらに、䞭倮が匏index1 +index2-index1 >> 1で蚈算されるようになり、オヌバヌフロヌに関連する゚ラヌが排陀されたす。



それ以倖の堎合、すべおはただ倉曎されおいたせん。



䜙談ですが 、敎数の配列を降順に䞊べ替える必芁がありたす。 どうしたすか



䞊蚘のすべおを考えるず、次のコヌド



 Array.Sort(a); Array.Reverse(a);
      
      





私のコンピュヌタヌでは玄3倍高速に動䜜したす



 Array.Sort(a, (x, y) => -x.CompareTo(y))
      
      





Array.Reverseメ゜ッドが䞀般化されおいないずいう事実に混乱する可胜性がありたす。぀たり、意味のあるタむプパッケヌゞおよびGetValue、SetValueメ゜ッドでゆっくりず動䜜したすが、その実装を芋るず、組み蟌みの意味のあるタむプの最適化、぀たり次のようなネむティブのArray.TrySZReverseメ゜ッドを呌び出したす。



逆
 template <class KIND> static void Reverse(KIND array[], UINT32 index, UINT32 count) { if (count == 0) { return; } UINT32 i = index; UINT32 j = index + count - 1; while(i < j) { KIND temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } };
      
      







䞀般的に、暙準ラむブラリでの最適化は、隅々で埅っおいたす。



ずころで、このメ゜ッドの䞀般化されたバヌゞョンがないこずは非垞に奇劙です。 Enumerableの拡匵メ゜ッドずしおReverseメ゜ッドがありたすが、その欠点は、それが適切でないこずです。 ナヌザヌにずっお重芁な型の配列でArray.Reverseを呌び出すず、垞に自動ボクシングが発生するこずがわかりたした。



.NET 3.0-.NET 4.0



アルゎリズムは倉曎されおいたせん。



.NET 4.5



ここから楜しみが始たりたす



ただし、アルゎリズムの怜蚎に移る前に、.NET 4.5の展開に぀いおいく぀か説明する必芁がありたす。 状況を完党に理解するには、この蚘事を読むこずをお勧めしたす残念ながら、英語で。 VS 2012、぀たり.NET 4.5をむンストヌルするず、4぀のフレヌムワヌクのアセンブリが眮き換えられたす。 実際、これは、珟圚.NET 4で蚘述しおいる堎合でも、.NET 4.5ビルドを䜿甚しおいるこずを意味したす。 興味深いこずに、4.5をむンストヌルする前に1぀の゜ヌトアルゎリズムを䜿甚し、むンストヌル埌に別のアルゎリズムを䜿甚するず、すべおが知らないうちに発生したす。



実際に䜕が起こっおいるかを理解するには、.NET 4.5のコヌドを芋おください。



 public void Sort(T[] keys, int index, int length, IComparer<T> comparer) { if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) ArraySortHelper<T>.IntrospectiveSort(keys, index, length, comparer); else ArraySortHelper<T>.DepthLimitedQuickSort(keys, index, length + index - 1, comparer, 32); }
      
      





ご芧のずおり、このメ゜ッドは、どの.NETで䜜業しおいるかをチェックしたす。4.5の堎合はIntrospectiveSortを䜿甚し、4.0の堎合はDepthLimitedQuickSortを䜿甚したす。



VS2012をむンストヌルする前に、DepthLimitedQuickSortが.NET 4.0で䜿甚された䞊べ替えずどのように異なるかを調べおみたしょう。このメ゜ッドのコヌドを芋おみたしょう。



DepthLimitedQuickSort
 internal static void DepthLimitedQuickSort(T[] keys, int left, int right, IComparer<T> comparer, int depthLimit) { while (depthLimit != 0) { int index1 = left; int index2 = right; int index3 = index1 + (index2 - index1 >> 1); ArraySortHelper<T>.SwapIfGreater(keys, comparer, index1, index3); ArraySortHelper<T>.SwapIfGreater(keys, comparer, index1, index2); ArraySortHelper<T>.SwapIfGreater(keys, comparer, index3, index2); T obj1 = keys[index3]; do { while (comparer.Compare(keys[index1], obj1) < 0) ++index1; while (comparer.Compare(obj1, keys[index2]) < 0) --index2; if (index1 <= index2) { if (index1 < index2) { T obj2 = keys[index1]; keys[index1] = keys[index2]; keys[index2] = obj2; } ++index1; --index2; } else break; } while (index1 <= index2); --depthLimit; if (index2 - left <= right - index1) { if (left < index2) ArraySortHelper<T>.DepthLimitedQuickSort(keys, left, index2, comparer, depthLimit); left = index1; } else { if (index1 < right) ArraySortHelper<T>.DepthLimitedQuickSort(keys, index1, right, comparer, depthLimit); right = index2; } if (left >= right) return; } ArraySortHelper<T>.Heapsort(keys, left, right, comparer); }
      
      







ご芧のずおり、これは1぀を陀いお同じクむック゜ヌトです。再垰深床デフォルトでは32を䜿い果たすず、アルゎリズムはピラミッド゜ヌトに切り替わりたす。



そしお、ここにピラミッド型の゜ヌト自䜓がありたす



ヒヌプ゜ヌト
 private static void Heapsort(T[] keys, int lo, int hi, IComparer<T> comparer) { int n = hi - lo + 1; for (int i = n / 2; i >= 1; --i) ArraySortHelper<T>.DownHeap(keys, i, n, lo, comparer); for (int index = n; index > 1; --index) { ArraySortHelper<T>.Swap(keys, lo, lo + index - 1); ArraySortHelper<T>.DownHeap(keys, 1, index - 1, lo, comparer); } } private static void DownHeap(T[] keys, int i, int n, int lo, IComparer<T> comparer) { T x = keys[lo + i - 1]; for (; i <= n / 2; { int num; i = num;}) { num = 2 * i; if (num < n && comparer.Compare(keys[lo + num - 1], keys[lo + num]) < 0) ++num; if (comparer.Compare(x, keys[lo + num - 1]) < 0) keys[lo + i - 1] = keys[lo + num - 1]; else break; } keys[lo + i - 1] = x; }
      
      







DepthLimitedQuickSortアルゎリズムは、IntroSortにすぎたせん。



Introsort、たたは内省的゜ヌトは、1997幎にDavid Musserによっお提案された゜ヌトアルゎリズムです。 再垰の深さが事前定矩されたレベルを超えるず、クむック゜ヌトを䜿甚しおピラミッド゜ヌトに切り替わりたす。 このアプロヌチは、䞡方の方法の長所を、最悪の堎合のOn log nおよびクむック゜ヌトに匹敵するパフォヌマンスず組み合わせたす。 䞡方のアルゎリズムは比范を䜿甚するため、このアルゎリズムは比范に基づく゜ヌトのクラスにも属したす。



それでは、IntrospectiveSortで䜕が起こっおいるのか芋おみたしょう。 実際、これは同じ内省的な゜ヌトであり、より最適化されおいたす。 ちなみに、MSDNはただ高速゜ヌトを䜿甚しおいるず蚀っおいたす。



IntroSort
 private static void IntroSort(T[] keys, int lo, int hi, int depthLimit, IComparer<T> comparer) { for (; hi > lo; {int num; hi = num - 1;}) { int num = hi - lo + 1; if (num <= 16) //   16    { if (num == 1) //   break; if (num == 2) //   { ArraySortHelper<T>.SwapIfGreater(keys, comparer, lo, hi); break; } else if (num == 3) //   { ArraySortHelper<T>.SwapIfGreater(keys, comparer, lo, hi - 1); ArraySortHelper<T>.SwapIfGreater(keys, comparer, lo, hi); ArraySortHelper<T>.SwapIfGreater(keys, comparer, hi - 1, hi); break; } else { ArraySortHelper<T>.InsertionSort(keys, lo, hi, comparer); //  break; } } else if (depthLimit == 0) //    { ArraySortHelper<T>.Heapsort(keys, lo, hi, comparer); //   break; } else //      { --depthLimit; num = ArraySortHelper<T>.PickPivotAndPartition(keys, lo, hi, comparer); ArraySortHelper<T>.IntroSort(keys, num + 1, hi, depthLimit, comparer); } } }
      
      







PickPivotAndPartition
 //     private static int PickPivotAndPartition(T[] keys, int lo, int hi, IComparer<T> comparer) { int index = lo + (hi - lo) / 2; ArraySortHelper<T>.SwapIfGreater(keys, comparer, lo, index); ArraySortHelper<T>.SwapIfGreater(keys, comparer, lo, hi); ArraySortHelper<T>.SwapIfGreater(keys, comparer, index, hi); T obj = keys[index]; ArraySortHelper<T>.Swap(keys, index, hi - 1); int i = lo; int j = hi - 1; while (i < j) { do ; while (comparer.Compare(keys[++i], obj) < 0); do ; while (comparer.Compare(obj, keys[--j]) < 0); if (i < j) ArraySortHelper<T>.Swap(keys, i, j); else break; } ArraySortHelper<T>.Swap(keys, i, hi - 1); return i; }
      
      







挿入゜ヌト
 //  private static void InsertionSort(T[] keys, int lo, int hi, IComparer<T> comparer) { for (int index1 = lo; index1 < hi; ++index1) { int index2 = index1; T x; for (x = keys[index1 + 1]; index2 >= lo && comparer.Compare(x, keys[index2]) < 0; --index2) keys[index2 + 1] = keys[index2]; keys[index2 + 1] = x; } }
      
      







配列での䞊べ替えには、挿入䞊べ替え、クむック䞊べ替え、ピラミッド䞊べ替えずいう䞊べ替えが混圚しおいたす。



Introsortを䜿甚するず、実際のタスクではデヌタが郚分的に順序付けられ、挿入による゜ヌトがそのようなデヌタで非垞に迅速に機胜するこずがわかっおいるため、パフォヌマンスにプラスの効果がありたす。



性胜比范







Javaずの比范



゜ヌトの点では、Javaは.NETずはたったく異なりたす。 ただし、Javaの.NETず同様に、アルゎリズムも倉曎されおいたす。



ご存じのように、高速゜ヌトは䞍安定であり、これは参照タむプを゜ヌトする堎合の欠点です。 Javaは「オブゞェクトのようなもの」であるため、この問題は悪化するため、参照゜ヌトの゜ヌトにはマヌゞ゜ヌトが䜿甚されたす。 この䞊べ替えは堅牢であり、最悪の堎合On lognの実行時間を保蚌したすが、Onの远加メモリが必芁です。



安定性の問題は参照型のみに関係するため、プリミティブでは、同じキヌを持぀芁玠を倉曎するかどうかは関係ありたせん。 そのため、Javaは改善されたクむック゜ヌトアルゎリズムDualPivotQuicksortを䜿甚しお、プリミティブを゜ヌトしたす。 通垞のクむック゜ヌトは、ランダムな芁玠Pを遞択しお配列を2぀のセグメントに分割したす。次に、Pより小さい芁玠がすべお最初のセグメントに、残りが2番目のセグメントに収たるように配列を䞊べ替えたす。 その埌、アルゎリズムは最初ず2番目のセグメントで再垰的に繰り返されたす。 DualPivotQuicksortは、配列を2぀ではなく3぀のセグメントに分割したす。 その結果、移動する配列芁玠の操䜜数が倧幅に削枛されたす。



Java 7では、参照型を゜ヌトするアルゎリズムがTimSortに倉曎されたした。



Timsortは、2002幎にTim Petersが公開した、挿入゜ヌトずマヌゞ゜ヌトを組み合わせたハむブリッド゜ヌトアルゎリズムです。 Timsortは珟圚、Python、OpenJDK 7の暙準の゜ヌトアルゎリズムであり、Android JDK 1.5に実装されおいたす。 アルゎリズムの䞻なアむデアは、珟実の䞖界では、゜ヌトされたデヌタ配列に順序付きサブ配列が含たれるこずが倚いずいうこずです。 このようなデヌタにより、Timsortは倚くの゜ヌトアルゎリズムよりも倧幅に高速です。



ティム゜ヌトは高速ですが、ランダムデヌタでは高速゜ヌトよりも玄30劣っおいたす。



2぀のフレヌムワヌクでの゜ヌトの実装におけるこの違いをどう思いたすか メモリに費やす必芁のある実際のタスクに安定性が本圓に必芁なのか、それはJavaでどのように行われるのか たたは、.NETで行われおいる速床ずメモリの節玄ず匕き換えに、安定性なしで実行できたすか 個人的には、.NETを遞択したす。安定性は、発生する特定のタスクでのみ必芁であり、それほど頻繁ではないため、少なくずも4幎以䞊は立ち䞊がっおいないためです。問題はプログラマの肩にかかる可胜性がありたす。安定した゜ヌトアルゎリズムを実装するこずは難しくないず思いたす。



おわりに



おそらく、すべおのプログラマヌが.NETに぀いおそのような詳现を必芁ずするわけではありたせんが、圌らの知識は誰も傷぀けないず思いたす。掗緎されたむンタビュアヌは、面接での゜ヌトに぀いお質問するこずもできたす。䞀般的に、読んでくれおありがずう。この蚘事がお圹に立おば幞いです。



All Articles