排序算法_快速排序

   尽管有前面复杂度较好的归并排序,但当今大多数排序程序都是基于快速排序算法。它是英国计算机科学家C.A.R.(Tony) Hoare发明的。

    wikipeda上的gif图描述快排:


    和前面归并排序一样,快速排序也采用了分而治之的策略,不过它在拆的时候就让序列“稍微有序”,越拆越有序,最后就不用像归并一样重建数组了。

    如何做到稍微有序呢?就是让偏大的元素尽量靠近序列末尾,较小的元素出现在序列开头。

    例如,对于下列一个序列:

    

    那么我们如何定义大和小呢?对于上面的序列,如果我们以50为分界线,比50大或等于的定义为‘大’,比50小的定义为‘小’,那么序列就会变成这个样子:

    

    如果将这个过程递归下去,那么就能得到一个有序序列了,这就是快排的基本思想。

   与合并排序一样,快排的简单情况是大小为0或1的数组,因为它们已经是排好序的了。快速排序算法中的递归部分包含以下步骤:

    1、选择一个元素作为小元素和大元素的之间的边界,如上面的50。这个元素被称为支点。目前,挑选任何元素都可以满足此目的,最简单的策略就是选择数组的第一个元素。

    2、重新排列数组中的元素,使大元素移向数组的末尾,小元素移向数组的开头。简单来讲,这一步的目标是将元素围绕着边界位置进行分割,使得该边界左边的所有值都小于支点,而右边的所有值都大于或等于支点。这个过程被称为划分数组。

    3、对每个划分的数组进行排序。局部有序保证整体有序,整体有序是局部有序的前提。先整体,后局部,分而治之。


    那么如何划分数组呢?我们需要两个“指针”或者叫“索引手”来进行。

    还是上面的序列,我们选第一个元素为支点。

    1、用两个指针,左手lh指向剩余元素的第一个元素,右手rh指向最后一个元素。就像这样:


2、将rh索引向左移动,直至它与lh重合或者指向的元素小于支点。在这里,位置7已经是小值了,因此rh不需要移动。

3、将lh索引向右移动,直至它与rh重合或者指向的元素值大于或等于支点。在这里,lh会移动到元素值为58的三号位置。


4、如果lh和rh索引值还没有到达相同的位置,那么就交换lh和rh位置的元素,像这样:


5、重复步骤2~4,直至lh和rh位置重合。例如,下一趟中,19和95将会交换。最终rh会与lh撞到一起:


6、除非所选的支点碰巧是整个数组中最小的元素(并且代码中包含了特殊的对这种情况的检查),否则lh和rh索引重合的位置就是数组中最右侧的小值所在的位置。剩下的步骤就是将这个值与位于数组开头处的支点元素互换:


示例代码:

public class QuickSort {
	
	public void sort(int[] array) {
		quicksort(array, 0, array.length);
	}
	
	private void quicksort(int[] array, int p1, int p2) {
		if (p2 - 1 <= p1) return;
		int boundary = partition(array, p1, p2);
		quicksort(array, p1, boundary);
		quicksort(array, boundary + 1, p2);
	}
	
	private int partition(int[] array, int p1, int p2) {
		int pivot = array[p1];
		int lh = p1 + 1;
		int rh = p2 - 1;
		while (true) {
			while (lh < rh && array[rh] >= pivot) rh--;
			while (lh < rh && array[lh] < pivot) lh++;
			if (lh == rh) break;
			int tmp = array[lh];
			array[lh] = array[rh];
			array[rh] = tmp;
		}
		
		if (array[lh] >= pivot) return p1;
		array[p1] = array[lh];
		array[lh] = pivot;
		return lh;
	}
	
	public static void main(String[] args) {
		int[] array = new int[]{56, 25, 37, 58, 95, 19, 73, 30};
		new QuickSort().sort(array);
		for (int n : array)
			System.out.printf("%-5d", n);
	}
}

   


 复杂度分析:

    平均情况O(N log N)

    最差情况O(N^2)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值