给定一个int数组A和它的大小n,对于这组数能组成的任意两个数组,若前面一个大于后面一个数字,则这两个数字组成一个逆序对。请设计一种高效的算法返回A中存在的逆序对个数。要求n不大于5000。
[1,2,3,4,5,6,7,0],8
返回:7
import java.util.*; //这个题和上一个求数组的秩是类似的,逆序对=当前数组长度 - 秩, //在秩的基础上,把左右子树对调一下就可以了,在秩的基础上修改两个不等号就ok了 public class AntiOrder { Node root = null; public int count(int[] A, int n) { int res = 0; for (int i = 0; i < n; i++) { res += helper(A[i]); } return res; } public int helper(int a) { if (root == null) { root = new Node(a); return 0; } else { root.insert(a); return root.getRank(a); } } } class Node { int leftSize = 0; Node left, right; int val; public Node(int v) { val = v; } public void insert(int v) { if (v > val) { if (left != null) left.insert(v); else left = new Node(v); leftSize++; } else { if (right != null) right.insert(v); else right = new Node(v); } } public int getRank(int v) { if (v == val) return leftSize; if (v > val) return left.getRank(v); if (v < val) return leftSize + 1 + right.getRank(v); return 0; } }
/* [1,2,3,4,5,6,7,0],8 7 类似11.8,也可以用multiset(底层为红黑树)做, 以0为例,end-cur即为0的逆序对数目 */ class AntiOrder { public: int count(vector<int> A, int n) { // write code here int ret = 0; multiset<int> mset; multiset<int>::iterator cur, end; for (int i = 0; i < n; i++) { mset.insert(A[i]); cur = mset.upper_bound(A[i]); end = mset.end(); while (cur != end) { ret++; cur++; } } return ret; } };
int merge(int[] A, int start, int mid, int end) {
int[] aux = new int[end - start + 1];
int i = start;
int j = mid + 1;
int k = 0;
int reverse = 0;
while (i <= mid && j <= end) {
if (A[i] <= A[j]) {
aux[k++] = A[i++];
} else {
reverse += mid - i + 1;
aux[k++] = A[j++];
}
}
while (i <= mid) {
aux[k++] = A[i++];
}
while (j <= end) {
aux[k++] = A[j++];
}
for (int m = 0; m < aux.length; ++m) {
A[start + m] = aux[m];
}
return reverse;
}
public int count(int[] A, int start, int end) {
if (end <= start) {
return 0;
}
int mid = (start + end) >> 1;
int count1 = count(A, start, mid);
int count2 = count(A, mid + 1, end);
return count1 + count2 + merge(A, start, mid, end);
}
public int count(int[] A, int n) {
// write code here
return count(A, 0, n - 1);
}
class AntiOrder { public: //归并排序,前面一块数据中没出现一个值比后面值的数,则前面之后的数都比后面的该数大 void merge_sort(vector& A,int start,int end,vector& tmp,int& count) { if(start == end) return; int mid = (start+end)/2; merge_sort(A,start,mid,tmp,count); merge_sort(A,mid+1,end,tmp,count); int i=start,j=mid+1; int ind = start; while(i<=mid && j<=end) { if(A[i] > A[j]) { count += mid-i+1; tmp[ind++] = A[j++]; }else { tmp[ind++] = A[i++]; } } while(i <= mid) { tmp[ind++] = A[i++]; } while(j <= end) { tmp[ind++] = A[j++]; } for(int k=start;k<=end;++k) { A[k] = tmp[k]; } } int count(vector A, int n) { // write code here vector tmp(n,0); int num = 0; merge_sort(A,0,n-1,tmp,num); return num; } };
import java.util.*; //其实也就是求一个数左边有少个比它大的,排序后p2之前的数就是p2的所有逆序对 (R-p2+1) public class AntiOrder { public int count(int[] A,int n) { if(A == null || n == 0) return 0; return mergSort(A,0,A.length-1); } public int mergSort(int[] arr,int L,int R){ if(L == R) return 0; int mid = L+ ((R-L) >> 1); return mergSort(arr,L,mid)+mergSort(arr,mid+1,R)+merg(arr,L,mid,R); } public int merg(int[] arr,int L,int mid,int R){ int[] help = new int[R-L+1]; int i = 0; int count = 0; int p1 = L; int p2 = mid+1; while(p1 <= mid && p2 <= R){ if(arr[p1] > arr[p2]){ count += (mid-L)+1; help[i++] = arr[p2++]; }else{ help[i++] = arr[p1++]; } } while(p1 <= mid){ help[i++] = arr[p1++]; } while(p2 <= R){ help[i++] = arr[p2++]; } for (int j = 0; j < help.length; j++) { arr[L + j] = help[j]; } return count; } }
import java.util.*; /* 错误的思路:先排序,再对比排序后的数字和排序前的数字位置,相同数字在不同位置之间的位置差的数目就是逆序对的数目 如何找相同数字的不同位置~用二分法可以降复杂度,时间复杂度O(nlogn) 很想知道我这个思路哪里错了啊!!!求大神解释 正确的思路:归并排序 */ /* public class AntiOrder { public int count(int[] A, int n) { int b[] =new int[n]; int j=0; int sum=0; for(int m:A){ b[j]=m; j++; } Arrays.sort(b); for(j=0;j<n;j++){ int start=0; int end =n-1; int mid=0; while(start<=end){ mid =(start+end)/2; if(A[j]==b[mid]) break; else if(A[j]>b[mid]) start=mid+1; else end =mid-1; } if(mid-j>0) sum=sum+mid-j; } return sum; } } */ public class AntiOrder { public int count(int[] A, int n) { if(A==null||A.length==0) return 0; int []copy=new int[n]; for(int i=0;i<n;i++){ copy[i]=A[i]; } return mergesort(A,copy,0,n-1); } public int mergesort(int[] A,int[] copy,int start,int end){ if(start==end){ copy[start] =A[start]; return 0; } int mid=(start+end)/2; int left =mergesort(copy,A,start,mid); //这里要注意了,copy和A的数组互换了,实际上我们比较的都是copy中的数组值,而不是A的数组值 //因为copy中数组是一直保持分段数组内部顺序的(即每次逆序对计算完,都会把计算完的逆序对顺序调整) //用copy数组比较才不会出现重复计算逆序对的情况,这里如果不互换顺序会多计算无数次逆序对 int right =mergesort(copy,A,mid+1,end); int i=mid,j=end,count=0,index=end; while(i>=start&&j>mid){ if(A[i]>A[j]){ count +=j-mid; copy[index--]=A[i--]; }else{ copy[index--]=A[j--]; } } while(i>=start){ copy[index--]=A[i--]; } while(j>mid){ copy[index--]=A[j--]; } return count+left+right; } } 运行时间:205ms 占用内存:13612k
归并排序 class AntiOrder { public: int mergeSort(vector<int>& A, vector<int>& temp, int left, int right) { if (left >= right) return 0; int reverseCount = 0; int mid = (left + right) >> 1; reverseCount += mergeSort(A, temp, left, mid); reverseCount += mergeSort(A, temp, mid + 1, right); int pos, i, j; for (pos = left,i = left, j = mid + 1; j <= right; ++j) { while (i <= mid && A[i] < A[j]) { temp[pos] = A[i]; ++pos; ++i; } if (i <= mid) reverseCount += mid - i + 1; temp[pos] = A[j]; ++pos; } while (i <= mid) temp[pos++] = A[i++]; memcpy(A.data() + left, temp.data() + left, (right - left + 1) * sizeof(int)); return reverseCount; } int count(vector<int> A, int n) { vector<int> temp(n, 0); return mergeSort(A, temp, 0, n - 1); } };
class AntiOrder: def count(self, A, n): self.rNums = 0 self.mergeSort(A) return self.rNums def mergeSort(self, A): if len(A) <= 1: return A m = len(A) / 2 a = self.mergeSort(A[:m]) b = self.mergeSort(A[m:]) return self.merge(a, b) def merge(self, A, B): c, i, j = [], 0, 0 while i < len(A) and j < len(B): if A[i] <= B[j]: c.append(A[i]) i += 1 else: c.append(B[j]) j += 1 self.rNums += len(A) - i #此为逆序对数 c += A[i:] c += B[j:] return c
class AntiOrder { public: int count(vector<int> data, int n) { // write code here if(data.size()<=1) return 0; int* copy=new int[data.size()]; //初始化数组 for(unsigned int i=0;i<data.size();i++) copy[i]=0; int count=InversePairCore(data,copy,0,data.size()-1); delete[] copy; return count; } int InversePairCore(vector<int>& data,int*& copy,int start,int end) { if(start==end) { copy[start]=data[start]; return 0; } //将数组拆分成两部分 int length=(end-start)/2; //分别计算左边部分和右边部分 int left=InversePairCore(data,copy,start,start+length)%1000000007; int right=InversePairCore(data,copy,start+length+1,end)%1000000007; //进行逆序计算 int i=start+length; int j=end; int index=end; int count=0; while(i>=start && j>=start+length+1) { if(data[i]>data[j]) { copy[index--]=data[i--]; //统计长度 count+=j-start-length; if(count>=1000000007)//数值过大求余 count%=1000000007; } else { copy[index--]=data[j--]; } } for(;i>=start;--i) { copy[index--]=data[i]; } for(;j>=start+length+1;--j) { copy[index--]=data[j]; } //排序 for(int i=start; i<=end; i++) { data[i] = copy[i]; } //返回最终的结果 return (count+left+right)%1000000007; } };
/* * 数组的逆序对就是当前数组长度-秩,与上题一样,维护一组二叉查找树,每次添加新值后得到该数组对应的秩,用当前数组长度-秩 * 对所有值的添加过程都进行逆序对计算,并加和即可 */ public class RankNode{ public int left_size = 0; public RankNode left, right; public int data = 0; public RankNode(int d) { // TODO Auto-generated constructor stub data = d; } public void insert(int d) { // TODO Auto-generated method stub if(d <= data){ if(left != null){ left.insert(d); }else{ left = new RankNode(d); } left_size++; }else{ if(right != null){ right.insert(d); }else{ right = new RankNode(d); } } } public int getRank(int d) { // TODO Auto-generated method stub if(d == data){ return left_size; }else if(d < data){ if(left == null){ return -1; }else{ return left.getRank(d); } }else{ int right_rank = right == null ? -1 : right.getRank(d); if(right_rank == -1) return -1; return left_size + 1 + right.getRank(d); } } } public int count(int[] A, int n) { // write code here if(A == null || A.length != n || n <= 0){ return 0; } RankNode root = new RankNode(A[0]); int[] ranks = new int[n]; int res = 0; for(int i = 1; i < n; i++){ root.insert(A[i]); ranks[i] = root.getRank(A[i]); res += i - ranks[i]; } return res; }
//Method 2: merge sort
大概思路是归并排序的思想,先分为2个左右部分,左部分的逆序对(只在左部分取2个数),右部分的逆序对(只在右部分取2个数),满足在左部分取一个数,右部分取一个数的逆序对,以上全部加起来就是总的逆序对。计算左右部分的逆序对的时候,利用递归。 class Solution { public: int InversePairs(vector<int> data) { return merge(data, 0, data.size() - 1); } int merge_count(vector<int> &data, int start, int mid, int end)//计算满足左部分一个数,右部分一个数条件的逆序对,此时左右部分已排好序 { vector<int> temp(data); int count = 0; int i = start; int j = mid + 1; int k = start; while (i <= mid&&j <= end) { if (data[i] <=data[j]) temp[k++] = data[i++]; else { temp[k++] = data[j++]; count = count + mid- i+1; } } while (i <= mid)temp[k++] = data[i++]; while (j <= end)temp[k++] = data[j++]; for (int m = start; m <= end; m++) data[m] = temp[m]; return count; } int merge(vector<int> &data, int start, int end)//左右部分分别递归, { if (start >= end) return 0; int mid = (start + end) / 2; int left=merge(data, start, mid); int right=merge(data, mid + 1, end); int count = merge_count(data, start, mid, end); return left + right + count; } };
//常规思想,找出每个元素的逆序对,复杂度是O(N*N) //利用归并排序的思想,先划分再合并,复杂度可以降到O(NlogN) class AntiOrder { public: void merge(vector<int> &v,int a1,int a2,int b1,int b2,int &pairs){ vector<int> tmp; int i=a1; int j=b1; while(i<=a2&&j<=b2){ if(v[i]<=v[j]){ tmp.push_back(v[i++]); pairs=pairs+j-b1; } else{ tmp.push_back(v[j++]); } } while(i<=a2){ tmp.push_back(v[i++]); pairs=pairs+b2-b1+1; } while(j<=b2) tmp.push_back(v[j++]); for(i=0,j=a1;j<=b2;j++) v[j]=tmp[i++]; } void fun(vector<int> &v,int p1,int p2,int &pairs){ if(p1==p2) return; fun(v,p1,(p1+p2)/2,pairs); fun(v,(p1+p2)/2+1,p2,pairs); merge(v,p1,(p1+p2)/2,(p1+p2)/2+1,p2,pairs); } int count(vector<int> A, int n) { int pairs=0; fun(A,0,n-1,pairs) ; return pairs; } };
import java.util.*; public class AntiOrder { public int count(int[] A, int n) { // write code here if (A == null || n == 0) { return 0; } return mergeSortRecursion(A, 0, n - 1); } /** * 递归实现归并排序 * * @param arr * @param l * @param r * @return 返回数组中的逆序对 */ public static int mergeSortRecursion(int[] arr, int l, int r) { if (l == r) { // 当待排序数组长度为1时,递归开始回溯,进行merge操作 return 0; } int mid = (l + r) / 2; return mergeSortRecursion(arr, l, mid) + mergeSortRecursion(arr, mid + 1, r) + merge(arr, l, mid, r); } /** * 合并两个已排好序的数组s[left...mid]和s[mid+1...right] * * @param arr * @param left * @param mid * @param right * @return 返回合并过程中累加逆序对 */ public static int merge(int[] arr, int left, int mid, int right) { int[] temp = new int[right - left + 1]; // 辅助存储空间 O(n) int index = 0; int i = left; int j = mid + 1; int inverseNum = 0; // 新增,用来累加数组逆序对 while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[index++] = arr[i++]; } else { // 当前一个数组元素大于后一个数组元素时,累加逆序对 // s[i] > s[j] -> s[i]...s[mid] > s[j] inverseNum += (mid - i + 1); temp[index++] = arr[j++]; } } while (i <= mid) { temp[index++] = arr[i++]; } while (j <= right) { temp[index++] = arr[j++]; } for (int k = 0; k < temp.length; k++) { arr[left++] = temp[k]; } return inverseNum; } }
# -*- coding:utf-8 -*- class AntiOrder: def count(self, A, n): # write code here self.res = 0 # 调用归并排序计算逆序数 self.mergeSort(A) return self.res def mergeSort(self, A): if len(A) <= 1: return A # 递归结束 mid = len(A) // 2 left = self.mergeSort(A[:mid]) # 左边排好序 right = self.mergeSort(A[mid:]) # 右边排好序 merged = [] while left and right: if left[0] <= right[0]: merged.append(left.pop(0)) else: self.res += len(left) # 逆序数 merged.append(right.pop(0)) # 剩余部分放在数组后面 merged.extend(right if right else left) return merged