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

这篇具有很好参考价值的文章主要介绍了【算法基础】(一)基础算法 --- 归并排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

✨个人主页: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. 归并 —— 合二为一

导图:

归并排序在算法里基础吗,算法基础,算法,排序算法,蓝桥杯,java,数据结构

  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中

附上总的代码文章来源地址https://www.toymoban.com/news/detail-810457.html

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. 归并,将左右两个有序序列合并成一个有序序列

导图:

归并排序在算法里基础吗,算法基础,算法,排序算法,蓝桥杯,java,数据结构

我们可以继续分治:

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

  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;
    }
}

到了这里,关于【算法基础】(一)基础算法 --- 归并排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构与算法:归并排序

    在讲解归并排序前,我们先看到一个问题: 对于这样两个有序的数组,如何将它们合并为一个有序的数组? 在此我们处理这个问题的思路就是:开辟一个新的数组,然后分别安置一个指针在左右数组,利用指针遍历数组,每次对比将比较小的那个元素插入到数组的尾部。 像

    2024年01月21日
    浏览(33)
  • 数据结构算法--5 归并排序

    我们先看一下归并排序是怎么归并的  两个有序列表,有low指针指向2,high指针指向6,mid指针指向9 再建一个新列表,12,所以1放到列表,右指针右移一位,再比较2和3,2放入列表,左指针右移一位,以此类推,肯定有一部分列表率先没有数,这时将另一列表直接append进入新

    2024年02月11日
    浏览(38)
  • python算法与数据结构---排序和归并排序

    掌握归并排序的基本原理 使用python语言解答归并排序题目 原理及过程 将两个有序的数组合并成一个有序数组称为 从上往下分解:把当前区间一分为二,直至分解为若干个长度为1的子数组 从上往下的合并:两个有序的子区域两两向上合并; 体现了分治思想,稳定排序 复杂

    2024年01月21日
    浏览(56)
  • 【数据结构】排序算法(二)—>冒泡排序、快速排序、归并排序、计数排序

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.冒泡排序 2.快速排序 2.1Hoare版 2.2占坑版 2.3前后指针版 2.4三数取中对快速排序的优化 2.5非递归版 3.归

    2024年02月08日
    浏览(35)
  • 【数据结构与算法】:非递归实现快速排序、归并排序

    🔥 个人主页 : Quitecoder 🔥 专栏 :数据结构与算法 上篇文章我们详细讲解了递归版本的快速排序,本篇我们来探究非递归实现快速排序和归并排序 快速排序的非递归实现主要依赖于栈(stack)来模拟递归过程中的函数调用栈。递归版本的快速排序通过递归调用自身来处理子

    2024年03月24日
    浏览(44)
  • [数据结构 -- 手撕排序算法第七篇] 递归实现归并排序

    目录 1、归并的思想 2、归并排序的思想 2.1 基本思想 2.2 图解分析 3、归并排序递归版本代码实现 3.1 代码解析 3.2 注意事项 3.2.1错误划分:[begin, mid-1],[mid, end] 3.2.2 正确划分:[begin, mid], [mid+1, end] 4、归并排序的测试 5、时间复杂度、空间复杂度分析 5.1 时间复杂度 5.2 空间复杂

    2024年02月16日
    浏览(37)
  • 【一起学数据结构与算法】几种常见的排序(插入排序、选择排序、交换排序、归并排序)

    排序是计算机内经常进行的一种操作,其目的是将一组 “无序” 的记录序列调整为 “有序” 的记录序列。分 内部排序 和 外部排序 ,若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能

    2023年04月09日
    浏览(30)
  • 【数据结构】- 排序(详细介绍几种排序算法!!!*直接插入排序,*希尔排序,*选择排序,*堆排序,*冒泡排序,*快速排序,*归并排序)

    排序无处不在,所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 内部排序 :数据元素全部放在内存中的排序。 外部排序 :数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。 今天

    2024年01月20日
    浏览(41)
  • 【数据结构与算法】万字剖析八大排序(直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序)

    初窥直接插入排序我们先来看一张动图: 由动图我们可以分析出直接插入排序是从 第二个数据开始遍历 ,与 前面的数据进行比较 如果小于 则让前面的数据向前移动 自己接着向前面的数据比较 直到比较到 大于等于自己的 数据或者 没有数据能进行比较 时停止 插入当前的位

    2023年04月13日
    浏览(42)
  • 【算法与数据结构】归并排序的代码实现(详细图解)以及master公式的讲解

    目录 1、归并排序  1.1、算法描述  1.2、图解说明 2、代码实现  3、master公式 3.1、公式以及结论 3.2、适用于某些特殊的递归 3.3、计算归并排序的时间复杂度 归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用 递归 或者说是 分治法 (Divide and Conquer)的一个非

    2024年02月08日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包