【基础算法】01:快速排序

快速排序


1.快速排序

(1)主要思想

快速排序是一种改进的交换排序算法,是对冒泡排序算法的优化,快速排序的主要思想是分治思想:

  1. 任取一个元素作为中心pivot
  2. 所有比pivot小的元素都放在前面,比pivot大的元素都放在后面,形成左右两个子表
  3. 对各子表重新选择中心元素pivot,并仍然按照该规则进行调整(递归操作)
  4. 直到每个子表的元素都只剩一个,结束排序(递归结束)

关于pivot插入到中间,并将元素分为左右子表的实现细节(核心)

  1. 方案1:新数组用于存放数据(空间消耗较大)

    • 新开一个数组用于存放数据,比pivot值大的元素放右边(从右向左),比pivot值小的元素放左边(从左向右),最后pivot值放到中间的空出的位置上,
  2. 方案2:减小使用的空间(只需要1个额外位置)以下是个人认为的快排的标准实现方式,

    • 将pivot值存储到 idx=0 的下标位置,此时low位置可用
    • 开始针对high指针,当high指针指向的值大于 nums[0] 时,将nums[high]搬运到 low 所指的位置,否则high--
    • 开始针对low指针,当low指针指向的值小于 nums[0] 时,将nums[low]搬运到 high 所指的位置,否则low++
    • low = high 时,左右子表划分完毕,将pivot值重新放回到 low = high 的位置,正式结束分表,
    • 特点:每一趟的子表的形成是采用从两头向中间交替式的逼近法
(2)局限性

快速排序局限性:

  1. 快速排序是所有内部排序方法中最好的一个
  2. 快速排序是一种不稳定的排序方法
  3. 快速排序不是原地排序(程序中使用了递归需要递归调用栈的支持,而栈的长度取决于递归调用的深度)
  4. 平均时间复杂度O(nlog2n):QSort—O(log2n)、Partition—O(n)
  5. 平均情况下需要使用O(logn)的栈空间,最坏情况下栈空间可以达到O(n)
  6. 快速排序不适用与对原本有序或基本有序的记录序列进行排序(划分元素值的随机性越好,排序速度越快,即非自然排序
  7. 改变划分元素的选取方法,最多只能改变算法平均时间性能,无法改变最坏情况下的时间性能O(n2)
  8. 由于每次枢轴pivot记录的关键字都是大于其他所有记录的关键字,致使一次划分之后得到的子序列(1)的长度为0,这时的快速排序就已经退化成为了没有改进措施的冒泡排序了。
(3)快速排序模板
区间划分思路1:
  1. 将low值取出单独存放在pivot中,
  2. 对数组进行排序,排序方式为:当右侧比pivot小时将high覆盖low处值、当左侧比pivot大时将low覆盖high处
  3. 直到low与high相遇时,将pivot放回low与high相遇的位置
  4. 一轮排序完毕(pivot所在位置为最终有序位置
  5. 递归处理左右两边区间
// pivot划分区间
int partition(SqList &sqlist, int low, int high) {
    int pivot = sqlist.data[low];
    while (low < high) {
        while (sqlist.data[high] >= pivot && low < high) high--;
        sqlist.data[low] = sqlist.data[high];
        while (sqlist.data[low] <= pivot && low < high) low++;
        sqlist.data[high] = sqlist.data[low];
    }
    sqlist.data[low] = pivot;
    return low;
}

// 递归划分
void quick_sort(SqList &sqlist, int low, int high) {
    if (low >= high) return;
    int p_idx = partition(sqlist, low, high);
    quick_sort(sqlist, low, p_idx - 1);
    quick_sort(sqlist, p_idx + 1, high);
}
void quick_sort(SqList &sqlist, int low, int high) {
    if (low >= high) return;
    int pivot = sqlist.data[low];
    int i = low, j = high;
    while (i < j) {
        while(sqlist.data[j] >= pivot && i < j) j--;
        sqlist.data[i] = sqlist.data[j];
        while(sqlist.data[i] <= pivot && i < j) i++;
        sqlist.data[j] = sqlist.data[i];
    }
    quick_sort(sqlist, low, i - 1);
    quick_sort(sqlist, i + 1, high);
}
区间划分思路2:
  1. 在low与hight的基础上再定义两个指针i、j,两个指针分别向中间移动
  2. 当 i 指针指向的数字小于pivot时,i 指针继续向右移动,直到 i 大于等于pivot时停下
  3. 当 j 指针指向的数字大于pivot时,j 指针继续向左移动,直到 j 小于等于pivot时停下
  4. 当 i、 j指针都停下时,若 i、j处于正常位置则交换对应值
  5. 否则表示while循环结束
  6. 递归处理左右两边区间

返回时要注意边界问题,low、high有可能会出现交叉的情况,

// pivot划分区间
int partition(SqList &sqlist, int low, int high) {
    int pivot = sqlist.data[low];
    low--; high++;
    while (low < high) {
        do low++; while(sqlist.data[low] < pivot);
        do high--; while(sqlist.data[high] > pivot);
        if (low < high) swap(sqlist.data[low], sqlist.data[high]);
    }
    return high;
}

//递归划分
void quick_sort(SqList &sqlist, int low, int high) {
    if (low >= high) return;
    int p_idx = partition(sqlist, low, high);
    quick_sort(sqlist, low, p_idx);
    quick_sort(sqlist, p_idx + 1, high);
}
void quick_sort(SqList &sqlist, int low, int high) {
    if (low >= high) return;
    int pivot = sqlist.data[low];
    int i = low - 1, j = high + 1;
    while (i < j) {
        do i++; while (sqlist.data[i] < pivot);
        do j--; while (sqlist.data[j] > pivot);
        if (i < j) swap(sqlist.data[i], sqlist.data[j]);
    }
    quick_sort(sqlist, low, j);
    quick_sort(sqlist, j + 1, high);
}

2.AcWing785.快速排序

#include <iostream>
using namespace std;

typedef struct {
    int data[100005];  //表数据
    int length;     //顺序表长度
} SqList;

void quick_sort(SqList &sqlist, int low, int high);

int main() {
    SqList sqlist;
    cin >> sqlist.length;
    for (int i = 0; i < sqlist.length; ++i) cin >> sqlist.data[i];
    // sqlist.length = 5;
    // int nums[5] = {3, 1, 2, 4, 5};
    // for (int i = 0; i < sqlist.length; ++i) sqlist.data[i] = nums[i];

    quick_sort(sqlist, 0, sqlist.length - 1);

    for (int i = 0; i < sqlist.length; ++i) cout << sqlist.data[i] << " ";
    cout << endl;
}

// 快速排序算法
int partition(SqList &sqlist, int low, int high) {
    int pivot = sqlist.data[low];
    while (low < high) {
        while (sqlist.data[high] >= pivot && low < high) high--;
        sqlist.data[low] = sqlist.data[high];
        while (sqlist.data[low] <= pivot && low < high) low++;
        sqlist.data[high] = sqlist.data[low];
    }
    sqlist.data[low] = pivot;
    return low;
}

void quick_sort(SqList &sqlist, int low, int high) {
    if (low >= high) return;
    int p_idx = partition(sqlist, low, high);
    quick_sort(sqlist, low, p_idx - 1);
    quick_sort(sqlist, p_idx + 1, high);
}

3.AcWing786.第k个数

tips:文章内容参考acwing算法刷题课程,题解图示内容及代码根据老师课程、以及自己对知识的理解,进行二次整理和部分补充,仅供学习参考使用,不可商业化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值