草庐IT

【算法基础】(一)基础算法 --- 归并排序

bit me 2023-04-05 原文

✨个人主页:bit me
✨当前专栏:算法基础
🔥专栏简介:该专栏主要更新一些基础算法题,有参加蓝桥杯等算法题竞赛或者正在刷题的铁汁们可以关注一下🌹 🌹 🌹

归并排序


 

💤一.归并排序(分治)

题目要求:

给定你一个长度为 n 的整数数列。
 
请你使用归并排序对这个数列按照从小到大进行排序。
 
并将排好序的数列按顺序输出。

输入格式:

输入共两行,第一行包含整数 n。
 
第二行包含 n 个整数(所有整数均在 1∼10^9 范围内),表示整个数列。

输出格式:

输出共一行,包含 n 个整数,表示排好序的数列。

数据范围:

1≤n≤100000

输入样例:

5
3 1 2 4 5

输出样例:

1 2 3 4 5

快排思路:

  1. 确定分界点:mid = (l + r) / 2
  2. 递归排序left,right
  3. 归并 —— 合二为一

导图:

  1. 首先我们需要设置下数据的范围,然后两个创建数组来方便我们后续使用,一个数组用来读取输入元素个数,一个数组用来临时归并合二为一
private static final int N = 100000 + 6;
private static int[] q = new int[N];
private static int[] t = new int[N];
  1. 数据的读取输入
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String str = input.readLine();
int n=Integer.parseInt(str);
String[] strs = input.readLine().split(" ");
for (int i = 0; i < n; i++) {
    q[i] = Integer.parseInt(strs[i]);
}

注意:在这里用BufferedReader是要比Scanner要好的,速度更快一点

  1. 然后排序函数代入,排序函数的实现
if (l >= r) return;
int mid = l + r >> 1;
sort(q, l, mid);
sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r) {
    if (q[i] <= q[j]) {
        t[k++] = q[i++];
    }
    else {
        t[k++] = q[j++];
    }
}
while (i <= mid) {
    t[k++] = q[i++];
}
while (j <= r) {
    t[k++] = q[j++];
}
for (i = l, j = 0; i <= r; i++, j++) {
    q[i] = t[j];
}
  1. 当左极值点大于等于右极值点,说明只有一个元素或者没有,直接返回
  2. 让分界点mid直接等于中点值
  3. 分界点mid俩边区域分别进行递归排序,数组名,起点和终点
  4. 两个指针i和j,分别从左边和中间开始往后走,分别比较元素大小,小的就纳入数组t中,直到有一个区域指针走完了,然后直接把另外的一个区域后面的衔接到数组t后面,就完成了排序
  5. 最后把t数组中的结果重新归并到数组q中

附上总的代码

public class TestDemo {
    private static final int N = 100000 + 6;
    private static int[] q = new int[N];
    private static int[] t = new int[N];

    public static void main(String[] args) throws IOException {
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
        String str = input.readLine();
        int n=Integer.parseInt(str);
        String[] strs = input.readLine().split(" ");
        for (int i = 0; i < n; i++) {
            q[i] = Integer.parseInt(strs[i]);
        }
        sort(q, 0, n - 1);
        for (int i = 0; i < n; i++){
            System.out.printf("%d ", q[i]);
        }
    }

    private static void sort(int[] q, int l, int r) {
        if (l >= r) return;
        int mid = l + r >> 1;
        sort(q, l, mid);
        sort(q, mid + 1, r);
        int k = 0, i = l, j = mid + 1;
        while (i <= mid && j <= r) {
            if (q[i] <= q[j]) {
                t[k++] = q[i++];
            }
            else {
                t[k++] = q[j++];
            }
        }
        while (i <= mid) {
            t[k++] = q[i++];
        }
        while (j <= r) {
            t[k++] = q[j++];
        }
        for (i = l, j = 0; i <= r; i++, j++) {
            q[i] = t[j];
        }
    }
}

 

💨二.逆序对的数量

题目要求:

给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。
 
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。

输入格式:

第一行包含整数 n,表示数列的长度。
 
第二行包含 n 个整数,表示整个数列。

输出格式:

输出一个整数,表示逆序对的个数。

数据范围:

1≤n≤100000,
数列中的元素的取值范围 [1,10^9]。

输入样例:

6
2 3 4 5 6 1

输出样例:

5

结合归并排序:

  1. [L,R] => [L,mid] + [mid+1,R]
  2. 递归排序[L,mid]和[mid+1,R]
  3. 归并,将左右两个有序序列合并成一个有序序列

导图:

我们可以继续分治:

整个区间的逆序数对分为左区间的逆序数对,右区间逆序数对和两个区间各组成的逆序数对,其中左区间和右区间的逆序对数量很直观,重点在于两者各取其一组成的逆序对怎么求

  1. 左区域逆序对数量:merge_sort(L,mid)
     
  2. 右区域逆序对数量:merge_sort(mid+1,R)
     
  3. 由归并排序我们可以知道左右俩区域都是按顺序来排列的,那么我们只需要找到左区域第一个大于右区域的最小数,那么它后面的数都是大于右区域的那个数字的,我们不妨设左区域那个元素下标为i,所以我们需要统计的个数是 mid - i + 1,恰巧在归并排序的合并过程中正好有两边序列依次比大小的过程
    if (a[i] <= a[j]) tem[k++] = a[i++]; else { tem[k++] = a[j++]; }
    else中的语句即代表左边数列中的数大于右边中的数了,我们可以在此时将mid-i+1加到总答案中去if (a[i] <= a[j]) tem[k++] = a[i++]; else { res += mid - i + 1; tem[k++] = a[j++]; }
     
  4. 问题解决后,我们的函数还没有解决问题的能力,只需要在递归出口的时候return 0;因为递归结束的时候数列中只有一个数了,不存在逆数对。还需要在递归归并排序的时候统计两边的逆序对数量之和long res = mergeSort(a, l, mid) + mergeSort(a, mid + 1, r);即可。

注意:题目中的数据范围最大是100000,一般对于数据范围大于10w的题目就要考虑数据溢出、时间复杂度等问题

当数列是一个倒序的数列时应该是逆序对数量最大的时候,每一个数可以和他后面所有的数形成逆数对,如果有n个数,那么总的逆序对数量为:(n-1)+(n-2)+……+1即n(n−1)/2个,大约n^2 / 2个,代入10w的数据范围得最多逆数对个数为:5×10 ^ 9,这个数据大于int的范围。所以这个题用int的话会溢出,我们采用long来存结果

附上总的代码

public class Main {
    public static void main(String[] args)  {
        Scanner scanner = new Scanner(new InputStreamReader(System.in));
        int n = scanner.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = scanner.nextInt();
        }
        System.out.println(merge_Sort(a, 0, n - 1));
        scanner.close();
    }

    private static long merge_Sort(int[] a, int l, int r) {
        if (l >= r){
            return 0;
		}
        int mid = l + r >> 1;
        long res = merge_Sort(a, l, mid) + merge_Sort(a, mid + 1, r);
        int tmp[] = new int[r-l+1];
        int k = 0, i = l, j = mid + 1;
        while (i <= mid && j <= r) {
            if (a[i] <= a[j]){
                tmp[k++] = a[i++];
            }else {
                res += mid - i + 1;
                tmp[k++] = a[j++];
            }
        }
        while (i <= mid){
            tmp[k++] = a[i++];
        }
        while (j <= r){
            tmp[k++] = a[j++];
        }
        for(i=l,j=0;i<=r;i++,j++){
        	a[i]=tmp[j];
        }
        return res;
    }
}

有关【算法基础】(一)基础算法 --- 归并排序的更多相关文章

  1. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  2. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  3. 软件测试基础 - 2

    Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功

  4. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  5. ruby-on-rails - 需要帮助最大化多个相似对象中的 3 个因素并适当排序 - 2

    我需要用任何语言编写一个算法,根据3个因素对数组进行排序。我以度假村为例(如Hipmunk)。假设我想去度假。我想要最便宜的地方、最好的评论和最多的景点。但是,显然我找不到在所有3个中都排名第一的方法。Example(assumingthereare20importantattractions):ResortA:$150/night...98/100infavorablereviews...18of20attractionsResortB:$99/night...85/100infavorablereviews...12of20attractionsResortC:$120/night

  6. ruby-on-rails - 在具有 ActiveRecord 条件的相关模型中按字段排序 - 2

    我正在尝试按Rails相关模型中的字段进行排序。我研究的所有解决方案都没有解决如果相关模型被另一个参数过滤?元素模型classItem相关模型:classPriority我正在使用where子句检索项目:@items=Item.where('company_id=?andapproved=?',@company.id,true).all我需要按相关表格中的“位置”列进行排序。问题在于,在优先级模型中,一个项目可能会被多家公司列出。因此,这些职位取决于他们拥有的company_id。当我显示项目时,它是针对一个公司的,按公司内的职位排序。完成此任务的正确方法是什么?感谢您的帮助。PS-我

  7. ruby - 按数字(从大到大)然后按字母(字母顺序)对对象集合进行排序 - 2

    我正在构建一个小部件来显示奥运会的奖牌数。我有一个“国家”对象的集合,其中每个对象都有一个“名称”属性,以及奖牌计数的“金”、“银”、“铜”。列表应该排序:1.首先是奖牌总数2.如果奖牌相同,按类型分割(金>银>铜,即2金>1金+1银)3.如果奖牌和类型相同,则按字母顺序子排序我正在用ruby​​做这件事,但我想语言并不重要。我确实找到了一个解决方案,但如果感觉必须有更优雅的方法来实现它。这是我做的:使用加权奖牌总数创建一个虚拟属性。因此,如果他们有2个金牌和1个银牌,加权总数将为“3.020100”。1金1银1铜为“3.010101”由于我们希望将奖牌数排序为最高的,因此列表按降序排

  8. ruby-on-rails - 在不重新查询数据库的情况下重新排序 Rails 中的事件记录? - 2

    例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果

  9. ruby-on-rails - 如何对对象数组进行排序? - 2

    我有一个对象如下:[{:id=>2,:fname=>"Ron",:lname=>"XXXXX",:photo=>"XXX"},{:id=>3,:fname=>"Dain",:lname=>"XXXX",:photo=>"XXXXXXX"},{:id=>1,:fname=>"Bob",:lname=>"XXXXXX",:photo=>"XXXX"}]我想按fname排序,不区分大小写,所以它会导致编号:1,3,2我该如何排序?我正在尝试:@people.sort!{|x,y|y[:fname]x[:fname]}但这没有任何效果。 最佳答案

  10. ruby - 使用自定义排序首选项对数组进行排序? - 2

    有人可以告诉我如何根据自定义字符串对嵌套数组进行排序吗?比如有没有办法排序:[['Red','Blue'],['Green','Orange'],['Purple','Yellow']]“橙色”、“黄色”,然后是“蓝色”?最终结果如下所示:[['Green','Orange'],['Purple','Yellow'],['Red','Blue']]它不是按字母顺序排序的。我很想知道我是否可以定义要排序的值以实现上述目标。 最佳答案 sort_by对于这种排序总是非常方便:a=[['Red','Blue'],['Green','Ora

随机推荐