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

来源:互联网 发布:当前非涉密网络是 编辑:程序博客网 时间:2024/06/06 01:48

算法思想

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


//分解:取任意一个位置的数,将数组划分为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__) */



算法总结

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

0 0
原创粉丝点击