算法导论学习之第二章-寻找逆序对

论坛 期权论坛 脚本     
匿名技术用户   2021-1-3 08:11   522   0

今天学习算法导论第二章中提到的逆序对问题,思前想后采用2种方式比较妥当。具体见下述分析。

逆序对问题:

逆序对(inversion pair)是指在序列{a0,a1,a2...an}中,若ai<aj(i>j),则(ai,aj)上一对逆序对。而逆序数 (inversion number)顾名思义就是序列中逆序对的个数。例如: 1 2 3是顺序,则逆序数是0;1 3 2中(2,3)满足逆序对的条件,所以逆序数只有1; 3 2 1中(1,2)(1,3)(2,3)满足逆序对,所以逆序是3。由定义不难想象,序列n的逆序数范围在[0,n*(n-1)/2],其中顺序时逆序数为 0,对于一个倒序的自然序列{an,an-1,an-2.......3,2,1}来说,是一个完全逆序序列。根据定义我们可以很方便的得到该序列的逆序对总数为 T(N)=∑(i,i<=n)fn,其中fn=(n-1)得到最终结果为T(N)=N*(N-1)/2.

就我所知,目前求这种逆序对最快的算法是利用归并排序,其时间复杂度为O(nlgn), 空间复杂度为O(n)

这里我给出一个O(n*n)的循环算法和一个O(nlgn)的分治算法。

  1. 循环算法,见下面代码:
public int countByLoop(Integer a[]){
  int count=0;
  for(int i=0;i<a.length;i++){
   for(int j=a.length-1;j>i;j--){
    if(a[i]>=a[j]){
     count++;
     System.out.printf("found=(i=%d,j=%d,a[i]=%d,a[j]=%d)\n",i,j,a[i],a[j]);
    }
   }
  }
  return count;
 }

2. 分治算法实现如下

public int count(Integer a[],int low,int high){

  int count=0;
  if(low<high){
   int mid=(low+high)/2; 
   count+=count(a,low,mid);   //分治先计算左边的数组
   count+=count(a,mid+1,high);  //计算右边的数组
   count+=mergeCount(a,low,mid,high);  //合并两个结算结果
  }
  return count;
 }
 /**
  * 采用分治法求得逆序对
  * @param a
  * @return
  */
 public int count(Integer a[]){
  return count(a,0,a.length-1);
 }
 
 private int mergeCount(Integer[] a, int low, int mid, int high) {
  int count=0;
  int i=low;
  int j=mid+1;
  
  while(i<=mid&&j<=high){
   if(a[i]<=a[j]){
    i++;
   }else{
    count+=mid-i+1;
    for(int k=i;k<=mid;k++){
     System.out.printf("(%d,%d)\n",a[k],a[j]);
    }
    j++;
   }
  }
  
  while(i<=mid)i++;
  while(j<=high)j++;
  return count;
 }

看到上述的算法,是不是觉得很眼熟呢,不错,其实和我们熟悉的归并排序算法很相似,只是这里不需要额外的辅助内存空间来保存排序

欢迎各位拍砖

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP