1. 程式人生 > >java——快速排序基準位置的選取方法和快速排序優化

java——快速排序基準位置的選取方法和快速排序優化

一、 快速排序基準位置的選取方法

1.固定位置法(就是選取的基準是固定的、一般是陣列的第一個元素)

2.隨機選取基準法

import java.util.Arrays;
import java.util.Random;

/**
 * @ClassName TestDemo3
 * @Description 快速排序,隨機選取基準法
 * @Author lzq
 * @Date 2018/10/27 22:24
 * @Version 1.0
 **/
public class TestDemo3 {
    public static void main(String[] args) {
        int[] array = {20,1,9,56,25,32,87,45,12,69,54,19,98,51,25};
        quickSort(array);
        System.out.println(Arrays.toString(array));

    }

    /**
     * 快速排序
     * @param array
     */
    public static void quickSort(int[] array) {
        sort(array,0,array.length-1);
    }

    /**
     * 遞迴的排序資料
     * @param array
     * @param start
     * @param end
     */
    private static void sort(int[] array,int start,int end) {
        Random random = new Random();
        int x = random.nextInt(end-start+1)+start;//產生在start到end之間的隨機數
        swap(array,start,x);
        int once = once(array,start,end);
        if(once > start) {  //排序左邊
            sort(array,start,once-1);
        }
        if(once < end) {  //排序右邊
            sort(array,once+1,end);
        }
    }

    /**
     * 一趟快速排序
     * @param array
     * @param start
     * @param end
     * @return
     */
    private static int once(int[] array,int start,int end) {
        int tmp = array[start];
        while(start < end) {
            while(start < end && array[end] >= tmp) {
                --end;
            }
            array[start] =  array[end];  //把比基準tmp大的數字移到後面

            while(start < end && array[start] <= tmp) {
                ++start;
            }
            array[end] = array[start];  //把比基準tmp小的數字移到前面
        }
        array[start] = tmp;   //把基準放在它該放的位置
        return start;    //返回基準的位置,後面分別在這個基準位置兩邊重複上面的操作
    }

    /**
     * 將產生的隨機下標與start下標交換,而這個新的array[start]會為成為後面比較
     * 的基準,這樣就達到了,隨機選取基準的目的
     * @param array
     * @param start
     * @param end
     */
    public static void swap(int[] array,int start,int end) {
        int tmp = array[start];
        array[start] = array[end];
        array[end] = tmp;
    }

}

3、三分取基準

這個方法也就是把陣列的第一個最後一個和最中間的元素拿出來比較一下,把三者中最大的放最後面,最小的放最前面,剩下那個元素放中間。然後在用遞迴的方式比較排序:

import java.util.Arrays;
/**
 * @ClassName TestDemo4
 * @Description 快速排序——三分取基法
 * @Author lzq
 * @Date 2018/10/27 22:33
 * @Version 1.0
 **/
public class TestDemo4 {
    public static void main(String[] args) {
        int[] array = {20,23,98,54,12,15,0,19,3,46,78,59,84};
        System.out.println("原陣列"+Arrays.toString(array));
        quickSort(array);
        System.out.println("排序後陣列:"+Arrays.toString(array));

    }
    /**
     * 快速排序
     * @param array
     */
    public static void quickSort(int[] array) {
        new_Sort(array,0,array.length-1);
    }

    /**
     * 三分取基
     * @param array
     * @param start
     * @param end
     */
    public static void new_Sort(int[] array,int start,int end) {
        int mid = start+(end-start)>>>1;
        if(array[start] > array[end])  {//目標  array[end] >= array[start]
            swap(array,start,end);
        }
        if(array[start] > array[mid]) { // mid > start
            swap(array,start,mid);
        }
        if(array[end] < array[mid]) {   //end< mid
            swap(array,end,mid);
        }
        System.out.println("經過new_Sort()後陣列"+Arrays.toString(array));
        sort(array,0,array.length-1);
    }

    /**
     * 交換陣列元素位置
     * @param array
     * @param start
     * @param end
     */
    public static void swap(int [] array ,int start,int end) {
        int temp = array[start];
        array[start] = array[end];
        array[end] = temp;
    }

    /**
     * 遞迴的排序資料
     * @param array
     * @param start
     * @param end
     */
    private static void sort(int[] array,int start,int end) {
        int once = once(array,start,end);
        if(once > start) {  //排序左邊
            sort(array,start,once-1);
        }
        if(once < end) {  //排序右邊
            sort(array,once+1,end);
        }
    }

    /**
     * 一趟快速排序
     * @param array
     * @param start
     * @param end
     * @return
     */
    private static int once(int[] array,int start,int end) {
        int tmp = array[start];
        while(start < end) {
            while(start < end && array[end] >= tmp) {
                --end;
            }
            array[start] =  array[end];  //把比基準tmp大的數字移到後面

            while(start < end && array[start] <= tmp) {
                ++start;
            }
            array[end] = array[start];  //把比基準tmp小的數字移到前面
        }
        array[start] = tmp;   //把基準放在它該放的位置
        return start;        //返回基準的位置,後面分別在這個基準位置兩邊重複上面的操作
    }


}

執行結果:

原陣列[20, 23, 98, 54, 12, 15, 0, 19, 3, 46, 78, 59, 84]
經過new_Sort()後陣列[0, 23, 98, 54, 12, 15, 20, 19, 3, 46, 78, 59, 84]
排序後陣列:[0, 3, 12, 15, 19, 20, 23, 46, 54, 59, 78, 84, 98]

二、優化:

1、當待排序陣列當中資料比較少的時候,用直插

import java.util.Arrays;

/**
 * @ClassName TestDemo1
 * @Description 快速排序的優化
 * @Author lzq
 * @Date 2018/10/27 21:24
 * @Version 1.0
 **/
public class TestDemo1 {
    public static void main(String[] args) {
        int[] array = {20,1,9,56,25,32,87,45,12,69,54,19};
        System.out.println(Arrays.toString(array));
        quickSort(array);
        System.out.println(Arrays.toString(array));

    }

    /**
     * 快速排序
     * @param array
     */
    public static void quickSort(int[] array) {
        sort(array,0,array.length-1);
    }

    /**
     * 遞迴的排序資料
     * @param array
     * @param start
     * @param end
     */
    private static void sort(int[] array,int start,int end) {
        if(array.length <= 100) {
            insert_Sort(array); //資料不多,用直接插入排序
        }else {
            int once = once(array, start, end);
            if (once > start) {  //排序左邊
                sort(array, start, once - 1);
            }
            if (once < end) {  //排序右邊
                sort(array, once + 1, end);
            }
        }
    }

    /**
     * 直接插入排序
     * @param array
     */
    public static void insert_Sort(int[] array) {
        int tmp = 0,j;
        for(int i = 1;i < array.length;i++) {
            tmp = array[i];
            for(j = i-1;j >= 0;j--) {
                if(array[j] > tmp) {
                    array[j+1] = array[j];  //比tmp大的向後移
                }else {
                    //直到找到比tmp小的停止比較,因為每次比較前面的已經有序,只是在找tmp應該插入的位置
                    break;
                }
            }
            array[j+1] = tmp;  //把tmp放到當前已排序的資料中應該放入的位置
        }
    }

    /**
     * 一趟快速排序
     * @param array
     * @param start
     * @param end
     * @return
     */
    private static int once(int[] array,int start,int end) {
        int tmp = array[start];
        while(start < end) {
            while(start < end && array[end] >= tmp) {
                --end;
            }
            array[start] =  array[end];  //把比基準tmp大的數字移到後面

            while(start < end && array[start] <= tmp) {
                ++start;
            }
            array[end] = array[start];  //把比基準tmp小的數字移到前面
        }
        array[start] = tmp;   //把基準放在它該放的位置
        return start;        //返回基準的位置,後面分別在這個基準位置兩邊重複上面的操作
    }
}

2、聚集相同元素法(基準一樣的元素),減少遞迴的次數

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

當i和j遍歷完後,也就把相同元素聚集完了,

這時,下一次基準的左右兩邊再次遞迴排序時,就不是這次基準位置的兩邊了,而是left的左邊和right的右邊。

import java.util.Arrays;

/**
 * @ClassName TestDemo5
 * @Description 聚集相同元素法
 * @Author lzq
 * @Date 2018/10/27 22:47
 * @Version 1.0
 **/
public class TestDemo5 {
    public static void main(String[] args) {
        int[] array = {20,20,20,56,25,32,87,45,12,69,54,20,98,51,25};
        quickSort(array);
        System.out.println(Arrays.toString(array));

    }

    /**
     * 快速排序
     * @param array
     */
    public static void quickSort(int[] array) {
        sort(array,0,array.length-1);
    }

    /**
     * 一趟快速排序
     * @param array
     * @param start
     * @param end
     * @return
     */
    private static int once(int[] array,int start,int end) {
        int tmp = array[start];
        while(start < end) {
            while(start < end && array[end] >= tmp) {
                --end;
            }
            array[start] =  array[end];  //把比基準tmp大的數字移到後面

            while(start < end && array[start] <= tmp) {
                ++start;
            }
            array[end] = array[start];  //把比基準tmp小的數字移到前面
        }
        array[start] = tmp;   //把基準放在它該放的位置
        return start;        //返回基準的位置,後面分別在這個基準位置兩邊重複上面的操作
    }

    /**
     * 遞迴的排序資料
     * @param array
     * @param start
     * @param end
     */
    private static void sort(int[] array,int start,int end) {
        int once = once(array, start, end);
        int left = once-1;
        int right = once+1;
        int[] brray = focus(array,start,end,once);
        left = brray[0];
        right = brray[1];
        if(once > start+1) {
            sort(array,start,left);
        }
        if(once < end-1) {
            sort(array,right,end);
        }
    }


    /**
     * 聚集相同元素
     * @param array
     * @param start
     * @param end
     * @param par
     * @return
     */
    public static int[] focus(int[] array, int start, int end, int par) {
        //查詢的範圍
        int left = par-1;
        int right = par+1;
        //交換的指引變數
        int parLeft = par-1;
        int parRight = par+1;
        //左邊找
        for (int i = left; i >=start; i--) {
            if(array[i] == array[par]) {
                if(i != parLeft) {
                    swap(array, parLeft, i);
                    parLeft--;
                }
                else {
                    parLeft--;
                }
            }
        }
        //右邊找
        for (int i = right; i <=end; i++) {
            if(array[i] == array[par]) {
                if(i != parRight) {
                    swap(array, parRight, i);
                    parRight++;
                }
                else{
                    parRight++;
                }
            }
        }
        return new int[] {parLeft,parRight};
    }

    /**
     * 交換元素位置
     * @param array
     * @param start
     * @param end
     */
    public static void swap(int [] array ,int start,int end){
        int temp = array[start];
        array[start] = array[end];
        array[end] = temp;
    }

}

執行結果:

[12, 20, 20, 20, 20, 25, 25, 32, 45, 51, 54, 56, 69, 87, 98]