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 算法手册 - 计数排序

相关推荐
仙俊红1 小时前
LeetCode487周赛T2,删除子数组后的最终元素
数据结构·算法
2501_920931706 小时前
React Native鸿蒙跨平台采用ScrollView的horizontal属性实现横向滚动实现特色游戏轮播和分类导航
javascript·react native·react.js·游戏·ecmascript·harmonyos
0思必得07 小时前
[Web自动化] Selenium处理动态网页
前端·爬虫·python·selenium·自动化
-dzk-7 小时前
【代码随想录】LC 59.螺旋矩阵 II
c++·线性代数·算法·矩阵·模拟
风筝在晴天搁浅7 小时前
hot100 78.子集
java·算法
Jasmine_llq7 小时前
《P4587 [FJOI2016] 神秘数》
算法·倍增思想·稀疏表(st 表)·前缀和数组(解决静态区间和查询·st表核心实现高效预处理和查询·预处理优化(提前计算所需信息·快速io提升大规模数据读写效率
超级大只老咪8 小时前
快速进制转换
笔记·算法
东东5168 小时前
智能社区管理系统的设计与实现ssm+vue
前端·javascript·vue.js·毕业设计·毕设
catino8 小时前
图片、文件的预览
前端·javascript
m0_706653238 小时前
C++编译期数组操作
开发语言·c++·算法