1 冒泡排序
每次回圈都比較前后兩個元素的大小,如果前者大于后者,則將兩者進行交換,這樣做會將每次回圈中最大的元素替換到末尾,逐漸形成有序集合,將每次回圈中的最大元素逐漸由隊首轉移到隊尾的程序形似“冒泡”程序,故因此得名,
一個優化冒泡排序的方法就是如果在一次回圈的程序中沒有發生交換,則可以立即退出當前回圈,因為此時已經排好序了(也就是時間復雜度最好情況下是
的由來),
public int[] bubbleSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
for (int i = 0; i < array.length - 1; i++) {
boolean flag = false;
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
//這里交換兩個資料并沒有使用中間變數,而是使用異或的方式來實作
array[j] = array[j] ^ array[j + 1];
array[j + 1] = array[j] ^ array[j + 1];
array[j] = array[j] ^ array[j + 1];
flag = true;
}
}
if (!flag) {
break;
}
}
return array;
}
2 選擇排序
每次回圈都會找出當前回圈中最小的元素,然后和此次回圈中的隊首元素進行交換,
public int[] selectSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
for (int i = 0; i < array.length; i++) {
int minIndex = i;
for (int j = i + 1; j < array.length; j++) {
if (array[j] < array[minIndex]) {
minIndex = j;
}
}
if (minIndex > i) {
array[i] = array[i] ^ array[minIndex];
array[minIndex] = array[i] ^ array[minIndex];
array[i] = array[i] ^ array[minIndex];
}
}
return array;
}
3 插入排序
插入排序的精髓在于每次都會在先前排好序的子集合中插入下一個待排序的元素,每次都會判斷待排序元素的上一個元素是否大于待排序元素,如果大于,則將元素右移,然后判斷再上一個元素與待排序元素...以此類推,直到小于等于比較元素時就是找到了該元素的插入位置,這里的等于條件放在哪里很重要,因為它是決定插入排序穩定與否的關鍵,
public int[] insertSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
for (int i = 1; i < array.length; i++) {
int temp = array[i];
int j = i - 1;
while (j >= 0 && array[j] > temp) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = temp;
}
return array;
}
4 希爾排序
希爾排序可以認為是插入排序的改進版本,首先按照初始增量來將陣列分成多個組,每個組內部使用插入排序,然后縮小增量來重新分組,組內再次使用插入排序...重復以上步驟,直到增量變為1的時候,這個時候整個陣列就是一個分組,進行最后一次完整的插入排序即可結束,
在排序開始時的增量較大,分組也會較多,但是每個分組中的資料較少,所以插入排序會很快,隨著每一輪排序的進行,增量和分組數會逐漸變小,每個分組中的資料會逐漸變多,但因為之前已經經過了多輪的分組排序,而此時的陣列會趨近于一個有序的狀態,所以這個時候的排序也是很快的,而對于資料較多且趨向于無序的資料來說,如果只是使用插入排序的話效率就并不高,所以總體來說,希爾排序的執行效率是要比插入排序高的,
希爾排序執行示意圖:

具體的實作代碼如下:
public int[] shellSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
int gap = array.length >>> 1;
while (gap > 0) {
for (int i = gap; i < array.length; i++) {
int temp = array[i];
int j = i - gap;
while (j >= 0 && array[j] > temp) {
array[j + gap] = array[j];
j = j - gap;
}
array[j + gap] = temp;
}
gap >>>= 1;
}
return array;
}
5 堆排序
堆排序的程序是首先構建一個大頂堆,大頂堆首先是一棵完全二叉樹,其次它保證堆中某個節點的值總是不大于其父節點的值,
因為大頂堆中的最大元素肯定是根節點,所以每次取出根節點即為當前大頂堆中的最大元素,取出后剩下的節點再重新構建大頂堆,再取出根節點,再重新構建…重復這個程序,直到資料都被取出,最后取出的結果即為排好序的結果,
public class MaxHeap {
/**
* 排序陣列
*/
private int[] nodeArray;
/**
* 陣列的真實大小
*/
private int size;
private int parent(int index) {
return (index - 1) >>> 1;
}
private int leftChild(int index) {
return (index << 1) + 1;
}
private int rightChild(int index) {
return (index << 1) + 2;
}
private void swap(int i, int j) {
nodeArray[i] = nodeArray[i] ^ nodeArray[j];
nodeArray[j] = nodeArray[i] ^ nodeArray[j];
nodeArray[i] = nodeArray[i] ^ nodeArray[j];
}
private void siftUp(int index) {
//如果index處節點的值大于其父節點的值,則交換兩個節點值,同時將index指向其父節點,繼續向上回圈判斷
while (index > 0 && nodeArray[index] > nodeArray[parent(index)]) {
swap(index, parent(index));
index = parent(index);
}
}
private void siftDown(int index) {
//左孩子的索引比size小,意味著索引index處的節點有左孩子,證明此時index節點不是葉子節點
while (leftChild(index) < size) {
//maxIndex記錄的是index節點左右孩子中最大值的索引
int maxIndex = leftChild(index);
//右孩子的索引小于size意味著index節點含有右孩子
if (rightChild(index) < size && nodeArray[rightChild(index)] > nodeArray[maxIndex]) {
maxIndex = rightChild(index);
}
//如果index節點值比左右孩子值都大,則終止回圈
if (nodeArray[index] >= nodeArray[maxIndex]) {
break;
}
//否則進行交換,將index指向其交換的左孩子或右孩子,繼續向下回圈,直到葉子節點
swap(index, maxIndex);
index = maxIndex;
}
}
private void add(int value) {
nodeArray[size] = value;
size++;
//構建大頂堆
siftUp(size - 1);
}
private void extractMax() {
//將堆頂元素和最后一個元素進行交換
swap(0, size - 1);
//此時并沒有洗掉元素,而只是將size-1,剩下的元素重新構建成大頂堆
size--;
//重新構建大頂堆
siftDown(0);
}
public int[] heapSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
nodeArray = new int[array.length];
for (int value : array) {
add(value);
}
for (int i = 0; i < array.length; i++) {
extractMax();
}
return nodeArray;
}
}
上面的經典實作中,如果需要變動節點時,都會來一次父子節點的互相交換操作(包括洗掉節點時首先做的要洗掉節點和最后一個節點之間的交換操作也是如此),如果仔細思考的話,就會發現這其實是多余的,在需要交換節點的時候,只需要siftUp操作時的父節點或siftDown時的孩子節點重新移到當前需要比較的節點位置上,而比較節點是不需要移動到它們的位置上的,此時直接進入到下一次的判斷中,重復siftUp或siftDown程序,直到最后找到了比較節點的插入位置后,才會將其插入進去,這樣做的好處是可以省去一半的節點賦值的操作,提高了執行的效率,同時這也就意味著,需要將要比較的節點作為引數保存起來,而在ScheduledThreadPoolExecutor原始碼中也正是這么實作的(《較真兒學原始碼系列-ScheduledThreadPoolExecutor(逐行原始碼帶你分析作者思路)》),
6 歸并排序
歸并排序使用的是分治的思想,首先將陣列不斷拆分,直到最后拆分成兩個元素的子陣列,將這兩個元素進行排序合并,再向上遞回,不斷重復這個拆分和合并的遞回程序,最后得到的就是排好序的結果,
合并的程序是將兩個指標指向兩個子陣列的首位元素,兩個元素進行比較,較小的插入到一個temp陣列中,同時將該陣列的指標右移一位,繼續比較該陣列的第二個元素和另一個元素…重復這個程序,這樣temp陣列保存的便是這兩個子陣列排好序的結果,最后將temp陣列復制回原陣列的位置處即可,
public int[] mergeSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
return mergeSort(array, 0, array.length - 1);
}
private int[] mergeSort(int[] array, int left, int right) {
if (left < right) {
//這里沒有選擇“(left + right) / 2”的方式,是為了防止資料溢位
int mid = left + ((right - left) >>> 1);
// 拆分子陣列
mergeSort(array, left, mid);
mergeSort(array, mid + 1, right);
// 對子陣列進行合并
merge(array, left, mid, right);
}
return array;
}
private void merge(int[] array, int left, int mid, int right) {
int[] temp = new int[right - left + 1];
// p1和p2為需要對比的兩個陣列的指標,k為存放temp陣列的指標
int p1 = left, p2 = mid + 1, k = 0;
while (p1 <= mid && p2 <= right) {
if (array[p1] <= array[p2]) {
temp[k++] = array[p1++];
} else {
temp[k++] = array[p2++];
}
}
// 把剩余的陣列直接放到temp陣列中
while (p1 <= mid) {
temp[k++] = array[p1++];
}
while (p2 <= right) {
temp[k++] = array[p2++];
}
// 復制回原陣列
for (int i = 0; i < temp.length; i++) {
array[i + left] = temp[i];
}
}
7 快速排序
快速排序的核心是要有一個基準資料temp,一般取陣列的第一個位置元素,然后需要有兩個指標left和right,分別指向陣列的第一個和最后一個元素,
首先從right開始,比較right位置元素和基準資料,如果大于等于,則將right指標左移,比較下一位元素;如果小于,就將right指標處資料賦給left指標處(此時left指標處資料已保存進temp中),left指標+1,之后開始比較left指標處資料,
拿left位置元素和基準資料進行比較,如果小于等于,則將left指標右移,比較下一位元素;而如果大于就將left指標處資料賦給right指標處,right指標-1,之后開始比較right指標處資料…重復這個程序,
直到left和right指標相等時,說明這一次比較程序完成,此時將先前存放進temp中的基準資料賦值給當前left和right指標共同指向的位置處,即可完成這一次排序操作,
之后遞回排序基礎資料的左半部分和右半部分,遞回的程序和上面講述的程序是一樣的,只不過陣列范圍不再是原來的全部陣列了,而是現在的左半部分或右半部分,當全部的遞回程序結束后,最終結果即為排好序的結果,
快速排序執行示意圖:

正如上面所說的,一般取第一個元素作為基準資料,但如果當前資料為從大到小排列好的資料,而現在要按從小到大的順序排列,則資料分攤不均勻,時間復雜度會退化為
,而不是正常情況下的
,此時采取一個優化手段,即取最左邊、最右邊和最中間的三個元素的中間值作為基準資料,以此來避免時間復雜度為
的情況出現,當然也可以選擇更多的錨點或者隨機選擇的方式來進行選取,
還有一個優化的方法是:像快速排序、歸并排序這樣的復雜排序方法在資料量大的情況下是比選擇排序、冒泡排序和插入排序的效率要高的,但是在資料量小的情況下反而要更慢,所以我們可以選定一個閾值,這里選擇為47(和原始碼中使用的一樣),當需要排序的資料量小于47時走插入排序,大于47則走快速排序,
private static final int THRESHOLD = 47;
public int[] quickSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
return quickSort(array, 0, array.length - 1);
}
private int[] quickSort(int[] array, int start, int end) {
// 如果當前需要排序的資料量小于等于THRESHOLD則走插入排序的邏輯,否則繼續走快速排序
if (end - start <= THRESHOLD - 1) {
return insertSort(array);
}
// left和right指標分別指向array的第一個和最后一個元素
int left = start, right = end;
/*
取最左邊、最右邊和最中間的三個元素的中間值作為基準資料,以此來盡量避免每次都取第一個值作為基準資料、
時間復雜度可能退化為O(n^2)的情況出現
*/
int middleOf3Indexs = middleOf3Indexs(array, start, end);
if (middleOf3Indexs != start) {
swap(array, middleOf3Indexs, start);
}
// temp存放的是array中需要比較的基準資料
int temp = array[start];
while (left < right) {
// 首先從right指標開始比較,如果right指標位置處資料大于temp,則將right指標左移
while (left < right && array[right] >= temp) {
right--;
}
// 如果找到一個right指標位置處資料小于temp,則將right指標處資料賦給left指標處
if (left < right) {
array[left] = array[right];
left++;
}
// 然后從left指標開始比較,如果left指標位置處資料小于temp,則將left指標右移
while (left < right && array[left] <= temp) {
left++;
}
// 如果找到一個left指標位置處資料大于temp,則將left指標處資料賦給right指標處
if (left < right) {
array[right] = array[left];
right--;
}
}
// 當left和right指標相等時,此時回圈跳出,將之前存放的基準資料賦給當前兩個指標共同指向的資料處
array[left] = temp;
// 一次替換后,遞回交換基準資料左邊的資料
if (start < left - 1) {
array = quickSort(array, start, left - 1);
}
// 之后遞回交換基準資料右邊的資料
if (right + 1 < end) {
array = quickSort(array, right + 1, end);
}
return array;
}
private int middleOf3Indexs(int[] array, int start, int end) {
int mid = start + ((end - start) >>> 1);
if (array[start] < array[mid]) {
if (array[mid] < array[end]) {
return mid;
} else {
return array[start] < array[end] ? end : start;
}
} else {
if (array[mid] > array[end]) {
return mid;
} else {
return array[start] < array[end] ? start : end;
}
}
}
private void swap(int[] array, int i, int j) {
array[i] = array[i] ^ array[j];
array[j] = array[i] ^ array[j];
array[i] = array[i] ^ array[j];
}
8 計數排序
以上的七種排序演算法都是比較排序,也就是基于元素之間的比較來進行排序的,而下面將要介紹的三種排序演算法是非比較排序,首先是計數排序,
計數排序會創建一個臨時的陣列,里面存放每個數出現的次數,比如一個待排序的陣列是[3, 3, 5, 2, 7, 4, 2],那么這個臨時陣列中記錄的資料就是[2, 2, 1, 1, 0, 1],表示2出現了兩次、3出現了兩次、4出現了一次、5出現了一次、6出現了零次、7出現了一次,那么最后只需要遍歷這個臨時陣列中的計數值就可以了,
public int[] countingSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
//記錄待排序陣列中的最大值
int max = array[0];
//記錄待排序陣列中的最小值
int min = array[0];
for (int i : array) {
if (i > max) {
max = i;
}
if (i < min) {
min = i;
}
}
int[] temp = new int[max - min + 1];
//記錄每個數出現的次數
for (int i : array) {
temp[i - min]++;
}
int index = 0;
for (int i = 0; i < temp.length; i++) {
//當輸出一個數之后,當前位置的計數就減一,直到減到0為止
while (temp[i]-- > 0) {
array[index++] = i + min;
}
}
return array;
}
從上面的實作中可以看到,計數排序僅適合資料跨度不大的場景,如果最大值和最小值之間的差距比較大,生成的臨時陣列就會比較長,比如說一個陣列是[2, 1, 3, 1000],最小值是1,最大值是1000,那么就會生成一個長度為1000的臨時陣列,但是其中絕大部分的空間都是沒有用的,所以這就會導致空間復雜度變得很高,
計數排序是穩定的排序演算法,但在上面的實作中并沒有體現出這一點,上面的實作沒有維護相同元素之間的先后順序,所以需要做些變換:將臨時陣列中從第二個元素開始,每個元素都加上前一個元素的值,還是拿之前的[3, 3, 5, 2, 7, 4, 2]陣列來舉例,計完數后的臨時陣列為[2, 2, 1, 1, 0, 1],此時做上面的變換,每個數都累加前面的一個數,結果為[2, 4, 5, 6, 6, 7],這個時候臨時陣列的含義就不再是每個數出現的次數了,此時記錄的是每個數在最后排好序的陣列中應該要存放的位置+1(如果有重復的就記錄最后一個),對于上面的待排序陣列來說,最后排好序的陣列應該為[2, 2, 3, 3, 4, 5, 7],也就是說,此時各個數最后一次出現的索引位為:1, 3, 4, 5, 6,分別都+1后就是2, 4, 5, 6, 7,這不就是上面做過變換之后的陣列嗎?(沒有出現過的數字不管它)所以,此時從后往前遍歷原陣列中的每一個值,將其減去最小值后,找到其在變換后的臨時陣列中的索引,也就是找到了最后排好序的陣列中的位置了,當然,每次找到臨時陣列中的索引后,這個位置的數需要-1,這樣如果后續有重復的該數字的話,就會插入到當前位置的前一個位置了,由此也說明了遍歷必須是從后往前遍歷,以此來維護相同數字之間的先后順序,
public int[] stableCountingSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
//記錄待排序陣列中的最大值
int max = array[0];
//記錄待排序陣列中的最小值
int min = array[0];
for (int i : array) {
if (i > max) {
max = i;
}
if (i < min) {
min = i;
}
}
int[] temp = new int[max - min + 1];
//記錄每個數出現的次數
for (int i : array) {
temp[i - min]++;
}
//將temp陣列進行轉換,記錄每個數在最后排好序的陣列中應該要存放的位置+1(如果有重復的就記錄最后一個)
for (int j = 1; j < temp.length; j++) {
temp[j] += temp[j - 1];
}
int[] sortedArray = new int[array.length];
//這里必須是從后往前遍歷,以此來保證穩定性
for (int i = array.length - 1; i >= 0; i--) {
sortedArray[temp[array[i] - min] - 1] = array[i];
temp[array[i] - min]--;
}
return sortedArray;
}
9 桶排序
上面的計數排序在陣列最大值和最小值之間的差值是多少,就會生成一個多大的臨時陣列,也就是生成了一個這么多的桶,而每個桶中就只插入一個資料,如果差值比較大的話,會比較浪費空間,那么我能不能在一個桶中插入多個資料呢?當然可以,而這就是桶排序的思路,桶排序類似于哈希表,通過一定的映射規則將陣列中的元素映射到不同的桶中,每個桶內進行內部排序,最后將每個桶按順序輸出就行了,桶排序執行的高效與否和是否是穩定的取決于哈希散列的演算法以及內部排序的結果,需要注意的是,這個映射演算法并不是常規的映射演算法,要求是每個桶中的所有數都要比前一個桶中的所有數都要大,這樣最后輸出的才是一個排好序的結果,比如說第一個桶中存1-30的數字,第二個桶中存31-60的數字,第三個桶中存61-90的數字...以此類推,下面給出一種實作:
public int[] bucketSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
//記錄待排序陣列中的最大值
int max = array[0];
//記錄待排序陣列中的最小值
int min = array[0];
for (int i : array) {
if (i > max) {
max = i;
}
if (i < min) {
min = i;
}
}
//計算桶的數量(可以自定義實作)
int bucketNumber = (max - min) / array.length + 1;
List<Integer>[] buckets = new ArrayList[bucketNumber];
//計算每個桶存數的范圍(可以自定義實作或者不用實作)
int bucketRange = (max - min + 1) / bucketNumber;
for (int value : array) {
//計算應該放到哪個桶中(可以自定義實作)
int bucketIndex = (value - min) / (bucketRange + 1);
//延遲初始化
if (buckets[bucketIndex] == null) {
buckets[bucketIndex] = new ArrayList<>();
}
//放入指定的桶
buckets[bucketIndex].add(value);
}
int index = 0;
for (List<Integer> bucket : buckets) {
//對每個桶進行內部排序,我這里使用的是快速排序,也可以使用別的排序演算法,當然也可以繼續遞回去做桶排序
quickSort(bucket);
if (bucket == null) {
continue;
}
//將不為null的桶中的資料按順序寫回到array陣列中
for (Integer integer : bucket) {
array[index++] = integer;
}
}
return array;
}
10 基數排序
基數排序不是根據一個數的整體來進行排序的,而是將數的每一位上的數字進行排序,比如說第一輪排序,我拿到待排序陣列中所有數個位上的數字來進行排序;第二輪排序我拿到待排序陣列中所有數十位上的數字來進行排序;第三輪排序我拿到待排序陣列中所有數百位上的數字來進行排序...以此類推,每一輪的排序都會累加上一輪所有前幾位上排序的結果,最終的結果就會是一個有序的數列,
基數排序一般是對所有非負整數進行排序的,但是也可以有別的手段來去掉這種限制(比如都加一個固定的數或者都乘一個固定的數,排完序后再恢復等等),基數排序和桶排序很像,桶排序是按數值的區間進行劃分,而基數排序是按數的位數進行劃分,同時這兩個排序都是需要依靠其他排序演算法來實作的(如果不算遞回呼叫桶排序本身的話),基數排序每一輪的內部排序會使用到計數排序來實作,因為每一位上的數字無非就是0-9,是一個小范圍的數,所以使用計數排序很合適,
基數排序執行示意圖:

具體的實作代碼如下:
public int[] radixSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
//記錄待排序陣列中的最大值
int max = array[0];
for (int i : array) {
if (i > max) {
max = i;
}
}
//獲取最大值的位數
int maxDigits = 0;
while (max != 0) {
max /= 10;
maxDigits++;
}
//用來計數排序的臨時陣列
int[] temp = new int[10];
//用來存放每輪排序后的結果
int[] sortedArray = new int[array.length];
for (int d = 1; d <= maxDigits; d++) {
//每次回圈開始前都要清空temp陣列中的值
replaceArray(temp, null);
//記錄每個數出現的次數
for (int a : array) {
temp[getNumberFromDigit(a, d)]++;
}
//將temp陣列進行轉換,記錄每個數在最后排好序的陣列中應該要存放的位置+1(如果有重復的就記錄最后一個)
for (int j = 1; j < temp.length; j++) {
temp[j] += temp[j - 1];
}
//這里必須是從后往前遍歷,以此來保證穩定性
for (int i = array.length - 1; i >= 0; i--) {
int index = getNumberFromDigit(array[i], d);
sortedArray[temp[index] - 1] = array[i];
temp[index]--;
}
//一輪計數排序過后,將這次排好序的結果賦值給原陣列
replaceArray(array, sortedArray);
}
return array;
}
private final static int[] sizeTable = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
/**
* 獲取指定位數上的數字是多少
*/
private int getNumberFromDigit(int number, int digit) {
if (digit < 0) {
return -1;
}
return (number / sizeTable[digit - 1]) % 10;
}
private void replaceArray(int[] originalArray, int[] replaceArray) {
if (replaceArray == null) {
for (int i = 0; i < originalArray.length; i++) {
originalArray[i] = 0;
}
} else {
for (int i = 0; i < originalArray.length; i++) {
originalArray[i] = replaceArray[i];
}
}
}
11 復雜度及穩定性
| 排序演算法 | 時間復雜度 | 空間復雜度 | 穩定性 | ||
|---|---|---|---|---|---|
| 平均情況 | 最好情況 | 最壞情況 | |||
| 冒泡排序 | ![]() |
![]() |
![]() |
![]() |
穩定 |
| 選擇排序 | ![]() |
![]() |
![]() |
![]() |
不穩定 |
| 插入排序 | ![]() |
![]() |
![]() |
![]() |
穩定 |
| 希爾排序 | 取決于增量的選擇 | ![]() |
不穩定 | ||
| 堆排序 | ![]() |
![]() |
![]() |
![]() |
不穩定 |
| 歸并排序 | ![]() |
![]() |
![]() |
![]() |
穩定 |
| 快速排序 | ![]() |
![]() |
![]() |
![]() |
不穩定 |
| 計數排序 | ![]() |
![]() |
![]() |
![]() |
穩定 |
| 桶排序 | 取決于桶散列的結果和內部排序演算法的時間復雜度 | ![]() |
穩定 | ||
| 基數排序 | ![]() |
![]() |
![]() |
![]() |
穩定 |
(其中:
- k表示計數排序中最大值和最小值之間的差值;
- l表示桶排序中桶的個數;
- d表示基數排序中最大值的位數,r表示是多少進制;
- 希爾排序的時間復雜度很大程度上取決于增量gap sequence的選擇,不同的增量會有不同的時間復雜度,文中使用的“gap=length/2”和“gap=gap/2”是一種常用的方式,也被稱為希爾增量,但其并不是最優的,其實希爾排序增量的選擇與證明一直都是個數學難題,而下圖列出的是迄今為止大部分的gap sequence選擇的方案)

12 小彩蛋:猴子排序
在幾十年的計算機科學發展中,誕生了很多優秀的演算法,大家都在為了能開發出更高效的演算法而努力,但是在這其中也誕生了一些僅供娛樂的搞笑演算法,猴子排序就是其中的一種,
猴子排序的實作很簡單,隨機找出兩個元素進行交換,直到隨機交換到最后能正確排好序的時候才會停止,
public int[] bogoSort(int[] array) {
if (array == null || array.length < 2) {
return array;
}
Random random = new Random();
while (!inOrder(array)) {
for (int i = 0; i < array.length; i++) {
int swapPosition = random.nextInt(i + 1);
if (swapPosition == i) {
continue;
}
array[i] = array[i] ^ array[swapPosition];
array[swapPosition] = array[i] ^ array[swapPosition];
array[i] = array[i] ^ array[swapPosition];
}
}
return array;
}
private boolean inOrder(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
if (array[i] > array[i + 1]) {
return false;
}
}
return true;
}
更多內容請關注微信公眾號:奇客時間

原創不易,未得準許,請勿轉載,翻版必究
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/173109.html
標籤:Java







