分治算法题:nlgn时间复杂度计算原序列的重要逆序个数
2017-04-16 23:38
281 查看
题目描述:
Recall the problem of finding the number of inversions. As in the text, we are given a sequence of n numbers a1, ... , an, which we assume are all distinct, and we define an inversion to be a pair i< j such that ai>aj.
We motivated the problem of counting inversions as a good measure of how different two orderings are. However, one might feel that this measure is too sensitive. Let’s call a pair a significant inversion if i< j and ai> 2aj. Given an O(nlogn) algorithm to count
the number of significant inversions between two orderings.
我的理解:让我们求一个序列的significant inversion个数,这个不同于一般的逆序数,但是求的思想是一样的,都是采用分治法和归并排序的算法实现。但是不同于普通的求逆序数算法,如果是求ai>aj的个数的话,可以在计算合并子序列的逆序数个数的过程中顺便排序。因为如果不满足ai>aj的话,那就将aj放入tmp序列中,继续比较ai和a(j+1)的大小。
在思考多久之后,决定在函数merge的实现中,将计算合并的过程中产生的重要逆序数和给这个合并子序列排序分开用两个while循环实现。具体实现看代码。
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<string>
#include<string.h>
#include<set>
#include<map>
#include <algorithm>
using namespace std;
/*使用分治法和归并排序的思想来算逆序数,nlgn复杂度,同时完成了对原序列的排序*/
//合并A[left]-A[mid]和A[mid+1]-A[right]
int merge(int A[], int left, int mid, int right) {
int *tmp = new int[right-left+1];
int ret = 0;
int i = left, j = mid + 1,cnt = 0;
int k = mid - left + 1;
/*计算满足题意的逆序数个数*/
while (i <= mid && j <= right) {
if(A[i] > 2 * A[j]) {
ret += k;
j++;
}
else{
k--;
i++;
}
}
/*给子序列合并排序*/
i = left, j = mid + 1;
while (i <= mid && j <= right) {
if(A[i] > A[j]) {
tmp[cnt++] = A[j];
j++;
}
else{
tmp[cnt++] = A[i];
i++;
}
}
while (i <= mid) {
tmp[cnt++] = A[i];
i++;
}
while (j <= right) {
tmp[cnt++] = A[j];
j++;
}
//至此,tmp为合并后排好序的序列 ,接下来将tmp的值复制给A
for (k = 0; k < right - left + 1; k++) {
A[left + k] = tmp[k];
}
delete []tmp;
return ret;
}
int inversionCount(int A[], int left, int right) {
if(left >= right) return 0;
int mid = (left + right) / 2;
int num1 = inversionCount(A, left, mid);
int num2 = inversionCount(A, mid+1, right);
return num1+num2+merge(A, left, mid, right);
}
int main() {
int n, num[100];
while(scanf("%d", &n) == 1) {
for (int i = 0; i < n; i++) scanf("%d", num + i);
int res = inversionCount(num, 0, n - 1);
/*输出重要逆序数个数*/
printf("%d\n", res);
/*遍历输出排好序的序列*/
for (int i = 0; i < n-1; i++) printf("%d ", num[i]);
printf("%d\n", num[n-1]);
}
return 0;
}
Recall the problem of finding the number of inversions. As in the text, we are given a sequence of n numbers a1, ... , an, which we assume are all distinct, and we define an inversion to be a pair i< j such that ai>aj.
We motivated the problem of counting inversions as a good measure of how different two orderings are. However, one might feel that this measure is too sensitive. Let’s call a pair a significant inversion if i< j and ai> 2aj. Given an O(nlogn) algorithm to count
the number of significant inversions between two orderings.
我的理解:让我们求一个序列的significant inversion个数,这个不同于一般的逆序数,但是求的思想是一样的,都是采用分治法和归并排序的算法实现。但是不同于普通的求逆序数算法,如果是求ai>aj的个数的话,可以在计算合并子序列的逆序数个数的过程中顺便排序。因为如果不满足ai>aj的话,那就将aj放入tmp序列中,继续比较ai和a(j+1)的大小。
在思考多久之后,决定在函数merge的实现中,将计算合并的过程中产生的重要逆序数和给这个合并子序列排序分开用两个while循环实现。具体实现看代码。
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<string>
#include<string.h>
#include<set>
#include<map>
#include <algorithm>
using namespace std;
/*使用分治法和归并排序的思想来算逆序数,nlgn复杂度,同时完成了对原序列的排序*/
//合并A[left]-A[mid]和A[mid+1]-A[right]
int merge(int A[], int left, int mid, int right) {
int *tmp = new int[right-left+1];
int ret = 0;
int i = left, j = mid + 1,cnt = 0;
int k = mid - left + 1;
/*计算满足题意的逆序数个数*/
while (i <= mid && j <= right) {
if(A[i] > 2 * A[j]) {
ret += k;
j++;
}
else{
k--;
i++;
}
}
/*给子序列合并排序*/
i = left, j = mid + 1;
while (i <= mid && j <= right) {
if(A[i] > A[j]) {
tmp[cnt++] = A[j];
j++;
}
else{
tmp[cnt++] = A[i];
i++;
}
}
while (i <= mid) {
tmp[cnt++] = A[i];
i++;
}
while (j <= right) {
tmp[cnt++] = A[j];
j++;
}
//至此,tmp为合并后排好序的序列 ,接下来将tmp的值复制给A
for (k = 0; k < right - left + 1; k++) {
A[left + k] = tmp[k];
}
delete []tmp;
return ret;
}
int inversionCount(int A[], int left, int right) {
if(left >= right) return 0;
int mid = (left + right) / 2;
int num1 = inversionCount(A, left, mid);
int num2 = inversionCount(A, mid+1, right);
return num1+num2+merge(A, left, mid, right);
}
int main() {
int n, num[100];
while(scanf("%d", &n) == 1) {
for (int i = 0; i < n; i++) scanf("%d", num + i);
int res = inversionCount(num, 0, n - 1);
/*输出重要逆序数个数*/
printf("%d\n", res);
/*遍历输出排好序的序列*/
for (int i = 0; i < n-1; i++) printf("%d ", num[i]);
printf("%d\n", num[n-1]);
}
return 0;
}
相关文章推荐
- 分治算法;随机化划分函数;快速排序;线性时间选择第K小元素;快速排序平均时间复杂度nlgn;
- 以时间复杂度O(n)计算最大子序列和
- n个无序整数,已知第i个数在排好序的序列中的位置为j,满足|i-j|<=K,请设计一种排序算法,对该序列进行排序。注:算法时间复杂度为O(nlgn)的得0分,复杂度为O(nk) 的得两分,总分是20分
- 在nlgn时间内实现逆序对数的计算
- 判断序列中是否存在两个元素之和为x,时间复杂度O(nlgn),算法导论练习2.3,linux纯C实现
- 逆序数 时间复杂度O(nlgn)
- 题目2.给出一个算法,它能用O(nlgn)的最坏情况运行时间,确定n个元素的任何排列中逆序对的数目
- 算法的度量-----时间复杂度问题的计算
- 改进合并排序法以查找元素序列中的逆序对数量,最坏运行时间O( nlog2(n) )
- 时间复杂度计算方法
- (C#)找出数组中最大子序列之和,分别以O(N^2),O(NlogN),O(N) 这3种时间复杂度求解
- 归并排序;分治算法;复杂度nlgn;附加逆序数算法;
- 如何计算时间复杂度
- 寻找逆序对的算法,要求运行时间为O(nlgn),算法导论答案
- 设M 是一个m×n 的矩阵,其中每行的元素从左到右单增有序,每列的元素从上到下单增有序。 给出一个分治算法计算出给定元素x 在M 中的位置或者表明x 不在M 中。分析算法的时间复杂性。
- 数组连续子序列的最大的和-四种算法,四种时间复杂度
- 时间复杂度计算
- 时间复杂度计算
- 时间复杂度计算O(n)
- 时间复杂度计算