数据结构实验九(排序算法)

当然这也是数据结构的最后一个实验了,通过以前9次实验给我们带来了很深的思考,现在对于编程算法应该来说有了很大的提升,对于做题也是非常好的,我想现在对于我来说,编程能力有了质的飞跃,很感谢这门数据结构课程,让我理解了很多!当然后面《算法设计与分析》这门课也十分重要(尽管很多专业并不会开,在我们这好像就软件工程才开,所以需要自学了),是编程能力的又一突破,我也希望学完这门课的同学也可以学习算法这门课,推荐教材:李春葆,《算法设计与分析(第二版)》,清华大学出版社。
最后,对前面的数据结构实验进行汇总吧!
实验一 链表的应用(城市信息)
实验二 栈的应用(进货信息)
实验三 二叉树的算法(递归与非递归)
实验四 哈夫曼编码
实验五 图的遍历(邻接矩阵与邻接表)
实验六 AOE关键路径
实验七 查找的应用(姓名查找)
实验八 查找的算法(平衡二叉树+Hash)

本次数据结构实验为排序算法,书上讲了大概7种排序,本人比较推荐的是快速排序和堆排序,这两个排序也是C++ STL sort函数的排序方法,这里列举了各种排序方法性能比较,目前整理出六种排序算法,其中基数排序等过段时间更新以下,让我缓缓!(明天还得搞六级。。)
百度网盘提取算法测试代码提取码:ss6l

排序方法平均时间复杂度辅助存储空间
简单排序 O ( n 2 ) O(n^{2}) O(n2)O(1)
快速排序 O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlogn)O(log(n)
堆排序 O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlogn)O(1)
归并排序 O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlogn)O(n)
基数排序O(d(n+rd))O(rd)

首先给出结构体代码

typedef struct{
    int key;
}RecordList;

以下代码中均附有详细注释,故不再给出解释,只给出最精炼的语言!
你的三连就是我创作的最大动力!

直接插入排序

直接插入,怎么想呢,就是从第2个数开始与前面的进行排序,然后改变位置。

void InsertSort(RecordList r[],int n){
    for(int i=2;i<=n;i++){
        //为什么i从2开始,因为i=1不用排序
        count++;//计数每一趟
        r[0]=r[i];//设置哨兵,将待排序放在r[0]
        int j=i-1;//j表示前面已经排序过的位置
        while(r[0].key<r[j].key){
            //此处从小到大排序
            //将j后移,因为前面已经排序了,所以遇到大于就停
            r[j+1]=r[j];
            j--;
        }
        r[j+1]=r[0];//此时将哨兵插入到j+1位置
        //现在进行每一趟的输出
        cout<<"第"<<count<<"趟排序:";
        for(int k=1;k<=n;k++){
            cout<<r[k].key<<" ";
        }
        cout<<endl;
    }
}

冒泡排序

冒泡怎么小,很简单,就是每一趟选最小的在第i个位置(i=1,2…n)

void BubbleSort(RecordList r[],int n){
    bool change=true;//设置flag,这样不用做无用的循环
    for(int i=1;i<=n-1&&change;i++){
        count++;
        change=false;//如果已经排序完了,那么会变成false,下一次循环则跳出
        //不过我觉得这样多次一举,直接让j从i+1开始比较不就可以了吗?
        for(int j=1;j<=n-1;j++){
            if(r[j].key>r[j+1].key){
                swap(r[j],r[j+1]);
                change=true;
            }
        }
        //现在进行每一趟的输出
        cout<<"第"<<count<<"趟排序:";
        for(int k=1;k<=n;k++){
            cout<<r[k].key<<" ";
        }
        cout<<endl;
    }
}

快速排序

快速排序怎么想,先选出一个基准,一般为第一个数,然后进行从右和从左进行排序,注意先从右开始,然后遇到比基准小的,再从左开始,遇到比基准大的,然后对刚选出来的进行交换,这样直到中间,此时一个基准已经完成,后面分治递归即可。

//一趟快速排序过程
int QKPass(RecordList r[],int low,int high){
    count++;
    //对r[low]和r[high]进行划分,得出基准位置
    RecordList x=r[low];//现在从low开始并且保存在x中,也就是说x作为基准,将会对数组进行划分,比他小的在左,大的在左边
    //关键步骤,注意先从指针j开始,再从i开始
    while(low<high){
        while(low<high&&r[high].key>=x.key){
            high--;
        }
        //为什么这里每次都要出现low<high,因为每一次调整都需要看是否基准到位了,如果没达到则进行调整
        if(low<high){
            r[low]=r[high];
            low++;
        }
        while(low<high&&r[low].key<x.key){
            low++;
        }
        if(low<high){
            r[high]=r[low];
            high--;
        }
    }
    r[low]=x;
    //现在进行每一趟的输出
    cout<<"第"<<count<<"趟排序:";
    for(int k=1;k<=N;k++){
        cout<<r[k].key<<" ";
    }
    cout<<endl;
    return low;
}

void QuickSort(RecordList r[],int low,int high){
    if(low<high){
        int pos=QKPass(r,low,high);
        //采用分治法进行排序,其实也有类似于希尔排序的味道
        QuickSort(r,low,pos-1);
        QuickSort(r,pos+1,high);
    }
}

选择排序

选择排序怎么想呢?简单来说,就是选出最小的在左边,思想类似于冒泡排序,不过冒泡是一个数一直到底,而选择排序呢就是相邻的数交换。

void SelectSort(RecordList r[],int n){
    for(int i=1;i<=n-1;i++){
        //为什么从1开始,不像插入排序从2开始,因为i=1也需要选出最小的在第一位
        count++;
        int k=i;//保存此时i的位置
        for(int j=i+1;j<=n;j++){
            //区别于冒泡排序,j从i+1开始,因为前面已经排序,那么只需要对后面的进行排序
            if(r[j].key<r[k].key){
                k=j;//后面的如果比此时第i位小那么保存此时j的位置
            }
        }
        if(k!=i){
            swap(r[i],r[k]);
        }
        //现在进行每一趟的输出
        cout<<"第"<<count<<"趟排序:";
        for(int k=1;k<=N;k++){
            cout<<r[k].key<<" ";
        }
        cout<<endl;
    }
}

堆排序

堆排序怎么想?首先你要有二叉树的基本知识,特别是层次遍历,对于二叉树的孩子双亲数组有认识才能理解算法的创建。注意本质就是从左到右进行选父亲结点,然后把根节点当在堆底。

//筛选法:改变一次堆
void sift(RecordList r[],int k,int m){
    //这是层次遍历的二叉树数组,r[k]为根,r[2k]左,r[2k+1]右,为什么呢?参考二叉树孩子双亲存储
    RecordList t=r[k];//保存根的位置
    int x=r[k].key;
    int i=k;//保存指针k的位置i
    int j=2*i;//注意只是指i的左子树,先从左到右进行遍历
    bool finished=false;//为什么要设置flag,类似于冒泡,避免无效多余的循环
    while(j<m&&!finished){
        //m是什么呢?m就是堆底
        //首先比较左右子树,如果左右子树大小不一样,则指针改变,变成右子树
        if(j<m&&r[j].key<r[j+1].key) j++;
        //如果父亲结点大于孩子结点则true,不然就交换位置,注意这是一次改变
        if(x>=r[j].key) finished=true;
        else{
            r[i]=r[j];
            i=j;
            j=2*i;
        }
    }
    //将t插入到此时i位置,如果没有改变则还是原位置,如果改变则进行位置转换
    r[i]=t;
}

//建立堆算法
void crt_heap(RecordList r[],int n){
    for(int i=n/2;i>=1;--i){
        //为什么从n/2开始呢?二叉树呗,教材以中序遍历为根,没进行一次都需要改变
        sift(r,i,n);
    }
}

//堆排序,前面两步都是前期工作,相当于每一次都要改变全部根节点
void HeapSort(RecordList r[],int n){
    crt_heap(r,n);
    for(int i=n;i>2;i--){
        //为什么从n开始,这样比较好操作
        count++;
        RecordList b=r[1];//第一位置
        r[1]=r[i];
        r[i]=b;
        sift(r,1,i-1);
        //现在进行每一趟的输出
        cout<<"第"<<count<<"趟排序:";
        for(int k=1;k<=N;k++){
            cout<<r[k].key<<" ";
        }
        cout<<endl;
    }
}

归并排序

归并排序怎么想呢?有没有感觉希尔排序?有增量的感觉。从中间开始拆分,然后排序,最后一个个的合并,这就是其本质。

void Merge(RecordList r1[],int low,int mid,int high,RecordList r[]){
    count++;
    //从中间开始归并,将r1[low..mid]和r1[mid+1...high]排列保存在r[]
    int i=low,j=mid+1,k=low;//k作为r的low位
    while((i<=mid)&&(j<=high)){
        //从中间分成两路,然后对两路进行排序
        if(r1[i].key<=r1[j].key){
            //如果小于中间的,那么放在r[low]中
            r[k]=r1[i];
            i++;
        }
        else{
            r[k]=r1[j];
            j++;
        }
        k++;//r[]遍历创建
    }
    while(i<=mid){
        r[k]=r1[i];
        k++;
        i++;
    }
    while(j<=high){
        r[k]=r1[j];
        k++;
        j++;
    }
}

void MergeSort(RecordList r1[],int low,int high,RecordList r[]){
    //排序后放在r[]中,r2作为辅助空间
    count++;
    RecordList *r2;
    r2=new RecordList[high-low+1];
    if(low==high) r[low]=r1[low];
    else{
        int mid=(low+high)/2;
        MergeSort(r1,low,mid,r2);
        MergeSort(r1,mid+1,high,r2);
        Merge(r2,low,mid,high,r);
    }
    delete r2;
}

总结

以上几种排序是最基本的算法,当然排序算法还是可以进行进一步优化的,有时间出一个优化算法。这些算法都是数据结构的基本功,尽管理解起来比较容易,但是代码实现还是有一定的难度,特别体现在快速、归并、堆排序中。对于排序的理解,我感觉是注意抓住指针(位置)的特性,什么时候改变位置,什么时候交换都是要比较清楚的。

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值