快速排序的深入优化探讨

news2025/2/5 20:03:54

快排性能的关键点分析

决定快排性能的关键点是每次单趟排序后,key对数组的分割,如果每次选key基本⼆分居中,那么快排的递归树就是颗均匀的满⼆叉树,性能最佳。但是实践中虽然不可能每次都是⼆分居中,但是性能也还是可控的。但是如果出现每次选到最小值/最大值,划分为0个和N-1的子问题时,时间复杂度为O(N^2),数组序列有序时就会出现这样的问题,但是当数组中有大量重复数据时,之前的快速排序方法就会比较慢,因此我们需要更进算法。

三路排序

三路划分算法思想讲解:
当面对有大量跟key相同的值时,三路划分的核心思想有点类似hoare的左右指针和lomuto的前后指针的结合。核心思想是把数组中的数据分为三段 [比key小的值]、[跟key相等的值] 、[比key大的值],所以叫做三路划分算法。结合下图,理解⼀下实现思想:

  1. key默认取left位置的值。
  2. left指向区间最左边,right指向区间最右边,cur指向left+1位置。
  3. cur遇到比key小的值后跟left位置交换,换到左边,left++cur++
  4. cur遇到比key大的值后跟right位置交换,换到右边,right--
  5. cur遇到跟key相等的值后,cur++
  6. 直到cur>right结束

在这里插入图片描述

#include<stdio.h>
#include<time.h>
#include<stdlib.h>

void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ",a[i]);
	}
	printf("\n");
}

void QuickSort(int* a, int left,int right)
{
	if (left >= right)
		return;
	//随机选key
	int randi = left + (rand() % (right - left + 1));
	swap(&a[left], &a[randi]);

	int begin = left;
	int end = right;
	int key = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < a[key])
		{
			swap(&a[cur],&a[left]);
			cur++;
			left++;
		}
		else if (a[cur] > a[key])
		{
			swap(&a[cur], &a[right]);
			right--;
		}
		else if (a[cur] == a[key])
		{
			cur++;
		}
	}
	QuickSort(a,begin,left-1);
	QuickSort(a, right + 1, end);
}

int* sortArray(int* nums, int numsSize, int* returnSize) 
{
	srand((unsigned int)time(NULL));
	QuickSort(nums, 0, numsSize - 1);
	*returnSize = numsSize;
	return nums;
}

int main()
{
	int arr[] = {2,5,7,6,1,4,3,9,8};
	int n = sizeof(arr) / sizeof(arr[0]);
	Print(arr,n);
	int* tmp=sortArray(arr, n,&n);
	Print(tmp, n);
	return 0;
}

在这里插入图片描述

自省排序( introsort)

自省排序的思路就是进行自我侦测和反省,快排递归深度太深(sgi stl中使用的是深度为2倍排序元素数量的对数值)那就说明在这种数据序列下,选key出现了问题,性能在快速退化,那么就不要再进行快排分割递归了,改换为堆排序进行排序。

#include<stdio.h>
#include<time.h>
#include<stdlib.h>

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ",a[i]);
	}
	printf("\n");
}

void Swap(int* x, int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
//向下调整算法
void AdjustDown(int* a, int n, int parent)
{
    int child = parent * 2 + 1;
    while (child < n)
    {
        //选出左右孩⼦中⼤的那⼀个
        if (child + 1 < n && a[child + 1] > a[child])
        {
            ++child;
        }
        if (a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}
//堆排序
void HeapSort(int* a, int n)
{
    //建堆--向下调整建堆-- O(N)
    for (int i = (n - 1 - 1) / 2; i >= 0; --i)
    {
        AdjustDown(a, n, i);
    }
    int end = n - 1;
    while (end > 0)
    {
        Swap(&a[end], &a[0]);
        AdjustDown(a, end, 0);
        --end;
    }
}
//插入排序
void InsertSort(int* a, int n)
{
    for (int i = 1; i < n; i++)
    {
        int end = i - 1;
        int tmp = a[i];
        //将tmp插⼊到[0, end]区间中,保持有序
        while (end >= 0)
        {
            if (tmp < a[end])
            {
                a[end + 1] = a[end];
                --end;
            }
            else
            {
                break;
            }
        }
        a[end + 1] = tmp;
    }
}

void IntroSort(int* a, int left, int right, int depth, int defaultDepth)
{
    if (left >= right)
        return;

    //数组⻓度⼩于16的小数组,换为插入排序,简单递归次数
    if (right - left + 1 < 16)
    {
        InsertSort(a + left, right - left + 1);
        return;
    }

    //当深度超过2 * logN时改用堆排序
    if (depth > defaultDepth)
    {
        HeapSort(a + left, right - left + 1);
        return;
    }

    depth++;
    int begin = left;
    int end = right;
    
    int randi = left + (rand() % (right - left + 1));
    Swap(&a[left], &a[randi]);

    int prev = left;
    int cur = prev + 1;
    int keyi = left;
    while (cur <= right)
    {
        if (a[cur] < a[keyi] && ++prev != cur)
        {
            Swap(&a[prev], &a[cur]);
        }
        ++cur;
    }
    Swap(&a[prev], &a[keyi]);
    keyi = prev;
    
    IntroSort(a, begin, keyi - 1, depth, defaultDepth);
    IntroSort(a, keyi + 1, end, depth, defaultDepth);
}


void QuickSort(int* a, int left, int right)
{
    int logn = 0;
    int depth = 0;
    int N = right - left + 1;
    for (int i = 1; i < N; i *= 2)
    {
        logn++;
    }
    IntroSort(a, left, right, depth, logn * 2);
}

int* sortArray(int* nums, int numsSize, int* returnSize) 
{
	srand((unsigned int)time(NULL));
	QuickSort(nums, 0, numsSize - 1);
	*returnSize = numsSize;
	return nums;
}

int main()
{
	int arr[] = {2,5,7,6,1,4,3,9,8};
	int n = sizeof(arr) / sizeof(arr[0]);
	Print(arr,n);
	int* tmp=sortArray(arr, n,&n);
	Print(tmp, n);
	return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2056657.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【电子信息工程大学生的职业准备策略指南】

电子信息工程是一个快速发展且应用广泛的领域&#xff0c;对于大学生来说&#xff0c;为未来的职业生涯做好准备是一项重要任务。 目录 一、扎实的专业知识基础 二、实践能力的培养 三、编程与软件开发技能 四、创新思维与解决问题的能力 五、专业认证与资格证书 六、实习…

Element UI中el-dialog作为子组件如何由父组件控制显示/隐藏~

1、这里介绍的是将el-dialog作为组件封装便于复用&#xff0c;如何通过父组件控制子组件dialog的显示与隐藏。 2、思路:首先el-dialog是通过dialogVisible的值是否为true或false来控制显示与隐藏的。那么我们可以通过父传子props来将true&#xff08;即showFlag的值&#xff0…

影刀RPA--如何获取网页当页数据?

&#xff08;1&#xff09;点击数据抓取-选择需要获取数据的地方-会弹出是否是获取整个表格&#xff08;当前页面&#xff09; &#xff08;2&#xff09;点击“是”&#xff1a;则直接获取整个表格数据-点击完成即可 &#xff08;3&#xff09;点击“否”&#xff1a;如果你想…

发那科机器人常见的异常解决方案

第一类错误&#xff1a;示教板空白、机器人死机 判断方法&#xff1a;1、PSU 上红色LED 亮代表电源供给报警&#xff0c;可能保险F4熔断、查看CP2、CP3线路&#xff08;200ACV输出&#xff09;、更换PSU&#xff1b;绿色PIN熄灭代表电源供给单元未获得200V 的交流电源输入&…

1.检查现场运行环境-《篮球比赛展示管理系统》现场管理员角色操作手册

篮球比赛现场PC电脑,充当管理端&#xff0c;内装的浏览器要求是Firefox、Chrome、Edge、Safari等最新版市场主流浏览器等。

SAP EWM PMR

目录 1 简介 2 业务流程 3 业务操作 4 主数据设置 4.1 ERP 主数据 4.2 EWM 主数据 5 后台配置 5.1 主数据 5.2 GI 流程 5.3 仓库内部移动流程 5.4 仓库任务设置 5.5 集成 5.6 EWM 链接 1 简介 EWM PMR 分 2 部分功能&#xff0c;第一是 MES-Driven Staging&#…

OSI七层网络模型 /TCP/IP五层模型以及封装分用的详细讲解

文章目录 协议分层的好处OSI七层网络模型TCP/IP五层网络模型网络设备所在的分层(重点)封装和分用 协议分层的好处 第一点&#xff1a; 在网络通信中&#xff0c;如果使用一个协议来解决所有的问题&#xff0c;那么这个协议就会非常的庞大&#xff0c;非常不利于去学习和理解&…

Ciallo~(∠・ω・ )⌒☆第二十二篇 入门request请求库使用

请求库是用于发送HTTP请求的工具。常见的请求库有requests&#xff0c;它是一个功能强大且易于使用的HTTP库。 使用requests库发送GET请求&#xff1a; import requests url "https://httpbin.org/get"# 携带get请求参数 params {"pn": 10,"size&q…

PyTorch的torchvision内置数据集使用,transform+pytorch联合使用

一、PyTorch的torchvision内置数据集介绍 我们前面的文章里谈到的数据集是我们自己找的一些自定义数据集。那么在Pytorch中存在2种数据集&#xff08;Dataset&#xff09;&#xff0c;即内置数据集&#xff08;Built-in dataset&#xff09;和自定义数据集&#xff08;Custom d…

架站点云自动拼接

southLidar pro 软件里面的架站点云无目标、无传感器的点云自动拼接算法&#xff0c;该算法的特征是速度快&#xff0c;精度高、稳定性高&#xff0c;大部分的场景都能一键自动拼接成功。速度、稳定性&#xff1a;比RealWorks 12、SCENE 2019等软件都快。精度&#xff1a;高于S…

python-docx 实现 Word 办公自动化

前言&#xff1a;当我们需要批量生成一些合同文件或者简历等。如果手工处理对于我们来说不仅工作量巨大&#xff0c;而且难免会出现一些问题。这个时候运用python处理word实现自动生成文件可极大的提高工作效率。 python-docx是python的第三方插件&#xff0c;用来处理word文件…

UPS快递查询|利用API对接国际物流轨迹

聚合国内外1500家快递公司的物流信息查询服务&#xff0c;使用API接口查询UPS快递的便捷步骤&#xff0c;首先选择专业的数据平台的快递API接口&#xff1a;https://www.tanshuapi.com/market/detail-68 以下示例是参考的示例代码&#xff1a; import requestsurl "http…

gstreamer系列 -- 获取媒体信息

Basic tutorial 9: Media information gathering

PyCharm单步调试

1、先在入口设置断点&#xff0c;再点击爬虫图标&#xff08;shift F9&#xff09;开始调试 调试图标如图&#xff1a; 2、蓝色光标表示当前运行在这行 3、快捷键 F7&#xff1a;进入当前行函数 F8&#xff1a;单步 F9&#xff1a;全速运行

语言基础/分析和实践 CC++ 位域结构数据类型

文章目录 概述位域和结构体的关系位域/位段的概念位域定义的语法位段结构的利弊 结构字段的定义和存储顺序小端系统上的结构字段存储大端系统上的结构字段存储小结&#xff08;承上启下&#xff09; 位域结构的存储&#xff08;对齐、填充、跨字节&#xff09;位域结构的Bit位序…

admob 广告分析

1、测试广告集成&#xff0c;官方文档 https://developers.google.com/admob/android/quick-start?hlzh-cn dependencies {implementation("com.google.android.gms:play-services-ads:23.3.0") }2、广告集成&#xff0c;集成测试激励广告。 public class MainAct…

学习大数据DAY41 Hive 分区表创建

目录 分区表 分区表应用场景 oracle 分区表种类 oracle 分区-范围分区 oracle 分区-列表分区 oracle 分区-散列分区 oracle 分区-组合分区 oracle 分区-分区表操作 hive 分区-创建分区表 hive 分区-分区表操作 hive 分区-动态分区表配置 上机练习 分区表 分区是将一…

常见古典密码介绍

文章目录 Vigenre 密码变异凯撒摩斯密码栅栏密码加密方式一加密方式二 Caesar和ROT13的区别ROT13加密原理ROT13查找表 Vigenre 密码 由于频率分析法可以有效的破解单表替换密码&#xff0c;法国密码学家维吉尼亚于1586年提出一种多表替换密码&#xff0c;   即维吉尼亚密码&…

什么是局域网管理软件?这款局域网管理软件简直太好用了丨好物分享

在信息技术日新月异的今天&#xff0c;企业的内部网络管理如同古代战场上的排兵布阵&#xff0c;需有精良之器以应对复杂多变的局势。 局域网&#xff0c;作为企业内部信息交流与资源共享的重要平台&#xff0c;其管理效率与安全性直接影响到企业的运营与发展。 一、局域网管理…

Docker-安装软件

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装MySQL&#xff08;一&#xff09;拉取MySQL镜像&#xff08;二&#xff09;运行MySQL容器&#xff08;1&#xff09;数据卷概念 &#xff08;三&#x…