国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

首頁 類庫下載 java類庫 簡易版的TimSort排序演算法

簡易版的TimSort排序演算法

Oct 31, 2016 am 10:53 AM
java

1. 簡易版本TimSort排序演算法原理與實(shí)作

TimSort排序演算法是Python和Java針對物件陣列的預(yù)設(shè)排序演算法。 TimSort排序演算法的本質(zhì)是歸併排序演算法,只是在歸併排序演算法上進(jìn)行了大量的最佳化。對於日常生活中我們需要排序的資料通常不是完全隨機(jī)的,而是部分有序的,或者部分逆序的,所以TimSort充分利用已有序的部分進(jìn)行歸併排序?,F(xiàn)在我們提供一個(gè)簡易版本TimSort排序演算法,它主要做了以下最佳化:

1.1利用原本已有序的片段

首先規(guī)定一個(gè)最小歸併長度。檢查數(shù)組中原本有序的片段,如果已有序的長度小於規(guī)定的最小歸併長度,則通過插入排序?qū)σ延行虻钠芜M(jìn)行進(jìn)行擴(kuò)充(這樣做的原因避免歸併長度較小的片段,因?yàn)檫@樣的效率比較低)。將有序片段的起始索引位置和已有序的長度入堆疊。

1.2避免一個(gè)較長的有序片段和一個(gè)較小的有序片段進(jìn)行歸併,因?yàn)檫@樣的效率比較低:

(1)如果棧中存在已有序的至少三個(gè)序列,我們用X ,Y,Z依序表示從棧頂向下的三個(gè)已有序列片段,當(dāng)三者的長度滿足X+Y>=Z時(shí)進(jìn)行歸併。

?? (1.1)如果X是三者中長度最大的,先將X,Y,Z出棧,應(yīng)該先歸併Y和Z,然後將Y和Z歸併的結(jié)果入棧,最後X入棧

?? ( 1.2)否則將X和Y出棧,歸併後結(jié)果入棧。注意,實(shí)際上我們不會真正的出棧,寫程式碼中有一些技巧可以達(dá)到相同的效果,而且效率更高。

(2)如果不滿足X+Y>=Z的條件或堆疊中僅存在兩個(gè)序列,我們用X,Y依序表示從棧頂向下的兩個(gè)已有序列的長度,如果X>= Y則進(jìn)行歸併,然後將歸併後的有序片段結(jié)果入棧。

1.3在歸併兩個(gè)已有序的片段時(shí),採用了所謂的飛奔(gallop)模式,這樣可以減少參與歸併的數(shù)據(jù)長度

假設(shè)需要?dú)w併的兩個(gè)已有序片段分別為X和Y ,如果X片段的前m個(gè)元素都比Y片段的首元素小,那麼這m個(gè)元素其實(shí)是不需要參與歸併的,因?yàn)闅w併後這m個(gè)元素仍然位於原來的位置。同理如果Y片段的最後n個(gè)元素都比X的最後一個(gè)元素大,那麼Y的最後n個(gè)元素也不必參與歸併。這樣就減少了歸併數(shù)組的長度(簡易版沒有這麼做),也較少了待排序數(shù)組與輔助數(shù)組之間資料來回復(fù)制的長度,進(jìn)而提高了歸併的效率。

2. Java原始碼

package datastruct;
 
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
 
public class SimpleTimSort<T extends Comparable<? super T>>{
    //最小歸并長度
    private static final int MIN_MERGE = 16;
    //待排序數(shù)組
    private final T[] a;
    //輔助數(shù)組
    private T[] aux;
    //用兩個(gè)數(shù)組表示棧
    private int[] runsBase = new int[40];
    private int[] runsLen = new int[40];
    //表示棧頂指針
    private int stackTop = 0;
     
    @SuppressWarnings("unchecked")
    public SimpleTimSort(T[] a){
        this.a = a;
        aux = (T[]) Array.newInstance(a[0].getClass(), a.length);
    }
     
    //T[from, to]已有序,T[to]以后的n元素插入到有序的序列中
    private void insertSort(T[] a, int from, int to, int n){
        int i = to + 1;
        while(n > 0){
            T tmp = a[i];
            int j;
            for(j = i-1; j >= from && tmp.compareTo(a[j]) < 0; j--){
                a[j+1] = a[j];
            }
            a[++j] = tmp;
            i++;
            n--;
        }
    }
     
    //返回從a[from]開始,的最長有序片段的個(gè)數(shù)
    private int maxAscendingLen(T[] a, int from){
        int n = 1;
        int i = from;
         
        if(i >= a.length){//超出范圍
            return 0;
        }
         
        if(i == a.length-1){//只有一個(gè)元素
            return 1;
        }
         
        //至少兩個(gè)元素
        if(a[i].compareTo(a[i+1]) < 0){//升序片段
            while(i+1 <= a.length-1 && a[i].compareTo(a[i+1]) <= 0){
                i++;
                n++;
            }
            return n;
        }else{//降序片段,這里是嚴(yán)格的降序,不能有>=的情況,否則不能保證穩(wěn)定性
            while(i+1 <= a.length-1 && a[i].compareTo(a[i+1]) > 0){
                i++;
                n++;
            }
            //對降序片段逆序
            int j = from;
            while(j < i){
                T tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
                j++;
                i--;
            }
            return n;
        }
    }
     
    //對有序片段的起始索引位置和長度入棧
    private void pushRun(int base, int len){
        runsBase[stackTop] = base;
        runsLen[stackTop] = len;
        stackTop++;
    }
     
    //返回-1表示不需要?dú)w并棧中的有序片段
    public int needMerge(){
        if(stackTop > 1){//至少兩個(gè)run序列
            int x = stackTop - 2;
            //x > 0 表示至少三個(gè)run序列
            if(x > 0 && runsLen[x-1] <= runsLen[x] + runsLen[x+1]){
                if(runsLen[x-1] < runsLen[x+1]){
                    //說明 runsLen[x+1]是runsLen[x]和runsLen[x-1]中最大的值
                    //應(yīng)該先合并runsLen[x]和runsLen[x-1]這兩段run
                    return --x;
                }else{
                    return x;
                }
            }else
            if(runsLen[x] <= runsLen[x+1]){
                return x;
            }else{
                return -1;
            }
        }
        return -1;
    }
     
    //返回后一個(gè)片段的首元素在前一個(gè)片段應(yīng)該位于的位置
    private int gallopLeft(T[] a, int base, int len, T key){
        int i = base;
        while(i <= base + len - 1){
            if(key.compareTo(a[i]) >= 0){
                i++;
            }else{
                break;
            }
        }
        return i;
    }
     
    //返回前一個(gè)片段的末元素在后一個(gè)片段應(yīng)該位于的位置
    private int gallopRight(T[] a, int base, int len, T key){
        int i = base + len -1;
        while(i >= base){
            if(key.compareTo(a[i]) <= 0){
                i--;
            }else{
                break;
            }
        }
        return i;
    }
     
    public void mergeAt(int x){
        int base1 = runsBase[x];
        int len1 = runsLen[x];
         
        int base2 = runsBase[x+1];
        int len2 = runsLen[x+1];
         
        //合并run[x]和run[x+1],合并后base不用變,長度需要發(fā)生變化
        runsLen[x] = len1 + len2; 
        if(stackTop == x + 3){
            //棧頂元素下移,省去了合并后的先出棧,再入棧
            runsBase[x+1] = runsBase[x+2];
            runsLen[x+1] = runsLen[x+2];
        }
        stackTop--;
         
        //飛奔模式,減小歸并的長度
        int from = gallopLeft(a, base1, len1, a[base2]);
        if(from == base1+len1){
            return;
        }
        int to = gallopRight(a, base2, len2, a[base1+len1-1]);
         
        //對兩個(gè)需要?dú)w并的片段長度進(jìn)行歸并
        System.arraycopy(a, from, aux, from, to - from + 1);
        int i = from;
        int iend = base1 + len1 - 1;
         
        int j = base2;
        int jend = to;
         
        int k = from;
        int kend = to;
         
        while(k <= kend){
            if(i > iend){
                a[k] = aux[j++];
            }else
            if(j > jend){
                a[k] = aux[i++];
            }else
            if(aux[i].compareTo(aux[j]) <= 0){//等號保證排序的穩(wěn)定性
                a[k] = aux[i++];
            }else{
                a[k] = aux[j++];
            }
            k++;
        }
    }
     
    //強(qiáng)制歸并已入棧的序列
    private void forceMerge(){
        while(stackTop > 1){
            mergeAt(stackTop-2);
        }
    }
     
    //timSort的主方法
    public void timSort(){
        //n表示剩余長度
        int n = a.length; 
         
        if(n < 2){
            return;
        }
         
        //待排序的長度小于MIN_MERGE,直接采用插入排序完成
        if(n < MIN_MERGE){
            insertSort(a, 0, 0, a.length-1);
            return;
        }
         
        int base = 0;
        while(n > 0){
            int len = maxAscendingLen(a, base);
            if(len < MIN_MERGE){
                int abscent = n > MIN_MERGE ?  MIN_MERGE - len : n - len;
                insertSort(a, base, base + len-1, abscent);
                len = len + abscent;
            }
            pushRun(base, len);
            n = n - len;
            base = base + len;
             
            int x;
            while((x  = needMerge()) >= 0 ){
                mergeAt(x);
            }
        }
        forceMerge();
    }
     
    public static void main(String[] args){
         
        //隨機(jī)產(chǎn)生測試用例
        Random rnd = new Random(System.currentTimeMillis());
        boolean flag = true;
        while(flag){
             
            //首先產(chǎn)生一個(gè)全部有序的數(shù)組
            Integer[] arr1 = new Integer[1000];
            for(int i = 0; i < arr1.length; i++){
                arr1[i] = i;
            }
             
            //有序的基礎(chǔ)上隨機(jī)交換一些值
            for(int i = 0; i < (int)(0.1*arr1.length); i++){
                int x,y,tmp;
                x = rnd.nextInt(arr1.length);
                y = rnd.nextInt(arr1.length);
                tmp = arr1[x];
                arr1[x] = arr1[y];
                arr1[y] = tmp;
            }
             
            //逆序部分?jǐn)?shù)據(jù)
            for(int i = 0; i <(int)(0.05*arr1.length); i++){
                int x = rnd.nextInt(arr1.length);
                int y = rnd.nextInt((int)(arr1.length*0.01)+x);
                if(y >= arr1.length){
                    continue;
                }
                while(x < y){
                    int tmp;
                    tmp = arr1[x];
                    arr1[x] = arr1[y];
                    arr1[y] = tmp;
                    x++;
                    y--;
                }
            }
             
            Integer[] arr2 = arr1.clone();
            Integer[] arr3 = arr1.clone();
            Arrays.sort(arr2);
             
            SimpleTimSort<Integer> sts = new SimpleTimSort<Integer>(arr1);
            sts.timSort();
             
            //比較SimpleTimSort排序和庫函數(shù)提供的排序結(jié)果比較是否一致
            //如果沒有打印任何結(jié)果,說明排序結(jié)果正確
            if(!Arrays.deepEquals(arr1, arr2)){
                for(int i = 0; i < arr1.length; i++){
                    if(!arr1[i].equals(arr2[i])){
                        System.out.printf("%d: arr1 %d  arr2 %d\n",i,arr1[i],arr2[i]);
                    }
                }
                System.out.println(Arrays.deepToString(arr3));
                flag = false;
            }
        }
    }
}

3.TimSort演算法應(yīng)注意的問題

TimSort演算法只會對連續(xù)的兩個(gè)片段進(jìn)行歸併,這樣才能保證演算法的穩(wěn)定性。

最小歸併長度和棧的長度存在一定的關(guān)係,如果增大最小歸併長度,則棧的長度也應(yīng)該增大,否則可能引起棧越界的風(fēng)險(xiǎn)(代碼中棧是透過長度為40的數(shù)組來實(shí)現(xiàn)的)。

4.完整版的TimSort演算法

實(shí)際上,完整版的TimSort演算法會在上述簡易TimSort演算法上還有大量的最佳化。例如有序序列小於最小歸併長度時(shí),我們可以利用類似二分查找的方式來找出應(yīng)該插入的位置來對陣列進(jìn)行長度擴(kuò)充。再例如飛奔模式中採用二分查找的方式查找第二個(gè)序列的首元素在第一個(gè)序列的位置,同時(shí)還可以使用較小的輔助空間完成歸併,有興趣的同學(xué)可以查看Java中的源代碼來學(xué)習(xí)。


本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願(yuàn)投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何在Java的地圖上迭代? 如何在Java的地圖上迭代? Jul 13, 2025 am 02:54 AM

遍歷Java中的Map有三種常用方法:1.使用entrySet同時(shí)獲取鍵和值,適用於大多數(shù)場景;2.使用keySet或values分別遍歷鍵或值;3.使用Java8的forEach簡化代碼結(jié)構(gòu)。 entrySet返回包含所有鍵值對的Set集合,每次循環(huán)獲取Map.Entry對象,適合頻繁訪問鍵和值的情況;若只需鍵或值,可分別調(diào)用keySet()或values(),也可在遍歷鍵時(shí)通過map.get(key)獲取值;Java8中可通過Lambda表達(dá)式使用forEach((key,value)-&gt

Java中的可比較與比較器 Java中的可比較與比較器 Jul 13, 2025 am 02:31 AM

在Java中,Comparable用於類內(nèi)部定義默認(rèn)排序規(guī)則,Comparator用於外部靈活定義多種排序邏輯。 1.Comparable是類自身實(shí)現(xiàn)的接口,通過重寫compareTo()方法定義自然順序,適用於類有固定、最常用的排序方式,如String或Integer。 2.Comparator是外部定義的函數(shù)式接口,通過compare()方法實(shí)現(xiàn),適合同一類需要多種排序方式、無法修改類源碼或排序邏輯經(jīng)常變化的情況。兩者區(qū)別在於Comparable只能定義一種排序邏輯且需修改類本身,而Compar

如何處理Java中的字符編碼問題? 如何處理Java中的字符編碼問題? Jul 13, 2025 am 02:46 AM

處理Java中的字符編碼問題,關(guān)鍵是在每一步都明確指定使用的編碼。 1.讀寫文本時(shí)始終指定編碼,使用InputStreamReader和OutputStreamWriter並傳入明確的字符集,避免依賴系統(tǒng)默認(rèn)編碼。 2.在網(wǎng)絡(luò)邊界處理字符串時(shí)確保兩端一致,設(shè)置正確的Content-Type頭並用庫顯式指定編碼。 3.謹(jǐn)慎使用String.getBytes()和newString(byte[]),應(yīng)始終手動(dòng)指定StandardCharsets.UTF_8以避免平臺差異導(dǎo)致的數(shù)據(jù)損壞。總之,通過在每個(gè)階段

JavaScript數(shù)據(jù)類型:原始與參考 JavaScript數(shù)據(jù)類型:原始與參考 Jul 13, 2025 am 02:43 AM

JavaScript的數(shù)據(jù)類型分為原始類型和引用類型。原始類型包括string、number、boolean、null、undefined和symbol,其值不可變且賦值時(shí)復(fù)制副本,因此互不影響;引用類型如對象、數(shù)組和函數(shù)存儲的是內(nèi)存地址,指向同一對象的變量會相互影響。判斷類型可用typeof和instanceof,但需注意typeofnull的歷史問題。理解這兩類差異有助於編寫更穩(wěn)定可靠的代碼。

Hashmap在Java內(nèi)部如何工作? Hashmap在Java內(nèi)部如何工作? Jul 15, 2025 am 03:10 AM

HashMap在Java中通過哈希表實(shí)現(xiàn)鍵值對存儲,其核心在於快速定位數(shù)據(jù)位置。 1.首先使用鍵的hashCode()方法生成哈希值,並通過位運(yùn)算轉(zhuǎn)換為數(shù)組索引;2.不同對象可能產(chǎn)生相同哈希值,導(dǎo)致衝突,此時(shí)以鍊錶形式掛載節(jié)點(diǎn),JDK8後鍊錶過長(默認(rèn)長度8)則轉(zhuǎn)為紅黑樹提升效率;3.使用自定義類作鍵時(shí)必須重寫equals()和hashCode()方法;4.HashMap動(dòng)態(tài)擴(kuò)容,當(dāng)元素?cái)?shù)超過容量乘以負(fù)載因子(默認(rèn)0.75)時(shí),擴(kuò)容並重新哈希;5.HashMap非線程安全,多線程下應(yīng)使用Concu

Java中的'靜態(tài)”關(guān)鍵字是什麼? Java中的'靜態(tài)”關(guān)鍵字是什麼? Jul 13, 2025 am 02:51 AM

InJava,thestatickeywordmeansamemberbelongstotheclassitself,nottoinstances.Staticvariablesaresharedacrossallinstancesandaccessedwithoutobjectcreation,usefulforglobaltrackingorconstants.Staticmethodsoperateattheclasslevel,cannotaccessnon-staticmembers,

在C中使用std :: Chrono 在C中使用std :: Chrono Jul 15, 2025 am 01:30 AM

std::chrono在C 中用於處理時(shí)間,包括獲取當(dāng)前時(shí)間、測量執(zhí)行時(shí)間、操作時(shí)間點(diǎn)與持續(xù)時(shí)間及格式化解析時(shí)間。 1.獲取當(dāng)前時(shí)間使用std::chrono::system_clock::now(),可轉(zhuǎn)換為可讀字符串但係統(tǒng)時(shí)鐘可能不單調(diào);2.測量執(zhí)行時(shí)間應(yīng)使用std::chrono::steady_clock以確保單調(diào)性,並通過duration_cast轉(zhuǎn)換為毫秒、秒等單位;3.時(shí)間點(diǎn)(time_point)和持續(xù)時(shí)間(duration)可相互操作,但需注意單位兼容性和時(shí)鐘紀(jì)元(epoch)

什麼是Java的重新進(jìn)入? 什麼是Java的重新進(jìn)入? Jul 13, 2025 am 02:14 AM

ReentrantLock在Java中提供比synchronized更靈活的線程控制。 1.它支持非阻塞獲取鎖(tryLock())、帶超時(shí)的鎖獲取(tryLock(longtimeout,TimeUnitunit))和可中斷等待鎖;2.允許設(shè)置公平鎖,避免線程飢餓;3.支持多個(gè)條件變量,實(shí)現(xiàn)更精細(xì)的等待/通知機(jī)制;4.需手動(dòng)釋放鎖,必須在finally塊中調(diào)用unlock()以避免資源洩漏;5.適用於需要高級同步控制的場景,如自定義同步工具或複雜並發(fā)結(jié)構(gòu),但對簡單互斥需求仍推薦使用synchro

See all articles