您的位置:首页 > 其它

常用排序、选择算法

2012-05-30 11:10 190 查看
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;
}else
break;
}
}
}


插入排序的平均时间复杂度是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 array
int helper[end];
while(i<=end){
helper[i] = a[i];
i++;
}

//start to merge left part and right part
int 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

面试的很少会直接考你排序,一般都是夹在在某些题目里。稍后我会更新一些题目上来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: