基本排序算法
冒泡排序
冒泡排序,它是最慢的排序算法之一,但也是一种最容易实现的排序。
之所以叫冒泡排序是因为使用这种排序算法时,数据值会像气泡一样从数组的一端漂浮到另一端。假设长在将一组数字按照升序排列,较大的值会浮动到数组的右侧,而较小的值则会浮动到数组的左侧。之所以会产生这种现象是因为算法会多次在数组中移动,比较相邻的数据,当左侧值大于右侧值时将它们进行交换
/** * 冒泡排序 * 解析: * 1、比较相邻的两个元素,如果前一个比后一个大,则交换位置 * 2、第一轮的时候最后一个元素应该是最大的一个 * 3、按照步骤一的方法进行相邻两个元素的比较,这个时候由于最后一个元素已经是最大的了,所以最后一个元素不用比较 * 缺点:效率最低的排序,但也是最容易实现的排序算法 */ function swap(arr, index1, index2) { var temp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = temp; } function bubbleSort(arr){ console.time('冒泡排序耗时'); var numElements = arr.length; var temp; for(var outer = numElements; outer > 1; outer--){ for(var inner = 0; inner < outer ; inner++){ if(arr[inner] > arr[inner + 1]){ swap(arr, inner, inner + 1); } } } console.timeEnd('冒泡排序耗时'); return arr; } var arr = [5,9,7,2,0,5,3,6,1,10]; console.log(arr.join(',')); console.log(bubbleSort(arr).join(',')
选择排序
选择排序从数组的开头开始,将第一个元素和其他元素进行比较。检测完所有元素后,最小的元素会被放到数组的第一个位置,然后算法会从第二个位置继续。这个过程一直进行排序,当进行到数组的倒数第二个位置时,所有的数据便完成了排序
选择排序会到嵌套循环。外循环从数组的第一个元素移动到倒数第二个元素;内循环从第二个数组元素到最后一个元素,查找比当前外循环所指向的元素小的元素。每次内循环迭代后,数组最小的值会被赋值到合适的位置
/**
* 选择排序 * 解析: * 首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。 * 以此类推,直到所有元素均排序完毕。 * * 缺点:选择排序每次交换的元素都有可能不是相邻的, 因此它有可能打破原来值为相同的元素之间的顺序. * 比如数组[2,2,1,3], 正向排序时, 第一个数字2将与数字1交换, 那么两个数字2之间的顺序将和原来的顺序不一致, * 虽然它们的值相同, 但它们相对的顺序却发生了变化. 我们将这种现象称作 不稳定性 * 对比: * 1、与冒泡排序相比,选择排序最多只交换一次,冒泡排序会交换n次,不考虑其他因素,选择排序的速度是冒泡排序的7倍, * 如果完全有序的情况下,冒泡排序不进行交换,选择排序会与自身交互,从这个角度上是冒泡排序,既然有序,为何要 * 排序,所以从算法上看,选择排序优于冒泡排序 */ function swap(arr, index1, index2) { var temp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = temp; } function selectionSort(arr){ console.time('选择排序耗时'); var min; for(var outer = 0; outer <arr.length - 1; outer++){ min = outer; for(var inner = outer + 1; inner < arr.length; inner++){ if(arr[inner] < arr[min]){ min = inner; } } swap(arr, outer, min); } console.timeEnd('选择排序耗时'); return arr; } var arr = [5,9,7,2,0,5,3,6,1,10]; console.log(arr.join(',')); console.log(selectionSort(arr).join(','));
插入排序
插入排序有两个循环。外循环将数组元素挨个移动,而内循环则对外循环中选中对元素及它后面对那个元素进行比较。如果外循环中选中对元素比内循环中选中对元素小,那么数组元素会向右移动,为内循环中这个元素腾出位置
/** * 四、插入排序 * 解析: * 1、从第一个元素开始,该元素可以认为已经被排序 * 2、取出下一个元素,在已经排序的元素序列中从后向前扫描 * 3、如果该元素(已排序)大于新元素,将该元素移到下一位置 * 4、重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 * 5、将新元素插入到下一位置中 * 6、重复步骤2 * * 对比: * 1、插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂 */ function insertSort(arr){ var temp, inner; for(var outer = 1; outer < arr.length; outer++){ temp = arr[outer]; inner = outer; while(inner > 0 && (arr[inner - 1] >= temp)){ arr[inner] = arr[inner - 1]; inner--; } arr[inner] = temp; } } var arr = [5,9,7,2,0,5,3,6,1,10]; console.log(arr.join(',')); console.log(insertSort(arr).join(','));
基本排序算法对计时比较
这三种排序算法对复杂度非常相似,从理论上来讲,它们对执行效率也应该差不多,要确定这三种算法对性能差异,我们可以使用一个非正式对计时系统来比较它们对数据集合进行排序所花费的事件。能够对算法进行计时非常重要,因为,对100个或对1000个元素进行排序时,你看不出这些排序算法对差异。但是如果对上百万元素进行排序,这些排序算法之间可能存在巨大的不同
var numElements = 100; var arr = new CArray(numElements); setData(numElements,arr); console.log(arr.join(',')); console.log(bubbleSort(arr).join(','));
function CArray(numElements) { var dataStore = []; for ( var i = 0; i < numElements; ++i ) { dataStore[i] = i; } return dataStore; } function setData(numElements,arr) { for ( var i = 0; i < numElements; ++i ) { arr[i] = Math.floor(Math.random() * (numElements + 1)); } }
取10000个数字,以5组数据对比,冒泡排序平均值是845~853ms,选择排序是61~66ms,插入排序是35~43ms,当数据量大的时候,性能就很明显了