排序
稳定排序:Ri = Rj,排序前Ri 领先于 Rj,若排序后Ri 仍然领先于 Rj,则称为稳定排序
非稳定排序:与稳定排序相反。
内排序:待排文件在计算机的内部存储器中,其排序过程在内存中进行,速度较快,但内存容量一般很小,文件的长度(记录个数)n受到一定限制。
外排序:排序中文件存入外存储器,排序过程借助于内外数据交换(或归并)来完成
本文章讲述内排序,可归纳为五类:
- 插入排序
直接插入排序(类似扑克牌插入)时间复杂度O(n2)
i:对应无序序列下标,j:对应有序序列下标
void insert_sort(int *a)
{
int i = 0,j = 0;
int tmp = 0;
for( i = 1; i < N; i++){
tmp = a[i];
for(j=i-1;j >= 0;j--){
if( a[j] > tmp ){
a[j+1] = a[j];
}else
break;
}
a[j+1] = tmp;
show(a);
}
}
(shell)希尔排序:时间复杂度O(nlogn)
对直接插入排序进行改进优化,减少直接排序中元素大量移动的时间和比较的次数。将序列分组,增量为d,序列相隔为d的所有元素为一组进行排序,排序完毕后增量为原来的一半,循环分组排序,直到 增量d 小于等于 0,最后进行直接插入排序。
void shell_sort(int *a)
{
int i = 0,j = 0;
int tmp = 0;
int d = 0;
for( d=N/2; d > 0; d /= 2){
for( i = d-1; i < N; i++){
tmp = a[i];
for(j=i-d;j >= 0;j -= d){
if( a[j] > tmp ){
a[j+d] = a[j];
}else
break;
}
a[j+d] = tmp;
}
show(a);
}
}
- 交换排序
冒泡排序(两两相邻记录的关键字,如果反序则调换,直到没有反序为止)时间复杂度O(n2)
void Bubblesort1(int k[],int n)
{
int i, j, tmp;
for( i = 0; i < n-1; i++){
for(j = n-1; j > i; j--){
if( k[j-1] > k[j] ){
tmp = k[j];
k[j] = k[j-1];
k[j-1] = tmp;
}
}
}
}
//优化
void Bubblesort2(int k[],int n)
{
int i, j, tmp;
bool flag = 1;
int t;
for( i = 0; i < n-1 && flag == 1; i++){
flag = 0;
for(j = n-1; j > i; j--){
if( k[j-1] > k[j] ){
tmp = k[j-1];
k[j-1] = k[j];
k[j] = tmp;
flag = 1;
}
}
for(t = 0; t < 10;t++)
printf("%d\t",k[t]);
printf("\n");
}
}
快速排序(序列第一个作为基值,并找出基值的位置,在将序列分为左右两子序列,在对左右两序列重复操作,递归实现)
int get_base_pos(int *a, int low, int high)
{
int base_val;
base_val = a[low];//第一个值作为基值
while(low < high){
while(low < high && base_val <= a[high])
high--;
//若基值比上界值大,则将上界对应下标high位置的数据赋给下界对应low位置
if(low < high)
a[low] = a[high];
while(low < high && base_val >= a[low])
low++;
//若基值比下界值小,则将下界对应下标low位置的数据赋给上界对应high位置
if(low < high)
a[high] = a[low];
}
//当low == high时,即为基值位置
a[low] = base_val;
return low;
}
void quick_sort(int *a,int low,int high)
{
int base_pos = 0;
//递归终止条件
if(low >= high)
return ;
base_pos = get_base_pos(a, low, high);
show(a);
quick_sort(a, low, base_pos-1);//处理左序列
quick_sort(a, base_pos+1, high);//处理右序列
}
快速排序的优化:
- 当数组巧好为比较极端时候,例如987654321这排列,那么快速排序取基值位置分隔只能分隔一边,导致效率最低,那么我们可以对数组的基值作相应优化。
- 快速排序对数量级小的序列效率较低,而直接插入排序的性能相对较高,所以应该判断数据的量级,从而选择使用两种排序方法
- 对递归的方式优化,使用尾递归,(尾递归:函数中递归形式的调用出现在函数末尾,编译器检测到会覆盖当前活跃记录,而不是重新创建栈空间,当函数返回时,因为栈帧没有其他事情可做,也就没有必要保证栈空间,使用栈空间大大减少,提高运行效率,一般递归都尽量写成尾递归方式。)
int get_base_pos(int *a, int low, int high)
{
int base_val;
/****************基值优化部分********************/
int tmp;
int m = low + (high - low)/2;
if(a[low] > a[high]){
tmp = a[low];
a[low] = a[high];
a[high] = tmp;
}
if(a(m) > a(high)){
tmp = a[m];
a[m] = a[high];
a[high] = tmp;
}
if(a(m) > a(low)){
tmp = a[m];
a[m] = a[low];
a[low] = tmp;
}
/***********************************************/
base_val = a[low];//第一个值作为基值
while(low < high){
while(low < high && base_val <= a[high])
high--;
//若基值比上界值大,则将上界对应下标high位置的数据赋给下界对应low位置
if(low < high)
a[low] = a[high];
while(low < high && base_val >= a[low])
low++;
//若基值比下界值小,则将下界对应下标low位置的数据赋给上界对应high位置
if(low < high)
a[high] = a[low];
}
//当low == high时,即为基值位置
a[low] = base_val;
return low;
}
void quick_sort(int *a,int low,int high)
{
int base_pos = 0;
//递归终止条件
if(low >= high)
return ;
while( low < high){
base_pos = get_base_pos(a, low, high);
if( base_pos-low < high- base_pos ){
quick_sort(a, low, base_pos-1);//处理左序列
low = base_pos + 1;
}else{
quick_sort(a, base_pos+1,high);//处理右序列
high = base_pos - 1;
}
}
}
- 选择排序:(通过 n-i 次关键字间的比较,从n-i+1 个记录中选出关键字最小的记录,并和第i个记录交换)时间复杂度O(n2)
void selectsort(int k[],int n)
{
int i,j,tmp,min;
for(i=0;i<n-1;i++){
min = i;
for(j=i+1;j<n-2;i++){
if( k[j] < k[min] ){
min = j;
}
}
if( min != i){
temp = k[min];
k[min] = k[i];
k[i] = temp;
}
}
}
堆排序:对选择排序的优化,时间复杂度O(nlogn)
对二叉树的应用,后续补上代码。。。。
- 归并排序(先将序列拆分成两部分直到序列只含单个元素,在两两进行合并形成有序的子序列,最后在合并成一个主序列,即排序结束)
递归实现
void merging(int *list1,int list1_size,int *list2,int list2_size)
{
int temp[N] = {0};
int i,j,k,m;
i = j = k = m = 0;
while( i < list1_size && j < list2_size ){
if( list1[i] < list2[j] )
temp[k++] = list1[i++];
else
temp[k++] = list2[j++];
}
while(i < list1_size)
temp[k++] = list1[i++];
while(j < list2_size)
temp[k++] = list2[j++];
for(m = 0; m < list1_size + list2_size; m++)
list1[m] = temp[m];
}
void mergesort(int k[], int Num)
{
if(Num <= 1)
return ;
int *list1 = k;
int list1_size = Num/2;
int *list2 = k + list1_size;
int list2_size = Num - list1_size;
mergesort(list1, list1_size);
mergesort(list2, list2_size);
merging(list1,list1_size,list2,list2_size);
}
迭代实现
void mergesort(int k[], int n)
{
int i, left_min, left_max, right_min, right_max,next;
int *temp = (int *)malloc(n * sizeof(int));
for( i=1; i < n; i*=2 ){
for( left_min = 0; left_min < n-i ; left_min = right_max){
right_min = left_min + i;
left_max = left_min + i;
right_max = left_max + i;
if( right_max > n){
right_max = n;
}
next = 0;
while( left_min < left_max && right_min < right_max ){
if( k[left_min] < k[right_min] ){
temp[next++] = k[left_min++];
}else{
temp[next++] = k[right_min++];
}
}
while(left_min < left_max ){
k[--right_min] = k[--left_max];
}
while( next > 0 ){
k[--right_min] = temp[--next];
}
}
}
}
- 基数排序
这里未作讲解,感兴趣的可以去其他论坛找资料
比较分析
这里说明一下经过优化的快速排序在数据级较大时发挥最好,数量级小的用直接插入,而堆排序一般用在对内存比较苛刻的单片机和嵌入式领域比较合适。