`
128kj
  • 浏览: 604082 次
  • 来自: ...
社区版块
存档分类
最新评论

利用线段树求逆序数(JAVA)

阅读更多
   设A[1…n]是一个包含n个不同数的数组。如果在i<j的情况下,有A[i]>A[j],则(i,j)就称为A中的一个逆序对(inversion)

   现给出一个数列,求该数列中的逆序对数(逆序数)。最直接的暴力方法;
  两层for循环就可以算出来逆序数:每遇到一个元素回头遍历寻找比其大的元素个数即可,
  当然向后寻找比其小的元素个数也可以,复杂度为O(n^2),代码:

   int sum = 0;
   for(int i = 0; i < size; ++i) {
      for(int j = i+1; j < size; ++j){
         if(arr[i] > arr[j]){
             ++sum;
         }
     }
  }
  return sum;


下面方法用线段树,逆序数就是一个“区间和”的问题:
对于数列中的每个元素,它对应的逆序数便是之前序列中大于该元素的元素个数和。

由于线段树的插入和查询操作皆可以在lgn的时间内完成,故遍历一个数列求逆序数的时间复杂度为O(nlgn)

例:HDU1394
    给你一个数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,
那么这样就形成了n个序列,那么每个序列都有一个逆序数,找出其中最小的一个输出!
样例:
Sample Input
10
1 3 6 9 0 8 5 7 4 2
Sample Output
16

分析:

线段树节点这样定义:
class Seg_Tree {//线段树节点
int left,right;
        int val;//区间内已插入的节点数
int calmid() {
return (left+right)/2;
}
   }

先以区间[0,9]为根节点建立val都为0的线段树, 
  
再看看怎样求下面序列的逆序数:
1 3 6 9 0 8 5 7 4 2
在线段树中插入1, 插入之前先询问区间[1,9]已插入的节点数(如果存在,必与1构成逆序)  v1=0
在线段树中插入3, 插入之前先询问区间[3,9]已插入的节点数(如果存在,必与3构成逆序)  v2=0
在线段树中插入6, 插入之前先询问区间[6,9]已插入的节点数(如果存在,必与6构成逆序)  v3=0
在线段树中插入9, 插入之前先询问区间[9,9]已插入的节点数(如果存在,必与9构成逆序)  v4=0
在线段树中插入0, 插入之前先询问区间[0,9]已插入的节点数(如果存在,必与0构成逆序)  v5=4
在线段树中插入8, 插入之前先询问区间[8,9]已插入的节点数(如果存在,必与8构成逆序)  v6=1
在线段树中插入5, 插入之前先询问区间[5,9]已插入的节点数(如果存在,必与5构成逆序)  v7=3
在线段树中插入7, 插入之前先询问区间[7,9]已插入的节点数(如果存在,必与7构成逆序)  v8=2
在线段树中插入4, 插入之前先询问区间[4,9]已插入的节点数(如果存在,必与4构成逆序)  v9=5
在线段树中插入2, 插入之前先询问区间[2,9]已插入的节点数(如果存在,必与2构成逆序)  v10=7
累加v1+……+v10  =22,这就是1 3 6 9 0 8 5 7 4 2的逆序数了.


这题要把第一个数放到最后
3 6 9 0 8 5 7 4 2 1
这样就增加了9个逆序,其逆序为22+9=31
6 9 0 8 5 7 4 2 1 3
这样增加了6个,减少了3个,其逆序数为31+6-3=34;
............
//公式:第一个数移到最后位置后逆序数
//sum=sum+(-low[a[i]]+up[a[i]]),low[a[i]]表示比a[i]小的数
//在0-(n-1)序列里low[a[i]]=a[i],up[a[i]]=n-a[i]-1;

最后AC过的代码:



 import java.util.Scanner;

     class Seg_Tree {//线段树节点
	int left,right;
        int val;//区间内已插入的节点数
	int calmid() {
		return (left+right)/2;
	}
   }

  public class Main{
     private int LL(int x) { return x<<1;}  //两倍;
     private int RR(int x) { return x<<1|1;} //两倍+1;
     Seg_Tree tt[];
 

    public Main(){
      tt=new Seg_Tree[16370];//用数组实现线段树
      for(int i=0;i<16370;i++)
          tt[i]=new Seg_Tree();
    }

    private void build(int left,int right,int idx) {//构建一棵val值全为0的线段树
	tt[idx].left = left;
	tt[idx].right = right;
	tt[idx].val = 0;
	if(left == right) return ;	
	int mid = tt[idx].calmid();
	build(left,mid,LL(idx));
	build(mid+1,right,RR(idx));
    }
   
   /*  如果将节点全部插入,应该是下面结果:
     C:\java>java Main
     10
     1 3 6 9 0 8 5 7 4 2
   [0,0]=.val=1 [0,1].val=2  [1,1].val=1  [0,2].val=3  [2,2].val=1  [0,4].val=5  [3,3].val=1  
    [3,4].val=2  [4,4].val=1  [0,9].val=10  [5,5].val=1  [5,6].val=2  [6,6].val=1  
    [5,7].val=3  [7,7].val=1  [5,9].val=5  [8,8].val=1  [8,9].val=2  [9,9].val=1
   */
    
   private void insert(int aim,int l,int r,int k){  //将aim插入到线段树
    if(tt[k].left==aim&&tt[k].right==aim) {
      tt[k].val++;return ;
    }  
  
    if(aim<=tt[k].calmid()) 
       insert(aim,l,tt[k].calmid(),LL(k));  
    else     
      insert(aim,tt[k].calmid()+1,r,2*k+1);  
    tt[k].val=tt[LL(k)].val+tt[RR(k)].val;  
  }  

    public void printTree(int i){//中序遍历线段树
      if(2*i>16370) return;
      printTree(2*i);
      if(tt[i].right!=0) System.out.print("["+tt[i].left+","+tt[i].right+"]"+".val="+tt[i].val+"  ");//注意,没有输出[0,0]
      printTree(2*i+1);
      
    }
       
  
   

    //查询[left,right]中已插入的节点数
    private int query(int left,int right,int idx){
	if(left == tt[idx].left && right == tt[idx].right) 
           return tt[idx].val;
	
	int mid = tt[idx].calmid();
	if(right <= mid){
		return query(left,right,LL(idx));
	} 
       else if(mid < left) {
		return query(left,right,RR(idx));
	} 
       else {
		return query(left,mid,LL(idx)) + query(mid+1,right,RR(idx));
	}
     }


  

     public static void main(String[] args){
        Scanner in=new Scanner(System.in);
	int n;
	while(in.hasNext()) {
          n=in.nextInt();
          Main ma=new Main();
          int val[]=new int[n];
	  ma.build(0,n-1,1);
	  int sum = 0;	
          for(int i=0;i<n;i++){
            val[i]=in.nextInt();
            sum += ma.query(val[i],n-1,1);//先查询
            ma.insert(val[i],0,n-1,1);//后插入
	   }
         // ma.printTree(1);
         // System.out.println();//中序遍历线段树
        //  System.out.println(sum);
	   int ret = sum;
	
        for(int i=0;i<n;i++){
	  sum = sum - val[i] + (n - val[i] - 1);
	  ret=Math.min(ret,sum);
	}
	 System.out.println(ret);
         
	}
       
   }
}



源码:
0
0
分享到:
评论

相关推荐

    利用归并排序求逆序数

    利用归并排序求逆序数,复杂度在O(nlgn)含测试用例

    归并排序求逆序数

    ### 归并排序求逆序数 #### 一、引言 在计算机科学与数据结构领域,排序算法是基础且重要的组成部分。其中,归并排序作为一种高效稳定的排序方法,在实际应用中有着广泛的应用场景。本篇文章将围绕如何利用归并...

    利用归并排序实现逆序数计算

    利用归并排序实现关于逆序数的计算,Java程序

    分治法求逆序数

    求逆序数的方法很多。最容易想到的办法是分别对序列中每一个元素求其逆序数,再求所有元素的逆序数总和,易分析得出这样的方法其时间复杂度为O(n2)。而这里采用的分治法求逆序数,其时间复杂度为O(nlogn)。

    mergeSort 求逆序数对matlab代码

    算法导论 课上的 用mergesort求逆序数对的matlab源码,想挣点分,所以就不免费下载了~~~~ 见谅

    算法-求排列的逆序数(信息学奥赛一本通-T1237).rar

    《算法-求排列的逆序数(信息学奥赛一本通-T1237)》这个主题涉及到的是计算机科学中的一个重要概念,逆序对或逆序数,它在算法设计和分析中扮演着关键角色,特别是在解决排序和组合优化问题时。逆序数是衡量一个...

    线段树完整版

    - 对于每次插入操作,利用线段树更新区间内的逆序对数量。 - 动态规划计算最小逆序数。 #### 五、线段树优化技巧 1. **懒惰标记** (`lazy tag`): 在进行区间更新时,可以在节点上打标记,表示这一整段区间都还...

    js_leetcode-求逆序数

    求逆序数 求逆序数 求逆序数 求逆序数 求逆序数

    求数组的逆序数

    在计算机科学和编程领域,"数组的逆序数"是一个重要的概念,特别是在算法分析和排序算法中。逆序数(Inversion Count)是指在数组或序列中,如果一个大于其后面的元素,则这样的对称为逆序对。逆序数就是数组中逆序...

    基于Java和Python语言使用函数输出一个整数的逆序数.zip

    基于Java和Python语言使用函数输出一个整数的逆序数.zip 基于Java和Python语言使用函数输出一个整数的逆序数.zip 基于Java和Python语言使用函数输出一个整数的逆序数.zip 基于Java和Python语言使用函数输出一个整数...

    逆序数程序

    逆序数程序是一种常见的计算机算法,特别是在本科和研究生级别的算法课程中经常被用作练习题目。这个程序的主要目的是处理一个整数序列,并计算在该序列中任意两个元素之间的逆序对数量。逆序对指的是在有序序列中...

    归并求逆序对 C语言实现

    ### 归并求逆序对 C语言实现 #### 知识点详解 1. **归并排序算法**:归并排序是一种经典的排序算法,采用分治策略来对数组进行排序。其基本思想是将待排序的数据分成两部分,分别对这两部分数据进行排序,然后再...

    分治法求数组中的逆序数

    有一实数序列a1,a2,....an,若i且ai&gt;aj,则(ai,aj)形成了一个逆序对,请使用分治算法求整个序列中逆序对个数,并分析算法时间复杂度。

    [Java算法设计]-逆序数字.java

    该资源提供了一份全面的指南,介绍了如何在Java中实现数字逆序。文档中涵盖了数字逆序的基本概念,包括如何从数字中提取数字,以及如何颠倒数字的顺序。此外,文档还包括一个逐步指南,介绍如何在Java中实现逆序数字...

    求逆序数对1

    逆序数对算法解析 在算法设计中,逆序数对是一个常见的问题, 它是指在一个序列中,所有的逆序数对的数量。逆序数对是指序列中两个元素之间的逆序关系,即一个元素的值大于另一个元素的值,但它们的索引却是反的。...

    POJ1804-Brainman【借助Mergesort求逆序数O(nlogn)】

    本题目的核心是利用归并排序(Mergesort)算法来实现逆序数的计算,以达到O(n log n)的时间复杂度,避免平方级别的效率消耗。 首先,我们要理解什么是逆序对。在一组排序的整数序列中,如果一个较大的数字位于较小...

    线段树的合并_黄嘉泰.pptx

    这意味着,如果我们对同一序列进行多次操作,可以利用这一特性来优化算法,例如通过持久化线段树来避免重复计算。 线段树的合并操作是其独特之处,它可以用来高效地处理一系列合并操作,如在POI18 rot题目中,解决...

    算法相关-求数列逆序数

    这个“算法相关-求数列逆序数”的主题涉及到一个经典的计算机科学问题,即如何有效地计算一个数列中的逆序对数量。逆序对是指在已排序的数列中,如果前面的元素大于后面的元素,那么这两个元素就构成一个逆序对。 ...

    学习电脑信息用while循环获得逆序数

    学习电脑信息用while循环获得逆序数 在计算机编程中,while循环是一种常用的循环结构,可以用来实现各种复杂的算法。今天,我们将学习如何使用while循环来获得逆序数。 什么是逆序数?逆序数是指将一个数字的各个...

Global site tag (gtag.js) - Google Analytics