一.歸併排序
1.基本思想
歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個子序列,每個子序列是有序的。然後再把有序子序列合併爲整體有序序列。
歸併排序就是利用歸併的思想實現的排序方法,效率比較高。其基本原理如下:對於給定的一組記錄,利用遞歸與分治技術將數據序列劃分成爲越來越小的半子表,在對半子表排序,最後再用遞歸方法將排好序的半子表合併成爲越來越大的有序序列。
2.思路
(1)申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
(2)設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
(3)比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
(4)重複步驟3直到某一指針達到序列尾
(5)將另一序列剩下的所有元素直接複製到合併序列尾
3.代碼
public class MergeSort {
//兩個數組合並
public static void merge(int[] nums, int low, int mid, int high) {
int[] newArr = new int [high-low+1]; //新合併元素數組
int i = low; // 左指針
int j = mid+1; // 右指針
int k = 0;
while(i<=mid && j<=high){ // 把較小的數先移到新數組中
if(nums[i]<nums[j]){
newArr[k++] = nums[i++];
}else{
newArr[k++] = nums[j++];
}
}
while (i <= mid){ // 把左邊剩餘的數移入新數組中
newArr[k++] = nums[i++];
}
while (j <= high) { // 把右邊邊剩餘的數移入新數組中
newArr[k++] = nums[j++];
}
// 把新數組中的數覆蓋nums數組
for (int temp = 0; temp < newArr.length; temp++) {
nums[temp + low] = newArr[temp];
}
}
public static void mergeSort(int[] nums, int low, int high) {
int mid = low + (high-low)/2; // 防止數字超出,堆棧溢出
if(low<high){
mergeSort(nums, low, mid); // 左邊子數組
mergeSort(nums, mid+1, high); //右邊子數組
merge(nums, low, mid, high); //左右子數組合並
}
}
}
測試:
@Test
public void testMergeSort(){
int numbers[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 };
MergeSort.mergeSort(numbers, 0, numbers.length - 1);
for(int i=0;i<numbers.length;i++){
System.out.print(numbers[i]+" ");
}
}
結果:
18 20 30 46 50 51 65 77 82 97
4.優缺點
歸併排序需要O(nlogn)的比較和O(nlogn)的數據移動。
(1)優點
①歸併排序的效率達到了巔峯
時間複雜度爲O(nlogn),這是基於比較的排序算法所能達到的最高境界。
②歸併排序是一種穩定的算法
這一點在某些場景下至關重要。
③歸併排序是最常用的外部排序方法
當待排序的記錄放在外存上,內存裝不下全部數據時,歸併排序仍然適用,當然歸併排序同樣適用於內部排序。
(2)缺點
歸併排序需要O(n)的輔助空間,而與之效率相同的快排和堆排分別需要O(logn)和O(1)的輔助空間,在同類算法中歸併排序的空間複雜度略高
二.基數排序
1.基本思想
基數排序(radix sort)屬於“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些“桶”中,藉以達到排序的作用,基數排序法是屬於穩定性的排序,其時間複雜度爲O (nlog(r)m),其中r爲所採取的基數,而m爲堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法
2.思路
(1)假設數組中最大位數爲d,將數組按照最小位數排序。
(2)按照位數爲10^(d-1)位(個位,十位,百位)排序d次,數組即爲有序序列了
案例:
存在數組:{73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100}
最大位數:3位
第1次按照個位排序:{100, 81, 22, 73, 93, 43, 33, 14, 55, 65, 28, 39 }
第2次按照十位排序:{100, 14, 22, 28, 33, 39, 43, 55, 65, 73, 81, 93 }
第3次按照百位排序:{ 14, 22, 28, 33, 39, 43, 55, 65, 73, 81, 93, 100}
3.代碼
public class RadixSort{
public static void radixSort(int[] numbers, int d){ //d表示最大的數有多少位
int n = 1; //取個位/十位/百位
int[][]temp = new int[10][numbers.length]; //數組的第一維表示可能的餘數0-9
int[]order = new int[10]; //數組order[i]用來表示該位是i的數的個數
//i控制鍵值排序依據在哪一位
for(int i=0; i<d; i++){
int m = 0;
for (int j = 0; j < numbers.length; j++) {
int remainder = (numbers[j]/n)%10; //除10取餘
temp[remainder][order[remainder]] = numbers[j];
order[remainder]++;
}
for(int k=0; k<10; k++){
if(order[k] != 0){
for (int l = 0; l < order[k]; l++) {
numbers[m] = temp[k][l];
m++;
}
}
order[k] = 0;
}
n *= 10;
}
}
}
測試:
@Test
public void testRadixSort(){
int[]data = {73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100};
RadixSort.radixSort(data, 3);
for(int i = 0; i < data.length; i++){
System.out.print(data[i] + " ");
}
}
結果:
14 22 28 33 39 43 55 65 73 81 93 100
4.優缺點
(1)優點
基數排序法是屬於穩定性的排序,其時間複雜度爲O (nlog(r)m),其中r爲所採取的基數,而m爲堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法。
(2)缺點
不呈現時空局部性,因爲在按位對每個數進行排序的過程中,一個數的位置可能發生巨大的變化,所以不能充分利用現代機器緩存提供的優勢。同時計數排序作爲中間穩定排序的話,不具有原地排序的特點,當內存容量比較寶貴的時候,還是有待商榷
三.參考
http://blog.csdn.net/jianyuerensheng/article/details/51262984
http://blog.csdn.net/Touch_2011/article/details/6785881