排序(Sort)是计算机程序设计中的一种重要操作,也是日常生活中经常遇到的问题。例如,字典中的单词是以字母的顺序排列,否则,使用起来非常困难。同样,存储在计算机中的数据的次序,对于处理这些数据的算法的速度和简便性而言,也具有非常深远的意义。
排序分为:外部排序 and 内部排序
这里我们讨论内部排序
冒泡排序法
冒泡排序法的基本思想是:对待排序记录关键字从后往前(逆序)进行多遍扫描,当发现相邻两个关键字的次序与排序要求的规则不符时,就将这两个记录进行交换。这样,关键字较小的记录将逐渐从后面向前面移动,就象气泡在水中向上浮一样,所以该算法也称为气泡排序法。
编写一个随机生成数列的函数<CreateData>:
#include <stdlib.h> int CreateData(int arr[],int n,int min,int max) //创建一个随机数组,a保存生成的数据,n为数组元素的数量 { int i,j,flag; srand(time(NULL)); if((max-min+1)<n) return 0; //最大数与最小数之差小于产生数组的数量,生成数据不成功 for(i=0;i<n;i++) { do { arr[i]=(max-min+1)*rand()/(RAND_MAX+1)+min; flag=0; for(j=0;j<i;j++) { if(arr[i]==arr[j]) flag=1; } }while(flag); } return 1; }
编写冒泡排序法程序:
void BubbleSort(int a[],int n) { int i,j,t; for(i=0;i<n-1;i++) { for(j=n-1;j>i;j--) { if(a[j-1]>a[j]) { t=a[j-1]; a[j-1]=a[j]; a[j]=t; } } printf("第%2d遍:",i+1); for(j=0;j<n;j++) printf("%d ",a[j]); printf(" "); } }
测试:
int main() { int i,a[ARRAYLEN]; for(i=0;i<ARRAYLEN;i++) a[i]=0; if(!CreateData(a,ARRAYLEN,1,100)) { printf("生成随机数不成功! "); getch(); return 1; } printf("原数据:"); for(i=0;i<ARRAYLEN;i++) printf("%d ",a[i]); printf(" "); BubbleSort(a,ARRAYLEN); printf("排序后:"); for(i=0;i<ARRAYLEN;i++) printf("%d ",a[i]); printf(" "); getch(); return 0; }
结果:
我们发现,在第三遍时我们就已经排序完毕,可是还是进行了第四第五遍,所以为了提升冒泡排序法的效率,可对BubbleSort函数进行改进,当在某一遍扫描时,发现数据都已经按顺序排列了,就不再进行后继的扫描,而结束排序过程。
该进就是对其添加了一个标志位,当已经排序好之后,就加上标志位不再排序。
void BubbleSort1(int a[],int n) { int i,j,t,flag=0; //flag用来标记是否发生交换 for(i=0;i<n-1;i++) { for(j=n-1;j>i;j--) { if(a[j-1]>a[j])//交换数据 { t=a[j-1]; a[j-1]=a[j]; a[j]=t; flag=1; } } printf("第%2d遍:",i+1); for(j=0;j<n;j++) printf("%d ",a[j]); printf(" "); if(flag==0) //没发生交换,直接跳出循环 break; else flag=0; } }
快速排序法
快速排序使用分治策略来把待排序数据序列分为两个子序列,具体步骤为:
(1)从数列中挑出一个元素,称该元素为“基准”。
(2)扫描一遍数列,将所有比“基准”小的元素排在基准前面,所有比“基准”大的元素排在基准后面。
(3)通过递归,将各子序列划分为更小的序列,直到把小于基准值元素的子数列和大于基准值元素的子数列排序。
快速排序详细步骤:
以一个数组作为示例,取区间第一个数为基准数。
初始时,i = 0; j = 9; X = a[i] = 72
由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。
从j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++; 这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; j--;
数组变为:
i = 3; j = 7; X=72
再重复上面的步骤,先从后向前找,再从前向后找。
从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++
从i开始向后找,当i=5时,由于i==j退出。
此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]。
数组变为:
可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。
/// <summary> /// 对数组dataArray中索引从left到right之间的数做排序 /// </summary> /// <param name="dataArray"></param> /// <param name="left">要排序数组的开始索引</param> /// <param name="right">要排序数组的结束索引</param> private static void QuickSort(int[] dataArray,int left,int right) { if (left < right) { int x = dataArray[left];//基准数 小的放左边 大的放右边 int i = left; int j = right;//用来做循环的标志位 while (true && i < j) { //从右向左比较 while (true && i < j) { if (dataArray[j] <= x) //找到了一个比基准数 小于等于的数字 应该把它放在x的左边 { dataArray[i] = dataArray[j]; break; } else { j--; //向左移动 到下一个数字 然后做比较 } } //从左向右比较 while (true && i < j) { if (dataArray[i] > x) { dataArray[j] = dataArray[i]; break; } else { i++; } } } //跳出循环 现在i=j i是中间位置 dataArray[i] = x;//left-i-right QuickSort(dataArray, left, i - 1); QuickSort(dataArray, i + 1, right); } }
测试:
static void Main(string[] args) { int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 }; QuickSort(data,0,data.Length-1); foreach (var temp in data) { Console.Write(temp + " "); } Console.ReadKey(); }
结果:
因为可能数据量比较小 所以看不出前二种方法的区别 可以试着增加数据量比较一下