JavaScript 排序集锦

JavaScript 之排序集锦

1⃣️ 快速排序

快速排序

单独开辟两个存储空间 leftright 来存储每次递归比 target 小和大的序列,每次递归直接返回 lefttargetright 拼接后的数组.
浪费大量存储空间,写法简单.

function quickSort(array) {
  if (array.length < 2) {
    return array;
  }
  const target = array[0];
  const left = [];
  const right = [];
  for (let i = 0; i < array.length; i++) {
    if (array[i] < target) {
      left.push(array[i]);
    } else {
      right.push(array[i]);
    }
  }
  return;
  quickSort(left).concat([target], quickSort(right));
}

2⃣️ 归并排序

利用归并的思想实现的排序方法。

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

  • 将已有序的子序列合并,得到完全有序的序列
  • 即先使每个子序列有序,再使子序列段间有序
  • 若将两个有序表合并成一个有序表,称为二路归并
归并排序
归并排序

分割数组时直接将数组分割为两个数组,合并时直接合并数组

function merge(front, end) {
  const temp = [];
  while (front.length && end.length) {
    if (front[0] < end[0]) {
      temp.push(front.shift());
    } else {
      temp.push(end.shift());
    }
  }
  while (front.length) {
    temp.push(front.shift());
  }
  while (end.length) {
    temp.push(end.shift());
  }
  return temp;
}

function mergeSort(array) {
  if (array.length < 2) {
    return array;
  }
  const mid = Math.floor(array.length / 2);
  const front = array.slice(0, mid);
  const end = array.slice(mid);
  return merge(mergeSort(front), mergeSort(end));
}

3⃣️ 选择排序

选择排序

每次循环选取一个最小的数字放到前面的有序序列中.

function selectionSort(array) {
  for (let i = 0; i < array.length - 1; i++) {
    let minIndex = i;
    for (let j = i + 1; j < array.length; j++) {
      if (array[j] < array[minIndex]) {
        minIndex = j;
      }
    }
    [array[minIndex], array[i]] = [array[i], array[minIndex]];
  }
  return array;
}

4⃣️ 插入排序

将左侧序列看成一个有序序列,每次将一个数字插入该有序序列。
插入时,从有序序列最右侧开始比较,若比较的数较大,后移一位。

插入排序
function insertSort(array) {
  for (let i = 1; i < array.length; i++) {
    let target = i;
    for (let j = i - 1; j >= 0; j--) {
      if (array[target] < array[j]) {
        [array[target], array[j]] = [array[j], array[target]];
        target = j;
      } else {
        break;
      }
    }
  }
  return array;
}

5⃣️ 冒泡排序

冒泡排序
  • 循环数组,比较当前元素和下一个元素,如果当前元素比下一个元素大,向上冒泡。
  • 这样一次循环之后最后一个数就是本数组最大的数。
  • 下一次循环继续上面的操作,不循环已经排序好的数。
  • 优化:当一次循环没有发生冒泡,说明已经排序完成,停止循环。
function bubbleSort(array) {
  for (let j = 0; j < array.length; j++) {
    let complete = true;
    for (let i = 0; i < array.length - j - 1; i++) {
      // 比较相邻数
      if (array[i] > array[i + 1]) {
        [array[i], array[i + 1]] = [array[i + 1], array[i]];
        complete = false;
      }
    }
    // 没有冒泡结束循环
    if (complete) {
      break;
    }
  }
  return array;
}

6⃣️ 堆排序

  • 创建一个大顶堆,大顶堆的堆顶一定是最大的元素。
  • 交换第一个元素和最后一个元素,让剩余的元素继续调整为大顶堆。
  • 从后往前以此和第一个元素交换并重新构建,排序完成。
堆排序
堆排序
// 构建大顶堆, 从第一个非叶子节点开始, 进行下沉操作.
function createHeap(array) {
  const len = array.length;
  const start = parseInt(len / 2) - 1;
  for (let i = start; i >= 0; i--) {
    adjust(array, i, len);
  }
}
// 将第target个元素进行下沉, 孩子节点有比他大的就下沉
function adjust(array, target, len) {
  for (let i = 2 * target + 1; i < len; i = 2 * i + 1) {
    // 找到孩子节点中最大的
    if (i + 1 < len && array[i + 1] > array[i]) {
      i = i + 1;
    }
    // 下沉
    if (array[i] > array[target]) {
      [array[i], array[target]] = [array[target], array[i]];
      target = i;
    } else {
      break;
    }
  }
}
function heapSort(array) {
  createHeap(array);
  // 交换第一个和最后一个元素, 然后重新调整大顶堆
  for (let i = array.length - 1; i > 0; i--) {
    [array[i], array[0]] = [array[0], array[i]];
    adjust(array, 0, i);
  }
  return array;
}

7⃣️ 希尔排序

希尔排序的核心在于间隔序列的设定.既可以提前设定好间隔序列,也可以动态的定义间隔序列.动态定义间隔序列的算法是《算法(第4版)》 的合著者 Robret Sedgewick 提出的.

  • 先将整个待排序记录序列分割成若干个子序列
  • 在序列内分别进行直接插入排序,待整个序列基本有序时,在对全体记录进行一次直接插入排序
希尔排序
希尔排序
function shellSort(array) {
  let len = array.length;
  let gap = parseInt(len / 2);
  let i, j, temp, result;
  // 复制数组
  result = array.slice(0);
  while (gap > 0) {
    for (i = gap; i < len; i++) {
      temp = result[i];
      j = i - gap;
      while (j >= 0 && temp < result[j]) {
        result[j + gap] = result[j];
        j = j - gap;
      }
      result[j + gap] = temp;
    }
    gap = parseInt(gap / 2);
  }
  return result;
}

8⃣️ 计数排序

  • 找出待排序的数组中最大和最小的元素;
  • 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;
  • 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加)
  • 反向填充目标数组:将每个元素 i 放在新数组的第 C(i)项,每放一个元素就将 C(i)减去 1
计数排序
计数排序
function countingSort(array, maxValue) {
  let bucket = new Array(maxValue + 1);
  let sortIndex = 0;
  let bucketLen = maxValue + 1;
  for (let i = 0; i < array.length; i++) {
    if (!bucket[array[i]]) {
      bucket[array[i]] = 0;
    }
    bucket[array[i]]++;
  }
  for (let j = 0; j < bucketLen; j++) {
    while (bucket[j] > 0) {
      array[sortIndex++] = j;
      bucket[j]--;
    }
  }
  return array;
}

9⃣️ 桶排序

  • 设置一个定量的数组当作空桶;
  • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
  • 对每个不是空的桶进行排序;
  • 从不是空的桶里把排好序的数据拼接起来。
桶排序
桶排序
function bucketSort(arr, bucketSize) {
  if (arr.length === 0) {
    return arr;
  }
  let minValue = arr[0];

  let maxValue = arr[0];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < minValue) {
      minValue = arr[i]; //输入数据的最小值
    } else if (arr[i] > maxValue) {
      maxValue = arr[i]; //输入数据的最大值
    }
  } //桶的初始化
  const DEFAULT_BUCKET_SIZE = 5; //设置桶的默认数量为5
  bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;

  let bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
  let buckets = new Array(bucketCount);

  for (let i = 0; i < buckets.length; i++) {
    buckets[i] = [];
  } //利用映射函数将数据分配到各个桶中
  for (let i = 0; i < arr.length; i++) {
    buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
  }

  arr.length = 0;

  for (let i = 0; i < buckets.length; i++) {
    insertionSort(buckets[i]); //对每个桶进行排序,这里使用了插入排序
    for (let j = 0; j < buckets[i].length; j++) {
      arr.push(buckets[i][j]);
    }
  }

  return arr;
}

🔟 基数排序

  • 该算法是稳定的排序法;
  • 在所有的情况下,时间复杂度均为 O(nlog(p)k),空间复杂度为 O(n*p)
    (其中 K 为关键词位数,p 为数据字符数(即基数 radix));
  • 若 n 很大,p 固定或很小,此方法将很有效。
  • 基数排序不需要进行元素间的比较,属于分配/分布排序;
  • 根据起始方向可分为 最高位优先 MSD 和 最低位优先 LSD
基数排序
基数排序
function radixSort(arr, n, radix) {
  if (!radix) {
    //默认为十进制
    radix = 10;
  }
  if (!n) {
    //如果未指定关键词位数将自动使用首个元素的位数作为n
    n = arr[0].toString().length;
  }
  var i,
    j,
    tmp,
    num,
    queues = [],
    q = arr.slice();
  for (i = 0; i < radix; i++) {
    queues.push([]); //生成Q[0]~Q[radix-1]
  }
  for (i = 0; i < n; i++) {
    //LSD低位起始
    while (q.length > 0) {
      tmp = q.shift();
      num = Math.floor((tmp / Math.pow(radix, i)) % radix); //获取某位数值
      //这里这个转换只能搞得动十进制= =其他就会有问题 因为不能直接用其他进制来进行运算 所以考虑使用字符串处理
      queues[num].push(tmp);
    }
    for (j = 0; j < radix; j++) {
      //重构q
      while (queues[j].length > 0) {
        q.push(queues[j].shift());
      }
    }
  }
  return q;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容

  • 十大经典排序算法 来源:https://github.com/wangguanfu/-Sorting-algori...
    星丶雲阅读 791评论 0 7
  • 该系列文章主要是记录下自己暑假这段时间的学习笔记,暑期也在实习,抽空学了很多,每个方面的知识我都会另起一篇博客去记...
    Yanci516阅读 12,212评论 6 19
  • 排序算法说明 (1)排序的定义:对一序列对象根据某个关键字进行排序; 输入:n个数:a1,a2,a3,…,an 输...
    code武阅读 658评论 0 0
  • 本文首发于我的个人博客:尾尾部落 排序算法是最经典的算法知识。因为其实现代码短,应该广,在面试中经常会问到排序算法...
    繁著阅读 4,572评论 3 119
  • C语言相比其他高级语言,就像内功和剑法一样。只会C语言可能并不能写出一个看起来就很厉害的程序,但C语言是你学好其他...
    C语言学习阅读 1,628评论 2 20