快速排序算法及优化

基本思想:

1)选择一个基准元素,通常选择第一个元素或者最后一个元素,

2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。

3)此时基准元素在其排好序后的正确位置

4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

快速排序的示例:

(a)一趟排序的过程:

(b)排序的全过程



算法的实现:

 递归实现:

import java.util.Arrays;

public class QuickSort {
	public static void main(String[] args) {
		int[] data = { 5, 1, 9, 3, 7, 4, 8, 6, 2 };
		System.out.println("排序前:" + Arrays.toString(data));
		quickSort(data, 0, data.length - 1);
		System.out.println("排序后:" + Arrays.toString(data));

	}

	private static void quickSort(int[] data, int low, int high) {
		int base;
		if (low < high) {
			base = partition(data, low, high);
			quickSort(data, low, base - 1);
			quickSort(data, base + 1, high);
		}
	}

	private static int partition(int[] data, int i, int j) {
		int base = data[i];// 选定第一个数为基准 (可以优化为三数取中,即取左右中三个数的中间数)

		while (i < j) {
			while (i < j && data[j] > base)
				j--;
			if (i < j) // 如果不加此比较,swap两个相同位置的记录,结果是0
				swap(data, i, j); // 将比枢轴记录小的交换到低端

			while (i < j && data[i] < base)
				i++;
			if (i < j)
				swap(data, i, j); // 将比枢轴记录大的交换到高端
		}
		return i;
	}

	private static void swap(int[] a, int i, int j) {

		a[i] = a[i] + a[j];
		a[j] = a[i] - a[j];
		a[i] = a[i] - a[j];
	}

	// 优化算法 优化递归操作及小数组时的排序方案
	private static void quickSort1(int[] data, int low, int high) {
		int base;
		int max_length_insert_sort = 9;// 当high-low大于常数时用快速排序
		if ((high - low) > max_length_insert_sort) {
			while (low < high) {
				base = partition1(data, low, high);
				quickSort1(data, low, base - 1);//对低子表递归排序
				low = base + 1;//尾递归
			}
		} else
			new StraightInsertionSort().insertionSort2(data);// 否则用插入排序
	}

	// 优化不必要的交换
	private static int partition1(int[] data, int i, int j) {
		int base = data[i];// 选定第一个数为基准
		int temp = base; // 备份枢轴
		while (i < j) {
			while (i < j && data[j] > base)
				j--;
			data[i] = data[j];// 采用替换而不是交换的方式进行操作

			while (i < j && data[i] < base)
				i++;
			data[j] = data[i];// 采用替换而不是交换的方式进行操作
		}
		data[i] = temp;// 将枢轴值替换回来
		return i;
	}

	// 三数取中 :即取三个关键字先进行排序,将中间数作为枢轴,一般取左端、右端、中间三个数。
	public int getMiddle(int[] data, int i, int j) {

		int m = i + (j - i) / 2;
		if (data[i] < data[j])
			swap(data, i, j);
		if (data[m] > data[j])
			swap(data, m, j);
		if (data[m] > data[i])
			swap(data, m, i);

		return data[i];

	}
}

通常我们在进行快速排序时,关键数据(即基准元素)一般选取序列的第一个元素,但在序列大部分有序时,这会致使最坏时间复杂度O(n^2)的出现,为了避免这种情况的发生,我们可以选择以下几种方法对其进行优化:

  • 三数取中;
  • 优化递归操作;
  • 使用并行或多线程处理子序列;
  • 随机选择关键数据进行快排,可以使用rand()方法;
  • 当待排序序列的长度分割到一定大小后,使用插入排序;
  • 在一次分割结束后,可以把与 Key 相等的元素聚在一起,继续下次分割时,不用再对与 key 相等元素分割。

算法分析:
快速排序的时间复杂度与关键字初始序列有关。
最坏时间复杂度:O(n^2):
以第一个数或最后一个数为基准时,当初始序列整体或局部有序时,快速排序的性能会下降。若整体有序,此时,每次划分只能分出一个元素,具有最坏时间复杂度,快速排序将退化成冒泡排序。
最好时间复杂度:O(n*以2为底n的对数) (平均)
每次选取的基准关键字都是待排序列的中间值,也就是说每次划分可以将序列划分为长度相等的两个序列。快速排序的递归过程可以用一棵二叉树来表示,递归树的高度是2为底的对数,每层需要比较的次数是n/2,所以最好时间复杂度是O(n*以2为底n的对数),因为很多时候输入序列都是乱序的,所以最好时间复杂度也是平均时间复杂度。

不稳定!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值