排序
排序概念
- 穩定性:兩個相等的數據,經過排序後,如果相對位置沒有變化則爲穩定排序,反之,爲不穩定排序。
- 內部排序與外部排序:如果一次性可以將所有數據加載到內存中進行排序則爲內部排序;反之,爲外部排序
1.插入排序
1.1 直接插入排序
-
整個區間被分爲:有序區間與無序區間;
-
原理:每次選擇無序區間的第一個元素,在有序區間內選擇合適的位置插入。[默認要排序的數據第一個元素是有序的]
-
應用場景:如果搬移元素個數比較少,就比較適合插入排序,序列接近有序或者數據較少時
-
穩定性:穩定
-
時間複雜度:O(n^2)
- 最優:O(n) -> 有序時
- 最差:O(n^2) -> 逆序
-
空間複雜度:O(1)
-
代碼實現:
public static void insertSort(int[] array){ for(int i = 1; i < array.length;i++){ //有序區間[0,i) //無序區間[i,array.length) int num = array[i]; //無序區間第一個數 int end = i-1; //比較的元素位置 while(end >= 0 && num < array[end]){ //當數組不越界同時當前數比相比較的數小的時候 array[end+1] = array[end]; //移位 end--; } //插入元素 array[end+1] = num; } }
1.2 希爾排序
-
原理:採用分組的方式,將序列變成:接近有序,將數據變少;[採用直接插入排序對分組後的數據排序]
-
應用場景:元素比較凌亂,個數比較多
-
穩定性:不穩定
-
時間複雜度:O(n^2)
-
空間複雜度:O(n)
-
代碼實現:
public static void shellSort(int[] array){ int gap = array.length; while(gap > 1){ gap = gap/3+1; //分組數 for(int i = gap;i < array.length;i++){ int num = array[i]; //分組後當前組中無序的第一個元素 int end = i - gap; while(end >= 0 && num < array[end]){ array[end+gap] = array[end]; end -= gap; } array[end+gap] = num; } } }
2.選擇排序
2.1 直接選擇排序
-
原理:每次從無序區間選出最大(或最小)的一個元素,存放在無序區間的最後;直到全部元素排完[n個元素排n-1次]
-
穩定性:不穩定
-
時間複雜度:O(n^2)
-
空間複雜度:O(1)
-
代碼實現:
public static void selectSort(int[] array){ for(int i = 0;i < array.length-1;i++){ //無序區間:[0,array.length-i) //有序區間:[arrar.length-i,array.length) int max = 0; for(int j = 1; j < array.length-i;j++){ if(array[j] > array[max]){ max = j; } } int temp = array[max]; array[max] = array[array.length-1-i]; array[array.length-1-i] = temp; } } //雙向選擇排序 public static void selectSortPlus(int[] array){ int begin = 0; int end = array.length-1; while(begin < end){ int min = begin; int max = begin; for (int i = begin+1; i <= end; i++) { if(array[i] < array[min]){ min = i; } if(array[i] > array[max]){ max = i; } } swap(array,min,begin); if(max == begin){ max = min; } swap(array,max,end); begin++; end--; } }
2.2 堆排序
-
原理:利用堆這種數據結構;首先建堆,利用堆刪除的思想來進行排序(用堆頂元素與堆中最後一個元素進行交換,將堆中元素減少一個,再將堆頂元素向下調整)
-
穩定性:不穩定
-
時間複雜度:O(n*log(n))
-
空間複雜度:O(1)
public satic void headSort(int[] array,int size){ //array排序數組,size數組長度 creatHeap(array,size); for(int i = size-1;i >= 0;i--){ swap(array,i,0); //將最大的節點與最後一個節點交換,以確保排序數組 shiftDown(array,size-1,0);//默認斬掉最後一個節點 } } //構建堆 private static void createHeap(int[] array,int size){ int parent = (size-2)%2; for(int i = parent;i >= 0;i--){ shiftDown(array,size,i); } } //向下調整 public static void shiftDown(int[] array,int size,int index){ int left = 2*index + 1; while(left < size){ int max = left; int right = left + 1; if(right < size){ if(array[right] > array[left]){ max = right; } } if(array[index] >= array[max]){ break; } int temp = array[index]; array[index] = array[max]; array[max] = temp; index = max; left = 2*max+1; } }
3.冒泡排序
-
原理:在無序區間,通過相鄰數的比較,將最大的數冒泡到無序區間的最後,持續這個過程,直到數組整體有序
-
穩定性:穩定
-
時間複雜度:O(n^2)
-
空間複雜度:O(1)
public static void bubbleSort(int[] arr){ for(int i = 0;i < arr.length-1;i++){ boolean isSorted = true; for(int j = 0;i < arr.length-1-i;j++){ if(array[j] > arr[j+1]){ swap(array,j,j+1); //交換 isSorted = false; } } if(isSorted){ //如果沒有交換則已排序完成 break; } } }
4.快速排序
-
原理:默認升序
- 從區間中取出一個數據作爲基準值,按照該基準值將區間分割左右兩個部分
- 按照快排思想排序左半部分
- 按照快排思想排序右半部分
-
應用場景:數據量大比較隨機(數據雜亂)
-
穩定性:不穩定
-
時間複雜度:O(n*logn)
-
空間複雜度:O(logn)
//交換法 //quickSort(array,0,array.length);[begin,end) public static void quickSort(int[] array,int begin,int end){ if(end - begin > 1){ int left = begin; int right = end-1; int baseNum = array[right]; while(left < right){ while(left < right && array[left] <= baseNum){ left++; } while(left < right && array[right] >= baseNum){ right--; } if(left < right){ swap(array,left,right); } } swap(array,end,left); quickSort(array,begin,left); quickSort(array,left+1,end); } }
//挖坑法 //quickSort(array,0,array.length-1);[begin,end] public static void quickSort(int[] array,int begin,int end){ if(begin < end){ int baseNum = array[begin]; //基數 int left = begin; int right = end; while(left < right){ while(left < end && array[right] >= baseNum){ right--; } if(left < end){ array[left] = array[right]; } while(left < end && array[left] =< baseNum){ left++; } if(left < end){ array[right] = array[left]; } } array[left] = baseNum; quickSort(array,begin,left-1); quickSort(array,left+1,end); } }
//[ left , right):找到比基數小的給前面移動 public static void quickSort(int[] array,int left,int right){ if(right - left > 1){ int cur = left; int prev = cur - 1; int key = array[right-1]; while(cur < right){ if(key > array[cur] && ++prev != cur){ //++prev != cur是因爲中間沒有比基數大的了不用移動,自排序 swap(array,prev,cur); } ++cur; } if(++prev != right-1){ swap(array,prev,right-1); } quickSort(array,left,prev); quickSort(array,prev+1,right); } }
非遞歸實現:
public static void quick(int[] array){
Stack<Integer> stack = new Stack<Integer>();
stack.push(array.length);
stack.push(0);
while(!stack.isEmpty()){
int left = stack.pop();
int right = stack.pop();
if(right - left > 1){
int div = quickSort(array, left, right);
stack.push(right);
stack.push(div+1);
stack.push(div);
stack.push(left);
}
}
}
public static int quickSort(int[] array,int left,int right){
int cur = left;
int prev = cur - 1;
int key = array[right-1];
while(cur < right){
if(key > array[cur] && ++prev != cur){ //++prev != cur是因爲中間沒有比基數大的了不用移動,自排序
swap(array,prev,cur);
}
++cur;
}
if(++prev != right-1){
swap(array,prev,right-1);
}
return prev;
}
5.歸併排序
-
原理:將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使
子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併
-
穩定性:穩定
-
時間複雜度:O(NlogN)
-
空間複雜度:O(n)
//合併兩個有序數組
private static void merget(int[] array,int left,int mid,int right,int[] temp){
int index1 = left;
int index2 = mid+1;
int index = 0;
while(index1 <= mid && index2 <= right){
if(array[index1] <= array[index2]){
temp[index++] = array[index1++];
}else{
temp[index++] = array[index2++];
}
}
while(index1 <= mid){
temp[index++] = array[index1++];
}
while(index2 <= right){
temp[index++] = array[index2++];
}
// index = 0;
// while(left <= right){
// array[left++]=temp[index++];
// }
System.arraycopy(temp,0,array,left,right-left+1);
}
//遞歸實現歸併排序[left,right]
private static void mergeSort(int[] arr, int left, int right, int[] temp) {
if(left < right){
int mid = (left+right)/2;
//向左遞歸
mergeSort(arr,left,mid,temp);
//向右遞歸
mergeSort(arr,mid+1,right,temp);
//合併
merge(arr,left,mid,right,temp);
}
}
//非遞歸 實現歸併排序
public static void mergetSort(int[] array){
int gap = 1;
int[] temp = new int[array.length];
while (gap < array.length){
for(int i= 0;i < array.length;i+=gap*2){
int mid = i+gap;
int right = mid+gap;
if(mid >= array.length){
mid = array.length-1;
}
if(right >= array.length){
right = array.length-1;
}
merget(array,i,mid,right,temp);
}
gap*=2;
}
}
總結
排序方法 | 最好 | 平均 | 最壞 | 空間複雜度 | 穩定性 |
---|---|---|---|---|---|
冒泡排序 | O(n) | O(n^2) | O(n^2) | O(1) | 穩定 |
插入排序 | O(n) | O(n^2) | O(n^2) | O(1) | 穩定 |
選擇排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不穩定 |
希爾排序 | O(n) | O(n^1.3) | O(n^2) | O(1) | 不穩定 |
堆排序 | O(n*logn) | O(n*logn) | O(n*logn) | O(1) | 不穩定 |
快速排序 | O(n*logn) | O(n*logn) | O(n^2) | O(logn)~O(n) | 不穩定 |
歸併排序 | O(n*logn) | O(n*logn) | O(n*logn) | O(1) | 穩定 |