排序算法之快速排序

1. 基本思想

任取待排序元素序列中的某个元素作为基准值,按照该排序码将待排序序列分割为两个子序列,其左子序列中所有元素均小于基准值,其右子序列中所有元素均大于基准值,然后将其左右子序列按照上述过程重复操作,直到所有元素均排列在相应位置上为止。

操作的基本过程:若规定基准值key始终为待排序序列的最后一个元素,定义两个索引left和right,left将在待排序序列中从前往后找到第一个大于key的元素,right将在待排序序列中从后往前找到第一个小于key的元素,均找到之后进行交换,如此重复操作直到left与right重合时与基准值key交换即可。

2. 举例说明

待排序数组为array[7]={9,5,2,7,8,4,6},size=7

(1)待排序区间为[0,size),待排序数组array[7]={9,5,2,7,8,4,6},基准值key=6,left指向待排序区间的开始,即下标为0的元素,right指向待排序区间的末尾,即下标为size-1=6的元素。

开始移动left和right(移动的条件为left<right):

①array[left]=array[0]=9>key=6,故找到了一个大于基准值的元素;array[right]=array[6]=6!<key=6,故right--继续寻找,array[right]=array[5]=4<key=6,故找到一个小于基准值的元素。将这两个元素进行交换后数组变为array[7]={4,5,2,7,8,9,6}

②array[left++]=array[1]=5<key=6,故left++继续寻找,array[left]=array[2]=2<key=6,故left++继续寻找,array[left]=array[3]=7>key=6,故找到了一个大于基准值的元素;array[right--]=array[4]=8>key=6,故right--继续寻找,由于left=3!<right=3,left与right重合,故将left指向的元素与基准值交换,数组变为array[7]={4,5,2,6,8,9,7}

此时可以看到基准值key=6的左边均为小于它的元素,右边均为大于它的元素!

待排序区间需要进行划分再排序,划分原则为上一次的基准值的下标的左边为一个待排序区间、右边为一个待排序区间,基准值key=6下标为3。故左边待排序区间为[0,3),故待排序数组为array={4,5,2},右边待排序区间为[4,size),故待排序数组array={8,9,7}

(2)待排序区间为[0,3),待排序数组array={4,5,2},基准值key=2,left指向待排序区间的开始,即下标为0的元素,right指向待排序区间的末尾,即下标为3-1=2的元素。

h开始移动left和right(移动的条件为left<right):

①array[left]=array[0]=4>key=2,故找到了一个大于基准值的元素;array[right]=array[2]!<key=2,故right--继续寻找,array[right]=array[1]=5!<key=2,故right--继续寻找,由于left=0!<right=0,left与right重合,故将left指向的元素与基准值交换,数组变为array={2,5,4}

此时可以看到基准值key=2的左边均为小于它的元素,右边均为大于它的元素!

待排序区间需要再次进行划分再排序,划分原则为上一次的基准值的下标的左边为一个待排序区间、右边为一个待排序区间,基准值key=2下标为0。故左边待排序区间为[0,0),右边待排序区间为[1,3)

(3)待排序区间为[0,0)不需要排序,待排序数组array为空

(4)待排序区间为[1,3),待排序数组array={2,5,4},基准值key=4,left指向待排序区间的开始,即下标为1的元素,right指向待排序区间的末尾,即下标为3-1=2的元素。

开始移动left和right(移动的条件为left<right):

①array[left]=array[1]=5>key=4故找到了大于基准值的元素;array[right]=array[2]=4!<key=4,故right--继续寻找,由于left=1!<right=1,left与right重合,故将left指向的元素与基准值交换,数组变为array={2,4,5}

此时可以看到基准值key=4的左边均为小于它的元素,右边均为大于它的元素!

(5)待排序区间为[4,size),待排序数组array={8,9,7},基准值key=7,left指向待排序区间的开始,即下标为4的元素,right指向待排序区间的末尾,即下标为size-1=6的元素。

①array[left]=array[4]=8>key=7,故找到了一个大于基准值的元素;array[right]=array[6]=7!<key7,故right--继续寻找,array[right]=array[5]=9!<key=7,故right--继续寻找,由于left=4!<right=4,left与right重合,故将left指向的元素与基准值交换,数组变为array={7,9,8}

此时可以看到基准值key=7的左边均为小于它的元素,右边均为大于它的元素!

待排序区间需要再次进行划分再排序,划分原则为上一次的基准值的下标的左边为一个待排序区间、右边为一个待排序区间,基准值key=7下标为4。故左边待排序区间为[4,4),故待排序数组为空,右边待排序区间为[5,size),故待排序数组array={9,8}

(6)待排序区间为[4,4)不需要排序,待排序数组array为空

(7)待排序区间为[5,size),基准值key=array[size-1]=array[6]=8,left指向待排序区间的开始,即下标为5的元素,right指向待排序区间的末尾,即下标为7-1=6的元素。待排序数组array={9,8}

开始移动left和right(移动的条件为left<right):

①array[left]=array[5]=9>key=8,故找到了大于基准值的元素;array[right]=array[6]=8!<key=8,故right--继续寻找,由于left=5!<right=5,left与right重合,故将left与基准值交换,数组变为array={8,9}

此时可以看到基准值key=8的左边均为小于它的元素,右边均为大于它的元素!

至此,整个排序结束,数组变为array[7]={2,4,5,7,8,9}

通过以上文字分析,即整个数组的排序将被依次递归分为两部分进行排序,直到分为的数组中元素个数为1表示不再分组进行排序。画图总结上述步骤:


3. 递归版本实现快速排序

void QuickSort(int array[],int64_t size)
{
    //当size<=1是直接return
    if(size<=1)
        return;                                                                                                            
    //定义一个函数_QuickSort用于递归处理数组
    _QuickSort(array,0,size);
}
void _QuickSort(int array[],int64_t beg,int64_t end)
{
    //当数组元素只有一个时,递归结束
    if(end-beg<=1)
        return;
    //定义Partion函数作用是对[beg,end)区间进行排序
    //排序成一个以某基准值为中心,左侧小于基准值右侧大于基准值
    //返回值表示的含义是排序后基准值所在的下标
    int64_t mid=Partion(array,beg,end);
    //接着继续对区间[beg,mid)进行快速排序
    _QuickSort(array,beg,mid);
    //继续对区间[mid+1,end)进行快速排序
    _QuickSort(array,mid+1,end);
}
int64_t Partion(int array[],int64_t beg,int64_t end)
{
    //定义好区间的范围
    int64_t left=beg;
    int64_t right=end-1;
    //取最后一个元素为基准值                                                                                               
    int key=array[end-1];
    //循环将元素放在合适的位置
    while(left<right)
    {
        //(1)从左向右寻找大于key的元素
        while(left<right&&array[left]<key)
        {
            left++;
        }
        //(2)从右向左寻找小于key的元素
        while(left<right&&array[right]>=key)
        {
            right--;
        }
        //找到之后进行交换
        if(left<right)
        {
            Swap(&array[left],&array[right]);
        }
    }
    //退出while循环后,将left、right同时指向的元素与key进行交换
    Swap(&array[left],&array[end-1]);
    //返回基准值下标位置
    return left;
}

4. 非递归版本实现快速排序

在递归实现快排中由于函数栈帧为其提供了栈结构,故在非递归版本中需要我们自己创建栈并利用栈结构实现。

void QuickSortByLoop(int array[],int64_t size)
{
    //当size<=1时直接return
    if(size<=1)
        return;
    //创建栈
    SeqStack stack;
    SeqStackInit(&stack);
    int64_t beg=0;
    int64_t end=size;
    SeqStackPush(&stack,beg);
    SeqStackPush(&stack,end);
    //循环条件为栈中含有元素                                                                                               
    while(stack.size>0)
    {
        SeqStackTop(&stack,&end);
        SeqStackPop(&stack);
        SeqStackTop(&stack,&beg);
        SeqStackPop(&stack);
        int64_t mid=Partion(array,beg,end);
        SeqStackPush(&stack,beg);
        SeqStackPush(&stack,mid);
        SeqStackPush(&stack,mid+1);
        SeqStackPush(&stack,end);
    }   
}   



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值