【排序算法】

一、插入排序

基本思想:每一趟将一个待排序的记录,按待排序的元素插入到已经排好序的一组记录的适当位置上,直到全部待排序记录全部插入为止。

1)直接插入排序

排序过程:

1、将待排序数组arr[1…n]看作两个集合,arr[1]为有序集合中元素,arr[2…n]为无序集合中元素,a[0]用来临时存放当前待排序记录

2、外层循环每次从无序集合中选择一个待插入元素(n-1次),每次使用顺序查找法,内层循环查找arr[i]在有序集合中的位置(将有序集合中大于待插入元素的记录后移一位)。

//直接插入排序
void Insert_Sort(int *arr,int n){
    int j = 0;
    for(int i = 2; i <= n; ++i){
        if(arr[i] < arr[i-1]){
            arr[0] = arr[i];
            arr[i] = arr[i-1];
            for(j = i-2; arr[0] < arr[j];--j)
                arr[j+1] = arr[j];
            arr[j+1] = arr[0];
        }
    }
}

算法特点:

​ 1、稳定排序;

​ 2、适用于顺序存储结构和链式存储结构;

​ 3、适用于初始记录基本有序的情况,当初始记录无序,n较大时,此算法时间复杂度较高,不宜采用。

稳定排序:指的是排序之后原来相同大小的元素相对位置没有改变。

2)希尔排序

当待排序记录个数越少、待排序记录中逆序越少,直接插入排序算法的效率越高。希尔排序通过将待排序记录进行分组来减少记录数量,通过对分组后的每个小组进行直接插入排序来减少逆序对的数量。

//希尔排序
//步长序列计算公式 n/2^k  n为排序规模 k取1,2,3,4 ... 当步长为1时为止
void shell_insert(int arr[],int n,int d){
    int j;
    for(int i = d+1; i <= n; i++){
        arr[0] = arr[i];
        for(j = i; j > d && arr[0] < arr[j-d]; j -= d){
            arr[j] = arr[j-d];
        }
        arr[j] = arr[0];
    }
}

void shell_sort(int arr[],int n){
    for(int gap = n / 2; gap > 0; gap /= 2){
        shell_insert(arr,n,gap);
    }
}

算法特点:

​ 1、不稳定排序;

​ 2、只能用于顺序结构,不能用于链式结构;

​ 3、增量可以有各种取法,但应该使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须是1;

​ 4、总的比较次数和移动次数比直接插入排序要少,n越大,效果越明显,适用于初始记录无序、n较大的情况。

二、选择排序

基本思想:每一趟排序从待排序的记录中选出关键字最小的记录,按顺序放在已排序的记录中,直到全部排完为止。

1)简单选择排序

//简单选择排序
void select_sort(int arr[],int n){
    int k;
    for(int i = 1; i < n; i++){	
        k = i;
        for(int j = i+1; j <= n; j++){
            if(arr[k] > arr[j])
                k = j;
        }
        if(k != i){
            arr[0] = arr[i]; arr[i] = arr[k]; arr[k] = arr[0];
        }
    }
}

算法特点:

​ 1、算法本身是稳定排序,也可以变成是不稳定排序;

​ 2、可以用于链式存储结构;

​ 3、移动次数少,当每一个记录占用的空间较多时,此方法比直接插入排序快。

2)堆排序

每次将堆顶元素取出,与末尾元素交换,然后按照要求调整前n-1个元素,使其仍然成堆,重复上述过程,直到剩余元素为1时为止。

//调整堆
void heap_adjust(int arr[],int low,int high){
    arr[0] = arr[low];
    for(int i = 2*low; i <= high; i *= 2){
        if(i<high && arr[i] < arr[i+1])
            i++;
        if(arr[0] >= arr[i])
            break;
        arr[low] = arr[i];low = i;
    }
    arr[low] = arr[0];
}

//初建堆
void creat_heap(int arr[],int n){
    for(int i = n / 2; i > 0; i--){
        heap_adjust(arr,i,n);
    }
}

//堆排序
void heap_sort(int arr[],int n){
    creat_heap(arr,n);
    for(int i = n; i > 1; i--){
        arr[0] = arr[1];
        arr[1] = arr[i];
        arr[i] = arr[0];
        heap_adjust(arr,1,i-1);
    }
}

算法特点:

​ 1、不稳定排序;

​ 2、只能用于顺序结构,不能用于链式结构;

​ 3、初始建堆所需要的比较次数比较多,记录较少时不宜采用,堆排序在最坏情况下的时间复杂度为O(nlog2 n),相对于快速排序最坏情况下的O(n^2)而言是个优点,当记录较多时较为高效。

三、交换排序

基本思想:两两比较待排序记录关键字,当两个关键字不满足次序要求时进行交换,直到整个序列满足要求为止。

1)冒泡排序

两两比较关键字,如逆序则交换顺序,较大关键字逐渐一端移动,直到序列有序。

//冒泡排序
void bubble_sort(int arr[],int n){
    int flag = 1 , len = n;
    int m = n;
    while((m > 0) && (flag == 1)){
        flag = 0;
        for(int j = 1; j <= m; j++){
            if(arr[j] > arr[j+1]){
                flag = 1;
                arr[0] = arr[j];arr[j] = arr[j+1]; arr[j+1] = arr[0];
            }
        }
        m--;
    }
} 

算法特点:

​ 1、稳定排序;

​ 2、可用于链式存储结构;

​ 3、记录移动次数较多,算法平均性能比直接插入排序差,当初始记录无序时,n较大时,不宜采用。

2)快速排序

冒泡排序只对相邻两个记录进行比较,因此每次只能消除一个逆序,而快速排序一次交换可消除多个逆序,从而提高排序性能。其主要原理是每次找到待排序组里面的中间值,然后分成左右两个区间,再分别排序这两个区间。

//快速排序
//一趟排序,返回中间位置
int partition(int arr[],int low,int high){
    arr[0] = arr[low];
    while(low < high){
        while((low < high) && arr[high] >= arr[0]) high--;
        arr[low] = arr[high];
        while((low < high) && arr[low] <= arr[0]) low++;
        arr[high] = arr[low]; 
    }
    arr[low] = arr[0];
    return low;
}

//递归进行排序
void QSort(int arr[],int low,int high){
    int pivotloc;
    if(low < high){
        pivotloc = partition(arr,low,high);
        QSort(arr,low,pivotloc-1);
        QSort(arr,pivotloc+1,high);
    }
}

void quick_sort(int arr[],int n){
    QSort(arr,1,n);
}

算法特点:

​ 1、不稳定排序;

​ 2、适用于顺序结构,很难用于链式结构;

​ 3、当n较大时,在平均情况下时所有内部排序方法中最快的一种,适用于初始记录无序,n较大的情况。

四、归并排序

基本思想:

把两个有序的区间结合在一起,假设初始序列含有n个记录,则可看成时n个有序的子序列,每个子序列的长度为1,然后两两并归,得到n/2个长度为2或1的有序子序列,类似于二叉树中的后序遍历,把区间分成两部分,一直分下去直到为1个元素的叶子结点。

//合并相邻位置上的有序表
void Merge(int arr1[],int arr2[],int low,int mid,int high){
    int i = low, j = mid+1, k = low;
    while(i <= mid && j <= high){
        if(arr1[i] <= arr1[j]) arr2[k++] = arr1[i++];
        else arr2[k++] = arr1[j++];
    }
    while(i <= mid) arr2[k++] = arr1[i++];
    while(j <= high) arr2[k++] = arr1[j++];  
}

void Msort(int arr1[],int arr2[],int low,int high){
    int mid; int arr3[high];
    if(low == high) arr2[low] = arr1[low];
    else{
        mid = (low + high) / 2;
        Msort(arr1,arr3,low,mid);
        Msort(arr1,arr3,mid+1,high);
        Merge(arr3,arr2,low,mid,high);
    }
}

//归并排序
void Merge_sort(int arr[],int n){
    Msort(arr,arr,1,n);
}

算法特点:

​ 1、稳定排序;

​ 2、可用于链式结构,且不需要附加存储空间,但递归实现仍需要开辟相应的递归工作栈。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值