<数据结构学习>排序——快速排序

本文介绍了如何使用快速排序算法优化版,通过选取基准元素为序列首位、中间和最末元素的中间值(median3),实现快速排序并计算median3值。当序列长度小于5时,采用插入排序。详细展示了C++代码实现过程。

目录

问题描述

 问题分析

问题解决


问题描述

要求根据给定输入,按照快速排序算法(殷人昆第2版教材 P349 程序8-12)进行排序,输出排序结果和median3的返回值。在划分时,以当前序列的首位元素、中间位置元素和最末元素的中间值为枢轴,记为median3。注意,如median3不在首位,需要和首位元素交换位置。

 注:1,cutoff值为5,不足cutoff使用插入排序。

        2,输入、输出格式参见测试用例0。

测试输入期待的输出时间限制内存限制额外进程
测试用例 1以文本方式显示
  1. 41↵
  2. 17↵
  3. 34↵
  4. 0↵
  5. 19↵
  6. #↵
以文本方式显示
  1. After Sorting:↵
  2. 0 17 19 34 41 ↵
  3. Median3 Value:↵
  4. none↵
1秒64M0
测试用例 2以文本方式显示
  1. 61↵
  2. 59↵
  3. 82↵
  4. -10↵
  5. 31↵
  6. -2↵
  7. -3↵
  8. 10↵
  9. 2↵
  10. 108↵
  11. 12↵
  12. 80↵
  13. -21↵
  14. 127↵
  15. 12↵
  16. #↵
以文本方式显示
  1. After Sorting:↵
  2. -21 -10 -3 -2 2 10 12 12 31 59 61 80 82 108 127 ↵
  3. Median3 Value:↵
  4. 12 -3 61 ↵
1秒64M0

 问题分析

我们采用快速排序优化版——选基准记录的三者取中快速算法。

每次在最左边、中间和最右边选取次小值作为基准元素,并将基准元素换到倒数第二个位置。

置两个移动下标分别从左、右向中心遍历,若遇到左边值比基准元素大的或者右边值比基准元素小的就交换。最后两下标指向相同的元素,把该元素和基准元素互换位置。

这样就把原序列按基准元素分成左右两边,左边的元素都小于基准元素,右边的元素都大于基准元素。

然后再分别在左右两边排序,递归即可。

问题解决

定义全局变量

#define N 10010
int num[N]; //需要排序的数组
int medium[N];  //mid数组
int countmid=0;  //mid个数

定义交换函数

void swapp(int x,int y) //直接传下标
{
    int temp;
    temp=num[x];
    num[x]=num[y];
    num[y]=temp;
}

求每次基准元素的函数

int median3(int left,int right)
{
    int mid=(left+right)/2;
    if(num[left]>num[mid])  //这三轮if完了之后能保证最左、中、最右三个元素按从小到大排序
    {
        swapp(left,mid);
    }
    if(num[left]>num[right])
    {
        swapp(left,right);
    }
    if(num[mid]>num[right])
    {
        swapp(mid,right);
    } 
    
    swapp(mid,right-1);  //把中间元素换到倒数第二个

    medium[countmid]=num[right-1]; //mid值计入数组
    countmid++;

    return num[right-1];  //返回本轮基准元素
    
}

定义划分函数

int Part(int left,int right)
{
    if(right-left<5)  //题目要求小于5个数时直接插入排序
    { 
        Insert(left,right);  //此函数稍后定义
        return -1;
    }
    int pivot=median3(left,right);//拿到基准元素
    int i=left;
    int j=right-1;
    while (i<j) 
    {
        while(i<j && num[++i]<pivot); //一直遍历到左边某元素大于等于基准元素后跳出
        while(i<j && num[--j]>pivot); //一直遍历到右边某元素小于等于基准元素后跳出
        
        swapp(j,i);  //交换两元素
    } 
    swapp(right-1,i); //最后交换基准元素和两指针同时指向的元素
    return i;  //返回基准元素下标
}

划分函数中用到的insert函数如下

void Insert(int left,int right) //直接插入排序
{
    int i,j,temp;
    for(i=left;i<=right;i++)
    {
        if(num[i]<num[i-1])
        {
            temp=num[i];
            for(j=i-1;j>=left && temp<num[j];j--)
            {
                num[j+1]=num[j];
            }
            num[j+1]=temp;
        }
    }
}

快速排序函数

void Quick(int left,int right)
{
    int mid;
    if(left<right)
    {
        mid=Part(left,right);
        if(mid!=-1){ //不用直接排序时说明数据还很多,要继续递归快速排序
        Quick(left,mid-1);
        Quick(mid+1,right);
        }
    }
}

main函数

int main()
{
    int l=0;
    string t; //注意读入数据的方法
    while (cin>>t)
    {
        if (t == "#") break;
        num[l] = stoi(t); //stoi()是将字符串类型转化为整型
        l++;
    }
    
        Quick(0,l-1);
        cout<<"After Sorting:"<<endl;
        for(int i=0;i<l;i++)
        {
            cout<<num[i]<<" ";
        }
        cout<<endl;
        cout<<"Median3 Value:"<<endl;
        if(countmid==0) cout<<"none"<<endl;
        else{
        for(int i=0;i<countmid;i++)
        {
            cout<<medium[i]<<" ";
        }
        cout<<endl;
        return 0;
        }
    
}

 可以积累一下连续读入不确定位数的整数直到读入某字符时停止读数据的方法。

其中stoi()是将字符类型串转化为整型的内置函数

完整代码如下:

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#define N 10010
using namespace std;
int num[N];
int medium[N];
int countmid=0;

void Insert(int left,int right)
{
    int i,j,temp;
    for(i=left;i<=right;i++)
    {
        if(num[i]<num[i-1])
        {
            temp=num[i];
            for(j=i-1;j>=left && temp<num[j];j--)
            {
                num[j+1]=num[j];
            }
            num[j+1]=temp;
        }
    }
}
void swapp(int x,int y) //直接传下标
{
    int temp;
    temp=num[x];
    num[x]=num[y];
    num[y]=temp;
}

int median3(int left,int right)
{
    int mid=(left+right)/2;
    if(num[left]>num[mid])
    {
        swapp(left,mid);
    }
    if(num[left]>num[right])
    {
        swapp(left,right);
    }
    if(num[mid]>num[right])
    {
        swapp(mid,right);
    }
    
    swapp(mid,right-1);

    medium[countmid]=num[right-1];
    countmid++;

    return num[right-1];
    
}

int Part(int left,int right)
{
    if(right-left<5)
    {
        Insert(left,right);
        return -1;
    }
    int pivot=median3(left,right);//拿到基准元素
    int i=left;
    int j=right-1;
    while (i<j)
    {
        while(i<j && num[++i]<pivot);
        while(i<j && num[--j]>pivot);
        
        swapp(j,i);
    }
    swapp(right-1,i);
    return i;
}

void Quick(int left,int right)
{
    int mid;
    if(left<right)
    {
        mid=Part(left,right);
        if(mid!=-1){
        Quick(left,mid-1);
        Quick(mid+1,right);
        }
    }
}
int main()
{
    int l=0;
    string t;
    while (cin>>t)
    {
        if (t == "#") break;
        num[l] = stoi(t);
        l++;
    }
    
        Quick(0,l-1);
        cout<<"After Sorting:"<<endl;
        for(int i=0;i<l;i++)
        {
            cout<<num[i]<<" ";
        }
        cout<<endl;
        cout<<"Median3 Value:"<<endl;
        if(countmid==0) cout<<"none"<<endl;
        else{
        for(int i=0;i<countmid;i++)
        {
            cout<<medium[i]<<" ";
        }
        cout<<endl;
        return 0;
        }
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值