目錄:
一战得、排序算法說明
? ? ?1.排序的定義
? ? ?2.術(shù)語解析
? ? ?3.算法分類
? ? ?4.比較和非比較的區(qū)別
? ? ?5.排序算法中IN-PLACE和OUT-PLACE是什么意思壁公?
? ? ?6.算法總結(jié)
二愿险、算法實現(xiàn)
歡迎下方評論留言节榜,文章持續(xù)更新優(yōu)化
一孝鹊、排序算法說明
1.排序的定義
對一序列對象根據(jù)某個關(guān)鍵字進行排序坪圾。
2.術(shù)語解析
穩(wěn)定 :如果a原本在b前面讼油,而a=b杰赛,排序之后a仍然在b的前面;
不穩(wěn)定 :如果a原本在b的前面矮台,而a=b乏屯,排序之后a可能會出現(xiàn)在b的后面;
????關(guān)于排序穩(wěn)定性的定義:
通俗地講就是能保證排序前兩個相等的數(shù)其在序列的前后位置順序和排序后它們兩個的前后位置順序相同瘦赫。在簡單形式化一下辰晕,如果Ai = Aj,Ai原來在位置前确虱,排序后Ai還是要在Aj位置前含友。
????問題來了,什么時候必須要求使用穩(wěn)定排序呢校辩?
由上面的定義可知道穩(wěn)定性排序保證了排序前兩個相等的數(shù)其在序列的前后位置順序和排序后它們兩個的前后位置順序相同窘问。那么,當現(xiàn)實應(yīng)用中排序的需求需要區(qū)別先后順序的時候就必須用到穩(wěn)定排序宜咒。
舉個例子:
??假如發(fā)獎學(xué)金惠赫,排在前三個的分別獲一、二故黑、三等獎儿咱,結(jié)果一排序把原來的第二位和第三位相同分數(shù)的進行了置換庭砍,估計拿到三等獎的不會樂意。
引用自 http://www.reibang.com/p/abe27f16b7b5
內(nèi)排序 :所有排序操作都在內(nèi)存中完成混埠;
外排序:由于數(shù)據(jù)太大逗威,因此把數(shù)據(jù)放在磁盤中,而排序通過磁盤和內(nèi)存的數(shù)據(jù)傳輸才能進行岔冀;
時間復(fù)雜度 : 一個算法執(zhí)行所耗費的時間凯旭。
空間復(fù)雜度 :運行完一個程序所需內(nèi)存的大小。
3.算法分類
??排序算法可以分為內(nèi)部排序和外部排序使套,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序罐呼,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部的排序記錄侦高,在排序過程中需要訪問外存嫉柴。
4.比較和非比較的區(qū)別
比較類排序:通過比較來決定元素間的相對次序,由于其時間復(fù)雜度不能突破O(nlogn)奉呛,因此也稱為非線性時間比較類排序计螺。
非比較類排序:不通過比較來決定元素間的相對次序,它可以突破基于比較排序的時間下界瞧壮,以線性時間運行登馒,因此也稱為線性時間非比較類排序
5.排序算法中IN-PLACE和OUT-PLACE是什么意思?
IN-PLACE: 占用常數(shù)內(nèi)存咆槽,不占用額外內(nèi)存
OUT-PLACE :占用額外內(nèi)存
IN-PLACE
假如問題規(guī)模是n陈轿,在解決問題過程中,只開辟了常數(shù)量的空間秦忿,與n無關(guān)麦射,這是原址操作,就是In-place灯谣。
舉個例子:
/**
* @param arr
* @return 冒泡排序潜秋,輸出升序排序數(shù)組
*/
public static int[] bubbleSort(int[] arr) {
//為了不對原數(shù)組的數(shù)據(jù)進行修改
arr = Arrays.copyOf(arr, arr.length);
int length = arr.length;
int t;
for (int i = 1; i < arr.length; i++) {
// 設(shè)定一個標記,若為true胎许,則表示此次循環(huán)沒有進行交換峻呛,也就是待排序列已經(jīng)有序,排序已經(jīng)完成呐萨。
boolean flag = true;
for (int j = 0; j < length - i; j++) {
//若是需要輸出降序的數(shù)據(jù)結(jié)果杀饵,只需要把下面的 > 換成 <
if (arr[j] > arr[j + 1]) {
t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
flag = false;
}
}
if (flag) {
return arr;
}
}
return arr;
}
在冒泡排序中,為了將arr排序谬擦,借用了一個t 的臨時變量切距,開辟了一個臨時空間,這個空間是常數(shù)量惨远,這就是in-place谜悟。
OUT-PLACE
如果開辟的輔助空間與問題規(guī)模有關(guān)话肖,則是out-place。
拿上面的例子來說葡幸,假設(shè)你把排序時把數(shù)組中的數(shù)按順序放入了一個新的數(shù)組最筒,我就開了一個n規(guī)模大小的數(shù)組,這個就與數(shù)據(jù)規(guī)模有關(guān)
6.算法總結(jié)
圖片名詞解釋:
n: 數(shù)據(jù)規(guī)模
k: “桶”的個數(shù)
In-place: 占用常數(shù)內(nèi)存蔚叨,不占用額外內(nèi)存
Out-place: 占用額外內(nèi)存
二床蜘、算法實現(xiàn)
算法實現(xiàn)參考以下文章 :
十大經(jīng)典排序算法-菜鳥教程
十大經(jīng)典排序算法(動圖演示)
排序算法
1.冒泡排序
冒泡排序是一種簡單的排序算法。它重復(fù)地走訪過要排序的數(shù)列蔑水,一次比較兩個元素邢锯,如果它們的順序錯誤就把它們交換過來。走訪數(shù)列的工作是重復(fù)地進行直到?jīng)]有再需要交換搀别,也就是說該數(shù)列已經(jīng)排序完成丹擎。這個算法的名字由來是因為越小的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端。
算法步驟:
- 比較相鄰的元素歇父。如果第一個比第二個大蒂培,就交換它們兩個;
- 對每一對相鄰元素作同樣的工作榜苫,從開始第一對到結(jié)尾的最后一對护戳,這樣在最后的元素應(yīng)該會是最大的數(shù);
- 針對所有的元素重復(fù)以上的步驟单刁,除了最后一個灸异;
- 重復(fù)步驟1~3,直到排序完成羔飞。
代碼實現(xiàn)
/**
* @author mumuxi
* @date 2020/1/19
*/
public class Test {
public static void main(String[] args) {
int[] sourceArray = new int[]{10, 5, 34, 56, 676, 43545, 232, 232, 1};
for (int i = 0; i < sourceArray.length; i++) {
System.out.println("source[" + i + "] = " + sourceArray[i]);
}
//如果不想對原數(shù)組的數(shù)據(jù)進行修改,可以進行copy后再排序
// int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
// bubbleSort(arr);
bubbleSort(sourceArray);
for (int i = 0; i < sourceArray.length; i++) {
System.out.println("source[" + i + "] = " + sourceArray[i]);
}
}
/**
* @param arr
* @return 冒泡排序檐春,輸出升序排序數(shù)組
*/
public static int[] bubbleSort(int[] arr) {
int length = arr.length;
int t;
for (int i = 1; i < arr.length; i++) {
// 設(shè)定一個標記逻淌,若為true,則表示此次循環(huán)沒有進行交換疟暖,也就是待排序列已經(jīng)有序卡儒,排序已經(jīng)完成。
boolean flag = true;
for (int j = 0; j < length - i; j++) {
//若是需要輸出降序的數(shù)據(jù)結(jié)果俐巴,只需要把下面的 > 換成 <
if (arr[j] > arr[j + 1]) {
t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
flag = false;
}
}
if (flag) {
return arr;
}
}
return arr;
}
}
冒泡排序還有一種優(yōu)化算法骨望,就是立一個 flag,當在一次序列遍歷中元素沒有發(fā)生交換欣舵,則證明該序列已經(jīng)有序擎鸠。(如上面的代碼所示)
冒泡排序在什么情況下最快?
當輸入的數(shù)據(jù)已經(jīng)是正序時缘圈。
那么在什么情況下最慢呢劣光?
當輸入的數(shù)據(jù)是反序時袜蚕。
2.選擇排序
??選擇排序是一種簡單直觀的排序算法,無論什么數(shù)據(jù)進去都是 O(n2) 的時間復(fù)雜度绢涡。所以用到它的時候牲剃,數(shù)據(jù)規(guī)模越小越好。唯一的好處可能就是不占用額外的內(nèi)存空間了吧雄可。
算法步驟:
- 首先在未排序序列中找到最性涓怠(大)元素,存放到排序序列的起始位置数苫。
- 再從剩余未排序元素中繼續(xù)尋找最邢凉椤(大)元素,然后放到已排序序列的末尾文判。
- 重復(fù)第二步过椎,直到所有元素均排序完畢。
代碼實現(xiàn)
/**
* @param arr
* @return 選擇排序, 輸出升序結(jié)果
*/
public static int[] selectionSort(int[] arr) {
int length = arr.length;
int min, tmp;
// 總共要經(jīng)過 N-1 輪比較
for (int i = 0; i < length - 1; i++) {
min = i;
// 每輪需要比較的次數(shù) N-i
for (int j = i + 1; j < length; j++) {
//若是需要輸出降序結(jié)果戏仓,則把下面的 < 改為 > 即可
if (arr[j] > arr[min]) {
// 記錄目前能找到的最小值元素的下標
min = j;
}
}
// 將找到的最小值和i位置所在的值進行交換
if (min != i) {
tmp = arr[min];
arr[min] = arr[i];
arr[i] = tmp;
}
}
return arr;
}
3.插入排序
??插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法疚宇。它的工作原理是通過構(gòu)建有序序列,對于未排序數(shù)據(jù)赏殃,在已排序序列中從后向前掃描敷待,找到相應(yīng)位置并插入。插入排序在實現(xiàn)上仁热,通常采用in-place排序(即只需用到O(1)的額外空間的排序)榜揖,因而在從后向前掃描過程中,需要反復(fù)把已排序元素逐步向后挪位抗蠢,為最新元素提供插入空間举哟。
算法步驟:
- 從第一個元素開始,該元素可以認為已經(jīng)被排序迅矛;
- 取出下一個元素妨猩,在已經(jīng)排序的元素序列中從后向前掃描;
- 如果該元素(已排序)大于新元素秽褒,將該元素移到下一位置壶硅;
- 重復(fù)步驟3,直到找到已排序的元素小于或者等于新元素的位置销斟;
- 將新元素插入到該位置后庐椒;
- 重復(fù)步驟2~5;
代碼實現(xiàn)
/**
* @param arr
* @return 插入排序蚂踊,輸出升序排序結(jié)果
*/
public static int[] insertionSort(int[] arr) {
int length = arr.length;
//保存要插入的數(shù)據(jù)
int tmp;
int j;
// 從下標為1的元素開始選擇合適的位置插入约谈,因為下標為0的只有一個元素,默認是有序的
for (int i = 1; i < length; i++) {
// 記錄要插入的數(shù)據(jù)
tmp = arr[i];
// 從已經(jīng)排序的序列最右邊的開始比較,找到比其小的數(shù)
j = i;
//輸出降序的排序結(jié)果只需要把下面的 < 換成 >
while (j > 0 && tmp < arr[j - 1]) {
arr[j] = arr[j - 1];
j--;
}
// 存在比其小的數(shù)窗宇,插入
if (j != i) {
arr[j] = tmp;
}
}
return arr;
}
4.希爾排序
??希爾排序措伐,也稱遞減增量排序算法,是插入排序的一種更高效的改進版本军俊。但希爾排序是非穩(wěn)定排序算法侥加。希爾排序的基本思想是:先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄"基本有序"時粪躬,再對全體記錄進行依次直接插入排序担败。
希爾排序是基于插入排序的以下兩點性質(zhì)而提出改進方法的:
- 插入排序在對幾乎已經(jīng)排好序的數(shù)據(jù)操作時,效率高镰官,即可以達到線性排序的效率提前;
- 但插入排序一般來說是低效的,因為插入排序每次只能將數(shù)據(jù)移動一位泳唠;
算法步驟:
- 選擇一個增量序列 t1狈网,t2,……笨腥,tk拓哺,其中 ti > tj, tk = 1;
- 按增量序列個數(shù) k脖母,對序列進行 k 趟排序士鸥;
- 每趟排序,根據(jù)對應(yīng)的增量 ti谆级,將待排序列分割成若干長度為 m 的子序列烤礁,分別對各子表進行直接插入排序。僅增量因子為 1 時肥照,整個序列作為一個表來處理脚仔,表長度即為整個序列的長度。
代碼實現(xiàn)
/**
* @param arr
* @return 希爾排序建峭,返回升序排序結(jié)果
*/
public static int[] shellSort(int[] arr) {
//增量的選取有多種玻侥,這里取最原始的 gap = n/2;
int gap = arr.length / 2;
while (gap > 0) {
//下面的for循環(huán)實際上就是普通的插入算法,普通的插入算法 gap(增量) = 1 ,這里只是把 1 換成了 gap(增量)
for (int i = gap; i < arr.length; i++) {
int tmp = arr[i];
int j = i;
//這里比普通的插入算法多加了個判斷 j-gap >=0;下面的 tmp < arr[j - gap]
//換成 tmp < arr[j - gap]就會輸出降序排序
while (j > 0 && j - gap >= 0 && tmp < arr[j - gap]) {
arr[j] = arr[j - gap];
j = j - gap;
}
if (j != i) {
arr[j] = tmp;
}
}
gap = gap / 2;
}
return arr;
}
5.歸并排序
??歸并排序(Merge sort)是建立在歸并操作上的一種有效的排序算法亿蒸。該算法是采用分治法(Divide and Conquer)的一個非常典型的應(yīng)用。
作為一種典型的分而治之思想的算法應(yīng)用掌桩,歸并排序的實現(xiàn)由兩種方法:
- 自上而下的遞歸(所有遞歸的方法都可以用迭代重寫边锁,所以就有了第 2 種方法);
- 自下而上的迭代波岛;
算法步驟:
申請空間茅坛,使其大小為兩個已經(jīng)排序序列之和,該空間用來存放合并后的序列;
設(shè)定兩個指針贡蓖,最初位置分別為兩個已經(jīng)排序序列的起始位置曹鸠;
比較兩個指針所指向的元素,選擇相對小的元素放入到合并空間斥铺,并移動指針到下一位置彻桃;
重復(fù)步驟 3 直到某一指針達到序列尾;
將另一序列剩下的所有元素直接復(fù)制到合并序列尾晾蜘。
代碼實現(xiàn)
/**
* @param arr
* @return 歸并排序邻眷,返回升序數(shù)組,不影響原數(shù)組數(shù)據(jù)
*/
public static int[] mergeSort(int[] arr) {
if (arr.length < 2) {
return arr;
}
int length = arr.length / 2;
int middle = (int) Math.floor(length);
int[] left = Arrays.copyOfRange(arr, 0, middle);
int[] right = Arrays.copyOfRange(arr, middle, arr.length);
return merge(mergeSort(left), mergeSort(right));
}
private static int[] merge(int[] left, int[] right) {
int[] result = new int[left.length + right.length];
int i = 0;
while (left.length > 0 && right.length > 0) {
//下面的< 換成 > 就是返回降序的數(shù)據(jù)
if (left[0] <= right[0]) {
result[i++] = left[0];
left = Arrays.copyOfRange(left, 1, left.length);
} else {
result[i++] = right[0];
right = Arrays.copyOfRange(right, 1, right.length);
}
}
while (left.length > 0) {
result[i++] = left[0];
left = Arrays.copyOfRange(left, 1, left.length);
}
while (right.length > 0) {
result[i++] = right[0];
right = Arrays.copyOfRange(right, 1, right.length);
}
return result;
}
圖文解析
6.快速排序
快速排序的基本思想:通過一趟排序?qū)⒋庞涗浄指舫瑟毩⒌膬刹糠痔藿唬渲幸徊糠钟涗浀年P(guān)鍵字均比另一部分的關(guān)鍵字小肆饶,則可分別對這兩部分記錄繼續(xù)進行排序,以達到整個序列有序岖常⊙蹦鳎快速排序又是一種分而治之思想在排序算法上的典型應(yīng)用。本質(zhì)上來看竭鞍,快速排序應(yīng)該算是在冒泡排序基礎(chǔ)上的遞歸分治法板惑。
算法步驟:
快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。具體算法描述如下:
- 從數(shù)列中挑出一個元素笼蛛,稱為 “基準”(pivot)洒放;
- 重新排序數(shù)列,所有元素比基準值小的擺放在基準前面滨砍,所有元素比基準值大的擺在基準的后面(相同的數(shù)可以到任一邊)往湿。在這個分區(qū)退出之后,該基準就處于數(shù)列的中間位置惋戏。這個稱為分區(qū)(partition)操作领追;
- 遞歸地(recursive)把小于基準值元素的子數(shù)列和大于基準值元素的子數(shù)列排序。
代碼實現(xiàn)
/**
* @author mumuxi
* @date 2020/1/19
*/
public class Test {
public static void main(String[] args) {
int[] sourceArray = new int[]{1, 0, 10, 78, 898, 874, 56, 9, 98, 100, 300};
int[] result = Arrays.copyOf(sourceArray, sourceArray.length);
result = quickSort(result, 0, result.length-1);
for (int i = 0; i < sourceArray.length; i++) {
System.out.println(sourceArray[i]);
}
for (int i = 0; i < result.length; i++) {
System.out.println(result[i]);
}
}
public static int[] quickSort(int[] arr, int left, int right) {
if (left < right) {
int partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex - 1);
quickSort(arr, partitionIndex + 1, right);
}
return arr;
}
private static int partition(int[] arr, int left, int right) {
// 設(shè)定基準值(pivot)
int pivot = left;
int index = pivot + 1;
for (int i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
swap(arr, pivot, index - 1);
return index - 1;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
7.堆排序
堆排序(Heapsort)是指利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計的一種排序算法响逢。堆積是一個近似完全二叉樹的結(jié)構(gòu)绒窑,并同時滿足堆積的性質(zhì):即子結(jié)點的鍵值或索引總是小于(或者大于)它的父節(jié)點。堆排序可以說是一種利用堆的概念來排序的選擇排序舔亭。分為兩種方法:
- 大頂堆:每個節(jié)點的值都大于或等于其子節(jié)點的值些膨,在堆排序算法中用于升序排列;
- 小頂堆:每個節(jié)點的值都小于或等于其子節(jié)點的值钦铺,在堆排序算法中用于降序排列订雾;
堆排序的平均時間復(fù)雜度為 Ο(nlogn)。
算法步驟:
- 創(chuàng)建一個堆 H[0……n-1]矛洞;
- 把堆首(最大值)和堆尾互換洼哎;
- 把堆的尺寸縮小 1,并調(diào)用 shift_down(0),目的是把新的數(shù)組頂端數(shù)據(jù)調(diào)整到相應(yīng)位置噩峦;
- 重復(fù)步驟 2锭沟,直到堆的尺寸為 1。
代碼實現(xiàn)
public static int[] heapSort(int[] arr ) {
int len = arr.length;
buildMaxHeap(arr, len);
for (int i = len - 1; i > 0; i--) {
swap(arr, 0, i);
len--;
heapify(arr, 0, len);
}
return arr;
}
private static void buildMaxHeap(int[] arr, int len) {
for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
heapify(arr, i, len);
}
}
private static void heapify(int[] arr, int i, int len) {
int left = 2 * i + 1;
int right = 2 * i + 2;
int largest = i;
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) {
swap(arr, i, largest);
heapify(arr, largest, len);
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
8.計數(shù)排序
計數(shù)排序不是基于比較的排序算法识补,其核心在于將輸入的數(shù)據(jù)值轉(zhuǎn)化為鍵存儲在額外開辟的數(shù)組空間中族淮。 作為一種線性時間復(fù)雜度的排序,計數(shù)排序要求輸入的數(shù)據(jù)必須是有確定范圍的整數(shù)李请。
算法步驟:
- 找出待排序的數(shù)組中最大和最小的元素
- 統(tǒng)計數(shù)組中每個值為i的元素出現(xiàn)的次數(shù)瞧筛,存入數(shù)組C的第i項
- 對所有的計數(shù)累加(從C中的第一個元素開始,每一項和前一項相加)
- 反向填充目標數(shù)組:將每個元素i放在新數(shù)組的第C(i)項导盅,每放一個元素就將C(i)減去1
代碼實現(xiàn)
public static int[] countingSort(int[] arr ) {
int maxValue = getMaxValue(arr);
return countingSort(arr, maxValue);
}
private static int[] countingSort(int[] arr, int maxValue) {
int bucketLen = maxValue + 1;
int[] bucket = new int[bucketLen];
for (int value : arr) {
bucket[value]++;
}
int sortedIndex = 0;
for (int j = 0; j < bucketLen; j++) {
while (bucket[j] > 0) {
arr[sortedIndex++] = j;
bucket[j]--;
}
}
return arr;
}
private static int getMaxValue(int[] arr) {
int maxValue = arr[0];
for (int value : arr) {
if (maxValue < value) {
maxValue = value;
}
}
return maxValue;
}
9.桶排序
桶排序是計數(shù)排序的升級版较幌。它利用了函數(shù)的映射關(guān)系,高效與否的關(guān)鍵就在于這個映射函數(shù)的確定白翻。為了使桶排序更加高效乍炉,我們需要做到這兩點:
- 在額外空間充足的情況下,盡量增大桶的數(shù)量
- 使用的映射函數(shù)能夠?qū)⑤斎氲?N 個數(shù)據(jù)均勻的分配到 K 個桶中
同時滤馍,對于桶中元素的排序岛琼,選擇何種比較排序算法對于性能的影響至關(guān)重要。
什么時候最快
當輸入的數(shù)據(jù)可以均勻的分配到每一個桶中巢株。
什么時候最慢
當輸入的數(shù)據(jù)被分配到了同一個桶中槐瑞。
算法步驟:
- 設(shè)置一個定量的數(shù)組當作空桶;
- 遍歷輸入數(shù)據(jù)阁苞,并且把數(shù)據(jù)一個一個放到對應(yīng)的桶里去困檩;
- 對每個不是空的桶進行排序;
- 從不是空的桶里把排好序的數(shù)據(jù)拼接起來那槽。
代碼實現(xiàn)
public class Test {
public static void main(String[] args) {
int[] sourceArray = new int[]{1, 0, 10, 78, 898, 874, 56, 9, 98, 100, 300,234,8348,823484,1000000};
int[] result = Arrays.copyOf(sourceArray, sourceArray.length);
result = bucketSort(result,5);
for (int i = 0; i < sourceArray.length; i++) {
System.out.println(sourceArray[i]);
}
for (int i = 0; i < result.length; i++) {
System.out.println(result[i]);
}
}
public static int[] bucketSort(int[] arr, int bucketSize) {
if (arr.length == 0) {
return arr;
}
int minValue = arr[0];
int maxValue = arr[0];
for (int value : arr) {
if (value < minValue) {
minValue = value;
} else if (value > maxValue) {
maxValue = value;
}
}
int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;
int[][] buckets = new int[bucketCount][0];
// 利用映射函數(shù)將數(shù)據(jù)分配到各個桶中
for (int i = 0; i < arr.length; i++) {
int index = (int) Math.floor((arr[i] - minValue) / bucketSize);
buckets[index] = arrAppend(buckets[index], arr[i]);
}
for (int[] bucket : buckets) {
int arrIndex = 0;
if (bucket.length <= 0) {
continue;
}
// 對每個桶進行排序悼沿,這里使用了插入排序
bucket = insertionSort(arr);
for (int value : bucket) {
arr[arrIndex] = value;
arrIndex++;
}
}
return arr;
}
/**
* 自動擴容,并保存數(shù)據(jù)
*
* @param arr
* @param value
*/
private static int[] arrAppend(int[] arr, int value) {
arr = Arrays.copyOf(arr, arr.length + 1);
arr[arr.length - 1] = value;
return arr;
}
/**
* @param arr
* @return 插入排序骚灸,輸出升序排序結(jié)果
*/
public static int[] insertionSort(int[] arr) {
// 對 arr 進行拷貝糟趾,不改變參數(shù)內(nèi)容
// arr = Arrays.copyOf(arr, arr.length);
int length = arr.length;
//保存要插入的數(shù)據(jù)
int tmp;
int j;
// 從下標為1的元素開始選擇合適的位置插入,因為下標為0的只有一個元素甚牲,默認是有序的
for (int i = 1; i < length; i++) {
// 記錄要插入的數(shù)據(jù)
tmp = arr[i];
// 從已經(jīng)排序的序列最右邊的開始比較义郑,找到比其小的數(shù)
j = i;
//輸出降序的排序結(jié)果只需要把下面的 < 換成 >
while (j > 0 && tmp < arr[j - 1]) {
arr[j] = arr[j - 1];
j--;
}
// 存在比其小的數(shù),插入
if (j != i) {
arr[j] = tmp;
}
}
return arr;
}
10.基數(shù)排序
基數(shù)排序是一種非比較型整數(shù)排序算法丈钙,其原理是將整數(shù)按位數(shù)切割成不同的數(shù)字魔慷,然后按每個位數(shù)分別比較。由于整數(shù)也可以表達字符串(比如名字或日期)和特定格式的浮點數(shù)著恩,所以基數(shù)排序也不是只能使用于整數(shù)。
算法步驟:
- 取得數(shù)組中的最大數(shù),并取得位數(shù)喉誊;
- arr為原始數(shù)組邀摆,從最低位開始取每個位組成radix數(shù)組;
- 對radix進行計數(shù)排序(利用計數(shù)排序適用于小范圍數(shù)的特點)伍茄;
代碼實現(xiàn)
public class Test {
public static void main(String[] args) {
int[] sourceArray = new int[]{1, 0, 10, 78, 898, 874, 56, 9, 98, 100, 300};
int[] result = Arrays.copyOf(sourceArray, sourceArray.length);
int maxDigit = getMaxDigit(result);
result = radixSort(result, maxDigit);
for (int i = 0; i < sourceArray.length; i++) {
System.out.println(sourceArray[i]);
}
for (int i = 0; i < result.length; i++) {
System.out.println(result[i]);
}
}
/**
* 獲取最高位數(shù)
*/
private static int getMaxDigit(int[] arr) {
int maxValue = getMaxValue(arr);
return getNumLenght(maxValue);
}
private static int getMaxValue(int[] arr) {
int maxValue = arr[0];
for (int value : arr) {
if (maxValue < value) {
maxValue = value;
}
}
return maxValue;
}
protected static int getNumLenght(long num) {
if (num == 0) {
return 1;
}
int lenght = 0;
for (long temp = num; temp != 0; temp /= 10) {
lenght++;
}
return lenght;
}
private static int[] radixSort(int[] arr, int maxDigit) {
int mod = 10;
int dev = 1;
for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
// 考慮負數(shù)的情況栋盹,這里擴展一倍隊列數(shù),其中 [0-9]對應(yīng)負數(shù)敷矫,[10-19]對應(yīng)正數(shù) (bucket + 10)
int[][] counter = new int[mod * 2][0];
for (int j = 0; j < arr.length; j++) {
int bucket = ((arr[j] % mod) / dev) + mod;
counter[bucket] = arrayAppend(counter[bucket], arr[j]);
}
int pos = 0;
for (int[] bucket : counter) {
for (int value : bucket) {
arr[pos++] = value;
}
}
}
return arr;
}
/**
* 自動擴容例获,并保存數(shù)據(jù)
*
* @param arr
* @param value
*/
private static int[] arrayAppend(int[] arr, int value) {
arr = Arrays.copyOf(arr, arr.length + 1);
arr[arr.length - 1] = value;
return arr;
}
}