返回

JavaScript 数据结构与算法之美

前端

前言

笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScript ,旨在入门数据结构与算法和方便以后复习。

文中包含了 十大经典排序算法 的思想、代码实现、一些例子、复杂度分析、动画、还有算法可视化工具。这应该是目前最全的 JavaScript 数据结构与算法教程之一。

十大经典排序算法

  1. 冒泡排序
  2. 选择排序
  3. 插入排序
  4. 希尔排序
  5. 归并排序
  6. 快速排序
  7. 堆排序
  8. 计数排序
  9. 桶排序
  10. 基数排序

排序算法比较

排序算法 时间复杂度 空间复杂度 稳定性
冒泡排序 O(n^2) O(1)
选择排序 O(n^2) O(1)
插入排序 O(n^2) O(1)
希尔排序 O(n log n) O(1)
归并排序 O(n log n) O(n)
快速排序 O(n log n) O(log n)
堆排序 O(n log n) O(1)
计数排序 O(n + k) O(k)
桶排序 O(n + k) O(n + k)
基数排序 O(n * k) O(n + k)

代码实现

十大经典排序算法的 JavaScript 代码实现如下:

// 冒泡排序
function bubbleSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }

  return arr;
}

// 选择排序
function selectionSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    let minIndex = i;
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }

    [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
  }

  return arr;
}

// 插入排序
function insertionSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    let currentElement = arr[i];
    let j = i - 1;
    while (j >= 0 && arr[j] > currentElement) {
      arr[j + 1] = arr[j];
      j--;
    }

    arr[j + 1] = currentElement;
  }

  return arr;
}

// 希尔排序
function shellSort(arr) {
  let gap = Math.floor(arr.length / 2);

  while (gap > 0) {
    for (let i = gap; i < arr.length; i++) {
      let currentElement = arr[i];
      let j = i - gap;
      while (j >= 0 && arr[j] > currentElement) {
        arr[j + gap] = arr[j];
        j -= gap;
      }

      arr[j + gap] = currentElement;
    }

    gap = Math.floor(gap / 2);
  }

  return arr;
}

// 归并排序
function mergeSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }

  let mid = Math.floor(arr.length / 2);
  let left = mergeSort(arr.slice(0, mid));
  let right = mergeSort(arr.slice(mid));

  return merge(left, right);
}

function merge(left, right) {
  let merged = [];
  let leftIndex = 0;
  let rightIndex = 0;

  while (leftIndex < left.length && rightIndex < right.length) {
    if (left[leftIndex] < right[rightIndex]) {
      merged.push(left[leftIndex]);
      leftIndex++;
    } else {
      merged.push(right[rightIndex]);
      rightIndex++;
    }
  }

  while (leftIndex < left.length) {
    merged.push(left[leftIndex]);
    leftIndex++;
  }

  while (rightIndex < right.length) {
    merged.push(right[rightIndex]);
    rightIndex++;
  }

  return merged;
}

// 快速排序
function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }

  let pivot = arr[0];
  let left = [];
  let right = [];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return [...quickSort(left), pivot, ...quickSort(right)];
}

// 堆排序
function heapSort(arr) {
  let heapSize = arr.length;

  // Build a max heap
  for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
    heapify(arr, i, heapSize);
  }

  // Sort the heap
  for (let i = heapSize - 1; i >= 0; i--) {
    [arr[0], arr[i]] = [arr[i], arr[0]];
    heapSize--;
    heapify(arr, 0, heapSize);
  }

  return arr;
}

function heapify(arr, i, heapSize) {
  let largest = i;
  let left = 2 * i + 1;
  let right = 2 * i + 2;

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

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

  if (largest !== i) {
    [arr[i], arr[largest]] = [arr[largest], arr[i]];
    heapify(arr, largest, heapSize);
  }
}

// 计数排序
function countingSort(arr, max) {
  let counts = new Array(max + 1).fill(0);

  for (let i = 0; i < arr.length; i++) {
    counts[arr[i]]++;
  }

  let sortedIndex = 0;
  for (let i = 0; i < counts.length; i++) {
    while (counts[i] > 0) {
      arr[sortedIndex++] = i;
      counts[i]--;
    }
  }

  return arr;
}

// 桶排序
function bucketSort(arr, bucketSize) {
  let minValue = Math.min(...arr);
  let maxValue = Math.max(...arr);
  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++) {
    let bucketIndex = Math.floor((arr[i] - minValue) / bucketSize);
    buckets[bucketIndex].push(arr[i]);
  }

  for (let i = 0; i < buckets.length; i++) {
    buckets[i] = insertionSort(buckets[i]);
  }

  let sortedIndex