快速排序总结

文章内容和代码来自《漫画算法》和数据结构教材。现进行一下代码编写练习。
1.双边循环法

    /**
	 * 双边循环法,从左右两端分别向中间进行比较和交换数据 递归实现
	 */
	void quickSortV1(int[] arr, int start, int end) {
		// 递归结束条件
		if (start >= end) {
			return;
		}
		// 得到基准元素pivot位置
		int pivotIndex = partitionV1(arr, start, end);
		// 根据基准元素,分成两部分进行递归
		quickSortV1(arr, start, pivotIndex - 1);
		quickSortV1(arr, pivotIndex + 1, end);
	}

	/**
	 * 一轮比较与交换divide and conquer
	 * 
	 * @param arr
	 * @param start
	 * @param end
	 * @return
	 */
	private int partitionV1(int[] arr, int start, int end) {
		int pivot = arr[start];
		int left = start;
		int right = end;
		while (left != right) {
			// 控制right指针比较并左移
			while (left < right && arr[right] > pivot) {
				right--;
			}

			// 控制left指针比较并左移
			while (left < right && arr[left] <= pivot) {
				left++;
			}
			// 交换left,right指向的元素
			if (left < right) {
				int p = arr[left];
				arr[left] = arr[right];
				arr[right] = p;
			}
		}

		// pivot和指针重合点交换
		arr[start] = arr[left];
		arr[left] = pivot;

		return left;
	}

	@Test
	public void fun1() {
		// int[] arr=new int[]{4,4,6,5,3,2,8,1};
		int[] arr = new int[] { 49, 38, 65, 97, 76, 13, 27, 49 };
		quickSortV1(arr, 0, arr.length - 1);
		System.out.println(Arrays.toString(arr));
	}

2.单边循环法

	/**
	 * 单边循环法, 
	 * 1.设置一个mark指针,表示小于pivot数据区域的边界 
	 * 2.从左向右开始遍历与pivot比较,如果大于等于pivot,继续向后
	 * 3.如果小于,则mark后移一位,并与小于pivot元素的位置进行交换
	 * 4.直到一轮遍历结束,最后将mark位置元素与pivot所在位置元素交换位置。
	 * 
	 * 
	 */
	void quickSortV2(int[] arr, int start, int end) {
		// 递归结束条件
		if (start >= end) {
			return;
		}
		// 得到基准元素pivot位置
		int pivotIndex = partitionV2(arr, start, end);
		// 根据基准元素,分成两部分进行递归
		quickSortV2(arr, start, pivotIndex - 1);
		quickSortV2(arr, pivotIndex + 1, end);
	}

	/**
	 * 分治 divide and conquer
	 * 
	 * @param arr
	 * @param start
	 * @param end
	 * @return
	 */
	private int partitionV2(int[] arr, int start, int end) {
		// 取第一个位置元素作为基准元素pivot
		int pivot = arr[start];
		int mark = start;

		for (int i = start + 1; i <= end; i++) {
			if (arr[i] < pivot) {
				mark++;
				int p = arr[mark];
				arr[mark] = arr[i];
				arr[i] = p;
			}
		}

		arr[start] = arr[mark];
		arr[mark] = pivot;
		return mark;
	}

	@Test
	public void fun2() {
		int[] arr = new int[] { 49, 38, 65, 97, 76, 13, 27, 49 };
		quickSortV2(arr, 0, arr.length - 1);
		System.out.println(Arrays.toString(arr));
	}

3.非递归法实现

	/**
	 * 非递归的方式进行实现 (绝大多数的递归逻辑,都可以用栈的方式来代替)
	 * 
	 * @param arr
	 * @param start
	 * @param end
	 */
	void quickSortV3(int[] arr, int start, int end) {
		Stack<Map<String, Integer>> stack = new Stack<>();
		Map<String, Integer> root = new HashMap<String, Integer>();
		root.put("start", start);
		root.put("end", end);
		stack.push(root);

		// 栈为空时结束循环
		while (!stack.isEmpty()) {
			Map<String, Integer> item = stack.pop();
			int pivotIndex = partitionV3(arr, item.get("start"), item.get("end"));

			// 根据基准元素分成2部分,把每一部分的起止下标入栈
			if (item.get("start") < pivotIndex - 1) {
				Map<String, Integer> left = new HashMap<>();
				left.put("start", item.get("start"));
				left.put("end", pivotIndex - 1);
				stack.push(left);
			}
			if (pivotIndex + 1 < item.get("end")) {
				Map<String, Integer> right = new HashMap<>();
				right.put("start", pivotIndex + 1);
				right.put("end", item.get("end"));
				stack.push(right);
			}
		}

	}

	private int partitionV3(int[] arr, int start, int end) {
		int pivot = arr[start];
		int mark = start;
		for (int i = start + 1; i <= end; i++) {
			if (arr[i] < pivot) {
				mark++;
				int p = arr[mark];
				arr[mark] = arr[i];
				arr[i] = p;
			}
		}
		arr[start] = arr[mark];
		arr[mark] = pivot;
		return mark;
	}

	@Test
	public void fun3() {
		int[] arr = new int[] { 49, 38, 65, 97, 76, 13, 27, 49 };
		quickSortV3(arr, 0, arr.length - 1);
		System.out.println(Arrays.toString(arr));
	}

数据结构教材的算法转换为C语言

#include
using namespace std;

/*
数据结构(C语言版) 严蔚敏,吴伟民
的快速排序算法转换为代码

*/

int Partition(int arr[], int low, int high){
	int pivot = arr[low];// 第一个记录作为pivot
	while(low<high){
		while(low<high && arr[high]>=pivot){
			--high;
		}
		arr[low]=arr[high];
		while(low<high && arr[low]<=pivot){
			++low;
		}
		arr[high]=arr[low];
	}
	arr[low]=pivot;
	return low;
}

void Qsort(int arr[], int low, int high){
	if(low < high){
		int pivotLoc = Partition(arr, low, high);
		Qsort(arr, low, pivotLoc-1);
		Qsort(arr, pivotLoc+1,high);
		
	}
}

int main(int argc, char* argv[]) {
	int arr[]={49,38,65,97,76,13,27,49};
	Qsort(arr,0,7);
	for(int i=0;i<8;i++){
		cout<<arr[i]<<",";
	}
	
	return 0;
}

分治过程如下图:
快速排序总结_第1张图片
总结:快速排序的平均时间复杂度为O(nlogn)。漫画算法的快排和数据结构教材的快排算法略有区别,主要体现在数据元素交换方式上。对于基准数据pivot的选择,代码中默认选择数组第一个元素,但是若初始记录序列按关键字有序或基本有序时,快排将蜕化为冒泡排序,其时间复杂度为O(n2),为改进之,通常依三者取中的法则来选取枢轴记录pivot,即比较第一个,最后一个,中间一个元素的大小,取中间者为pivot;漫画算法里面还提到了随机选择一个元素作为基准元素作为pivot,然而也依然有极小的几率选到数列的最大值或最小值,影响分治的效果。
其实就是每一轮交换数据后,返回pivot当前位置,根据pivot位置将一个数组分成2个子数组,对子数组继续递归处理,直到子数组长度变为1。

你可能感兴趣的:(算法)