1 插入排序
插入排序的左邊是有序的,依次從右邊拿出一個數,插入到左邊,直到右邊沒有更多的數,此時左邊的數已經全部按序排列。
插入排序的C語言實現
void insertion_sort(int array[], int length){int i,j;for(i=1;i<length;i++){for(j=i;j>0;j--){if(array[j]<array[j-1]){int temp = array[j];array[j] = array[j-1];array[j-1] = temp;}elsebreak;}}}
插入排序的平均時間複雜度是O(N^2),當輸入序列已經排好序的情況下,可以達到O(N)。所以當數組已經部分排好序的情況下,插入排序的效率較高。
這裡是一個關於插入排序的舞蹈視頻:http://t.cn/hros0W 。不管怎麼樣,視頻只能協助你理解排序的一個總體思路,但是只有切身用代碼實現了,才算是真正會了,有時候少一個等號,迭代少了一次,都會導致結果的錯誤。至於完全理解每個演算法的適用場合,還需要在工作中的不斷經驗積累,以及良好的數學功底。
2 冒泡排序 Bubble Sort
冒泡大概是最好理解的一個排序演算法之一了,不過我第一次接觸的時候也花了一些時間才明白過來。從名字上來看,冒泡排序就像是水裡的泡泡一樣,最大的元素不斷的浮出水面,排到整個數組的最後。具體的說,第一次迭代,最大的元素會排到數組最後,第二次迭代,次大的元素會排到數組的倒數第二,第三次迭代,第三大的元素會排到數組的倒數第三……以此類推。
冒牌排序的平均時間複雜度是O(N^2),最壞的情況是O(N^2),最優情況O(N)。
冒泡排序的C實現代碼:
void bubbleSort(int arr[],int len){int i,j;int flag;for(i = 0;i < len;i++){flag = 0;for(j = 0; j < len -i -1; j++){if(arr[j]>arr[j+1]){//swap arr[j], arr[j+1];int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;flag = 1;}}/*if in this loop, flag has never been changed, then the whole array is already sorted*/if(flag == 0){break;}}}
冒泡排序的舞蹈視頻:http://t.cn/hrf58M
3 快速排序
快速排序如它的名字一樣,是速度最快的排序之一。快速排序的平均時間複雜度是O(NlogN),最壞的情況是O(N^2)。
快速排序是一種遞迴排序演算法,首先選擇一個pivot軸心元素,讓數組中比pivot小的元素排到其左邊,比它大的元素排到右邊。然後再遞迴用快速排序整理pivot左邊的部分,以及右邊的部分。直到整體實現有序。這裡使元素分別排到pivot兩邊的演算法叫partition分區演算法。最好的pivot選取是選擇一個數組的中數median,如果不巧選到了一個數組裡的Max or Min,就會導致最壞情況的發生。具體的分析就不在這裡說了。如果想知道去看看CLRS演算法導論,裡面有詳細的partition演算法,還有關於如何選取一個好的pivot。
快速排序的C語言簡單實現:
void quicksort(int array[],int left,int right){int index = partition(array,left,right);/*recursively sort the left part*/if(left<index-1){quicksort(array,left,index-1);}/*recursively sort the right part*/if(index<right){quicksort(array,index,right);}}/*return the index of pivot element*/int partition(int array[],int left,int right){int pivot = array[(left+right)/2];while(left<right){/*find the element that should on right side of pivot*/while(array[left]<pivot){left++;}/*find the element that should on left side of pivot*/while(array[right]>pivot){right--;}/*swap left and right*/if(left<=right){int tmp = array[left];array[left] = array[right];array[right] = tmp;left++;right--;}}return left;}
同樣在最後給出舞蹈視頻:http://t.cn/ScTA1d
4 歸併排序 Merge Sort
一直想不通為什麼要把Merge翻譯成歸併…
Merge Sort也用到了遞迴,更確切的說法是用到了Divide and Conquer的分治思想,分:把一個數組分成2段,先將左邊排序,再將右邊排序,合:最後融合到一個數組裡。
虛擬碼:
void mergeSort(
left = mergeSort(array, 0, middle);
right = mergeSort(array, middle, end);
merge(left, right);
}
Merge Sort的平均和最壞的時間複雜度都是O(NlogN),至於怎麼得出的可以參看演算法導論CLRS,簡單的說一下:設時間複雜度是T(n),那麼從上面虛擬碼可以推出遞迴式:T(n) = 2T(n/2) + O(n), 這裡的O(n)最後merge過程的時間複雜度,可以求出T(n)是O(NlogN),有個公式的,你也可以推理或者猜一個答案來反證,總之方法很多,如果你本科演算法課好好聽了應該會有印象。
簡單的說一下Merge過程,現在有兩個排好了序的數組,然後要融合它們,怎麼做?分別從兩個數組的頭部開始迭代,逐個比較,小的那個拿走,直到迭代完成。
Merge Sort的C語言實現:
void mergeSort(int a[],int start,int end){if(start<end){int mid = (start+end)/2;mergeSort(a,start,mid);mergeSort(a,mid+1,end);merge(a,start,mid,end);}}void merge(int a[], int start, int mid, int end){int i = start;//copy array a into a temporary helper arrayint helper[end];while(i<=end){helper[i] = a[i];i++;}//start to merge left part and right partint left = start;int right = mid+1;//pointer 'current' is pointing at current index of array 'a'int current = start;while(left <= mid && right <= end){if(helper[left]<=helper[right]){a[current] = helper[left];left++;}else{a[current] = helper[right];right++;}current++;}//copy the remaining of left part into array 'a'while(left <= mid){a[current] = helper[left];current++;left++;}}
還有一點關於merge過程想說的,上面的最後,只需要copy左半部分剩下的到原數組裡,為什麼呢?因為右半部分如果有剩下,那麼它已經在原數組裡正確的位置上了。
例如,merge這樣一個數組的左右兩個部分:2 3 7 || 1 8 9, 左邊會先被迭代光,此時右邊還剩8和9,但是位置已然已經正確了;再例如 1 8 9 || 2 3 7 ,這時候就是左邊會剩下,需要迭代完。
最後給出merge sort的視頻 http://v.youku.com/v_show/id_XMzMyODk5Njg4.html
面試的很少會直接考你排序,一般都是夾在在某些題目裡。稍後我會更新一些題目上來。