Skip to content

Commit

Permalink
Merge ef7d9ba into 02d9753
Browse files Browse the repository at this point in the history
  • Loading branch information
vaskoz committed Oct 26, 2020
2 parents 02d9753 + ef7d9ba commit d50336b
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ problems from
* [Day 355](https://github.com/vaskoz/dailycodingproblem-go/issues/704)
* [Day 356](https://github.com/vaskoz/dailycodingproblem-go/issues/705)
* [Day 357](https://github.com/vaskoz/dailycodingproblem-go/issues/706)
* [Day 358](https://github.com/vaskoz/dailycodingproblem-go/issues/707)
* [Day 359](https://github.com/vaskoz/dailycodingproblem-go/issues/708)
* [Day 360](https://github.com/vaskoz/dailycodingproblem-go/issues/709)
* [Day 361](https://github.com/vaskoz/dailycodingproblem-go/issues/712)
Expand Down
152 changes: 152 additions & 0 deletions day358/problem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package day358

import "container/list"

// BigOhOne is a data structure that performs all operations
// in O(1) time: constant time operations.
type BigOhOne interface {

// Plus adds a key with value 1.
// If the key already exists, increment its value by one.
Plus(key interface{})

// Minus decrements the value of a key.
// If the key's value is currently 1, remove it.
Minus(key interface{})

// Max returns the key with the highest value.
Max() (interface{}, int)

// Min returns the key with the lowest value.
Min() (interface{}, int)
}

type bigOhOne struct {
entryList *list.List
lookupEntryByKey map[interface{}]*list.Element
lookupListByCount map[int]*list.Element
}

type entry struct {
count int
values *list.List
lookupByKey map[interface{}]*list.Element
}

func (bo *bigOhOne) Plus(key interface{}) {
eSource := bo.lookupEntryByKey[key]

if eSource == nil {
vals := list.New()
lookup := make(map[interface{}]*list.Element)
lookup[key] = vals.PushFront(key)

newEntry := &entry{
count: 0,
values: vals,
lookupByKey: lookup,
}
eSource = bo.entryList.PushFront(newEntry)
}

sourceEntry := eSource.Value.(*entry)

eTarget := bo.lookupListByCount[sourceEntry.count+1]

if eTarget == nil {
newEntry := &entry{
count: sourceEntry.count + 1,
values: list.New(),
lookupByKey: make(map[interface{}]*list.Element),
}
eTarget = bo.entryList.InsertAfter(newEntry, eSource)
}

targetEntry := eTarget.Value.(*entry)

bo.lookupEntryByKey[key] = eTarget
bo.lookupListByCount[targetEntry.count] = eTarget
targetEntry.lookupByKey[key] = targetEntry.values.PushFront(key)

sourceEntryKey := sourceEntry.lookupByKey[key]
sourceEntry.values.Remove(sourceEntryKey)
delete(sourceEntry.lookupByKey, key)

if len(sourceEntry.lookupByKey) == 0 {
delete(bo.lookupListByCount, sourceEntry.count)
bo.entryList.Remove(eSource)
}
}

func (bo *bigOhOne) Minus(key interface{}) {
eSource := bo.lookupEntryByKey[key]

if eSource == nil {
return
}

sourceEntry := eSource.Value.(*entry)

eTarget := bo.lookupListByCount[sourceEntry.count-1]

if eTarget == nil {
newEntry := &entry{
count: sourceEntry.count - 1,
values: list.New(),
lookupByKey: make(map[interface{}]*list.Element),
}
eTarget = bo.entryList.InsertBefore(newEntry, eSource)
}

targetEntry := eTarget.Value.(*entry)

bo.lookupEntryByKey[key] = eTarget
bo.lookupListByCount[targetEntry.count] = eTarget
targetEntry.lookupByKey[key] = targetEntry.values.PushFront(key)

sourceEntryKey := sourceEntry.lookupByKey[key]
sourceEntry.values.Remove(sourceEntryKey)
delete(sourceEntry.lookupByKey, key)

if len(sourceEntry.lookupByKey) == 0 {
delete(bo.lookupListByCount, sourceEntry.count)
bo.entryList.Remove(eSource)
}

if targetEntry.count == 0 {
delete(bo.lookupEntryByKey, key)
delete(bo.lookupListByCount, targetEntry.count)
bo.entryList.Remove(eTarget)
}
}

func (bo *bigOhOne) Max() (interface{}, int) {
if bo.entryList.Len() == 0 {
return nil, 0
}

back := bo.entryList.Back().Value.(*entry)
val := back.values.Back().Value

return val, back.count
}

func (bo *bigOhOne) Min() (interface{}, int) {
if bo.entryList.Len() == 0 {
return nil, 0
}

front := bo.entryList.Front().Value.(*entry)
val := front.values.Front().Value

return val, front.count
}

// New returns a new instance of a BigOhOne data structure.
func New() BigOhOne {
return &bigOhOne{
entryList: list.New(),
lookupEntryByKey: make(map[interface{}]*list.Element),
lookupListByCount: make(map[int]*list.Element),
}
}
83 changes: 83 additions & 0 deletions day358/problem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package day358

import "testing"

// nolint
var actions = []struct {
key interface{}
action string
count int
}{
{nil, "max", 0},
{nil, "min", 0},
{"cat", "plus", 1},
{"dog", "plus", 1},
{"cat", "minus", 1},
{"dog", "minus", 1},
{"foo", "plus", 3},
{"bar", "plus", 4},
{"bar", "max", 4},
{"foo", "min", 3},
{"foo", "plus", 2},
{"foo", "max", 5},
{"bar", "min", 4},
{"foo", "minus", 3},
{"bar", "max", 4},
{"foo", "min", 2},
{"foo", "minus", 1},
{"bar", "max", 4},
{"foo", "min", 1},
{"foo", "minus", 1},
{"bar", "max", 4},
{"bar", "min", 4},
}

func TestBigOhOne(t *testing.T) {
t.Parallel()

bo := New()

for _, a := range actions {
switch a.action {
case "plus":
for i := 0; i < a.count; i++ {
bo.Plus(a.key)
}
case "minus":
for i := 0; i < a.count; i++ {
bo.Minus(a.key)
}
case "min":
if key, count := bo.Min(); key != a.key || count != a.count {
t.Errorf("Expected Min (%v,%v) got (%v,%v)", a.key, a.count, key, count)
}
case "max":
if key, count := bo.Max(); key != a.key || count != a.count {
t.Errorf("Expected Max (%v,%v) got (%v,%v)", a.key, a.count, key, count)
}
}
}
}

func BenchmarkBigOhOne(b *testing.B) {
bo := New()

for i := 0; i < b.N; i++ {
for _, a := range actions {
switch a.action {
case "plus":
for i := 0; i < a.count; i++ {
bo.Plus(a.key)
}
case "minus":
for i := 0; i < a.count; i++ {
bo.Minus(a.key)
}
case "min":
bo.Min()
case "max":
bo.Max()
}
}
}
}

0 comments on commit d50336b

Please sign in to comment.