【2023年】Javaが共通のソートアルゴリズムを実装

1.二分探索法

  1. 前提条件: ソートされた配列が必要です
  2. 左境界Lと右境界Rを定義し、探索範囲を確認し、ループで二分探索を行う(3,4)
  3. 中央のインデックスを取得します M = Floor((L+R)/2)
  4. 中間インデックスの値の値A[M]と検索対象の値Tを比較し、
    1. A[M] == T は見つかったことを意味し、中間インデックスを返します
    2. A[M]>T、中央の値の右側の他の要素が T (検索された値) より大きい場合、比較せず、中央のインデックス M-1 を右境界として設定し、再度検索します。
    3. A[M]>T、中央の値の左側にある他の要素は T (検索された値) より小さいため、比較する必要はありません。中央のインデックス M-1 を右側の境界に設定して、再度検索します。

二分探索を使用する場合、奇数の二進数が中央の値になります。

偶数を 2 で割って左中央の値を取る

/**
     *二分查找
*@parama查找数组
*@paramt查找的值
*@return
*/
private static int binarySearch1(int[] a,int t) {
    
    
//        l:左边索引,r右边索引,中间索引
        int l = 0 ,r=a.length -1,m;
        int i=1;
        while (l<=r){
    
    
            m=(l+r)>>>1;
            System.out.println("第"+i+"轮:左边下标--"+l+",右边下标--"+r+",中间下标--"+m+"中间下标的值--"+a[m]);
            if (a[m] == t){
    
    
                return m;  //表示查找到了
            }else if (a[m] >t){
    
    
                r=m-1;   //把右边界设置为中间索引-1
            }else {
    
    
                l=m+1;   //把左边界设置为中间索引+1
            }
            i++;
        }
        return -1;
    }

2. バブルソーティング

  • コンセプト:

    1. 2 つの隣接する数値が比較されるたびに、a[j]>a[j+1] 要素の場合、2 つの要素が交換され、両方が再度比較されます。これをバブリング ラウンドと呼びます。結果は、最大のものになります。要素を右端に配置
    2. 配列全体がソートされるまで、上記の手順を繰り返します。
  • 最適化方法:

    • バブリングの各ラウンド中に、最後の交換インデックスを次のバブリング ラウンドの比較回数として使用できます。この値が 0 の場合は、配列全体が正常であり、外側のループが直接終了することを意味します。
  • オプション 1:

    /**
         *冒泡排序
    *@parama
    */
    public static void bubble(int[] a){
          
          
    //        循环次数
            for (int j = 0; j < a.length-1; j++) {
          
          
                boolean swapped = false;
    //          比较次数
    //            因为每次都会有一位排好序的数,所有比较次数可以设置为每次都比上一次少1,以此减少比较次数
                for (int i = 0; i < a.length-1-j; i++) {
          
          
                    System.out.println("比较次数:"+i);
    
    //             每一次交换,把swapped改为true
                    if (a[i] > a[i+1]){
          
          
    										swap(a,i,i+1);
                        swapped = true;
                    }
                }
                System.out.println("第"+j+"轮,循环"+Arrays.toString(a));
    //          没产生交换时退出循环
                if (!swapped){
          
          
                    break;
                }
            }
        }
    
  • オプション II:

    /**
         *冒泡排序
    *@parama
    */
    public static void bubble(int[] a){
          
          
    //        每一轮交换次数
            int n = a.length-1;
    //        循环次数
            while (true){
          
          
                boolean swapped = false;
                int last = 0;  //表示最后异常交换的下标
    
    //          比较次数
    //            因为每次都会有一位排好序的数,所有比较次数可以设置为每次都比上一次少1,以此减少比较次数
                for (int i = 0; i < n; i++) {
          
          
                    System.out.println("比较次数:"+i);
                    if (a[i] > a[i+1]){
          
          
    swap(a,i,i+1);
                        last = i;  //每一轮比较的交换次数
                    }
                }
                n = last;  //把最后一轮比较的下标赋给n
    
                System.out.println("第n轮,循环"+Arrays.toString(a));
    //            当最后一轮比较的下标是0时,代表比较完毕,没有产生交换,退出循环
                if (n == 0){
          
          
                    break;
                }
            }
        }
    

3. 並べ替えを選択します

  • 概要:

    • 配列をソート済みとソートされていない 2 つのサブセットに分割します。各ラウンドで、ソートされていないサブセットから最小の要素が選択され、ソートされたサブセットに入れられます。
    • 配列全体がソートされるまで、上記の手順を繰り返します。
  • バブルソートとの比較

    • 両方の平均複雑さはO ( n 2 ) O(n^2)です。O ( n2 )
    • 選択ソートは交換が少ないため、一般にバブリングより高速です。
    • ただし、セットが高度に順序付けされている場合は、選択よりもバブリングの方が優れています。これは、バブルの比較中に、セットが順序付けされているかどうかを判断するための交換があるかどうかを記録できますが、選択の並べ替えはできないためです。
    • バブルは安定ソート、値が同じ場合は交換なし、選択は不安定ソート

4. クイックソート

  • 概要

    1. ソートの各ラウンドで、パーティショニングの参照ポイントが選択されます。
      1. 参照点より小さい要素を 1 つのパーティションに入力し、参照点より大きい要素を別のパーティションに入力します。
      2. パーティショニングが完了すると、ピボット要素の位置が最終位置になります。
    2. サブパーティションの要素の数が 1 以下になるまで、つまり並べ替えが完了するまで、サブパーティションで上記のプロセスを繰り返します。これは主に分割統治の考え方を反映しています
  • 分類:

    1. 一方的なループクイックアレンジメント
      1. データム要素として右端の要素を選択します
      2. j ポインタは参照点よりも小さい要素を見つける役割を果たし、要素が見つかると i と交換されます。
      3. i ポインタは、参照点より小さい要素の境界を維持し、各スワップのターゲット インデックスでもあります。
      4. 最後に、基準点を分割位置である i に置き換えます。
    2. 両側サイクルの迅速な調整
      1. 左端の要素をデータム要素として選択します
      2. j ポインタは参照点より小さい要素を右から左に見つける役割を果たし、i ポインタは参照点より大きい要素を左から右に見つける役割を担います。この 2 つが見つかると、i と j になるまで交換されます。交差する。
      3. 最後に参照点を i と交換します (このとき i と j は等しい)。i は分割位置です。
      4. 要点
        1. 基準点は左側にあり、j の後に i が続く必要があります。
        2. while( i < j && a[j] > pv ) j-- // i < jを追加する必要があります
        3. while ( i < j && a[i] <= pv ) i++ //必ず加≤

1.一方的なクイックソートコードの実装

         public static void quick(int[] a,int l,int h){
    
    
             if (l>=h){
    
    
                 return;
             }
     //        p:索引值 ,用于做子分区的左右边界
             int p =partition(a, l, h);
     quick(a, l,p-1); //         左边分区的范围确认
     quick(a, p+1,h); //        右边分区的范围确认
     
         }
     
     /**
          *@Description//TODO单边快排
     *@param:a数组
     *@param:l左边界
     *@param:h右边界
     *@return:int表示基准点元素所在的正确索引,用它确认下一轮分区的边界
     **/
     private static int partition(int[] a,int l,int h){
    
    
     
             int pv = a[h];  //      基准点的值
             int i = l;
             for (int j = l;j < h; j++){
    
    
                 if(a[j] < pv){
    
    
     
                     if (i != j){
    
      //当i和j指向的是同一个元素时,代表没必要交换,
     swap(a,i,j);
                     }
                     i++;
                 }
             }
             if (i != h){
    
    
     swap(a,h,i);
             }
             System.out.println("比较后的值:"+Arrays.toString(a)+" 基准点下标="+i);
             return i;
         }

2.双方向クイックソートコードの実装

           
   	public static void quick(int[] a,int l,int h){
    
    
               if (l>=h){
    
    
                   return;
               }
       //        p:索引值 ,用于做子分区的左右边界
               int p =partition(a, l, h);
       quick(a, l,p-1); //         左边分区的范围确认
       quick(a, p+1,h); //        右边分区的范围确认
       
           }
       
       /**
            *@Description//TODO双边快排
       *@param:a数组
       *@param:l左边界
       *@param:h右边界
       *@return:int表示基准点元素所在的正确索引,用它确认下一轮分区的边界
       **/
       private static int partition(int[] a,int l,int h){
    
    
               int pv = a[l];
               int i = l;
               int j = h;
               while (i < j){
    
    
       //          寻找的顺序也不能更改,必须先执行j从右向左,再执行i从左向右寻找
       
        //            j从右向左找小的 ,必须加i < j 条件,因为不加会出现i走过头,跑到比j大的位置取拿取元素
                   while (i < j && a[j] > pv){
    
    
                       j--;
                   }
       //            i从左向右找大的  ,a[i] <= pv必须加等于,因为a[i]最开始是从左边界开始,就是等于pv
                   while (i < j && a[i] <= pv){
    
    
                       i++;
                   }
       
       swap(a,i,j);
               }
       //          基准点和j交换位置,j代表分区位置
       swap(a,l,j);
               System.out.println(Arrays.toString(a)+" j="+j);
               return j;
       
           }
       ```

おすすめ

転載: blog.csdn.net/weixin_52315708/article/details/131581956