您的位置:首页 > 其它

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

2014-10-17 17:41 232 查看
算法思想
//采用分治策略,将原数组,分解为三部分:左边的 + 已最终定位的节点位置 + 右边的

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


算法总结
由于一般待排序的记录都是无序的,所以该算法很少会出现最坏情况,又因为其无论是在时间代价上和空间代价上,其解都是最优的,所以其广泛的运用于各种排序算法中
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: