算法之排序——快速排序

快速排序详解Quicksort

快排(快速排序)也是递归排序中的一种,也是分而治之的思想在排序中的一个体现,另一个体现为归并排序;
相对于归并排序,快排没有使用其他数组,没有额外的空间复杂度;
快排的思想也是分组,不同于归并排序,归并排序用额外的数组来合并分组;而快排,不实用额外数组。

归并排序:递归分组,当分组到最小值(数组length<=1)时,再用一个新数组,递归合并分组。
快速排序:递归分组,选择基点(任意选择),根据基点做排序后,利用基点进行分组;依次递归。

快速排序的3个基本步骤:

  1. 从数组中选择一个元素作为基准点
  2. 分区,排序数组,所有比基准值小的元素摆放在左边区域,而大于基准值的摆放在右边区域。每次分割结束以后基准值会插入到中间去。
  3. 最后利用递归,将摆放在左边的数组和右边的数组在进行一次上述的1和2操作。

分区会用到两个指针,一个指针会指向比基点大的元素,一个指针指向比基点小的元素;
两个指针可以在同一个方向,也可以在不同方向;

通用部分代码:

// 交换数组元素
function switchItem(arr, a, b) {
  let temp
  temp = arr[b]
  arr[b] = arr[a]
  arr[a] = temp
}

// 快速排序
function quickSort(arr) {
  return quick(arr, 0, arr.length - 1)
}
// 分组后递归排序
function quick(arr, left, right) {
  if (arr.length > 1) {
    // 根据分区后,获得基点索引,依次递归分区
    let indext = partition(arr, left, right)
    if (left < indext - 1) {
      quick(arr, left, indext - 1)
    }
    if (indext + 1 < right) {
      quick(arr, indext + 1, right)
    }
  }
  return arr
}

针对分区时;指针放置方向不同,分区方式也不同 ,介绍两种分区方式:

方式一:指针在不同方向

步骤如下:

  1. 左指针逐个格子向右移动,当遇到大于或等于基点的值时,就停下来。
  2. 右指针逐个格子向左移动,当遇到小于或等于基点的值时,就停下来。
  3. 将两指针所指的值交换位置。
  4. 重复上述步骤,直至两指针重合,或左指针移到右指针的右边。
  5. 将基点与左指针所指的值交换位置。

示意图如下(选取最左边元素为基点):
算法之排序——快速排序_第1张图片
算法之排序——快速排序_第2张图片
算法之排序——快速排序_第3张图片
算法之排序——快速排序_第4张图片
算法之排序——快速排序_第5张图片
算法之排序——快速排序_第6张图片
算法之排序——快速排序_第7张图片
算法之排序——快速排序_第8张图片
代码如下:

function partition(arr, left, right) {
  let point = right

  while (left < right) {
  	// 左指针移动
    while (left < right && arr[left] <= arr[point]) {
      left++
    }
    // 右指针移动
    while (left < right && arr[point] <= arr[right]) {
      right--
    }
    // 当左右指针停下,满足条件,交换元素
    if (left < right) {
      switchItem(arr, left, right)
      left++
      right--
    }
  }
  // 交换基点和左指针
  switchItem(arr, left, point)
  // 返回基点所在指针,为了后面递归分区
  return left
}

方式二:指针在同方向

步骤如下(以指针方向同在左边为例):

  1. 两个指针分别为上下指针,初始化都指向形参为left的元素。
  2. 上指针依次遍历数组;当遇见小于基点的元素停下;
  3. 交换上指针和下指针元素;下指针元素加1;
  4. 循环完后,交换基点和下指针元素。

下指针指向第一个大于基点的元素;上指针总在寻找小于基点的元素。
当上下指针交换后,下指针后面的元素均小于基点;
最后循环完毕后,下指针和基点交换后,分区完成。小于基点的元素在下指针左边,大于基点的元素在下指针右边。
且基点已经放置到分区中间。

function partition(arr, left, right) {
  let point = right
  // 下指针
  let dPoint = left
  // 上指针遍历数组
  for (let uPoint = dPoint; uPoint < arr.length - 1; uPoint++) {
    if (arr[uPoint] < arr[point]) {
      switchItem(arr, uPoint, dPoint)
      dPoint++
    }
  }
  switchItem(arr, point, dPoint)
  return dPoint
}

你可能感兴趣的:(数据结构与算法)