Java は、一般的に使用されるソート アルゴリズム (クイック ソート、マージ ソート、基数ソート) を実装します。

一般的な並べ替えアルゴリズム

ここに画像の説明を挿入

クイックソート

1 はじめに

クイックソートは、Tony Hall によって開発された並べ替えアルゴリズムです。平均して、n 個の項目を並べ替えるには O(nlogn) 個の比較が必要です。最悪の場合、Ο(n2) の比較が必要になりますが、これは一般的ではありません。実際、クイックソートは、その内部ループがほとんどのアーキテクチャで効率的に実装できるため、通常、他の Ο(nlogn) アルゴリズムよりも大幅に高速です。

クイックソートは分割統治戦略を使用して、リストを 2 つのサブリストに分割します。

クイック ソートは、ソート アルゴリズムにおける分割統治の考え方のもう 1 つの典型的な応用です。本質的に、クイック ソートはバブル ソートに基づく再帰的な分割統治法と見なされるべきです。

クイックソートの名前はシンプルで失礼ですが、名前を聞いたらすぐにその存在の意味がわかり、高速かつ効率的です。これは、ビッグデータの最も高速な並べ替えアルゴリズムの 1 つです。ワーストケースの時間計算量は O(n²) に達しますが、それらは優れており、ほとんどの場合、平均時間計算量が O(n logn) の並べ替えアルゴリズムよりも優れたパフォーマンスを発揮します。

クイック ソートの最悪の実行ケースは、順次配列のクイック ソートなど、O(n²) です。しかし、その償却期待時間は O(nlogn) であり、O(nlogn) 表記に含まれる定数係数は小さく、複雑さが O(nlogn) で安定しているマージ ソートよりもはるかに小さくなります。したがって、順序が弱い乱数シーケンスの大部分では、マージ ソートよりもクイック ソートの方が常に優れています。

2. 思考分析

クイック ソート アルゴリズムは、複数の比較と交換によるソートを実装します。そのソート プロセスは次のとおりです。
(1) まず、カットオフ値を設定し、カットオフ値によって配列を左右の部分に分割します。
(2) カットオフ値以上のデータを配列の右側に集め、カットオフ値未満のデータを配列の左側に集めます。このとき、左側の各要素はカットオフ値未満であり、右側の各要素はカットオフ値以上である。
(3) すると、左右のデータを独立してソートすることができます。左側の配列データの場合、境界値をとってデータのこの部分を左右に分割し、小さい値を左側に配置し、大きい値を右側に配置します。右側の配列データも同様に処理できます。
(4) 上記のプロセスを繰り返すと、これが再帰的定義であることがわかります。左部分を再帰的にソートした後、右部分の順序を再帰的にソートします。左部分と右部分のデータのソートが完了すると、配列全体のソートも完了します。

3. 図表

3.1 スタート

arr 配列の両端からそれぞれ開始します検出まず右から左に 8 より小さい数を見つけ、次に左から右に 8 より大きい数を見つけて、それらを交換します。ここでは、シーケンスの左端と右端をそれぞれ指す 2 つの変数 L (左) と R (右) を使用できます。最初に、L が配列の左端、つまり数値 8 を指すようにします。R が配列の右端、つまり数値 11 を指すものとします。
ここに画像の説明を挿入

3.2 右から左にベンチマークよりも小さい数値に遭遇する

ここに画像の説明を挿入

3.3 右側で停止し、左から右にベンチマークより大きい数値を探します。

ここに画像の説明を挿入

3.4 LとRに対応する番号を交換する

ここに画像の説明を挿入

交換後
ここに画像の説明を挿入

3.5 上記の方法の続き

まず R がディスパッチを開始します。ここで設定する基数は一番左の数字なので、Rを先に行かせることが非常に重要です(理由は自分で考えてください)。センチネル R は、8 未満の数字を見つけて停止するまで、左に一歩ずつ移動します (つまり、R–-)。次に、センチネル L は、8 より大きい数値を見つけて停止するまで、右に一歩ずつ移動します (つまり、L++)。

交換
ここに画像の説明を挿入

交換後

ここに画像の説明を挿入

このときRは左に移動し続けます
ここに画像の説明を挿入

するとLが動きます
ここに画像の説明を挿入

L == R の場合、この位置は 8 の位置であることを意味し、交換
ここに画像の説明を挿入

得る:

ここに画像の説明を挿入
これで最初の検出ラウンドが終了します。このとき、基準番号8を分割点とし、8の左側の数字はすべて8以下、8の右側の数字はすべて8以上となる。先ほどのプロセスを振り返ると、実際には、R の使命はベンチマークの数値より小さい数値を見つけることであり、L の使命は L と R が一致するまでベンチマークの数値よりも大きい数値を見つけることです。

3.6 左側の数字の扱い

ここに画像の説明を挿入

3.7 右側の数値の処理

ここに画像の説明を挿入

得る:
ここに画像の説明を挿入

4. コードの実装

/**
 * 快速排序
 * @author 尹稳健~
 * @version 1.0
 * @time 2022/9/10
 */
public class QuickSort {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    8, 12, 19, -1, 45, 0, 14, 4, 11};
        quickSort(arr,0,arr.length-1);
        for (int i = 0; i < arr.length; i++) {
    
    
            System.out.print(arr[i]+" ");
        }
    }
    public static void quickSort(int[] arr,int start, int end){
    
    
        // 什么时候结束排序?
        if (start>end){
    
    
            return;
        }
        // 左指针
        int L = start;
        // 右指针
        int R = end;
        // 基准
        int pivot = arr[start];
        // 只要L!=R就继续循环
        while (L < R){
    
    
            // 右边大于基准,从右向左移动
            while (L<R && arr[R] >= pivot){
    
    
                R --;
            }
            // 左边小于基准,从左向右移动
            while (L<R&& arr[L] <= pivot){
    
    
                L ++;
            }
            // 说明右边有小于基准的数,左边有大于基准的数,交换
            if (L<R){
    
    
                int temp = arr[L];
                arr[L] = arr[R];
                arr[R] = temp;
            }
        }
        // L 与 R碰面 找到了 基准的位置
        arr[start] = arr[L];
        arr[L] = pivot;

        // 左边排序
        quickSort(arr,start,R-1);
        // 右边排序
        quickSort(arr,L+1,end);

    }

}

クイック ソート この記事はよく書かれており、他の人のクイック ソートの書き方と理解を参照することができます。


マージソート

1 はじめに

マージ ソートは、マージ操作に基づいた効果的で安定したソート アルゴリズムであり、分割統治 (Divide and Conquer) の非常に典型的なアプリケーションです。順序付けられたサブシーケンスを結合して、完全に順序付けられたシーケンスを取得します。つまり、最初に各サブシーケンスを順番に作成し、次にサブシーケンス セグメントを順番に作成します。2 つのソート済みリストを 1 つのソート済みリストにマージすることを、双方向マージと呼びます。

2. 思考分析

マージソートは分割統治の考え方を使用しており、ルールが難しいのが特徴です

  • マージされたシーケンスを格納するために使用される、2 つの並べ替えられたシーケンスの合計サイズになるようにスペースを適用します。
  • 初めに分解シーケンス、シーケンスを各グループのシーケンスに分解します。数量は1です、次にマージソートを実行します
  • 設定2つのポインター、初期位置は、ソートされた 2 つのシーケンスの開始位置です。
  • 2 つのポインターが指す要素を比較し、比較的小さい要素を選択してマージ スペースに配置し、ポインターを次の位置に移動します。
  • 特定のポインターがシーケンスの最後に到達するまで、前のステップを繰り返します。
  • 別のシーケンスの残りの要素をすべて、マージされたシーケンスの最後に直接コピーします。

3. 図表

3.1 すべてのシーケンスを分割、分解する

ここに画像の説明を挿入

3.2 並べ替えと結合の開始

ここに画像の説明を挿入

充填するたびにポインタが 1 ビット移動します
ここに画像の説明を挿入

ここに画像の説明を挿入

左の配列に要素がない場合、右の配列が直接埋められます。
ここに画像の説明を挿入

得る
ここに画像の説明を挿入

4. コードの実装

/**
 * 归并排序
 * @author 尹稳健~
 * @version 1.0
 * @time 2022/9/12
 */
public class MergeSort {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    1, 5, 6, 3, 2, 8, 7, 4};
        int[] temp = new int[arr.length];
        mergeSort(arr,0,arr.length-1,temp);
        for (int i = 0; i < arr.length; i++) {
    
    
            System.out.print(arr[i]+" ");
        }
    }

    /** 分解,并合并排序 */
    public static void mergeSort(int[] arr,int left ,int right,int[] temp){
    
    
        if (left<right){
    
    
            //中间索引
            int mid = (left+right)/2;
            //向左递归分解
            mergeSort(arr,left,mid,temp);
            //向右递归分解
            mergeSort(arr,mid+1,right,temp);
            //合并
            merge(arr,left,mid,right,temp);
        }
    }

    /**
     * 合并排序
     * @param arr   未排序的数组
     * @param left  左边有序子数组的初始索引
     * @param mid   中间索引
     * @param right 右边最大索引
     * @param temp  临时数组
     */
    public static void merge(int[] arr,int left,int mid,int right,int[] temp){
    
    
        // 左边指针索引
        int leftPoint = left;
        // 右边指针索引
        int rightPoint = mid+1;
        // 临时数据的指针
        int tempLeft = 0;

        // 两个序列都不为空时
        while (leftPoint <= mid && rightPoint <= right){
    
    
            //如果第一个序列的元素小于第二序列的元素,就将其放入temp中
            if (arr[leftPoint] <= arr[rightPoint]){
    
    
                temp[tempLeft] = arr[leftPoint];
                leftPoint++;
                tempLeft ++;
            }else{
    
    
                temp[tempLeft] = arr[rightPoint];
                rightPoint++;
                tempLeft ++;
            }
        }

        // 右序列为空,直接将做序列的所有元素填充进去
        while (leftPoint <= mid){
    
    
            temp[tempLeft] = arr[leftPoint];
            leftPoint++;
            tempLeft++;
        }

        // 左序列为空,直接将右序列的所有元素填充进去
        while (rightPoint <= right){
    
    
            temp[tempLeft] = arr[rightPoint];
            rightPoint++;
            tempLeft++;
        }

        //将临时数组中的元素放回数组arr中
        tempLeft = 0;
        leftPoint = left;
        while (leftPoint <= right){
    
    
            arr[leftPoint] = temp[tempLeft];
            leftPoint++;
            tempLeft++;
        }


    }
}



基数ソート

1 はじめに

基数ソート(基数ソート)は、「バケットソート」やビンソートとも呼ばれる「分散ソート」に属し、その名の通り、キー値の部分情報でソート対象の要素を、順番にいくつかの「バケット」に分散します。ソートの役割を達成するために、基数ソート方法は安定したソートであり、その時間計算量は O (nlog®m) です。ここで、r は使用される基数、m はヒープの数です。ある時点で、基数ソートは次のようになります。他の安定性ソートよりも効率的です。

2. 思考分析

  • 比較するすべての値(正の整数)を同じ桁長に統一し、桁数を短くしてゼロ埋めする数値
  • 下位ビットから順に並べ替えます
  • 最下位の桁から最上位の桁へ(一の位→十の位→百の位→…→最上位の桁)ソートが完了すると、番号列は順序付けされたシーケンスになります。
  • 最大値を取得するために必要な桁数
    • 最大数を String 型に変更して、その長さを調べることができます

3. 図表

まず、[0-9] を表すバケットが 10 個必要です。

3.1 最初のラウンドは単位に従ってバケットに入れられます

ここに画像の説明を挿入
バケツの中に複数の要素がある場合は、先に入った要素が最初に取り出されます。
ここに画像の説明を挿入

3.2 第 2 ラウンドは 10 の単位に従ってバケットに入れられます。

ここに画像の説明を挿入
10の位がない場合は、1の位の前に0を追加します

ここに画像の説明を挿入

3.3 百の位に従って第 3 ラウンドがバレルに入れられます。

ここに画像の説明を挿入

一つ一つ取り出したら
ここに画像の説明を挿入

3.34 第 4 ラウンドは数千に従ってバケツに入れられます

ここに画像の説明を挿入

取り出して次のものを入手します。

ここに画像の説明を挿入

4. コードの実装


/**
 * 基数排序
 * @author 尹稳健~
 * @version 1.0
 * @time 2022/9/13
 */
public class BaseSorted {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    43, 52, 1, 89, 190};
        sort(arr);
        for (int i = 0; i < arr.length; i++) {
    
    
            System.out.print(arr[i] + " ");
        }
    }

    public static void sort(int[] arr){
    
    
        int maxLength = getMaxLength(arr);
        // 定义个二维数组桶,存储元素
        int[][] bucket = new int[10][arr.length];
        // 存储每个桶中有多少个元素
        int[] elementCounts = new int[10];
        for (int times = 1,step = 1; times < maxLength + 1; times++,step *= 10) {
    
    
            // 遍历数组,将元素存入桶中
            for (int i = 0; i < arr.length; i++) {
    
    
                // 获取位数上的数
                int digits = arr[i] / step % 10;
                bucket[digits][elementCounts[digits]] = arr[i];
                elementCounts[digits] ++;
            }

            //将桶中的元素重新放回到数组中
            //用于记录应该放入原数组的哪个位置
            int index = 0;
            for (int i = 0; i < 10; i++) {
    
    
                // 从桶中按放入顺序依次取出元素,放入原数组
                int position = 0;
                while ( elementCounts[i] > 0){
    
    
                    arr[index] = bucket[i][position];
                    position++;
                    elementCounts[i]--;
                    index++;
                }
            }



        }

    }

    /** 获取数组中元素长度最长的长度 */
    public static int getMaxLength(int[] arr){
    
    
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
    
    
            if (arr[i] > max ){
    
    
                max = arr[i];
            }
        }
        return (max + "").length();
    }
}

おすすめ

転載: blog.csdn.net/weixin_46073538/article/details/126802732