一,快速排序问题的提出?
当我们想到排序,并且我们只思考基础的语法+最暴力的手法,就是使用循环遍历的方法来寻找最合适的插入的位置,需要我们注意的是这是一个 O(n3) 复杂度的算法,这样的话排序的数据范围(数据的多少)就会相对的比较小,在这个时候我们就要想办法去优化这个算法,从而使这个排序算法在更广泛的数据范围内能实现.
同时, 我们想到了桶排序,但是桶排序在数据范围(数据的大小)上受到极大的影响,我们无法实现在数据值比较大的情况下完成这个排序操作(一旦数据范围达到了107以上就可能TLE)
当然我们还有另一种办法,简单的冒泡排序思想,首先把一切的数值都存到数组之中,我们使用线性代数中逆序对的相关思想
设置指针进行两次的循环遍历 , 如果后面的值比前面的大我们就进行一次交换的操作
虽然冒泡排序时间复杂度降到了O(n2) ,但是对于我们巨大的数据范围这个时间复杂度还是不够看…这个时候我们想有没有一种算法能够完美的适应各种的排序操作。因此,快速排序出现了。
二,快速排序的算法思想
快速排序在本质上就是一种递归思想(和一些简单的循环组成),当我们遇到一个数量为 n 的数组需要进行排序的时候,我们首先想到的就是把这个数组存起来,存起来之后我们应该怎么办呢?
或许我们可以设置一个中间值,把左右两边分成两个数组(A数组B数组) , A数组内的一切元素都是小于中间值mid ,B数组中的元素都是大于中间值mid,这样的话,在mid左右两边的数据就被完全的处理好了,左边的数据无论怎么变换都一定在左边这一组数据中。
完成这一步骤之后我们就处理完成了这个大的数组的一部分排序操作,可以看作这两个数组(我们在代码实现中会把Mid并到前后数组其中一个中,故只有两个数组)之间存在一种排序关系。然后我们再进行对下面分出来的小数组的排序操作,进行几次递归操作之后 ,我们分出的两个数组之中,每个只有一个元素。
我们知道左右数组之间是有顺序关系的,那就可以在n(数组元素) == 1 时 直接确定这个顺序就是最终的顺序,我们在赋值完成之后直接跳出循环即可。
三,快速排序的代码实现
首先,快速排序应该处理的第一个问题就是对把数组中的内容分成两个数组,以mid划分,首先mid的选取一定是数组内部的,我们选用的mid建议选用用随机数产生的的值,但是为了方便理解,我们每次选用数组最中间的值最为mid值。下面我们来完成比较大小的操作
#include <iostream>
using namespace std ;
void quick_sort(int l , int r , int q[])
//q[]是要进行排序的数组 ,l 是他最小的下标 , r是他最大的下标
{
int i = l - 1 , j = r + 1 , x = q[l + r >> 1] ;
// 我们下面要使用到 do while 循环 ,我们把指针都扩大1 ,x取q的中值,l + r >> 1 为位运算 ,相当于(l + r) / 2
while ( i < j )
{
do i ++ ; while(a[i] < x );
do j -- ; while(a[j] > x );
//寻找前面比x大的和后面比x小的进行交换
if(i < j )
{
swap(a[i],a[j]);
}
}
完成之后,我们就要思考下面的递归操作,我们应当怎么做,根据前文的检索我们知道了mid数组是被储存到了B数组里面,我们要思考以下把剩下的两个数组到底从那里分来最合适,首先我们可以模拟一下这个操作。(上面一行为初始数组,同意一色为)
这个是其中一个特殊的例子,其中i == j ,在一般情况下 i = j + 1 ;当然这种情况是因为中间数组是两边都可以取到的情况下产生的。本文仅提醒读者这个情况下 i != j + 1 ,但是在操作的时候还是可以看作i = j + 1 ,避免进入思维黑洞,这个部分可以直接跳过我们在思考明白这个问题之后可以简单的发现在下标[l , j]和[j +1 , r]这个里面他被分成了两个数组.
quick_sort(l , j , q[]);
quick_sort(j + 1 , r , q[]);
处理完成这两部操作之后我们看快速排序的整个的算法
#include <iostream>
using namespace std ;
const int N = 100010 ;
void quick_sort (int l ,int r ,int q[])
{
if(l >= r ) return ;
int i = l - 1 , j = r + 1 ,x = q[(r + l )>> 1];
while (i < j )
{
do i ++ ; while (q[i] < x );
do j -- ; while (q[j] > x );
if(i < j )
swap(q[i],q[j]);
}
quick_sort(l , j , q);
quick_sort(j + 1 , r , q) ;
}
int main ()
{
int n ;
cin >> n;
int q[N] ;
for(int i = 0 ; i < n ; i ++ )
cin >> q[i] ;
quick_sort(0,n - 1,q);
for(int i = 0 ; i < n ; i ++ )
cout << q[i] << " " ;
return 0 ;
}
四,快速排序的算法思考和分析
1.算法复杂度的分析
快速排序算法最高的时间复杂度是O(n2),最低时间复杂度是O(nlog2n)
最复杂的时间出现在每次都遍历到最小值或者最大值当作算法的中间值,我们第二次遍历会进行n次
最简单的时间复杂度出现在每次完美的分开左右,遍历的图形就像一个完全二叉树;
2,mid中间数的思考
最优解
整个数可以使用 ctime头文件下的random()随机数组来随机取,防止数据怪胎hank
普遍性
整个中间数取任意数组内的值都可以