算法导论-----排序的9种实现(C/C++)

目录

《算法导论》中并没有讨论这么多排序算法。在此罗列出来,仅仅是为了今后便于查看。基数排序、桶排序后续补充。。。。。


A、冒泡排序

冒泡排序有很多种实现方式。下面总结常见的几种,并对冒泡排序进行改进。
冒泡排序1

//冒泡升序排序1,强烈推荐,好记
void BubbleSort(int arr[],int length) {  
    int tmp;
    int i,j;
    for(i=length-1; i>0; i--) {
        for(j=0; j<i; j++) {
            if(arr[j] > arr[j+1]) {
                tmp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=tmp;
            }
        }
    }
}

冒泡升序排序2

void BubbleSort(int arr[],int length) {  //冒泡排序2
    int i,j;
    int tmp;
    for(i=0; i<length-1; i++) {
        for(j=i+1; j<length; j++)
            if(arr[i] > arr[j]) {
                tmp=arr[i];
                arr[i]=arr[j];
                arr[j]=tmp;
            }
    }
}

冒泡升序排序3

void BubbleSort(int arr[],int length) {  //冒泡排序3
    int i,j;
    int tmp;
    for(i=0; i<length-1; i++) {
        for(j=length-2; j>=i; j--)   //从右向左冒泡,较小的数向前端冒泡
            if(arr[j] > arr[j+1]) {
                tmp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=tmp;
            }
    }
}

冒泡升序排序4(对冒泡1进行优化,冒泡2、3同理可以优化)

void BubbleSort(int arr[],int length) {  //冒泡排序(优化)4
    int i,j;
    int tmp;
    bool flag=true; //设置一个标记,数组中没有逆序就置flag为false,排序结束;
                    //有逆序则置为true,做进一步操作。
    for(i=length-1;i>0 && flag ; i--){
        flag =false;
        for(j=0;j<i;j++){
            if(arr[j] > arr[j+1] ){
                tmp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=tmp;
                flag=true;//存在逆序,所以置为true
            }
        }
    }
}

B、选择排序

选择排序时间复杂度为 O(n2) ,是一个不稳定的算法。

void SelectionSort(int arr[],int length){
    int i,j,tmp;
    int minPos;
    for(i=0;i<length-1;i++){
        minPos=i;       //将当前下标定义为最小值的下标
        for(j=i+1;j<length;j++){
            if(arr[minPos] > arr[j])//如果小于当前的最小值
                minPos=j;           //将此值的下标赋值给minPos
        }
        if(minPos!=i){      //如果当前i位置上的值不是最小值,则交换
            tmp=arr[minPos];
            arr[minPos]=arr[i];
            arr[i]=tmp;
        }
    }
}

C、插入排序

插入排序时间复杂度为 O(n2) ,是一个稳定的算法。
插入排序C实现1

void InsectionSort(int arr[],int length){   //插入排序1
    int i,j;
    int tmp;
        for(i=1;i<length;i++){  //从1位置开始遍历数组。0位置视为已排序
            tmp=arr[i];         //记录待插入的元素,tmp为哨兵
        for(j=i-1;j>=0 && arr[j]>tmp ;j--){
            arr[j+1]=arr[j];    //待插入元素前面有元素比tmp大,较大的元素向后移动一个位置
        }
        arr[j+1]=tmp;
    }
}

插入排序C实现2

void InsectionSort(int arr[],int length){   //插入排序2(因为要设置哨兵,arr[0]位置不能存元素)
    int i,j;
    for(i=2;i<length;i++){      //依次将arr[2]--arr[n]插入到前面的已排序序列中
        if(arr[i] < arr[i-1])   //arr[i]小于前驱,需要插入已有序的前驱部分中
            arr[0]=arr[i];      //设置arr[0]设置为哨兵。节约一个临时变量tmp。下面的移位操作更简单
        for(j=i-1;arr[0] < arr[j];j--){
            arr[j+1]=arr[j];
        }
        arr[j+1]=arr[0];
    }
}

D、折半插入排序

折半插入排序仅仅减少了比较元素的次数,约为 O(nlog2n) ,且比较次数与待排数组的初始化状态无关。而元素的移动次数没有改变,移动次数依赖于数组的初始化状态。时间复杂度为 O(n2) ,是一个稳定算法。

void BinaryInsectionSort(int arr[],int length) {
    int i,j,low,high,mid,tmp;
    for(i=1; i<length; i++) {   //依次将arr[1]---arr[length-1]插入到前面已排序序列中,arr[0]视为已排序
        tmp=arr[i];             //用哨兵tmp记录待插入的元素
        low=0;
        high=i-1;               //设置二分查找的起点、终点
        while(low<=high) {
            mid=(low+high)/2;
            if(arr[mid] > tmp)
                high=mid-1;
            else
                low=mid+1;
        }
        for(j=i-1; j>=high+1; j--) {//移动元素
            arr[j+1]=arr[j];
        }
        arr[high+1]=tmp;
    }
}

E、归并排序

  归并排序和快速排序都是分治算法的思想,但又明显的不同,对照学习会有更大收获。空间复杂度为 O(n) ,时间复杂度为 O(nlog2n) ,2-路归并排序算法是一个稳定的排序算法。

MergeSort
  多数教材上仅仅写出伪算法,所以下面贴出可运行的C代码。其中Merge函数是重点,有些教材上都写错了。一点要小心。

#include <stdio.h>
#include <stdlib.h>
#define MAX 100
int arr[MAX];

void Merge(int arrA[],int low,int middle,int high) {//Merge函数有多种实现方式,下面贴出来
    int i,j,k;
    int * arrB=new int[high-low+1];                 //动态申请一个辅助数组

    i=low;
    j=middle+1;
    k=0;                           //k为arrB数组的索引,从0开始。很多书上写k=low,在本方法中不适合;

    while(i<=middle && j<=high) {
        if(arrA[i]<=arrA[j])
            arrB[k++]=arrA[i++];
        else
            arrB[k++]=arrA[j++];
    }
    while(i<=middle) {
        arrB[k++]=arrA[i++];
    }
    while(j<=high) {
        arrB[k++]=arrA[j++];
    }

    for(i=low,k=0; i<=high; k++, i++) {
        arrA[i]=arrB[k];
    }
    delete [] arrB;
}

void MergeSort(int arr[],int low,int high) {
    int middle;
    if(low<high) {
        middle=(low+high)/2;            //取中点
        MergeSort(arr,low,middle);      //对arr[low:middle]进行排序
        MergeSort(arr,middle+1,high);   //对arr[middle+1:high]进行排序

        Merge(arr,low,middle,high);     //合并
    }
}

void PrintArray(int arr[],int length) {  //打印数组,用于查看排序效果
    printf("[");
    for(int i=0; i<length; i++) {
        if(i==length-1)
            printf("%d]\n",arr[i]);
        else
            printf("%d ,",arr[i]);
    }
}
int main() {
    int num=0;
    printf("请输入数组元素个数:");
    scanf("%d",&num);

    for(int i=0; i<num; i++) { //读入待排序数据
        scanf("%d",&arr[i]);
    }

    printf("排序前数据为:");
    PrintArray(arr,num);

    MergeSort(arr,0,num-1);

    printf("排序后数据为:");
    PrintArray(arr,num);
    return 0;
}

Merge函数的第二种实现方式

int arrB[MAX];              //把辅助数组arrB定义在全局范围。也省得delete操作了
void Merge(int arrA[],int low,int middle,int high) {
    int i,j,k;
    for(i=low;i<=high;i++)  //将arrA[]中的数据,备份到arrB[]中
        arrB[i]=arrA[i];

    i=low;
    j=middle+1;
    k=low;              

    while(i<=middle && j<=high) {   
        if(arrB[i]<=arrB[j])        //比较arrB[]的左右两段中的元素
            arrA[k++]=arrB[i++];    //较小的元素赋值到arrA[]中
        else
            arrA[k++]=arrB[j++];
    }
    while(i<=middle) {              //arrB[]前半段未检测完,直接拼接arrA[]后面
        arrA[k++]=arrB[i++];
    }
    while(j<=high) {               //arrB[]后半段未检测完,直接拼接arrA[]后面
        arrA[k++]=arrB[j++];
    }
}

Merge函数的第三种实现方式

//左右两个子数组,分别设置一个哨兵,避免每次比较都必须检查子数组是否为空,从而简化代码。
//仅给出伪代码,详细解释请翻阅《算法导论》17页。

这里写图片描述

F、快速排序

  快速排序是一种非常重要的排序,在学习程序设计、数据结构、算法设计与分析等课程的时候,老师三令五申,多次讲到。这里仅仅贴出实现方式。
  快速排序算法的详细设计思路请参考另一篇博文:算法导论——-快速排序QuickSort  快速排序算法中,最重要的就是Partition函数。Partition函数有好几个实现方式。请参考另外一篇博文:(后续补上)

//交换数组中两个元素位置
void swap(int &a,int & b)   {
    int tmp;
    tmp=a;
    a=b;
    b=tmp;
}

int Partition(int * Arr,int low,int high) {  //划分方法。有多种实现方式。
    //i和j分别指向数组下界和上界,pivot是轴点,本算法默认选取左端为轴点
    int i=low,j=high,pivot=Arr[low];
    while (i<j) {
        /* j自j位置开始向左扫面,如果j位置所对应的元素的值大于等于pivot,则j前移一个位置(即j--)。
        重复该过程,直到找到第一个小于pivot的元素R[j],将R[j]和R[i]进行交换,i++。
        其实交换后R[j]所对应的元素就是pivot。*/
        while (i<j && Arr[j]>=pivot)  {
            j--;
        }
        if (i<j) {
            swap(Arr[i++],Arr[j]);//注意这里是交换元素,另外还有挖坑法实现,是元素覆盖。
        }
        /* 令i自i位置开始向右扫描,如果i位置所对应的元素的值小于等于pivot,则i后移(即i++)。
        重复该过程,直至找到第一个大于pivot的元素R[i],将R[i]与R[j]进行交换,j--。
        其实,交换后R[i]所对应的元素就是pivot。*/
        while (i<j  && Arr[i]<=pivot) {
            i++;
        }
        if (i<j) {
            swap(Arr[i],Arr[j--]);
        }
    }
    return  i;                               //i和j相同,即基准元素pivot的最终位置。返回i的值
}
void QuickSort(int * Arr,int low,int high) {  //对数组Arr[low  high]进行快速排序
    int pivotpos;                             //划分的基本元素所在的位置
    if(low<high) {                            //区间长度大于1时才排序
        pivotpos=Partition(Arr,low,high);     //对Arr[low high]进行划分
        QuickSort(Arr,low,pivotpos-1);
        QuickSort(Arr,pivotpos+1,high);
    }
}

G、希尔排序

希尔排序是对插入排序的一个改进算法。排序的思路和算法分析,详见另一篇博文。算法导论——ShellSort希尔排序

void shellSort (int[] a, int n)
{
    int i, j, k, h, tmp;
    int[] cols = {1391376, 463792, 198768, 86961, 33936, 13776, 4592,
                    1968, 861, 336, 112, 48, 21, 7, 3, 1}
    for (k=0; k<16; k++)
    {
        h=cols[k];
        for (i=h; i<n; i++)           //i=h,意味着从第二行开始对每列InsertSort
        {
            tmp=a[i];                 //插入排序需要的临时变量。
            j=i;
            while (j>=h && a[j-h]>tmp)
            {
                a[j]=a[j-h];
                j=j-h;
            }
            a[j]=tmp;
        }
    }
}

H、堆排序

  堆排序是一种基于完全二叉树的树形选择排序 方法。在排序的过程中将待排序列看成是一颗完全二叉树的顺序存储结构,树上的每一个结点对应数组中的一个元素,可以利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序序列中构建“二叉堆”,简称“建堆”操作。从而在二叉堆的根部找到关键字最大(小)的元素。这种叫“堆”的数据结构可以保存每趟排序过程中的中间比较结果。堆按性质可分为大顶堆和小顶堆。如果想让序列按升序(降序)排序,就需要将待排序的n个元素构造成大顶堆(小顶堆)。此时堆顶即为最大值(最小值),将它和堆数组的尾元素交换。然后将剩余的n-1个待排元素重新建堆,从而得到n个元素中的次大元素,将它和堆数组的尾元素交换。反复迭代,最终得到一个有序的序列。关于堆排序更详细的解释,请参考另一篇博文:算法导论——堆排序heapsort

#include <stdio.h>
#include <stdlib.h>

int arrtest[100];

void swap(int *a,int *b) {
    int temp=*a;
    *a=*b;
    *b=temp;
}

void maxHeapify(int arr[],int i,int heapsize) {      //维护堆的性质
    int left=2*i+1;
    int right=2*i+2;
    int largest;
    if (left<=heapsize  && arr[left]>arr[i])
        largest=left;
        else
            largest=i;
    if (right<=heapsize  && arr[right]>arr[largest])
        largest=right;
    if(largest!=i) {
        swap(&arr[largest],&arr[i]);
        maxHeapify(arr,largest,heapsize);
    }
}

void buildMaxHeap(int arr[],int heapsize) {          //建堆
    for(int i=(heapsize-1)/2; i>=0; i--) {
        maxHeapify(arr,i,heapsize);
    }
}

void heapSort(int arr[],int len) {                   //堆排序
    buildMaxHeap(arr,len-1);
    for(int i=len-1; i>0; i--) {
        swap(&arr[0],&arr[i]);
        buildMaxHeap(arr,i-1);
    }
}

void PrintArray(int arr[],int length) {             //打印数组,用于查看排序效果
    printf("[");
    for(int i=0; i<length; i++) {
        if(i==length-1)
            printf("%d]\n",arr[i]);
        else
            printf("%d ,",arr[i]);
    }
}


int main() {
    int num=0;
    printf("请输入数组元素个数:");
    scanf("%d",&num);

    for(int i=0; i<num; i++) {                         //读入待排序数据
        scanf("%d",&arrtest[i]);
    }

    printf("排序前数据为:");
    PrintArray(arrtest,num);

    heapSort(arrtest,num);

    printf("排序后数据为:");
    PrintArray(arrtest,num);
    return 0;
}
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值