常用排序算法(JavaScript)

本文详细介绍并对比了多种经典的排序算法,包括冒泡排序、选择排序、插入排序、归并排序、堆排序、快速排序和希尔排序。每种算法均附有实现代码及运作流程解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录:

1.冒泡排序
2.选择排序
3.插入排序
4.归并排序
5.堆排序
6.快速排序
7.希尔排序

冒泡排序:

冒泡排序算法的运作如下:

1. 比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。

2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3. 针对所有的元素重复以上的步骤,除了最后一个。

4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

冒泡排序的代码如下:

 function BubbleSort(arr){

        var len = arr.length;
        for(var i=0;i<len-1;i++){  //控制比较的趟数
            for(var j=0;j<len-i-1;j++){  //控制每一趟比较的次数
                if(arr[j] > arr[j+1]){
                    var temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }


选择排序:

选择排序也是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。(找到最小的元素放到第一位,再找到第二小的元素放到第二位依次类推。。。)

选择排序的代码如下:

function SelectionSort(arr){
var len = arr.length;
for(var i=0;i<len-1;i++){
for(var j=i+1;j<len;j++){
if(arr[i]>arr[j]){
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;

}


插入排序:

具体算法描述如下:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5

插入排序的代码如下:

function InsertionSort(arr){
    var len = arr.length;
   for (var i = 1; i < len; i++){         // 类似抓扑克牌排序
       var get = arr[i];                 // 右手抓到一张扑克牌
       var j = i - 1;                  // 拿在左手上的牌总是排序好的
       while (j >= 0 && arr[j] > get)    // 将抓到的牌与手牌从右向左进行比较
       {
           arr[j + 1] = arr[j];            // 如果该手牌比抓到的牌大,就将其右移
           j--;
       }
       arr[j + 1] = get; // 直到该手牌比抓到的牌小(或二者相等),将抓到的牌插入到该手牌右边(相等元素的相对次序未变,所以插入排序是稳定的)
   }
   return arr;

}


归并排序:

 归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

  归并排序的代码如下:

  function MergeSort(arr1,arr2){
     var  len1 = arr1.length,
         len2 = arr2.length,
         i = 0,
         j = 0,
         k = 0,
         arr = Array(len1+len2);
      while (i<len1 && j<len2) {
          if(arr1[i] < arr2[j]){
              arr[k++] = arr1[i++];
          }else {
              arr[k++] = arr2[j++];
          }
      }
      while (i<len1){
          arr[k++] = arr1[i];
      }
      while (j<len2){
          arr[k++] = arr2[j++];
      }
      return arr;
  }


堆排序:
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。


同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子


我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

构造大根堆:我们从最后一个非叶子结点开始,从左至右,从下至上进行调整。

代码如下:

function headAdjust(elements, pos, len){
  //将当前节点值进行保存
  var swap = elements[pos];

  //定位到当前节点的左边的子节点
  var child = pos * 2 + 1;

  //递归,直至没有子节点为止
  while(child < len){
    //如果当前节点有右边的子节点,并且右子节点较大的场合,采用右子节点
    //和当前节点进行比较
    if(child + 1 < len && elements[child] < elements[child + 1]){
      child += 1;
    }

    //比较当前节点和最大的子节点,小于则进行值交换,交换后将当前节点定位
    //于子节点上
    if(elements[pos] < elements[child]){
      elements[pos] = elements[child];
      pos = child;
      child = pos * 2 + 1;
    }
    else{
      break;
    }

    elements[pos] = swap;
  }
}

//构建堆
function buildHeap(elements){ //从最后一个拥有子节点的节点开始,将该节点连同其子节点进行比较, //将最大的数交换与该节点,交换后,再依次向前节点进行相同交换处理, //直至构建出大顶堆(升序为大顶,降序为小顶) for(var i=elements.length/2; i>=0; i--){ headAdjust(elements, i, elements.length); } } function sort(elements){ //构建堆 buildHeap(elements); //从数列的尾部开始进行调整 for(var i=elements.length-1; i>0; i--){ //堆顶永远是最大元素,故,将堆顶和尾部元素交换,将 //最大元素保存于尾部,并且不参与后面的调整 var swap = elements[i]; elements[i] = elements[0]; elements[0] = swap; //进行调整,将最大)元素调整至堆顶 headAdjust(elements, 0, i); } } var elements = [3, 1, 5, 7, 2, 4, 9, 6, 10, 8]; console.log('before: ' + elements); sort(elements); console.log(' after: ' + elements);

快速排序:

步骤为:

  1. 从序列中挑出一个元素,作为"基准"(pivot).
  2. 把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
  3. 对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。

  快速排序的代码如下:

      function QuickSort (arr) {
  if (arr.length <= 1) { return arr; }
  var pivotIndex = Math.floor(arr.length / 2);
  var pivot = arr.splice(pivotIndex, 1)[0];
  var left = [];
  var right = [];
  for (var i = 0; i < arr.length; i++){
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  return quickSort(left).concat([pivot], quickSort(right));

};


希尔排序:

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。(先分组,在进行直接插入排序)

function ShellSort(arr){
var len = arr.length;
for (var fraction = Math.floor(len / 2); fraction > 0; fraction = Math.floor(fraction / 2)) {
   for (var i = fraction; i < len; i++) {
       for (var j = i - fraction; j >= 0 && arr[j] > arr[fraction + j]; j -= fraction) {
           var temp = arr[j];
           arr[j] = arr[fraction + j];
           arr[fraction + j] = temp;
       }
   }
}
return arr;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值