first commit
This commit is contained in:
commit
57d1dee00b
7 files changed
+465
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
## The questions I screwed
|
||||||
|
|
||||||
|
Questions (and their answers) I screwed up during interviews[^1].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
## Question
|
||||||
|
|
||||||
|
Implement a *Least Recently Used* (lru) cache
|
||||||
|
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package lrucache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Creator func(key string) interface{}
|
||||||
|
|
||||||
|
type cacheKey struct {
|
||||||
|
Value interface{}
|
||||||
|
DoubleLinkedListNode *list.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
type LRUCache struct {
|
||||||
|
cache map[string]*cacheKey
|
||||||
|
ddl *list.List
|
||||||
|
creator Creator
|
||||||
|
length int
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(creator Creator, length int) *LRUCache {
|
||||||
|
return &LRUCache{
|
||||||
|
cache: make(map[string]*cacheKey),
|
||||||
|
creator: creator,
|
||||||
|
ddl: new(list.List).Init(),
|
||||||
|
length: length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lru *LRUCache) add(key string, val interface{}) interface{} {
|
||||||
|
lru.ddl.PushFront(key)
|
||||||
|
lru.cache[key] = &cacheKey{val, lru.ddl.Front()}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lru *LRUCache) Size() int {
|
||||||
|
return len(lru.cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lru *LRUCache) removeExtraKeys() {
|
||||||
|
if lru.ddl.Len() < lru.length {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for e := lru.ddl.Back(); lru.ddl.Len() > lru.length; {
|
||||||
|
delete(lru.cache, e.Value.(string))
|
||||||
|
be := e.Prev()
|
||||||
|
lru.ddl.Remove(e)
|
||||||
|
e = be
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lru *LRUCache) Get(key string) interface{} {
|
||||||
|
if val, ok := lru.cache[key]; ok {
|
||||||
|
lru.ddl.MoveToFront(val.DoubleLinkedListNode)
|
||||||
|
return val.Value
|
||||||
|
}
|
||||||
|
ret := lru.add(key, lru.creator(key))
|
||||||
|
lru.removeExtraKeys()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// Example of an lru implementation using double linked lists (not thread safe)
|
||||||
|
package lrucache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mockCreator(key string) interface{} {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
func doubleLinkedListContains(l *list.List, elm string) bool {
|
||||||
|
for node := l.Front(); node != nil; node = node.Next() {
|
||||||
|
if node.Value.(string) == elm {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func toString(a interface{}) string {
|
||||||
|
if i, ok := a.(string); ok {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
panic("cannot convert in string")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAdd(t *testing.T) {
|
||||||
|
lru := New(mockCreator, 10)
|
||||||
|
if lru.add("key", "val") != "val" {
|
||||||
|
t.Error("must returns the value passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if toString(lru.ddl.Front().Value) != "key" {
|
||||||
|
t.Error("key must be ther first item after adding it")
|
||||||
|
}
|
||||||
|
if val, ok := lru.cache["key"]; !ok {
|
||||||
|
t.Error("key not in cache")
|
||||||
|
} else if toString(val.Value) != "val" {
|
||||||
|
t.Error("bad value associated to key")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveExtraKeys(t *testing.T) {
|
||||||
|
lru := New(mockCreator, 4)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
lru.add(strconv.Itoa(i), strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
if lru.Size() != 10 {
|
||||||
|
t.Error("bad initial length")
|
||||||
|
}
|
||||||
|
lru.removeExtraKeys()
|
||||||
|
if lru.Size() != 4 {
|
||||||
|
t.Error("bad final length. should be 4 but have ", lru.Size())
|
||||||
|
}
|
||||||
|
if lru.ddl.Len() != 4 {
|
||||||
|
t.Error("double linked list has incorrect length")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLRUCacheState(lru *LRUCache, expected ...string) error {
|
||||||
|
expectedSize := len(expected)
|
||||||
|
if len(lru.cache) != expectedSize {
|
||||||
|
return errors.New("cache of wrong size")
|
||||||
|
}
|
||||||
|
|
||||||
|
if lru.ddl.Len() != expectedSize {
|
||||||
|
return errors.New("double linked list of wrong size")
|
||||||
|
}
|
||||||
|
for _, e := range expected {
|
||||||
|
if _, ok := lru.cache[e]; !ok {
|
||||||
|
return errors.New("key " + e + " not in cache")
|
||||||
|
}
|
||||||
|
if !doubleLinkedListContains(lru.ddl, e) {
|
||||||
|
return errors.New("key " + e + " not in double linked list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGet(t *testing.T) {
|
||||||
|
lru := New(mockCreator, 2)
|
||||||
|
if v := lru.Get("0"); v != "0" {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
if err := checkLRUCacheState(lru, "0"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lru.Get("1")
|
||||||
|
if err := checkLRUCacheState(lru, "1", "0"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lru.Get("2")
|
||||||
|
if err := checkLRUCacheState(lru, "2", "1"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lru.Get("1")
|
||||||
|
if err := checkLRUCacheState(lru, "1", "2"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lru.Get("3")
|
||||||
|
if err := checkLRUCacheState(lru, "3", "1"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
## Question
|
||||||
|
|
||||||
|
You have *n* streams of sorted object, create a function that will merge them in a
|
||||||
|
sorted steam.
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package sorting
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
type IsGreaterer interface {
|
||||||
|
// return true iff "this" is strictly greater than e
|
||||||
|
IsGreater(e IsGreaterer) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type alwaysBigger struct{}
|
||||||
|
|
||||||
|
func (a *alwaysBigger) IsGreater(e IsGreaterer) bool {
|
||||||
|
return e != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stream chan IsGreaterer
|
||||||
|
|
||||||
|
type streamOutput struct {
|
||||||
|
Val IsGreaterer
|
||||||
|
Idx int
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFirstElementOfAllTheStreams(streams []Stream) []IsGreaterer {
|
||||||
|
values := make([]IsGreaterer, len(streams))
|
||||||
|
ch := make(chan *streamOutput)
|
||||||
|
for i, _ := range values {
|
||||||
|
go func(idx int) {
|
||||||
|
v := <-streams[idx]
|
||||||
|
ch <- &streamOutput{v, idx}
|
||||||
|
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for streamOutput := range ch {
|
||||||
|
values[streamOutput.Idx] = streamOutput.Val
|
||||||
|
i++
|
||||||
|
if i == len(streams) {
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func allStreamsButOne(streams []Stream, idx int) []Stream {
|
||||||
|
ret := make([]Stream, len(streams)-1)
|
||||||
|
for i, s := range streams {
|
||||||
|
if i < idx {
|
||||||
|
ret[i] = s
|
||||||
|
} else if i > idx {
|
||||||
|
ret[i-1] = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func prependToStream(streams Stream, toPrepend IsGreaterer) Stream {
|
||||||
|
ret := make(Stream)
|
||||||
|
go func() {
|
||||||
|
ret <- toPrepend
|
||||||
|
for e := range streams {
|
||||||
|
ret <- e
|
||||||
|
}
|
||||||
|
close(ret)
|
||||||
|
}()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func prependValueToEachStream(streams []Stream, values []IsGreaterer) []Stream {
|
||||||
|
ret := make([]Stream, len(streams))
|
||||||
|
for i := 0; i < len(streams); i++ {
|
||||||
|
ret[i] = prependToStream(streams[i], values[i])
|
||||||
|
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams []Stream) (Stream, IsGreaterer, []Stream) {
|
||||||
|
firstElements := readFirstElementOfAllTheStreams(streams)
|
||||||
|
streams = prependValueToEachStream(streams, firstElements)
|
||||||
|
|
||||||
|
var smallestStreamIndex int = -1
|
||||||
|
var smallestItem IsGreaterer = &alwaysBigger{}
|
||||||
|
var secondSmallestItem IsGreaterer = &alwaysBigger{}
|
||||||
|
|
||||||
|
for i, e := range firstElements {
|
||||||
|
if smallestItem.IsGreater(e) {
|
||||||
|
smallestItem = e
|
||||||
|
smallestStreamIndex = i
|
||||||
|
} else if secondSmallestItem.IsGreater(e) {
|
||||||
|
secondSmallestItem = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := secondSmallestItem.(*alwaysBigger); ok {
|
||||||
|
return streams[smallestStreamIndex], nil, []Stream{}
|
||||||
|
}
|
||||||
|
return streams[smallestStreamIndex], secondSmallestItem, allStreamsButOne(streams, smallestStreamIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortStreamInChannel(o Stream, streams []Stream) {
|
||||||
|
if len(streams) == 1 {
|
||||||
|
for e := range streams[0] {
|
||||||
|
o <- e
|
||||||
|
}
|
||||||
|
close(o)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
smallestStream, secondSmallestItem, greaterStreams := getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams)
|
||||||
|
if secondSmallestItem == nil {
|
||||||
|
sortStreamInChannel(o, append(greaterStreams, smallestStream))
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
for elm := range smallestStream {
|
||||||
|
if !elm.IsGreater(secondSmallestItem) {
|
||||||
|
o <- elm
|
||||||
|
} else {
|
||||||
|
smallestStream = prependToStream(smallestStream, elm)
|
||||||
|
sortStreamInChannel(o, append(greaterStreams, smallestStream))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sortStreamInChannel(o, greaterStreams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes sorted streams as input and return the merged result stream
|
||||||
|
func SortStreams(streams ...Stream) Stream {
|
||||||
|
ret := make(Stream)
|
||||||
|
go sortStreamInChannel(ret, streams)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package sorting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockIsGreaterer int
|
||||||
|
|
||||||
|
func (i MockIsGreaterer) IsGreater(e IsGreaterer) bool {
|
||||||
|
return i > e.(MockIsGreaterer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func streamEqual(stream Stream, expected ...int) bool {
|
||||||
|
i := 0
|
||||||
|
for v := range stream {
|
||||||
|
if i >= len(expected) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if MockIsGreaterer(expected[i]) != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return i == len(expected)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateStream(values ...int) Stream {
|
||||||
|
s := make(Stream)
|
||||||
|
go func() {
|
||||||
|
for _, val := range values {
|
||||||
|
s <- MockIsGreaterer(val)
|
||||||
|
}
|
||||||
|
close(s)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadFirstElementOfAllTheStreams(t *testing.T) {
|
||||||
|
s1 := CreateStream(0, 1)
|
||||||
|
arr := readFirstElementOfAllTheStreams([]Stream{s1})
|
||||||
|
if !reflect.DeepEqual(arr, []IsGreaterer{MockIsGreaterer(0)}) {
|
||||||
|
t.Error(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if next := <-s1; next != MockIsGreaterer(1) {
|
||||||
|
t.Error("read too many element in the list")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s1 = CreateStream(0, 1, 2)
|
||||||
|
s2 := CreateStream(5, 6, 7)
|
||||||
|
s3 := CreateStream(90, 91, 92)
|
||||||
|
arr = readFirstElementOfAllTheStreams([]Stream{s1, s2, s3})
|
||||||
|
if !reflect.DeepEqual(arr, []IsGreaterer{MockIsGreaterer(0), MockIsGreaterer(5), MockIsGreaterer(90)}) {
|
||||||
|
t.Error(arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllStreamsButOne(t *testing.T) {
|
||||||
|
streams := []Stream{CreateStream(0, 10, 1), CreateStream(1, 10, 1), CreateStream(2, 10, 1)}
|
||||||
|
|
||||||
|
res := allStreamsButOne(streams, 1)
|
||||||
|
if len(res) != 2 {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
if e := <-res[0]; e != MockIsGreaterer(0) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
if e := <-res[1]; e != MockIsGreaterer(2) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(t *testing.T) {
|
||||||
|
streams := []Stream{CreateStream(0, 1), CreateStream(3, 4), CreateStream(7, 8)}
|
||||||
|
stream, e, streams := getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams)
|
||||||
|
var _ = stream
|
||||||
|
_ = streams
|
||||||
|
if e != MockIsGreaterer(3) {
|
||||||
|
t.Error("getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams does not return the second smallest value", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := <-stream; v != MockIsGreaterer(0) {
|
||||||
|
t.Error("return streams is not the smalest", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(streams) != 2 {
|
||||||
|
t.Error("bad number of returned streams")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(streams) != 2 {
|
||||||
|
t.Error("bad number of returned streams")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortStreams(t *testing.T) {
|
||||||
|
if s := SortStreams(CreateStream(0, 1, 2)); !streamEqual(s, 0, 1, 2) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream(0, 1), CreateStream(2, 3)); !streamEqual(s, 0, 1, 2, 3) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream(0), CreateStream(2)); !streamEqual(s, 0, 2) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream(0, 5), CreateStream(2)); !streamEqual(s, 0, 2, 5) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream(0, 5), CreateStream(2, 7)); !streamEqual(s, 0, 2, 5, 7) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream(0, 5), CreateStream(2, 7, 9, 10, 12)); !streamEqual(s, 0, 2, 5, 7, 9, 10, 12) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream(), CreateStream(2, 7)); !streamEqual(s, 2, 7) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream(), CreateStream(2, 7), CreateStream(3, 8)); !streamEqual(s, 2, 3, 7, 8) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream()); !streamEqual(s) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := SortStreams(CreateStream(1, 2), CreateStream(2, 3)); !streamEqual(s, 1, 2, 2, 3) {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in new issue
Block a user