Reputation: 946
Permutation P = [a1, a2, ... , aN]
of the first N
natural numbers can be represented by a list of inversions I = [i1, i2, ... , iN]
, where iK
tells us how many numbers that are greater than K
can be found before K
in the permutation P
.
Example: if P = [3, 1, 4, 2]
, then I = [1, 2, 0, 0]
(3 is placed 1, 3 and 4 are placed before 2, whereas 3 and 4 are not preceded by any bigger number).
There is an obvious algorithm that converts a permutation from standard to inversion form and runs in O(N^2)
(we just follow the definition and count). The same holds for the inverse conversion (which is slightly less straight forward).
Is there an algorithm that has lower time complexity?
Upvotes: 1
Views: 283
Reputation: 946
Meanwhile, I have found a simple solution, regarding pseudo-code, which works in O(n log n)
, also.
initialize AVL-tree T
initialize array I of length n
for i = 1, 2, ... , n:
I[P[i]] = T.countGreaterThan(P[i])
T.add(P[i])
Upvotes: 0
Reputation: 8537
There is a simple iterative dynamic-programming algorithm to solve this: for all i from 1 to n
(length of the permutation) take the number i
and look how many elements in P
left of i
have already been seen. Since we process i
in increasing order, we know that the elements not seen are the elements bigger than i
- and so we count and write down the number of those elements. The trick is to introduce an external list than keeps track of which elements in P
have already been seen.
For the start, lets look at how to do it in O(n^2)
way. For example, if P=[4, 3, 2, 1]
, then the algorithm would executed as follows:
Create a structure tree
initialized to zeros. It holds "1" in position j
if element with j-th position in P
has already been seen by the iterative algorithm.
Take 1, determine that pos==3
. Write "1" down in the tree[pos]
. Calculate num_seen=sum(tree[0:3])
which is equal to 0. Write down pos - num_seen + 1
at I[0]
. After this: tree = [0, 0, 0, 1], I = [3, 0, 0, 0]
Take 2, write "1" down in the tree[1] and 1 at I[1]. tree = [0, 1, 0, 1], I=[3,1,0,0]
.
Take 3, write "1" down in the tree[2] and 0 at I[2]. tree = [0, 1, 1, 1], I=[3,1,0,0]
.
Take 4, write "1" down in the tree[0] and 0 at I[3]. tree = [1, 1, 1, 1], I=[3,1,0,0]
.
The second trick is to use an efficient data structure to count the number of the seen elements in O(n log n)
time instead of O(n^2)
as above.
Here is Python code that uses a Fenwick tree to count the number of seen elements in a fast way:
def ft_sum(tree, a, b):
if a == 0:
s = 0;
while b >= 0:
s += tree[b];
b = (b & (b + 1)) - 1
return s
return ft_sum(tree, 0, b) - ft_sum(tree, 0, a - 1)
def ft_adjust(tree, k, v):
while k < len(tree):
tree[k] += v
k |= k + 1
def calcI(P):
n = len(P)
tree = [0] * n
I = [0] * n
positions = [0] * n
for i in xrange(n):
positions[P[i]-1] = i
tree = [0] * n
for i in xrange(n):
pos = positions[i]
ft_adjust(tree, pos, 1)
num_seen = ft_sum(tree, 0, pos)
I[i] = pos - num_seen + 1
return I
Upvotes: 1