快速排序
快速排序的原理
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
假如我们需要排序一串数组:
左右指针法:
第一步:
首先先在数组中找到一位基准数也可以称做“key”,通常是数组的(第一位)或(最后一位);
我们先以第一位设为基准数(key);
第二步:
我们需要把选择的基准数移至数组的某个正确的位置:
比如说基准数是3 移动到一个位置,该位置左边都是<=3的数,而该位置右边都是>=3的数;
因为是左右指针法,是从数组两端分别开始寻找,先从右往左找一个小于3的数,再从左往右找一个大于3的数,然后交换他们。在这里可以用两个变量,变量的名称可以为“left”和“right”。在刚开始可以用“left”指向最左边,“right”指向最右边。
在这里需要注意:若选择最左边的数据作为key,则需要 “right” 先走;若选择最右边的数据作为key,则需要 “left” 先走。
所以是 “right”先出发,一步一步向左走(right=right -1),直到遇到一个小于 基准数3的数停下来指向那个数,然后“left”再出发,一步一步向右走(left=left +1),直到遇到一个大于基准数3的数停下来。
然后让“left”所指的数与“right”所指的数进行交换。
到此为止,才是第一次交换的结束。接下来进行第二次交换,以上述过程一样,让“right”先进行向左移动(right=right - 1),找到一个小于基准数3的数,直到找到1,这个时候“left”和“right”所指向的数相同。
每当“left”和“right”相遇时,将所指向的 1 与基准数 3进行交换。
此时将基准数放回原位置,这时“key”的左边都是小于3的数,而“key”的右边的都是大于3的数。这个是后就可以把“key”看作是分界线将这串数组看出两组数组,“key”左边一组,“key”右边为一组。
我们先来处理“key”左边的这组数组吧!
我们可以看到,好巧不巧,我随便输入的一组数组,此时根本不需要处理。
但是如何判断他不需要再进行处理了呢?
——————
当重新处理数组时,也是一样的重新设置好基准数 1 ,“left”指向最左边 1,“right”指向原来“key”的左边一位也就是 2,将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作。
接下来处理原“key”的右边数组。
此时 以上面相同的方法进行处理,先是“right”向左找到小于基准数 6,在“left”向右找到大于基准数 6的数,停下。
找到进行交换。
然后“right”再次寻找,指向4停止,此时“left”和“right”指向同一个数,将其与“key”进行交换。
然后再将“key”当做分界线,将这组数组在进行分别的处理。
按照与上面一样的方法进行操作,最终会将数组成功排序成功:
细心的同学可能已经发现,快速排序的每一轮处理其实就是将这一轮的基准数归位,直到所有的数都归位为止,排序就结束了。接下来用图示的方法来展示完整的过程:
代码实现:
void quick_sort(int *arr, int begin,int end)
{
if (end < begin )
return;
//left 指向最左边
int left = begin ;
//right 指向最右边
int right = end ;
int* key = &arr[begin];
while (left != right)
{
while (arr[right] >= *key && right > left)
{
right--;
}
while (arr[left] <= *key && left < right)
{
left++;
}
if (left < right)
{
swap(&arr[left],&arr[right]);
}
}
swap(&arr[left],key);
quick_sort(arr, begin, left-1);
quick_sort(arr, left+1, end );
}
整体代码:
void swap(int*left,int *right)
{
int tem = *left;
*left = *right;
*right = tem;
}
void quick_sort(int *arr, int begin,int end)
{
if (end < begin )
return;
//left 指向最左边
int left = begin ;
//right 指向最右边
int right = end ;
int* key = &arr[begin];
while (left != right)
{
while (arr[right] >= *key && right > left)
{
right--;
}
while (arr[left] <= *key && left < right)
{
left++;
}
if (left < right)
{
swap(&arr[left],&arr[right]);
}
}
swap(&arr[left],key);
quick_sort(arr, begin, left-1);
quick_sort(arr, left+1, end );
}
int main()
{
int arr[] = {
6,1,2,7,9 ,10,3,4,5,0,8 };
int sz = sizeof arr / sizeof arr[1];
int begin = 0;
int end = sz-1;
quick_sort(arr, begin,end);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
快速排序使用分治法策略来把一个串行分为两个子串行。
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。