Acwing 排序

1.快速排序

主要思想:基于分治思想。通过选择一个基准元素,将数组分为两部分,左边部分元素都小于等于基准,右边部分元素都大于等于基准。然后对这两部分分别递归地进行排序。

分区逻辑:双指针算法

  • 左指针i从左往右找到第一个大于等于基准的元素
  • 右指针从右往左找到第一个小于等于基准的元素
  • 交换两个元素,使得左侧部分小于等于基准,右侧部分大于等于基准

l是待排序区间的左边界,r是右边界

  1. 确定分界点x(基准),可以取左边界的值q[l],或右边界的值q[r],或者中间位置的值q[(l + r)/2]
  2. 根据基准值,调整区间,使得左半边区间的值全都≤ x,右半边区间的值全都≥ x .
    采用双指针:左指针i从左边界l-1开始,往右扫描,右指针j从右边界r+1开始,往左扫描

为什么初始是l-1,r+1?
因为后续的不管执行交换与否,首先都先将i,j向内移动一位,所以一开始的两个指针都设置为超出边界一个位置

当满足条件q[i] < x时,i右移;直到不满足条件时,即q[i] >= xi停下;

然后移动右指针jj 当满足条件q[j] > x时,j左移;直到不满足条件时,即q[j] <= xj停下;

交换q[i]q[j]i右移一位,j左移一位,重复上面的操作,直到ij相遇(最终i和j的位置为:i==j或i=j+1)。 此时左半区间的数都满足≤x,且左半区间的最后一个数的下标为j,右半区间的数都满足≥ x,且右半区间的第一个数的下标为i

  1. 递归处理左右两段
    • 若用j来作为区间的分界,则[l, j] 都是≤x[j + 1, r]都是≥x
    • 若用i来作为区间的分界,则[l, i - 1]都是≤x[i, r]都是≥x

注意:
递归取[l, j][j + 1, r]区间时,基准值不能取右边界x=q[r],不然会出现死循环问题,此时常取左边界x=q[l]或中间值 (eg:1,2 会出现死循环)
同理当递归取[l, i - 1][i,r]区间时,基准值不能取左边界x=q[l],不然会出现死循环问题,此时常取左边界x=q[r] 或中间值 (eg:1,2 会出现死循环)
快排不稳定,平均时间复杂度nlogn。最坏情况(例如数组已经有序的情况下),时间复杂度为 𝑂 ( 𝑛 2 ) 𝑂(𝑛^2) O(n2),但通过选择中间值作为基准,可以减少最坏情况的发生。

这里给出一个快排的板子:

// 快速排序函数,q[] 是待排序的数组,l 是左边界,r 是右边界
void quick_sort(int q[], int l, int r) {
    if (l >= r) return;  // 递归终止条件:当左边界大于或等于右边界时停止递归

    // 选择中间元素作为基准数,并初始化左右指针 i 和 j
    int x = q[(l + r) / 2], i = l - 1, j = r + 1;

    // 使用双指针法进行分区
    while (i < j) {
        // 从左边开始找到第一个大于等于基准数的元素
        do i++; while (q[i] < x);
        // 从右边开始找到第一个小于等于基准数的元素
        do j--; while (q[j] > x);
        // 如果 i 和 j 没有相遇,交换 q[i] 和 q[j],确保左边都比基准数小,右边都比基准数大
        if (i < j) swap(q[i], q[j]);
    }
    
    // 递归处理左半部分
    quick_sort(q, l, j);
    // 递归处理右半部分
    quick_sort(q, j + 1, r);
}

Acwing 785.快速排序
在这里插入图片描述
具体实现代码(详解版):

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e6 + 10;  

int n;        
int q[N];     // 存储待排序的数组

// 快速排序函数,q[] 是待排序的数组,l 是左边界,r 是右边界
void quick_sort(int q[], int l, int r) {
    if (l >= r) return;  // 递归终止条件:当左边界大于或等于右边界时停止递归

    // 选择中间元素作为基准数,并初始化左右指针 i 和 j
    int x = q[(l + r) / 2], i = l - 1, j = r + 1;

    // 使用双指针法进行分区
    while (i < j) {
        // 从左边开始找到第一个大于等于基准数的元素
        do i++; while (q[i] < x);
        // 从右边开始找到第一个小于等于基准数的元素
        do j--; while (q[j] > x);
        // 如果 i 和 j 没有相遇,交换 q[i] 和 q[j],确保左边都比基准数小,右边都比基准数大
        if (i < j) swap(q[i], q[j]);
    }
    
    // 递归处理左半部分
    quick_sort(q, l, j);
    // 递归处理右半部分
    quick_sort(q, j + 1, r);
}

int main() {
    cin >> n;  

   
    for (int i = 0; i < n; i++) cin >> q[i];

    
    quick_sort(q, 0, n - 1);

   
    for (int i = 0; i < n; i++) cout << q[i] << ' ';
    cout << endl;  

    return 0;  
}

Acwing 786.第k个数
在这里插入图片描述
实现思路:直接进行快排,然后注意第k个数的数组下标是k-1,即答案就是q[ k - 1].

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e6 + 10;
int n , k;
int q[N];

//快速排序
void quick_sort(int q[],int l , int r){
    if(l >= r) return ;
    int x = q[(l + r) / 2], i = l - 1, j = r + 1;
    
    while(i < j){
        do i ++ ; while(q[i] < x);
        do j -- ; while(q[j] > x);
        if(i < j) swap(q[i] , q[j]);
    }
    quick_sort(q, l ,j);
    quick_sort(q, j + 1 ,r);
}

int main(){
    cin >> n >> k ;
    for(int i = 0 ; i < n ; i ++) cin >> q[i];
    
    quick_sort(q, 0 , n - 1);
    
    //第k个数
    cout << q[k - 1] << endl;
    
    return 0;
}

2.归并排序

主要思想:也是基于分治思想

  • 先确认分界点(下标):一般取中间点(l + r) /2;
  • 对左右两个子数组分别进行递归排序
  • 将两个排好序的子数组合二为一

实现思路:设置左右指针和一个临时数组temp,左指针指向左区间的的第一个元素,右指针指向右区间的第一个元素,循环比较左右指针所指元素,两者较小的元素放入temp数组中,指针后移继续比较。直至某一指针到达末尾,将其中一个未放置完的区间的数再都放入temp数组。

归并排序的时间复杂度为 𝑂 ( 𝑛 l o g 𝑛 ) 𝑂(𝑛log𝑛) O(nlogn),即使在最坏的情况下也是如此。它的性能较为稳定,适用于大规模数据的排序任务

归并排序的板子:

const int N = 1e6 + 10;  // 定义数组容量为 10^6 + 10

int n;
int q[N], temp[N];  // q[] 存储待排序的数组,temp[] 是归并时的临时数组

// 归并排序函数,q[] 是待排序的数组,l 是左边界,r 是右边界
void merge_sort(int q[], int l, int r) {
    if (l >= r) return;  // 递归终止条件:如果只有一个元素,直接返回

    int mid = (l + r) / 2;  // 取中间点,将数组分成两部分

    // 递归处理左半部分
    merge_sort(q, l, mid);
    // 递归处理右半部分
    merge_sort(q, mid + 1, r);

    // 合并两个有序的部分
    int i = l, j = mid + 1, k = 0;  // i 追踪左半部分,j 追踪右半部分,k 追踪临时数组
    while (i <= mid && j <= r) {
        if (q[i] <= q[j]) temp[k++] = q[i++];  // 左边小或相等时,将左边的元素放入临时数组
        else temp[k++] = q[j++];               // 否则将右边的元素放入临时数组
    }

    // 如果左半部分还有剩余元素,放入临时数组
    while (i <= mid) temp[k++] = q[i++];
    // 如果右半部分还有剩余元素,放入临时数组
    while (j <= r) temp[k++] = q[j++];

    // 将临时数组中的元素拷贝回原数组的对应位置
    for (i = l, k = 0; i <= r; i++, k++) q[i] = temp[k];
}

Acwing 787.归并排序
在这里插入图片描述
具体实现代码(详解版):

#include <iostream>
using namespace std;

const int N = 1e6 + 10;  // 定义数组容量为 10^6 + 10

int n;
int q[N], temp[N];  // q[] 存储待排序的数组,temp[] 是归并时的临时数组

// 归并排序函数,q[] 是待排序的数组,l 是左边界,r 是右边界
void merge_sort(int q[], int l, int r) {
    if (l >= r) return;  // 递归终止条件:如果只有一个元素,直接返回

    int mid = (l + r) / 2;  // 取中间点,将数组分成两部分

    // 递归处理左半部分
    merge_sort(q, l, mid);
    // 递归处理右半部分
    merge_sort(q, mid + 1, r);

    // 合并两个有序的部分
    int i = l, j = mid + 1, k = 0;  // i 追踪左半部分,j 追踪右半部分,k 追踪临时数组
    while (i <= mid && j <= r) {
        if (q[i] <= q[j]) temp[k++] = q[i++];  // 左边小或相等时,将左边的元素放入临时数组
        else temp[k++] = q[j++];               // 否则将右边的元素放入临时数组
    }

    // 如果左半部分还有剩余元素,放入临时数组
    while (i <= mid) temp[k++] = q[i++];
    // 如果右半部分还有剩余元素,放入临时数组
    while (j <= r) temp[k++] = q[j++];

    // 将临时数组中的元素拷贝回原数组的对应位置
    for (i = l, k = 0; i <= r; i++, k++) q[i] = temp[k];
}

int main() {
    cin >> n;  // 输入数组长度

    // 读取 n 个元素存入数组 q 中
    for (int i = 0; i < n; i++) cin >> q[i];

    // 调用归并排序算法,排序整个数组
    merge_sort(q, 0, n - 1);

    // 输出排序后的数组
    for (int i = 0; i < n; i++) cout << q[i] << ' ';
    cout << endl;  // 换行

    return 0;  // 程序结束
}

Acwing.788求逆序对的数量
在这里插入图片描述
实现思路:

  • 根据归并排序的思想,将数组分为各自有序的左右两个区间[l,mid],[mid+1,r],采用双指针开始分别指向两个区间的第一个元素,相互比较选出较小的那个元素,然后后移,不断循环,直到一个区间遍历完。
  • 在比较过程中,设i指向左区间,j指向右区间,由于两个区间各自有序,逆序对只会出现一种情况,即左区间存在大于右区间元素的元素。
  • a[i]>a[j],则左区间中从i开始到mid的元素都大于a[j],与a[j]组成逆序对,数量为mid-i+1

注意:对于给定n个数,最坏的情况为逆序,则逆序对数为n(n-1)/2个,题中数据个数范围为100000,则最大结果会超出int的存储范围(-231~231-1),所以虽好使用long long来存储最终结果

具体实现代码(详解版):

#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;  
const int N = 1e6 + 10;  
int n;  
int q[N], temp[N];  // q[] 用于存储输入数组,temp[] 用于合并时的临时数组

// 归并排序函数,返回区间 [l, r] 的逆序对数量
LL merge_sort(int l, int r) {
    if (l >= r) return 0;  // 如果区间内只有一个元素,返回 0,表示没有逆序对
    
    int mid = (l + r) / 2;  // 找到中间位置
    
    // 递归处理左半部分和右半部分,并计算各自的逆序对数量
    LL res = merge_sort(l, mid) + merge_sort(mid + 1, r);
    
    // 合并两个有序区间,同时统计逆序对
    int i = l, j = mid + 1, k = 0;  // i 指向左区间,j 指向右区间,k 是 temp 的索引
    while (i <= mid && j <= r) {
        if (q[i] <= q[j]) {
            temp[k++] = q[i++];  // 如果左区间的元素小于等于右区间,取左区间的元素
        } else {
            temp[k++] = q[j++];  // 否则取右区间的元素
            res += mid - i + 1;  // 统计逆序对的数量,mid - i + 1 表示当前左区间剩余的元素个数
        }
    }
    
    // 如果左区间还有剩余元素,直接加入 temp[]
    while (i <= mid) temp[k++] = q[i++];
    // 如果右区间还有剩余元素,直接加入 temp[]
    while (j <= r) temp[k++] = q[j++];
    
    // 将 temp[] 中的元素复制回原数组 q[]
    for (int i = l, k = 0; i <= r; i++, k++) q[i] = temp[k];
    
    return res;  // 返回逆序对的数量
}

int main() {
   
    cin >> n;
   
    for (int i = 0; i < n; i++) cin >> q[i];
    
    // 输出逆序对的数量
    cout << merge_sort(0, n - 1) << endl;
    
    return 0;
}

以上就是两种经典常考的排序算法,快排的思想是选择基准点,然后进行分区;而归并排序是选择一个位置,将原序列划分为两个序列,再分别进行排序,最后合并为一个有序数组。可以看到各有优缺点,下面进行一个简答的总结:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《RabbitMQ篇》消息应答和发布确认

消息应答 消息应答机制&#xff1a;消费者接收信息并处理完之后&#xff0c;告诉rabbitmq该信息已经处理&#xff0c;rabbitmq可以把该信息删除了. 消息自动重新入队&#xff1a;如果处理某个消息的消费者异常关闭了&#xff0c;没有发送ACK确认&#xff0c;rabbitmq会将其重…

金九银十软件测试面试题(800道)

今年你的目标是拿下大厂offer&#xff1f;还是多少万年薪&#xff1f;其实这些都离不开日积月累的过程。 为此我特意整理出一份&#xff08;超详细笔记/面试题&#xff09;它几乎涵盖了所有的测试开发技术栈&#xff0c;非常珍贵&#xff0c;人手一份 肝完进大厂 妥妥的&#…

postgresql 安装

一、下载 PostgreSQL: File Browser 下载地址 PostgreSQL: File Browser 上传到服务器,并解压 二、安装依赖 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel 创建postgresql 和目录 useradd …

Java-基础

1. 导入模块不能纯粹的复制粘贴&#xff0c;要从new里导入&#xff0c;因为前者建立不了关联 2. 数组 String[] name{"张三","李四","王五"};int[] numsnew int[]{1,2,3};//二维String[][] names{{"张三","李四"},{"…

39 C 语言枚举类型、枚举常量、枚举变量、枚举的遍历、枚举数组、枚举与 switch

目录 1 什么是枚举 2 定义枚举类型 2.1 语法格式 2.2 枚举元素的特点 2.3 案例演示 3 枚举变量 3.1 什么是枚举变量 3.2 定义枚举变量的多种方式 3.3 案例演示 1&#xff1a;标准版枚举类型 3.4 案例演示 2&#xff1a;简化版枚举类型 3.5 案例演示 3&#xff1a;匿…

uniapp学习(005-2 详解Part.2)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战&#xff0c;开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第41p-第p51的内容 文章目录 mainifest.json文件配置获取微信小程序appid注册微信小程序微信小程序控制台图形界…

22. 括号生成【回溯】

文章目录 22. 括号生成解题思路Go代码 22. 括号生成 22. 括号生成 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["((()))",&quo…

node.js服务器基础

node.js的事件循环 node.js是基于事件驱动的&#xff0c;通常在代码中注册想要等待的事件&#xff0c;设定好回调函数&#xff0c;当事件触发的时候就会调用回调函数。如果node.js没有要处理的事件了&#xff0c;那整个就结束了;事件里面可以继续插入事件&#xff0c;如果有事…

手撕数据结构 —— 带头双向循环链表(C语言讲解)

目录 0.前言 1.什么是带头双向循环链表 理解带头 ​编辑 理解双向 理解循环 2.带头双向循环链表的实现 List.h文件中接口总览 具体实现 结点的定义 申请结点 初始化 打印链表 尾插 尾删 头插 头删 ​编辑​编辑 获取大小 查找 在指定位置前插入 ​编辑…

数据结构--线性表双向链表的实现

目录 思路设计 总体思维导图 插入部分 头插法尾插法 任意位置插入 删除部分 头结点 尾节点 中间节点 只有头结点且删除的就是头结点 ​编辑 清空链表部分 遍历清空链表的所有节点 不遍历清空 各部分代码 Main部分 MyListedList部分 IndexOutOfException部分 …

YOLO11改进 | 注意力机制 | 结合静态和动态上下文信息的注意力机制

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 上下文Transformer&#xff08;CoT&…

WPF中的布局

布局原则 1、不显式设置元素大小。 2、不使用绝对定位。 元素应该根据容器的内容来进行排列。绝对定位在开发前期会带来一些便捷&#xff0c;但扩展性比较差。一旦显示器尺寸或分辨率发生改变&#xff0c;界面的显示效果可能会达不到预期的效果。 3、布局容器可以嵌套使用 常…

【Axure原型分享】标签管理列表

今天和大家分享通过标签管理列表的原型模板&#xff0c;包括增删改查搜索筛选排序分页翻页等效果&#xff0c;这个模板是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;初始数据我们只要在中继器表格里填写即可&#xff0c;具体效果可以观看下方视频或者打开预览地址…

【经管】上市公司供应链金融数据(1990-2023年)

上市公司供应链金融是指上市公司利用其产业链核心地位&#xff0c;通过整合金融资源&#xff0c;为供应链上下游企业提供包括融资、结算、风险管理等在内的综合金融服务。为了衡量上市公司的供应链金融水平&#xff0c;参考周兰等&#xff08;2022&#xff09;的研究方法&#…

【C++入门篇 - 3】:从C到C++第二篇

文章目录 从C到C第二篇new和delete命名空间命名空间的访问 cin和coutstring的基本使用 从C到C第二篇 new和delete 在C中用来向系统申请堆区的内存空间 New的作用相当于C语言中的malloc Delete的作用相当于C语言中的free 注意&#xff1a;在C语言中&#xff0c;如果内存不够…

IBM Flex System服务器硬件监控指标解读

随着企业IT架构的日益复杂&#xff0c;服务器的稳定运行对于保障业务连续性至关重要。IBM Flex System作为一款模块化、可扩展的服务器解决方案&#xff0c;广泛应用于各种企业级环境中。为了确保IBM Flex System服务器的稳定运行&#xff0c;监控易作为一款专业的IT基础设施监…

git维护【.gitignore文件】

在工程下添加 .gitignore 文件【git忽略文件】 *.class .idea *.iml *.jar /*/target/更多&#xff1a; # Compiled class file *.class# Log file *.log *.imi *.lst# BlueJ files *.ctxt# Mobile Tools for Java (J2ME) .mtj.tmp/# Package Files # *.jar *.war *.nar *.ea…

【MySQL 保姆级教学】数据库基础(重点)(2)

目录 1. 什么是数据库1.1 数据库的定义1.2 mysql 和 mysqld1.3 文件和数据库 2. 数据库的分类3. 连接数据库3.1 数据库的安装3.2 连接服务器&#xff08;数据库&#xff09;3.3 服务器 数据库 表 三者的关系 4. 数据库-表 和目录-文件 的关系5. MySQL 框架6. SQL 分类7. 储存引…

DDoS攻击快速增长,如何在抗ddos防护中获得主动?

当下DDoS攻击规模不断突破上限。前段时间&#xff0c;中国首款3A《黑神话&#xff1a;悟空》也在一夜之内遭受到28万次攻击DDoS攻击&#xff0c;严重影响到全球玩家的游戏体验。Gcore发布的数据也显示了 DDoS攻击令人担忧的趋势&#xff0c;尤其是峰值攻击已增加到了令人震惊的…

CNN-GRU时序预测 | MATLAB实现CNN-GRU卷积门控循环单元时间序列预测

时序预测 | MATLAB实CNN-GRU卷积门控循环单元时间序列预测 目录 时序预测 | MATLAB实CNN-GRU卷积门控循环单元时间序列预测预测效果基本介绍模型描述程序设计参考资料预测效果 基本介绍 本次运行测试环境MATLAB2020b 提出了一种基于卷积神经网络(Convolutional Neural Network…