介绍
1.快速排序属于交换排序(如果发现元素次序相反,则交换元素位置),而交换排序是属于内排序一种,内排序即所有数据都存在内存中,与外排序不一样,这个后续文章再说。
2.快速排序采用分而治之的思想,每次找到一个元素,将所有元素与之对比,将小于该元素的元素放在左边,大于则放在右边(方向可以不同,这里只是举个例子),这样再从左边的这堆元素中,按照刚刚同样的方法操作,右边也同样,就这样一直递归下去,这样就能每次固定住一个数的位置,当分不下去的时候,所有元素就变成有序的了
分析
假设有一个数组[6, 1, 2, 7, 9, 3, 4, 5,10,8],我们来看看快速排序如何实现这一过程,我们选择始终第一个元素作为基准数:
[6, 1, 2, 7, 9, 3, 4, 5,10,8]
将元素6归位
[5, 1, 2, 4, 3, 6, 9, 7, 10, 8]
元素6左边的数组序列,将元素5归位
[3, 1, 2, 4, 5]
元素5左边的数组,将元素3归位
[2, 1, 3, 4]
元素3左边的数组,将元素2归位
[1, 2]
这里采用实际上递归(实际上这一步返回后需要归位元素3右边的元素,但是这里只有一个4,因此不会做任何交换,如果不明白,递归需要了解一下,大兄弟),这样,将元素6左边的元素全部归位,即
[1, 2, 3, 4, 5, 6, 9, 7, 10, 8]
接下来对元素6右边的元素进行递归归位,这样最后会获得排好序的数组
代码
接下来就上代码,这里有个问题其实就是基准值得选定,我们这里每次选择第一个作为基准值
public class QuickSort {
public void quickSort(int[] nums, int start, int end) {
// 创建两个游标,i,j,初始值分别数组开头下标值,数组结尾下标值
int i = start;
int j = end;
if (start < end) {
// 保存基准值
int temp = nums[i];
// 向数组中间扫描,判断是否全部扫描完毕,没有就继续循环
while (i < j) {
// 这里的意思就是当nums[j]小于基准值时,将nums[i] 和nums[j]交换,否则减小游标,
//这里稍微难以理解,其实就是将小于基准值得元素放到左边,
//由于基准值被保存,所以覆盖基准值不会导致数据丢失
while (i < j && nums[j] >= temp) j--;
nums[i] = nums[j];
// 将大于基准值的元素放到右边,若小于等于基准值则将游标指向下一个,继续判断
while (i < j && nums[i] <= temp) i++;
nums[j] = nums[i];
}
// 最后将基准值归位,这时基准值左边是小于基准的,右边是大于基准的
nums[i] = temp;
// 对基准值左边的数组进行同样操作
quickSort(nums, start, i - 1);
// 对基准值右的数组进行同样操作
quickSort(nums, i + 1, end);
}
}
}
算法分析
1.假设每次基准值都被排到中间,这时快速排序的时间复杂度最低,为
2.假设选择了以下情况下最大或者最小的元素作为基准值,会出现最差的时间复杂度
1)数组已经是正序(same order)排过序的。
2)数组已经是倒序排过序的。
3)所有的元素都相同(1、2的特殊情况)