Skip to content

Commit

Permalink
add method to count the number of inversions in the slice
Browse files Browse the repository at this point in the history
  • Loading branch information
denpeshkov committed Feb 23, 2024
1 parent 04a1289 commit ab1172c
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
1 change: 1 addition & 0 deletions invcount/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Function to count the number of inversions in the array based on merge sort
55 changes: 55 additions & 0 deletions invcount/invcount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package invcount

import (
"cmp"
)

// Count returns the number of inversions in the input slice.
// Time complexity is N*log(N)
func Count[T ~[]E, E cmp.Ordered](s T) int {
aux := make(T, len(s))

sc := append(T(nil), s...)
return sortCount(sc, aux)
}

func sortCount[T ~[]E, E cmp.Ordered](s, a T) int {
if len(s) <= 1 {
return 0
}

m := len(s) / 2
invsL := sortCount(s[:m], a)
invsR := sortCount(s[m:], a)
invs := invsL + invsR + mergeCount(s[:m], s[m:], a)

copy(s, a)

return invs
}

func mergeCount[T ~[]E, E cmp.Ordered](s1, s2, a T) int {
invs := 0

l1, l2 := len(s1), len(s2)
for i, j, k := 0, 0, 0; k < l1+l2; k++ {
switch {
case i >= l1:
a[k] = s2[j]
j++
case j >= l2:
a[k] = s1[i]
i++
case s1[i] <= s2[j]:
a[k] = s1[i]
i++
case s1[i] > s2[j]:
a[k] = s2[j]
j++
// all to the right are inversions
invs += l1 - i
}
}

return invs
}
39 changes: 39 additions & 0 deletions invcount/invcount_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package invcount

import (
"slices"
"testing"
)

func TestCountInversions(t *testing.T) {
cases := []struct {
input []int
expected int
}{
{[]int{1, 2, 3, 4, 5}, 0}, // Sorted array
{[]int{2, 4, 1, 3, 5}, 3}, // (2, 1), (4, 1), (4, 3)
{[]int{5, 4, 3, 2, 1}, 10}, // Maximum inversions for a reversed array
{[]int{3, 5, 1, 2, 4}, 5}, // (3, 1), (3, 2), (5, 1), (5, 2), (5,4)
{[]int{1, 1, 1, 1, 1}, 0}, // All elements are same, no inversions
{[]int{2, 1}, 1}, // (2, 1)
{[]int{}, 0}, // Empty array
{[]int{1}, 0}, // Single element array
{[]int{1, 3, 5, 2, 4, 6}, 3}, // (3, 2), (5, 2), (5, 4)
{[]int{5, 3, 1, 2, 4}, 6}, // (5, 3), (5, 1), (5, 2), (5, 4), (3, 1), (3, 2)
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9}, 0},
{[]int{9, 8, 7, 6, 5, 4, 3, 2, 1}, 36},
{[]int{5, 3, 7, 2, 8, 4, 6, 1}, 16},
{[]int{1, 4, 2, 5, 7, 4, 2, 4, 6, 0, 7, 4, 6, 7, 3, 2, 5, 6, 4, 7, 3, 6, 1}, 100},
}

for _, c := range cases {
cc := append([]int(nil), c.input...)
got := Count(cc)
if got != c.expected {
t.Errorf("Count(%v) == %d, expected %d", c.input, got, c.expected)
}
if !slices.Equal(cc, c.input) {
t.Errorf("Count(%v) modified input", c.input)
}
}
}

0 comments on commit ab1172c

Please sign in to comment.