王道数据结构源码实战ch8排序

插入排序

在这里插入图片描述

直接插入排序

//直接插入排序
void InsertSort(Elemtype A[],int n)
{
    int j;
    for(int i=2; i<=n; i++)  //i=0处用来放哨兵
    {
        A[0]=A[i];
        for(j=i-1; A[0]<A[j]; j--) //不按下标来控制,只看元素大小
        {
            A[j+1]=A[j];
        }
        A[j+1]=A[0];
    }
}

折半插入排序

//折半插入排序
void HalfInsertSort(Elemtype A[],int n)
{
    int i,j,low,mid,high;
    for(int i=2; i<=n; i++)  //i=0处用来放哨兵
    {
        A[0]=A[i];
        low=1;
        high=i-1;
        while(low<=high)
        {
            mid=(low+high)/2;
            if(A[mid]>A[0])
                high=mid-1;
            else
                low=mid+1;
        }
//如果没有找到相等的元素,那么最后high会到low的左边或者low跑到high的右边,high的右边都是更大的元素,low的左边都是更小的元素
//如果找到了相等的元素,由于这里没有return,只会发生low跑到high的右边才会退出循环,此时high指向相等的元素,只有high+1右移,稳定性也能得到保证
        for(j=i-1; j>=high+1; j--) //high+1非常妙
        {
            A[j+1]=A[j];
        }
        A[j+1]=A[0];
    }
}

希尔排序

在这里插入图片描述

//希尔排序
//先写出插入排序,然后外面套一个缩减步长的循环,并且把所有的步长1换成dk
void ShellSort(Elemtype A[],int n)
{
    int j; //必须在外面定义,因为最后一步需要把哨兵放回j+dk位置
    for(int dk=n/2; dk>=1; dk/=2) //dk为步长  //步长为1时退化为插入排序
    {
        for(int i=dk+1; i<=n; i++) //以dk为步长进行插入排序
        {
            A[0]=A[i];  //设置哨兵
            for(j=i-dk; j>0&&A[0]<A[j]; j-=dk) //步长较大时,比较时可能会直接越界
                A[j+dk]=A[j];
            A[j+dk]=A[0];
        }
    }
}

交换排序

冒泡排序

在这里插入图片描述

//冒泡排序
//可以从后开始把小的元素前移,也可以从前开始把大的元素后移
void BubbleSort(Elemtype A[],int n)
{
    bool flag;   //用flag来观察一趟排序中有没有交换元素,如果没有,说明序列已经有序
    for(int i=0; i<n-1; i++) //第i轮表示已经有i个元素已经冒泡了,进行n-1轮,最后一个元素不用排了
    {
        flag=false;   //使用flag可以在序列有序时(在某一轮中,没有进行交换),提前结束程序
        for(int j=n-1; j>i; j--) //i=j不能取,因为后面会访问A[j-1],取了就越界了。代入i=0看一下就知道了(j-1最终冒到最上面=0,那么j最小=1)
            if(A[j-1]>A[j])
            {
                swap(A[j-1],A[j]);
                flag=true;
            }
        if(flag==false)
            return;  //某一轮中,没有一次交换,说明序列已经有序,可以提前结束
    }
}

快速排序

在这里插入图片描述

//挖坑法分割,只能low和high交替填坑(不能一边连续找两个大于或小于pivot的元素)
int Partition1(Elemtype A[],int low,int high)
{
    Elemtype pivot=A[low];
    while(low<high)
    {
        while(low<high&&A[high]>pivot)   //因为默认low为pivot,所以从high开始找元素填空
            high--;
        A[low]=A[high];
        while(low<high&&A[low]<pivot)   //为什么内层要重复判断low<high,因为在内层循环过程中,随时可能提前结束
            low++;
        A[high]=A[low];
    }
    A[low]=pivot;   //此时high=low,用哪个下标都行
    return low;  //把已经定位的元素小标传出来,用来划分下一轮子区间
}
//遍历法分割,比较简单(以最右边元素为pivot)
int Partition2(Elemtype A[],int low,int high)
{
    int left=low;   //只用来指向小于pivot的元素
    for(int i=low; i<high; i++) //遍历pivot后面所有元素
    {
        if(A[i]<A[high])   //i始终比left走的快,所以不会产生left和i指向两个小于pivot的元素,导致小的元素遗留在右边
        {
            swap(A[i],A[left]);    //每找到一个小于pivot的元素,与left处交换(可以保证left前面所有元素都是小于pivot的,left和i之间所有元素都是大于pivot的)
            left++;
        }
    }
    swap(A[left],A[high]);   //最后,把pivot放到应该在的位置
    return left;
}

//快速排序主函数
void QuickSort(Elemtype A[],int low,int high)
{
    if(low<high) //分割到子区间只有一个元素为止
    {
        Elemtype pivot=Partition2(A,low,high);
        QuickSort(A,low,pivot-1);
        QuickSort(A,pivot+1,high);
    }
}

选择排序

简单选择排序

在这里插入图片描述

//简单选择排序
void SelectSort(Elemtype A[],int n)
{
    for(int i=0; i<n-1; i++) //执行完n-1轮,最大的元素已经在最后一个位置
    {
        int min=A[i];           //假设第i个元素为当前轮的最小元素
        int index=i;
        for(int j=i+1; j<n; j++)
        {
            if(A[j]<min)
            {
                min=A[j];
                index=j;
            }
        }
        if(A[i]!=A[index])
            swap(A[i],A[index]);
    }
}

堆排序

在这里插入图片描述

//调整一个元素为根的子树为大根堆
void HeadAdjust(int A[],int k,int n)
{
    A[0]=A[k];  //暂存根节点
    for(int i=2*k; i<=n; i*=2) //寻找子树中最大的节点
    {
        if(i<n&&A[i]<A[i+1])  //如果当前节点有右孩子,且右孩子比左孩子大,选择右孩子向上
            i++;
        if(A[0]>=A[i])   //当前节点是最大的,不需要调整
            break;
        else             //当前节点不是最大的,A[i]向上调整
        {
            A[k]=A[i];
            k=i;           //为了进入下一轮循环,检查被调整上来的节点是否满足大根堆要求
        }
    }
    A[k]=A[0];   //最后的下坠位置
}
//建立大根堆
void BuildMaxHeap(int A[],int n)  //时间复杂度O(n)
{
    for(int i=n/2; i>0; i--)        //想下往上处理非叶子节点
        HeadAdjust(A,i,n);          //而对于每个子树,小元素从上往下下坠
}
//堆排序主函数,每趟把堆顶元素与待排序序列最后一个元素交换
void HeapSort(int A[],int n)
{
    BuildMaxHeap(A,n);
    for(int i=n; i>1; i--)
    {
        swap(A[i],A[1]);  //把堆顶元素和堆底元素交换(逻辑上把当前最大的元素排除在堆外)
        HeadAdjust(A,1,i-1); //恢复剩余元素为大根堆,(1号元素为根,i-1个元素)
    }
}

归并排序

在这里插入图片描述

//二路归并过程
int *B=new Elemtype[50]; //辅助空间
void Merge(Elemtype A[],int low,int mid,int high)  //[low,mid]和[mid+1,high]内部是有序的,把这两个区间进行二路归并
{
    int i,j,k;
    for(k=low; k<=high; k++) //把A数组复制一份到B
        B[k]=A[k];
    for(i=low,j=mid+1,k=i; i<=mid&&j<=high; k++) //只有有一个区间元素走完,跳出循环
    {
        if(B[i]<=B[j])
            A[k]=B[i++];
        else
            A[k]=B[j++];
    }
    while(i<=mid)
        A[k++]=B[i++];
    while(j<=high)
        A[k++]=B[j++];
}

//归并排序主函数
void MergeSort(int A[],int low,int high)
{
    if(low<high)
    {
        int mid=(low+high)/2;
        MergeSort(A,low,mid);
        MergeSort(A,mid+1,high);
        Merge(A,low,mid,high);   //起到排序作用的是Merge函数
    }
}

基数排序

在这里插入图片描述

外部排序

在这里插入图片描述

算法性能比较和其他

算法最好平均最差空间复杂度稳定性
直接插入排序O(n)O(n2)O(n2)O(1)稳定
冒泡排序O(n)O(n2)O(n2)O(1)稳定
简单选择排序O(n2)O(n2)O(n2)O(1)不稳定
希尔排序O(n1.3) -O(n2)O(n2)O(1)不稳定
快速排序O( n l o g 2 n nlog_2n nlog2n)O( n l o g 2 n nlog_2n nlog2n)O( n 2 n^2 n2)O( l o g 2 n log_2n log2n)不稳定
堆排序O( n l o g 2 n nlog_2n nlog2n)O( n l o g 2 n nlog_2n nlog2n)O( n l o g 2 n nlog_2n nlog2n)O(1)不稳定
二路归并排序O( n l o g 2 n nlog_2n nlog2n)O( n l o g 2 n nlog_2n nlog2n)O( n l o g 2 n nlog_2n nlog2n)O(n)稳定
基数排序O(d(n+r))O(d(n+r))O(d(n+r))O( r )稳定

Python中封装的接口是归并
C++封装的接口是快拍
Java封装的接口是快排&&堆排

完整代码

#include<bits/stdc++.h>
using namespace std;
typedef int Elemtype;
typedef struct SSTable
{
    Elemtype *elem;  //线性表起始位置
    int TableLen; //元素个数
} SSTable;

//随机生成线性表用来测试
void ST_init(SSTable &ST,int len)
{
    ST.TableLen=len;
    ST.elem=(Elemtype*)malloc(sizeof(Elemtype)*ST.TableLen);
    srand(time(NULL));  //随机生成0-99的元素
    for(int i=0; i<ST.TableLen; i++)
        ST.elem[i]=rand()%100;
}

//打印下标从0开始的数组
void ST_print(SSTable ST)
{
    for(int i=0; i<ST.TableLen; i++)
        cout<<ST.elem[i]<<" ";
    cout<<endl;
}

//打印带哨兵的数组(从下标1开始)
void ST_print2(SSTable ST)
{
    for(int i=1; i<=ST.TableLen; i++)
        cout<<ST.elem[i]<<" ";
    cout<<endl;
}

//交换两个元素
void swap(Elemtype &a,Elemtype &b)
{
    Elemtype temp;
    temp=a;
    a=b;
    b=temp;
}


//直接插入排序
void InsertSort(Elemtype A[],int n)
{
    int j;
    for(int i=2; i<=n; i++)  //i=0处用来放哨兵
    {
        A[0]=A[i];
        for(j=i-1; A[0]<A[j]; j--) //不按下标来控制,只看元素大小
        {
            A[j+1]=A[j];
        }
        A[j+1]=A[0];
    }
}

//折半插入排序
void HalfInsertSort(Elemtype A[],int n)
{
    int i,j,low,mid,high;
    for(int i=2; i<=n; i++)  //i=0处用来放哨兵
    {
        A[0]=A[i];
        low=1;
        high=i-1;
        while(low<=high)
        {
            mid=(low+high)/2;
            if(A[mid]>A[0])
                high=mid-1;
            else
                low=mid+1;
        }
//如果没有找到相等的元素,那么最后high会到low的左边或者low跑到high的右边,high的右边都是更大的元素,low的左边都是更小的元素
//如果找到了相等的元素,由于这里没有return,只会发生low跑到high的右边才会退出循环,此时high指向相等的元素,只有high+1右移,稳定性也能得到保证
        for(j=i-1; j>=high+1; j--) //high+1非常妙
        {
            A[j+1]=A[j];
        }
        A[j+1]=A[0];
    }
}

//希尔排序
//先写出插入排序,然后外面套一个缩减步长的循环,并且把所有的步长1换成dk
void ShellSort(Elemtype A[],int n)
{
    int j; //必须在外面定义,因为最后一步需要把哨兵放回j+dk位置
    for(int dk=n/2; dk>=1; dk/=2) //dk为步长  //步长为1时退化为插入排序
    {
        for(int i=dk+1; i<=n; i++) //以dk为步长进行插入排序
        {
            A[0]=A[i];  //设置哨兵
            for(j=i-dk; j>0&&A[0]<A[j]; j-=dk) //步长较大时,比较时可能会直接越界
                A[j+dk]=A[j];
            A[j+dk]=A[0];
        }
    }
}

//冒泡排序
//可以从后开始把小的元素前移,也可以从前开始把大的元素后移
void BubbleSort(Elemtype A[],int n)
{
    bool flag;   //用flag来观察一趟排序中有没有交换元素,如果没有,说明序列已经有序
    for(int i=0; i<n-1; i++) //第i轮表示已经有i个元素已经冒泡了,进行n-1轮,最后一个元素不用排了
    {
        flag=false;   //使用flag可以在序列有序时(在某一轮中,没有进行交换),提前结束程序
        for(int j=n-1; j>i; j--) //i=j不能取,因为后面会访问A[j-1],取了就越界了。代入i=0看一下就知道了(j-1最终冒到最上面=0,那么j最小=1)
            if(A[j-1]>A[j])
            {
                swap(A[j-1],A[j]);
                flag=true;
            }
        if(flag==false)
            return;  //某一轮中,没有一次交换,说明序列已经有序,可以提前结束
    }
}

//以下三个函数均为快速排序的组成部分
//挖坑法分割,只能low和high交替填坑(不能一边连续找两个大于或小于pivot的元素)
int Partition1(Elemtype A[],int low,int high)
{
    Elemtype pivot=A[low];
    while(low<high)
    {
        while(low<high&&A[high]>pivot)   //因为默认low为pivot,所以从high开始找元素填空
            high--;
        A[low]=A[high];
        while(low<high&&A[low]<pivot)   //为什么内层要重复判断low<high,因为在内层循环过程中,随时可能提前结束
            low++;
        A[high]=A[low];
    }
    A[low]=pivot;   //此时high=low,用哪个下标都行
    return low;  //把已经定位的元素小标传出来,用来划分下一轮子区间
}

//遍历法分割,比较简单(以最右边元素为pivot)
int Partition2(Elemtype A[],int low,int high)
{
    int left=low;   //只用来指向小于pivot的元素
    for(int i=low; i<high; i++) //遍历pivot后面所有元素
    {
        if(A[i]<A[high])   //i始终比left走的快,所以不会产生left和i指向两个小于pivot的元素,导致小的元素遗留在右边
        {
            swap(A[i],A[left]);    //每找到一个小于pivot的元素,与left处交换(可以保证left前面所有元素都是小于pivot的,left和i之间所有元素都是大于pivot的)
            left++;
        }
    }
    swap(A[left],A[high]);   //最后,把pivot放到应该在的位置
    return left;
}

//快速排序主函数
void QuickSort(Elemtype A[],int low,int high)
{
    if(low<high) //分割到子区间只有一个元素为止
    {
        Elemtype pivot=Partition2(A,low,high);
        QuickSort(A,low,pivot-1);
        QuickSort(A,pivot+1,high);
    }
}


//简单选择排序
void SelectSort(Elemtype A[],int n)
{
    for(int i=0; i<n-1; i++) //执行完n-1轮,最大的元素已经在最后一个位置
    {
        int min=A[i];           //假设第i个元素为当前轮的最小元素
        int index=i;
        for(int j=i+1; j<n; j++)
        {
            if(A[j]<min)
            {
                min=A[j];
                index=j;
            }
        }
        if(A[i]!=A[index])
            swap(A[i],A[index]);
    }
}

//以下三个函数为堆排序的组成部分
//调整一个元素为根的子树为大根堆
void HeadAdjust(int A[],int k,int n)
{
    A[0]=A[k];  //暂存根节点
    for(int i=2*k; i<=n; i*=2) //寻找子树中最大的节点
    {
        if(i<n&&A[i]<A[i+1])  //如果当前节点有右孩子,且右孩子比左孩子大,选择右孩子向上
            i++;
        if(A[0]>=A[i])   //当前节点是最大的,不需要调整
            break;
        else             //当前节点不是最大的,A[i]向上调整
        {
            A[k]=A[i];
            k=i;           //为了进入下一轮循环,检查被调整上来的节点是否满足大根堆要求
        }
    }
    A[k]=A[0];   //最后的下坠位置
}

//建立大根堆
void BuildMaxHeap(int A[],int n)  //时间复杂度O(n)
{
    for(int i=n/2; i>0; i--)        //想下往上处理非叶子节点
        HeadAdjust(A,i,n);          //而对于每个子树,小元素从上往下下坠
}

//堆排序主函数,每趟把堆顶元素与待排序序列最后一个元素交换
void HeapSort(int A[],int n)
{
    BuildMaxHeap(A,n);
    for(int i=n; i>1; i--)
    {
        swap(A[i],A[1]);  //把堆顶元素和堆底元素交换(逻辑上把当前最大的元素排除在堆外)
        HeadAdjust(A,1,i-1); //恢复剩余元素为大根堆,(1号元素为根,i-1个元素)
    }
}

//以下两个函数为归并排序的组成部分
//二路归并过程
int *B=new Elemtype[50]; //辅助空间
void Merge(Elemtype A[],int low,int mid,int high)  //[low,mid]和[mid+1,high]内部是有序的,把这两个区间进行二路归并
{
    int i,j,k;
    for(k=low; k<=high; k++) //把A数组复制一份到B
        B[k]=A[k];
    for(i=low,j=mid+1,k=i; i<=mid&&j<=high; k++) //只有有一个区间元素走完,跳出循环
    {
        if(B[i]<=B[j])
            A[k]=B[i++];
        else
            A[k]=B[j++];
    }
    while(i<=mid)
        A[k++]=B[i++];
    while(j<=high)
        A[k++]=B[j++];
}
//归并排序
void MergeSort(int A[],int low,int high)
{
    if(low<high)
    {
        int mid=(low+high)/2;
        MergeSort(A,low,mid);
        MergeSort(A,mid+1,high);
        Merge(A,low,mid,high);   //起到排序作用的是Merge函数
    }
}


int main()
{
    SSTable ST;
    Elemtype A[10]= {64,94,95,79,69,84,18,22,12,78}; //使用固定数据
    Elemtype B[11]= {0,64,94,95,79,69,84,18,22,12,78}; //使用固定数据(有哨兵的情况使用)
    memcpy(ST.elem,A,sizeof(A));  //整型和浮点型不能用strcpy,strcpy遇0会结束,而memcpy可以按内存大小来复制
    ST.TableLen=10;
    //ST_init(ST,10);   //使用随机生成数据

    cout<<"原始序列:"<<endl;
    ST_print(ST);

    cout<<"冒泡排序结果:"<<endl;
    BubbleSort(A,10);
    memcpy(ST.elem,A,sizeof(A));
    ST_print(ST);

    cout<<"快速排序结果:"<<endl;
    QuickSort(A,0,9);
    memcpy(ST.elem,A,sizeof(A));
    ST_print(ST);

    cout<<"插入排序结果:"<<endl;
    InsertSort(B,10);
    memcpy(ST.elem,B,sizeof(B));
    ST_print2(ST);

    cout<<"折半插入排序结果:"<<endl;
    HalfInsertSort(B,10);
    memcpy(ST.elem,B,sizeof(B));
    ST_print2(ST);

    cout<<"希尔排序结果:"<<endl;
    ShellSort(B,10);
    memcpy(ST.elem,B,sizeof(B));
    ST_print2(ST);

    cout<<"简单选择排序结果:"<<endl;
    SelectSort(A,10);
    memcpy(ST.elem,A,sizeof(A));
    ST_print(ST);

    cout<<"堆排序结果:"<<endl;
    HeapSort(B,10);
    memcpy(ST.elem,B,sizeof(B));
    ST_print2(ST);

    cout<<"归并序结果:"<<endl;
    MergeSort(A,0,9);
    memcpy(ST.elem,A,sizeof(A));
    ST_print(ST);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WYF19999

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值