成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

從TimSort說(shuō)起

Jeffrrey / 1457人閱讀

摘要:通過(guò)中的源碼可以看到,在時(shí)是調(diào)用了的方法,完成排序再返回而,對(duì)原始類型,里用的是快速排序,對(duì)于對(duì)象類型,則使用歸并排序。到了快速排序升級(jí)為雙基準(zhǔn)快排雙基準(zhǔn)快排三路快排歸并排序升級(jí)為歸并排序的改進(jìn)版。

大家可能對(duì)timsort并不是很熟悉,不過(guò)說(shuō)起Collections.sort(list) 應(yīng)該并不陌生。

public static > void sort(List list) {
Object[] a = list.toArray();
Arrays.sort(a);
ListIterator i = list.listIterator();
for (int j=0; j

通過(guò)jdk6中的Collections源碼可以看到,在sort時(shí)是調(diào)用了Arrays的sort方法,完成排序再返回list

  

      public static void sort(Object[] a) {
            Object[] aux = (Object[])a.clone();
            mergeSort(aux, a, 0, a.length, 0);
        }
            private static void mergeSort(Object[] src,
                      Object[] dest,
                      int low,
                      int high,
                      int off) {
        int length = high - low;
    
        // Insertion sort on smallest arrays
            if (length < INSERTIONSORT_THRESHOLD) {
                for (int i=low; ilow &&
                 ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                        swap(dest, j, j-1);
                return;
            }
    
            // Recursively sort halves of dest into src
            int destLow  = low;
            int destHigh = high;
            low  += off;
            high += off;
            int mid = (low + high) >>> 1;
            mergeSort(dest, src, low, mid, -off);
            mergeSort(dest, src, mid, high, -off);
    
            // If list is already sorted, just copy from src to dest.  This is an
            // optimization that results in faster sorts for nearly ordered lists.
            if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
                System.arraycopy(src, low, dest, destLow, length);
                return;
            }
    
            // Merge sorted halves (now in src) into dest
            for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
                if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
                    dest[i] = src[p++];
                else
                    dest[i] = src[q++];
            }
        }

而Arrays.sort(),對(duì)原始類型(int[],double[],char[],byte[]),JDK6里用的是快速排序,對(duì)于對(duì)象類型(Object[]),JDK6則使用歸并排序。
到了jdk7,快速排序升級(jí)為雙基準(zhǔn)快排(雙基準(zhǔn)快排 vs 三路快排);歸并排序升級(jí)為歸并排序的改進(jìn)版TimSort。
再到了JDK8, 對(duì)大集合增加了Arrays.parallelSort()函數(shù),使用fork-Join框架,充分利用多核,對(duì)大的集合進(jìn)行切分然后再歸并排序,而在小的連續(xù)片段里,依然使用TimSort與DualPivotQuickSort。
談到優(yōu)化過(guò)程,先來(lái)回憶一下歸并排序,長(zhǎng)度為1的數(shù)組是已經(jīng)排序好的。對(duì)長(zhǎng)度為n>1的數(shù)組,將其分為2段(partition)(最常見(jiàn)的做法是從中間分開(kāi))。對(duì)兩段數(shù)組遞歸進(jìn)行歸并排序,完成后將其合并(merge):通過(guò)掃描個(gè)已排序的數(shù)組并總是挑出兩者中較小數(shù)作為合并數(shù)組中的下一個(gè)元素,來(lái)將兩個(gè)已排序數(shù)組合并形成一個(gè)更大的已排序數(shù)組。
此時(shí)如果我們遇到這樣一個(gè)數(shù)組,{5, 6, 7, 8, 9, 10, 1, 2, 3},我們利用快排或者歸并帶來(lái)的開(kāi)銷,似乎都不劃算,因?yàn)檎б豢粗恍枰徊骄湍芡瓿膳判?。這也是timSort的一個(gè)主要優(yōu)勢(shì),適應(yīng)性排序,先把5至10截取,1之3截取成兩段,之后再歸并排序,再來(lái)看一個(gè)數(shù)組,{64, 32, 16, 8, 4, 2, 1},對(duì)于這樣的案例,通過(guò)適應(yīng)性排序的思路,直接反轉(zhuǎn),再來(lái)檢查,已經(jīng)不需要額外的工作了。TimSort在此基礎(chǔ)上還進(jìn)行了其他的一些優(yōu)化,這也助推了該算法的成功。下面我們通過(guò)源碼來(lái)進(jìn)一步了解一下。

    static  void sort(T[] a, int lo, int hi, Comparator c) {
        if (c == null) {
            Arrays.sort(a, lo, hi);
            return;
        }
    
        rangeCheck(a.length, lo, hi);
        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted
    
        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }
    
        /**
         * March over the array once, left to right, finding natural runs,
         * extending short natural runs to minRun elements, and merging runs
         * to maintain stack invariant.
         */
        TimSort ts = new TimSort<>(a, c);
        int minRun = minRunLength(nRemaining);
        do {
            // Identify next run
            int runLen = countRunAndMakeAscending(a, lo, hi, c);
    
            // If run is short, extend to min(minRun, nRemaining)
            if (runLen < minRun) {
                int force = nRemaining <= minRun ? nRemaining : minRun;
                binarySort(a, lo, lo + force, lo + runLen, c);
                runLen = force;
            }
    
            // Push run onto pending-run stack, and maybe merge
            ts.pushRun(lo, runLen);
            ts.mergeCollapse();
    
            // Advance to find next run
            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);
    
        // Merge all remaining runs to complete sort
        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }

首先是非空的判斷,如果沒(méi)有提供comparator,會(huì)調(diào)用Arrays.sort,其實(shí)也是ComparableTimSort,后面是算法的主體:如果元素個(gè)數(shù)小于2,直接返回,因?yàn)檫@兩個(gè)元素已經(jīng)排序了
如果元素個(gè)數(shù)小于一個(gè)閾值(默認(rèn)為),調(diào)用 binarySort,這是一個(gè)不包含合并操作的 mini-TimSort。
在關(guān)鍵的 do-while 循環(huán)中,不斷地進(jìn)行排序,合并,排序,合并,一直到所有數(shù)據(jù)都處理完。
然后會(huì)找出run的最小長(zhǎng)度,少于這個(gè)最小長(zhǎng)度就需要對(duì)其進(jìn)行擴(kuò)展,再來(lái)看下binarySort

private static  void binarySort(T[] a, int lo, int hi, int start,
                                   Comparator c) {
    assert lo <= start && start <= hi;
    if (start == lo)
        start++;
    for ( ; start < hi; start++) {
        T pivot = a[start];

        // Set left (and right) to the index where a[start] (pivot) belongs
        int left = lo;
        int right = start;
        assert left <= right;
        /*
         * Invariants:
         *   pivot >= all in [lo, left).
         *   pivot <  all in [right, start).
         */
        while (left < right) {
            int mid = (left + right) >>> 1;
            if (c.compare(pivot, a[mid]) < 0)
                right = mid;
            else
                left = mid + 1;
        }
        assert left == right;

        /*
         * The invariants still hold: pivot >= all in [lo, left) and
         * pivot < all in [left, start), so pivot belongs at left.  Note
         * that if there are elements equal to pivot, left points to the
         * first slot after them -- that"s why this sort is stable.
         * Slide elements over to make room for pivot.
         */
        int n = start - left;  // The number of elements to move
        // Switch is just an optimization for arraycopy in default case
        switch (n) {
            case 2:  a[left + 2] = a[left + 1];
            case 1:  a[left + 1] = a[left];
                     break;
            default: System.arraycopy(a, left, a, left + 1, n);
        }
        a[left] = pivot;
    }
}

binarySort 對(duì)數(shù)組 a[lo:hi] 進(jìn)行排序,并且a[lo:start] 是已經(jīng)排好序的。算法的思路是對(duì) a[start:hi] 中的元素,每次使用 binarySearch 為它在 a[lo:start] 中找到相應(yīng)位置,并插入。
另一個(gè)關(guān)鍵函數(shù)是mergeCollapse

/**
 * Examines the stack of runs waiting to be merged and merges adjacent runs
 * until the stack invariants are reestablished:
 *
 *     1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
 *     2. runLen[i - 2] > runLen[i - 1]
 *
 * This method is called each time a new run is pushed onto the stack,
 * so the invariants are guaranteed to hold for i < stackSize upon
 * entry to the method.
 */
private void mergeCollapse() {
    while (stackSize > 1) {
        int n = stackSize - 2;
        if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1]) {
            if (runLen[n - 1] < runLen[n + 1])
                n--;
            mergeAt(n);
        } else if (runLen[n] <= runLen[n + 1]) {
            mergeAt(n);
        } else {
            break; // Invariant is established
        }
    }
}

它會(huì)把已經(jīng)排序的 run 合并成一個(gè)大 run,此大 run 也會(huì)排好序。并的過(guò)程會(huì)一直循環(huán)下去,一直到注釋里提到的循環(huán)不變式得到滿足。
合并的時(shí)候,有會(huì)特別的技巧。假設(shè)兩個(gè) run 是 run1,run2 ,先用 gallopRight在 run1 里使用 binarySearch 查找 run2 首元素 的位置 k, 那么 run1 中 k 前面的元素就是合并后最小的那些元素。然后,在 run2 中查找 run1 尾元素 的位置 len2 ,那么 run2 中 len2 后面的那些元素就是合并后最大的那些元素。最后,根據(jù)len1 與 len2 大小,調(diào)用 mergeLo 或者 mergeHi 將剩余元素合并。
篇幅限制,不能全都說(shuō)完了,感興趣的讀者可以移步TimeSort in java 7

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/66221.html

相關(guān)文章

  • Java Arrays.asList()與Arrays.sort()分析

    摘要:與分析聲明文章均為本人技術(shù)筆記,轉(zhuǎn)載請(qǐng)注明出處示例源碼將數(shù)組或者指定個(gè)數(shù)的對(duì)象轉(zhuǎn)換為是的內(nèi)部類實(shí)例,與不是一回事,長(zhǎng)度固定,只能遍歷訪問(wèn),不能使用修改集合相關(guān)的方法,比如方法會(huì)拋出異常適配器模式修改數(shù)組內(nèi)容后,內(nèi)容也會(huì)隨之改變,體現(xiàn)適配器模 Java Arrays.asList()與Arrays.sort()分析 聲明 文章均為本人技術(shù)筆記,轉(zhuǎn)載請(qǐng)注明出處https://segment...

    pkhope 評(píng)論0 收藏0
  • Java新手非常容易犯的一個(gè)錯(cuò)誤

    摘要:最近一直在做底層方面的研究,所以這段時(shí)間就沒(méi)寫相關(guān)的東西,但恰巧今天同事問(wèn)我一個(gè)問(wèn)題,在幫他解決完這個(gè)問(wèn)題之后,我發(fā)現(xiàn),這個(gè)問(wèn)題對(duì)新手來(lái)說(shuō)還是非常容易犯的,所以在這里記錄下。首先看下面這段代碼這段代碼的功能就是對(duì)進(jìn)行排序,內(nèi)元素類型是。 最近一直在做底層方面的研究,所以這段時(shí)間就沒(méi)寫java相關(guān)的東西,但恰巧今天同事問(wèn)我一個(gè)問(wèn)題,在幫他解決完這個(gè)問(wèn)題之后,我發(fā)現(xiàn),這個(gè)問(wèn)題對(duì)java新手...

    niuxiaowei111 評(píng)論0 收藏0
  • 前百度面試官整理的——Java后端面試題(二)

    摘要:另外,還可以調(diào)用和等很便利的方法,以返回表示字段,方法,以及構(gòu)造器的對(duì)象的數(shù)組。運(yùn)行結(jié)果無(wú)參構(gòu)造器有參構(gòu)造器和實(shí)現(xiàn)原理和區(qū)別和區(qū)別是一個(gè)集合接口。 對(duì)象的四種引用 強(qiáng)引用只要引用存在,垃圾回收器永遠(yuǎn)不會(huì)回收 showImg(https://segmentfault.com/img/bVbsYsz?w=652&h=52); 可直接通過(guò)obj取得對(duì)應(yīng)的對(duì)象 如 obj.equels(new...

    thekingisalwaysluc 評(píng)論0 收藏0
  • CodeSalt | Python解決按學(xué)生年齡排序的實(shí)際問(wèn)題

    摘要:解決按學(xué)生年齡排序的實(shí)際問(wèn)題問(wèn)題定義一個(gè)包含姓名性別年齡,需要按年齡給學(xué)生排序。輸出按照年齡進(jìn)行排序好的。思路使用冒泡排序,比較相鄰的學(xué)生,如果第一個(gè)學(xué)生的值比第二個(gè)學(xué)生的值大,那么就整體交換這兩個(gè)元素。 Python解決按學(xué)生年齡排序的實(shí)際問(wèn)題 問(wèn)題:定義一個(gè)Class:包含姓名name、性別gender、年齡age,需要按年齡給學(xué)生排序。輸入:包含學(xué)生對(duì)象的List。輸出:按照年齡...

    yangrd 評(píng)論0 收藏0
  • Python學(xué)習(xí)之路21-序列構(gòu)成的數(shù)組

    摘要:第行把具名元組以的形式返回。對(duì)序列使用和通常號(hào)兩側(cè)的序列由相同類型的數(shù)據(jù)所構(gòu)成當(dāng)然不同類型的也可以相加,返回一個(gè)新序列。從上面的結(jié)果可以看出,它雖拋出了異常,但仍完成了操作查看字節(jié)碼并不難,而且它對(duì)我們了解代碼背后的運(yùn)行機(jī)制很有幫助。 《流暢的Python》筆記。接下來(lái)的三篇都是關(guān)于Python的數(shù)據(jù)結(jié)構(gòu),本篇主要是Python中的各序列類型 1. 內(nèi)置序列類型概覽 Python標(biāo)準(zhǔn)庫(kù)...

    ralap 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<