From b577ac1dd444ae6d0658010983f01985a99e0ed6 Mon Sep 17 00:00:00 2001 From: Leonid Edrenkin Date: Sun, 22 Nov 2020 11:24:51 +0100 Subject: [PATCH 1/2] Add Count() method to the API Count() method returns the number of independent components --- README.md | 6 +++++- safe-unionfind.go | 7 +++++-- unionfind.go | 20 ++++++++++++++++++-- unionfind_test.go | 20 +++++++++++++++++++- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cd4943c..e27c475 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ uf.Union(4, 6) fmt.PrintLn(uf.Find(2)) // Prints 1 fmt.PrintLn(uf.Connected(1, 2)) // Prints true fmt.PrintLn(uf.Connected(1, 3)) // Prints false +fmt.PrintLn(uf.Count()) // Prints 6 ``` @@ -39,8 +40,11 @@ Same as Find. #### `uf.Connected(p, q)` Checks if p and q are connected. +#### `uf.Count()` +Returns the number of independent components. + ## EXTRA There is also a goroutine safe version of unionfind in the file `safe-unionfind`. ## Licence -MIT - Theo Despoudis \ No newline at end of file +MIT - Theo Despoudis diff --git a/safe-unionfind.go b/safe-unionfind.go index 4e25022..5718fca 100644 --- a/safe-unionfind.go +++ b/safe-unionfind.go @@ -27,7 +27,6 @@ SOFTWARE. // Thread safe version of Union-Find using plain locks. - type ThreadSafeUnionFind struct { sync.RWMutex uf *UnionFind @@ -47,7 +46,6 @@ func (suf *ThreadSafeUnionFind) Union(p int, q int) { suf.uf.Union(p, q) } - func (suf *ThreadSafeUnionFind) Root(p int) int { suf.Lock() defer suf.Unlock() @@ -68,4 +66,9 @@ func (suf *ThreadSafeUnionFind) Connected(p int, q int) bool { return suf.uf.Root(p) == suf.uf.Root(p) } +func (suf *ThreadSafeUnionFind) Count() int { + suf.Lock() + defer suf.Unlock() + return suf.uf.Count() +} diff --git a/unionfind.go b/unionfind.go index c2c9443..f312ed0 100644 --- a/unionfind.go +++ b/unionfind.go @@ -36,8 +36,9 @@ SOFTWARE. // type UnionFind struct { - root []int - size []int + root []int + size []int + count int } // New returns an initialized list of size @@ -50,6 +51,7 @@ func (uf *UnionFind) init(size int) *UnionFind { uf = new(UnionFind) uf.root = make([]int, size) uf.size = make([]int, size) + uf.count = size for i := 0; i < size; i++ { uf.root[i] = i @@ -62,8 +64,15 @@ func (uf *UnionFind) init(size int) *UnionFind { // Union connects p and q by finding their roots and comparing their respective // size arrays to keep the tree flat func (uf *UnionFind) Union(p int, q int) { + if p == q { + return + } + qRoot := uf.Root(q) pRoot := uf.Root(p) + if qRoot == pRoot { + return + } if uf.size[qRoot] < uf.size[pRoot] { uf.root[qRoot] = uf.root[pRoot] @@ -72,6 +81,8 @@ func (uf *UnionFind) Union(p int, q int) { uf.root[pRoot] = uf.root[qRoot] uf.size[qRoot] += uf.size[pRoot] } + + uf.count-- } // Root or Find traverses each parent element while compressing the @@ -99,3 +110,8 @@ func (uf *UnionFind) Find(p int) int { func (uf *UnionFind) Connected(p int, q int) bool { return uf.Root(p) == uf.Root(q) } + +// Count returns the number of independent items. +func (uf *UnionFind) Count() int { + return uf.count +} diff --git a/unionfind_test.go b/unionfind_test.go index 5f4a03c..47361fe 100644 --- a/unionfind_test.go +++ b/unionfind_test.go @@ -19,6 +19,7 @@ var _ = Suite(&MySuite{}) func (s *MySuite) TestNew(c *C) { uf := New(10) + c.Assert(uf.count, Equals, 10) c.Assert(uf.size, DeepEquals, []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) c.Assert(uf.root, DeepEquals, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) } @@ -29,9 +30,10 @@ func (s *MySuite) TestRootEmpty(c *C) { c.Assert(uf.Root(10), Equals, -1) c.Assert(uf.Root(9), Equals, 9) + c.Assert(uf.Count(), Equals, 10) } -// Test Root when It has connected components +// Test Root when it has connected components func (s *MySuite) TestRootConnected(c *C) { uf := New(10) @@ -43,6 +45,7 @@ func (s *MySuite) TestRootConnected(c *C) { c.Assert(uf.Root(8), Equals, 2) c.Assert(uf.Root(2), Equals, 2) c.Assert(uf.Root(5), Equals, 6) + c.Assert(uf.Count(), Equals, 6) } // Test Union when when we connect the same item @@ -52,6 +55,20 @@ func (s *MySuite) TestUnionSame(c *C) { uf.Union(2, 2) c.Assert(uf.Root(2), Equals, 2) + c.Assert(uf.Count(), Equals, 10) +} + +// Test Union when when we connect items with the same root +func (s *MySuite) TestUnionSameRoot(c *C) { + uf := New(5) + uf.Union(0, 1) + uf.Union(1, 2) + uf.Union(2, 3) + c.Assert(uf.Count(), Equals, 2) + + uf.Union(0, 3) + + c.Assert(uf.Count(), Equals, 2) } // Test Union checks size array when it assigns the root @@ -63,4 +80,5 @@ func (s *MySuite) TestUnionChecksSizes(c *C) { uf.Union(2, 3) c.Assert(uf.size, DeepEquals, []int{1, 4, 1, 1, 1}) + c.Assert(uf.Count(), Equals, 2) } From b7b83b98aa85da354893469ab40cfb29bbd0e2c7 Mon Sep 17 00:00:00 2001 From: Leonid Edrenkin Date: Sun, 22 Nov 2020 11:36:50 +0100 Subject: [PATCH 2/2] Improve test coverage a bit --- unionfind.go | 5 ++++- unionfind_test.go | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/unionfind.go b/unionfind.go index f312ed0..7f0d638 100644 --- a/unionfind.go +++ b/unionfind.go @@ -63,6 +63,7 @@ func (uf *UnionFind) init(size int) *UnionFind { // Union connects p and q by finding their roots and comparing their respective // size arrays to keep the tree flat +// Time complexity is amortized O(1) func (uf *UnionFind) Union(p int, q int) { if p == q { return @@ -88,6 +89,7 @@ func (uf *UnionFind) Union(p int, q int) { // Root or Find traverses each parent element while compressing the // levels to find the root element of p // If we attempt to access an element outside the array it returns -1 +// Time complexity is amortized O(1) func (uf *UnionFind) Root(p int) int { if p > len(uf.root)-1 { return -1 @@ -111,7 +113,8 @@ func (uf *UnionFind) Connected(p int, q int) bool { return uf.Root(p) == uf.Root(q) } -// Count returns the number of independent items. +// Count returns the number of independent items +// Time complexity is O(1) func (uf *UnionFind) Count() int { return uf.count } diff --git a/unionfind_test.go b/unionfind_test.go index 47361fe..6b9466e 100644 --- a/unionfind_test.go +++ b/unionfind_test.go @@ -30,6 +30,7 @@ func (s *MySuite) TestRootEmpty(c *C) { c.Assert(uf.Root(10), Equals, -1) c.Assert(uf.Root(9), Equals, 9) + c.Assert(uf.Find(9), Equals, 9) c.Assert(uf.Count(), Equals, 10) } @@ -80,5 +81,6 @@ func (s *MySuite) TestUnionChecksSizes(c *C) { uf.Union(2, 3) c.Assert(uf.size, DeepEquals, []int{1, 4, 1, 1, 1}) + c.Assert(uf.Connected(0, 3), Equals, true) c.Assert(uf.Count(), Equals, 2) }