前天看到知乎上有一篇文章在吐槽阮一峰老师的快速排序算法,这里插一句题外话,我以为人非圣贤孰能无过,尽信书不如无书,学习的过程也就是不断发现错误改正错误的过程,有人帮咱们纠正了这个错误咱们应该开心,可是我以为不该该批判阮一峰老师,他也在不断地学习,不断地纠错成长,因此你们都同样,无所谓误导,若是出错的不是他,是更厉害的牛人呢?JavaScript的做者呢?因此你们都会出错,咱们也应该多思考,抱着怀疑的态度接纳,时刻思考这是否是最优的解法,还有没有更好的呢,我想这才是咱们应该作的.
而我,做为一个计算机专业的前端,却不能很好地实现各类思想的排序算法,我以为很惭愧,因此我就抽时间仔细查看了<<数据结构与算法分析:C语言描述+中文版.pdf>>这本书,下面我就对我理解的各类思想的排序算法作一下总结,但愿能够给你们一些参考和收获,若有不妥之处,烦请指出,也能够分享大家以为更好地想法,我以为你们一块儿学习一块儿进步是最快乐的事~前端
(1) 时间复杂度的概念
算法的时间复杂度是一个函数,他定性地描述了某个算法的运行时间,经常使用大O符号,不包括这个函数的低阶项和高阶项系数.
(2) 计算方法算法
举例以下:shell
for(i = 1; i<= n; i++) { for(j = 1; j <= n; j++) { c[i][j] = 0; // 该步骤属于基本操做的执行次数: n的平方 for( k= 1;k <= n; k++) { c[i][j] += a[i][k] * b[k][j]; // 该步骤属于基本操做的执行次数: n的三次方 } } }
咱们能够获得T(n) = n^3 + n^2,咱们能够肯定n^3为T(n)的同数量级,f(n)=n^3;而后T(n) / f(n) = 1 + 1/n 求极限为常数1,因此该算法的时间复杂度为:
T(n) = O(n^3);数组
说明: 为了方便我接下来都是使用N来代指数组元素个数的.
个人建议: 我建议你们先看代码,看不懂代码的时候对着代码看图解,这样方便更好的理解前端工程师
冒泡排序的主要思想就是对一个长度为n的数组进行遍历, i从n-1到1的,数组的前i个元素的最大值放在i位置上,假想冒泡排序是一个竖着的水柱,遍历的过程就是,大的值(重的)不断沉下来,小的值(轻的)不断浮上去,这样遍历结束后,每一个位置上的值都比他前面的值大,排序结束.数据结构
最坏状况下的时间复杂度: o(n^2);
最好状况下的时间复杂度: o(n^2);dom
function bubbleSort(arr) { for(var i = arr.length - 1; i > 1; i--) { for(var j=0; j < i; j++) { if(arr[j] > arr[j+1]) { var temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } return arr; } var arr = [34,8,64,51,32,21]; bubbleSort(arr); // [8, 21, 32, 34, 51, 64]
function bubbleSort(arr, n) { if(n <= 1) { return arr; } else { for(var j=0; j < n - 1; j++) { if(arr[j] > arr[j+1]) { var temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } return bubbleSort(arr, --n); } } var arr = [34,8,64,51,32,21]; bubbleSort(arr, arr.length); // [8, 21, 32, 34, 51, 64]
选择排序的主要思想就是i 从 0 循环到到length - 1, 依次找出待排数组中从 i 到 length - 1 位置上的最小值放在 i 位置上.这样最后获得的数组就是排好序的数组了.函数
最坏状况下的时间复杂度: o(n^2);
最好状况下的时间复杂度: o(n^2);post
function selectSort(arr) { var len = arr.length, min = 0; for(var i = 0;i < len - 1; i++) { min = i; // 默认最小值的位置 for(var j = i + 1; j < len; j++){ if(arr[min] > arr[j]) { min = j; } } if(min != i) { var temp = arr[min];arr[min] = arr[i]; arr[i] = temp; } } return arr; } var arr = [34,8,64,51,32,21]; selectSort(arr);
function selectSort(arr, n, min) { var len = arr.length; if(n < len - 1) { for(var j = n + 1; j < len; j++){ if(arr[min] > arr[j]) { min = j; } } if(min != n) { var temp = arr[min];arr[min] = arr[n]; arr[n] = temp; } n++; return selectSort(arr, n, min); } return arr; } var arr = [34,8,64,51,32,21]; selectSort(arr, 0, 0);
插入排序有 n-1 趟排序组成,对于 i=1 到 i=n-1 趟,内层循环j从 i 到 1, 若是这其中有 j-1 位置上的元素大于 i 位置上的元素,就将该元素后移,知道条件不成立退出循环,这个时候大的值都被移动到后面了,j这个位置就是i位置上的元素应该在的位置.这样保证了每次循环i位置前的全部元素都是排好序的,新的循环就只须要 将 i 位置上的元素 和 j-1(也就是初始的 i-1) 位置上的元素做比较,若是大于则无需再往前比较,若是小于则继续往前比较后移.学习
最坏状况下的时间复杂度: o(n^2);
最好状况下的时间复杂度: o(n);
function insertSort(arr) { var n = arr.length,temp = 0; for(var i = 1; i < n; i++) { temp = arr[i]; for(j = i; j > 0 && arr[j-1] > temp; j--) { arr[j] = arr[j - 1]; } arr[j] = temp; } return arr; } var arr = [34,8,64,51,32,21]; insertSort(arr); // [8, 21, 32, 34, 51, 64]
function insertSort(arr, n) { if(n > 0 && n < arr.length){ var i = j = n, temp = arr[n]; while(j > 0 && arr[j - 1] > temp) { arr[j] = arr[j - 1]; j--; } arr[j] = temp; i++; return insertSort(arr, i); } return arr; } var arr = [34,8,64,51,32,21]; insertSort(arr, 1); // [8, 21, 32, 34, 51, 64]; // 这个函数的调用限定了第一次调用n的值只能传1
顾名思义,快速排序是在实践中最快的已知排序算法,它的平均运行时间是O(Nlog₂N).快速排序的关键在于枢纽元的选取,有一种比较推荐的选取方法就是选取左端的值,右端的值,中间位置的值(L(left + right) / 2)这三个数的中位数.举例: 输入为8,1,4,9,6,3,5,2,7,0, 左边元素8, 右边元素0,中间位置上的元素L(0+9)/2是4位置上的元素是6,L在表示向下取整.
8,0,6的中位数,先排序0,6,8, 这三个数的中位数是6.
经过一趟排序将要排序的部分分割成独立的两部分,其中一部分数据都比另一部分的全部数据都要小,而后再按此方法对这两部分数据进行快速排序,整个排序过程能够递归进行,依次达到整个数据变成有序序列.
实现步骤
平均状况下的时间复杂度: o(nlog₂n);
最好状况下的时间复杂度: o(n);
function quickSort(arr, left, right) { if(left >= right) return; var i = left; var j = right - 1; var privot = arr[left]; //console.log(privot); while(i < j) { while(i<j && arr[j] >= privot) j--; arr[i] = arr[j]; while(i<j && arr[i] <= privot) i++; arr[j]=arr[i]; } arr[i]=privot; quickSort(arr, left, i); quickSort(arr, i+1, right); } var arr = [49,38,65,97,76,13,27,49,55,04]; quickSort(arr, 0, arr.length);
function mainProduce(arr, left, right) { var i = left, j = right - 1; var rendomIndex = Math.floor(Math.random() * (j - i)) + left; var temp = arr[left];arr[left] = arr[rendomIndex];arr[rendomIndex] = temp; var privot = arr[left]; while(i < j) { while(i<j && arr[j] >= privot) j--; var temp = arr[i];arr[i] = arr[j];arr[j] = temp; while(i<j && arr[i] <= privot) i++; var temp = arr[j];arr[j] = arr[i];arr[i] = temp; } arr[i]=privot; return i; } function quickSort(arr, left, right) { var s = []; if(left < right) { var mid = mainProduce(arr, left, right); if(mid > left + 1) { s.push(left);s.push(mid); } if(mid < right - 1) { s.push(mid + 1);s.push(right); } while(s.length !== 0) { right = s.pop(); left = s.pop(); mid = mainProduce(arr, left, right); if(mid > left + 1) { s.push(left);s.push(mid); } if(mid < right - 1) { s.push(mid + 1);s.push(right); } } } return arr; } var arr = [49,38,65,97,76,13,27,49,55,04]; quickSort(arr, 0, arr.length);
希尔排序是把记录按照下标的必定增量分组,对每组使用插入排序;随着增量逐渐减小,分割的数组愈来愈大,当增量减至1,整个数组排序完成,算法终止.
主要步骤
希尔排序是不稳定的,它在不断地交换的过程当中会改变原来相等的元素的顺序.
平均状况下的时间复杂度: o(nlog₂n);
最好状况下的时间复杂度: o(n);
图片源于自百度百科: 图片来源
function shellSort(arr, increment) { var len = arr.length; if(increment > 0) { for(var i = increment; i < len; i++) { for(var j = i - increment; j >= 0 && arr[j] > arr[j + increment]; j -= increment) { var temp = arr[j]; arr[j] = arr[j + increment]; arr[j + increment] = temp; } } return shellSort(arr, Math.floor(increment/2)); } return arr; } var arr = [49,38,65,97,76,13,27,49,55,04]; shellSort(arr, Math.floor(arr.length / 2));
function shellSort(arr) { var len = arr.length; for(var increment = Math.floor(len / 2); increment > 0; increment = Math.floor(increment / 2)) { for(var i = increment; i < len; i++) { for(var j = i - increment; j >= 0 && arr[j] > arr[j + increment]; j -= increment) { var temp = arr[j]; arr[j] = arr[j + increment]; arr[j + increment] = temp; } } } return arr; } var arr = [49,38,65,97,76,13,27,49,55,04]; shellSort(arr);
希尔排序的主要思想就是递归将数组层层分割,直到分割成最小的单元,而后再比较,提供一个新的空数组arrayC,将分割的左右两个数组中小的数放进数组,而后再层层回溯向上合并.获得最终的arrayC就是排序后的数组.
平均状况下的时间复杂度: O(nlog₂n);
最好状况下的时间复杂度: O(nlog₂n) ;
var result = []; function mergeArray(left, right) { result = []; while(left.length > 0 && right.length > 0) { if(left[0] < right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } return result.concat(left).concat(right); } function mergerSort(arr) { if(arr.length <= 1) { return arr; } var middle = Math.floor(arr.length / 2); var left = arr.slice(0, middle); var right = arr.slice(middle); return mergeArray(mergerSort(left), mergerSort(right)); } var arr = [49,38,65,97,76,13,27,49,55,04]; mergerSort(arr, 0, arr.length);
因为归并排序的非递归实现比较复杂,我这里就不作讲解了,我以为若是真的须要用到,读者可自行研究.
这是我写的最用心的一篇博客了,万事开头难,我已经开头了,就是一种突破.但愿我能够继续坚持下去,不断充电,不断输出,成为一个优秀的前端工程师,加油 ^-^ ^-^.
欢迎帮我纠正错误和有疑问的人与我交流, it will be my pleasure. 个人qq号: 2510909248.
推荐阅读
1) 十大经典排序算法总结(JavaScript描述)