聊一聊快速排序(Js)
快速排序
基本思路
双指针+递归分治(本质是一个创建二叉树搜索树的过程)
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
我的理解
上面的基本思路是参考网络上大佬的文章整理的出来的,我来说说我的理解。
在将要排序的数据中选取一个数作为基准数,将这些数据中比所选取的基准数小的数放在所选取基准数的左边为左数组,将比所选取基准数大的数组放在右边为右数组。
通过递归的方式重复循环1中的过程达到排序的目的。
下面是我的代码
let testArray = [3, 1, 2, 5, 6, 4];
let quickSort = (array) => {
if (array.length < 2) return array;
let leftArray = [];
let rightArray = [];
let baseDigit = array[0];
array.forEach(element => {
if (element < baseDigit) {
leftArray.push(element);
} else if (element > baseDigit) {
rightArray.push(element);
}
});
return quickSort(leftArray).concat(baseDigit, quickSort(rightArray))
};
quickSort(testArray);
某乎上一篇文章的思路
基本思路跟我上述理解大同小异,主要来看看这篇文章具体的实现过程。下面借用原文的图来讲解(原文的图做的很好就不单独画图了,主要讲一讲原文没解释需要注意的地方,和对该篇文章做一个补充),底部附原文链接。
1.数组[2,3,1,5,6,4],创建两指针,一个只想头一个指向尾,再确定一个基准数。
(注意:为了方便后面递归是能够确定基准数,这里基准数选取,第一个数或者最后一个数)
2.开始第一次的递归处理,尾指针先从右往左扫,扫到第一个小于(注意是小于,而不是小于等于哦)基准数的位置停住,这时候头指针再从左往右扫,扫到第一个大于基准数的位置停住,这时候是下面的图示状态:
(注意:这里如果基准数选区的第一个数,应该尾指针先往左侧扫,若基准数选取为最后一个属则,应是头指针向往右扫)
交换两个指针所指的数,成为了下面的状态:
3.两个数交换完毕,右指针此时指的是arr[2] = 3, 左指针指着arr[1] = 1;交换完毕后右指针继续从当前位置往左扫,扫到1的时候发现和左指针相遇了,那么这个时候就结束左右指针的扫描,左右指针同时指着arr[1] = 1,即:
此时退出循环扫描的过程,交换基准数与左右指针同时所指的数的位置,开头说了,基准数我选择的是arr[0] = 2, 指针指的是arr[1] = 1; 交换过后就变成了:
这时候就发现基准数已经出现在了它排完序后应该在的位置(排完序后是[1,2,3,4,5,6],2出现在了第2位),比这个基准数小的数组出现在了它的左边([1]出现在了2的左边),比基准数大的出现在了它的右边([3,5,6,4]出现在了2的右边)。
4.之后的过程就是对左右数组的分别递归处理。
function quickSort(arr, begin, end) {
//递归出口
if(begin >= end)
return;
var l = begin; // 左指针
var r = end; //右指针
var temp = arr[begin]; //基准数,这里取数组第一个数
//左右指针相遇的时候退出扫描循环
while(l < r) {
//右指针从右向左扫描,碰到第一个小于基准数的时候停住
while(l < r && arr[r] >= temp)
r --;
//左指针从左向右扫描,碰到第一个大于基准数的时候停住
while(l < r && arr[l] <= temp)
l ++;
//交换左右指针所停位置的数
[arr[l], arr[r]] = [arr[r], arr[l]];
}
//最后交换基准数与指针相遇位置的数
[arr[begin], arr[l]] = [arr[l], arr[begin]];
//递归处理左右数组
quickSort(arr, begin, l - 1);
quickSort(arr, l + 1, end);
} var arr = [2,3,4,1,5,6]
quickSort(arr, 0, 5);
console.log(arr)
百科上的思路
百科上的思路跟上述某乎文章基本一致,不过再细节方面不同,这里主要讲已将它们不同的地方,详情请参考原文。(需注意之处也在和上文相同不在赘述)
主要的不同之处在于再上述2,3步骤。
百科上给的方式是:假设让右指针先扫,扫到了比基准数小的,就讲该数与基准数值交换位置,此时左指针指向基准数,再让左指针往右扫描,扫到比基准数大的交换左右指针数值,两指针相遇时直接退出这次递归,通过这样的的方式来达到第一次递归的目的。
上文中则是:假设让右指针先扫,扫到了比基准数小的,指针停住,再让左指针往右扫描扫到比基准数大的数再停住,然后交换两指针指向的值,反复调用,两指针相遇时与基准数的数值进行交换。
相对于理解来说我认为是,百科的方式更容易理解(其实是我先理解了百科的方式让后想到了自己的思路,最后才理解了某乎的方式)。
const quickSort = (array) => {
const sort = (arr, left = 0, right = arr.length - 1) => {
if (left >= right) {//如果左边的索引大于等于右边的索引说明整理完毕
return
}
let i = left
let j = right
const baseVal = arr[j] // 取无序数组最后一个数为基准值
while (i < j) {//把所有比基准值小的数放在左边大的数放在右边
while (i < j && arr[i] <= baseVal) { //找到一个比基准值大的数交换
i++
}
arr[j] = arr[i] // 将较大的值放在右边如果没有比基准值大的数就是将自己赋值给自己(i 等于 j)
while (j > i && arr[j] >= baseVal) { //找到一个比基准值小的数交换
j--
}
arr[i] = arr[j] // 将较小的值放在左边如果没有找到比基准值小的数就是将自己赋值给自己(i 等于 j)
}
arr[j] = baseVal // 将基准值放至中央位置完成一次循环(这时候 j 等于 i )
sort(arr, left, j-1) // 将左边的无序数组重复上面的操作
sort(arr, j+1, right) // 将右边的无序数组重复上面的操作
}
const newArr = array.concat() // 为了保证这个函数是纯函数拷贝一次数组
sort(newArr)
return newArr
}
性能
既然这里给出了三种方式来实现快排,那我们就来测试一下性能
由于百科的方法有问题再5位数以上会报错10000后面不测试百科方法
第一个数我的方法
在1000个相同随机数的情况下
在100000个相同随机数的情况下
结论
从性能上讲是某乎的方法更高。
附测试代码
// 我的方法
let myQuickSort = (array) => {
if (array.length < 2) return array;
let leftArray = [];
let rightArray = [];
let baseDigit = array[0];
array.forEach(element => {
if (element < baseDigit) {
leftArray.push(element);
} else if (element > baseDigit) {
rightArray.push(element);
}
});
return myQuickSort(leftArray).concat(baseDigit, myQuickSort(rightArray))
};
// 某乎的方法
let moHu = (arr, begin, end) => {
//递归出口
if (begin >= end)
return;
var l = begin; // 左指针
var r = end; //右指针
var temp = arr[begin]; //基准数,这里取数组第一个数
//左右指针相遇的时候退出扫描循环
while (l < r) {
//右指针从右向左扫描,碰到第一个小于基准数的时候停住
while (l < r && arr[r] >= temp)
r--;
//左指针从左向右扫描,碰到第一个大于基准数的时候停住
while (l < r && arr[l] <= temp)
l++;
//交换左右指针所停位置的数
[arr[l], arr[r]] = [arr[r], arr[l]];
}
//最后交换基准数与指针相遇位置的数
[arr[begin], arr[l]] = [arr[l], arr[begin]];
//递归处理左右数组
moHu(arr, begin, l - 1);
moHu(arr, l + 1, end);
};
//百科的方法
let baiKe = (array) => {
let sort = (arr, left = 0, right = arr.length - 1) => {
if (left >= right) {//如果左边的索引大于等于右边的索引说明整理完毕
return
}
let i = left;
let j = right;
const baseVal = arr[j];// 取无序数组最后一个数为基准值
while (i < j) {//把所有比基准值小的数放在左边大的数放在右边
while (i < j && arr[i] <= baseVal) { //找到一个比基准值大的数交换
i++
}
arr[j] = arr[i]; // 将较大的值放在右边如果没有比基准值大的数就是将自己赋值给自己(i 等于 j)
while (j > i && arr[j] >= baseVal) { //找到一个比基准值小的数交换
j--
}
arr[i] = arr[j] // 将较小的值放在左边如果没有找到比基准值小的数就是将自己赋值给自己(i 等于 j)
}
arr[j] = baseVal; // 将基准值放至中央位置完成一次循环(这时候 j 等于 i )
sort(arr, left, j - 1); // 将左边的无序数组重复上面的操作
sort(arr, j + 1, right) // 将右边的无序数组重复上面的操作
};
const newArr = array.concat();// 为了保证这个函数是纯函数拷贝一次数组
sort(newArr);
return newArr
};
// 生成一个1-count的随机数组
let createTestArray = (count) => {
let temArray = [];
while (count > 0) {
temArray.unshift(count);
count--;
}
let i = temArray.length;
while (i) {
let j = Math.floor(Math.random() * i--);
[temArray[j], temArray[i]] = [temArray[i], temArray[j]];
}
return temArray;
};
// 测试
let testQuickSort = (name, func, arr, moHu) => {
if (!!moHu) {
console.time(name);
func(arr, moHu.begin, moHu.end);
console.timeEnd(name);
return;
}
console.time(name);
func(arr);
console.timeEnd(name);
};
// 生成1-100000的随机数组
const testArray = createTestArray(100000);
testQuickSort('myQuickSort', myQuickSort, testArray);
testQuickSort('moHu', moHu, testArray, {begin: 0, end: 99999});
// testQuickSort('baiKe', baiKe, testArray);
最后
最后记一笔,快速排序是不稳定排序也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
文章连接:
某乎:微软前端社招笔试详解
百科:百度百科
聊一聊快速排序(Js)的更多相关文章
- 今天聊一聊nuxt.js(上)
背景 近期在做内部系统的重构,从一线业务彻底的重构,经过充分的考虑我们准备把这个项目打造成前台业务的试验站,比如ssr和一些其他的前沿技术的探索,积累充分的经验后待合适的契机应用到C端的项目中. 既然 ...
- 递归版快速排序-JS代码
"use strict" var arr1=[11,21,3,4,0]; function qSort(arr){ var mid,left,right,len,i,j,empty ...
- io.js的服務器突破
Node.js与io.js那些事儿 InfoQ中文站 05月20日 14:26 去年12月,多位重量级Node.js开发者不满Joyent对Node.js的管理,自立门户创建了io.js.io.js的 ...
- Underscore.js 常用类型判断以及一些有用的工具方法
1. 常用类型判断以及一些有用的工具方法 underscore.js 中一些 JavaScript 常用类型检查方法,以及一些工具类的判断方法. 首先我们先来谈一谈数组类型的判断.先贴出我自己封装好的 ...
- Node.js 模块化你所需要知道的事
一.前言 我们知道,Node.js是基于CommonJS规范进行模块化管理的,模块化是面对复杂的业务场景不可或缺的工具,或许你经常使用它,但却从没有系统的了解过,所以今天我们来聊一聊Node.js模块 ...
- 【跟着子迟品 underscore】常用类型判断以及一些有用的工具方法
Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中. 阅读一些著名框架类库的源码,就好像和一个个大师对 ...
- js 中的快速排序算法简单实现
对于快速排序,最早是在c++中看到,它是利用指针来交换顺序,其实无论哪种语言,原理 和 思想都是一样,然而真正用起来的时候就特别容易忽略一些事实,导致实现失败.废话少说,下面用js实现一下快速排序: ...
- JS实现冒泡排序,插入排序和快速排序(从input中获取内容)
以前参加面试的时候,被问到过让用JS实现一个快速排序,当时太年轻,并没有回答上来. 于是,这里便把三种排序都用JS来做了一下.结合html,从input文本框中获取输入进行排序. 关于这几种算法的原理 ...
- JS快速排序和去重
JS的快速排序和JS去重在面试的时候问的挺多的.下面是我对快速排序的理解,和快速排序,去重的代码. 1.什么是快速排序? 第一步: 快速排序就是去个中间值,把比中间值小的放在左边设为arrLeft,比 ...
随机推荐
- add two nums
问题描述: 给定两个链表,计算出链表对应位置相加的和,如果和大于10要往后进位.用链表返回结果.其实上是一种大数加法.可以把一个大数倒着写存入链表,然后两个链表相加就是所需要的大数相加的和 输入 2 ...
- lvs+keepalive实现主从效果,以及RS健康监测和tcp,udp实现非web的负载均衡
前面文章讲到了tcp和udp负载均衡,但是没有健康监测,这几天我优化了一下上次的操作.当然,我也是用的跨网段的通讯,因为线上业务主要是海外业务,所以做了iptables流量转发 IP: lvs-mas ...
- Ubuntu编译安装crtmp-server
下载源码 从GitHub上下载:https://github.com/j0sh/crtmpserver.git 编译安装 apt-get install cmake apt-get install l ...
- python笔记:#003#PyCharm 的初始设置
PyCharm 的初始设置(知道) 目标 恢复 PyCharm 的初始设置 第一次启动 PyCharm 新建一个 Python 项目 设置 PyCharm 的字体显示 PyCharm 的升级以及其他 ...
- 用分支限界法解决人员安排问题(Personnel assignment problem)
最近考期博主比较忙,先把思路简单说说,图和代码考完试补. 人员安排问题,即给出员工集合和工作集合,寻找最合理的安排. 对于员工集合P,员工集合会依据某个f来给出某种顺序,需要按该顺序P(i)进行工作安 ...
- GitHub学习笔记:本地操作
安装过程略,假设你已经注册好了Github, 已经有了一个准备好的程序.我们的一切工作都是基于Git Shell,与GUI客户端无关. 在使用前你先要配置好config中的几个内容,主要是你自己的个人 ...
- Flask信号和wtforms
一.信号 1.1.所有内置信号 request_started = _signals.signal('request-started') # 请求到来前执行 request_finished = _s ...
- Python_字符串的大小写变换
''' lower().upper().capitalize().title().swapcase() 这几个方法分别用来将字符串转换为小写.大写字符串.将字符串首字母变为大写.将每个首字母变为大写以 ...
- TCP连接和 time_wait、close_waite
TCP连接和 time_wait.close_waite tags:time_wait close_waite RST TCP 引言:前两天朋友公司的服务器垮掉了,最后查出的原因是发现大量的time_ ...
- Code Review Checklist
左按:当年需要一份详细的代码评审清单作参考,翻译了此文. 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] General Code Smoke Test 通用测试 Comm ...