自学内容网 自学内容网

TypeScript 算法手册【快速排序】

在这里插入图片描述

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

1. 快速排序简介

1.1 快速排序定义

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

用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 选择基准元素

const pivot = arr[Math.floor(arr.length / 2)];

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

2.2 划分数组

const left = arr.filter(x => x < pivot);
const middle = arr.filter(x => x === pivot);
const right = arr.filter(x => x > pivot);

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

2.3 递归排序

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

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

3. 快速排序的优化

3.1 三数取中法选择基准

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 插入排序与快速排序结合

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朵)时,直接用更简单的方法来排列这些花。这样可以减少整理的复杂度,提高整体的效率。

案例代码和动态图

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


原文地址:https://blog.csdn.net/bobostudio1995/article/details/142681552

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!