1.冒泡排序
人们开始学习排序算法时,通常都先学冒泡算法,因为它在所有排序算法中最简单。然而,从运行时间的角度来看,冒泡排序是最差的一个。冒泡排序比较所有相邻的两个项,如果第一个比第二个大,则交换它们。元素项向上移动至正确的顺序,就好像气泡升至表面一样,冒泡排序因此得名。
编码思路:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
- 之所以叫冒泡排序,每一轮两两比较之后,都会冒出一个本轮最大的数,将其移动到本轮尾部。
代码:
// 冒泡排序
function bubbleSort(arr) {
// 循环次数
for (let i = 1; i < arr.length; i++) {
// 比较
for (let j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换位置
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
2.选择排序
选择排序算法是一种原址比较排序算法。选择排序大致的思路是找到数据结构中的最小值并将其放置在第一位,接着找到第二小的值并将其放在第二位,以此类推。如此往复,直到将整个数组排序。
编码思路
代码:
// 选择排序
function selectSort(arr) {
// 循环次数
for (let i = 0; i < arr.length - 1; i++) {
// 假设一个最小值的下标
let minIndex = i;
// 通过一轮比较出一个最小值下标
for (let j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j]) {
// 让贤
minIndex = j;
}
}
// 交换位置(如果相同没必要交换)
if(minIndex != i){
let temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
3.插入排序
对比生活中整理扑克牌的思路,将每一张牌插入到其他已经有序的牌中的适当位置。在计算机的实现中,为了要给插入的元素腾出空间,我们需要将其余元素在插入之前向后移动一位。
它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
编码思路:
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤 2~5。
代码:
var len = arr.length;
var j, temp;
for (var i = 1; i < len; i++) {
j = i - 1;
temp = arr[i];
while (j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
3.4 其它排序
排序的算法有很多种,上面的几种是我们比较容易理解的,下面再列举几种排序思想,了解即可。
3.4.1 归并排序
归并排序是一种分而治之算法。其思想是将原始数组切分成较小的数组,直到每个小数组只有一个位置,接着将小数组归并成较大的数组,直到最后只有一个排序完毕的大数组。
// 先自上往下递归拆分数组为单个元素, 再利用merge方法,自下往上合并元素返回结果
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;
}
3.4.2 快速排序
和归并排序一样,快速排序也使用分而治之的方法,将原始数组分为较小的数组(但它没有像归并排序那样将它们分割开)。
(1) 首先,从数组中选择一个值作为主元(pivot),也就是数组中间的那个值。
(2) 创建两个指针(引用),左边一个指向数组第一个值,右边一个指向数组最后一个值。移动左指针直到我们找到一个比主元大的值,接着,移动右指针直到找到一个比主元小的值,然后交换它们,重复这个过程,直到左指针超过了右指针。这个过程将使得比主元小的值都排在主元之前,而比主元大的值都排在主元之后。这一步叫作划分(partition)操作。
(3) 接着,算法对划分后的小数组(较主元小的值组成的子数组,以及较主元大的值组成的子数组)重复之前的两个步骤,直至数组已完全排序。
3.4.3 计数排序
分布式排序使用已组织好的辅助数据结构(称为桶),然后进行合并,得到排好序的数组。计数排序使用一个用来存储每个元素在原始数组中出现次数的临时数组。在所有元素都计数完成后,临时数组已排好序并可迭代以构建排序后的结果数组。它是用来排序整数的优秀算法(它是一个整数排序算法),时间复杂度为O(n+k),其中k是临时计数数组的大小;但是,它确实需要更多的内存来存放临时数组。
3.4.4 桶排序
桶排序(也被称为箱排序)也是分布式排序算法,它将元素分为不同的桶(较小的数组),再使用一个简单的排序算法,例如插入排序(用来排序小数组的不错的算法),来对每个桶进行排序。然后,它将所有的桶合并为结果数组。
3.4.5 基数排序
基数排序也是一个分布式排序算法,它根据数字的有效位或基数(这也是它为什么叫基数排序)将整数分布到桶中。基数是基于数组中值的记数制的。比如,对于十进制数,使用的基数是10。因此,算法将会使用10个桶用来分布元素并且首先基于个位数字进行排序,然后基于十位数字,然后基于百位数字,以此类推。