TypeScript 算法手册【快速排序】

文章目录

    • [1. 快速排序简介](#1. 快速排序简介)
      • [1.1 快速排序定义](#1.1 快速排序定义)
      • [1.2 快速排序特点](#1.2 快速排序特点)
    • [2. 快速排序步骤过程拆解](#2. 快速排序步骤过程拆解)
      • [2.1 选择基准元素](#2.1 选择基准元素)
      • [2.2 划分数组](#2.2 划分数组)
      • [2.3 递归排序](#2.3 递归排序)
    • [3. 快速排序的优化](#3. 快速排序的优化)
      • [3.1 三数取中法选择基准](#3.1 三数取中法选择基准)
      • [3.2 插入排序与快速排序结合](#3.2 插入排序与快速排序结合)
      • 案例代码和动态图
    • [4. 快速排序的优点](#4. 快速排序的优点)
    • [5. 快速排序的缺点](#5. 快速排序的缺点)
    • 总结

【 已更新完 TypeScript 设计模式 专栏,感兴趣可以关注一下,一起学习交流🔥🔥🔥 】

1. 快速排序简介

1.1 快速排序定义

快速排序是一种高效的、使用分治策略的排序算法。它的核心思想是"选择基准,分而治之"。想象一下,你是一位园丁,需要整理一大片杂乱的花园。你采用这样的策略:首先随机选择一株花作为参考,然后将其他花分成两组,一组是比这株花"矮"的,另一组是比这株花"高"的。你对这两组花分别重复这个过程,直到每组只剩下一株花。将所有的花按高度排列,就得到了一个整齐有序的花园。这就是快速排序的基本思想。

用TypeScript代码表示一个简单的快速排序:

typescript 复制代码
function quickSort(arr: number[]): number[] {
  if (arr.length <= 1) return arr;

  const pivot = arr[Math.floor(arr.length / 2)];
  const left = arr.filter(x => x < pivot);
  const middle = arr.filter(x => x === pivot);
  const right = arr.filter(x => x > pivot);

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

1.2 快速排序特点

  1. 分治思想:快速排序采用分治策略,将复杂问题分解为简单子问题
  2. 原地排序:快速排序可以在原数组上进行,不需要额外的存储空间
  3. 时间复杂度:平均情况下为O(nlogn),最坏情况下为O(n^2)
  4. 不稳定性:快速排序是不稳定的排序算法

2. 快速排序步骤过程拆解

2.1 选择基准元素

typescript 复制代码
const pivot = arr[Math.floor(arr.length / 2)];

这就像园丁从花园中间随机选择一株花作为参考,这株花将成为我们划分其他花的标准。

2.2 划分数组

typescript 复制代码
const left = arr.filter(x => x < pivot);
const middle = arr.filter(x => x === pivot);
const right = arr.filter(x => x > pivot);

这个步骤就像园丁将其他花分成三组:比参考花"矮"的、与参考花一样高的、比参考花"高"的。在实际的快速排序实现中,我们通常会使用双指针法在原地完成这个过程,就像园丁在花园里来回走动,将花朵移到合适的位置。

2.3 递归排序

typescript 复制代码
return [...quickSort(left), ...middle, ...quickSort(right)];

这个步骤就像园丁对"矮"组和"高"组的花朵重复之前的过程,直到所有的花都被正确地排列在花园里。

3. 快速排序的优化

3.1 三数取中法选择基准

typescript 复制代码
function medianOfThree(arr: number[], left: number, right: number): number {
  const mid = Math.floor((left + right) / 2);
  if (arr[left] > arr[mid]) [arr[left], arr[mid]] = [arr[mid], arr[left]];
  if (arr[left] > arr[right]) [arr[left], arr[right]] = [arr[right], arr[left]];
  if (arr[mid] > arr[right]) [arr[mid], arr[right]] = [arr[right], arr[mid]];
  return mid;
}

function quickSortOptimized(arr: number[], left: number = 0, right: number = arr.length - 1): number[] {
  if (left < right) {
    const pivotIndex = medianOfThree(arr, left, right);
    const pivotNewIndex = partition(arr, left, right, pivotIndex);
    quickSortOptimized(arr, left, pivotNewIndex - 1);
    quickSortOptimized(arr, pivotNewIndex + 1, right);
  }
  return arr;
}

function partition(arr: number[], left: number, right: number, pivotIndex: number): number {
  const pivot = arr[pivotIndex];
  [arr[pivotIndex], arr[right]] = [arr[right], arr[pivotIndex]];
  let storeIndex = left;
  for (let i = left; i < right; i++) {
    if (arr[i] < pivot) {
      [arr[i], arr[storeIndex]] = [arr[storeIndex], arr[i]];
      storeIndex++;
    }
  }
  [arr[storeIndex], arr[right]] = [arr[right], arr[storeIndex]];
  return storeIndex;
}

这个优化版本就像园丁不是随机选择参考花,而是从花园的开始、中间和结尾各选一朵花,然后选择这三朵花中"中间"高度的那朵作为参考,这样可以减少选到"最高"或"最矮"的花作为参考的概率,从而提高排序效率。

3.2 插入排序与快速排序结合

typescript 复制代码
function hybridQuickSort(arr: number[], left: number = 0, right: number = arr.length - 1, threshold: number = 10): number[] {
  if (right - left + 1 <= threshold) {
    return insertionSort(arr, left, right);
  }
  
  if (left < right) {
    const pivotIndex = medianOfThree(arr, left, right);
    const pivotNewIndex = partition(arr, left, right, pivotIndex);
    hybridQuickSort(arr, left, pivotNewIndex - 1, threshold);
    hybridQuickSort(arr, pivotNewIndex + 1, right, threshold);
  }
  return arr;
}

function insertionSort(arr: number[], left: number, right: number): number[] {
  for (let i = left + 1; i <= right; i++) {
    let current = arr[i];
    let j = i - 1;
    while (j >= left && arr[j] > current) {
      arr[j + 1] = arr[j];
      j--;
    }
    arr[j + 1] = current;
  }
  return arr;
}

这个优化版本就像园丁在整理花园时,发现某一小片区域的花朵数量少于某个阈值(比如10朵)时,直接用更简单的方法来排列这些花。这样可以减少整理的复杂度,提高整体的效率。

案例代码和动态图

typescript 复制代码
const array = [38, 27, 43, 3, 9, 48, 10];
const sortedArray = quickSort(array);
console.log(sortedArray); // [3, 9, 10, 27, 38, 43, 48]

4. 快速排序的优点

  1. 高效性:在平均情况下,快速排序是所有同数量级(O(nlogn))排序算法中最快的
  2. 原地排序:快速排序可以在原数组上进行,不需要额外的存储空间
  3. 缓存友好:快速排序的数据访问模式对缓存友好,这使得它在实际应用中表现优秀

5. 快速排序的缺点

  1. 不稳定性:快速排序是不稳定的排序算法
  2. 最坏情况性能:在最坏情况下(已经排序的数组),时间复杂度退化到O(n^2)
  3. 对小规模数据,不如插入排序等简单算法效率高

总结

快速排序就像是园丁整理一大片杂乱的花园。它告诉我们,面对一大片混乱的花朵,可以通过选择一株参考花,将花园分成两部分,然后逐步整理。这种"分而治之"的思想不仅在整理花园中有用,在我们日常解决复杂问题时也常常能派上用场。

快速排序的高效性和原地排序的特性,使它在实际应用中表现出色。特别是在处理大规模数据时,快速排序常常是首选的算法。然而,它的不稳定性和在某些特殊情况下的性能退化,也提醒我们在选择算法时需要考虑具体的应用场景。

喜欢的话就点个赞 ❤️,关注一下吧,有问题也欢迎讨论指教。感谢大家!!!

下期预告: TypeScript 算法手册 - 计数排序

相关推荐
沉默璇年30 分钟前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder36 分钟前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727571 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
Swift社区1 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman1 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
会发光的猪。1 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客2 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
猫爪笔记2 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
IT 青年2 小时前
数据结构 (1)基本概念和术语
数据结构·算法
前端李易安2 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js