排序算法

来源:互联网 发布:不可抗力网络剧百度云 编辑:程序博客网 时间:2024/06/02 03:57

本文对学习过的排序算法做简要的总结。如发现错误,欢迎纠正。

本文排序默认为升序。

1. 插入排序(冒泡排序)

插入排序也称为冒泡排序,学过C语言的应该都学习过。程序在下面。

冒泡排序使用了两个for循环:外循环for和内循环for。

原理如下:外循环for在第i次循环时,arr[0]至arr[i-1]已经排序完毕。第i次执行完以后,arr[0]至arr[i]排序完毕。也就是说,外循环for确认第i个元素的位置。

                     内循环for每次执行,都会判断当前的j-1元素是否比j元素大,如果大则交换这两个元素,直到j元素大于等于j-1元素。

#include <stdio.h>void swap(int *i, int *j){int temp;temp = *i;*i = *j;*j = temp;}void sort1(int arr[], int num){int i, j;for(i = 1; i < num; i++)for(j = i; j > 0 && arr[j-1]>arr[j]; j--)swap(&arr[j], &arr[j-1]);}void main(){ int x[10] = {51, 35, -3, 17, 24, -5, 11, 38, 90, 62};int i;sort1(x, 10);printf("After sort:\n");for(i = 0; i < 10; i++)printf("%d ", x[i]);printf("\n");}

输出结果:

After sort:
-5 -3 11 17 24 35 38 51 62 90

2. 改进的插入排序

冒泡排序时,每次都要交换相邻两个元素的值,这就要用到三个赋值语句。那么是否可以通过什么方法,减少赋值语句。

可以看出,交换相邻两个元素的值只不过是为了让第i个元素插入到正确的位置,那么我们可以考虑在执行内循环for之前,将第i个元素先取出来,然后来查找第i个

元素应该插入的位置,最后将第i个元素插入该位置即可。

程序如下。当内循环for执行完时,j也就是我们需要插入的位置。

#include <stdio.h>void sort2(int arr[], int num){int i, j, tmp;for(i = 1; i < num ; i++){tmp = arr[i];for(j = i; j > 0 && arr[j-1]> tmp; j--)arr[j] = arr[j-1];arr[j] = tmp;    /*插入*/}}void main(){int i;int x[10] = {87, 56, 12, 65, -6, 63, 1, 98, 23, 85};sort2(x, 10);printf("After sort:\n");for(i = 0; i < 10; i++)printf("%d ", x[i]);printf("\n");}
输出结果:

After sort:
-6 1 12 23 56 63 65 85 87 98

3.  快速排序1

   这一节将介绍最简单的快速排序。该排序算法每次以待排序数组中的第一个元素为中值,将所以小于该值的元素放在数组左边,大于该元素放在数组的右边,最后将中值插入正确的位置。然后将中值左侧的子数组作为待排序数组重新调用该算法,同时也将中值右侧的子数组作为待排序数组重新调用该算法,直到最后只有一个或没有元素待排序。如此下去。可见,该算法使用了递归。

   算法如下。
代码16~17为基准条件,当只有一个或没有元素待排序时,递归终止。
代码20-24完成了对左右两个子数组的划分,在执行第i次for循环时,m始终指向小于中值的最后一个元素,也就是说arr[lower+1...m] < 中值,且arr[m+1 ...i-1] >= 中值。
代码24将中指插入了正确的位置。
代码26和27则递归调用算法对左子数组和右子数组进行排序。

#include <stdio.h>swap(int *a, int*b){int temp;temp = *b;*b = *a;*a = temp;}void qsort1(int arr[], int lower, int upper){int i, m;if(lower >= upper)return;  m = lower;for(i = lower+1; i <= upper; i++){if(arr[i] < arr[lower])swap(&arr[++m], &arr[i]);}swap(&arr[m], &arr[lower]);qsort1(arr, lower, m-1);qsort1(arr, m+1, upper);}void main(){int x[10] = {4, 54, 23, 12, 87, 85, 63, 31, 65, 43};int i;qsort1(x, 0, 9);for(i = 0; i < 10; i++)printf("%d ", x[i]);printf("\n");}

结果如下:

4 12 23 31 43 54 63 65 85 87 

4. 快速排序2

现在假设对n个相同元素组成的数组进行排序。对于这种排序,插入排序的运行时间为O(n),因为每个元素的移动距离都为0。
而快速排序的性能非常差,N-1次划分中每次划分都需要O(n)时间来去掉一个元素,所以总的运行时间为O(n)。因此对快速排序1进行修改,使用双向划分来解决该问题。
算法如下面所示。
代码16~17为基准条件,当只有一个或没有元素带排序时,递归终止。
代码23为最外层while循环,该循环终止时表示排序已经完成。
代码24~25从左往右遍历数组,直到发现大于中值t的元素,因此该do...while退出时,i指向大于中值的元素。
代码26~27从右往左遍历数组,直到发现小于中值t的元素,因此该do...while退出时,j指向小于于中值的元素。
代码29到30判断是否该次排序是否结束。
代码31交换两个元素。交换以后,i指向小于中指的元素,而j指向小于中指的元素。
代码33当最外面的while(1)退出时,从左往右看,j指向最后一个小于中值的元素,因此对该元素和中指进行交换。
代码26和27则递归调用算法对左子数组和右子数组进行排序。

#include <stdio.h>void swap(int *a, int*b){int tmp;tmp = *b;*b = *a;*a = tmp;}void qsort3(int arr[], int lower, int upper){int t, i, j;if(lower >= upper)return;t = arr[lower];i = lower;j = upper+1;while(1){do i++;while(i <= upper && arr[i] < t);do j--;while(arr[j] > t);if(i > j)break;swap(&arr[i],&arr[j] );}swap(&arr[lower],&arr[j] );qsort3(arr, lower, j-1);qsort3(arr, j+1, upper);}void main(){int x[10] = {55, 41, 59, 26, 26, 26, 53, 58, 97, 93};int i;printf("Before qsort:\n");for(i = 0; i < 10; i++)printf("%d ", x[i]);printf("\n");qsort3(x, 0, 9);printf("After qsort:\n");for(i = 0; i < 10; i++)printf("%d ", x[i]);printf("\n");} 

结果如下:
Before qsort:
55 41 59 26 26 26 53 58 97 93
After qsort:
26 26 26 41 53 55 58 59 93 97


2012.08.05添加算法1,2。
2012.08.08添加算法3。
2012.08.14添加算法4。


原创粉丝点击