排序算法----快速排序
快排运用的基本思想是经典的荷兰国旗问题。就是给定一个无序数组和一个数(在数组中出现),将数组中大于给定数的放在左边(小于区),等于给定数的放在中间(等于区),大于给定数的放在右边(大于区)。然后在小于区和大于区继续这一过程,不断递归进行,直到最终小于区、大于区只有一个元素为止。
为什么时间复杂度都是O(NlgN),相比于堆排序,归并排序,快排更加受到青睐呢?这是因为快排的处理逻辑很简单,导致其常数项很小,运行时间较其他时间复杂度相同的排序算法要小。但是要注意快排无法做到稳定性,比如要对一些自定义类的许多实例进行排序,此时要考虑排序的稳定性,使用快排就不合适了。而像基本数据类型的排序,不考虑稳定性,这时快排就是一个很好的选择。
快排中每一趟partition过程中影响效率的一个关键是枢纽点的选择。选择的不好,partition过后小于区、大于区的大小严重不平衡,这样会造成快排的退化,时间复杂度可能到O(N)。最好的枢纽点选择是partition过后小于区、大于区的大小基本相等。
根据枢纽点的选择不同,快排可以分为经典快排和随机快排。经典快排每次选择最后一个元素作为枢纽点(存在极大的退化风险),随机快排是每次随机选择一个元素作为枢纽点,长期来看,这是一个概率问题,数学证明长期随机快排的时间复杂度为O(N)。下面代码是将经典快排的基础上进行优化,成为一个简单随机快排。
package young.unit01; /** * 随机快速排序 * 时间复杂度:|最坏O(N^2):就是选取的轴点极差,每次只能确定一个位置,其他数都大于或者小于它,问题规模每次只是小于1 * |最好及长期期望(O(NlgN)):选取的轴点很好,左右区域大小几乎一致,相当于每次将问题规模缩减一半 * 空间复杂度:|最坏O(N)---每次确定一个位置,需要一个变量记住位置,递归n次, * |最好O(lgN)---每次记住小于区域右边界,大于区域左边界;递归lgN次 * * 稳定性:一般可以说做不到稳定性,partition过程很难做到稳定性 * 例如: |7 3 6 0 0|3 * r l * 第一个7>3,所以第一个0与大于区域左边第一个数0交换,第二个0就到第一个0前面了 * ... * 遍历到之前第一个0时,0<3,所以这个0与小于区域右边第一个数交换,此时这个0还是位于第二个0后面 * 两个0的相对顺序不可能再回归原来次序 * 做不到稳定性 * * 论文级别可以做到快排稳定性(0,1 stable sort) * @author sunmin * @Description:TODO * @date:2018年6月17日 下午1:16:14 * @Copyright: 2018 www.xy.com. All rights reserved. * 内部代码,严禁外泄 */ public class T10_QuickSort { public static void quickSort(int[] nums, int left, int right) { if (left < right) { swap(nums, left + (int)(Math.random() * (right - left + 1) / 2), right); int[] p = T09_Partition.partition(nums, left, right); T09_Partition.partition(nums, left, p[0] - 1); T09_Partition.partition(nums, p[1] + 1, right); } } private static void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } }
package young.unit01; import java.util.Arrays; /** * 荷兰国旗问题 * @author sunmin * @Description:TODO * @date:2018年6月17日 下午1:00:50 * @Copyright: 2018 www.xy.com. All rights reserved. * 内部代码,严禁外泄 */ public class T09_Partition { public static int[] partition(int[] nums, int left, int right) { int less = left - 1; int more = right; int p = nums[right]; while (left < more) { if (nums[left] < p) { swap(nums, ++less, left++); } else if (nums[left] > p) { swap(nums, --more, left); } else { left++; } } swap(nums, more++, right); return new int[]{less + 1, more - 1}; } private static void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } public static void main(String[] args) { int[] a = new int[]{3, 6, 5, 7, 2, 9, 1, 5, 5}; int[] k = partition(a, 0, a.length - 1); System.out.println(Arrays.toString(a)); System.out.println(Arrays.toString(k)); } }
浙公网安备 33010602011771号