快速排序的算法思想是分治算法,分而治之,将对整体的排序转换成对部分的排序,当部分完成了排序,整体排序也就完成了。算法的时间复杂度为O(nlogn)。
双边循环法:设置一个基准元素(一般数组第一位)pivot,两个指针left,right。left指向数组的第一位(不一定是0),right指向数组最后一位(不一定是array.length-1)。
1.首先从right指针开始,如果元素大于pivot则right指针左移,否则right停止,切换到left指针。
2.从left指针开始,如果元素小于pivot则left指针右移,否则left停止。
3.交换left和right的元素。
4.重复1-3步骤直到left,right指针重合。
5.将pivot基准元素替换到指针重合处,此轮排序完成,此时pivot左侧元素均小于pivot,pivot右侧元素均大于pivot。
单边循环法:设置一个基准元素(一般数组第一位)pivot,一个指针mark指向数组的第一位,mark作为分割点。
1.遍历给定数组,索引设置为i。如果i处元素大于pivot,继续遍历,否则进入步骤2。
2.mark指针右移一位,交换i处元素和mark处元素的值。
3.重复步骤1-2。
4.遍历完成时交换pivot和mark处元素值,此轮排序完成,此时pivot左侧元素均小于pivot,pivot右侧元素均大于pivot。
public class MyQuickSort {
public static void main(String[] args) {
//分治思想,分而治之
//从小到大排序
MyQuickSort myQuickSort = new MyQuickSort();
int[] array = {8, 4, 3, 5, 6, 2, 9, 7};
// myQuickSort.quickSort(array, 0, array.length - 1);
myQuickSort.quickSortByStack(array, 0, array.length - 1);
System.out.println(Arrays.toString(array));
}
//非递归方式-栈
public void quickSortByStack(int[] array, int startIndex, int endIndex) {
Stack<Map<String, Integer>> stack = new Stack<>();
Map<String, Integer> rootMap = new HashMap<>();
rootMap.put("startIndex", startIndex);
rootMap.put("endIndex", endIndex);
stack.push(rootMap);
while (!stack.isEmpty()) {
Map<String, Integer> pop = stack.pop();
//获取基准元素位置,此过程完成了一轮排序
int pivotIndex = getPivotByOnePointer(array, pop.get("startIndex"), pop.get("endIndex"));
if (pop.get("startIndex") < pivotIndex - 1) {
//左半区域入栈
Map<String, Integer> leftMap = new HashMap<>();
leftMap.put("startIndex", pop.get("startIndex"));
leftMap.put("endIndex", pivotIndex - 1);
stack.push(leftMap);
}
if (pivotIndex + 1 < pop.get("endIndex")) {
//右半区域入栈
Map<String, Integer> right = new HashMap<>();
right.put("startIndex", pivotIndex + 1);
right.put("endIndex", pop.get("endIndex"));
stack.push(right);
}
}
}
//递归方式
public void quickSort(int[] array, int startIndex, int endIndex) {
//跳出递归
if (startIndex >= endIndex) {
return;
}
//获取基准元素的索引,此过程也完成了一轮排序
// int pivotIndex = getPivotByTwoPointer(array, startIndex, endIndex);
int pivotIndex = getPivotByOnePointer(array, startIndex, endIndex);
//将数组分成两部分,分别快速排序,当各个部分完成排序,整体也就完成了排序。
quickSort(array, startIndex, pivotIndex - 1);
quickSort(array, pivotIndex + 1, endIndex);
}
//双边循环法
public int getPivotByTwoPointer(int[] array, int startIndex, int endIndex) {
//简化步骤,取数组第一个元素为基准元素
int pivot = array[startIndex];
//left指针左侧元素都会比基准元素小
int left = startIndex;
//right指针右侧元素都会比基准元素大
int right = endIndex;
while (left != right) {
//从右侧开始找到比基准元素小的值才停下来,指针重合停下来
while (array[right] >= pivot && left < right) {
right--;
}
//从左侧开始找到比基准元素大的值才停下来,指针重合停下来
while (array[left] <= pivot && left < right) {
left++;
}
//left==right时不用交换
if (left < right) {
//交换left,right指针位置元素,交换后使得较小的元素在左侧,较大的元素在右侧。
int temp = array[right];
array[right] = array[left];
array[left] = temp;
}
}
//pivot和指针重合点交换
array[startIndex] = array[right];
array[right] = pivot;
return right;
}
//单边循环法
public int getPivotByOnePointer(int[] array, int startIndex, int endIndex) {
//基准元素
int pivot = array[startIndex];
//通过基准元素将数组分成两部分,一部分元素小于基准元素,一部分元素大于基准元素,mark是分割点。
int mark = startIndex;
int temp;
for (int i = startIndex + 1; i <= endIndex; i++) {
//当前元素小于基准元素时,mark右移且交换mark和i处的值
if (array[i] < pivot) {
mark++;
temp = array[i];
array[i] = array[mark];
array[mark] = temp;
}
}
//交换startIndex和mark处的值
array[startIndex] = array[mark];
array[mark] = pivot;
return mark;
}
}