剑指offer(C++)-JZ51:数组中的逆序对(算法-排序)

简介: 剑指offer(C++)-JZ51:数组中的逆序对(算法-排序)

题目描述:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007

数据范围:  对于 50% 的数据,size≤104

对于100% 的数据, size≤105


数组中所有数字的值满足 0≤val≤109


要求:空间复杂度 O(n),时间复杂度O(nlogn)

示例:

输入:

[1,2,3,4,5,6,7,0]


返回值:

7

解题思路:

本题常规解题思路就是暴力法,但是本题要求复杂度为nlogn,那就可以用归并排序来解决了。


  1. 正常写一个归并排序。
  2. 在归并排序组合排序过程时,会把两子区间数据依次比较,重构新的顺序;此时如果左侧数值A大于右侧数值B,那就可以直接把A到中间位置C的数据个数直接累加。因为子区间已经完成排序,既然A都大于B了,那A到C之间所有的数据也必然大于B,所以可构成逆序对。
  3. 这样操作下来,时间复杂度满足题目要求了。

测试代码:

class Solution {
public:
    int mod = 1000000007;
    // 归并排序
    int mergeSort(vector<int>& data, int left, int right){
        // 停止
        if(left >= right)
            return 0;
        // 中间位
        int mid = (left + right) / 2;
        // 拆分合并,累加逆序对数量
        int count = mergeSort(data, left, mid) + mergeSort(data, mid + 1, right);
        count %= mod;
        // 排序
        int i = left, j = mid + 1;
        vector<int> temp(data);
        for(int t = left; t <= right; ++t){
            // i如果到了mid+1,说明左子区间遍历完毕,后续直接取右区间数据即可
            if(i == mid + 1){
                data[t] = temp[j];
                j++;
            }
            // j如果到了right+1,说明右子区间遍历完毕,后续直接取左区间数据即可
            // 如果左侧值小于右侧值,不符合逆序对,正常排序即可
            else if(j == right + 1 || temp[i] <= temp[j]){
                data[t] = temp[i];
                i++;
            }
            // 如果左侧值大于右侧值,说明左侧当前位置到左子区间结束的数值,都大于该右侧值,直接计算数量:mid-i+1;然后正常排序
            // 这样操作降低了时间复杂度
            else{
                data[t] = temp[j];
                j++;
                count += mid - i + 1;
            }
        }
        return count % mod;
    }
    // 逆序对
    int InversePairs(vector<int>& nums) {
        int size = static_cast<int>(nums.size());
        int count = mergeSort(nums, 0, size - 1);
        return count;
    }
};


相关文章
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
90 8
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
91 7
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
43 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
2月前
|
存储 算法 C++
高精度算法(加、减、乘、除,使用c++实现)
高精度算法(加、减、乘、除,使用c++实现)
619 0
高精度算法(加、减、乘、除,使用c++实现)
|
2月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
34 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
2月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
26 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
2月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
33 0
|
2月前
|
存储 算法 搜索推荐
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
78 0
|
2月前
|
存储 算法 决策智能
【算法】博弈论(C/C++)
【算法】博弈论(C/C++)
|
2月前
|
存储 算法 C++
【算法】哈希映射(C/C++)
【算法】哈希映射(C/C++)