快速排序(Quick Sort)
1.基本思想
选定一个基准数,通过一趟排序将要排序的数据分割成独立的两部分,其中左部分的数据比基准数小,右部分的数据比基准数大,然后再按此方法先选取基准数,再对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
简单来讲就是:每次排序时选取一个基准数,将小于等于基准数的数全部放在基准数的左边,将大于等于基准点的数全部放在基准数的右边,待基准数归位,再继续拆分成两部分继续执行上述操作。
2.算法设计
现在对"6 1 2 7 9 3 4 5 10 8"这10个数进行从小到大排序
- 首先进行选取基准数,为了方便先选取第一个数6作为基准数。接下来,目的是把这个序列中所有比基准数小的放在基准数左边,所有比基准数大的放在基准数右边。
- 那么我们设置两个指针变量分别为i、j,i指向序列的最左边,j指向序列的最右边,我们把i和j称为哨兵i和哨兵j。然后从两端开始"探测"。先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换它们。
- 因为此处设置的基准数时最左边的数,所以需要让哨兵j先出发(先想想为什么)。哨兵j一步一步向左移动(j–),直到找到第一个小于基准数6的数,就停止。接着哨兵i出动,一步一步向右移动(i++),直到找到第一个大于基准数6的数就停下,这个过程如下:
现在交换哨兵i和哨兵j所指的元素的值,如下:
- 第一次交换结束。接下来哨兵j继续向左移动,寻找比基准数6小的数,找到了4,就停止。而哨兵i也继续向右,找到了9,停止。
进行交换操作。
- 第二次交换结束,"探测"继续。哨兵j继续向左移动,发现了3满足要求就停下来。哨兵i也继续向右移动,此时发现哨兵i和哨兵j相遇,它们都移动到3面前。说明此时"探测"结束(已经不需要再继续移动,因为哨兵i已经处理完左部分,哨兵j已经处理完右部分),将基准数6和3交换(这一过程可以回答为什么要哨兵j先走,试试看如果是哨兵i先走会出现什么结果)。如下:
第一轮"探测"结束。此时以6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。回顾第一轮的过程,哨兵j的使命就是要找小于基准数的数,哨兵i的使命是找出大于基准数的数,直到哨兵i和哨兵j碰头为止。
现在基准数6已经归位。以6为分界点把该序列分割成两部分,左边的序列是"3 1 2 5 4",右边的序列是"9 7 10 8"。接下来还需要继续处理这两个序列,目前它们的顺序还是很混乱。按照刚刚的方法,比如先处理左序列。
- 选取第一个数3作为基准数。现在将这个序列以3为基准数进行调整,使得3左边的数都小于3,3右边的数都大于3。得出的结果序列是:2 1 3 5 4。
- 现在3已经归位。接下来又以3为分界点,把该序列拆分两部分,分别处理3左边的序列"2 1"和3右边的序列"5 4"。对于"2 1"以2为基准数进行调整,处理完毕之后的序列为"1 2",到此2已经归位。而序列"1"只有一个数,就不用进行任何处理。到此对于序列"2 1"已经全部处理完毕,得到的序列是"1 2"。序列"5 4"的处理也仿照此方法,最后得到的序列:1 2 3 4 5 6 9 7 10 8
- 对于序列"9 7 10 8"也模拟刚才的过程,直到不可拆分出新的子序列为止。最终得到这样的序列:1 2 3 4 5 6 7 8 9 10。
到此,排序已经全部完毕。快速排序的每一轮处理其实就是将这一轮的基准数归位,直到所有的数都归位为止。
3.代码
public class QuickSort {
static void quicksort(int[] a, int left, int right) {
int i, j, t, temp; // temp来存储基准数
if(left > right) {
return ;
}
// 选择基准数
temp = a[left];
i = left;
j = right;
// i和j还没有相遇
while(i != j) {
// 必须先从右开始,即j开始出发,寻找一个小于等于基准数的数
while(a[j] >= temp && j > i) {
j--;
}
// 然后从左出发,寻找一个大于等于基准数的数
while(a[i] <= temp && j > i) {
i++;
}
// 交换两个数在数组中的位置
if(j > i) { // 当j和i还没有相遇时
t = a[j];
a[j] = a[i];
a[i] = t;
}
}
// 最终将基准数归位
a[left] = a[i];
a[i] = temp;
// 继续处理左部分的
quicksort(a, left, i-1);
// 处理右部分的
quicksort(a, i+1, right);
}
public static void main(String[] args) {
int[] a = {6,1,2,7,9,3,4,5,10,8};
quicksort(a, 0, a.length-1);
System.out.println(Arrays.toString(a));
}
}
4.复杂度
- 时间复杂度
快速排序的最差时间复杂度跟冒泡排序是一样的,都是O(N^2),它的平均时间复杂度为O(NlogN)。
- 空间复杂度
可以看出就是待排序的数的数量,即O(N)。
5.优缺点
- 优点
快速排序速度很快,相比于冒泡排序,每次交换都是跳跃式的交换。
- 缺点
不稳定,不适合对象排序
有帮助的可以点个赞噢!有错误的也请在评论区帮我指出,有时可能写错甚至自己理解错。我很高兴去帮助人,因为在我新手时也是经常被别人帮助,很感谢那些博主。
有要转载的请提供转载地址。