排序原理
快速排序采用的是分治策略,也叫分治法。原理就是将一个大问题拆解成与原问题相同的小问题,递归解决小问题,将小问题的解融合长大问题的解。快速排序的主要思想就是随机选取一个标量将它放到已经排好序中它应该所在的位置上,什么叫标量应该所在的位置呢,也就是该标量的左边全部小于该标量,该标量的右边全部大于该标量,然后分别再对标量两边的数组进行以上逻辑处理,以此逻辑递归下去,直到所有元素有序为止。
具体流程如:第一步先从数组中随机选取一个标量,然后同时从数组的头向后遍历,以及从尾向前遍历,直到两个只针相遇为止,遍历过程中,从头向后遍历中如果遇到比标量大小的元素则什么也不做继续向后遍历,如果遇到比标量大的元素则停止,并记录该元素,同时从尾部向前边遍历中,如果遇到比标量大的元素则什么也不做,继续向前遍历,如果遇到如果遇到比标量小的元素则停止,这个时候交换两个只针所指元素的位置,当两个只针遇到了以后则遍历停止,并将标量应该所在的位置的下标记录下来,以该下标为基础将数组查分成两个小问题,在重复上述步骤,知道排序完成为止。
时间复杂第
快速排序的时间复杂度分为两种情况
- 如果随机抽取的标量是和数组中的最大或者最小值,那么时间复杂为O(n2)。因为当第一次循环的时候,划分的此时为n-1次,并且第一次划分数组的元素个数比未排序前仅仅少了一个元素,那么第i次划分的其实长度为n-i+1,所需要的比较次数为n-i次,故比较的从次数达到了最大值n*(n-1)/2=o(n2).
- 如果随机抽取的标量为数组的中值,那么时间复杂度为O(nlog2n)。
排序代码
package com.alg.sort;
import org.junit.Test;
import java.util.Random;
public class QuickSort {
public void quickSort2(int[] a, int l, int r) {
//递归终止判断,当标量已经是数组最后一位了,就该终止程序
if (l >= r) {
return;
}
int p = partition2(a, l, r);
quickSort2(a, l, p);
quickSort2(a, p + 1, r);
}
/**
* 数组中重复元素过多导致快速排序的时间复杂度为O(n^2),
* 优化方式为:选定一个标量,从数组的两头开始遍历分别遇到比变量大的或者比标量小的停止
* 并交换两个停止位置元素的位置。
*/
public int partition2(int[] arr, int l, int r) {
int t = arr[l];
//优化1:随机抽取数组中的元素作为标量,优化近乎有序的数组的快速排序
Random random = new Random();
int randomIndex = random.nextInt(r) % (r - l + 1) + l;
arr[l] = arr[randomIndex];
arr[randomIndex] = t;
int v = arr[l];
int i = l + 1;
int j = r;
while (true) {
//从左往右遍历,该循环停止了,那么就遇到大于等于标量的元素了,边界的i要小于等于r,因为r是数组遍历技术的位置。
while (i <= r && arr[i] < v) {
i++;
}
//从右往左遍历,该循环停止了就遇到了小于等于标量的元素了,边界j要大于等l,不能比L小,因为L是数组排序开始的位置
while (j >= l && arr[j] > v) {
j--;
}
//当i和j相遇了,则没有元素需要遍历了
if (i > j) {
break;
}
//将大于等于标量和小于等于标量的元素进行位置交换,这样重复的元素就别分散到了元素的两边
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
return j;
}