快速排序(三向切分快速排序)

快速排序(交换排序,递归操作)

  • 思路:

    • 快速排序核心思想是找一个基点,然后让基点左右两遍元素依次和它比较,如果左边某个元素大于基点且右边某个小于基点,则将这俩交换;直到左右两边到达基点,越位之后开始递归,总体分为两部分一左一右,而一左一右之中又可以分为一左一右,这样递归下去,直到两端为止。这个过程类似一生二、二生四、四生八......
    • 这里可以看出快速排序是不稳定的
      (假设,左边第一个数大于基数,最右边有两个相同的数都小于基数,于是,左边的数先和右边后一个数交换,之后如果左边的第二个数仍然大于基数,那么再次进行交换后,两个相同的数的相对位置就发生了改变)

以下用一个图例说明快速排序过程,这里给出一组测试数组:-9,78,0,23,-56,70;图中pivot表示基准,假设基准=(0+5)/2=2

'一生二' 步骤如下:

递归开始

具体代码

public static void quickSort(int[] nums, int left, int right) {
		int pivot = nums[(left + right) / 2];//基准找的中间的
		int l = left;//基准左边的
		int r = right;//基准右边
		int temp = 0;//用于交换的临时变量
		while (l < r) {//当索引下标基准左边的小于右边的,说明基准两边的值需要排序
			while (nums[l] < pivot)//基准左边的值小于基准时,则不用交换,继续比较下一个,当基准左边的某个值大于基准时,跳出循环,此时l为需要交换的元素下标
				l++;
			while (nums[r] > pivot)//同理,同上
				r--;
			if (l == r)//如果当基准左右索引相等时,则退出当前循环,主要是(为了避免同一索引位置的交换)。
				break;
			temp = nums[l];
			nums[l] = nums[r];
			nums[r] = temp;
			if (nums[l] == pivot)//为了避免重复执行上面两个while语句,(当只有两个元素进行比较交换后,那么就应该退出本次循环)
				r--;
			if (nums[r] == pivot)//同理,同上
				l++;
			 System.out.println(Arrays.toString(nums));
		}
		r--;//上面的循环执行结束后,基准左小右大成立
		l++;//必然l==r==pivot的下标,所以进行r--和l++是为了给左右递归做准备!
		if (left < r)//当基准左边第一个r的索引大于0时,那么说明基准左边起码有≥2个值,需要再进行排序
			quickSort(nums, left, r);//左递归
		if (l < right)//同理,同上
			quickSort(nums, l, right);//有递归
	}

测试

before:2020/02/13 20:39:07:910
after:2020/02/13 20:39:09:310
800w数据(数据区间在0——1000w)排序:花了大概不到2s
时间复杂度:最坏情况:O(n^2) 最好情况:O(nlogn)

三向切分的快速排序(改进快排之一)

它的出现是对含有以任意概率分布的重复元素的输入进行优化。
基准为左边第一个元素,lt初始位置也是最左边,有一个索引i从基准右边开始遍历,如果发现nums[i]<基准,则进行交换并将lt和i加一;否则如果nums[i]>基准,那么就和右边gt指向的元素进行交换并且gt+1;如果nums[i]=基准,则i++跳过当前元素,进行下一次处理;

public static void sort(int[] nums,int l,int r){
		if (r<=l)return;
		int lt=l,i=l+1,gt=r;
		int pivot=nums[l];
		while(i<=gt){
			int cmp=compare(nums[i],pivot);
			if(cmp<0) exch(nums,lt++,i++);
			else if(cmp>0) exch(nums,i,gt--);
			else i++;
		}
		sort(nums,l,lt-1);
		sort(nums,gt+1,r);
	}
	private static void exch(int[] nums,int i,int j){
		int temp=nums[i];
		nums[i]=nums[j];
		nums[j]=temp;
	}
	private static int compare(int a,int b){
		if(a<b)return -1;
		else if(a>b) return 1;
		else return 0;
	}

* 对于包含大量重复元素的数组,它将排序时间从线性对数级降低到了线性级别!!!当存在重复元素时,三向切分无疑是最优的算法!
posted @ 2020-07-03 18:16  两仪子  阅读(769)  评论(0编辑  收藏  举报