分治思想
原地排序
时间复杂度: O ( N l o g N ) O(NlogN) O(NlogN)
空间复杂度: O ( 1 ) O(1) O(1)
优势: 时间复杂度和归并相同,但是更加节省空间,实现相对堆排序来说简单易懂
最重要就是那个寻找基准坐标的函数partitoin,现在比较流行就是用单边循环来完成。
劣势: 不稳定啊,非常依赖基准点的选择,时间复杂度不会稳定的停留在
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN)。
递归+单边循环
其实很多人记不住快排就是因为记不住单边循环的partition函数,我们只要明白这个函数到底有什么功能,以及如何实现就ok了。
partition函数的功能
快排的通篇的要义就是找基准点,找基准点的过程就是排序的过程,但是这个基准点(pivotIndex)是非常随机的,因为那个基准值(pivotValue)的选择是随机选的,咱们这个partition函数里一般来说选的是第一个元素作为基准值。
基准点其实就是个数组的索引值,它代表着,这个点的坐标的左边的所有元素都比当前这个点代表的基准值来的小;右边的都比这基准值大。
partition函数操作步骤:
1)选第一个元素为基准值(pivotValue)
2)选start为基准点
3)遍历判断是否存在比当前的基准值还要小的元素
4)如果存在,就和移动到数组的左边。(重复3,4步骤,直到结束)
5)当前的pivotindex和第一个元素进行交换,返回当前pivotIndex。
如果上面看不懂,我们来看个图
看图明白了吧,partition函数 就是要告诉你,基准点左右我安排好了,你就按照基准点给安排就好。
这就上代码来看看:
show the code
public static void quickSort2(int[] arr, int n) {
quickSort2(arr, 0, n-1);
}
public void quickSort2(int[] arr, int start, int end) {
if (start > end) {
return;
}
int pivotIndex = partition2(arr, start, end);
quickSort2(arr, start, pivotIndex - 1);
quickSort2(arr, pivotIndex + 1, end);
}
/**
* 单边循环解决
* @param arr
* @param startIndex
* @param endIndex
* @return
*/
private int partition2(int[] arr, int startIndex, int endIndex) {
int pivotIndex = startIndex;
int pivotValue = arr[startIndex];
for (int i = startIndex + 1; i <= endIndex; ++i) {
if (pivotValue > arr[i]) {
pivotIndex++;
swap(arr, pivotIndex, i);
}
}
swap(arr, startIndex, pivotIndex);
return pivotIndex;
}
非递归实现+单边循环
递归的本质就是栈,如果在利用栈来实现递归也是ok的。
show the code
private String START_INDEX = "startIndex";
private String END_INDEX = "endIndex";
public void quickSortNoRe(int[] arr, int start, int end) {
Stack<Map<String, Integer>> stack = new Stack<>();
Map<String, Integer> rootParam = new HashMap<>();
rootParam.put(START_INDEX, start);
rootParam.put(END_INDEX, end);
stack.push(rootParam);
while (!stack.isEmpty()) {
Map<String, Integer> tempMap = stack.pop();
int tempStart = tempMap.get(START_INDEX);
int tempEnd = tempMap.get(END_INDEX);
int pivotIndex = partition3(arr, tempStart, tempEnd);
if (tempStart < pivotIndex - 1) {
Map<String, Integer> leftParam = new HashMap<>();
leftParam.put(START_INDEX, start);
leftParam.put(END_INDEX, pivotIndex - 1);
stack.push(leftParam);
}
if (tempEnd > pivotIndex + 1) {
Map<String, Integer> rightParam = new HashMap<>();
rightParam.put(START_INDEX, pivotIndex + 1);
rightParam.put(END_INDEX, end);
stack.push(rightParam);
}
}
}
/**
* 单边循环解决
* @param arr
* @param startIndex
* @param endIndex
* @return
*/
private int partition3(int[] arr, int startIndex, int endIndex) {
int pivotIndex = startIndex;
int pivotValue = arr[startIndex];
for (int i = startIndex + 1; i <= endIndex; ++i) {
if (pivotValue > arr[i]) {
pivotIndex++;
if (pivotIndex != i) {
swap(arr, pivotIndex, i);
}
}
}
swap(arr, startIndex, pivotIndex);
return pivotIndex;
}