Javaの基本-マージソート、非再帰的クイックソート

1.再帰的なクイックソートの最適化

  • 最初の最適化のアイデア

1.まず、クイックソートは測定単位として1万などの大量のデータを対象とし、次にクイックソートの時間計算量と空間計算量が他のソートと比較して最適であり、次に少量のデータを対象とします。 10、100など、クイックソートは明らかに最善ではありません
。2。次に、ベンチマークを探した後、毎回クイックソートを実行します。これにより、データの順序がゆっくりとなります。今回は、低と高の間のデータがa未満の場合です。与えられた値たとえば、100の場合、挿入ソートとソートの間を呼び出すことができます。

public static void quick(int[] arr,int low,int high){
    
    
        if(low > high){
    
    
            return;
        }
        //第一个优化:当low,high之间的数据个数少于某一个范围,可以调用直接插入排序
        if(high-low+1 < 100){
    
    
            insertsort2(arr,low,high);
            return;
        }
    }
    //用来优化的的直接插入排序
    public static void insertsort2(int[] arr,int low,int high){
    
    
        for(int i = low+1;i <= high;i++) {
    
    
            int tmp = arr[i];
            int j = i-1;
            for (;j >= low;j--){
    
    
                if (arr[j] > tmp) {
    
    
                    arr[j+1] = arr[j];
                }else{
    
    
                    break;
                }
            }
            arr[j+1] = tmp;
        }
    }
   
  • 2番目の最適化

1.たとえば、データのグループ{1、2、3、4、5}の場合、左側に添え字0が付いたデータが参照として使用されるたびに、参照位置が決定されるたびに、極端な状況であり、データはすべて参照にありますベンチマークの位置を決定するために、誰もがそれが決定されるたびにベンチマークの左右にデータを持っているのは良いと思います、または極端なケースは整理されます、次に、両側が次の場合に良いことは明らかです
。2。したがって、各ベンチマークの両側にデータがあることを確認するために、毎回左端の低値と右端の高値を取得し、中央の添え字mid = (high + low)/2。これらの3つの添え字は、データの中央値に対応します。つまり、(arr [mid] <arr [low] <arr [high])であり、最後の低い添え字は中央値です。どちらに関係なくポジションはこれらの3つのポジションで見つかります。ローポジションのExchangeと比較するだけで、ローは常にこの中央値になります。

public static void quick(int[] arr,int low,int high){
    
    
        if(low > high){
    
    
            return;
        }
        }
        //第二次优化,取三个数中位数,这样的基准就不会出现极端情况
        mid(arr,low,high);
        //par是基准
        int par = parttion(arr,low,high);
        quick(arr,low,par-1);
        quick(arr,par+1,high);
    }
    //优化的取三个数字的中位数
    public static void mid(int[] arr,int low,int high){
    
    
        /*int mid = (high+low)/2;
        if (arr[low] < arr[mid] && arr[mid] < arr[high]){
            int tmp = arr[low];
            arr[low] = arr[mid];
            arr[mid] =tmp;
            System.out.println(low);
        }else if(arr[high] > arr[low] && arr[high] < arr[mid]) {
            int tmp = arr[high];
            arr[high] = arr[low];
            arr[low] =tmp;
            System.out.println(low);
        }else{
            System.out.println(low);
            return;
        }*/
        int mid = (low+high)/2;
        //array[mid] < array[low] < array[high]
        if(arr[low] >= arr[high]) {
    
    
            swap(arr,low,high);
        }
        if(arr[low] <= arr[mid]) {
    
    
            swap(arr,low,mid);
        }
        if(arr[mid] >= arr[high]) {
    
    
            swap(arr,low,mid);
        }
    }
  • 両方の最適化のすべてのコード
public static void quicksort(int[] arr){
    
    
        quick(arr,0,arr.length-1);

    }
    //
    public static void quick(int[] arr,int low,int high){
    
    
        if(low > high){
    
    
            return;
        }
        //第一个优化:当low,high之间的数据个数少于某一个范围,可以调用直接插入排序
        if(high-low+1 < 100){
    
    
            insertsort2(arr,low,high);
            return;
        }
        //第二次优化,取三个数中位数,这样的基准就不会出现极端情况
        mid(arr,low,high);
        //par是基准
        int par = parttion(arr,low,high);
        quick(arr,low,par-1);
        quick(arr,par+1,high);
    }
    //用来优化的的直接插入排序
    public static void insertsort2(int[] arr,int low,int high){
    
    
        for(int i = low+1;i <= high;i++) {
    
    
            int tmp = arr[i];
            int j = i-1;
            for (;j >= low;j--){
    
    
                if (arr[j] > tmp) {
    
    
                    arr[j+1] = arr[j];
                }else{
    
    
                    break;
                }
            }
            arr[j+1] = tmp;
        }
    }
    //优化的取三个数字的中位数
    public static void mid(int[] arr,int low,int high){
    
    
        /*int mid = (high+low)/2;
        if (arr[low] < arr[mid] && arr[mid] < arr[high]){
            int tmp = arr[low];
            arr[low] = arr[mid];
            arr[mid] =tmp;
            System.out.println(low);
        }else if(arr[high] > arr[low] && arr[high] < arr[mid]) {
            int tmp = arr[high];
            arr[high] = arr[low];
            arr[low] =tmp;
            System.out.println(low);
        }else{
            System.out.println(low);
            return;
        }*/
        int mid = (low+high)/2;
        //array[mid] < array[low] < array[high]
        if(arr[low] >= arr[high]) {
    
    
            swap(arr,low,high);
        }
        if(arr[low] <= arr[mid]) {
    
    
            swap(arr,low,mid);
        }
        if(arr[mid] >= arr[high]) {
    
    
            swap(arr,low,mid);
        }
    }
    public static void swap(int[] arr,int low,int high){
    
    
        int tmp = arr[low];
        arr[low] = arr[high];
        arr[high] = tmp;
    }
    //划分函数
    public static int parttion(int[] arr,int start,int end){
    
    
        //申请一个tmp空间用来放基准的值(基准一般选择的是边上的值)
        int tmp = arr[start];//这里取的是最左边的值,那么一会就先从最右边开始找[end]位置
        while(start < end){
    
    
            while(start < end && arr[end] >= tmp){
    
    
                end--;
            }
            //到了这里,从while出来有俩种情况,不满足start < end或者不满足arr[end] >= tmp
            if (start >= end){
    
    
                //arr[start] = tmp;
                break;//直接退出,因为已经遍历完毕
            }else{
    
    //else就是arr[end] < tmp
                arr[start] = arr[end];
            }
            while(start < end && arr[start] <= tmp){
    
    
                start++;
            }
            //到了这里,同样从while出来有俩种情况,不满足start < end或者不满足arr[start] <= tmp
            if (start > end){
    
    
                //arr[end] = tmp;
                break;
            }else{
    
    
                arr[end] = arr[start];
            }
        }
        arr[start] = tmp;
        return start;//返回基准移动到位置,这样基准的左边全小于它,右边全都大于它
    }


2.非再帰的クイックソート

  • アイデア

1.スタックを使用して非再帰的な高速ソートを実現します
。2。次のデータセットでは、参照位置が初めて見つかった後、赤い四角(添え字番号5)があり
ます。3。再帰用。 、左側を残しておく必要がありますか?右側の基準位置と同じ基準位置の決定を実行します。次に、非再帰の場合、左側のデータの下位(No. 0)添え字を最初にスタックに入れ、ハイ(No. 4)をスタックに入れ、新しいロー(6)をスタックに押し込み、新しいハイ(9)をスタックに押し込みます
。4。次に、最初にスタックが空かどうかを判断します。空でない場合は、空の場合、スタックの一番上を高に、2番目を低に割り当て、位置を見つけます。次に、スタックに押し込み、スタックから飛び出し、スタックが空になるまで配置します。

ここに画像の説明を挿入

//非递归实现快速排序(需要一个栈)
    public static void quicksort1(int[] arr){
    
    
        Norquick(arr,0,arr.length-1);

    }
    //非递归实现快速排序
    public static void Norquick(int[] arr,int low,int high){
    
    
        Stack<Integer> stack = new Stack<>();
        int par = parttion(arr,low,high);
        if (par > low+1){
    
    
            stack.push(low);
            stack.push(par-1);
        }
        if (par < high-1){
    
    
            stack.push(par+1);
            stack.push(high);
        }
        while(!stack.empty()){
    
    
            int end = stack.pop();
            int start = stack.pop();
            par = parttion(arr,start,end);
            if (par > start+1){
    
    
                stack.push(start);
                stack.push(par-1);
            }
            if (par < end-1){
    
    
                stack.push(par+1);
                stack.push(end);
            }
        }
    }

3.マージソート

  • 次の図に示すように、最初に分割してからマージします。
    ここに画像の説明を挿入
//归并排序
    public static void mergesort(int[] arr){
    
    
        mergeSortInternal(arr,0,arr.length-1);
    }
    //分割
    public static void mergeSortInternal(int[] arr,int low,int high){
    
    
        if(low >= high){
    
    
            return;
        }
        int mid = (low + high)/2;
        mergeSortInternal(arr,low,mid);
        mergeSortInternal(arr,mid+1,high);
        //分割完成,就开始合并

        merge(arr,low,high,mid);
    }
    //合并方法
    public static void merge(int[] arr,int low,int high,int mid){
    
    
        int s1 = low;
        int s2 = mid+1;
        int[] tmp = new int[high-low+1];//临时存放合并后的数据
        //开始合并
        int k = 0;//tmp开始下标
        while(s1 <= mid && s2 <= high){
    
    //俩个段都有数据
            if (arr[s1] <= arr[s2]){
    
    
                tmp[k++] = arr[s1++];

            }else{
    
    
                tmp[k++] = arr[s2++];
            }
        }
        //第一个归并段还有很多数据
        while(s1 <= mid){
    
    //第一个还有若干个数据
            tmp[k++] = arr[s1++];
        }
        while(s2 <= high){
    
    //第二个还有若干个数据
            tmp[k++] = arr[s2++];
        }
        //此时就有序了
        for (int i = 0;i < tmp.length;i++){
    
    
            arr[low+i] = tmp[i];//将临时存放好的数据给人家放回去;
        }
    }

おすすめ

転載: blog.csdn.net/qq_45665172/article/details/109678230