C++快速排序及优化(三路快排)

news/2024/5/19 21:47:48 标签: c++, 算法, 快速排序

快速排序常用模板

  • 这里的下标范围是[left,right]

代码模板:

void Quick_Sort(vector<int> &nums, int left, int right)
{
    if (left < right) {
        int i = left, j = right;
        // 中轴值随机获取
        swap(nums[rand()%(right - left + 1) + left], nums[left]);
        int temp = nums[left];
        while (i < j) {
            while (i < j && nums[j] >= temp) {
                --j;
            }
            swap(nums[i], nums[j]);
            while (i < j && nums[i] <= temp) {
                ++i;
            }
            swap(nums[i], nums[j]);
        }
        // 此时的i=j,所以不用纠结是i-1还是j-1
        Quick_Sort(nums, left, i - 1);
        Quick_Sort(nums, i + 1, right);
    }
}
  • 但是当数组区间较小时,如果继续使用快排的话会导致效率降低。此时的数组已经基本有序,改用插入排序提高排序的效率

优化1:快排+插入

代码模板:

void Quick_Sort(vector<int> &nums, int left, int right)
{
    if (left < right) {
    	// 当区间较小时,改用插入排序,此处不进行实现
    	if (right - left < 15) {
    		Insert_Sort(nums, left, right);
    		return ;
    	}
        int i = left, j = right;
        // 中轴值随机获取
        swap(nums[rand()%(right - left + 1) + left], nums[left]);
        int temp = nums[left];
        while (i < j) {
            while (i < j && nums[j] >= temp) {
                --j;
            }
            swap(nums[i], nums[j]);
            while (i < j && nums[i] <= temp) {
                ++i;
            }
            swap(nums[i], nums[j]);
        }
        Quick_Sort(nums, left, i - 1);
        Quick_Sort(nums, i + 1, right);
    }
}
  • 但是如果数组区间内相等元素较多算法最后可能退化为O(n^2)的算法,所以就有了优化三路快排

优化2:三路快排

思路:

  • 核心思想:将数组分为三部分

    • 小于中轴值部分 [left,l-1]
    • 等于中轴值部分 [l,r]
    • 大于中轴值部分 [r+1,right]
  • i指针从左往右遍历

    • 如果nums[i]小于temp的值,就交换nums[l]和nums[i]i和l同时向右移动
    • 如果nums[i]等于temp的值,i往右移动
    • 如果nums[i]大于temp的值,就交换nums[r]和nums[i]r向左移动i不动
      • 因为i跟r交换后,还需要再比较交换后的数,所以i不能动
  • 遍历结束时

    • l在第一个等于中轴值位置上
    • r在最后一个等于中轴值位置上
    • i在一个等于中轴值位置的后一个,即i = r + 1
      在这里插入图片描述

代码模板:

// 三路快排
void Quick_Sort(vector<int> &nums, int left, int right)
{
    if (left < right) {
        // 当区间较小时,改用插入排序,此处不进行实现
        if (right - left < 15) {
            Insert_Sort(nums, left, right);
            return;
        }
        int l = left;
        int r = right;
        int i = left + 1; // i = left也可以,不过会多一次无意义的比较
        // 随机中轴值
        swap(nums[left], nums[rand() % (right - left + 1) + left]);
        int temp = nums[left];
        while (i <= r) {
            if (nums[i] < temp) {
                swap(nums[i++], nums[l++]);
            }
            else if (nums[i] == temp) {
                i++;
            }
            // 换完以后的nums[i]不一定比temp小,所以i不能移动
            else if (nums[i] > temp) {
                swap(nums[i], nums[r--]);
            }
        }
        // 循环结束后,[l,r]区间内的数都为temp,i在r+1的位置
        // [left,l-1]
        Quick_Sort(nums, left, l - 1);
        // [r+1,right]
        Quick_Sort(nums, r + 1, right);
    }
}

提供代码测试三路快排

#include <bits/stdc++.h>
using namespace std;

class Print
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};

// 插入排序
void Insert_Sort(vector<int> &nums, int left, int right)
{
    // 注意下标从left开始
    for (int i = left; i <= right; i++) {
        int temp = nums[i];
        int j;
        // j用来找比nums[i]小的数,也就是j+1是nums[i]的位置
        // 如果nums[j]比nums[i]大的话,就移到后面去
        // 注意:循环里的判断要用temp不能用nums[i],nums[i]在循环里可能会改变
        for (j = i - 1; j >= 0 && nums[j] > temp; j--) {
            nums[j + 1] = nums[j];
        }
        nums[j + 1] = temp;
    }
}

// 三路快排
void Quick_Sort(vector<int> &nums, int left, int right)
{
    if (left < right) {
        // 当区间较小时,改用插入排序,此处不进行实现
        if (right - left < 15) {
            Insert_Sort(nums, left, right);
            return;
        }
        int l = left;
        int r = right;
        int i = left + 1; // i = left也可以,不过会多一次无意义的比较
        // 随机中轴值
        swap(nums[left], nums[rand() % (right - left + 1) + left]);
        int temp = nums[left];
        // cout <<"temp: " << temp << " | " << endl;
        // for_each(nums.begin(), nums.end(), Print());
        // puts("");
        while (i <= r)
        {
            if (nums[i] < temp) {
                swap(nums[i++], nums[l++]);
                // cout << nums[i] << " < " << temp << " 跟l交换之后,l和i同时移动" << endl;
            }
            else if (nums[i] == temp) {
                i++;
                // cout << nums[i] << " = " << temp << " i移动" << endl;
            }
            // 换完以后的nums[i]不一定比temp小,所以i不能移动
            else if (nums[i] > temp) {
                swap(nums[i], nums[r--]);
                // cout << nums[i] << " > " << temp << " 跟r交换之后,r向左移动" << endl;
            }
            // for_each(nums.begin(), nums.end(), Print());
            // puts("");
        }
        // puts("\n");
        // 循环结束后,[l,r]区间内的数都为temp,i在r+1的位置
        // [left,l-1]
        Quick_Sort(nums, left, l - 1);
        // [r+1,right]
        Quick_Sort(nums, r + 1, right);
    }
}

int main()
{
    vector<int> nums = {3, 1, 6, 4, 5, 3, 3, 1, 7, 11, 5, 7, 3, 1, 1, 2, 7, 5, 9, 10, 8};
    Quick_Sort(nums, 0, nums.size() - 1);
    for_each(nums.begin(), nums.end(), Print());
}

http://www.niftyadmin.cn/n/1735400.html

相关文章

vs code C++万能头无法使用

vs code C的万能头突然无法使用&#xff0c;如下图所示 解决方案&#xff1a; 进入path设置 根据自己的保存路径&#xff0c;找到这个文件夹 进入include文件夹中&#xff0c;新建一个bits文件夹&#xff0c;在里面创建一个stdc.h文件。 stdc.h文件内容如下&#xff1a; // C#…

source insight 中文间距过小汉字重合

** source insight 字符串中中文间距过小 ** 问题如下&#xff1a; 输入中文时正常,但用鼠标选择时会乱码,删除一个汉字需要delete两次&#xff0c;一个汉字由两个乱码组成&#xff0c;如下图所示&#xff1a; 分析原因&#xff1a; 这是因为字体间距太小&#xff0c;导致中…

Unity2018连接MySQL数据库(总结版)

本文章涉及的编译器版本&#xff1a; Unity2018.4.18f1 mysql-8.0.28 Visual Studio2019 文章目录1. Visual Studio中下载MySql.Data插件2. MySql官网下载插件3. 尝试Visual Studio连接Mysql4. 在Unity中导入dll文件5. 编写SQL脚本进行测试6. 打包后无法连接数据库1. Visual …

ubuntu20.4无法生成core文件如何解决

1、查看coredump是否打开 首先进入root模式&#xff0c;查看当前core文件大小的限制 ulimit -c得到的结果跟对应含义如下&#xff1a; 0 表示关闭&#xff0c;不会生成core文件unlimited 表示core文件的大小无限制 2、修改core文件大小限制 可以使用下面的命令来修改这一值…

Ubuntu下安装mysql服务并设置远程访问

Ubuntu下安装mysql服务并设置远程访问 文章目录Ubuntu下安装mysql服务并设置远程访问1. 卸载mysql服务2. 安装mysql服务2.1 安装mysql服务2.2 登录root并修改密码2.3&#xff08;如果修改失败需要修改秘密等级&#xff09;2.4 安装libmysqlclient-dev2.5 安装aptitude3. 测试C语…

Linux下lsof命令的用法

lsof命令基本使用方法 文章目录lsof命令基本使用方法1. lsof介绍1.1 lsof命令使用1.2 lsof中各列信息关于FD的补充说明1.3 lsof的选项信息1.4 具体使用方法和示例1.5 一些疑问2. 文件句柄及空间释放问题1. lsof介绍 lsof可以查看你所打开的文件、打开文件的进程、甚至可以找回被…

在openwrt使用C语言增加ubus接口(包含C uci操作)

在openwrt使用C语言增加ubus接口&#xff08;包含C uci操作&#xff09; 文章目录在openwrt使用C语言增加ubus接口&#xff08;包含C uci操作&#xff09;创建自己的软件包软件包结构编写代码和启动脚本&#xff08;重点&#xff09;案例大致分析实现过程ubus_demo.initubus_de…

Lua调用C的动态库步骤及接口分析

Lua调用C的动态库 C语言可以完成一些lua不好实现的功能&#xff0c;当程序主体使用lua完成时&#xff0c;便需要掌握该技巧调用C来帮助我们达到目的&#xff0c;通过调用C的动态库简化操作流程。 大致流程如下&#xff1a; 使用C语言编写方法提供给lua调用将C文件打包成动态…