一、冒泡排序
算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
1、比较相邻的两个元素,如果前一个比后一个大,则交换位置。
2、比较完第一轮的时候,最后一个元素是最大的元素。
3、这时候最后一个元素是最大的,所以最后一个元素就不需要参与比较大小。
var arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length; j++) {
if (arr[j] > arr[j + 1]) {
var num = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = num
}
}
}
console.log(arr)
var x = [
['张三', 18, '1990-5-8'],
['李四', 22, '2020-5-8'],
['王五', 13, '1988-8-9']
]
function bubbleSort(arr, key) {
// key参数指定比较规则
for (var i = 0; i < arr.length - 1; i++) {
for (var j = 0; j < arr.length - 1 - i; j++) {
if (key) {
if (key(arr[j], arr[j + 1])) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
} else {
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
bubbleSort(x,function(a,b){return a[2]>b[2]})
console.log(x)
二、选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
1.假设未排序序列的第一个是最大值,记下该元素的位置,从前往后比较
2.若某个元素比该元素大,覆盖之前的位置
3.重复第二个步骤,直到找到未排序的末尾
4.将未排序元素的第一个元素和最大元素交换位置
5.重复前面几个步骤,直到所有元素都已经排序。
var x = [13,7,1,25,9,2]
for(var i=0;i<x.length-1;i++){
var select = i;
console.log('选择的索引:',i)
for(var j=i+1;j<x.length;j++){
if(x[select]>x[j]){
select=j
}
}
var temp = x[i]
x[i]=x[select]
x[select]=temp
console.log(x)
}
//选择的索引: 0
//(6) [1, 7, 13, 25, 9, 2]
//选择的索引: 1
//(6) [1, 2, 13, 25, 9, 7]
//选择的索引: 2
//(6) [1, 2, 7, 25, 9, 13]
//选择的索引: 3
//(6) [1, 2, 7, 9, 25, 13]
//选择的索引: 4
//(6) [1, 2, 7, 9, 13, 25]
三、快速排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
1.在数据集之中,选择一个元素作为”基准”(pivot)。
2.所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。
3.对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
var x = [13, 7, 1, 25, 9, 2]
function quickSort(arr) {
if (arr.length <= 1) {
return arr
} else {
var base = arr[0] // 基准值
var max_arr = []; //大的
var min_arr = []; //小的
var eq_arr = [arr[0]]; //等于的
for (var i = 1; i < arr.length; i++) {
if (base > arr[i]) {
min_arr.push(arr[i])
} else if (base < arr[i]) {
max_arr.push(arr[i])
} else {
eq_arr.push(arr[i])
}
}
return quickSort(min_arr).concat(eq_arr).concat(quickSort(max_arr))
}
}
console.log(quickSort(x))
四、插入排序
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤2~5。
var brr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
function insertionSort(arr) {
var len = arr.length;
var preIndex, current;
for (var i = 1; i < len; i++) {
preIndex = i - 1;
current = arr[i];
while (preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = current;
}
return arr;
}
console.log(insertionSort(brr))
五、归并排序
归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
1.把长度为n的输入序列分成两个长度为n/2的子序列
2.对这两个子序列分别采用归并排序
3.将两个排序好的子序列合并成一个最终的排序序列
function mergeSort(arr) { // 采用自上而下的递归方法
var len = arr.length;
if (len < 2) {
return arr;
}
var middle = Math.floor(len / 2),
left = arr.slice(0, middle),
right = arr.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {
var result = [];
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
六、希尔排序
希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
1.先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序
2.选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
3.按增量序列个数k,对序列进行k 趟排序;
4.每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
function shallSort(array) {
var increment = array.length;
var i
var temp; //暂存
var count = 0;
do {
//设置增量
increment = Math.floor(increment / 3) + 1;
for (i = increment ; i < array.length; i++) {
console.log(increment);
if (array[i] < array[i - increment]) {
temp = array[i];
for (var j = i - increment; j >= 0 && temp < array[j]; j -= increment) {
array[j + increment] = array[j];
}
array[j + increment] = temp;
}
}
}
while (increment > 1)
return array;
}
七、堆排序
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。
1.将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区
2.将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n]
3.由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成
var len;
function buildMaxHeap(arr) { //建堆
len = arr.length;
// [n/2-1]表示的是最后一个有子节点 (本来是n/2(堆从1数起),但是这里arr索引是从0开始,所以-1)
for (var i = Math.floor(len/2)-1; i>=0; i--) {
maxHeapify(arr, i);
}
//对每一个节点(非叶节点),做堆调整
}
function maxHeapify(arr, i) { //堆调整
var left = 2*i+1,
right = 2*i+2,
largest = i; //i为该子树的根节点
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) { //即上面的if中有一个生效了
swap(arr, i, largest); //交换最大的为父节点
maxHeapify(arr, largest); //交换后,原值arr[i](往下降了)(索引保存为largest),
//作为根时,子节点可能比它大,因此要继续调整
}
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function heapSort(arr) {
buildMaxHeap(arr);
for (var i = arr.length-1; i > 0; i--) {
swap(arr, 0, i);
len--;
maxHeapify(arr, 0);
}
return arr;
}
八、计数排序
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序
1.找出待排序的数组中最大和最小的元素;
2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
3.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
4.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
function countingSort(arr){
var len = arr.length,
Result = [],
Count = [],
min = max = arr[0];
console.time('countingSort waste time:');
/*查找最大最小值,并将arr数置入Count数组中,统计出现次数*/
for(var i = 0;i<len;i++){
Count[arr[i]] = Count[arr[i]] ? Count[arr[i]] + 1 : 1;
min = min <= arr[i] ? min : arr[i];
max = max >= arr[i] ? max : arr[i];
}
/*从最小值->最大值,将计数逐项相加*/
for(var j = min;j<max;j++){
Count[j+1] = (Count[j+1]||0)+(Count[j]||0);
}
/*Count中,下标为arr数值,数据为arr数值出现次数;反向填充数据进入Result数据*/
for(var k = len - 1;k>=0;k--){
/*Result[位置] = arr数据*/
Result[Count[arr[k]] - 1] = arr[k];
/*减少Count数组中保存的计数*/
Count[arr[k]]--;
/*显示Result数组每一步详情*/
console.log(Result);
}
console.timeEnd("countingSort waste time:");
return Result;
}
var arr = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(countingSort(arr));
九、基数排序
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
1.取得数组中的最大数,并取得位数;
2.arr为原始数组,从最低位开始取每个位组成radix数组;
3.对radix进行计数排序(利用计数排序适用于小范围数的特点)
'use strict'
var arr = [1010, 1000, 999, 666, 444, 333, 333, 123, 50, 22, 12, 10, 5, 2, 1, 0, 0];
function radixSort(arr, numLenght) {
function strLenght(str) {
var str = str.toString();
return str.length;
}
var resultArr = [],
count = 0,
mo = 10,
yu = 1;
// 控制 计数排序的次数
while (numLenght) {
// 总是初始化一个临时数组
let tmpArr = [];
var arrLenght = arr.length;
// arr 数组索引i值, 元素的最少长度 1
var i = 0, strL = 1;
// 计数排序
while (arrLenght) {
arrLenght--;
count += 1;
// 不足位的元素 用 0 补位
let index = Math.floor((arr[i] % mo) / yu);
// 等于0时, 已经有序. splice元素,并追加到排序好的数组末尾.
// 减少待排序元素个数, 优化性能.
if (index == 0) {
if (strL == strLenght(arr[i])) {
resultArr.push(arr.splice(i, 1)[0]);
continue
// splice 元素后,数组总长度减少1,索引i值 不增长, 跳出本次循环.
}
}
// 不是数组时, 初始化为空数组
if (!tmpArr[index]) {
tmpArr[index] = [];
}
// 二维数组 添加元素
tmpArr[index].push(arr[i]);
// arr 数组索引自增
i++;
}
console.log(count);
arr = [];
// 从二维数组中, 按顺序 push 到新空数组中, 准备下次 计数排序
for (let i = 0; i < tmpArr.length; i++) {
var innerArr = tmpArr[i];
if (innerArr) {
for (let v of innerArr) {
arr.push(v);
// count += 1;
}
}
}
mo *= 10, yu *= 10, numLenght--, strL++;
// 数字位数 自减到等于0时, arr 数组已经有序, 合并即可
if (numLenght == 0) {
resultArr = resultArr.concat(arr);
}
// console.log(resultArr);
}
console.log(count);
return resultArr;
}
console.log(radixSort(arr, 4));
十、桶排序
桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
1.设置一个定量的数组当作空桶
2.遍历输入数据,并且把数据一个一个放到对应的桶里去
3.对每个不是空的桶进行排序
4.从不是空的桶里把排好序的数据拼接起来
//插入排序
function insertion_sort(A){
for(let i=1; i<A.length; i++){
let p = i-1
const x = A[i]
while(p>=0 && A[p]>x){
A[p+1] = A[p]
p--
}
A[p+1] = x
}
}
//桶排序
function bucket_sort(A, k, s){ //A排序数组,k桶子数量,s桶子空间尺度
const buckets = Array.from({length:k}, ()=>[]) //创建桶子
//把元素放入对应桶子
for(let i=0; i<A.length; i++){
//计算需要放入桶子序号
const idx = ~~(A[i]/s)
buckets[idx].push(A[i])
}
//对每个桶子进行排序
for(let i=0; i<buckets.length; i++){
//此处选取插入排序, 空间消耗少,元素少常数时间消耗短
insertion_sort(buckets[i])
}
//把每个桶子数据合并
return [].concat(...buckets)
}
摘要链接:https://www.jianshu.com/p/ad45b42cc567