您的位置:首页 > 理论基础 > 数据结构算法

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

2018-03-10 19:58 393 查看
前面写了四个简单的排序算法,学习的时候也觉得很好理解,没有难度,可是,到快速排序我差点被打败,还好本宝挺了过来,现在觉得快排及其优化还是相当简单了,可能当时学习的时候不在状态吧!所以,现在的你是否也被快排倒的脑子一团糟,没关系,跟我的思路一起往下走。。。

一.快速排序的基本思想

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,以此达到整个数据变成有序序列。

整个过程可以递归也可以不递归,所以,快速排序可以分为两种,一种是递归的,一种是非递归的。

二.快速排序的三个步骤

(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

我觉得这个人写的超级好,本来我在努力好好写,后来发现了这篇博客,发现根本无法超越,大家如果看到这里,先谢谢你点开了这篇拙略的博客,然后推荐大家根据上面那篇博客学习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐