# 快速排序

若数组片段的长度大于 1,则随机从数组片段中取出一个元素作为主元(pivot),将数组分为两份 A,B,这个位置之前的为一份(A),这个位置之后的为一份(B),将 A 中所有比 pivot 大的元素全部放到 B 中将 B 中比 pivot 小的元素全部放大 A 中,然后分别递归的对数组片段 A 和 B 分别重复这个过程,直到所有的元素都有序即完成排序。

快速排序是基于分治思想的排序算法。

# 排序过程

# 算法实现

# 朴素法

朴素法虽然比较好理解,也比较好记忆,但是实际上并不快,因为每次都简单的按特定的规则选取主元,如果主元的选择情况并不好的话,那我们的快速排序算法就跟冒泡排序差不多了。

/**
 * 对数组片段进行快速排序
 * @param {Array<number>} arr 需要进行排序的数组
 * @param {number} left 待排序数组片段的开始索引
 * @param {number} right 待排序数组片段的结束索引
 */
function _quickSort(arr, left, right) {
  // 如果数组片段的长度小于或者等于1,无需进行排序
  if (left >= right) {
    return;
  }
  // 随机取一个元素作为主元
  let pivot = arr[left];
  let i = left;
  let j = right;
  while (i < j) {
    // 从数组片段右侧找比主元小的元素
    while (i < j && arr[j] > pivot) {
      j--;
    }
    // 说明此刻已经找到了比主元小的元素
    if (i < j) {
      arr[i] = arr[j];
      // 缩小规模
      i++;
    }
    // 从数组片段左侧找比主元大的元素
    while (i < j && arr[i] < pivot) {
      i++;
    }
    // 说明找到了比主元大的元素
    if (i < j) {
      arr[j] = arr[i];
      j--;
    }
  }
  // 当退出循环的时候,i == j, 此刻这个位置之前所有的元素都比主元小,这个位置之后的所有元素都比主元大,这个位置就是我们存放主元的位置
  arr[i] = pivot;
  // 递归的对左半部分元素进行快速排序
  _quickSort(arr, left, i - 1);
  // 递归的对右半部分元素进行快速排序
  _quickSort(arr, i + 1, right);
}

/**
 * 对数组进行快速排序
 * @param {Array<number>} arr 需要进行排序的数组
 */
function quickSort(arr) {
  _quickSort(arr, 0, arr.length - 1);
}

# 三元取中法

/**
 * 定义数据的规模,如果少于这个量,则用简单排序,否则使用快速排序
 */
const N = 50;
/**
 * 定义直接插入排序的方法,此直接插入排序方法和普通的插入排序有区别,因为是针对数组的某一个片段进行排序,因此需要引入一个offset偏移量参数
 * @param {Array} arr 待排序数组
 * @param {Number} offset 初始偏移量
 * @param {Number} length 待排序片段的总长度
 */
function _insertionSort(arr, offset, length) {
  let temp, i;
  for (let p = offset + 1; p < offset + length; p++) {
    temp = arr[p];
    for (i = p; i > offset && temp < arr[i - 1]; i--) {
      arr[i] = arr[i - 1];
    }
    arr[i] = temp;
  }
}

/**
 * 三元取中法获取主元
 */
function _mediant3(arr, left, right) {
  let center = Math.floor((left + right) / 2);
  if (arr[left] > arr[right]) {
    _swap(arr, left, right);
  }

  if (arr[left] > arr[center]) {
    _swap(arr, left, center);
  }

  if (arr[center] > arr[right]) {
    _swap(arr, right, center);
  }
  // 并且把主元藏在数组片段的倒数第二位
  _swap(arr, right - 1, center);
  return arr[right - 1];
}

/**
 * 交换数组中的2个元素
 */
function _swap(arr, i, j) {
  let temp = arr[j];
  arr[j] = arr[i];
  arr[i] = temp;
}

/**
 * 对数组片段进行快速排序
 */
function _quickSort(arr, left, right) {
  // 数据片段的长度
  let len = right - left + 1;
  // 如果数据规模少于它,则使用简单排序
  if (len <= N) {
    _insertionSort(arr, left, len);
  } else {
    // 使用三元法获取主元
    let pivot = _mediant3(arr, left, right);
    let i = left;
    let j = right - 1;
    while (true) {
      while (arr[--j] > pivot);
      while (arr[++i] < pivot);
      if (i >= j) {
        break;
      }
      _swap(arr, i, j);
    }
    _swap(arr, i, right - 1);
    _quickSort(arr, left, i - 1);
    _quickSort(arr, i + 1, right);
  }
}

/**
 * 对数组进行快速排序
 */
function quickSort(arr) {
  _quickSort(arr, 0, arr.length - 1);
}

# 复杂度与稳定性

快速排序是不稳定的排序算法

平均算法时间复杂度:O(n*logn),最坏:O(N²)