5.4找中位数
快速排速的算法思想其实就是建立在找中位数的基础上,先确定第一次找到的中位数的位置,此时该中位数的位置直至排序结束都是固定不变的,接着循环在该中位数两侧的一堆数值中找中位数的操作,简单来说,就是先找到第一个中位数,接着重复寻找中位数,直至数组完全有序为止。
找找中位数的基本思想:
表格示例:
在一堆无序的数值中,先假设首位的数值为中位数,即此时该假设中位数的下标为0,此时下标为0的位置也定义为begin,然后从后往前找一个比该中位数小或相等的数值,定义此时该值的下标为end,如果没找到,则end的位置向前进1,如果找到了,则该位置先不动,再从起始位置begin,向右寻找比假设中位数的值大的数,如果没有找到,则begin进1,如果找到了,则该位置不动,此时如果begin与end没有相遇,则交换begin与end位置上的值,重复上面的操作直至begin与end相遇,此时该位置就是中位数应该处于的位置,如果该位置不是下标为0的位置,则交换该位置与下标为0位置上的值,此时就找到了中位数,快速排序的思想就是建立在找中位数的基础上循环操作的。
代码示例:
public class Test {
public static void main(String[] args) {
Random rand = new Random();
int[] array = new int[10];
final int LIMIT = array.length * 100;
//生成array.length个不重复的取值范围为数组长度100倍的随机数
int size = 0;
array[size++] = rand.nextInt(LIMIT) + 1;//第一个值不需要验证是否已经重复
boolean no;
for (int t; size < array.length; ) {
t = rand.nextInt(LIMIT) + 1;
no = true;
for (int j = 0; j < size; j++) {
if (array[j] == t) {
no = false;
break;
}
}
if (no) {
array[size++] = t;
}
}
System.out.println("之前:");
for (int i : array) {
System.out.print(i + "\t");
}
//找中位数
int begin = 0, end = array.length - 1;
int mid = array[0], t;//假设第0个数是中位数
while (begin < end) { //begin与end没相遇进入相遇,如果相遇,则不进入循环
while (begin < end && mid <= array[end]) {//没相遇,并且end位置的数值不比mid小,则end--向左寻找
end--;
}
while (begin < end && mid >= array[begin]) {//没相遇,并且begin为值得数值不比mid大,则begin++向右寻找
begin++;
}
if (begin < end) {//没相遇,交换begin和end位置上的数值
t = array[begin];
array[begin] = array[end];
array[end] = t;
}
}
if (0 != begin) {//如果中间值不在begin和end相遇的位置,交换0位置和相遇位置上的值
array[0] = array[begin];
array[begin] = mid;
}
System.out.println("\n之后:");
for (int i : array) {
System.out.print(i + "\t");
}
}
}
5.5快速排序
快速排序是对冒泡排序的一种改进,快速排序的思想其实就是建立在找中位数的基础上,进行重复操作的。
基本思想:
1,、通过一趟排序将要排序的数据分割成独立的两部分
2、其中一部分的所有数据都比另外一部分的所有数据都要小
3、然后再按此方法对这两部分数据分别进行快速排序
4、整个排序过程可以递归进行,以此达到整个数据变成有序序列.
快速排序动图演示:(来自网络图片)
代码示例:
import java.util.Arrays;
public class QuackSort {
public static void main(String[] args) {
int[] arr = { -9, 78, 0, 23, -567, 70 };
quackSort(arr, 0, arr.length - 1);
System.out.println("arr排序的结果是:" + Arrays.toString(arr));
}
//
public static void quackSort(int[] arr, int left, int right) {
int l = left; // 左索引
int r = right; // 右索引
int pivot = arr[(left + right) / 2]; // pivot 中轴
int temp = 0; // 临时变量,作为交换时使用
// while循环的目的:让,比pivot 值小的放到左边,比pivot 值大的放到右边
while (l < r) {
// 在pivot左边一直找,找到一个大于等于pivot的值,才退出
while (arr[l] < pivot) {
l += 1;
}
// 在pivot右边一直找,找到一个小于等于pivot的值,才退出
while (arr[r] > pivot) {
r -= 1;
}
// 如果l >= r,则说明pivot 的左右两的值,已经按照左边全部是
// 小于等于pivot值,右边全部是大于等于pivot值.
if (l >= r) {
break;
}
// 数据交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// 如果交换完后,发现这个arr[l] == pivot值,相等 r--, 前移
if (arr[l] == pivot) {
r -= 1;
}
// 如果交换完后,发现这个arr[r] == pivot值,相等 l++, 后移
if (arr[r] == pivot) {
l += 1;
}
}
//如果 l == r, 必须l++, r--, 否则为出现栈溢出
if(l == r){
l += 1;
r -= 1;
}
//向左递归
if(left < r){
quackSort(arr, left, r);
}
//向右递归
if(right > l){
quackSort(arr, l, right);
}
}
}
总结:
本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法,相较于冒泡排序、选择排序以及插入排序等归并排序来说性能上更优,所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。