快速排序
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod,即D&C)。
基本思想:
快速排序使用分治的思想,从待排序序列中选取一个记录的关键字为pivot(基准值),通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字不大于pivot,另一部分记录的关键字不小于pivot,之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
快速排序算法的基本步骤为(默认升序):
- 选择关键字:从待排序序列中,按照某种方式选出一个元素作为 pivot 作为关键字(也叫基准)。
- 置 pivot 分割序列:通过某种方式将关键字置于一个特殊的位置,把序列分成两个子序列。此时,在关键字 pivot 左侧的元素小于或等于 pivot,右侧的元素大于 pivot(这个过程称为一趟快速排序)。
- 对分割后的子序列按上述原则进行分割,直到所有子序列为空或者只有一个元素。此时,整个快速排序完成。
最优的分割方法:当每次选择恰当的基准值后,可使得分割后两边的长度尽可能相等时,此时的算法复杂度可以由O( n 2 n^2 n2)缩小到O(nlogn). 由此可见,基准值的选取直接影响了算法的速度,这是快排算法中十分重要的一环。
BUG版本
void quick_sort(int arr[], int size, int left, int right)
{
int pivot = set_pivot_1(arr,size,left,right);
int i = left;//第一次找到比基准数大的元素索引
int j = right;;//第一次找到比基准数小的元素索引
//基线条件
if (left >= right)
//left=right说明该区块只含有一个元素,left>right说明该区块为空
{
return;
}
//递归条件
while (i < j)//实行分区
{
while (arr[j] >= pivot && i < j)
{
j--;
}
while (arr[i] <= pivot && i < j)
{
i++;
}
if (i < j)
{
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i], &arr[left]);
quick_sort(arr, size, left, i - 1);
quick_sort(arr, size, i + 1, right);
}
2020.4.21日 BUG:
对于头尾基准值法,代码可行。
但是对于随机基准值法和三数求中基准值法,代码出现了bug。
(会出现莫名其妙的数据???
int set_pivot_1(int arr[], int size,int left,int right)
{
//头尾基准值法
return arr[left];
}
int set_pivot_2(int arr[], int size,int left,int right)
{
//三数求中基准值法
int mid = left + (right - left)/2;
if (arr[left] > arr[mid])
{
swap(&arr[left], &arr[mid]);
}
if (arr[left] > arr[right])
{
swap(&arr[left], &arr[right]);
}
if (arr[mid] > arr[right])
{
swap(&arr[mid], &arr[right]);
}
swap(&arr[left], &arr[mid]);
return arr[left];
}
int set_pivot_3(int arr[], int size,int left,int right)
{
//随机基准值法
srand((unsigned)time(NULL));
int key = rand() % (right - left) + left;
swap(&arr[key], &arr[left]);
return arr[left];
}
我是BUG收割机(自嗨
请允许吃完晚饭后的我小小的自恋一下——我是debug王!!
对于三数取中基准数的算法错误,终于找到原因啦
问题出在主函数上:
分析:倘若是如此,先取基准值,在判断基线条件的话。因为有可能出现
left > right的情况,也就是该区块为空的时候。
此时,按照三数取中基准数的算法:
int mid = left + (right - left)/2;
此时的mid可能为负值,这样就会造成对内存的非法访问,程序自然就会报错了,like this:
对于随机中基准数的算法错误,原因也逐渐浮现出来:
int key = rand() % (right - left) + left;
这里要考虑 left=right,所属区块为空的情况下,right - left = 0 ,而数字%0的运算是无定义的,所以自然会出错啦~
正确版本
所以正确版本应该是先判断基准条件,在来确定基准值:
void quick_sort(int arr[], int size, int left, int right)
{
//基线条件
if (left >= right)
//left=right说明该区块只含有一个元素,left>right说明该区块为空
{
return;
}
int pivot = set_pivot_1(arr,size,left,right);//头尾基准值法
int i = left;//第一次找到比基准数大的元素索引
int j = right;;//第一次找到比基准数小的元素索引
//递归条件
while (i < j)//实行分区
{
while (arr[j] >= pivot && i < j)
{
j--;
}
while (arr[i] <= pivot && i < j)
{
i++;
}
if (i < j)
{
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i], &arr[left]);
quick_sort(arr, size, left, i - 1);
quick_sort(arr, size, i + 1, right);
}
大功告成!