随机快排时间复杂度是N平方?

141 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情

数组分区—partition

给定一个数组arr,和一个整数num,请把小于等于num的数放在数组左边,大于num的数放在右边。

分析:设计一个<=区,该区域一开始在数组左侧,只用一个变量记录它的右边界,所以一开始<=区在下标为-1的位置,然后开始遍历数组(0,1,2…N-1)

遍历规则:
  • i位置的数[i] <= num:把当前数和<=区的下一个数交换,然后<=区向右扩一个位置,当前数跳到下一个(i++)
  • [i] > num:什么也不干,直接i++

题目升级

将数组分为三段,[< | = | >] ——经典的荷兰国旗问题,小于部分和大于部分可以无序。

分析

设计一个>区,往左扩,<区,往右扩,i位置从左往右遍历,必然会中三中逻辑:

  • [i] == num:直接i++
  • [i] < num:[i]与<区的下一个数(或者说右一个数)交换,然后<区右扩一个,i++
  • [i] > num:[i]与>区的左一个数交换,然后>区左扩,i停在原地
  • 什么时候停?i和>区的左边界撞上的时候
随机快排
  • 快排1.0版本:以arr[R]做划分值,进行一次partition搞定一个数,然后左侧范围区递归,以左侧范围的arr[R]做划分值,右侧同理,周而复始
  • 快排2.0版本:与1.0一样,但是一次能搞定等于arr[R]的一批数
  • 快排3.0版本:随机选一个数(如[i]位置的数),将其与arr[R]交换之后,以arr[R]做划分值。就等同于随机选一个数拿它做划分值,剩下的所有过程跟快排2.0一样

随机快排时间复杂度分析

  • 划分值越靠近中间,性能越好,越靠近两边,性能越差
  • 随机选一个数进行划分的目的就是让好情况和坏情况都变成概率事件
  • 把每一种情况都列出来,会有每种情况下的时间复杂度,但概率都是1/N
  • 把每一种情况都考虑,时间复杂度就是这种概率模型下的长期期望
  • 时间复杂度O(N*logN)和额外空间复杂度O(logN)都是这么来的
  • 快排为什么要用递归运行,因为要记录每一个中点的位置或划分点的位置,递归空间就是每次记录划分值的空间,这是避免不掉的。
package com.harrison.class03;

public class Code04_PartitionAndQuickSort {
	// 在数组arr[L...R]上,以arr[R]做划分值
	// 小于等于arr[R]的数放左侧(左侧的数可以无序)
	// 大于arr[R]的数放右侧(右侧的数可以无序)
	public static int partition(int[] arr,int L,int R) {
		if(L>R) {
			return -1;
		}
		if(L==R) {
			return L;
		}
		int lessEqual=L-1;// 小于等于区的右边界
		int i=L;// 当前位置
		while(i<R) {
			if(arr[i]<=arr[R]) {
				swap(arr, i, ++lessEqual);
			}
			i++;
		}
		// L...lessEqual lessEqual+1...R-1         R
		// L.............lessEqual+1 lessEqual+2...R
		swap(arr, ++lessEqual, R);
		return lessEqual;
	}
	
	// arr[L...R] 玩荷兰国旗问题的划分,以arr[R]做划分值
	// <arr[R] | ==arr[R] | >arr[R]
	public static int[] netherlandFlags(int[] arr,int L,int R) {
		if(L>R) {
			return new int[] {-1,-1};
		}
		if(L==R) {
			return new int[] {L,R};
		}
		int less=L-1;// 小于区右边界
		int more=R;// 大于区左边界
		int i=L;// 当前位置
		while(i<more) {
			if(arr[i]==arr[R]) {
				i++;
			}else if(arr[i]<arr[R]) {
				swap(arr, i++, ++less);
			}else {
				swap(arr, i, --more);
			}
			swap(arr, more, R);
		}
		// L...less less+1...more-1 more...R-1    R
		// L...less less+1..........more more+1...R
		return new int[] {less+1,more};
	}
	
	public static void swap(int[] arr, int i, int j) {
		int t = arr[i];
		arr[i] = arr[j];
		arr[j] = t;
	}
	
	//快排1.0
	public static void quickSort1(int[] arr) {
		if(arr==null || arr.length<2) {
			return ;
		}
		process1(arr, 0, arr.length-1);
	}
	
	public static void process1(int[] arr,int L,int R) {
		if (L >= R) {
			return;
		}
		int M=partition(arr, L, R);
		process1(arr, L, M-1);
		process1(arr, M+1, R);
	}
	
	//快排2.0
	public static void quickSort2(int[] arr) {
		if(arr==null || arr.length<2) {
			return ;
		}
		process1(arr, 0, arr.length-1);
	}
	
	public static void process2(int[] arr,int L,int R) {
		if (L >= R) {
			return;
		}
		int[] equalArea=netherlandFlags(arr, L, R);
		process2(arr, L, equalArea[0]-1);
		process2(arr, equalArea[1]+1, R);
	}
	
	public static void quickSort3(int[] arr) {
		if(arr==null || arr.length<2) {
			return ;
		}
		process1(arr, 0, arr.length-1);
	}
	
	//快排3.0
	public static void process3(int[] arr,int L,int R) {
		if (L >= R) {
			return;
		}
		swap(arr, L+(int)(Math.random()*(R-L+1)), R);
		int[] equalArea=netherlandFlags(arr, L, R);
		process3(arr, L, equalArea[0]-1);
		process3(arr, equalArea[1]+1, R);
	}
	
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
		}
		return arr;
	}

	public static int[] copyArray(int[] arr) {
		if (arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for (int i = 0; i < arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}

	public static boolean isEqual(int[] arr1, int[] arr2) {
		if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
			return false;
		}
		if (arr1 == null && arr2 == null) {
			return true;
		}
		if (arr1.length != arr2.length) {
			return false;
		}
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}
	
	public static void main(String[] args) {
		int testTimes=1000000;
		int maxValue=100;
		int maxSize=100;
		boolean succeed=true;
		for(int i=0; i<testTimes; i++) {
			int[] arr1=generateRandomArray(maxSize, maxValue);
			int[] arr2=copyArray(arr1);
			int[] arr3=copyArray(arr1);
			quickSort1(arr1);
			quickSort2(arr2);
			quickSort3(arr3);
			if(!isEqual(arr1, arr2) || !isEqual(arr2, arr3)) {
				succeed=false;
				printArray(arr1);
				printArray(arr2);
				printArray(arr3);
				break;
			}
		}
		System.out.println(succeed?"Nice":"Oops");
	}
}