亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

一、什么是快速排序

快速排序是由冒泡排序演變而來,比冒泡排序更快的排序算法。之所以快,是因為快速排序用了分治法。

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

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

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

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

二、基準元素的選擇

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

1、選擇第一個元素

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


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

2、隨機選擇

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

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

三、元素的交換

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

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

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

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

首先,選定基準元素p,并設置左右兩個指針 l 和 r

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

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

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

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

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

import java.util.Arrays;

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

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

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

        while(l!=r){
            //控制右指針向左移動,找到小于基準元素的那個數(shù)
            while((l<r)&&(arr[r]>p)){
                r--;
            }
            //控制左指針向右移動,找到大于基準元素的那個數(shù)
            while((l<r)&&(arr[l]<=p)){
                l++;
            }
            //交換l指針和r指針所指的元素
            if(l<r){
                int tmp=arr[l];
                arr[l]=arr[r];
                arr[r]=tmp;
            }
        }

        //交換基準元素和重合點的元素
        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ù)列的一邊對元素進行遍歷交換。

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

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

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

/*
     * 分治法(單邊循環(huán)法)
     * arr  待排序數(shù)組
     * startIndex  起始下標
     * endIndex  結束下標
     * */
    public static int partition(int arr[],int startIndex,int endIndex)
    {
        int p=arr[startIndex];//基準元素(可取隨機位置)
        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;
            }
        }

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

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

附:算法優(yōu)化

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

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

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

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

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

    private static int minThree(int[]array,int left,int right) {
        //三數(shù)取中法,優(yōu)化遞歸實現(xiàn)的快速排序
        //使得最壞情況時,快速排序變?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ù)據(jù)量大時就像二叉樹一樣,每一組數(shù)據(jù)往下走一層都會被分成兩組,而到了最后幾層,則會因為數(shù)據(jù)量的龐大而被分成許多組進行遞歸,此時的遞歸開銷就會很大,很有可能導致~~棧溢出~~,

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

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

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

此時即便是一開始就用直接插入排序,時間也會相差無幾。

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

public class QuickSort {
    /**
     * 快速排序
     * 時間復雜度:代碼未優(yōu)化時:最好情況(滿二叉樹或完全二叉樹):O(n*logn),    最壞情況(順序和逆序時):O(n^2)
     * 空間復雜度:代碼未優(yōu)化時:最好情況(滿二叉樹或完全二叉樹):O(logn),    最壞情況(順序和逆序時):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ù)量小,使用直接插入排序
        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下標挖一個坑
        int tmp = array[left];
        while (left < right) {
            // 讓right下標去找比tmp小的數(shù)
            while (right > left && array[right] >= tmp) {
                right--;
            }
            // 填left下標的坑,此時right下標變成一個坑了
            array[left] = array[right];
            // 讓left下標去找比tmp大的數(shù)
            while (left < right && array[left] <= tmp) {
                left++;
            }
            // 填right下標的坑,此時left下標變成一個坑了
            array[right] = array[left];
        }
        // 將基準值放到合適的位置
        array[left] = tmp;
        // 返回基準下標
        return left;
    }

    //Hoare法
    private static int partition3(int[] array,int left,int right) {
        // 基準值
        int tmp = array[left];
        // 基準下標
        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這兩個數(shù)進行交換
            swap(array,left,right);
        }
        // 將基準值放到合適的位置
        swap(array,index,right);
        // 返回基準下標
        return right;
    }


    //前后指針法
    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)化遞歸實現(xiàn)的快速排序
        //使得最壞情況時,快速排序變?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;
    }

}

總結

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

相關文章

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

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

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

    如何使用Jenkins構建GIT+Maven項目

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

    Java enum 對枚舉元素的賦值和取值方式

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

    JNI語言基本知識

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

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

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

    你真的理解Java中的ArrayList嗎

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

    Mybatis-Plus自動生成的數(shù)據(jù)庫id過長的解決

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

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

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

    Java老矣 尚能飯否?

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

    Tomcat報錯:HTTP Status 500 (Wrapper cannot find servlet class)

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

最新評論