10の古典的なソートアルゴリズム

10の古典的なソートアルゴリズム

[[432257]]

ソースコードはこちら GitHub: https://github.com/nateshao/leetcode

頑固なアルゴリズムシリーズ

面接では、基本的に最初にアルゴリズムについて質問します。特に大企業では、アルゴリズムに対する要件が非常に高くなります。

プログラマーの場合、アルゴリズムが得意な人は特に早くコードを書くことができます(私たちのクラスにもそういう生徒が何人かいます)。そして、問題に遭遇したとき、彼は多くの解決策を考えることができます。

もちろん、アルゴリズムを学ぶことは比較的退屈なことです。しかし、アルゴリズムの考え方を理解すると、アルゴリズムは本当に魔法のようなもので、アルゴリズムから導き出された考え方は非常に重要であることがわかります。そのため、Qianyuはアルゴリズムに関する一連の記事を引き続き研究し、皆さんと一緒に学び、向上していきます。この記事では主に、上位 10 個の古典的なソート アルゴリズムについて説明します。さあ、始めましょう!

時間計算量の理解

定数時間演算: 演算がデータ量に関係せず、毎回一定時間内に完了する場合、定数演算と呼ばれます。時間の複雑さは、アルゴリズム プロセスにおける定数操作の数を示す指標です。多くの場合、O (ビッグオーと発音) で表現されます。具体的には、演算数が定数の式では、高次の項のみが必要で、低次の項や高次の項の係数は必要ありません。残りの部分を f(N) として記録すると、時間計算量は O(f(N)) になります。

アルゴリズム プロセスの品質を評価するには、まず時間複雑度インジケーターを確認し、次にさまざまなデータ サンプルでの実際の実行時間、つまり定数時間を分析します。

古典的なソートアルゴリズムのトップ10

アルゴリズム時間計算量(平均) 時間計算量(最悪の場合) 時間計算量(最適) 空間の複雑さ安定性
バブルソート (n2) (n2) の上) オー(1) 安定させる
選択ソート (n2) (n2) (n2) オー(1) 不安定
挿入ソート (n2) (n2) の上) オー(1) 安定させる
シェルソート O(n1.3) (n2) の上) オー(1) 不安定
クイックソート O(nlog2n) (n2) O(nlog2n) O(nlog2n) 不安定
マージソート O(nlog2n) O(nlog2n) O(nlog2n) の上) 安定させる
カウントソート O(n+k) O(n+k) O(n+k) O(n+k) 安定させる
基数ソート O(n*k) O(n*k) O(n*k) O(n+k) 安定させる
バケットソート (n2) O(n*k) の上) O(n+k) 安定させる
ヒープソート O(nlog2n) O(nlog2n) O(nlog2n) オー(1) 安定させる

ソートの定義

キーに従ってオブジェクトのシーケンスをソートします。

用語

  • 安定: a が元々 b の前にあり、a=b の場合、ソート後も a は b の前にあります。
  • 不安定: a が元々 b の前にあり、a=b の場合、ソート後に a が b の後に表示されることがあります。
  • 内部ソート: すべてのソート操作はメモリ内で実行されます。
  • 外部ソート: データが大きすぎるため、データはディスク上に配置され、ディスクとメモリ間のデータ転送を通じてソートが実行されます。
  • 時間の複雑さ: アルゴリズムの実行にかかる時間。
  • 空間計算量: プログラムを実行するために必要なメモリの量。

1. バブルソート

時間計算量、追加の空間計算量

バブルソートは、バブルソートとも呼ばれ、単純なソートアルゴリズムです。ソートする配列を繰り返し処理し、一度に 2 つの要素を比較して、順序が間違っている場合はそれらを交換します。シーケンスを訪問する作業は、交換が不要になるまで、つまりシーケンスがソートされるまで繰り返されます。このアルゴリズムの名前は、小さな要素が交換を通じてゆっくりとシーケンスの先頭に「浮かんで」いくという事実に由来しています。

アイデア:

隣接する要素を比較します。最初のものが 2 番目のものより大きい場合は、それらを交換します。

隣接する要素の各ペアに対して同じ操作を実行します。最初は最初のペアから始めて、最後は最後のペアで終了します。このステップが完了すると、最後の要素が最大の数値になります。

最後の要素を除くすべての要素に対して上記の手順を繰り返します。

コード実装:

  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. /**
  4. * @日付Shao Tongjieによって2021/10/29 16:52作成
  5. * @WeChatパブリックアカウントプログラマーQianyu
  6. * 個人ウェブサイト www.nateshao.cn
  7. * @ブログ https://nateshao.gitee.io
  8. * @GitHub https://github.com/nateshao
  9. * @Gitee https://gitee.com/nateshao
  10. * 説明: 元のリンク https://zfhelo.gitee.io/2020/06/14/1/
  11. */
  12. パブリッククラス Code_01_BubbleSortTest {
  13. 公共 静的void main(String[] args) {
  14. int [] arr = {3、44、38、5、47、15、36、26、27、2、46、4、19、50、48};
  15. バブルソート(arr);
  16. System.out.println (Arrays.toString(arr)) ;
  17. }
  18. 公共 静的voidバブルソート( int []arr){
  19. (arr == null && arr.length < 2) の場合 {
  20. 戻る;
  21. }
  22. ブール値 isSwap;
  23. // 外側のループは比較ラウンドの回数を制御します
  24. ( int i = 0; i < arr.length - 1; i++) {
  25. isSwap = false ;
  26. // 内側のループは隣接する要素を比較します
  27. ( int j = 0; j < arr.length - 1 - i; j++) {
  28. if (arr[j] > arr[j + 1]) { // 前の数字が後ろの数字より大きい場合は、入れ替える
  29. スワップ(arr, j, j + 1);
  30. スワップ = true ;
  31. }
  32. }
  33.  
  34. スワップの場合
  35. return ; // コードの最適化。ループ内で要素が入れ替わらない場合は、配列がすでに整列していることを意味します。
  36. }
  37. }
  38. }
  39.  
  40. /**
  41. * 3番目の数字を使わずに2つの数字を入れ替える
  42. *
  43. * @param arr
  44. * @パラメータ i
  45. * @param j
  46. */
  47. 公共 静的void swap( int [] arr, int i, int j) {
  48. arr[i] = arr[i] ^ arr[j];
  49. arr[j] = arr[i] ^ arr[j];
  50. arr[i] = arr[i] ^ arr[j];
  51. }
  52.  
  53. //公共 静的void swap( int [] arr, int i, int j) {
  54. //整数  temp = arr[i];
  55. // arr[i] = arr[j];
  56. // arr[j] = temp ;
  57. // }
  58.  
  59. //公共 静的void swap( int [] arr, int a, int b) {
  60. // arr[a] = arr[a] + arr[b];
  61. // arr[b] = arr[a] - arr[b];
  62. // arr[a] = arr[a] - arr[b];
  63. // }
  64. }

2. 挿入ソート

挿入ソートの動作原理は、順序付けられたシーケンスを構築することです。ソートされていないデータの場合は、ソートされたシーケンスを後ろから前へスキャンし、対応する位置を見つけて挿入します。挿入ソートは通常、インプレースソート(つまり、O(1)の追加スペースのみを必要とするソート)によって実装されます。したがって、後ろから前へスキャンするプロセスでは、最新の要素を挿入するためのスペースを確保するために、ソートされた要素を繰り返し後方に移動する必要があります。

アイデア:

  • 最初の要素から始めて、要素はソートされているとみなすことができます。
  • 次の要素を取り出し、ソートされた要素の順序で後ろから前へスキャンします。
  • 要素(すでにソートされている)が新しい要素より大きい場合は、要素を次の位置に移動します。
  • ソートされた要素が新しい要素以下になる位置が見つかるまで、手順 3 を繰り返します。
  • その位置に新しい要素を挿入した後;
  • 手順2~5を繰り返します。

コード実装:

  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. /**
  4. * @日付Shao Tongjieによって2021/10/29 21:56作成
  5. * @WeChatパブリックアカウントプログラマーQianyu
  6. * 個人ウェブサイト www.nateshao.cn
  7. * @ブログ https://nateshao.gitee.io
  8. * @GitHub https://github.com/nateshao
  9. * @Gitee https://gitee.com/nateshao
  10. * 説明:
  11. */
  12. パブリッククラス Code_02_InsertionSortTest {
  13. 公共 静的void main(String[] args) {
  14. int [] arr = {3、44、38、5、47、15、36、26、27、2、46、4、19、50、48};
  15. 挿入ソート(arr);
  16. ( int i = 0; i < arr.length; i++) {
  17. システム.out.print (arr[i] + " " );
  18. }
  19. }
  20.  
  21. 公共 静的void挿入ソート( int []arr){
  22. (arr == null || arr.length < 2) の場合 {
  23. 戻る;
  24. }
  25. ( int i = 1; i < arr.length; i++) {
  26. ( int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j -- ) {  
  27. スワップ(arr, j, j + 1);
  28. }
  29. }
  30. }
  31.  
  32. 公共 静的void swap( int [] arr, int i, int j) {
  33. 整数  temp = arr[i];
  34. arr[i] = arr[j];
  35. arr[j] =一時;
  36. }
  37. }

3. シェルソート

シェルソートは挿入ソートの改良版です。

挿入ソートは、基本的にすでに順序付けられている配列をソートするのに効率的ですが、要素を移動する場合、交換できるのは隣接する 2 つの要素のみです。シェル ソートは、交換される要素が隣接する要素に限定されないようにギャップを追加します。間隔は徐々に狭くなり、1 に減少すると、配列は基本的に順序付けられます。

大きな間隔の順序は間隔を縮小しても崩れない

コード実装:

  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. java.util.Arrays をインポートします。
  4.  
  5. /**
  6. * @日付Shao Tongjieによって2021/10/30 10:44作成
  7. * @WeChatパブリックアカウントプログラマーQianyu
  8. * 個人ウェブサイト www.nateshao.cn
  9. * @ブログ https://nateshao.gitee.io
  10. * @GitHub https://github.com/nateshao
  11. * @Gitee https://gitee.com/nateshao
  12. * 説明: シェルソート
  13. */
  14. パブリッククラス Code_04_ShellSort {
  15. 公共 静的void main(String[] args) {
  16. int [] arr = {3、44、38、5、47、15、36、26、27、2、46、4、19、50、48};
  17. シェルソート(arr);
  18. System.out.println (Arrays.toString(arr)) ;
  19. シェルソート1(arr);
  20. System.out.println (Arrays.toString(arr)) ;
  21. }
  22.  
  23. /**
  24. * シェルソートは順序付けられたシーケンスに挿入するときに交換メソッドを使用します
  25. *
  26. * @param arr
  27. */
  28. 公共 静的voidシェルソート( int []arr){
  29. (arr == null && arr.length < 2) の場合 {
  30. 戻る;
  31. }
  32. //ギャップを増やし、徐々に増分を減らす
  33. ( intギャップ = arr.length / 2; ギャップ > 0; ギャップ /= 2) {
  34. //ギャップ要素から始めて、それが属するグループを1つずつ直接挿入して並べ替えます
  35. ( int i = ギャップ; i < arr.length; i++) {
  36. 整数j = i;
  37. (j - ギャップ >= 0 && arr[j] < arr[j - ギャップ]) {
  38. // 挿入ソートは交換法を使用する
  39. swap(arr, j, j - ギャップ);
  40. j -= ギャップ;
  41. }
  42. }
  43. }
  44. }
  45.  
  46. /**
  47. * シェルソートでは、順序付けられたシーケンスに挿入するときに移動メソッドが使用されます。
  48. *
  49. * @param arr
  50. */
  51. 公共 静的void shellSort1( int [] arr) {
  52. (arr == null && arr.length < 2) の場合 {
  53. 戻る;
  54. }
  55. //ギャップを増やし、徐々に増分を減らす
  56. ( intギャップ = arr.length / 2; ギャップ > 0; ギャップ /= 2) {
  57. //ギャップ要素から始めて、それが属するグループを1つずつ直接挿入して並べ替えます
  58. ( int i = ギャップ; i < arr.length; i++) {
  59. 整数j = i;
  60. 整数  temp = arr[j];
  61. (arr[j] < arr[j - ギャップ])の場合{
  62. (j - ギャップ >= 0 && temp < arr[j - ギャップ]) {
  63. //メソッドを移動
  64. arr[j] = arr[j - ギャップ];
  65. j -= ギャップ;
  66. }
  67. arr[j] =一時;
  68. }
  69. }
  70. }
  71. }
  72.  
  73. 公共 静的void swap( int [] arr, int a, int b) {
  74. arr[a] = arr[a] + arr[b];
  75. arr[b] = arr[a] - arr[b];
  76. arr[a] = arr[a] - arr[b];
  77.  
  78. }
  79. }

4. 選択ソート

時間計算量、追加の空間計算量

まず、ソートされていないシーケンス内の最小 (最大) の要素を見つけて、ソートされたシーケンスの開始位置に格納します。次に、残りのソートされていない要素から最小 (最大) の要素を探し続け、ソートされたシーケンスの最後に配置します。すべての要素がソートされるまでこれを繰り返します。

アイデア:

配列内の最小(最大)の数値を見つける

現在走査中の配列の最初の要素と最小の数値を交換する

最初の2つの手順を繰り返して、トラバーサル配列の範囲を狭めます。

コード実装:

  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. /**
  4. * @日付Shao Tongjieによって2021/10/29 22:51作成
  5. * @WeChatパブリックアカウントプログラマーQianyu
  6. * 個人ウェブサイト www.nateshao.cn
  7. * @ブログ https://nateshao.gitee.io
  8. * @GitHub https://github.com/nateshao
  9. * @Gitee https://gitee.com/nateshao
  10. * 説明: 並べ替えを選択
  11. */
  12. パブリッククラス Code_03_SelectionSortTest {
  13. 公共 静的void main(String[] args) {
  14. int [] arr = {3、44、38、5、47、15、36、26、27、2、46、4、19、50、48};
  15. 選択ソート(arr);
  16. ( int i = 0; i < arr.length; i++) {
  17. システム.out.print (arr[i] + " " );
  18. }
  19. }
  20.  
  21. 公共 静的void選択ソート( int []arr){
  22. 整数 ;
  23. ( int i = 0; i < arr.length - 1; i++) {
  24. min = i; // デフォルトでは、現在ソートされている配列の最初の要素が最小値になります
  25. ( int j = i + 1; j < arr.length; j++) {
  26. if (arr[ min ] > arr[j]) { // より小さな最小インデックスを見つける
  27. 最小値= j;
  28. }
  29. }
  30.  
  31. もし (i != min ) {
  32. スワップ(arr, i, min );
  33. }
  34. }
  35. }
  36.  
  37. 公共 静的void swap( int [] arr, int i, int j) {
  38. 整数  temp = arr[i];
  39. arr[i] = arr[j];
  40. arr[j] =一時;
  41. }
  42. }

5. クイックソート

ソート パスにより、ソートするデータは 2 つの独立した部分に分割され、一方の部分のすべてのデータはもう一方の部分のすべてのデータよりも小さくなります。次に、この方法に従って、2 つの部分のデータが別々にすばやくソートされます。ソート プロセス全体を再帰的に実行して、データ全体を順序付けられたシーケンスに変換できます。

アイデア:

  1. 左の最初の数字を基本値として取る
  2. 右から左へトラバースして参照番号より小さい値を見つけ、それを左側の位置に割り当てます。
  3. 左から右へ移動して参照番号より小さい値を見つけ、それを右側の位置に割り当てます。
  4. 左と右の人差し指が合うまで2、3を繰り返します
  5. 会議が行われる座標に参照値を配置します。
  6. ピボット値の左と右のサブ配列を再帰的に返します
  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. java.util.Arrays をインポートします。
  4.  
  5. /**
  6. * @日付Shao Tongjieによって2021/10/30 12:09作成
  7. * @WeChatパブリックアカウントプログラマーQianyu
  8. * 個人ウェブサイト www.nateshao.cn
  9. * @ブログ https://nateshao.gitee.io
  10. * @GitHub https://github.com/nateshao
  11. * @Gitee https://gitee.com/nateshao
  12. * 説明: クイックソート
  13. */
  14. パブリッククラス Code_05_QuickSort {
  15. 公共 静的void main(String[] args) {
  16. int [] arr = {3、44、38、5、47、15、36、26、27、2、46、4、19、50、48};
  17. クイックソート(arr, 0, arr.length - 1);
  18. System.out.println (Arrays.toString(arr)) ;
  19. }
  20.  
  21. /**
  22. * @param arr ソートする列
  23. * @param leftIndex ソートする列の開始位置
  24. * @param rightIndex ソートする列の終了位置
  25. */
  26. プライベート静的voidクイックソート( int [] arr、 int左インデックス、 int右インデックス) {
  27. /**
  28. * 左辺が右辺より大きい場合は、直接戻ります
  29. */
  30. 左インデックス >= 右インデックスの場合 {
  31. 戻る;
  32. }
  33.  
  34. 整数 = 左インデックス;
  35. 整数 = 右インデックス;
  36. //ソートされる最初の要素が参照値として使用されます
  37. 整数 キー= arr[];
  38.  
  39. // left = rightになるまで、左側と右側を交互にスキャンします 
  40. <){
  41. while (>&& arr[] >=キー) {
  42. //右から左にスキャンして、参照値より小さい最初の要素を見つけます
  43. - ;  
  44. }
  45.  
  46. // この要素を見つけて、arr[ right ] を arr[ left ]に格納します。
  47. arr[] = arr[];
  48.  
  49. while (<&& arr[] <=キー) {
  50. //左から右にスキャンして、参照値より大きい最初の要素を見つけます
  51. ++;
  52. }
  53.  
  54. // この要素を見つけて、arr[ left ] を arr[ right ]に格納します。
  55. arr[] = arr[];
  56. }
  57. //参照値を返す
  58. arr[] =キー;
  59. //参照値の左側の要素を再帰的にソートする
  60. クイックソート(arr, 左インデックス,- 1);
  61. // 基本値の右側の要素を再帰的にソートします。
  62. クイックソート(arr,+ 1, 右インデックス);
  63. }
  64. }

6. マージソート

時間計算量、追加の空間計算量

マージソートは、マージ操作に基づいた効果的なソートアルゴリズムです。このアルゴリズムは、分割統治法の非常に典型的な応用です。順序付けられたサブシーケンスをマージして、完全に順序付けられたシーケンスを取得します。つまり、最初に各サブシーケンスを順序付けし、次にサブシーケンスのセグメントを順序付けます。 2 つの順序付きリストが 1 つの順序付きリストに結合される場合は、双方向結合と呼ばれます。マージソートは安定したソート方法です。

アイデア:

2 つのソートされたシーケンスの合計サイズを持つスペースを申請します。このスペースは、結合されたシーケンスを格納するために使用されます。

2 つのポインタを設定します。その初期位置は、2 つのソートされたシーケンスの開始位置になります。

2つのポインタが指す要素を比較し、比較的小さい要素を選択してマージスペースに配置し、ポインタを次の位置に移動します。

ポインタがシーケンスの最後に到達するまでステップ3を繰り返します。

他のシーケンスの残りの要素をすべて、結合されたシーケンスの末尾に直接コピーします。

例えば:

例: 配列A: [4,6,9] 配列B: [3, 2, 1]

まず配列 A と B をソートします。配列 A と B。新しい配列 C を定義します。配列 A を比較し、小さい方を前に置きます。配列Aをソートした後、配列Bを配列Cにコピーします。

C配列: [1,2,3,4,6,9]

コード実装:

  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. java.util.Arrays をインポートします。
  4.  
  5. /**
  6. * @日付Shao Tongjieによって2021/10/30 12:15作成
  7. * @WeChatパブリックアカウントプログラマーQianyu
  8. * 個人ウェブサイト www.nateshao.cn
  9. * @ブログ https://nateshao.gitee.io
  10. * @GitHub https://github.com/nateshao
  11. * @Gitee https://gitee.com/nateshao
  12. * 説明:
  13. */
  14. パブリッククラス Code_06_MergeSort {
  15. 公共 静的void main(String[] args) {
  16. 整数テスト時間 = 500000;
  17. 最大サイズ = 20;
  18. 整数最大値 = 100;
  19. ブール値 成功 = true ;
  20. ( int i = 0; i < testTime; i++)の場合{
  21. int [] arr1 = ランダム配列を生成します(maxSize、maxValue);
  22. int [] arr2 = copyArray(arr1);
  23. マージソート(arr1);
  24. コンパレータ(arr2);
  25. (!isEqual(arr1、arr2))の場合{
  26. 成功 = false ;
  27. printArray(arr1);
  28. printArray(arr2);
  29. 壊す;
  30. }
  31. }
  32. System.out.println (succeed? 「いいね!」 : 「最悪!」 );
  33.  
  34. int [] arr = generateRandomArray(maxSize, maxValue);
  35. printArray(arr);
  36. マージソート(arr);
  37. printArray(arr);
  38.  
  39. }
  40. /**
  41. * 配列の長さが空の場合、または配列の長さが 1 の場合。比較せずに直接返す
  42. * @param arr
  43. */
  44. 公共 静的voidマージソート( int []arr){
  45. (arr == null || arr.length < 2) の場合 {
  46. 戻る;
  47. }
  48. マージソート(arr, 0, arr.length - 1);
  49. }
  50.  
  51. /**
  52. * この範囲には数字が1つしかないので、それを直接返します
  53. * @param arr
  54. * @param l
  55. * @param r
  56. */
  57. 公共 静的void mergeSort( int [] arr, int l, int r) {
  58. もし(l == r){
  59. 戻る;
  60. }
  61. int mid = l + ((r - l) >> 1); // LとRの中点 (L + R) / 2
  62. mergeSort(arr, l, mid); // 左部分をソートする T(n/2)
  63. mergeSort(arr, mid + 1, r); // 右側の部分をソートする T(n/2)
  64. merge(arr, l, mid, r); // 左部分と右部分をマージする O(n)
  65. // 合計時間計算量: T(N) = 2T(n/2) + O(N)
  66. }
  67.  
  68. 公共 静的voidマージ( int [] arr, int l, int m, int r) {
  69. int [] help = 新しいint [r - l + 1];
  70. 整数i = 0;
  71. int p1 = l; // 配列 l、左側の最初の配列の最小値。
  72. int p2 = m + 1; // 右側の最初のものの最小値。
  73. while (p1 <= m && p2 <= r) { // p1p2のどちらか小さい方を新しい配列に入れて、配列を再ソートします
  74. help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
  75. }
  76. /**
  77. * p1、p2、2つの数値のうち1つは範囲外である必要があります
  78. */
  79. (p1 <= m) の間 {
  80. help[i++] = arr[p1++];
  81. }
  82. (p2 <= r) の間
  83. help[i++] = arr[p2++];
  84. }
  85. (i = 0; i < help.length; i++)の場合{
  86. arr[l + i] = help[i]; // 最後に配列をarr[i]にコピーします
  87. }
  88. }
  89.  
  90. /**
  91. * コンパレータ
  92. * @param arr
  93. */
  94. 公共 静的voidコンパレータ( int []arr){
  95. 配列.sort(arr);
  96. }
  97.  
  98. /**
  99. * 任意の配列を生成する
  100. * @param 最大サイズ
  101. * @param 最大値
  102. * @戻る 
  103. */
  104. 公共 静的  int [] ランダム配列を生成する( int最大サイズ、 int最大値) {
  105. int [] arr = 新しいint [( int ) ((maxSize + 1) * Math.random())];
  106. ( int i = 0; i < arr.length; i++) {
  107. arr[i] = ( int ) ((maxValue + 1) * Math.random()) - ( int ) (maxValue * Math.random());
  108. }
  109. arrを返します
  110. }
  111.  
  112. /**
  113. * 配列をコピー
  114. * @param arr
  115. * @戻る 
  116. */
  117. 公共 静的  int [] コピー配列( int [] arr) {
  118. もしarr == nullの場合{
  119. 戻る ヌル;
  120. }
  121. int [] res = 新しいint [arr.length];
  122. ( int i = 0; i < arr.length; i++) {
  123. res[i] = arr[i];
  124. }
  125. resを返します
  126. }
  127.  
  128. /**
  129. * 2 つの配列は等しいですか?
  130. * @パラメータ arr1
  131. * @param arr2
  132. * @戻る 
  133. */
  134. 公共 静的ブール値isEqual( int [] arr1, int [] arr2) {
  135. ((arr1 == null && arr2 != null ) || (arr1 != null && arr2 == null )) {
  136. 戻る 間違い;
  137. }
  138. (arr1 == null && arr2 == null )の場合{
  139. 戻る 真実;
  140. }
  141. (arr1の長さ!= arr2の長さ)の場合{
  142. 戻る 間違い;
  143. }
  144. ( int i = 0; i < arr1.length; i++) {
  145. (arr1[i] != arr2[i]) の場合 {
  146. 戻る 間違い;
  147. }
  148. }
  149. 戻る 真実;
  150. }
  151.  
  152. /**
  153. * 配列を印刷する
  154. * @param arr
  155. */
  156. 公共 静的void printArray( int []arr){
  157. もしarr == nullの場合{
  158. 戻る;
  159. }
  160. ( int i = 0; i < arr.length; i++) {
  161. システム.out.print (arr[i] + " " );
  162. }
  163. System.out.println( ) ;
  164. }
  165. }

7. カウントソート

カウンティングソートはバケットソートの一種です。これは非比較ベースのソートアルゴリズムです。

その利点は、特定の範囲内で整数をソートする場合、その複雑さは (k は整数の範囲) であり、どの比較ソート アルゴリズムよりも高速であることです。もちろん、これは時間と引き換えにスペースを犠牲にする手法であり、その時点では比較ベースのソートほど効率は良くありません。

アイデア:

  1. 配列内の最大値と最小値を見つける
  2. 対応する長さ(最大 - 最小 + 1)の配列を作成します。
  3. 各数字の出現回数を数える array[val - min]++
  4. ターゲット配列を埋める

コード実装:

  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. java.util.Arrays をインポートします。
  4.  
  5. /**
  6. * @日付Shao Tongjieによって2021/10/30 14:38作成
  7. * @WeChatパブリックアカウントプログラマーQianyu
  8. * 個人ウェブサイト www.nateshao.cn
  9. * @ブログ https://nateshao.gitee.io
  10. * @GitHub https://github.com/nateshao
  11. * @Gitee https://gitee.com/nateshao
  12. * 説明: カウントソート
  13. * オリジナルリンク: https://baijiahao.baidu.com/s?id=1613842852280800384&wfr=spider& for =pc
  14. */
  15. パブリッククラス Code_07_CountSort {
  16.  
  17. 公共 静的void main(String[] args) {
  18. int [] 配列 = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
  19. int [] sortedArray = countSort(配列);
  20. システム.out.println (Arrays.toString(sortedArray));
  21. }
  22.  
  23. 公共 静的  int [] countSort( int [] 配列) {
  24. //1. 系列の最大値と最小値を取得し、その差を計算する
  25. 整数 最大値= 配列[0];
  26. 整数 最小値= 配列[0];
  27. ( int i = 1; i < 配列の長さ; i++) {
  28. 配列[i] >最大値場合
  29. 最大値= 配列[i];
  30. }
  31. 配列[i] < min場合
  32. min = 配列[i];
  33. }
  34. }
  35. int d =最大値-最小値;
  36. //2. 統計配列を作成し、対応する要素の数を数える
  37. int [] countArray = 新しいint [d + 1];
  38. ( int i = 0; i < 配列の長さ; i++) {
  39. countArray[配列[i] - min ]++;
  40. }
  41. //3. 統計配列を変換します。次の要素は前の要素の合計に等しくなります。
  42. 整数 合計= 0;
  43. ( int i = 0; i < countArray.length; i++) {
  44. 合計+= countArray[i];
  45. countArray[i] =合計;
  46. }
  47. //4. 元のシーケンスを逆順に走査し、統計配列から正しい位置を見つけて、結果配列に出力します。
  48. int [] sortedArray = 新しいint [array.length];
  49. ( int i = array.length - 1; i >= 0; i --) {  
  50. sortedArray[countArray[配列[i] - min ] - 1] = 配列[i];
  51. countArray[配列[i] - min ] --;  
  52. }
  53. ソートされた配列を返します
  54. }
  55. }

カウントソートの制限:

1. シーケンスの最大値と最小値の差が大きすぎる場合、カウントソートは適用できません。

たとえば、0 から 1 億までの範囲の 20 個のランダムな整数が与えられた場合、カウントソートを使用すると、長さ 1 億の配列を作成する必要があります。スペースが大幅に無駄になるだけでなく、時間の複雑さも増大します。

2. シーケンスの要素が整数でない場合は、カウントソートは適用できません。

シーケンス内のすべての要素が 25.213 や 0.00000001 などの小数の場合、対応する統計配列を作成できません。これは明らかにカウントソートには使用できません。

8. 基数ソート

基数ソートはバケットソートの一種です。原理としては、整数を桁数に応じて異なる数値に分割し、各桁を個別に比較します。各ビットの比較が完了すると、配列は並べ替えられます。最上位ビットを比較すると、配列は順序付けられたシーケンスになります。

コード実装:

  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. java.util.Arrays をインポートします。
  4.  
  5. /**
  6. * @日付Shao Tongjieによって2021/10/30 15:15作成
  7. * @WeChatパブリックアカウントプログラマーQianyu
  8. * 個人ウェブサイト www.nateshao.cn
  9. * @ブログ https://nateshao.gitee.io
  10. * @GitHub https://github.com/nateshao
  11. * @Gitee https://gitee.com/nateshao
  12. * 説明: 基数ソート
  13. * オリジナルリンク: https://blog.csdn.net/weixin_44537194/article/details/87302788
  14. */
  15. パブリッククラス Code_08_RadixSort {
  16. 公共 静的void main(String[] args) {
  17. int [] 配列 = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
  18. radixSort(配列);
  19. システム.out.println (Arrays.toString(array));
  20. }
  21.  
  22. 公共 静的void radixSort( int [] arr) {
  23. // ループ回数を知るために、配列に最大の数値を保存します
  24. 整数  max = Integer .MIN_VALUE; // (最小の整数)
  25. // 配列を走査して最大値を見つける
  26. ( int i = 0; i < arr.length; i++) {
  27. 最大値<arr[i])の場合{
  28. 最大値= arr[i];
  29. }
  30. }
  31.  
  32. // 最大桁数を計算する。この方法は非常に賢い
  33. int最大長さ = (最大+ "" ).length();
  34. // データを一時的に保存するための配列
  35. int [][] temp = 新しいint [10][arr.length];
  36. // バケット内の要素の位置を格納するために使用します
  37. int [] counts = new int [arr.length];
  38.  
  39. // 最初の 1 桁の数字は余りを得るのが簡単です。2 番目のラウンドは 10 で割って余りを出し、その後 100 の位の数字を 100 で割ります。
  40. // ご覧のとおり、ループ回数に応じて変化する別の変数があります。
  41.  
  42. //ループ回数
  43. ( int i = 0, n = 1; i < maxLength; i++, n *= 10) {
  44. // 各ラウンドの残りを取る
  45. ( int j = 0; j < arr.length; j++)の場合{
  46. // 余りを計算する
  47. int ys = (arr[j] / n) % 10;
  48. // コンビニエンスストアのデータを指定された配列に格納します。格納するバケットと格納する順序の 2 つの情報があります。
  49. temp [ys][counts[ys]] = arr[j];
  50. //レコード数
  51. counts[ys]++;
  52. }
  53.  
  54. //記録する数字は、次の位置に配置する必要があります
  55. 整数 インデックス= 0;
  56. // 各ループの後に数字を取り出す
  57. ( int k = 0; k < counts.length; k++) {
  58. // レコード数の配列内の現在の残余レコードはゼロではありません
  59. カウント[k]が0の場合
  60. ( int l = 0; l < counts[k]; l++) {
  61. // 要素を取得する
  62. arr[インデックス] = temp [k][l];
  63. インデックス++;
  64. }
  65. // 取り出した後に数量をゼロにする
  66. カウント[k] = 0;
  67. }
  68. }
  69. }
  70. }
  71. }

9. バケットソート

バケット ソート、またはビン ソートは、配列を有限数のバケットに分割することによって機能するソート アルゴリズムです。各バケットは個別にソートされます。

アイデア:

  1. 空のバケットとして使用する配列の固定数を設定します。
  2. シーケンスを検索し、アイテムを 1 つずつ対応するバケットに入れます。
  3. 空ではない各バケットをソートします。
  4. 空ではないバケットからアイテムを元の順序に戻します。

コード実装:

  1. パッケージ com.nateshao.basic_01_ten_sort;
  2.  
  3. java.util.ArrayList をインポートします。
  4. java.util.Arrays をインポートします。
  5. java.util.List をインポートします。
  6. java.util.Random をインポートします。
  7.  
  8. /**
  9. * @日付Shao Tongjieによって2021/10/30 15:56作成
  10. * @WeChatパブリックアカウントプログラマーQianyu
  11. * 個人ウェブサイト www.nateshao.cn
  12. * @ブログ https://nateshao.gitee.io
  13. * @GitHub https://github.com/nateshao
  14. * @Gitee https://gitee.com/nateshao
  15. * 説明: バケットソート
  16. */
  17. パブリッククラス Code_09_BucketSort {
  18.  
  19. 公共 静的void main(String[] args) {
  20. int [] arr = {3、44、38、5、47、15、36、26、27、2、46、4、19、50、48};
  21. バケットソート(arr);
  22. System.out.println (Arrays.toString(arr)) ;
  23. }
  24.  
  25. 公共 静的voidバケットソート( int []arr){
  26. 整数 最大値= arr[0];
  27. 整数 最小値= arr[0];
  28. ( int i = 1; i < arr.length; i++) {
  29. 最大値<arr[i])の場合{
  30. 最大値= arr[i];
  31. }
  32. もし(最小値>arr[i]){
  33. 最小値= arr[i];
  34. }
  35. }
  36. int bucketNum = ( max - min ) / 10 + 1; // バケットの数
  37. List[] バケット = 新しいArrayList[bucketNum];
  38. ( int i = 0; i < バケット長さ; i++) {
  39. バケット[i] = 新しいArrayList <Integer> ();
  40. }
  41.  
  42. // 要素をバケットに入れる
  43. ( int i = 0; i < arr.length; i++) {
  44. int a = (arr[i] - min ) / 10;
  45. バケット[a] .add (arr[i]);
  46. }
  47.  
  48. 整数 インデックス= 0;
  49. for (List< Integer > b : バケット) {
  50. insertionSort(b); // バケット内の要素をソートする
  51. for (整数num : b) {
  52. arr[ index ++] = num; // ソートされた要素を取り出す
  53. }
  54. }
  55. }
  56.  
  57. // 挿入ソート
  58. 公共 静的void挿入ソート(リスト<整数> arr) {
  59. ( int i = 1; i < arr.size ( ) ); i++) {
  60. // 2番目から始めて、前のものより小さいかどうかを確認します
  61. (arr.get(i) < arr.get(i - 1))の場合{
  62. // 1つずつ前方に検索します。この数より大きい場合は、1つ前に戻ります
  63. 整数  temp = arr.get(i);
  64. 整数j = i - 1;
  65. j >= 0 && temp < arr.get(j) の場合{
  66. arr.set (j + 1, arr.get(j));
  67. j --;  
  68. }
  69. // それより小さい数値を探し、それをこの数値の後の要素に代入します 
  70. arr.set (j+1, temp );
  71. }
  72. }
  73. }
  74. }

10. ヒープソート

ヒープソートとは、ヒープデータ構造を使用して設計されたソートアルゴリズムを指します。ヒープは、完全なバイナリ ツリーを近似し、ヒープの特性を満たす構造です。つまり、子ノードのキー値またはインデックスは常に親ノードよりも小さい (または大きい) です。ヒープは完全な二分木です。完全な二分木とは、上から下、左から右に欠けているノードがない木です。簡単に言えば、ノードに右サブツリーがある場合、左サブツリーも存在する必要があります。

ヒープソートの説明:

上の図では、ノード 3 には右サブツリーがありますが、左サブツリーがないため、これは完全なバイナリ ツリーではありません。

ヒープは、大きなトップ ヒープと小さなトップ ヒープに分割されます。

  • 最大ヒープの親ノードの値は子ノードの値以上である
  • 小さなトップヒープの親ノードの値は子ノードの値以下です

配列内のすべての要素をヒープにマップすると、そのインデックスは次の関係にあることがわかります。左ノードのインデックス = 親ノード * 2 + 1 右ノードのインデックス = 親ノード * 2 + 2

最大ヒープの場合、ルート ノードがその最大値になります。ランダム配列をヒープに変換するには、上記の関係に従って親ノードと子ノードの要素を交換するだけです。次に、ルート ノードと最後のノードを交換します。次に、配列の長さを短くします。配列をヒープに変換するときに、配列内の最大の数値を取得して、それを配列の末尾に移動することもできます。ヒープを変換しています...

アイデア:

配列全体をヒープに構築する

ルートノードと最後のノードを入れ替える

ルートノードからheapifyを開始する

すべての要素が走査されるまで 2 と 3 を繰り返します。

コード実装:

  1. パッケージ com.nateshao.basic_class_01;
  2.  
  3. java.util.Arrays をインポートします。
  4.  
  5. パブリッククラス Code_03_HeapSort {
  6.  
  7. 公共 静的void main(String[] args) {
  8. 整数テスト時間 = 500000;
  9. 最大サイズ = 20;
  10. 整数最大値 = 100;
  11. ブール値 成功 = true ;
  12. ( int i = 0; i < testTime; i++)の場合{
  13. int [] arr1 = ランダム配列を生成します(maxSize、maxValue);
  14. int [] arr2 = copyArray(arr1);
  15. ヒープソート(arr1);
  16. コンパレータ(arr2);
  17. (!isEqual(arr1、arr2))の場合{
  18. 成功 = false ;
  19. 壊す;
  20. }
  21. }
  22. System.out.println (succeed? 「素晴らしい!」 : 「最悪だ!」 );
  23.  
  24. int [] arr = generateRandomArray(maxSize, maxValue);
  25. printArray(arr);
  26. ヒープソート(arr);
  27. printArray(arr);
  28. }
  29.  
  30. /**
  31. * ヒープソート
  32. * @param arr
  33. */
  34. 公共 静的voidヒープソート( int []arr){
  35. (arr == null || arr.length < 2) の場合 {
  36. 戻る;
  37. }
  38. // 大きなルートヒープを作成する
  39. ( int i = 0; i < arr.length; i++) {
  40. ヒープ挿入(arr, i);
  41. }
  42. 整数 サイズ= arr.length;
  43. swap(arr, 0, --size); // 最後の桁が最初の桁と入れ替わります。 ヒープサイズマイナス1  
  44. (サイズ> 0)の場合
  45. heapify(arr, 0, size ); // 大きなルートヒープの調整を続ける
  46. swap(arr, 0, --size); // 続行します。最後の桁が最初の桁と入れ替わります。 ヒープサイズマイナス1  
  47. }
  48. }
  49.  
  50. /**
  51. * 大きな根の山を作る
  52. *
  53. * @param arr
  54. * @paramインデックス 
  55. */
  56. 公共 静的voidヒープ挿入( int []arr, int  索引) {
  57. (arr[インデックス] > arr[(インデックス- 1) / 2]) {
  58. swap(arr,インデックス, (インデックス- 1) / 2);
  59. インデックス= (インデックス- 1) / 2;
  60. }
  61. }
  62.  
  63. 公共 静的voidヒープ化( int []arr, int  インデックス int  サイズ) {
  64. 整数  left = index * 2 + 1; // 左の子。 i*2+1 右の子:+ 1
  65. while ( left < size ) { // ヒープサイズ
  66. //左の子と子を比較し、値の大きい方を取得します
  67. int largest = left + 1 < size && arr[ left + 1] > arr[ left ] ? left + 1 : left ;
  68. // ノードをその左と右の子と比較し、大きい方のインデックスを取得します
  69. 最大 = arr[最大] > arr[インデックス] ? 最大:インデックス;
  70. // 私なら続ける必要はありません
  71. if (最大 ==インデックス) {
  72. 壊す;
  73. }
  74. swap(arr, largest, index ); // largest != index  
  75. インデックス= 最大;
  76. =インデックス* 2 + 1;
  77. }
  78. }
  79.  
  80. 公共 静的void swap( int [] arr, int i, int j) {
  81. tmp = arr[i];
  82. arr[i] = arr[j];
  83. arr[j] = tmp;
  84. }
  85.  
  86. 公共 静的voidコンパレータ( int []arr){
  87. 配列.sort(arr);
  88. }
  89.  
  90. 公共 静的  int [] ランダム配列を生成する( int最大サイズ、 int最大値) {
  91. int [] arr = 新しいint [( int ) ((maxSize + 1) * Math.random())];
  92. ( int i = 0; i < arr.length; i++) {
  93. arr[i] = ( int ) ((maxValue + 1) * Math.random()) - ( int ) (maxValue * Math.random());
  94. }
  95. arrを返します
  96. }
  97.  
  98. 公共 静的  int [] コピー配列( int [] arr) {
  99. もしarr == nullの場合{
  100. 戻る ヌル;
  101. }
  102. int [] res = 新しいint [arr.length];
  103. ( int i = 0; i < arr.length; i++) {
  104. res[i] = arr[i];
  105. }
  106. resを返します
  107. }
  108.  
  109. 公共 静的ブール値isEqual( int [] arr1, int [] arr2) {
  110. ((arr1 == null && arr2 != null ) || (arr1 != null && arr2 == null )) {
  111. 戻る 間違い;
  112. }
  113. (arr1 == null && arr2 == null )の場合{
  114. 戻る 真実;
  115. }
  116. (arr1の長さ!= arr2の長さ)の場合{
  117. 戻る 間違い;
  118. }
  119. ( int i = 0; i < arr1.length; i++) {
  120. (arr1[i] != arr2[i]) の場合 {
  121. 戻る 間違い;
  122. }
  123. }
  124. 戻る 真実;
  125. }
  126.  
  127. 公共 静的void printArray( int []arr){
  128. もしarr == nullの場合{
  129. 戻る;
  130. }
  131. ( int i = 0; i < arr.length; i++) {
  132. システム.out.print (arr[i] + " " );
  133. }
  134. System.out.println( ) ;
  135. }
  136. }

要約する

上記は、古典的なソートアルゴリズムのトップ 10 です。実際、企業の面接に行くと、最初は何も聞かれず、アルゴリズムをテストされるだけです。しかし、職場ではアルゴリズムはほとんど使われません。

しかし、私たちには何もできない、それが現実です。もちろん、アルゴリズムは有能なプログラマーが習得しなければならない最も基本的な知識です。アーキテクトやリーダーなど、開発の最前線に立つポジションでは、コードを書いている限り、アルゴリズムの部分は面接で取り上げられる必要があります。

参考リンク:

  • ソートの定義: https://blog.csdn.net/weixin_41190227/article/details/86600821
  • 古典的なソートアルゴリズムのトップ 10: https://zfhelo.gitee.io/2020/06/14/1
  • ヒルソート: https://www.cnblogs.com/chengxiao/p/6104371.html
  • カウントソート: https://baijiahao.baidu.com/s?id=1613842852280800384&wfr=spider&for=pc
  • 基数ソート: https://blog.csdn.net/weixin_44537194/article/details/87302788

<<:  AIロボットが産業監視を強化

>>:  インタープリタパターンを使用して、要素のXPathパスを取得するためのアルゴリズムを実装します。

ブログ    
ブログ    

推薦する

人工知能シナリオにおける HBase の使用

近年、人工知能は、特にビッグデータと組み合わせて使用​​されることで、ますます人気が高まっています。...

無人配送はJD.com、Alibaba、SF Expressの「新たなお気に入り」となっているが、全国的に普及するには10年かかるかもしれない!

[[222058]]無人運転車、ドローン、無人倉庫、無人駅、配達ロボットなどの「無人技術」が、電子...

...

深度はディープニューラルネットワークに具体的に何をもたらすのでしょうか?

[[186161]]起源近年、人工知能は爆発的な成長を遂げており、ディープラーニングはその主な原動...

...

これら10機関からの24の調査データはAIのトレンドを理解するのに役立ちます

[[256519]] 2019年1月現在の人工知能の現状は?最近の調査では、AI の人気、測定可能な...

金メダルレベルの数学スキル:DeepMindの幾何学的推論モデルがNatureに掲載され、コードはオープンソースで、フィールズ賞受賞者が賞賛

今回、人工知能アルゴリズムが国際数学オリンピック(IMO)で大きな進歩を遂げました。本日発行された国...

人工知能はリモートセンシングデータの大きな可能性を解き放ち、国勢調査の手作業が置き換えられるかもしれない

畳み込みニューラルネットワーク(CNN)と衛星画像データを使用して地域の所得レベルを予測する手法がま...

...

...

AI企業の成人式:自由が996と衝突し、技術的理想が地上戦争と衝突する

戦争の理由はすべて、例外なく一つのこと、つまり生き残ることにつながります。狼の本能がなければ、生き残...

インドネシアのゴミ分別:人工知能が役に立つ

上海市は7月に「史上最も厳しいゴミ分別措置」を実施し始めて以来、ゴミ分別は多くの人々の日常生活におけ...