数据结构之排序2(快速排序及其优化)

前面写了四个简单的排序算法,学习的时候也觉得很好理解,没有难度,可是,到快速排序我差点被打败,还好本宝挺了过来,现在觉得快排及其优化还是相当简单了,可能当时学习的时候不在状态吧!所以,现在的你是否也被快排倒的脑子一团糟,没关系,跟我的思路一起往下走。。。
一.快速排序的基本思想
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,以此达到整个数据变成有序序列。
整个过程可以递归也可以不递归,所以,快速排序可以分为两种,一种是递归的,一种是非递归的。
二.快速排序的三个步骤
(1)选择基准值:在待排序数中,按照某种方式挑出一个元素,作为 “基准”。
(2)分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准值左边的元素都比该基准值小,在基准值右边的元素都比基准值大
(3)在基准值的左右两边不断重复(1)(2)步骤,直到整个数列有序为止。
三.选择基准值
根据快速排序的三个步骤你会有所疑问,就是到底基准值是怎么找到的,什么样的值才能称为基准值。。?
当然,基准值不是我想让它是它就是的,这个必须由我们程序根据不同的序列来确定,这里,我将给出三种基准值的选择方法。
1.固定位置
固定取序列的第一个或最后一个值为基准值,每次找到基准值的正确位置,不断重复完成排序。

这里写图片描述
如图所示,我给出一个序列,给出两个指向变量,分别指向arr数组的0号和len - 1 位置,再给出一个临时量tmp方便我们交换数据。固定位置选择基准是选择第一个或者最后一个数作为基准,这里我演示选择第一个数的情况。
这里写图片描述

经过这样一次,基准2已经在自己正确的位置,也就是说2的左边都小于2,右边的数都大于2。
我用代码实现一下固定选择基准的过程:

int Partion(int *arr, int low, int high)//快速排序寻找基准值
{
    int tmp=arr[low];//固定第一个值
    while (low < high)//跳出while的条件是当low++,high--到相遇或者low>high
    {
        while (low < high && arr[high] >=tmp)
        //先用后边值和tmp比较,一直执行high--直到找到比tmp小的值,找到之后退出while循环
        {
            high--;
        }
        if (low >= high)//退出while循环说明找到了比tmp小的值,这时判断一下low和high的位置,如果low<high说明这个值是在基准的右边比基准小,所以执行else
        {
            break;
        }
        else
        {
            arr[low] = arr[high];//把这时high的值放在空的low里
        }
        while (low < high && arr[low] <= tmp)
        {
            low++;
        }
        if (low >= high)
        {
            break;
        }
        else
        {
            arr[high] = arr[low];
        }
    }
    arr[low] = tmp;
    return low;
}

2.随机找基准
我们想,在待排序的部分已经部分有序的情况下,还这样固定位置选择基准会多排几次,多做几次无用功,导致效率降低,所以就引进了随机找基准的方法。
我们看下代码实现:

/*随机选择枢轴的位置,区间在low和high之间*/  
int SelectPivotRandom(int arr[],int low,int high)  
{  
    //产生枢轴的位置  
    srand((unsigned)time(NULL));  
    int pivotPos = rand()%(high - low) + low;  

    //把枢轴位置的元素和low位置元素互换,此时可以和普通的快排一样调用划分函数  
    swap(arr[pivotPos],arr[low]);  
    return arr[low];  
}  

但是随即取基准值法也有一个缺点
3.三分取中法
与一般快速排序不同,三分取中法会选取最左值、中间值和最右值三个值中的中间值作为基准值,这样选取的基准值会降低最坏情况发生的几率。
这里写图片描述

void Swap(int *arr,int start,int end)//交换start和end
{
    int tmp;
    tmp = arr[start];
    arr[start] = arr[end];
    arr[end] = tmp;
}

void Median_Of_Three(int *arr,int start,int mid,int end)//三分取中法
{
    //交换的目的是让arr[mid]<arr[start]<arr[end]
    if(arr[mid] > arr[end])
    {
        Swap(arr,mid,end);
    }
    if(arr[mid] > arr[start])
    {
        Swap(arr,mid,start);
    }
    if(arr[start] > arr[end])
    {
        Swap(arr,start,end);
    }
}

经过这样交换之后,三个值中的中间值就会在start位置,再利用快速排序。
四.快速排序的优化
1.把和基准值相等的数靠近基准值
优化的时候我们这样想,把和基准值相等的数字都交换到基准值旁边,这样如果比较时和基准值相等就比较下一个,少去了很多比较的步骤。

void Focus_Nums(int *arr,int par,int *left,int *right,int start,int end)
{
    int LeftIndex = par - 1;
    int RightIndex = par + 1;
    for(int i = par - 1;i >=start;i--)
    {
        if(arr[i] == arr[par])
        {
            if( i != LeftIndex)
            {
                Swap(arr,i,LeftIndex);
                LeftIndex--;
            }
            else
            {
                LeftIndex--;
            }
        }
    }
    *left = LeftIndex;

    for(int j = par + 1;j<=end;j++)
    {
        if(arr[j] == arr[par])
        {
            if(j != RightIndex)
            {
                Swap(arr,RightIndex,j);
                RightIndex++;
            }
            else
            {
                RightIndex++;
            }
        }
    }

    *right = RightIndex;
}

四.题外话
http://blog.csdn.net/insistgogo/article/details/7785038
我觉得这个人写的超级好,本来我在努力好好写,后来发现了这篇博客,发现根本无法超越,大家如果看到这里,先谢谢你点开了这篇拙略的博客,然后推荐大家根据上面那篇博客学习。

猜你喜欢

转载自blog.csdn.net/qq_39110766/article/details/79185877
今日推荐