快速排序

快速排序是已知的在实践中最快的排序算法。 它的最坏时间复杂度是O(n^2),平均时间复杂度是O(nlogn)。

快速排序的算法主要的思想在于分部算法(partition),下面的步骤是基于升序排序的假设:
    1.选取合适的枢纽。
    2.遍历整个数组,将数组中小于或者等于枢纽的元素放在左边,大于或者等于枢纽的元素放在右边。

快排的最坏情况出现在输入的数据是有序的,且是逆序的时候。为了避免最坏情况的发生,枢纽的选择是一个关键。
很明显,枢纽的选择最好就是数组的中值,但是要获得数组中的中值,我们需要扫描一遍数组。为此,我们需要一个近似的做法,使用三数中值来代替枢纽。具体的做法就是选择最左端,最右端和中间的元素,三个元素的中值作为枢纽。

快排的划分算法,每次划分都会使数组越来越接近有序。而 当被排序的是小数组且基本有序的时候,使用插入排序能获得更好的时间复杂度。因为插入排序能避免递归。据统计,当数组大小小于或者等于10的时候使用插入排序来代替快速排序,能提高25%的速度。
有关插入排序,可以看我的这一篇博文:http://blog.csdn.net/sky453589103/article/details/43371913

下面是快速排序的实现 :
#ifndef QSORT_H
#define QSORT_H

#include <vector>

int Partition(std::vector<int> &nums, size_t start, size_t end);

void QSort(std::vector<int> &nums, const size_t &start, const size_t &end) {
  if (start >= end || end >= nums.size()) {
    return ;
  }
  int pos = Partition(nums, start, end);
  if (pos >= 0) {
    QSort(nums, start, pos);
    QSort(nums, pos + 1, end);
  }
}

void Swap(int &lhs, int &rhs) {
  int temp = lhs;
  lhs = rhs;
  rhs = temp;
}

int Partition(std::vector<int> &nums, size_t start, size_t end) {
  if (start > end || end >= nums.size()) {
    return -1;
  }
  int middle = (start + end) >> 1;
  if (nums[start] > nums[middle]) {
    if (nums[end] < nums[middle]) {
      // nums[end] < nums[middle] < nums[start]
      Swap(nums[start], nums[middle]);
    }
    else if (nums[start] > nums[end]) {
      // nums[middle] <= nums[end] < nums[start]
      Swap(nums[start], nums[end]);
    }
  }
  else if (nums[start] < nums[middle]) {
    if (nums[end] > nums[middle]) {
      // nums[start] < nums[middle] < nums[end]
      Swap(nums[start], nums[middle]);
    }
    else if (nums[end] > nums[start]) {
      // nums[start] < nums[end] <= nums[middle]
      Swap(nums[start], nums[middle]);
    }
  }
  int key = nums[start];
  while (start < end) {
    while (start < end && nums[end] >= key) {
      --end;
    }
    if (start < end) {
      nums[start] = nums[end];
    }
    while (start < end && nums[start] <= key) {
      ++start;
    }
    if (start < end) {
      nums[end] = nums[start];
    }
  }
  nums[start] = key;

  return start;
}

#endif // QSORT_H

需要注意的有两个点:
1.Partition函数中的获得三数中值的方法。在注释中已经解释了做法,这里就不在多说。
2.Partition函数是基于下面的思想:
a)先把第一个元素保存在key中,三数中值的处理保证了第一个元素是三数中值;
b)从后往前找,找到第一个小于key的元素,如果start < end就将end位置的元素放到start的位置 (一开始的保存第一个元素,保证了start位置的元素不会被覆盖),注意,这时候end位置的元素是重复的
c)从前往后找,找到第一个大于key的元素, 如果start < end就将start位置的元素放到end的位置,因为b步骤保证了end位置是重复的,因此end位置的元素不会被覆盖。而这个赋值操作又导致可start位置的元素是重复的。
d)如果start >= end则结束循环,否则继续执行b步骤。
3.在结束循环的时候,start位置的元素左边是小于key,右边是大于key的,而start位置的元素是重复的,因此用key放到start位置,保证数组中元素不变(不是位置不变,内含元素不变)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值