陣列排序(冒泡、快速)
面試的時候總愛問排序,其實排序並不難,只是將一個排序的思路轉成程式語言實現。對於實際工作中,我們遇到的業務邏輯,要比排序的邏輯複雜的多,卻奇怪於有的公司總愛問這個,更奇怪的是,總有朋友答不上來。(說來慚愧,答不上來的人裡也包括我自己,不是不會寫,只是不知道什麼是快速排序)。這裡整理了下面兩個排序。
先來說說氣泡排序,氣泡排序就是將當前位置的數依次和後面位置的數比較,將大數放在前面,小數放在後面。
如:int[] arr = {3,2,6,4,1,5,8,9,7};排序完以後的結果:int[] arr = {9,8,7,6,5,4,3,2,1};
3和2比較,3在前面,不需要交換;3和6比較,6放前面;6和4比較,6放前面;。。。如此繼續比較下去。
下面看看程式怎麼寫:
package mytest; public class TestSortUtil { public static int[] sort(int[] arr) { for(int i=0;i<arr.length;i++) { for(int j=i+1;j<arr.length;j++) { //i=0,j=1,2,3,4,...j初始值是j=i+1=0+1。後面j++,也就是第一個元素和後面的元素依次比較。 if(arr[i]<arr[j]) { //當arr[i]後面的元素小於後面的元素交換。 int tmp = arr[i]; arr[i]=arr[j]; arr[j]=tmp; } //print(arr);//如果想看排序過程開啟這裡的輸出就可以 } } return arr; } public static void print(int[] arr) { for(int i=0;i<arr.length;i++) { System.out.print(" "+arr[i]); } System.out.println(); } public static void main(String args[]) { int[] arr = {3,2,6,4,1,5,8,9,7}; print(arr); print(sort(arr)); } }
氣泡排序就是這樣。
我們再來說說快速排序;先看百科定義:快速排序(Quicksort)是對氣泡排序的一種改進;它的基本思想是:通過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。
首先,要選一個分割點key(百科叫關鍵資料),通常是陣列的第一個資料。(分割點最好是隨機從數組裡取出來一個數據)
然後,就是第一遍排序,分割點值小的放前面,分割點大的資料放後面。一個從前向後比較arr[i]<key,取到第一個下標i;一個從後向前比較arr[j]>key,取到第一下標j;將第一個大於key的值和第一個小於key的值互換。i==j時比較到中間位置結束;這樣就分成了兩部分資料。舉例:int[] arr = {3,2,6,4,1,5,8,9,7}; 如果key是3,從前向後找到的第一個值是6,從後向前找到的第一個值是1;則6和1交換。
然後,前後兩部分分別再進行遞迴排序,這樣就可以將程式寫成一個遞迴呼叫。
下面再來看看程式
package mytest;
public class TestFastSort {
public static int[] fastsort(int[] arr,int begin,int end) {
int left = begin;//排序開始的時候:begin=0,end=N-1;
int right = end;
int key = arr[begin];//以第一個陣列元素作為關鍵資料,賦值給key,即key=arr[begin];
while(left != right) {
while(arr[left] < key) {//從前向後搜尋
left++;
}
while(arr[right] > key) {//從後向前搜尋
right--;
}
//此時arr[left]是第一個大於key的值;arr[right]是第一個小於key的值
int temp = arr[left];
arr[left] = arr[right];
arr[right]=temp;
//完成這兩個值交換
print(arr);
}//當left和right相同時,迴圈結束。
if(begin<left) {
fastsort(arr,begin,left-1);
}
if(right<end) {
fastsort(arr,right+1,end);
}
return arr;
}
public static void print(int[] arr) {
for(int i=0;i<arr.length;i++) {
System.out.print(" "+arr[i]);
}
System.out.println();
}
public static void main(String args[]) {
int[] arr = {3,2,6,4,1,5,8,9,0,7};
print(arr);
print(fastsort(arr,0,arr.length-1));
}
}
如此,是不是明白了,為什麼說快速排序就是氣泡排序的優化了吧?其實就是先進行第一遍排序,選出中間值分成兩部分,然後對兩部分再分別排序。
那多的這一步,為什麼能比氣泡排序快呢?答降低了複雜度,那麼如何降低了複雜度?
先來看看冒泡順序:
1. 時間複雜度:O(n^2)
氣泡排序耗時的操作有:比較 + 交換(每次交換兩次賦值)。時間複雜度如下:
1) 最好情況:序列是升序排列,在這種情況下,需要進行的比較操作為(n-1)次。交換操作為0次。即O(n)
2) 最壞情況:序列是降序排列,那麼此時需要進行的比較共有n(n-1)/2次。交換運算元和比較運算元一樣。即O(n^2)
3) 漸進時間複雜度(平均時間複雜度):O(n^2)
2. 空間複雜度:O(1)
從實現原理可知,氣泡排序是在原輸入陣列上進行比較交換的(稱“就地排序”),所需開闢的輔助空間跟輸入陣列規模無關,所以空間複雜度為:O(1)1.
再來看看快速排序
1. 時間複雜度:O(nlog2n)
快速排序耗時的操作有:比較 + 交換(每次交換兩次賦值)。時間複雜度如下:
1) 最好情況:選擇的基準值剛好是中間值,分割槽後兩分割槽包含元素個數接近相等。因為,總共經過x次分割槽,根據2^x<=n得出x=log2n,每次分割槽需用n-1個元素與基準比較。所以O(nlog2n)
2) 最壞情況:每次分割槽後,只有一個分割槽包含除基準元素之外的元素。這樣就和氣泡排序一樣慢,需n(n-1)/2次比較。即O(n^2)
3) 漸進時間複雜度(平均時間複雜度):O(nlog2n)
2. 空間複雜度:O(1)
從實現原理可知,快速排序是在原輸入陣列上進行比較分割槽的(稱“就地排序”),所需開闢的輔助空間跟輸入陣列規模無關,所以空間複雜度為:O(1)