Java数据结构与算法——快速排序

一.快速排序介绍
快速排序的思想:在每一轮挑选一个基准元素,并让比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分。这种思路叫做分治法。
时间复杂度:假设元素是n,平均情况下需要logn轮,因此快速排序总体的时间复杂度是O(nlogn)。
基准元素(pivot)的选择:随机选择一个元素作为基准元素。

二.算法图解
Java数据结构与算法——快速排序_第1张图片
三.递归法实现快速排序

package data.structure;

import java.util.Arrays;

/**
 * @author mlnk
 * 快速排序(递归方式)
 * 核心思想:采取分治法,指定一个基准元素,遍历数列,比基准元素大的不动,比基准元素小的放到基准元素左边。
 * 如果遍历到的元素小于基准元素,做两件事:1.mark指针加1;2.将mark指针所在的元素与遍历到的元素互换位置。
 * mark指针代表比pivot小的区域。
 * 最后,将基准元素与mark指针所在位置的元素互换,即可将数组一分为二
 */
public class MlnkQuickSort {
	static int count = 0;//第几轮遍历
	public static void quickSort(int[] arr, int startIndex, int endIndex) {
		//递归结束条件
		if (startIndex >= endIndex) {
			return;
		}
		//得到基准元素位置
		int pivotIndex = partition(arr, startIndex, endIndex);
		//根据基准元素分成两部分进行排序
		quickSort(arr, startIndex, pivotIndex-1);
		quickSort(arr, pivotIndex+1, endIndex);
	}

	/**
	 * 分治,单边循环法
	 * @param arr
	 * @param startIndex
	 * @param endIndex
	 * @return
	 */
	private static int partition(int[] arr, int startIndex, int endIndex) {
		//取第一个元素的位置作为基准元素
		int pivot = arr[startIndex];
		//一个指针,代表小于基准元素的区域边界
		int mark = startIndex;
		
		for (int i = startIndex+1; i <= endIndex; i++) {
			//小于基准元素,做了两件事:1.mark指针右移一位,因为小于pivot的区域边界增大了1
			//2.让最新遍历到的元素和mark指针所在位置的元素交换位置,因为最新遍历的元素归属于小于pivot的区域
			if (arr[i] < pivot) {
				mark++;
				int tmp = arr[mark];
				arr[mark] = arr[i];
				arr[i] = tmp;
			}
		}
		
		arr[startIndex] = arr[mark];//最后把pivot元素交换到mark指针所在位置,这一轮结束
		arr[mark] = pivot;
		count++;
		System.out.println("第" + count + "轮遍历:" + Arrays.toString(arr));
		return mark;
	}
	
	public static void main(String[] args) {
		int[] arr = new int[]{5,8,6,4,3,7,9,1};
		quickSort(arr, 0, arr.length-1);
	}
}

运行结果:
Java数据结构与算法——快速排序_第2张图片
四.栈方式实现快速排序

绝大多数的递归逻辑,都可以用栈来实现。
代码中的调用,本身就使用了一个方法调用栈。每次进入一个新方法就相当于入栈;每次有方法返回就相当于出栈。
所以,我们可以构造一个栈,用来存储每次方法调用的参数(起止下标)。
类似下面这样:

quickSort(0,7)
{
	quickSort(0,2)
	quickSort(4,7)
}

下面给出具体实现:

	public static void quickSort(int[] arr, int startIndex, int endIndex) {
		//用一个集合来代替递归的函数栈
		Stack<Map<String, Integer>> quickSortStack = new Stack<>(); 
		//整个数列的起止下标,以哈希的形式入栈
		Map rootParam = new HashMap<>();
		rootParam.put("startIndex", startIndex);
		rootParam.put("endIndex", endIndex);
		quickSortStack.push(rootParam);
		
		//循环结束条件:栈为空时
		while (!quickSortStack.isEmpty()) {
			//栈顶元素出栈,得到起止下标
			Map<String, Integer> startAndEndIndex = quickSortStack.pop();
			//得到基准元素位置
			int pivotIndex = partition(arr, startAndEndIndex.get("startIndex"), 
					startAndEndIndex.get("endIndex"));
			//根据基准元素分成两部分,把每部分的起止下标入栈
			if (startAndEndIndex.get("startIndex") < pivotIndex - 1) {
				Map<String, Integer> leftParam = new HashMap<>();
				leftParam.put("startIndex", startAndEndIndex.get("startIndex"));
				leftParam.put("endIndex", pivotIndex - 1);
				quickSortStack.push(leftParam);
			}
			if (pivotIndex + 1 < startAndEndIndex.get("endIndex")) {
				Map<String, Integer> rightParam = new HashMap<>();
				rightParam.put("startIndex", pivotIndex+1);
				rightParam.put("endIndex", startAndEndIndex.get("endIndex"));
				quickSortStack.push(rightParam);
			}
		}
	}

每次循环,让栈顶元素出栈,然后进行分治,按照基准元素分成左右两部分,再分别入栈。当栈为空时,说明排序语句完毕,退出循环。

你可能感兴趣的:(Java数据结构与算法)