JavaScript实现十大排序算法

目录

概览

一、冒泡排序

1、算法描述

2、图示

3、代码 

二、选择排序

1、算法描述

2、图示

 3、代码

三、插入排序

1、算法描述

2、图示

​编辑 3、代码

四、希尔排序

1、算法描述

2、图示

3、代码

五、并归排序

1、算法描述

2、图示

​编辑​编辑3、代码

六、快速排序

1、算法描述

2、图示

​编辑​编辑 3、代码

七、堆排序

1、算法描述

2、图示

3、代码

八、计数排序

1、算法描述

2、图示

3、代码

九、桶排序

1、算法描述

2、图示

3、代码

十、基数排序

1、算法描述

2、图示

3、代码


概览

十大排序算法可以分成两类:

  • 比较类别排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
  • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

在这里插入图片描述

算法的时间复杂度:

在这里插入图片描述 

相关概念:
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
 

!!以下算法均按照升序排序!!

一、冒泡排序

1、算法描述

从头开始,一次比较相邻的两个元素,如果前一个元素比后一个元素小,则往后移动一个元素继续比较;如果前一个元素比后一个元素大,则两个元素交换位置,再往后移一个元素继续比较。

每一趟比较下来,最后一个元素肯定是当前趟最大的,进行下一趟比较时,该元素就不再参与比较了。

2、图示

在这里插入图片描述

3、代码 

function bubbleSort(arr) {
  let len = arr.length;
  // 一共比较多少趟
  for (let i = 0; i < len - 1; i++) {
    // 每趟进行比较,j=要进行排序的元素个数,每一趟排下来最后1个元素都不进行下一趟的排序
    for (let j = 0; j < len - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr;
}

let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];

console.log(bubbleSort(arr)); // [3,  4,  5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

二、选择排序

1、算法描述

每趟比较中找到最小的元素,将最小的元素与首元素进行位置交换,首元素就不再进行下一趟排序,重复操作直到完成排序

2、图示

在这里插入图片描述

 3、代码

function selectSort(arr) {
  let len = arr.length;
  for (let i = 0; i < len - 1; i++) {
    let minIndex = i;  // 记录最小元素的索引
    for (let j = i + 1; j < len; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;   // 如果有遇到比首元素小的,就记录该索引,这样就能找到当前趟最小元素的索引
      }
    }
    [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];  // 交换首元素和最小元素的位置
  }
  return arr;
}

let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];

console.log(selectSort(arr));  // [3,  4,  5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

三、插入排序

1、算法描述

  1. 第一个元素默认已排好序,从第二个元素开始。
  2. 如果第二个元素比第一个元素小,那就交换二者位置,否则不交换,前两个元素的顺序已经固定。
  3. 第三个元素与前两个元素进行比较,从后向前比,先与第二个元素比,如果比第二个元素小,再与第一个元素比,比第一个元素小,就放在第一个元素的位置,第一个元素和第二个元素向后移一个位置;如果第三个元素不比第二个元素小,不交换位置,这趟的排序以固定,也就是前三个元素的大小顺序已经排好。
  4. 之后的排序也是这么进行。

2、图示

在这里插入图片描述 3、代码

function insertSort(arr) {
  let len = arr.length;
  for (let i = 1; i < len; i++) {
    let now = arr[i]; // 当前趟的元素
    let lastIndex = i - 1; // 已经排好序的序列里的最后一个元素
    // 从后向前遍历已经排好序的序列
    // 如果当前趟的元素now比已经排好序的序列的元素小
    while (lastIndex >= 0 && arr[lastIndex] > now) {
      arr[lastIndex + 1] = arr[lastIndex]; // 将比now大的元素向后移一个位置
      lastIndex--;
    }
    arr[lastIndex + 1] = now; // 将当前趟的元素now插入到已经排好序的序列里
  }
  return arr;
}

let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];

console.log(insertSort(arr)); // [3,  4,  5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

四、希尔排序

插入排序的改进版。与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。

1、算法描述

  1. 选择一个增量 k=arr.length/2 进行排序,增量k为几就将该arr分成几组,每组有 arr.length/k=2 个元素。然后组内元素从前往后进行比较,按照升序排列好,再将小组并成一个大arr。
  2. 再次分组,分成k/2组,每个组有 arr.length/k/2 个元素,再次将组内元素按升序进行排序,排序好在并成大arr。
  3. 重复上述操作,直至arr排好序。
  4. 插入排序时,并不是一个分组内的数字一次性用插入排序完成,而是每个分组交叉进行。

2、图示

3、代码

function shellSort(arr) {
  let len = arr.length;
  // 第一次分组是k=arr.length/2,之后的每次分组都是k/2
  for (let k = Math.floor(len / 2); k > 0; k = Math.floor(k / 2)) {
    // 遍历每个分组的元素,i在分组里表示的是每个组内最后一个元素
    // 在原数组arr里表示的是分组后的第一个元素,然后后面的元素依次与前面的元素进行比较
    for (let i = k; i < len; i++) { // i控制当前进行排序的是哪一个小组
      let j = i;  // j控制的是当前小组的最后一个元素
      // 各小组内进行比较时,按升序排列
      while (j - k >= 0 && arr[j] < arr[j - k]) {
        [arr[j], arr[j - k]] = [arr[j - k], arr[j]];
        j = j - k;  // 让j变成当前小组的上一个元素
      }
    }
  }
  return arr;
}

let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];

console.log(shellSort(arr)); // [3,  4,  5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

五、并归排序

分治法

1、算法描述

把长度为n的arr分成两个长度为n/2的子序列,一直划分到最小个数之后,两两比较,合并成长度为2的小序列,然后再小序列间比较,一直重复合并成最终的arr

2、图示

3、代码

function mergeSort(arr) {
  let len = arr.length;
  if (len < 2) {
    return arr;
  }
  let mid = Math.floor(len / 2);
  let left = arr.slice(0, mid);
  let right = arr.slice(mid);
  return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
  let result = [];

  while (left.length > 0 && right.length > 0) {
    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;
}

let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];

console.log(mergeSort(arr)); // [3,  4,  5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

六、快速排序

分治法

1、算法描述

  1. 从数列中挑出一个元素,称为 “基准”(pivot);
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序

2、图示

在这里插入图片描述 3、代码

 

function quickSort(arr, left, right) {
  let len = arr.length,
    partitionIndex,
    left = typeofleft != "number" ? 0 : left,
    right = typeofright != "number" ? len - 1 : right;

  if (left < right) {
    partitionIndex = partition(arr, left, right);
    quickSort(arr, left, partitionIndex - 1);
    quickSort(arr, partitionIndex + 1, right);
  }
  return arr;
}

function partition(arr, left, right) {
  // 分区操作
  let pivot = left, // 设定基准值(pivot)
    index = pivot + 1;
  for (let i = index; i <= right; i++) {
    if (arr[i] < arr[pivot]) {
      swap(arr, i, index);
      index++;
    }
  }
  swap(arr, pivot, index - 1);
  return index - 1;
}

function swap(arr, i, j) {
  let temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];

console.log(quickSort(arr)); // [3,  4,  5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

七、堆排序

1、算法描述

  1. 通过构建大顶堆
  2. 将堆顶的最大数拿出,与堆底的叶子节点进行交换
  3. 接着,树剪掉最大数的叶子
  4. 再对堆进行调整,重新变成大顶堆
  5. 返回步骤2,以此循环,直至取出所有数

2、图示

在这里插入图片描述 

3、代码

var len; // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量

function buildMaxHeap(arr) {
  // 建立大顶堆
  len = arr.length;
  for (var i = Math.floor(len / 2); i >= 0; i--) {
    heapify(arr, i);
  }
}

function heapify(arr, i) {
  // 堆调整
  var left = 2 * i + 1,
    right = 2 * i + 2,
    largest = i;

  if (left < len && arr[left] > arr[largest]) {
    largest = left;
  }

  if (right < len && arr[right] > arr[largest]) {
    largest = right;
  }

  if (largest != i) {
    swap(arr, i, largest);
    heapify(arr, 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--;
    heapify(arr, 0);
  }
  return arr;
}

let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 12, 46, 4, 19, 50, 48];

console.log(heapSort(arr)); // [3,  4,  5, 12, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

八、计数排序

1、算法描述

  1. 找出待排序的数组中最大和最小的元素;
  2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
  4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

2、图示

在这里插入图片描述

3、代码

function countingSort(arr, maxValue) {
  let bucket = newArray(maxValue + 1),
    sortedIndex = 0;
  (arrLen = arr.length), (bucketLen = maxValue + 1);

  for (let i = 0; i < arrLen; i++) {
    if (!bucket[arr[i]]) {
      bucket[arr[i]] = 0;
    }
    bucket[arr[i]]++;
  }

  for (let j = 0; j < bucketLen; j++) {
    while (bucket[j] > 0) {
      arr[sortedIndex++] = j;
      bucket[j]--;
    }
  }

  return arr;
}

九、桶排序

1、算法描述

  1. 设置一个定量的数组当作空桶
  2. 遍历输入数据,并且把数据一个一个放到对应的桶里去;
  3. 对每个不是空的桶进行排序;
  4. 从不是空的桶里把排好序的数据拼接起来

2、图示

 

3、代码

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]; // 输入数据的最大值
    }
  }

  // 桶的初始化
  let DEFAULT_BUCKET_SIZE = 5; // 设置桶的默认数量为5
  bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
  let bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
  let buckets = newArray(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;
}

十、基数排序

1、算法描述

  1. 取得数组中的最大数,并取得位数;
  2. arr为原始数组,从最低位开始取每个位组成radix数组;
  3. 对radix进行计数排序(利用计数排序适用于小范围数的特点)

2、图示

在这里插入图片描述

3、代码

let counter = [];
function radixSort(arr, maxDigit) {
  let mod = 10;
  let dev = 1;
  for (let i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
    for (let j = 0; j < arr.length; j++) {
      let bucket = parseInt((arr[j] % mod) / dev);
      if (counter[bucket] == null) {
        counter[bucket] = [];
      }
      counter[bucket].push(arr[j]);
    }
    let pos = 0;
    for (let j = 0; j < counter.length; j++) {
      let value = null;
      if (counter[j] != null) {
        while ((value = counter[j].shift()) != null) {
          arr[pos++] = value;
        }
      }
    }
  }
  return arr;
}

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值