欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java經(jīng)典算法之快速排序詳解

 更新時(shí)間:2024年07月06日 09:04:52   作者:zcx-yyds  
這篇文章主要給大家介紹了關(guān)于Java經(jīng)典算法之快速排序的相關(guān)資料,需快速排序是一種分治法的排序算法,其基本思想是通過(guò)一趟排序?qū)⒋判虻臄?shù)據(jù)分割成獨(dú)立的兩部分,其中一部分的所有元素均比另一部分的元素小,然后分別對(duì)這兩部分繼續(xù)進(jìn)行排序,需要的朋友可以參考下

一、什么是快速排序

快速排序是由冒泡排序演變而來(lái),比冒泡排序更快的排序算法。之所以快,是因?yàn)榭焖倥判蛴昧?strong>分治法。

相同的是,與冒泡排序一樣,快速排序也屬于交換排序,通過(guò)元素之間的比較和交換來(lái)排序。

不同的是,冒泡排序每一輪只把一個(gè)元素冒泡到數(shù)列的一端,而快速排序每輪挑選一個(gè)基準(zhǔn)元素,讓比它小的元素移動(dòng)到一端,讓比它大的元素移動(dòng)到另一端,從而把數(shù)列拆解成兩個(gè)部分。

這種每次將數(shù)列分為兩個(gè)部分的方法就叫做分治法。

分治法的好處體現(xiàn)在相比于冒泡排序它會(huì)有更小的時(shí)間復(fù)雜度,對(duì)于n的元素的冒泡排序時(shí)間復(fù)雜度為O(n2),而快速排序總體的平均時(shí)間復(fù)雜度為O(nlogn)。

二、基準(zhǔn)元素的選擇

在使用分治法的過(guò)程中以基準(zhǔn)元素為中心把其他元素移到它的兩邊,那么如何選擇基準(zhǔn)元素呢?

1、選擇第一個(gè)元素

最簡(jiǎn)單的方法是直接選擇數(shù)列第一個(gè)元素為基準(zhǔn)元素,但是這種方法在有些特殊情況下會(huì)出現(xiàn)問(wèn)題:


對(duì)于這種原本是逆序的數(shù)列,每輪都只能確定基準(zhǔn)元素的位置,這種情況下快速排序需要進(jìn)行n輪,時(shí)間復(fù)雜度退化到了O(n2)

2、隨機(jī)選擇

為了解決時(shí)間復(fù)雜度過(guò)大的情況,我們可以隨機(jī)選擇一個(gè)元素,并與首位元素互換位置,雖然這種情況下也有幾率選到數(shù)列的最大或最小值,但大多情況下都是可以的。

所以,雖然快速排序的平均時(shí)間復(fù)雜度為O(nlogn),但最壞情況下也可以達(dá)到O(n2)。

三、元素的交換

選好了基準(zhǔn)元素,就要將其他元素移到基準(zhǔn)元素兩邊,具體實(shí)現(xiàn)有兩種方法:

  • 雙邊循環(huán)法
  • 單邊循環(huán)法

1、雙邊循環(huán)法

對(duì)以下數(shù)列按從小到大進(jìn)行排序:

首先,選定基準(zhǔn)元素p,并設(shè)置左右兩個(gè)指針 l 和 r

開(kāi)始循環(huán)后,從r指針開(kāi)始,讓r指針元素與基準(zhǔn)元素做比較,如果大于等于p,則r指針向左移動(dòng);如果小于p,則停止移動(dòng),換到l指針。

對(duì)于當(dāng)前數(shù)列,r指針元素為1,1<4,所以r指針停止移動(dòng),換到l指針。

換到l指針后,讓l指針元素與基準(zhǔn)元素做比較,如果小于等于p,則l指針向右移動(dòng);如果大于p,則停止移動(dòng)。

按照此思路,后續(xù)步驟如下:

實(shí)現(xiàn)代碼如下:

import java.util.Arrays;

public class quickSort {
    public static void quickSort(int arr[],int startIndex,int endIndex){
        //遞歸結(jié)束條件為startIndex大于或等于endIndex
        if(startIndex>=endIndex){
            return;
        }

        //得到基準(zhǔn)元素位置
        int pIndex=partition(arr,startIndex,endIndex);

        //根據(jù)基準(zhǔn)元素分兩部分進(jìn)行遞歸排序
        quickSort(arr,startIndex,pIndex-1);
        quickSort(arr,pIndex+1,endIndex);
    }
    /*
    * 分治法(雙邊循環(huán)法)
    * arr  待排序數(shù)組
    * startIndex  起始下標(biāo)
    * endIndex  結(jié)束下標(biāo)
    * */
    public static int partition(int arr[],int startIndex,int endIndex)
    {
        int p=arr[startIndex];//基準(zhǔn)元素(可取隨機(jī)位置)
        int l=startIndex;//左指針
        int r=endIndex;//右指針

        while(l!=r){
            //控制右指針向左移動(dòng),找到小于基準(zhǔn)元素的那個(gè)數(shù)
            while((l<r)&&(arr[r]>p)){
                r--;
            }
            //控制左指針向右移動(dòng),找到大于基準(zhǔn)元素的那個(gè)數(shù)
            while((l<r)&&(arr[l]<=p)){
                l++;
            }
            //交換l指針和r指針?biāo)傅脑?
            if(l<r){
                int tmp=arr[l];
                arr[l]=arr[r];
                arr[r]=tmp;
            }
        }

        //交換基準(zhǔn)元素和重合點(diǎn)的元素
        arr[startIndex]=arr[l];
        arr[l]=p;
        return l;
    }

    public static void main(String[] args) {
        int arr[]={4,7,6,5,3,2,8,1};
        quickSort(arr,0,7);
        System.out.println(Arrays.toString(arr));
    }
}

2、單邊循環(huán)法

雙邊循環(huán)更加直觀,但代碼比較麻煩,而單邊循環(huán)法從數(shù)列的一邊對(duì)元素進(jìn)行遍歷交換。

開(kāi)始循環(huán)選定基準(zhǔn)元素p,再設(shè)置一個(gè)mark指針指向數(shù)列起始位置,mark代表著小于基準(zhǔn)元素區(qū)域的右邊界。

從基準(zhǔn)元素的下一位開(kāi)始遍歷,若元素大于基準(zhǔn)元素,則繼續(xù)往后遍歷。如果小于基準(zhǔn)元素,先將mark指針右移一位,然后將最新遍歷的元素與基準(zhǔn)元素交換。

單邊循環(huán)法與雙邊循環(huán)法主要是partition函數(shù)的實(shí)現(xiàn)不一樣

/*
     * 分治法(單邊循環(huán)法)
     * arr  待排序數(shù)組
     * startIndex  起始下標(biāo)
     * endIndex  結(jié)束下標(biāo)
     * */
    public static int partition(int arr[],int startIndex,int endIndex)
    {
        int p=arr[startIndex];//基準(zhǔn)元素(可取隨機(jī)位置)
        int mark=startIndex;

        for(int i=startIndex+1;i<=endIndex;i++){
            if(arr[i]<arr[mark]){
                mark++;
                int tmp=arr[mark];
                arr[mark]=arr[i];
                arr[i]=tmp;
            }
        }

        //交換基準(zhǔn)元素和mark指針的元素
        arr[startIndex]=arr[mark];
        arr[mark]=p;
        return mark;
    }

可以看出,單邊循環(huán)法只需要一個(gè)循環(huán)即可,比雙邊循環(huán)法要簡(jiǎn)單很多。

附:算法優(yōu)化

快速排序在最壞情況下,時(shí)間復(fù)雜度竟然達(dá)到了O(n2),這哪里快速啊,所以下面就要進(jìn)行優(yōu)化了。

優(yōu)化基準(zhǔn)的選取

共有兩種方案: 1??隨機(jī)選取基準(zhǔn)法,這要是倒霉起來(lái),依然有可能會(huì)次次都隨機(jī)選到最極端最壞的情況,所以這個(gè)不用。 2??三數(shù)取中法,這個(gè)可以保證不會(huì)讓你選到最極端最壞的情況。

三數(shù)取中法:在上面的算法中,我們的基準(zhǔn)選取的都是left下標(biāo),
而三數(shù)取中指的是在left,right,mid( (left + right)/2 )這三個(gè)下標(biāo)在中選取一個(gè)中間值作為基準(zhǔn),不是最大也不是最小,就保證了不會(huì)出現(xiàn)極端情況。

出現(xiàn)了以上的最壞情況,也就是讓快速排序變成了二分查找。

    private static int minThree(int[]array,int left,int right) {
        //三數(shù)取中法,優(yōu)化遞歸實(shí)現(xiàn)的快速排序
        //使得最壞情況時(shí),快速排序變?yōu)槎植檎?
        int mid = (left+right)/2;
        if(array[right] > array[left]) {
            int tmp = array[left];
            array[left] = array[right];
            array[right] = tmp;
        }
        if(array[mid] > array[left]) {
            return left;
        }
        if(array[mid] > array[right]) {
            return mid;
        }
        return right;
    }

優(yōu)化少量數(shù)據(jù)時(shí)的排序方案

數(shù)據(jù)量大時(shí)就像二叉樹(shù)一樣,每一組數(shù)據(jù)往下走一層都會(huì)被分成兩組,而到了最后幾層,則會(huì)因?yàn)閿?shù)據(jù)量的龐大而被分成許多組進(jìn)行遞歸,此時(shí)的遞歸開(kāi)銷(xiāo)就會(huì)很大,很有可能導(dǎo)致~~棧溢出~~,

因此我們可以設(shè)定一個(gè)數(shù)量閘口,當(dāng)每組的數(shù)據(jù)小的到了這個(gè)閘口,就采用比較簡(jiǎn)單的直接插入排序。

而且在快速排序的不斷遞歸下,數(shù)據(jù)一定是越來(lái)越有序的,直接插入排序的效率也會(huì)更高。

數(shù)據(jù)小時(shí)

此時(shí)即便是一開(kāi)始就用直接插入排序,時(shí)間也會(huì)相差無(wú)幾。

優(yōu)化后的完整代碼

public class QuickSort {
    /**
     * 快速排序
     * 時(shí)間復(fù)雜度:代碼未優(yōu)化時(shí):最好情況(滿二叉樹(shù)或完全二叉樹(shù)):O(n*logn),    最壞情況(順序和逆序時(shí)):O(n^2)
     * 空間復(fù)雜度:代碼未優(yōu)化時(shí):最好情況(滿二叉樹(shù)或完全二叉樹(shù)):O(logn),    最壞情況(順序和逆序時(shí)):O(n)
     * 穩(wěn)定性:不穩(wěn)定
     * @param array
     */

    public static void quickSort(int[] array) {
        quick(array,0, array.length-1);
    }
    private static void quick(int[] array,int left,int right) {
        if(left >= right) {
            return;
        }
        // 設(shè)置數(shù)量閘口,
        // 數(shù)量小,使用直接插入排序
        if(right - left + 1 < 14) {
            InsertSort(array);
            return;
        }
        
        // 將三數(shù)取中法取得的中間值換到left處
        swap(array,minThree(array,left,right),left);
        int piovt = partition(array,left,right);

        quick(array, left, piovt-1);
        quick(array,piovt+1,right);
    }

    //挖坑法
    private static int partition(int[] array,int left,int right) {
        // 在left下標(biāo)挖一個(gè)坑
        int tmp = array[left];
        while (left < right) {
            // 讓right下標(biāo)去找比tmp小的數(shù)
            while (right > left && array[right] >= tmp) {
                right--;
            }
            // 填left下標(biāo)的坑,此時(shí)right下標(biāo)變成一個(gè)坑了
            array[left] = array[right];
            // 讓left下標(biāo)去找比tmp大的數(shù)
            while (left < right && array[left] <= tmp) {
                left++;
            }
            // 填right下標(biāo)的坑,此時(shí)left下標(biāo)變成一個(gè)坑了
            array[right] = array[left];
        }
        // 將基準(zhǔn)值放到合適的位置
        array[left] = tmp;
        // 返回基準(zhǔn)下標(biāo)
        return left;
    }

    //Hoare法
    private static int partition3(int[] array,int left,int right) {
        // 基準(zhǔn)值
        int tmp = array[left];
        // 基準(zhǔn)下標(biāo)
        int index = left;
        while (left < right) {
            // 讓right找比tmp小的數(shù)
            while (right > left && array[right] >= tmp) {
                right--;
            }
            // 讓left找比tmp大的數(shù)
            while (left < right && array[left] <= tmp) {
                left++;
            }
            // 讓left與right這兩個(gè)數(shù)進(jìn)行交換
            swap(array,left,right);
        }
        // 將基準(zhǔn)值放到合適的位置
        swap(array,index,right);
        // 返回基準(zhǔn)下標(biāo)
        return right;
    }


    //前后指針?lè)?
    private static int partition2(int[] array, int left, int right) {
        int prev = left ;
        int cur = left+1;
        while (cur <= right) {
            if(array[cur] < array[left] && array[++prev] != array[cur]) {
                swap(array,cur,prev);
            }
            cur++;
        }
        swap(array,prev,left);
        return prev;
    }

    private static int minThree(int[]array,int left,int right) {
        //三數(shù)取中法,優(yōu)化遞歸實(shí)現(xiàn)的快速排序
        //使得最壞情況時(shí),快速排序變?yōu)槎植檎?
        int mid = (left+right)/2;
        if(array[right] > array[left]) {
            int tmp = array[left];
            array[left] = array[right];
            array[right] = tmp;
        }
        if(array[mid] > array[left]) {
            return left;
        }
        if(array[mid] > array[right]) {
            return mid;
        }
        return right;
    }

    private static void swap(int[] array,int a,int b) {
        int tmp = array[a];
        array[a] = array[b];
        array[b] = tmp;
    }

}

總結(jié)

到此這篇關(guān)于Java經(jīng)典算法之快速排序詳解的文章就介紹到這了,更多相關(guān)Java快速排序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot數(shù)據(jù)訪問(wèn)和數(shù)據(jù)視圖的使用方式詳解

    springboot數(shù)據(jù)訪問(wèn)和數(shù)據(jù)視圖的使用方式詳解

    這篇文章主要為大家介紹了springboot數(shù)據(jù)訪問(wèn)和數(shù)據(jù)視圖的使用方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • 如何使用Jenkins構(gòu)建GIT+Maven項(xiàng)目

    如何使用Jenkins構(gòu)建GIT+Maven項(xiàng)目

    這篇文章主要介紹了如何使用Jenkins構(gòu)建GIT+Maven項(xiàng)目,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java enum 對(duì)枚舉元素的賦值和取值方式

    Java enum 對(duì)枚舉元素的賦值和取值方式

    這篇文章主要介紹了Java enum 對(duì)枚舉元素的賦值和取值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • JNI語(yǔ)言基本知識(shí)

    JNI語(yǔ)言基本知識(shí)

    JNI是Java Native Interface的縮寫(xiě),它提供了若干的API實(shí)現(xiàn)了Java和其他語(yǔ)言的通信(主要是C&C++)。接下來(lái)通過(guò)本文給大家分享jni 基礎(chǔ)知識(shí),感興趣的朋友一起看看吧
    2017-10-10
  • java List出現(xiàn)All elements are null問(wèn)題及解決

    java List出現(xiàn)All elements are null問(wèn)題及解決

    這篇文章主要介紹了java List出現(xiàn)All elements are null問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-11-11
  • 你真的理解Java中的ArrayList嗎

    你真的理解Java中的ArrayList嗎

    這篇文章主要給大家介紹了關(guān)于Java中ArrayList的相關(guān)資料,ArrayList就是傳說(shuō)中的動(dòng)態(tài)數(shù)組,用MSDN中的說(shuō)法,就是Array的復(fù)雜版本,需要的朋友可以參考下
    2021-08-08
  • Mybatis-Plus自動(dòng)生成的數(shù)據(jù)庫(kù)id過(guò)長(zhǎng)的解決

    Mybatis-Plus自動(dòng)生成的數(shù)據(jù)庫(kù)id過(guò)長(zhǎng)的解決

    這篇文章主要介紹了Mybatis-Plus自動(dòng)生成的數(shù)據(jù)庫(kù)id過(guò)長(zhǎng)的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • mybatis中映射文件(mapper)中的使用規(guī)則

    mybatis中映射文件(mapper)中的使用規(guī)則

    這篇文章主要介紹了mybatis中映射文件(mapper)中的使用規(guī)則,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java老矣 尚能飯否?

    Java老矣 尚能飯否?

    Java老矣,尚能飯否?各類(lèi)編程語(yǔ)言橫空出世,紛戰(zhàn)不休,然而 TIOBE 的語(yǔ)言排行榜上,Java 卻露出了明顯的頹勢(shì)。這個(gè)老牌的語(yǔ)言,未來(lái)會(huì)是怎樣?
    2017-06-06
  • Tomcat報(bào)錯(cuò):HTTP Status 500 (Wrapper cannot find servlet class)解決辦法

    Tomcat報(bào)錯(cuò):HTTP Status 500 (Wrapper cannot find servlet class)

    這篇文章主要介紹了Tomcat報(bào)錯(cuò):HTTP Status 500 (Wrapper cannot find servlet class)解決辦法的相關(guān)資料,需要的朋友可以參考下
    2016-11-11

最新評(píng)論