zoukankan      html  css  js  c++  java
  • 剑指Offer对答如流系列

    面试题51:数组中的逆序对

    题目描述

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

    问题分析

    大多数人的第一反应就是顺序扫描整个数组,对每个数字都和后面的数字比较大小,时间复杂度为O(n^2),效率太低。

    利用归并排序的思想,先将数组分解成为n个长度为1的子数组,然后进行两两合并同时排好顺序。(在排序的时候计算逆序对)

    归并排序是经典排序算法之一,其核心是将待排数组不断细分,然后排序最后再合并,这是经典的分治策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)

    在这里插入图片描述

    (a) 把长度为4的数组分解成两个长度为2的子数组;

    (b) 把长度为2的数组分解成两个成都为1的子数组;

    (c) 把长度为1的子数组 合并、排序并统计逆序对

    (d) 把长度为2的子数组合并、排序并统计逆序对

    在对两个子区域合并排序时,记左边区域(下标为start~mid)的引用为i,右边区域(下标为mid+1~end)的引用为j,两个引用都指向该区域内最大的数字,排序时:

    1. 如果i指向的数字大于j指向的数字,说明:逆序对有j-mid个,我们把i指向的数字放入临时创建的排序数组中,然后令i-1,指向该区域前一个数字,继续进行排序;

    2. 如果i指向的数字小于等于j指向的数字,说明暂时不存在逆序对,将j指向的数字放入临时创建的排序数组中,然后令j-1,指向该区域前一个数字,继续进行排序;

    3. 某一子区域数字都放入排序数组后,将另一个子区域剩下的数字放入排序数组中,完成排序;

    4. 最后将排序好的数字按顺序赋值给原始数组的两个子区域,以便合并后的区域与别的区域合并。

    问题解决

        // 主程序
        public int inversePairs(int [] array) {
            if(array==null || array.length<=0) {
                return 0;
            }
            int count=getCount(array,0,array.length-1);
            return count;
        }
    
        private static int getCount(int[] array,int start,int end){
            if(start>=end) {
                return 0;
            }
            int mid=(end+start)>>1;
            int left=getCount(array,start,mid);
            int right=getCount(array,mid+1,end);
    
            // 合并
            int count=0;
            // 左边区域的引用
            int i=mid;
            // 右边区域的引用
            int j=end;
            // 临时区域
            int[] temp= new int[end-start+1];
            // 临时区域的引用
            int k=end-start;
    
            while(i>=start && j>=mid+1){
                if(array[i]>array[j]){
                    count+=(j-mid);
                    temp[k--]=array[i--];
                }else{
                    temp[k--]=array[j--];
                }
            }
            while(i>=start) {
                temp[k--]=array[i--];
            }
            while(j>=mid+1) {
                temp[k--]=array[j--];
            }
    
            // temp已经排好序 拿过来即可
            for(k=0; k<temp.length; k++) {
                array[k+start]=temp[k];
            }
    
            return count+left+right;
        }
    
  • 相关阅读:
    centos7.x网卡bond配置
    twemproxy源码解析系列三Twemproxy配置文件解析及相关组件初始化过程
    twemproxy源码解析系列二关键数据结构分析
    Nginx 变量漫谈(一)(转)
    twemproxy源码解析系列一特性及启动流程分析
    理解 Linux 的处理器负载均值
    man命令使用
    awk 查找文件中数字 字符串 email
    非阻塞socket调用connect, epoll和select检查连接情况示例
    Mysql日期类型大小比较拉取给定时间段的记录
  • 原文地址:https://www.cnblogs.com/JefferyChenXiao/p/12246542.html
Copyright © 2011-2022 走看看