排序算法學(xué)習(xí)筆記(nlogn部分)

歸并排序

自頂向下進(jìn)行歸并排序(方法1)

注意:
1.對(duì)于已經(jīng)有序的數(shù)組,插入排序的效率要高于歸并排序

    // 將arr[l...mid]和arr[mid+1...r]兩部分進(jìn)行歸并
    private static void merge(Comparable[] arr, int l, int mid, int r) {

        Comparable[] aux = Arrays.copyOfRange(arr, l, r+1);

        // 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
        int i = l, j = mid+1;
        for( int k = l ; k <= r; k ++ ){

            if( i > mid ){  // 如果左半部分元素已經(jīng)全部處理完畢
                arr[k] = aux[j-l]; j ++;
            }
            else if( j > r ){   // 如果右半部分元素已經(jīng)全部處理完畢
                arr[k] = aux[i-l]; i ++;
            }
            else if( aux[i-l].compareTo(aux[j-l]) < 0 ){  // 左半部分所指元素 < 右半部分所指元素
                arr[k] = aux[i-l]; i ++;
            }
            else{  // 左半部分所指元素 >= 右半部分所指元素
                arr[k] = aux[j-l]; j ++;
            }
        }
    }

    // 遞歸使用歸并排序,對(duì)arr[l...r]的范圍進(jìn)行排序
    private static void sort(Comparable[] arr, int l, int r) {

        //優(yōu)化2:對(duì)于元素個(gè)數(shù)較少的時(shí)候,可以進(jìn)行        
        //插入排序的操作,將會(huì)節(jié)省更多的時(shí)間
        //nlogn算法前面都會(huì)有一定的系數(shù),在元素個(gè)  
        //數(shù)較少的時(shí)候,插入排序相比于歸并排序要 
        //更快
        //優(yōu)化后的代碼
        //if(r-l <= 15)
        //     insertSort(arr,l,r);
        if (l >= r)
            return;

        int mid = (l+r)/2;
        sort(arr, l, mid);
        sort(arr, mid + 1, r);
        //優(yōu)化1:
        //在merge之前,需要判斷arr[mid]>arr[mid+1]`    
        //是否成立,如果不成立,那么已經(jīng)有序,就不需 
        //要進(jìn)行歸并操作
        //優(yōu)化下面的代碼改成
        //if(arr[mid] > arr[mid+1])
        //      merge(arr,l,mid,r);

        merge(arr, l, mid, r);
    }

    public static void sort(Comparable[] arr){

        int n = arr.length;
        sort(arr, 0, n-1);
    }
自底向上的歸并排序(方法2)

由于沒(méi)有使用數(shù)組下標(biāo)直接獲取數(shù)組中的元素,所以可以對(duì)鏈表實(shí)現(xiàn)歸并排序

    //本函數(shù)跟上面的一樣的
    // 將arr[l...mid]和arr[mid+1...r]兩部分進(jìn)行歸并
    private static void merge(Comparable[] arr, int l, int mid, int r) {

        Comparable[] aux = Arrays.copyOfRange(arr, l, r+1);

        // 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
        int i = l, j = mid+1;
        for( int k = l ; k <= r; k ++ ){

            if( i > mid ){  // 如果左半部分元素已經(jīng)全部處理完畢
                arr[k] = aux[j-l]; j ++;
            }
            else if( j > r ){   // 如果右半部分元素已經(jīng)全部處理完畢
                arr[k] = aux[i-l]; i ++;
            }
            else if( aux[i-l].compareTo(aux[j-l]) < 0 ){  // 左半部分所指元素 < 右半部分所指元素
                arr[k] = aux[i-l]; i ++;
            }
            else{  // 左半部分所指元素 >= 右半部分所指元素
                arr[k] = aux[j-l]; j ++;
            }
        }
    }

    public static void sort(Comparable[] arr){

        int n = arr.length;

        // Merge Sort Bottom Up 無(wú)優(yōu)化版本
        for (int sz = 1; sz < n; sz *= 2)
            for (int i = 0; i < n - sz; i += sz+sz)

                //改進(jìn)1:如下代碼改成
                //if(arr[i+sz-1].compareTo(arr[i+sz]) >0)
                //merge(arr, i, i+sz-1,Math.min(i+sz+sz-1,n-1));

                // 對(duì) arr[i...i+sz-1] 和 arr[i+sz...i+2*sz-1]進(jìn)行歸并
                merge(arr, i, i+sz-1, Math.min(i+sz+sz-1,n-1));

    }

快速排序

原始版本

1.該版本比優(yōu)化后的歸并排序更快
注:可以使用隨機(jī)數(shù)作為目標(biāo)元素的下標(biāo),從而避免有序的時(shí)候遞歸樹(shù)深度接近于n

    // 對(duì)arr[l...r]部分進(jìn)行partition操作
    // 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
    private static int partition(Comparable[] arr, int l, int r){

        // 隨機(jī)在arr[l...r]的范圍中, 選擇一個(gè)數(shù)值作為標(biāo)定點(diǎn)pivot
        swap( arr, l , (int)(Math.random()*(r-l+1))+l );

        Comparable v = arr[l];

        int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
        for( int i = l + 1 ; i <= r ; i ++ )
            if( arr[i].compareTo(v) < 0 ){
                j ++;
                swap(arr, j, i);
            }

        swap(arr, l, j);

        return j;
    }

    // 遞歸使用快速排序,對(duì)arr[l...r]的范圍進(jìn)行排序
    private static void sort(Comparable[] arr, int l, int r){

        //優(yōu)化1:
        // 對(duì)于小規(guī)模數(shù)組, 使用插入排序
        //if( r - l <= 15 ){
        //    InsertionSort.sort(arr, l, r);
        //    return;
        //}
        //原始版本:
        if(r <= l)
            return;
        

        int p = partition(arr, l, r);
        sort(arr, l, p-1 );
        sort(arr, p+1, r);
    }

    public static void sort(Comparable[] arr){
        int n = arr.length;
        sort(arr, 0, n-1);
    }
思路2:兩路排序

解決的是:
解決擁有非常多重復(fù)元素的時(shí)候本排序?qū)碛休^好的效果.不然,當(dāng)擁有非常多的重復(fù)元素的時(shí)候,重復(fù)元素將會(huì)倒向一邊.
用兩個(gè)指針?lè)謩e指向數(shù)組的兩端,然后循環(huán),遇到兩邊不符合的元素,調(diào)換位置,直到左邊的指針與右邊的指針重合或者超過(guò)右邊的指針即可

    // 雙路快速排序的partition
    // 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
    private static int partition(Comparable[] arr, int l, int r){

        // 隨機(jī)在arr[l...r]的范圍中, 選擇一個(gè)數(shù)值作為標(biāo)定點(diǎn)pivot
        swap( arr, l , (int)(Math.random()*(r-l+1))+l );

        Comparable v = arr[l];

        // arr[l+1...i) <= v; arr(j...r] >= v
        int i = l+1, j = r;
        while( true ){
            // 注意這里的邊界, arr[i].compareTo(v) < 0, 不能是arr[i].compareTo(v) <= 0
            // 思考一下為什么?
            while( i <= r && arr[i].compareTo(v) < 0 )
                i ++;

            // 注意這里的邊界, arr[j].compareTo(v) > 0, 不能是arr[j].compareTo(v) >= 0
            // 思考一下為什么?
            while( j >= l+1 && arr[j].compareTo(v) > 0 )
                j --;

            // 對(duì)于上面的兩個(gè)邊界的設(shè)定, 有的同學(xué)在課程的問(wèn)答區(qū)有很好的回答:)
            // 大家可以參考: http://coding.imooc.com/learn/questiondetail/4920.html

            if( i > j )
                break;

            swap( arr, i, j );
            i ++;
            j --;
        }

        swap(arr, l, j);

        return j;
    }

    // 遞歸使用快速排序,對(duì)arr[l...r]的范圍進(jìn)行排序
    private static void sort(Comparable[] arr, int l, int r){

        // 對(duì)于小規(guī)模數(shù)組, 使用插入排序
        if( r - l <= 15 ){
            InsertionSort.sort(arr, l, r);
            return;
        }

        int p = partition(arr, l, r);
        sort(arr, l, p-1 );
        sort(arr, p+1, r);
    }

    public static void sort(Comparable[] arr){

        int n = arr.length;
        sort(arr, 0, n-1);
    }

另一種優(yōu)化:三路快排

    // 遞歸使用快速排序,對(duì)arr[l...r]的范圍進(jìn)行排序
    private static void sort(Comparable[] arr, int l, int r){

        // 對(duì)于小規(guī)模數(shù)組, 使用插入排序
        if( r - l <= 15 ){
            InsertionSort.sort(arr, l, r);
            return;
        }

        // 隨機(jī)在arr[l...r]的范圍中, 選擇一個(gè)數(shù)值作為標(biāo)定點(diǎn)pivot
        swap( arr, l, (int)(Math.random()*(r-l+1)) + l );

        Comparable v = arr[l];

        int lt = l;     // arr[l+1...lt] < v
        int gt = r + 1; // arr[gt...r] > v
        int i = l+1;    // arr[lt+1...i) == v
        while( i < gt ){
            if( arr[i].compareTo(v) < 0 ){
                swap( arr, i, lt+1);
                i ++;
                lt ++;
            }
            else if( arr[i].compareTo(v) > 0 ){
                swap( arr, i, gt-1);
                gt --;
            }
            else{ // arr[i] == v
                i ++;
            }
        }

        swap( arr, l, lt );

        sort(arr, l, lt-1);
        sort(arr, gt, r);
    }

    public static void sort(Comparable[] arr){

        int n = arr.length;
        sort(arr, 0, n-1);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 初級(jí)排序算法 排序算法關(guān)注的主要是重新排列數(shù)組元素,其中每個(gè)元素都有一個(gè)主鍵。排序算法是將所有元素主鍵按某種方...
    深度沉迷學(xué)習(xí)閱讀 1,593評(píng)論 0 1
  • 一些概念 數(shù)據(jù)結(jié)構(gòu)就是研究數(shù)據(jù)的邏輯結(jié)構(gòu)和物理結(jié)構(gòu)以及它們之間相互關(guān)系,并對(duì)這種結(jié)構(gòu)定義相應(yīng)的運(yùn)算,而且確保經(jīng)過(guò)這...
    Winterfell_Z閱讀 6,573評(píng)論 0 13
  • 冒泡排序 冒泡排序相對(duì)來(lái)說(shuō)是較為簡(jiǎn)單的一種排序,它的思想就是在每一次循環(huán)中比較相鄰的兩個(gè)數(shù),通過(guò)交換的方式,將最小...
    陌上疏影涼閱讀 644評(píng)論 0 3
  • 不知不覺(jué),2018年已經(jīng)到來(lái)。結(jié)束了2017一整年的忙碌,不管收獲多少,成功幾許,給過(guò)去一年忙碌的自己一些犒勞,終...
    ComRatings_f46a閱讀 796評(píng)論 1 0
  • 少年不識(shí)愁滋味,愛(ài)上層樓。愛(ài)上層樓。為賦新詞強(qiáng)說(shuō)愁。 而今識(shí)盡愁滋味,欲說(shuō)還休。欲說(shuō)還休。卻道天涼...
    二三一也閱讀 714評(píng)論 1 2

友情鏈接更多精彩內(nèi)容