摘要:因为是在已经分组排序过的基础上进行插入排序,所以效率高。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。形成左右两个分区,再递归按之前的步骤排序。
算法复杂度
不是科班生的我,第一次看见时间复杂度之类的名词表示很懵逼,所以就找了网上的资料补习了下:
时间复杂度:是指执行算法所需要的计算工作量
空间复杂度:是指算法在计算机内执行时所需存储空间的度量
排序算法稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
这里不详细说
参考:算法的时间复杂度和空间复杂度-总结、理解排序算法的稳定性、算法和算法的复杂度
排序算法名词解释:
n:数据规模
k:“桶”的个数
In-place:占用常数内存,不占用额外内存
Out-place:占用额外内存
冒泡排序下面的算法实现升序
顾名思义,从第一个开始比较相邻的两个,小的数网上冒。
实现function bubleSort (arr) { var len = arr.length; var temp; for (var i=0; i选择排序arr[j+1]) { temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } return arr }
假设第一个最小, 往后寻找比它小的,记下其index,一轮结束后将index上的数与开始假定的数交换位置。
实现function selectionSort (arr) { var len = arr.length; var minIndex, temp; for (var i=0; i插入排序 直接插入排序arr[j]) { minIndex = j } } temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } return arr; }
打扑克的同志应该比较好理解。假设第一个元素是已经排序好的,将后一个元素提取出来,往前依次比较,比自己大的数往后挪,插入到第一次遇见比自己小的元素后面,组成一个新的序列。
function insertionSort (arr) { var len = arr.length; var current, preIndex; for (var i=1; i希尔排序=0 && current < arr[preIndex]) { arr[preIndex+1] = arr[preIndex]; preIndex--; } arr[preIndex+1] = current; } return arr }
实质为分组插入排序。为了方便理解,借用网上某哥的图,参考链接在下文。
因为是在已经分组排序过的基础上进行插入排序,所以效率高。
//因为核心是插入排序,所以我们改造直接插入排序 function directInsertionSort(arr, gap) { var len = arr.length; var current, preIndex; for (var i=gap; i=0 && arr[preIndex] > current) { arr[preIndex+gap] = arr[preIndex]; preIndex -= gap; } arr[preIndex+gap] = current; } return arr; } //编写希尔排序函数 function shellSort (arr) { var len = arr.length; var gap = 1; //设置gap(希尔增量),这里我们给出比较常用的h值为h = 3 * h + 1 while (gap < len/3) { gap = gap * 3 + 1; } for (gap; gap>0; gap = Math.floor(gap/3)) { directInsertSort(arr, gap); } return arr; }
遇见的问题,关于参数的传递:函数参数的传递可以分为按值传递和引用传递。
步长序列可以看一下wiki
类似直接插入,后一个元素(拿来比较的元素)与已排序的中间值m = (i-1) >> 1(位移运算,相当于Math.floor((i-1)/2))进行比较,如果i上的值大于m上的值,则与高半区折半比较,最终将比i上值高的区域往后移,将i上的值插入。如
arr = [2, 6, 7, 6, 8] //前三个是已经排好的。 //range = [low, high] = [0, 2], i = 3, current = arr[i] // m = 1, arr[i] >= arr[m], rang = [2, 2] // m = 2, arr[i] < arr[m] // 变换位置 ==> arr = [2, 6, 6, 7, 8] ... ...
function binaryInsertionSort (arr) { var len = arr.length; var low, height, current, m; for (var i=1; i归并排序> 1; if (current >= arr[m]) {// = 是为了保证稳定性 low = m + 1; }else { height = m - 1; } } for (var j=i; j>low; j--) { arr[j] = arr[j-1]; } arr[low] = current; } return arr; }
采取分而治之的思想。递归分组、比较产生两个已排序序列,再依次比较两组开头元素,较小的元素放入申请的新数组中。
归并函数可以通过递归、迭代实现。
主要做的两件事就是分解、合并(下面并不是按照执行顺序,只是思路):
[3, 5, 6, 2, 9] -------------------------------------- 分: [3, 5] [6, 2, 9] [3] [5] [6] [2, 9] [2] [9] -------------------------------------- 合: [2, 9] [3, 5] [2, 6, 9] [2, 3, 5, 6, 9]
function merge (left, right) { var result = []; while (left.length && right.length) { var item = left[0] <= right[0] ? left.shift() : right.shift(); result.push(item); } return result.concat(left.length ? left : right); } function mergeSort (arr) { var len = arr.length; if (len === 1) { return arr; } var m = len >> 1; var left = arr.slice(0, m); var right = arr.slice(m); return merge(mergeSort(left), mergeSort(right)) }
递归可能会造成堆栈溢出的问题。
迭代主要做的两件事就是分解、合并(下面并不是按照执行顺序,只是思路):
[3, 5, 6, 2, 9] -------------------------------------- 分: [3] [5] [6] [2] [9] -------------------------------------- 合: [3, 5] [2, 6] [9] [2, 3, 5, 6] [9] [2, 3, 5, 6, 9]
function merge (left, right) { var result = []; while (left.length && right.length) { var item = left[0] <= right[0] ? left.shift() : right.shift(); result.push(item); } return result.concat(left.length ? left : right); } function mergeSort (arr) { var len = arr.length; var result = []; //分组,每组有i个元素 for (var i=1; i<=len; i*=2) { //比较相邻两组,有一组剩余就退出 for (var j=0; j+i快速排序 快速排序是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
实现
步骤:选择一个元素作为基准,下面选择第一个,依次比较后面的元素,将小于基准的元素放在基准的左边,剩余放右边。形成左右两个分区,再递归按之前的步骤排序。function swap (arr, i, j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } function partition (arr, left, right) { var pivot = left; var index = left + 1; for (var i=index; i<=right; i++) { if (arr[i] < arr[pivot]) { swap(arr, index, i); index++; } } swap(arr, index-1, pivot); return index-1 } function quickSort (arr, left, right) { var len = arr.length; var partitionIndex; left = typeof left === "number" ? left : 0; right = typeof right === "number" ? right : len-1; if (left < right) { partitionIndex = partition (arr, left, right); quickSort(arr, left, partitionIndex-1); quickSort(arr, partitionIndex+1, right); } return arr; }快速排序排序效率非常高. 虽然它运行最糟糕时将达到O(n²)的时间复杂度, 但通常, 平均来看, 它的时间复杂为O(nlogn), 比同样为O(nlogn)时间复杂度的归并排序还要快. 快速排序似乎更偏爱乱序的数列, 越是乱序的数列, 它相比其他排序而言, 相对效率更高.
Chrome的v8引擎为了高效排序, 在排序数据超过了10条时, 便会采用快速排序. 对于10条及以下的数据采用的便是插入排序.
参考十大经典排序算法
JS中可能用得到的全部的排序算法
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88256.html
摘要:栈被称为一种后入先出的数据结构。散列使用的数据结构叫做散列表。这些操作需要求助于其他数据结构,比如下面介绍的二叉查找树。 前言 在过去的几年中,得益于Node.js的兴起,JavaScript越来越广泛地用于服务器端编程。鉴于JavaScript语言已经走出了浏览器,程序员发现他们需要更多传统语言(比如C++和Java)提供的工具。这些工具包括传统的数据结构(如链表,栈,队列,图等),...
摘要:常见的内部排序算法有插入排序希尔排序选择排序冒泡排序归并排序快速排序堆排序基数排序等。这里主要介绍选择排序。一图胜千言算法描述选择排序是一种简单直观的排序算法,无论什么数据进去都是的时间复杂度。重复第二步,直到所有元素均排序完毕。 常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。这里主要介绍选择排序。 一图胜千言: showImg(...
摘要:高级排序算法总结希尔排序间隔序列可以动态定义,不过对于大部分的实际应用场景,算法要用到的间隔序列可以提前定义好有一些公开定义的间隔序列,使用它们会得到不同的结果。 高级排序算法总结 希尔排序 function shellsort(array, gaps) { for (var g = 0; g < gaps.length; g++) { for ...
摘要:散列表上面的地图向我们展示了如何用广度优先搜索的思想找到北京到广州的最短路线。在广度优先搜索中,我们需要用到队列的这种思想来实现查找。建立了下面这个模型武汉广州西藏上海上海武汉广州代码完整实现,利用递归和广度优先搜索的思想实现。 什么是广度优先搜索? 如果只是是背概念,幼儿园的小朋友都能背下来念给你听。 假设看这篇文章的都和我一样是个前端工程师,我们要从广度优先搜索(BFS)中学到什么...
阅读 3563·2021-10-15 09:43
阅读 3486·2021-09-02 15:21
阅读 2192·2021-08-11 11:23
阅读 3237·2019-08-30 15:54
阅读 1922·2019-08-30 13:54
阅读 3199·2019-08-29 18:35
阅读 667·2019-08-29 16:58
阅读 1735·2019-08-29 12:49