ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

荷兰国旗问题与快速排序

2021-12-30 15:01:37  阅读:173  来源: 互联网

标签:国旗 right int arr 数组 荷兰 small 排序 left


将一个数组按照某个划分值,划分为三部分,即小于该值的部分,等于部分和大于部分,这个问题称为荷兰国旗问题。
由于荷兰国旗由三种颜色组成,而该问题是将数组划分为三部分,两者具有相似性,所以该问题由此得名。

简化版荷兰国旗问题

先考虑简化版问题。将一个数组划分为两部分,如小于等于划分值的部分和大于部分。这里可以想到使用双指针。两个指针分别是小于等于区的右边界和遍历时下标,这样就把数组分为三部分,小于等于区、大于区、待遍历区。
在这里插入图片描述
这里划分值取为5。
这样,就相当于是小于等于区推着大于区往后遍历,当数组遍历完成后,数组划分也顺利完成。

int flag1(vector<int> &arr)
{
    int i=0,small=-1;//遍历数组的指针和小于等于区域的左边界
    int key=arr[0];//划分值这里取数组的第一个值,也可以取其他值
    while(i<arr.size())
    {
        if(arr[i]<=key)
        {
            swap(arr[small+1],arr[i]);//交换小于等于区的下个数和该遍历数
            ++small;
        }
        ++i;
    }
    return 0;
}

这里swap()的功能是将两个数进行交换,这里代码省略。

荷兰国旗问题

现在考虑将数组划分为三部分的问题。由上面简化版问题而来,这里再多设置一个指针,表示大于区右边界。这样,数组就分为了四部分,小于区、等于区、待遍历区、大于区。
在这里插入图片描述
这里划分值依然取为5。
这样,就相当于是小于区推着等于区往后遍历,而大于区逐渐向左扩。当数组遍历到大于区边界时,数组划分也顺利完成。

int flag2(vector<int> &arr)
{
    int i=0,small=-1;//遍历数组的指针和小于区域的右边界
    int big=arr.size();//大于区域的左边界
    int key=arr[0];//划分值这里取数组的第一个值,也可以取其他值
    while(i<big)
    {
        if(arr[i]<key)
        {
            swap(arr[small+1],arr[i]);//交换小于区的下个数和该遍历数
            ++small;
            ++i;
        }else if(arr[i]==key)
        {
            ++i;
        }else
        {
            swap(arr[big-1],arr[i]);
            --big;
        }
    }
    return 0;
}

快速排序

快速排序是应用较广的排序算法,其本质就是以数组中的某个值为划分值,将数组进行划分,从而确定这个值的位置,而后再进行递归或迭代。这里面就用到了荷兰国旗问题。

快速排序1.0

1.0版本的快速排序算法,就是以数组中的第一个值为划分值,将数组划分为小于等于区和大于区两部分,而后再将该值与小于等于区最后一个值交换,将该值放到最后的位置。而后继续递归或迭代。

int partition1(vector<int> &arr,int left,int right)
{
    if(left>=right)
    {
        return 0;
    }
    int i=left,small=left-1;//遍历数组的指针和小于等于区域的左边界
    int key=arr[left];//划分值这里依然取数组的第一个值
    while(i<=right)
    {
        if(arr[i]<=key)
        {
            swap(arr[small+1],arr[i]);
            ++small;
        }
        ++i;
    }
    swap(arr[left],arr[small]);//将划分值放到小于等于区的最后一个位置上
    partition1(arr,0,small-1);
    partition1(arr,small+1,right);

}

int qsort1(vector<int> &arr)
{
    partition1(arr,0,arr.size()-1);
    return 0;
}

快速排序2.0

2.0版本,与1.0版本的区别,在于多划分了一个等于区。1.0与2.0的区别就如同荷兰国旗问题的简化版与正式版。

int partition2(vector<int> &arr,int left,int right)
{
    if(left>=right)
    {
        return 0;
    }
    int i=left,small=left-1;//遍历数组的指针和小于区域的右边界
    int big=right+1;//大于区域的左边界
    int key=arr[left];
    while(i<big)
    {
        if(arr[i]<key)
        {
            swap(arr[small+1],arr[i]);//交换小于区的下个数和该遍历数
            ++small;
            ++i;
        }else if(arr[i]==key)
        {
            ++i;
        }else
        {
            swap(arr[big-1],arr[i]);
            --big;
        }
    }
    partition2(arr,0,small);
    partition2(arr,big,right);
    return 0;
}

int qsort2(vector<int> &arr)
{
    partition2(arr,0,arr.size()-1);
    return 0;
}

注意arr[i]比划分值大时,i值不变。
2.0版本比1.0版本稍快,因为2.0版本一次解决了一批数。

快速排序3.0

1.0和2.0最大的弊端,在于原数组在基本有序的情况下,划分值有可能选的很偏,使得时间复杂度逼近O(N2),为了改善这一点,我们可以随机选取划分值,而后将其与原数组第一个值交换,这样就与1.0,2.0版本一致了。

int partition3(vector<int> &arr,int left,int right)
{
    if(left>=right)
    {
        return 0;
    }
    int i=left,small=left-1;//遍历数组的指针和小于等于区域的左边界
    int index=left+rand()%(right-left+1);//划分值在排序范围内随机抽取一个数
    swap(arr[index],arr[left]);//将该抽取值与范围内第一个值交换,接下来就和1.0一样了
    int key=arr[left];
    while(i<=right)
    {
        if(arr[i]<=key)
        {
            swap(arr[small+1],arr[i]);
            ++small;
        }
        ++i;
    }
    swap(arr[left],arr[small]);//将划分值放到小于等于区的最后一个位置上
    partition3(arr,0,small-1);
    partition3(arr,small+1,right);

}

int qsort3(vector<int> &arr)
{
    partition3(arr,0,arr.size()-1);
    return 0;
}

注意,如果分为四部分,可以不用交换随机选取的值和数组第一个值。

int partition4(vector<int> &arr,int left,int right)
{
    if(left>=right)
    {
        return 0;
    }
    int i=left,small=left-1;//遍历数组的指针和小于区域的右边界
    int big=right+1;//大于区域的左边界
    int key=arr[left+rand()%(right-left+1)];
    while(i<big)
    {
        if(arr[i]<key)
        {
            swap(arr[small+1],arr[i]);//交换小于区的下个数和该遍历数
            ++small;
            ++i;
        }else if(arr[i]==key)
        {
            ++i;
        }else
        {
            swap(arr[big-1],arr[i]);
            --big;
        }
    }
    partition4(arr,0,small);
    partition4(arr,big,right);
    return 0;
}

int qsort4(vector<int> &arr)
{
    partition4(arr,0,arr.size()-1);
    return 0;
}

这样的话,由于都是随机选取划分值,时间复杂度为O(Nlog N)。
另外,快速排序的空间复杂度为O(log N)。在1.0与2.0版本中,最差情况会变为O(N)。

对数器

最后,还是把对数器代码贴出来

int compare()//对数器
{
    int times=1;
    vector<int> arr1;
    for(;times<=TIME_N;++times)
    {
        for(int i=0;i<LEN;++i)
        {
            arr1.push_back(rand()&1023);
        }
        vector<int> arr2(arr1);
        qsort3(arr1);
        qsort4(arr2);
        //比较两个排序后的数据
        if(arr1!=arr2)
        {
            return times;
        }
    }
    return 0;
}

这里可以用不同的排序方法对比,返回值为0则说明算法基本没有问题。

标签:国旗,right,int,arr,数组,荷兰,small,排序,left
来源: https://blog.csdn.net/m0_65931372/article/details/122233090

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有