日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java中mergesort函数怎么用_MergeSort与TimSort,ComparableTimSort

發(fā)布時(shí)間:2023/12/31 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中mergesort函数怎么用_MergeSort与TimSort,ComparableTimSort 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

MergeSort歸并排序?qū)σ呀?jīng)反向排好序的輸入時(shí)復(fù)雜度為O(n^2),而TimSort就是針對這種情況,對MergeSort進(jìn)行優(yōu)化而產(chǎn)生的,平均復(fù)雜度為nO(log n),最好的情況為O(n),最壞情況nO(log n)。并且TimSort是一種穩(wěn)定性排序。思想是先對待排序列進(jìn)行分區(qū),然后再對分區(qū)進(jìn)行合并,看起來和MergeSort步驟一樣,但是其中有一些針對反向和大規(guī)模數(shù)據(jù)的優(yōu)化處理。

通過一個(gè)例子來說:ArrayList中的sort(),調(diào)用了Arrays.sort()

@Override

@SuppressWarnings("unchecked")

public void sort(Comparator super E> c) {

final int expectedModCount = modCount;

Arrays.sort((E[]) elementData, 0, size, c);

if (modCount != expectedModCount) {

throw new ConcurrentModificationException();

}

modCount++;

}

Arrays.sort()

public static void sort(T[] a, int fromIndex, int toIndex,

Comparator super T> c) {

if (c == null) {

sort(a, fromIndex, toIndex);

} else {

rangeCheck(a.length, fromIndex, toIndex);

if (LegacyMergeSort.userRequested)

legacyMergeSort(a, fromIndex, toIndex, c);

else

TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);

}

}

1. 當(dāng)Comparator == null時(shí),調(diào)用sort(a, fromIndex, toIndex);如下

public static void sort(Object[] a, int fromIndex, int toIndex) {

rangeCheck(a.length, fromIndex, toIndex);

//該分支將被刪除

if (LegacyMergeSort.userRequested)

legacyMergeSort(a, fromIndex, toIndex);

else

ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0);

}

/** To be removed in a future release. */

private static void legacyMergeSort(Object[] a,

int fromIndex, int toIndex) {

Object[] aux = copyOfRange(a, fromIndex, toIndex);

mergeSort(aux, a, fromIndex, toIndex, -fromIndex);

}

sort方法,參數(shù)[frimeIndex, toIndex)左閉右開,采用的算法能夠保證穩(wěn)定性,相等元素按原來順序排列。傳入的數(shù)組部分有序則能保證事件復(fù)雜度遠(yuǎn)小于nlg(n);若完全雜亂無序,則為n;若數(shù)組中存在連續(xù)升序或降序的情況都能被很好的利用起來

(這里的穩(wěn)定是指比較相等的數(shù)據(jù)在排序之后仍然按照排序之前的前后順序排列。對于基本數(shù)據(jù)類型,穩(wěn)定性沒有意義,而對于對象類型,穩(wěn)定性是比較重要的,因?yàn)閷ο笙嗟鹊呐袛嗫赡苤皇桥袛嚓P(guān)鍵屬性,最好保持相等對象的非關(guān)鍵屬性的順序與排序前一直;)

1) 當(dāng)LegacyMergeSort.userRequested為true的情況下(該分支會在未來被棄用),采用legacyMergeSort,否則采用ComparableTimSort。

(為什么會被棄用,如一開始所說的。TimSort就應(yīng)運(yùn)而生,包括接下來介紹的ComparableTimSort,與前者基本相同唯一區(qū)別的是后者需要對象是Comparable可比較的,不需要特定Comparator,而前者利用提供的Comparator進(jìn)行排序)

LegacyMergeSort.userRequested的字面意思大概就是“用戶請求傳統(tǒng)歸并排序”的意思,通過System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");

設(shè)置

mergeSort()

//To be removed in a future release.未來會棄用

@SuppressWarnings({"unchecked", "rawtypes"})

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) { //7

for (int i=low; i

for (int j=i; j>low &&

((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.

//這里說的是:mid兩側(cè)的元素都是有序的,若此時(shí)src[mid-1] <=src[mid],

則不用在進(jìn)行比較,節(jié)省時(shí)間

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++];

}

}

上面的代碼:當(dāng)數(shù)組大小小于7時(shí),采用插入排序,否則采用歸并排序

TimSort的重要思想是分區(qū)與合并

分區(qū)

分區(qū)的思想是掃描一次數(shù)組,把連續(xù)正序列(如果是升序排序,那么正序列就是升序序列)(也就是后面所指的run),如果是反序列,把分區(qū)里的元素反轉(zhuǎn)一下。 例如

1,2,3,6,4,5,8,6,4 劃分分區(qū)結(jié)果為

[1,2,3,6],[4,5,8],[6,4]

然后反轉(zhuǎn)反序列

[1,2,3,6],[4,5,8],[4,6]

合并

考慮一個(gè)極端的例子,比如分區(qū)的長度分別為 10000,10,1000,10,10,我們當(dāng)然希望是先讓10個(gè)10合并成20, 20和1000合并成1020如此下去, 如果從從左往右順序合并的話,每次都用到10000這個(gè)數(shù)組和去小的數(shù)組合并,代價(jià)太大了。所以我們可以用一個(gè)策略來優(yōu)化合并的順序。

2) 接著上面的例子,1)的LegacyMergeSort情況說完接下來調(diào)用的是ComparableTimSort.sort

static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen) {

assert a != null && lo >= 0 && lo <= hi && hi <= a.length;

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

//當(dāng)數(shù)組大小小于32是,調(diào)用“mini-TimeSort”

if (nRemaining < MIN_MERGE) { //32

int initRunLen = countRunAndMakeAscending(a, lo, hi);

binarySort(a, lo, hi, lo + initRunLen);

return;

}

..........未完待續(xù)

}

首先來分析該函數(shù)中當(dāng)數(shù)組大小小于32時(shí),調(diào)用的“mini-TimeSort”情況:

一開始調(diào)用了countRunAndMakeAscending(a, lo, hi)函數(shù),得到一個(gè)長度initRunLen

@SuppressWarnings({"unchecked", "rawtypes"})

private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {

assert lo < hi;

int runHi = lo + 1;

if (runHi == hi)

return 1;

// Find end of run, and reverse range if descending

if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending

while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)

runHi++;

reverseRange(a, lo, runHi);

} else { // Ascending

while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)

runHi++;

}

return runHi - lo;

}

該函數(shù)有啥用?數(shù)組a,求從lo開始連續(xù)的升序或降序(會被反轉(zhuǎn)變成升序)的元素個(gè)數(shù)。求出的這個(gè)長度有啥用途?優(yōu)化接下來調(diào)用的函數(shù)

binarySort(a, lo, lo + force, lo + runLen);代碼如下

//start參數(shù)傳進(jìn)來的是lo+runLen,現(xiàn)在數(shù)組情況是a[lo, lo+runlen-1]為升序,

a[lo+runLen, hi)為亂序,該方法就是從lo+runLen開始往后一個(gè)個(gè)取出來與前面有序數(shù)組進(jìn)行比較排序,

采用二分法

@SuppressWarnings({"fallthrough", "rawtypes", "unchecked"})

private static void binarySort(Object[] a, int lo, int hi, int start) {

assert lo <= start && start <= hi;

if (start == lo)

start++;

for ( ; start < hi; start++) {

Comparable pivot = (Comparable) 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 (pivot.compareTo(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;

}

}

前面提到的在數(shù)組大小<32情況下,采用“mini-TimeSort”,實(shí)質(zhì)是二分排序,利用countRunAndMakeAscending求得的長度來進(jìn)行優(yōu)化。

int start參數(shù)傳進(jìn)來的是lo+runLen,現(xiàn)在數(shù)組情況是a[lo, lo+runlen-1]為升序,a[lo+runLen, hi)為亂序,該方法就是從lo+runLen開始往后一個(gè)個(gè)取出來與前面有序數(shù)組進(jìn)行比較排序,采用二分法。該函數(shù)時(shí)間復(fù)雜度為nlg(n),不過在最壞情況下需要n^2次移動。

現(xiàn)在來分析數(shù)組數(shù)目>32的情況:

接著上面未完的代碼

/**

* 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.

*/

ComparableTimSort ts = new ComparableTimSort(a, work, workBase, workLen);

int minRun = minRunLength(nRemaining);

do {

// 找出下個(gè)分區(qū)的起始位置,方法上面介紹過

int runLen = countRunAndMakeAscending(a, lo, hi);

// 如果run stack中的run太小, 就擴(kuò)展至min(minRun, nRemaining)

if (runLen < minRun) {

int force = nRemaining <= minRun ? nRemaining : minRun;

binarySort(a, lo, lo + force, lo + runLen);

runLen = force;

}

// 把run放到run stack, 條件滿足會進(jìn)行合并

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;

//合并剩下的run

ts.mergeForceCollapse();

assert ts.stackSize == 1;

我們來分析代碼:首先ComparableTimSort ts = new ComparableTimSort(a, work, workBase, workLen);創(chuàng)建了ComparableTimSort對象

int stackLen = (len < 120 ? 5 :

len < 1542 ? 10 :

len < 119151 ? 24 : 49);

runBase = new int[stackLen];

runLen = new int[stackLen];

構(gòu)造函數(shù)里對這三個(gè)變量賦值,他們是干嘛的?

/**

* A stack of pending runs yet to be merged. Run i starts at

* address base[i] and extends for len[i] elements. It's always

* true (so long as the indices are in bounds) that:

*

* runBase[i] + runLen[i] == runBase[i + 1]

*

* so we could cut the storage for this, but it's a minor amount,

* and keeping all the info explicit simplifies the code.

*/

private int stackSize = 0; // run的個(gè)數(shù),run指的是分區(qū)

private final int[] runBase; // runBase[0]第一個(gè)分區(qū)里第一個(gè)元素下標(biāo),runBase[1]第二個(gè)分區(qū)第一個(gè)元素下標(biāo).....

private final int[] runLen; // runLen[0]第一個(gè)分區(qū)長度...

接著調(diào)用的是minRunLength,返回的數(shù)要么小于16,要么是16,要么介于[16, 32]之間

private static int minRunLength(int n) {

assert n >= 0;

int r = 0; // Becomes 1 if any 1 bits are shifted off

while (n >= MIN_MERGE) { //32

r |= (n & 1);

n >>= 1;

}

return n + r;

}

mergeCollapse:什么時(shí)候會進(jìn)行合并呢?之所以進(jìn)行判斷是為了防止這樣的情況:1000,10,100,10,10,這是五個(gè)分區(qū)的長度,最好的情況是先將小的分區(qū)合并,最后在和最大的分區(qū)合并,這個(gè)方法就是這個(gè)目的

//后兩個(gè)分區(qū)的和大于前一個(gè)分區(qū),則中間的分區(qū)與最小的分區(qū)先合并

//否則合并后兩個(gè)分區(qū)

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

}

}

}

2. 當(dāng)Comparable != null時(shí),調(diào)用的方法類似

在Comparator != null情況下主要調(diào)用了TimSort.sort,看TimSort的代碼與ComparableTimSort幾乎一樣,只是在元素比較時(shí)用了調(diào)用者給的Comparator來進(jìn)行比較。

暫時(shí)分析到這.........

總結(jié)

以上是生活随笔為你收集整理的java中mergesort函数怎么用_MergeSort与TimSort,ComparableTimSort的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。