一些排序算法

记得有一个明星程序员说,做一个 web developer 是不用学习数学的。那么自然也不用学习算法了。作为一个前端,在实际工作中确实很少实现什么排序算法,因为在 JavaScript 的语言层面,就已经实现 sort 函数了。但是学习这些排序算法,依然有助于我们理解这些语言相关内置函数的原理。

人类从来不给自己设限,才是我们一直前进的动力。

冒泡排序

冒泡排序的基本思想很简单,通过两层循环,来逐渐交换相邻的位置,大的放在右边,小的放在左边,外层的第一次循环选出最大的数字,第二次选出第二大的数字,以此类推。代码如下:

function swap (sortArr, index1, index2) {
    var temp = sortArr[index1]
    sortArr[index1] = sortArr[index2]
    sortArr[index2] = temp
}

function arr2str (arr, delimiter) {
    return arr.join(delimiter)
}

var sortArr = [10, 8, 3, 2, 2, 4, 9, 5, 4, 3];

function bubbleSort (sortArr) {
    // 外层的迭代从元素最大索引值开始
    for (var outer = sortArr.length - 1; outer > 1; outer--) {
        for (var inner = 0; inner < outer; inner++) {// 
            if (sortArr[inner] > sortArr[inner + 1]) {
                swap(sortArr, inner, inner + 1)
            }
        }
        console.log(arr2str(sortArr, ' '))
    }
    return sortArr
}

console.group('冒泡排序')
console.log(sortArr.join(' '))
console.log(bubbleSort(sortArr.slice()).join(' '))
console.groupEnd()

2020-12-29 更新:

let count = 0;
let swapCount = 0;
// 这个是选择排序,只是数组位置交换的过于频繁了
function bubbleSort2(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      count++;
      if (arr[i] > arr[j]) {
        swapCount++;
        [arr[i], arr[j]] = [arr[j], arr[i]]; // 把最小的放在左边
      }
    }
  }

  return arr;
}
// 这个实现是正宗的冒泡排序,相邻的 2 个元素对比
function bubbleSort(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length - 1 - i; j++) {
      count++;
      if (arr[j] > arr[j + 1]) {
        swapCount++;
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // 把最小的放在左边
      }
    }
  }

  return arr;
}
// let arr = [10, 8, 3, 2, 2, 4, 9, 5, 4, 3];
let arr = [1, 3, 8, 2, 5, 9, 0, 4, 7, 6];
bubbleSort(arr);
console.log(arr, count, swapCount);

选择排序

选择排序的核心思想是从排序的元素中选择最小的元素,放在第一位;再从余下的元素中,选择最小的,再放到第二位,以此类推,直到排序成功。

核心代码如下:

function selectSort (arr) {
    var minIndex
    var temp
    
    for (var outer = 0; outer < arr.length - 1; outer++) {
        minIndex = outer // guess current is minest num
        for (var inner = outer + 1; inner < arr.length; inner++) {
            if (arr[inner] < arr[minIndex]) {
                minIndex = inner // save minest arrary item index to minIndex
            }
        }
        swap(arr, outer, minIndex)
        console.log(arr2str(arr))
    }
    return arr
}
console.group('选择排序')
console.log(selectSort(sortArr.slice()).join(' '))
console.groupEnd()

以下是 c++ 的实现:

// 泛型
template<typename T>
void selectionSort(T arr[], int n) {
    for (int i = 0; i < n; i++) {
        
        int minIndex = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        
        swap(arr[i], arr[minIndex]);
    }
}

插入排序

插入排序的思想也非常简单,只是把这种思想转化为代码就要难一点。我们从一堆乱序的元素中选出一个放在已排序好元素中的合适位置,并将右侧的元素往右移动一个位置,我们具体看代码:

function insertSort (arr) {
    for (var outer = 1; outer < arr.length; outer++) {
        var temp = arr[outer] // save current iteration value
        var inner = outer
        while (inner > 0 && arr[inner - 1] > temp) {
            arr[inner] = arr[inner - 1]
            inner--
        }
        arr[inner] = temp // put the iteration in correct position
        console.log(arr2str(arr))
    }
    return arr
}
console.group('插入排序')
console.log(insertSort(sortArr).join(' '))
console.groupEnd()

短短几行代码,需要仔细看看。

下面我们来介绍一下希尔排序、归并排序和快速排序这几种高级排序算法。我清晰的记得一次电话面试中,tx 面试官让我说一下快速排序的核心思想。

希尔排序

在了解希尔排序之前,可以先看一下这个视频(需要翻墙)。希尔排序和插入排序的思想非常类似,只不过希尔排序定义了一个间隔位置,用来减少交换的次数。

希尔排序要解释清楚还是挺费劲的,如果看懂了上面的视频,那么理解起来也不难。核心代码如下。

function shellSort (arr) {
    var gaps = [5, 3, 1]

    for (var g = 0; g < gaps.length; g++) {
        for (var i = gaps[g]; i < arr.length; i++) {
            var temp = arr[i]
            // point code
            for (var j = i; j >= gaps[g] && arr[j - gaps[g]] > temp; j -= gaps[g]) {

                arr[j] = arr[j - gaps[g]] // point code

            }
            arr[j] = temp // point code
            console.log(arr2str(arr))
        }
    }
    return arr
}
console.group('希尔排序')
console.log(shellSort(sortArr).join(' '))
console.groupEnd()

对于初学者可能需要花一定的时间去理解上面的代码,可以再查查其它的相关文章。

归并排序

在理解归并排序前,我们还是先看一个视频。代码我就不贴出了,行数有点多。

2021-04-16 更新:递归版本的归并排序还是挺简单的,仔细琢磨琢磨。

var arr = [0, 4, 1, -3, 9, -3, 10];

function mergeSort(arr) {
  if (arr.length <= 1) return arr;

  let mid = (arr.length / 2) | 0;
  let left = arr.slice(0, mid);
  let right = arr.slice(mid);
  return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
  let res = [];
  while (left.length && right.length) {
    if (left[0] < right[0]) {
      res.push(left.shift());
    } else {
      res.push(right.shift());
    }
  }
  res.push(...left, ...right);
  return res;
}

var res = mergeSort(arr);
console.log(res);

快速排序

在视频面前,文字显得不够直白,还是看视频吧。

function quickSort (arr) {
    if (!arr.length) return []
    var pivot = arr[0]
    var lessArr = []
    var bigArr = []

    for (var i = 1; i < arr.length; i++) {
        if (pivot >= arr[i]) {
            lessArr.push(arr[i])
        } else {
            bigArr.push(arr[i])
        }
    }

    // console.log(arr2str(arr))
    return quickSort(lessArr).concat(pivot, quickSort(bigArr))
}

console.group('快速排序')
console.log(quickSort(sortArr).join(' '))
console.groupEnd()

JavaScript 版本的快速排序实现如上,实现起来还是挺简洁的。

参考链接

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

作者: 曾小乱

喜欢写点有意思的东西

《一些排序算法》有一个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据