首页 > 其他分享 >06-归并排序

06-归并排序

时间:2023-11-09 09:13:46浏览次数:29  
标签:归并 06 arr int mid marge 数组 排序

6. 归并排序

6.1 基础归并排序

分层两半,而后合并。

重点:MargeSort把比较变成了有序的东西,这个有序的东西可以帮我们做很多事情

6.1.1 递归的归并排序

两个函数:

分:process(arr,L,R) --> 保证[L,R]范围上有序。

public static void mSort(int[] arr , int l, int r){
	if(l == r){
        return;
	}
	int mid = (l+r)/2;
	mSort(arr,l,mid);
	mSort(arr,mid+1,r);
	marge(arr,l,mid,r);
}

合:每个process中都要利用marge合一下,利用一个辅助数组长度就是R-L。把这两部分按顺序复制到辅助数组中,再把辅助数组替换到L-R之间。

public static void marge(int[] arr , int l,int mid, int r){
    int[] help = new int[r-l+1];
    int i = l, j = mid+1;
    int k = 0;
    while(i <= mid && j <= r){
        if(arr[i] <= arr[j]){
            help[k] = arr[i];
            i++;
        }else {
            help[k] = arr[j];
            j++;
        }
        k++;
    }

    while(i <= mid){
        help[k] = arr[i];
        i++;
        k++;
    }
    while(j <= r){
        help[k] = arr[j];
        j++;
        k++;
    }
    for (int m = 0; m < help.length; m++) {
        arr[l+m] = help[m];
    }
}

6.1.2 非递归的归并排序

非递归模拟步长来进行计算,marge不变。

step从1开始不断翻倍直到超过N,停止。

按照步长为step从0到n遍历,可以计算出right和mid,然后marge即可。

public static void mSortWithoutRecursion(int[] arr ){
    int  N =  arr.length;
    int step = 1;
    // 5 4 8 2 3

    while(step < N){
        int left = 0, mid, right;
        while(left < N){
            right = left+step*2-1 >= N-1?N-1:(left+step*2-1);
            mid = (left+right)/2;
            marge(arr,left,mid,right);
            left = right+1;

        }
        //// 假设integer.max = 2e9,N = 2e9; step = 1e9+1 ,翻倍就炸了
        if(step > N/2){
            break;
        }
        step <<=1;
    }
}

6.2 数组小和

1. 题目

在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和。求数组小和。

例子: [1,3,4,2,5]

1左边比1小的数:没有

3左边比3小的数:1

4左边比4小的数:1、3

2左边比2小的数:1

5左边比5小的数:1、3、4、2

所以数组的小和为1+1+3+1+1+3+4+2=16 

2. 思路

数组最小和也可以看成:假设第i个数后面有x个比他大,那么在数组最小和中,第i个数出现的次数就是i*x次。

第i个数出现的次数似乎就是x次,而累加的值为arr[i]*x。(2023年9月24日)

ps:通过降序考虑也可以。

因此通过归并排序,在归并的时候,一个数组有分成左右部分(L/R),此时两部分应该都有序,规则如下:

  1. L中的数如果入小于R中的数(也就是进入辅助数组的时候),记录R中剩下的元素的个数(也就是比L[i]大的数的个数),这个数乘上L[i]就是当前这个阶段,比L[i]大的数的累加值。

  2. R中的数如果进入辅助数组,不进行相加,因为R在右侧,右侧的数不在乎有什么数在左面。

  3. 递归只需要返回smallNum(左)+smallNum(右)+marge(左右合并的数组)即可。

3.代码

public static int smallSum(int[] arr , int l, int r){
        if(l == r || arr.length == 0){
            return 0;
        }
        int mid = (l+r)/2;
        return smallSum(arr,l,mid)+
        smallSum(arr,mid+1,r)+
        marge(arr,l,mid,r);

    }
    public static int marge(int[] arr , int l,int mid, int r){
        int[] help = new int[r-l+1];
        int i = l, j = mid+1;
        int k = 0;
        int ans = 0;
        while(i <= mid && j <= r){
            if(arr[i] < arr[j]){
				// 这里记录了左侧数组进入后右侧数组剩余的元素个数
                ans += arr[i]*(r-j+1);
                help[k++] = arr[i++];
            }else {
                help[k++] = arr[j++];
            }
        }
        while(i <= mid) help[k++] = arr[i++];
        while(j <= r) help[k++] = arr[j++];
        for (int m = 0; m < help.length; m++) arr[l+m] = help[m];
        return ans;
    }

6.3 翻转对

1. 题目

https://leetcode.cn/problems/reverse-pairs/

在一个数组中,对于每个数num,求有多少个后面的数 * 2 依然比num小,求总个数。

比如:[3,1,7,0,2]
3的后面有:1,0
1的后面有:0
7的后面有:0,2
0的后面没有
2的后面没有
所以总共有5个

2. 思路

如果已经部分排好序了,如[1,3] 和[0,2,7],可以O(n)的方式知道左数组中的数在右数组中的btt的个数。

初始arr[i] = 1,arr[j] = 0;
如果arr[j]*2 < arr[i] :j++并且记录答案为j;  --- 越界可以开long
否则 i++; (因为arr[i] < arr[i+1],所以j前面的数一定也可以满足前提,直接从j判断即可)
右侧的数不需要操作,因为需要去重复

3.代码

public static int marge(int[] arr , int l,int mid, int r){
    // 假设L[l...mid],R[mid+1...r]都已经排好序了,求此时L中的数对于R中的数的btt
    int ans = 0;
    int j = 0;
    for(int i = l; i <= mid; i++){
        while(j+mid+1 <= r &&(long)arr[i] > (long)arr[j+mid+1]*2){
            j++;
        }
        ans += j;
    }
    
// 正常的归并
    int[] help = new int[r-l+1];
    int i = l;
    j = mid+1;
    int k = 0;
    while(i <= mid && j <= r){
        if(arr[i] <= arr[j]){
            help[k] = arr[i];
            i++;
        }else {
            help[k] = arr[j];
            j++;
        }
        k++;
    }

    while(i <= mid){
        help[k] = arr[i];
        i++;
        k++;
    }
    while(j <= r){
        help[k] = arr[j];
        j++;
        k++;
    }
    for (int m = 0; m < help.length; m++) {
        arr[l+m] = help[m];
    }
    return ans;
}

6.4 逆序对

1. 题目

https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/

在一个数组中,任何一个前面的数a,和任何一个后面的数b,如果(a,b)是降序的,就称为逆序对。返回数组中逆序对的个数。

2. 思路

归并的本质就是无序的代码可以让我们有序比较。

对于有序数组L[l...mid] 和 R[mid+1...r]来说,

1. L数组加入help的时候无序更改(因为左侧代表已经算过逆序对的数)
2. R数组中加入help的时候,逆序对个数增加为:mid-i+1个。即,此时L数组中还未加入help中的数的个数。

3.代码

    public int merge(int[] nums,int l,int mid,int r){
        if(l == r){
            return 0;
        }
// 核心代码开始
        // 假设L[l...mid] 和 R[mid+1...r]都有序的情况下
        int j = 0;
        int ans = 0;
        // i为遍历R数组的index
        for(int i = l; i <= mid; i++){
            while(j+mid+1 <= r && nums[i] > nums[j+mid+1]){
                ans += mid-i+1;
                j++;
            }
        }
// 核心代码结束
        int[] help = new int[r-l+1];
        int i = l;
        j = mid+1;
        int k = 0;
        while(i <= mid && j <= r){
            if(nums[i] < nums[j]) help[k++] = nums[i++];
            else help[k++] = nums[j++];
        }
        while(i <= mid) help[k++] = nums[i++];
        while(j <= r) help[k++] = nums[j++];

        for(int m = 0; m < help.length; m++){
            nums[m+l] = help[m];
        }
        return ans;
    }

6.7 区间和的个数(未完成)

1. 题目

https://leetcode.cn/problems/count-of-range-sum/

给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中,值位于范围 [lower, upper] (包含 lower 和 upper)之内的 区间和的个数 。

区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。

输入:nums = [-2,5,-1], lower = -2, upper = 2
输出:3
解释:存在三个区间:[0,0]、[2,2] 和 [0,2] ,对应的区间和分别是:-2 、-1 、2 。

输入:nums = [0], lower = 0, upper = 0
输出:1
 

2. 思路

首先,求一个范围内的值在lower,upper之间,就要先确定一个范围内的值 => 优化:前缀和来求。

然后用归并排序,得到

3.代码

标签:归并,06,arr,int,mid,marge,数组,排序
From: https://www.cnblogs.com/ylw-blogs/p/17818904.html

相关文章

  • 09-堆排序
    9.堆排序9.1完全二叉树在满二叉树路上的树。如果二叉树是完全二叉树,并且用数组表示,则:位置i的左右孩子节点为2i+1和2i+2位置i的父节点为(i-1)/29.2堆堆是完全二叉树堆有大根小根之分他的每颗子树都必须满足大根/小根堆9.3堆排序1.题目​ 堆排序......
  • 11-桶排序
    11.桶排序桶排序不是基于比较的排序,利用一个容器来进行存储额外信息进而提升速度(O(n))11.1计数排序​ 看数的范围,建立一个数组,然后记录每个数出现的次数,再按照次数来进行建立数组1.题目​ 有n个公司员工,年龄在16到200之间,用O(n)的复杂度来进行排序。2.思路​ 哈希数组:new[2......
  • P4069 题解
    简要题意给定一棵\(n\)个点的树,树有边权。对每个点维护一个集合\(S_u\),一开始集合均包含整数\(123456789123456789\)。设\({\rmdis}_{a,b}\)为树上两点\(a\),\(b\)的距离。共\(m\)次操作,分为如下两种:stab:设\(f\)为\(s\),\(t\)路径上的点集,对与\(\forall......
  • vue+element拖动排序功能
    vue+element拖动排序功能安装npminstallvuedraggable-S引用importdraggablefrom'vuedraggable'注册组件components:{draggable},通过draggable标签来使用代码<draggablev-model="urlPic":move="onMove"@start=......
  • 【洛谷 P4414】[COCI2006-2007#2] ABC 题解(排序)
    [COCI2006-2007#2]ABC题面翻译【题目描述】三个整数分别为。这三个数字不会按照这样的顺序给你,但它们始终满足条件:。为了看起来更加简洁明了,我们希望你可以按照给定的顺序重新排列它们。【输入格式】第一行包含三个正整数,不一定是按这个顺序。这三个数字都小于或等于。第二行包......
  • [-006-]-Python3+Unittest+Selenium Web UI自动化测试之悬浮窗口中的元素点击
     1.分析现状:PPT模板悬浮出现悬浮窗口悬浮窗口中分为4大类:PPT模板,PPT模板页,PPT关系图,PPT图表大类下存在小类点击可跳转但是此页面里还存在PPT模板下的总结汇报等此种情况的元素此情况如果仅用text定位是无法定位到的所以排除了text定位方式2.解决方法:首先我们看下悬浮窗......
  • 2008秋-计算机软件基础-多关键字排序
    /*多关键字排序:先按总分由高到低排序,若总分相同则按数学成绩由高到低排序,若总分和数学成绩都相同,则按英语成绩由高到低排序。*/#include<stdio.h>structstudent{intxuehao;charxingming[10];intshuxue;intyingyu;intyuwen;intzongf......
  • C语言程序设计 选择排序简介
    选择排序选择排序是通过每一趟排序过程中从待排序记录中选择出关键字最小(大)的记录,将其依次放在数据表的最前或最后端的方法来实现整个数据表的有序排列。本节将介绍选择排序方法中最简单且最常用的简单选择排序。选择排序基本思想 第一趟排序在所有待排序的n个记录中选出关键字最......
  • C语言程序设计 冒泡排序简介
    冒泡排序基本思想将n个记录看作按纵向排列,每趟排序时自下至上对每对相邻记录进行比较,若次序不符合要求(逆序)就交换。每趟排序结束时都能使排序范围内关键字最小的记录象一个气泡一样升到表上端的对应位置,整个排序过程共进行n-1趟,依次将关键字最小、次小、第三小…的各个记录“冒到......
  • 2008秋-计算机软件基础-冒泡排序
    /*Title:冒泡排序Author:emanlee算法功能:冒泡排序算法实现将一个长度为n的线性表r上的所有元素按关键字升序排列。*/#include<stdio.h>voidbubblesort(intr[],intn){/*elementsarestoredinr[1]tor[n]*/inti,j,flag;inttemp;flag=1;i......