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());
}
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值