Javascript算法之快速排序

快速排序是一个时间复杂度为O(nlogn)的算法,可以快速处理百万数量级数据。

1.主要思想

第一步:找到一个基准值(v)
第二步:进行划分,使得比基准值小的值在基准值(v)左边,比基准值大的值在基准值(v)右边
第三步:对划分好的左右两部分,再重复进行一、二步

2.举个栗子

假设有一个数组[34,21,56,43,33,89,3]

第一步:选择数组的第一个值为"基准值"
在这里插入图片描述

第二步: 按照顺序,将每个元素与"基准值"进行比较,形成两个子集,一个"小于34",另一个"大于等于34"
在这里插入图片描述

第三步:对划分好的左右两部分,再重复进行一、二步

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.具体实现

在具体实现中会出现一个swap方法,主要用于把数组内的两个位置进行交换,

	function swap(arr, a, b) {
	  if (a===b) return
	  let c = arr[a];
	  arr[a] = arr[b];
	  arr[b] = c;
	}
一、定义三个函数

quickSort函数: 主要用于被外部调用;参数为数组;无返回值;
sort函数:主要用于递归处理数组;参数为数组和要进行划分的数组的起始下角标;无返回值;
_partation函数:用于处理划分数组;参数为数组和要进行划分的数组的起始下角标;返回值为“基准值”所在位置

function quickSort (arr) {}
function sort (arr, l, r) {}
function _partation (arr, l, r) {}
二、quickSort函数

首先,检查数组元素的个数,小于等于1,返回;

	function quickSort (arr) {
		+ let n = arr.length
		+ if (n<=1) return
	}

其次,进行数组的划分

	function quickSort (arr) {
		let n = arr.length
		if (n<=1) return
		+  _partation(arr, 0, n-1)
	}
三、sort函数

首先,确定递归的停止条件,当数组的左角标大于等于右角标时,说明已经没有再进行数组划分的必要了,直接返回

扫描二维码关注公众号,回复: 4554370 查看本文章
	function sort (arr, l, r) {
		+ if (l>=r) return
	}

其次,进行数组的划分,得到“基准值”的位置

	function sort (arr, l, r) {
		 if (l>=r) return
		 + let j = _partation(arr, l, r)
	}

最后,再次调用sort函数,继续进行“基准值”左右两个数组划分; [l…j-1]为小于“基准值”的元素集合

	function sort (arr, l, r) {
		 if (l>=r) return
		 let j = _partation(arr, l, r)
		 + sort(arr, l, j-1)
		 + sort(arr, j+1, r)
	}
四、_partation函数

首先,定义一些变量用来记录关键信息值

v: 代表“基准值”,这里暂定数组第一个元素为“基准值”,即l值;
lpos: 代表小于“基准值”部分的最后一个元素的下角标;

	function _partation (arr, l, r) {
		+ let lpos = l;
		+ let v = arr[l];
	}

然后,按照顺序,与“基准值”进行比较,小于“基准值”的元素放到lpos的下一个位置,并且lpos++,让lpos继续代表小于“基准值”部分的最后一个元素的下角标,直到循环结束

	function _partation (arr, l, r) {
		let lpos = l;
		let v = arr[l];
		+ for (let i = l+1;i<=r;i++){
			+ if (arr[i]<v) {
				+ swap(arr, i, lpos+1);
				+ lpos++;
			+ }
		+ }
	}

接着,循环完成。把lpos位置上的元素和l上的元素(即基准值)进行交换,实现lpos位置之前的元素均小于“基准值”,并且返回“基准值”的位置

	function _partation (arr, l, r) {
		let lpos = l;
		let v = arr[l];
		for (let i = l+1;i<=r;i++){
			if (arr[i]<v) {
				swap(arr, i, lpos+1);
				lpos++;
			 }
		}
		+ swap(arr,  l, lpos );
		+ return lpos;
	}
五、优化

1.取基准值的优化
在数组近乎有序时候,基准值从第一个元素开始取,也就是会获得一个最小值,在它划分时会出现,下面的情况,
例如:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

原本最好可以划分为3层,但是这里却变成了7层,算法会退化成O(n^2)级别。
优化方法:基准值变成一个随机数,这样在数据足够大时,每次取到最小值的概率会变得非常小。

	function _partation (arr, l, r) {
		let lpos = l;
		+ let randomIndex = parseInt(Math.random() * (r - l + 1) + l);
		+ swap(arr, l, randomIndex);
		let v = arr[l];
		for (let i = l+1;i<=r;i++){
			if (arr[i]<v) {
				swap(arr, i, lpos+1);
				lpos++;
			 }
		}
		swap(arr,  l, lpos );
		return lpos;
	}

2.当要划分的数组,个数小到一定程度的时候,可以借助插入排序,完成数组排序。
插入排序也是个神奇的算法,以后会总结,这里就先不说了。

这是本人第一次总结所学知识,行文思路不是很好,本文内容有借鉴阮一峰老师在写快速排序分析的思路,写的不好,望大家海涵,内容有误的地方,望大家指出。
参考资料:
https://github.com/liuyubobobo/Play-with-Algorithms
http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html

猜你喜欢

转载自blog.csdn.net/fengffen/article/details/85048154
今日推荐