14. 逆序数(题目043)
题目:
计算一个全由大写字母组成的字符串的逆序数。
解答:
求逆序数和排序的思路是类似的,稳定的排序算法都可以用来计算逆序数。
方法1:
最直接的方法,采用冒泡排序和插入排序的思路,一共需要遍历(n-1)*(n-2)次,比较慢。
方法2:
利用归并排序的思想加速这个过程。一个序列的逆序数等于它的两个递归子过程产生的逆序数与合并过程产生的逆序数之和。在合并两个有序序列list1和list2时,若list1[i]>list2[j],就说明list1中从i开始的元素都大于j,则逆序数就要加length(list1)-i。
时间复杂度O(NlgN),且可应用于不是字符串的序列。
int MergeInversion(char *s, int n)
{
int inversions = 0;
if (n > 1)
{
intmid = n / 2;
inversions += MergeInversion(s, mid);
inversions += MergeInversion(s + mid, n - mid);
inversions += Merge(s, n);
}
return inversions;
}
int Merge(char *s, int n)
{
char *buf = malloc(n);
memcpy(buf, s, n);
int i = 0, j = mid, k = 0, inversion = 0,mid = n / 2;
while (i < mid && j < n)
if (buf[i] <= buf[j])
s[k++] = buf[i++];
else
{
s[k++] = buf[j++];
inversion += mid - i;
}
while (i < mid)
s[k++] = buf[i++];
while (j < n)
s[k++] = buf[j++];
free(buf);
return inversion;
}
方法3:
注意题中的字符串只由大写字母组成,对于这种元素范围固定的问题,可以用计数排序的思路,时间复杂度是O(n)*O(m)的,其中m是元素范围。
int CountInversion(const char *s)
{
int count[26] = {0}, len = strlen(s), inversion = 0, i, j;
for (i = len - 1; i > -1; --i)
{
int c = s[i] - 'A';
++count[c];
for(j = 0; j < c; ++j)
inversion += count[j];
}
return inversion;
}