From 76ca4e2a0ee9f3b654f4def13fc7034552575248 Mon Sep 17 00:00:00 2001 From: huangsimin Date: Fri, 19 Jul 2019 17:41:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84:=20linkedlist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../arraylist.go => array_list/array_list.go} | 0 .../array_list_test.go} | 0 list/linked_list/iterator.go | 87 +++++++ .../linked_list.go} | 112 +++++++-- .../linked_list_test.go} | 213 +++++++++++++++--- map/linked_hashmap/linked_hashmap.go | 121 ++++++++++ map/linked_hashmap/linked_hashmap_test.go | 98 ++++++++ 7 files changed, 580 insertions(+), 51 deletions(-) rename list/{arraylist/arraylist.go => array_list/array_list.go} (100%) rename list/{arraylist/arraylist_test.go => array_list/array_list_test.go} (100%) create mode 100644 list/linked_list/iterator.go rename list/{linkedlist/linkedlist.go => linked_list/linked_list.go} (70%) rename list/{linkedlist/linkedlist_test.go => linked_list/linked_list_test.go} (68%) create mode 100644 map/linked_hashmap/linked_hashmap.go create mode 100644 map/linked_hashmap/linked_hashmap_test.go diff --git a/list/arraylist/arraylist.go b/list/array_list/array_list.go similarity index 100% rename from list/arraylist/arraylist.go rename to list/array_list/array_list.go diff --git a/list/arraylist/arraylist_test.go b/list/array_list/array_list_test.go similarity index 100% rename from list/arraylist/arraylist_test.go rename to list/array_list/array_list_test.go diff --git a/list/linked_list/iterator.go b/list/linked_list/iterator.go new file mode 100644 index 0000000..0b12219 --- /dev/null +++ b/list/linked_list/iterator.go @@ -0,0 +1,87 @@ +package linkedlist + +type Iterator struct { + ll *LinkedList + cur *Node +} + +func (iter *Iterator) Value() interface{} { + return iter.cur.value +} + +func (iter *Iterator) Prev() bool { + if iter.cur == iter.ll.head { + return false + } + iter.cur = iter.cur.prev + return iter.cur != iter.ll.head +} + +func (iter *Iterator) Next() bool { + if iter.cur == iter.ll.tail { + return false + } + iter.cur = iter.cur.next + return iter.cur != iter.ll.tail +} + +func (iter *Iterator) MoveToHead() { + iter.cur = iter.ll.head +} + +func (iter *Iterator) MoveToTail() { + iter.cur = iter.ll.tail +} + +type CircularIterator struct { + pl *LinkedList + cur *Node +} + +func (iter *CircularIterator) Value() interface{} { + return iter.cur.value +} + +func (iter *CircularIterator) Prev() bool { + if iter.pl.size == 0 { + return false + } + + if iter.cur == iter.pl.head { + iter.cur = iter.pl.tail.prev + return true + } + + iter.cur = iter.cur.prev + if iter.cur == iter.pl.head { + iter.cur = iter.pl.tail.prev + } + + return true +} + +func (iter *CircularIterator) Next() bool { + if iter.pl.size == 0 { + return false + } + + if iter.cur == iter.pl.tail { + iter.cur = iter.pl.head.next + return true + } + + iter.cur = iter.cur.next + if iter.cur == iter.pl.tail { + iter.cur = iter.pl.head.next + } + + return true +} + +func (iter *CircularIterator) MoveToHead() { + iter.cur = iter.pl.head +} + +func (iter *CircularIterator) MoveToTail() { + iter.cur = iter.pl.tail +} diff --git a/list/linkedlist/linkedlist.go b/list/linked_list/linked_list.go similarity index 70% rename from list/linkedlist/linkedlist.go rename to list/linked_list/linked_list.go index 6d22c4a..d7709d8 100644 --- a/list/linkedlist/linkedlist.go +++ b/list/linked_list/linked_list.go @@ -1,6 +1,11 @@ package linkedlist -import "fmt" +import ( + "fmt" + "log" + + "github.com/davecgh/go-spew/spew" +) type Node struct { prev *Node @@ -18,12 +23,6 @@ type LinkedList struct { size uint } -// var nodePool *sync.Pool = &sync.Pool{ -// New: func() interface{} { -// return &Node{} -// }, -// } - func New() *LinkedList { l := &LinkedList{} l.head = &Node{} @@ -37,6 +36,14 @@ func New() *LinkedList { return l } +func (l *LinkedList) Iterator() *Iterator { + return &Iterator{ll: l, cur: l.head} +} + +func (l *LinkedList) CircularIterator() *CircularIterator { + return &CircularIterator{pl: l, cur: l.head} +} + func (l *LinkedList) Clear() { l.head.next = nil l.tail.prev = nil @@ -191,7 +198,7 @@ func (l *LinkedList) Index(idx uint) (interface{}, bool) { } func (l *LinkedList) Insert(idx uint, values ...interface{}) { - if idx > l.size { + if idx > l.size { // 插入的方式 可能导致size的范围判断不一样 return } @@ -265,13 +272,37 @@ func (l *LinkedList) Insert(idx uint, values ...interface{}) { l.size += uint(len(values)) } -func (l *LinkedList) InsertIf(every func(idx uint, value interface{}) int, values ...interface{}) { +// InsertState InsertIf的every函数的枚举 从左到右 1 为前 2 为后 insert here(2) ->cur-> insert here(1) +// UninsertAndContinue 不插入并且继续 +// UninsertAndBreak 不插入并且停止 +// InsertBack cur后插入并且停止 +// InsertFront cur前插入并且停止 +type InsertState int + +const ( + // UninsertAndContinue 不插入并且继续 + UninsertAndContinue InsertState = 0 + // UninsertAndBreak 不插入并且停止 + UninsertAndBreak InsertState = -1 + // InsertBack cur后插入并且停止 + InsertBack InsertState = 2 + // InsertFront cur前插入并且停止 + InsertFront InsertState = 1 +) + +// InsertIf every函数的枚举 从左到右遍历 1 为前 2 为后 insert here(2) ->cur-> insert here(1) +func (l *LinkedList) InsertIf(every func(idx uint, value interface{}) InsertState, values ...interface{}) { idx := uint(0) // 头部 for cur := l.head.next; cur != nil; cur = cur.next { - isInsert := every(idx, cur.value) - if isInsert != 0 { // 1 为前 -1 为后 insert here(-1) ->cur-> insert here(1) + insertState := every(idx, cur.value) + + if insertState == UninsertAndContinue { + continue + } + + if insertState > 0 { // 1 为前 2 为后 insert here(2) ->cur-> insert here(1) var start *Node var end *Node @@ -285,13 +316,13 @@ func (l *LinkedList) InsertIf(every func(idx uint, value interface{}) int, value end = node } - if isInsert < 0 { + if insertState == InsertBack { cprev := cur.prev cprev.next = start start.prev = cprev end.next = cur cur.prev = end - } else { + } else { // InsertFront cnext := cur.next cnext.prev = end start.prev = cur @@ -300,8 +331,11 @@ func (l *LinkedList) InsertIf(every func(idx uint, value interface{}) int, value } l.size += uint(len(values)) - break + return } + + // 必然 等于 UninsertAndBreak + return } } @@ -315,8 +349,9 @@ func remove(cur *Node) { } func (l *LinkedList) Remove(idx uint) (interface{}, bool) { - if idx >= l.size { - panic(fmt.Sprintf("out of list range, size is %d, idx is %d", l.size, idx)) + if l.size <= idx { + log.Printf("out of list range, size is %d, idx is %d\n", l.size, idx) + return nil, false } l.size-- @@ -346,21 +381,50 @@ func (l *LinkedList) Remove(idx uint) (interface{}, bool) { panic(fmt.Sprintf("unknown error")) } -func (l *LinkedList) RemoveIf(every func(idx uint, value interface{}) int) (result []interface{}, isRemoved bool) { +// RemoveState RemoveIf的every函数的枚举 +// RemoveAndContinue 删除并且继续 +// RemoveAndBreak 删除并且停止 +// UnremoveAndBreak 不删除并且停止遍历 +// UnremoveAndContinue 不删除并且继续遍历 +type RemoveState int + +const ( + // RemoveAndContinue 删除并且继续 + RemoveAndContinue RemoveState = iota + // RemoveAndBreak 删除并且停止 + RemoveAndBreak + // UnremoveAndBreak 不删除并且停止遍历 + UnremoveAndBreak + // UnremoveAndContinue 不删除并且继续遍历 + UnremoveAndContinue +) + +// RemoveIf every的遍历函数操作remove过程 如果没删除result 返回nil, isRemoved = false +func (l *LinkedList) RemoveIf(every func(idx uint, value interface{}) RemoveState) (result []interface{}, isRemoved bool) { // 头部 idx := uint(0) +TOPFOR: for cur := l.head.next; cur != l.tail; idx++ { - j := every(idx, cur.value) - switch { - case j > 0: + removeState := every(idx, cur.value) + switch removeState { + case RemoveAndContinue: result = append(result, cur.value) isRemoved = true temp := cur.next remove(cur) cur = temp l.size-- - continue - case j < 0: + continue TOPFOR + case RemoveAndBreak: + result = append(result, cur.value) + isRemoved = true + temp := cur.next + remove(cur) + cur = temp + l.size-- + return + case UnremoveAndContinue: + case UnremoveAndBreak: return } @@ -377,6 +441,10 @@ func (l *LinkedList) Values() (result []interface{}) { return } +func (l *LinkedList) String() string { + return spew.Sprint(l.Values()) +} + func (l *LinkedList) Traversal(every func(interface{}) bool) { for cur := l.head.next; cur != l.tail; cur = cur.next { if !every(cur.value) { diff --git a/list/linkedlist/linkedlist_test.go b/list/linked_list/linked_list_test.go similarity index 68% rename from list/linkedlist/linkedlist_test.go rename to list/linked_list/linked_list_test.go index d00083e..8c7943a 100644 --- a/list/linkedlist/linkedlist_test.go +++ b/list/linked_list/linked_list_test.go @@ -90,7 +90,28 @@ func TestPopBack(t *testing.T) { } -func TestInsert(t *testing.T) { +func TestInsert2(t *testing.T) { + l := New() + + // "[4 3 2 1 0]" + for i := 0; i < 5; i++ { + l.PushFront(0, i) + } + + // step1: [0 0] -> step2: [1 0 0 0] front + if l.String() != "[4 0 3 0 2 0 1 0 0 0]" { + t.Error(l.String()) + } + + l.Insert(l.Size(), 5) + + // step1: [0 0] -> step2: [4 0 3 0 2 0 1 0 0 0] front size is 10, but you can insert 11. equal to PushBack [4 0 3 0 2 0 1 0 0 0 5] + if l.String() != "[4 0 3 0 2 0 1 0 0 0 5]" { + t.Error(l.String()) + } +} + +func TestInsert1(t *testing.T) { l1 := New() l2 := New() // "[4 3 2 1 0]" @@ -149,13 +170,13 @@ func TestInsertIf(t *testing.T) { l.Insert(0, i) } - // "[4 3 2 1 0]" + // "[4 3 2 1 0]" 插入两个11 for i := 0; i < 2; i++ { - l.InsertIf(func(idx uint, value interface{}) int { + l.InsertIf(func(idx uint, value interface{}) InsertState { if value == 3 { - return 1 + return InsertFront } - return 0 + return UninsertAndContinue }, 11) } @@ -168,11 +189,11 @@ func TestInsertIf(t *testing.T) { // "[4 3 2 1 0]" for i := 0; i < 2; i++ { - l.InsertIf(func(idx uint, value interface{}) int { + l.InsertIf(func(idx uint, value interface{}) InsertState { if value == 0 { - return -1 + return InsertBack } - return 0 + return UninsertAndContinue }, 11) } @@ -183,11 +204,11 @@ func TestInsertIf(t *testing.T) { // "[4 3 2 1 0]" for i := 0; i < 2; i++ { - l.InsertIf(func(idx uint, value interface{}) int { + l.InsertIf(func(idx uint, value interface{}) InsertState { if value == 0 { - return 1 + return InsertFront } - return 0 + return UninsertAndContinue }, 11) } @@ -385,13 +406,10 @@ func TestRemove(t *testing.T) { t.Error("should be [3 2 1] but result is", result, "Size is", l.Size()) } - defer func() { - if err := recover(); err == nil { - t.Error("should be out of range but is not") - } - }() + if _, rvalue := l.Remove(3); rvalue != false { + t.Error("l is empty") + } - l.Remove(3) } func TestRemoveIf(t *testing.T) { @@ -401,11 +419,11 @@ func TestRemoveIf(t *testing.T) { l.PushFront(i) } - if result, ok := l.RemoveIf(func(idx uint, value interface{}) int { + if result, ok := l.RemoveIf(func(idx uint, value interface{}) RemoveState { if value == 0 { - return 1 + return RemoveAndContinue } - return 0 + return UnremoveAndContinue }); ok { if result[0] != 0 { t.Error("result should is", 0) @@ -414,11 +432,11 @@ func TestRemoveIf(t *testing.T) { t.Error("should be ok") } - if result, ok := l.RemoveIf(func(idx uint, value interface{}) int { + if result, ok := l.RemoveIf(func(idx uint, value interface{}) RemoveState { if value == 4 { - return 1 + return RemoveAndContinue } - return 0 + return UnremoveAndContinue }); ok { if result[0] != 4 { t.Error("result should is", 4) @@ -433,11 +451,11 @@ func TestRemoveIf(t *testing.T) { t.Error("should be [3 2 1] but result is", result) } - if result, ok := l.RemoveIf(func(idx uint, value interface{}) int { + if result, ok := l.RemoveIf(func(idx uint, value interface{}) RemoveState { if value == 4 { - return 1 + return RemoveAndContinue } - return 0 + return UnremoveAndContinue }); ok { t.Error("should not be ok and result is nil") } else { @@ -451,17 +469,154 @@ func TestRemoveIf(t *testing.T) { t.Error("should be [3 2 1] but result is", result) } - l.RemoveIf(func(idx uint, value interface{}) int { + l.RemoveIf(func(idx uint, value interface{}) RemoveState { if value == 3 || value == 2 || value == 1 { - return 1 + return RemoveAndContinue } - return 0 + return UnremoveAndContinue }) result = spew.Sprint(l.Values()) if result != "" { t.Error("result should be , but now result is", result) } + + if results, ok := l.RemoveIf(func(idx uint, value interface{}) RemoveState { + if value == 3 || value == 2 || value == 1 { + return RemoveAndContinue + } + return UnremoveAndContinue + }); ok { + t.Error("why ok") + } else { + if results != nil { + t.Error(results) + } + } + +} + +func TestRemoveIf2(t *testing.T) { + l := New() + // "[4 3 2 1 0]" + for i := 0; i < 5; i++ { + l.PushFront(i) + } + + for i := 0; i < 5; i++ { + l.PushFront(i) + } + + // 只删除一个 + if result, ok := l.RemoveIf(func(idx uint, value interface{}) RemoveState { + if value == 0 { + return RemoveAndBreak + } + return UnremoveAndContinue + }); ok { + if result[0] != 0 { + t.Error("result should is", 0) + } + } else { + t.Error("should be ok") + } + + var resultstr string + resultstr = spew.Sprint(l.Values()) + if resultstr != "[4 3 2 1 4 3 2 1 0]" { + t.Error("result should is", resultstr) + } + + // 只删除多个 + if result, ok := l.RemoveIf(func(idx uint, value interface{}) RemoveState { + if value == 4 { + return RemoveAndContinue + } + return UnremoveAndContinue + }); ok { + + resultstr = spew.Sprint(result) + if resultstr != "[4 4]" { + t.Error("result should is", result) + } + + resultstr = spew.Sprint(l.Values()) + if resultstr != "[3 2 1 3 2 1 0]" { + t.Error("result should is", resultstr) + } + + } else { + t.Error("should be ok") + } +} + +func TestIterator(t *testing.T) { + ll := New() + for i := 0; i < 10; i++ { + ll.PushFront(i) + } + + iter := ll.Iterator() + + for i := 0; iter.Next(); i++ { + if iter.Value() != 9-i { + t.Error("iter.Next() ", iter.Value(), "is not equal ", 9-i) + } + } + + if iter.cur != iter.ll.tail { + t.Error("current point is not equal tail ", iter.ll.tail) + } + + for i := 0; iter.Prev(); i++ { + if iter.Value() != i { + t.Error("iter.Prev() ", iter.Value(), "is not equal ", i) + } + } +} + +func TestCircularIterator(t *testing.T) { + ll := New() + for i := 0; i < 10; i++ { + ll.PushFront(i) + } + + iter := ll.CircularIterator() + + for i := 0; i != 10; i++ { + iter.Next() + if iter.Value() != 9-i { + t.Error("iter.Next() ", iter.Value(), "is not equal ", 9-i) + } + } + + if iter.cur != iter.pl.tail.prev { + t.Error("current point is not equal tail ", iter.pl.tail.prev) + } + + if iter.Next() { + if iter.Value() != 9 { + t.Error("iter.Value() != ", iter.Value()) + } + } + + iter.MoveToTail() + for i := 0; i != 10; i++ { + iter.Prev() + if iter.Value() != i { + t.Error("iter.Prev() ", iter.Value(), "is not equal ", i) + } + } + + if iter.cur != iter.pl.head.next { + t.Error("current point is not equal tail ", iter.pl.tail.prev) + } + + if iter.Prev() { + if iter.Value() != 0 { + t.Error("iter.Value() != ", iter.Value()) + } + } } func BenchmarkPushBack(b *testing.B) { diff --git a/map/linked_hashmap/linked_hashmap.go b/map/linked_hashmap/linked_hashmap.go new file mode 100644 index 0000000..a255dab --- /dev/null +++ b/map/linked_hashmap/linked_hashmap.go @@ -0,0 +1,121 @@ +package linkedhashmap + +import ( + "fmt" + + linkedlist "github.com/474420502/focus/list/linked_list" + "github.com/davecgh/go-spew/spew" +) + +// LinkedHashmap +type LinkedHashmap struct { + list *linkedlist.LinkedList + hmap map[interface{}]interface{} +} + +// New +func New() *LinkedHashmap { + lhmap := &LinkedHashmap{list: linkedlist.New(), hmap: make(map[interface{}]interface{})} + return lhmap +} + +// PushBack if key exists, push value replace the value is exists. size is unchanging +func (lhmap *LinkedHashmap) PushBack(key interface{}, value interface{}) { + if _, ok := lhmap.hmap[key]; !ok { + lhmap.list.PushBack(key) + } + lhmap.hmap[key] = value +} + +// PushFront if key exists, push value replace the value is exists. size is unchanging +func (lhmap *LinkedHashmap) PushFront(key interface{}, value interface{}) { + if _, ok := lhmap.hmap[key]; !ok { + lhmap.list.PushFront(key) + } + lhmap.hmap[key] = value +} + +// Insert 如果成功在该位置返回True, 否则返回false +func (lhmap *LinkedHashmap) Insert(idx uint, key interface{}, value interface{}) bool { + if _, ok := lhmap.hmap[key]; !ok { + + return true + } + return false +} + +// Get +func (lhmap *LinkedHashmap) Get(key interface{}) (interface{}, bool) { + value, ok := lhmap.hmap[key] + return value, ok +} + +// Clear +func (lhmap *LinkedHashmap) Clear() { + lhmap.list.Clear() + lhmap.hmap = make(map[interface{}]interface{}) +} + +// Remove if key not exists reture nil, false. +func (lhmap *LinkedHashmap) Remove(key interface{}) (interface{}, bool) { + if v, ok := lhmap.hmap[key]; ok { + delete(lhmap.hmap, key) + lhmap.list.RemoveIf(func(idx uint, lkey interface{}) linkedlist.RemoveState { + if lkey == key { + return linkedlist.RemoveAndBreak + } + return linkedlist.UnremoveAndContinue + }) + return v, true + } + return nil, false +} + +// RemoveIndex +func (lhmap *LinkedHashmap) RemoveIndex(idx uint) (interface{}, bool) { + if lhmap.list.Size() <= idx { + panic(fmt.Sprintf("out of list range, size is %d, idx is %d", lhmap.list.Size(), idx)) + } + + if _, ok := lhmap.hmap[key]; ok { + delete(lhmap.hmap, key) + lhmap.list.RemoveIf(func(idx uint, lkey interface{}) linkedlist.RemoveState { + if lkey == key { + return linkedlist.RemoveAndBreak + } + return linkedlist.UnremoveAndContinue + }) + } +} + +// Empty returns true if map does not contain any elements +func (lhmap *LinkedHashmap) Empty() bool { + return lhmap.Size() == 0 +} + +// Size returns number of elements in the map. +func (lhmap *LinkedHashmap) Size() uint { + return lhmap.list.Size() +} + +// Keys returns all keys left to right (head to tail) +func (lhmap *LinkedHashmap) Keys() []interface{} { + return lhmap.list.Values() +} + +// Values returns all values in-order based on the key. +func (lhmap *LinkedHashmap) Values() []interface{} { + values := make([]interface{}, lhmap.Size()) + count := 0 + lhmap.list.Traversal(func(key interface{}) bool { + values[count] = lhmap.hmap[key] + count++ + return true + }) + return values +} + +// String returns a string +func (lhmap *LinkedHashmap) String() string { + return spew.Sprint(lhmap.Values()) +} diff --git a/map/linked_hashmap/linked_hashmap_test.go b/map/linked_hashmap/linked_hashmap_test.go new file mode 100644 index 0000000..e109207 --- /dev/null +++ b/map/linked_hashmap/linked_hashmap_test.go @@ -0,0 +1,98 @@ +package linkedhashmap + +import ( + "reflect" + "testing" +) + +func TestPush(t *testing.T) { + lhm := New() + lhm.PushFront(1, "1") + lhm.PushBack("2", 2) + var values []interface{} + values = lhm.Values() + + var testType reflect.Type + + if testType = reflect.TypeOf(values[0]); testType.String() != "string" { + t.Error(testType) + } + + if testType = reflect.TypeOf(values[1]); testType.String() != "int" { + t.Error(testType) + } + + // 1 2 + lhm.PushFront(4, "4") // 4 1 2 + lhm.PushBack("3", 3) // 4 1 2 3 + + if lhm.String() != "[4 1 2 3]" { + t.Error(lhm.String()) + } +} + +func TestBase(t *testing.T) { + lhm := New() + for i := 0; i < 10; i++ { + lhm.PushBack(i, i) + } + + if lhm.Empty() { + t.Error("why lhm Enpty, check it") + } + + if lhm.Size() != 10 { + t.Error("why lhm Size != 10, check it") + } + + lhm.Clear() + if !lhm.Empty() { + t.Error("why lhm Clear not Empty, check it") + } + + if lhm.Size() != 0 { + t.Error("why lhm Size != 0, check it") + } +} + +func TestGet(t *testing.T) { + lhm := New() + for i := 0; i < 10; i++ { + lhm.PushBack(i, i) + } + + for i := 0; i < 10; i++ { + lhm.PushBack(i, i) + } + + if lhm.Size() != 10 { + t.Error("why lhm Size != 10, check it") + } + + for i := 0; i < 10; i++ { + if v, ok := lhm.Get(i); !ok || v != i { + t.Error("ok is ", ok, " get value is ", v) + } + } +} + +func TestRemove(t *testing.T) { + lhm := New() + for i := 0; i < 10; i++ { + lhm.PushBack(i, i) + } + + var resultStr = "[0 1 2 3 4 5 6 7 8 9]" + for i := 0; i < 10; i++ { + if lhm.String() != resultStr { + t.Error(lhm.String(), resultStr) + } + + lhm.Remove(i) + if lhm.Size() != uint(9-i) { + t.Error("why lhm Size != ", uint(9-i), ", check it") + } + + resultStr = resultStr[0:1] + resultStr[3:] + } +}