思想
快速排序有分治的思想
首先一块连续的空间为q 左端点为l 右端点为r
- 确定分界点
分界点可以取区间上的随机一点 例如左端点,右端点,中点
记录此时的值 例如中点就是q[ (l+r) / 2]
- 调整范围
将将左端点的左侧都放置成小于等于分界点的数
将右端点的右侧都放置成大于等于分界点的数
(如果要是降序排序将上述过程反转 即左侧都大于等于分界点 右侧都小于等于分界点)
- 递归处理左右两端
递归调用 处理分界点左侧的数据和右侧的数据
代码实现
双指针
思路
- 定义两个变量 i j 标记左端和右端
- 遍历分界点左侧的元素 当遇到大于等于分界点的元素时停下
- 遍历分界点右侧的元素 当遇到小于分界点的元素时停下
- 此时两个标记变量分别指向不属于左侧和右侧的元素(即左侧大于分界点,右侧小于分界点的元素)
- 交换两个标记点标记的元素 交换结束后继续遍历
void quick_sort(int q[], int l, int r)
{
if (l >= r)
{
return;
}
int i = l - 1;
int j = r + 1;
//这里l-1和r+1的原因是在下面的循环中是do-while结构
//所以要在左侧-1 右侧+1
int k = q[(l + r) / 2];
//标记点
while (i < j)//当左右标记点相遇 说明已经全部调整结束循环
{
do {
i++;
} while (q[i] < k);//如果大于等于q[k]则停止,开始右侧循环
do {
j--;
} while (q[j] > k);//找到左侧小于等于q[k]的元素
if (i < j)//循环结束 两个标记点处的元素交换 各取所需
swap(q[i], q[j]);
}
quick_sort(q, l, j);//递归处理左侧
quick_sort(q, j + 1, r);//递归处理右侧
}
时间复杂度
先说结论
在最糟情况下,其运行时间为O(n2)。。在平均情况下,快速排序的运行时间为O(nlogn)。
最坏情况
如果你选择的数组本身就是有序且你选择第一个元素为分界点
此时你进行一次大循环后 发现分界点左侧没有元素 因为你选择的分界点就是最小的元素
在后面递归处理两侧元素时 相当于再次进行了一个(n-1)个元素的递归
即这一段的时间复杂度就是o(n);
因为每次都要遍历一遍 都要遍历一遍所有元素 就是o(n)
在最糟情况下 该算法的运行时间为O(n) * O(n) = O(n2)。
平均情况
如果选用中间元素为分界点
则每次大循环后分界点两侧的元素数量基本相同
这时在递归就将数据拆成两半 如此拆分 类似于二分查找的折半
时间复杂是O(logn)
因为每次都要遍历一遍 都要遍历一遍所有元素 就是o(n)
在平均情况下 该算法的运行时间为O(n) * O(logn)= O(nlogn)。