算法笔记六:使用分治策略执行快速排序

算法思想

//采用分治策略,将原数组,分解为三部分:左边的 + 已最终定位的节点位置 + 右边的


//分解:取任意一个位置的数,将数组划分为3部分:左边+该数的最终位置+右边,继续对左右两边递归执行分解操作


//解决:求出给定位置数的最终位置,即保证左边都比它小,右边都比它大



//合并:在分解的过程中,就已经将元素的最终位置给计算好了,不需要任何操作

分解—>解决—>合并

在分解的步骤中,什么条件下是阻止其继续分解的终止阀呢?:

分解的只有一个元素了

解决:

找出给定元素应该在该数组出现的最终位置,这里只要做一次数组的遍历就可以计算出来了,具体实现方式请参见实现代码的注释部分


合并:

不需要执行任何动作,解决的过程中就已经将要解决的数子的位置给计算好了


最好情况

O(n*log2n)


最坏情况

O(n^2)


下面我们来分析下这里的时间代价:


假设数组是一个已经排好序的数组,那么在分解的过程中,每个元素的初始化位置就是其最终位置,则如果每次选择数组中的第一个元素执行分解的话,那么就会导致每次都分解成只有右边一侧的子数组,而由于每次解决的代价都是数组大小n,则总的代价为:n + (n-1) + (n-2) + … + 1 = O(n^2)


如果每次分解都能将数组分割为两部分(假定为平均的2个部分,有利于分析),则有:

T(n) = T(n/2) + T(n/2) + O(n)

使用带入法:

T(n) = T(n/4) + T(n/4)+ T(n/4)+ T(n/4) + O(n)  + O(n/2)

使用递归树的分析不难得出:T(n) =  O(n*lgn)


这里需要理解的是,即使是每次分解后的数组不是均衡的,比如左边是9/10,右边是1/10,依然不影响这个这个递归树的高度,所以无论是否均衡,其代价都是O(n*lgn),而如果每次分解后只产生一颗子树,那么树的高度将大大增加,导致的时间代价就是上面分析的最坏情况n^2


空间代价上,只是两个元素交换时需要开辟一个元素的额外空间,基本忽略不计!


算法实现

//
//  QuickSort.h
//  p1
//
//  Created by MinerGuo on 14-10-17.
//  Copyright (c) 2014年 MinerGuo. All rights reserved.
//

#ifndef __p1__QuickSort__
#define __p1__QuickSort__

#include <stdio.h>


//采用分治策略,将原数组,分解为三部分:左边的 + 已最终定位的节点位置 + 右边的

//分解:取任意一个位置的数,将数组划分为3部分:左边+该数的最终位置+右边,继续对左右两边递归执行分解操作

//解决:求出给定位置数的最终位置,即保证左边都比它小,右边都比它大

//合并:在分解的过程中,就已经将元素的最终位置给计算好了,不需要任何操作
class QuickSort {
    void changeTwoItems(int * data,int i,int j){
        int tmp = *(data + i);
        *(data + i) = *(data + j);
        *(data + j) = tmp;
    }
    
public:
    
    //分解
    void split(int * data,int left,int right){
        //只有一个元素时终止
        if(left >= right){
            return;
        }
        //这里选取参考数组的位置是可以随机的
        int referenceDataPosition = left;
        //解决
        int finalPosition = solve(data,left,right,referenceDataPosition);
        //fianlPosition 元素已经定位完毕
        //分解左半部分
        split(data,left,finalPosition -1);
        //分解右半部分
        split(data,finalPosition + 1,right);
    }

    //给定数组的区间,以及要找出数组最终位置的那个数据
    //返回该数据的最终位置,
    //这里的方法是:先从后往前找,发现第一个比它小的,调换,从前往后找,这样保证了后面都比他大
    //然后从前往后找,找到第一个大于等于它的,调换,再从后往前找,直到左右相等
    int solve(int * data,int left,int right,int referenceDataPosition){
        int finalDataPosition = referenceDataPosition;
        int referencDataValue = *(data + referenceDataPosition);
        
        int lp = left,rp=right,direction = -1;
        while(lp < rp){
            if(direction == -1){
                //从后往前
                for(;rp>=lp;rp--){
                    if(*(data + rp) < referencDataValue){
                        changeTwoItems(data,rp,finalDataPosition);
                        finalDataPosition = rp;
                        direction = 1;
                        break;
                    }
                }
            }else{
                //从前往后
                for(;lp<=rp;lp++){
                    if(*(data + lp) >= referencDataValue){
                        changeTwoItems(data,lp,finalDataPosition);
                        finalDataPosition = lp;
                        direction = -1;
                        break;
                    }
                }
            }
        }
        
        return finalDataPosition;
    }
    
    
    //排序入口
    void do_sorting(int * data,int size){
        split(data, 0, size - 1);
    }
    
};

#endif /* defined(__p1__QuickSort__) */



算法总结

由于一般待排序的记录都是无序的,所以该算法很少会出现最坏情况,又因为其无论是在时间代价上和空间代价上,其解都是最优的,所以其广泛的运用于各种排序算法中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值