快速排序
分析
时间复杂度
- 快速排序和Shell排序都有多种策略可选择,快速排序对于中轴值的选择,选择得好每次左右子问题的规模就相近,则只需要划分lgN次就可以将整个排序划分完全
- 而每个子问题划分成下一个子问题的时间复杂度是n(即partition()算法).所以整体就是NlgN.
- 如果划分得不好,即每次左右子问题规模差距极大,即每次划分为长度为1和left(剩余)两个子问题,则共需划分N次,所以最差时间为N^2
空间复杂度
- 由划分子问题的次数决定调用栈的次数,和上面一样是[lgn,n]
稳定性
- 稳定性取决于划分子问题时是否保证稳定性.
- 快慢指针法是不稳定的,因为也是使用交换法进行调整分组,这种交换是直接交换位于"大于段"的任意两个元素来给"小于等于段"腾位置,所以对于"大于段"中的两个等值元素有可能被打乱顺序.
- 对于顺序存储结构应该是不稳定的 (如果能将partition()函数写成稳定性的划分是可以实现稳定的).
- 链式存储结构可实现稳定性, patition()函数目的同 LC86. 分隔链表 对于这题我有大多人没有想到的方法, 最后附上
算法实现
快排的partition()函数
- 这个算法其实是比较难的, 快排中其它部分的代码几乎就是模板一样固定死了. 而partition()函数的写法有3种及以上.
- 等价于下面两个问题 (第一个问题勉强算是, 只是需要稍微改造一下, 快排中用到时我进行了改动)
- 给定数组nums,再给一个数num,把小于等于num的数放在数组的左边,大于num的放数组右边.空间O(1)时间O(n)
- 给定数组nums,再给一个数num,把小于num的数放在数组的左边, 等于num的数放在数组的中间,大于num的放数组右边.空间O(1)时间O(n)
public class Partition extends Sort {
public static void main(String[] args) {
new Partition().correctTest(null);
}
@Override
public void sortNums(int[] nums) {
segmentTwoPart_LeftRightPoint_Variant(nums, 6);
}
public void segmentTwoPart_FastSlowPoint(int[] nums, int num) {
int lessEqualR = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] <= num) {
swap(nums, i, lessEqualR);
lessEqualR++;
}
}
System.out.println(lessEqualR - 1);
}
public void segmentTwoPart_FastSlowPoint_Variant(int[] arr) {
int lessEqualR = -1;
int index = 0;
int N = arr.length;
while (index < N) {
if (arr[index] <= arr[N - 1]) {
swap(arr, ++lessEqualR, index++);
} else {
index++;
}
}
System.out.println(lessEqualR);
}
public static void segmentTwoPart_OppositePointer(int[] nums, int num) {
int lessEqualR = 0;
for (int i = nums.length - 1; i >= lessEqualR; i--) {
if (nums[i] <= num) {
swap(nums, lessEqualR, i);
lessEqualR++;
i++;
}
}
System.out.println(lessEqualR);
}
public void segmentThreePart_FastSlowPoint(int[] nums, int num) {
int lessR = 0;
int equalsR = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] < num) {
swap(nums, i, equalsR);
swap(nums, lessR, equalsR);
lessR++;
equalsR++;
} else if (nums[i] == num) {
swap(nums, i, equalsR);
equalsR++;
}
}
System.out.println("等于段的范围: [" + lessR + ", " + (equalsR - 1) + "]");
}
public void segmentThreePart_OppositePoint(int[] nums, int num) {
int lessR = 0;
int moreL = nums.length - 1;
for (int i = 0; i <= moreL; i++) {
if (nums[i] < num) {
swap(nums, lessR, i);
lessR++;
} else if (nums[i] == num) {
} else if (nums[i] > num) {
swap(nums, moreL, i);
moreL--;
i--;
}
}
System.out.println("等于段的范围: [" + lessR + ", " + moreL + "]");
}
public void segmentTwoPart_LeftRightPoint(int[] nums, int num) {
if (nums.length <= 1) {
return;
}
int left = 0;
int right= nums.length - 1;
while(left < right) {
while (left < right && nums[left] <= num) {
left++;
}
while (left < right && nums[right] > num) {
right--;
}
if (left < right) {
swap(nums, left, right);
}
}
System.out.println(left - 1);
}
public void segmentTwoPart_LeftRightPoint_Variant(int[] nums, int num) {
if (nums.length <= 1) {
return;
}
int left = 0;
int right= nums.length - 1;
while(left < right) {
while (left < right && nums[right] > num) {
right--;
}
while (left < right && nums[left] <= num) {
left++;
}
if (left < right) {
swap(nums, left, right);
}
}
System.out.println(left);
}
public void segmentTwoPart_Pit(int[] nums) {
int left = 0;
int right = nums.length - 1;
int pivot = nums[0];
while (left < right) {
while (left < right && nums[right] > pivot) {
right--;
}
nums[left] = nums[right];
while (left < right && nums[left] <= pivot) {
left++;
}
nums[right] = nums[left];
}
int pivotIndex = left;
nums[pivotIndex] = pivot;
System.out.println("小于等于段尾元素下标: " + pivotIndex);
}
}
快速排序完整代码
public class QuickSort extends Sort {
public static void main(String[] args) {
new QuickSort().correctTest(null);
}
@Override
public void sortNums(int[] nums) {
quickSortProcess(nums);
}
public void quickSortProcess(int[] nums) {
quickSortSegmentTwoPart01(nums, 0, nums.length - 1);
}
public int quickSortPivotChoose(int[] nums, int begin, int end) {
int index = begin + (int)(Math.random() * (end - begin + 1));
swap(nums, begin, index);
return begin;
}
public void quickSortSegmentTwoPart01(int[] nums, int begin, int end) {
if (begin >= end) {
return;
}
int pivotChose = quickSortPivotChoose(nums, begin, end);
int[] pivotIndexes = segmentThreePart_FastSlowPoint(nums, begin, end, nums[pivotChose]);
quickSortSegmentTwoPart02(nums, begin, pivotIndexes[0] - 1);
quickSortSegmentTwoPart02(nums, pivotIndexes[1] + 1, end);
}
public int[] segmentThreePart_FastSlowPoint(int[] nums, int begin, int end, int pivot) {
int lessR = begin;
int equalR = begin;
for (int i = begin; i <= end; i++) {
if (nums[i] < pivot) {
swap(nums, i, equalR);
swap(nums, lessR, equalR);
equalR++;
lessR++;
} else if (nums[i] == pivot) {
swap(nums, i, equalR);
equalR++;
}
}
int equalBegin = lessR;
int equalEnd = equalR - 1;
int[] pivotIndexes = new int[]{equalBegin, equalEnd};
return pivotIndexes;
}
public void quickSortSegmentTwoPart02(int[] nums, int begin, int end) {
if (begin >= end) {
return;
}
int pivotChose = quickSortPivotChoose(nums, begin, end);
int pivotIndex = segmentTwoPart_FastSlowPoint(nums, begin, end, nums[pivotChose]);
quickSortSegmentTwoPart01(nums, begin, pivotIndex - 1);
quickSortSegmentTwoPart01(nums, pivotIndex + 1, end);
}
public int segmentTwoPart_FastSlowPoint(int[] nums, int begin, int end, int pivot) {
int lessEqualR = begin;
for (int i = begin; i <= end; i++) {
if (nums[i] <= pivot) {
swap(nums, i, lessEqualR);
lessEqualR++;
}
}
swap(nums, begin, lessEqualR - 1);
return lessEqualR - 1;
}
}
补充说明
- Sort类在排序专栏文章中