From 7f107bf6c3cf28a1a4c33b3752be3df62b241760 Mon Sep 17 00:00:00 2001 From: huangsimin Date: Tue, 12 Mar 2019 10:54:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=91=E9=87=8F=E6=B5=8B=E8=AF=95=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 14 + LICENSE | 21 ++ README.md | 2 + avltree/avltree.go | 550 ++++++++++++++++++++++++++++ avltree/avltree_test.go | 316 ++++++++++++++++ priority_list/iterator.go | 87 +++++ priority_list/priority_list.go | 138 +++++++ priority_list/priority_list_test.go | 148 ++++++++ priority_queue/iterator.go | 4 + priority_queue/priority_queue.go | 4 + 10 files changed, 1284 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 avltree/avltree.go create mode 100644 avltree/avltree_test.go create mode 100644 priority_list/iterator.go create mode 100644 priority_list/priority_list.go create mode 100644 priority_list/priority_list_test.go create mode 100644 priority_queue/iterator.go create mode 100644 priority_queue/priority_queue.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f86e4a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# ---> Go +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +*.log +.vscode diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d449d3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b7a80c2 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# structure + diff --git a/avltree/avltree.go b/avltree/avltree.go new file mode 100644 index 0000000..b822946 --- /dev/null +++ b/avltree/avltree.go @@ -0,0 +1,550 @@ +package bstree + +import ( + "github.com/davecgh/go-spew/spew" + + "github.com/emirpasic/gods/utils" +) + +type Node struct { + children [2]*Node + parent *Node + height int + child int + value interface{} +} + +func (n *Node) String() string { + if n == nil { + return "nil" + } + + p := "nil" + if n.parent != nil { + p = spew.Sprint(n.parent.value) + } + return spew.Sprint(n.value) + "(" + p + "-" + spew.Sprint(n.child) + "|" + spew.Sprint(n.height) + ")" +} + +type AVLTree struct { + root *Node + size int + comparator utils.Comparator +} + +func New(comparator utils.Comparator) *AVLTree { + return &AVLTree{comparator: comparator} +} + +func (avl *AVLTree) replace(old *Node, newN *Node) { + + if old.parent == nil { + setChild(newN, 0, old.children[0]) + setChild(newN, 1, old.children[1]) + + newN.parent = nil + newN.child = -1 + newN.height = old.height + + avl.root = newN + } else { + + setChild(newN, 0, old.children[0]) + setChild(newN, 1, old.children[1]) + + newN.parent = old.parent + newN.child = old.child + newN.height = old.height + old.parent.children[old.child] = newN + } +} + +func setChild(p *Node, child int, node *Node) { + p.children[child] = node + if node != nil { + node.child = child + node.parent = p + } +} + +func (avl *AVLTree) Remove(v interface{}) *Node { + + if n, ok := avl.GetNode(v); ok { + + avl.size-- + if avl.size == 0 { + avl.root = nil + return n + } + + left := getHeight(n.children[0]) + right := getHeight(n.children[1]) + + if left == -1 && right == -1 { + p := n.parent + p.children[n.child] = nil + avl.fixRemoveHeight(p) + return n + } + + var cur *Node + if left > right { + cur = n.children[0] + for cur.children[1] != nil { + cur = cur.children[1] + } + + cleft := cur.children[0] + cur.parent.children[cur.child] = cleft + if cleft != nil { + cleft.child = cur.child + cleft.parent = cur.parent + } + + } else { + cur = n.children[1] + for cur.children[0] != nil { + cur = cur.children[0] + } + + cright := cur.children[1] + cur.parent.children[cur.child] = cright + if cright != nil { + cright.child = cur.child + cright.parent = cur.parent + } + } + + cparent := cur.parent + avl.replace(n, cur) + // 考虑到刚好替换的节点是 被替换节点的孩子节点的时候, 从自身修复高度 + if cparent == n { + avl.fixRemoveHeight(cur) + } else { + avl.fixRemoveHeight(cparent) + } + + return n + } + + return nil +} + +func (avl *AVLTree) Get(v interface{}) (interface{}, bool) { + n, ok := avl.GetNode(v) + if ok { + return n.value, true + } + return n, false +} + +func (avl *AVLTree) GetNode(v interface{}) (*Node, bool) { + + n := avl.root + for n != nil { + switch c := avl.comparator(v, n.value); c { + case -1: + n = n.children[0] + case 1: + n = n.children[1] + case 0: + return n, true + default: + panic("Get comparator only is allowed in -1, 0, 1") + } + } + + return nil, false +} + +func (avl *AVLTree) Put(v interface{}) { + avl.size++ + node := &Node{value: v} + if avl.size == 1 { + avl.root = node + return + } + + cur := avl.root + parent := cur.parent + child := -1 + + for { + + if cur == nil { + parent.children[child] = node + node.parent = parent + node.child = child + + if node.parent.height == 0 { + avl.fixPutHeight(node.parent) + } + return + } + + parent = cur + c := avl.comparator(node.value, cur.value) + if c > -1 { // right + child = 1 + cur = cur.children[child] + } else { + child = 0 + cur = cur.children[child] + } + } + +} + +func (avl *AVLTree) String() string { + if avl.size == 0 { + return "" + } + str := "AVLTree" + "\n" + output(avl.root, "", true, &str) + + return str +} + +func (avl *AVLTree) debugString() string { + if avl.size == 0 { + return "" + } + str := "AVLTree" + "\n" + outputfordebug(avl.root, "", true, &str) + return str +} + +func (avl *AVLTree) TraversalBreadth() (result []interface{}) { + var traverasl func(cur *Node) + traverasl = func(cur *Node) { + if cur == nil { + return + } + result = append(result, cur.value) + traverasl(cur.children[0]) + traverasl(cur.children[1]) + } + traverasl(avl.root) + return +} + +func (avl *AVLTree) TraversalDepth(leftright int) (result []interface{}) { + + if leftright < 0 { + var traverasl func(cur *Node) + traverasl = func(cur *Node) { + if cur == nil { + return + } + traverasl(cur.children[0]) + result = append(result, cur.value) + traverasl(cur.children[1]) + } + traverasl(avl.root) + } else { + var traverasl func(cur *Node) + traverasl = func(cur *Node) { + if cur == nil { + return + } + traverasl(cur.children[1]) + result = append(result, cur.value) + traverasl(cur.children[0]) + } + traverasl(avl.root) + } + + return +} + +func (avl *AVLTree) lrrotate(cur *Node) *Node { + + r := cur.children[1] + rl := r.children[0] + if cur.parent == nil { + avl.root = rl + rl.parent = nil + } else { + setChild(cur.parent, cur.child, rl) + } + + rll := rl.children[0] + rlr := rl.children[1] + + setChild(cur, 1, rll) + setChild(r, 0, rlr) + + setChild(rl, 0, cur) + setChild(rl, 1, r) + + cur.height = getMaxChildrenHeight(cur) + 1 + r.height = getMaxChildrenHeight(r) + 1 + rl.height = getMaxChildrenHeight(rl) + 1 + + return rl +} + +func (avl *AVLTree) rlrotate(cur *Node) *Node { + + l := cur.children[0] + lr := l.children[1] + if cur.parent == nil { + avl.root = lr + lr.parent = nil + } else { + setChild(cur.parent, cur.child, lr) + } + + lrr := lr.children[1] + lrl := lr.children[0] + + setChild(cur, 0, lrr) + setChild(l, 1, lrl) + setChild(lr, 1, cur) + setChild(lr, 0, l) + + cur.height = getMaxChildrenHeight(cur) + 1 + l.height = getMaxChildrenHeight(l) + 1 + lr.height = getMaxChildrenHeight(lr) + 1 + + return lr +} + +func (avl *AVLTree) rrotate(cur *Node) *Node { + + l := cur.children[0] + + setChild(cur, 0, l.children[1]) + + l.parent = cur.parent + if cur.parent != nil { + cur.parent.children[cur.child] = l + } else { + avl.root = l + } + l.child = cur.child + + l.children[1] = cur + cur.child = 1 + cur.parent = l + + cur.height = getMaxChildrenHeight(cur) + 1 + l.height = getMaxChildrenHeight(l) + 1 + + return l +} + +func (avl *AVLTree) lrotate(cur *Node) *Node { + + r := cur.children[1] + + // 右左节点 链接 当前的右节点 + setChild(cur, 1, r.children[0]) + + // 设置 需要旋转的节点到当前节点的 链条 + r.parent = cur.parent + if cur.parent != nil { + cur.parent.children[cur.child] = r + } else { + avl.root = r + } + r.child = cur.child + + // 当前节点旋转到 左边的 链条 + + r.children[0] = cur + cur.child = 0 + cur.parent = r + + // 修复改动过的节点高度 先从低开始到高 + cur.height = getMaxChildrenHeight(cur) + 1 + r.height = getMaxChildrenHeight(r) + 1 + + return r +} + +func getMaxAndChildrenHeight(cur *Node) (h1, h2, maxh int) { + h1 = getHeight(cur.children[0]) + h2 = getHeight(cur.children[1]) + if h1 > h2 { + maxh = h1 + return + } + + maxh = h2 + return +} + +func getMaxChildrenHeight(cur *Node) int { + h1 := getHeight(cur.children[0]) + h2 := getHeight(cur.children[1]) + if h1 > h2 { + return h1 + } + return h2 +} + +func getHeight(cur *Node) int { + if cur == nil { + return -1 + } + return cur.height +} + +func (avl *AVLTree) fixRemoveHeight(cur *Node) { + + for { + + lefth, rigthh, lrmax := getMaxAndChildrenHeight(cur) + + // 判断当前节点是否有变化, 如果没变化的时候, 不需要往上修复 + isBreak := false + if cur.height == lrmax+1 { + isBreak = true + } else { + cur.height = lrmax + 1 + } + + // 计算高度的差值 绝对值大于2的时候需要旋转 + diff := lefth - rigthh + if diff < -1 { + r := cur.children[1] // 根据左旋转的右边节点的子节点 左右高度选择旋转的方式 + if getHeight(r.children[0]) > getHeight(r.children[1]) { + cur = avl.lrrotate(cur) + } else { + cur = avl.lrotate(cur) + } + } else if diff > 1 { + l := cur.children[0] + if getHeight(l.children[1]) > getHeight(l.children[0]) { + cur = avl.rlrotate(cur) + } else { + cur = avl.rrotate(cur) + } + } else { + + if isBreak { + return + } + + } + + if cur.parent == nil { + return + } + + cur = cur.parent + } + +} + +func (avl *AVLTree) fixPutHeight(cur *Node) { + + for { + + lefth := getHeight(cur.children[0]) + rigthh := getHeight(cur.children[1]) + + // 计算高度的差值 绝对值大于2的时候需要旋转 + diff := lefth - rigthh + if diff < -1 { + r := cur.children[1] // 根据左旋转的右边节点的子节点 左右高度选择旋转的方式 + if getHeight(r.children[0]) > getHeight(r.children[1]) { + cur = avl.lrrotate(cur) + } else { + cur = avl.lrotate(cur) + } + + } else if diff > 1 { + l := cur.children[0] + if getHeight(l.children[1]) > getHeight(l.children[0]) { + cur = avl.rlrotate(cur) + } else { + cur = avl.rrotate(cur) + } + } else { + // 选择一个child的最大高度 + 1为 高度 + if lefth > rigthh { + cur.height = lefth + 1 + } else { + cur.height = rigthh + 1 + } + } + + if cur.parent == nil || cur.height < cur.parent.height { + return + } + cur = cur.parent + } +} + +func output(node *Node, prefix string, isTail bool, str *string) { + + if node.children[1] != nil { + newPrefix := prefix + if isTail { + newPrefix += "│ " + } else { + newPrefix += " " + } + output(node.children[1], newPrefix, false, str) + } + *str += prefix + if isTail { + *str += "└── " + } else { + *str += "┌── " + } + + *str += spew.Sprint(node.value) + "\n" + + if node.children[0] != nil { + newPrefix := prefix + if isTail { + newPrefix += " " + } else { + newPrefix += "│ " + } + output(node.children[0], newPrefix, true, str) + } + +} + +func outputfordebug(node *Node, prefix string, isTail bool, str *string) { + + if node.children[1] != nil { + newPrefix := prefix + if isTail { + newPrefix += "│ " + } else { + newPrefix += " " + } + outputfordebug(node.children[1], newPrefix, false, str) + } + *str += prefix + if isTail { + *str += "└── " + } else { + *str += "┌── " + } + + suffix := "(" + parentv := "" + if node.parent == nil { + parentv = "nil" + } else { + parentv = spew.Sprint(node.parent.value) + } + suffix += parentv + "-" + spew.Sprint(node.child) + "|" + spew.Sprint(node.height) + ")" + *str += spew.Sprint(node.value) + suffix + "\n" + + if node.children[0] != nil { + newPrefix := prefix + if isTail { + newPrefix += " " + } else { + newPrefix += "│ " + } + outputfordebug(node.children[0], newPrefix, true, str) + } +} diff --git a/avltree/avltree_test.go b/avltree/avltree_test.go new file mode 100644 index 0000000..8619d42 --- /dev/null +++ b/avltree/avltree_test.go @@ -0,0 +1,316 @@ +package bstree + +import ( + "testing" + + "github.com/emirpasic/gods/trees/redblacktree" + + "github.com/emirpasic/gods/trees/avltree" + + "github.com/davecgh/go-spew/spew" + + "github.com/Pallinder/go-randomdata" + + "github.com/emirpasic/gods/utils" +) + +func TestRotate(t *testing.T) { + avl := New(utils.IntComparator) + + content := "" + for i := 0; i < 10; i++ { + v := randomdata.Number(0, 1000) + content += spew.Sprint(v) + " " + avl.Put(v) + } + + t.Error(content) + src := avl.String() + t.Error(src) + + lsrc := avl.String() + t.Error(lsrc) + // rrotate(&avl.root) + rsrc := avl.String() + t.Error(rsrc) + + if src == rsrc { + t.Error("src == rsrc") + } +} + +func TestPutStable(t *testing.T) { + avl := New(utils.IntComparator) + for _, v := range []int{7, 14, 15, 20, 30, 21} { + t.Error(v) + avl.Put(v) + t.Error(avl.debugString()) + } + // avl = New(utils.IntComparator) + // for _, v := range []int{88, 77, 80} { + // avl.Put(v) + // t.Error(avl.String()) + // } +} + +func TestDiffPutRandom(t *testing.T) { + avl := New(utils.IntComparator) + godsavl := avltree.NewWithIntComparator() + + content := "" + m := make(map[int]int) + for i := 0; len(m) < 10; i++ { + v := randomdata.Number(0, 10000) + if _, ok := m[v]; !ok { + m[v] = v + content += spew.Sprint(v) + " " + t.Error(v) + avl.Put(v) + t.Error(avl.String()) + godsavl.Put(v, v) + } + } + t.Error(godsavl.String()) +} + +func TestPutComparatorRandom(t *testing.T) { + + for n := 0; n < 1000000; n++ { + avl := New(utils.IntComparator) + godsavl := avltree.NewWithIntComparator() + + content := "" + m := make(map[int]int) + for i := 0; len(m) < 10; i++ { + v := randomdata.Number(0, 65535) + if _, ok := m[v]; !ok { + m[v] = v + content += spew.Sprint(v) + " " + // t.Error(v) + avl.Put(v) + // t.Error(avl.String()) + godsavl.Put(v, v) + } + } + + if avl.String() != godsavl.String() { + t.Error(content) + break + } + } + + // t.Error(content) + // t.Error(avl.String()) + // t.Error(godsavl.String()) + // t.Error(avl.String() == godsavl.String()) + +} + +func TestGet(t *testing.T) { + avl := New(utils.IntComparator) + for i := 0; i < 15; i++ { + avl.Put(randomdata.Number(0, 1000)) + } + t.Error(avl.String()) + t.Error(avl.Get(500)) +} + +func TestRemoveAll(t *testing.T) { + + for c := 0; c < 10000; c++ { + // f, _ := os.OpenFile("./out.log", os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0666) + // log.SetOutput(f) + + avl := New(utils.IntComparator) + var l []int + for i := 0; i < 100; i++ { + v := randomdata.Number(0, 100000) + l = append(l, v) + avl.Put(v) + } + // defer func() { + // if err := recover(); err != nil { + // panic(avl.String()) + // } + // }() + // log.Println(avl.TraversalBreadth()) + for i := 0; i < 100; i++ { + // log.Println(l[i]) + // log.Println(avl.debugString()) + avl.Remove(l[i]) + } + } +} + +func TestRemove(t *testing.T) { + + // avl := New(utils.IntComparator) + + // var l []int + // for _, v := range []int{86, 97, 9, 61, 37, 45, 97, 43, 95, 8} { + // l = append(l, v) + // avl.Put(v) + // } + + // for i := 0; i < len(l); i++ { + // // log.Println(i) + // log.Println("begin", l[i], avl.debugString()) + // avl.Remove(l[i]) + // log.Println("end", l[i], avl.debugString()) + // } + +ALL: + for N := 0; N < 500000; N++ { + avl := New(utils.IntComparator) + gods := avltree.NewWithIntComparator() + + var l []int + m := make(map[int]int) + + for i := 0; len(l) < 10; i++ { + v := randomdata.Number(0, 100) + if _, ok := m[v]; !ok { + l = append(l, v) + m[v] = v + avl.Put(v) + gods.Put(v, v) + } + } + + src1 := avl.String() + src2 := gods.String() + + for i := 0; i < 10; i++ { + avl.Remove(l[i]) + gods.Remove(l[i]) + if spew.Sprint(gods.Values()) != spew.Sprint(avl.TraversalDepth(-1)) && avl.size != 0 { + // if gods.String() != avl.String() && gods.Size() != 0 && avl.size != 0 { + t.Error(src1) + t.Error(src2) + t.Error(avl.debugString()) + t.Error(gods.String()) + t.Error(l[i]) + // t.Error(avl.TraversalDepth(-1)) + // t.Error(gods.Values()) + break ALL + + } + } + } +} + +const PutCompartorSize = 300000 +const NumberMax = 60000000 + +func BenchmarkRemove(b *testing.B) { + + avl := New(utils.IntComparator) + b.N = PutCompartorSize + + var l []int + for i := 0; i < b.N; i++ { + v := randomdata.Number(0, NumberMax) + l = append(l, v) + avl.Put(v) + } + + b.ResetTimer() + b.StartTimer() + + for i := 0; i < b.N; i++ { + avl.Remove(l[i]) + } +} + +func BenchmarkGodsRemove(b *testing.B) { + avl := avltree.NewWithIntComparator() + b.N = PutCompartorSize + + var l []int + for i := 0; i < b.N; i++ { + v := randomdata.Number(0, NumberMax) + l = append(l, v) + avl.Put(v, v) + } + + b.ResetTimer() + b.StartTimer() + + for i := 0; i < b.N; i++ { + avl.Remove(l[i]) + } +} + +func BenchmarkGet(b *testing.B) { + avl := New(utils.IntComparator) + + b.N = PutCompartorSize + for i := 0; i < b.N/2; i++ { + avl.Put(randomdata.Number(0, NumberMax)) + } + + b.ResetTimer() + b.StartTimer() + for i := 0; i < b.N; i++ { + avl.Get(randomdata.Number(0, NumberMax)) + } +} + +func BenchmarkGodsRBGet(b *testing.B) { + rb := redblacktree.NewWithIntComparator() + + b.N = PutCompartorSize + for i := 0; i < b.N/2; i++ { + rb.Put(randomdata.Number(0, NumberMax), i) + } + + b.ResetTimer() + b.StartTimer() + for i := 0; i < b.N; i++ { + rb.Get(randomdata.Number(0, NumberMax)) + } +} + +func BenchmarkGodsAvlGet(b *testing.B) { + rb := avltree.NewWithIntComparator() + + b.N = PutCompartorSize + for i := 0; i < b.N/2; i++ { + rb.Put(randomdata.Number(0, NumberMax), i) + } + + b.ResetTimer() + b.StartTimer() + for i := 0; i < b.N; i++ { + rb.Get(randomdata.Number(0, NumberMax)) + } +} + +func BenchmarkPut(b *testing.B) { + avl := New(utils.IntComparator) + + b.N = PutCompartorSize + for i := 0; i < b.N; i++ { + avl.Put(randomdata.Number(0, NumberMax)) + } + +} + +func BenchmarkGodsRBPut(b *testing.B) { + rb := redblacktree.NewWithIntComparator() + + b.N = PutCompartorSize + for i := 0; i < b.N; i++ { + rb.Put(randomdata.Number(0, NumberMax), i) + } + +} + +func BenchmarkGodsPut(b *testing.B) { + avl := avltree.NewWithIntComparator() + + b.N = PutCompartorSize + for i := 0; i < b.N; i++ { + avl.Put(randomdata.Number(0, NumberMax), i) + } +} diff --git a/priority_list/iterator.go b/priority_list/iterator.go new file mode 100644 index 0000000..cc1738d --- /dev/null +++ b/priority_list/iterator.go @@ -0,0 +1,87 @@ +package plist + +type Iterator struct { + pl *PriorityList + cur *Node +} + +func (iter *Iterator) Value() interface{} { + return iter.cur.value +} + +func (iter *Iterator) Prev() bool { + if iter.cur == iter.pl.head { + return false + } + iter.cur = iter.cur.prev + return iter.cur != iter.pl.head +} + +func (iter *Iterator) Next() bool { + if iter.cur == iter.pl.tail { + return false + } + iter.cur = iter.cur.next + return iter.cur != iter.pl.tail +} + +func (iter *Iterator) MoveToHead() { + iter.cur = iter.pl.head +} + +func (iter *Iterator) MoveToTail() { + iter.cur = iter.pl.tail +} + +type CircularIterator struct { + pl *PriorityList + 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/priority_list/priority_list.go b/priority_list/priority_list.go new file mode 100644 index 0000000..9a1c17a --- /dev/null +++ b/priority_list/priority_list.go @@ -0,0 +1,138 @@ +package plist + +import ( + "strings" + + "github.com/davecgh/go-spew/spew" + "github.com/emirpasic/gods/utils" +) + +type Node struct { + prev, next *Node + value interface{} +} + +type PriorityList struct { + head, tail *Node + size int + comparator utils.Comparator +} + +func New(compartor utils.Comparator) *PriorityList { + pl := &PriorityList{head: &Node{}, tail: &Node{}, size: 0, comparator: compartor} + pl.head.next = pl.tail + pl.tail.prev = pl.head + return pl +} + +func (pl *PriorityList) String() string { + content := "" + + cur := pl.head.next + + for ; cur != pl.tail; cur = cur.next { + content += spew.Sprint(cur.value) + " " + } + content = strings.TrimRight(content, " ") + return content +} + +func (pl *PriorityList) RString() string { + content := "" + + cur := pl.tail.prev + + for ; cur != pl.head; cur = cur.prev { + content += spew.Sprint(cur.value) + " " + } + content = strings.TrimRight(content, " ") + return content +} + +func (pl *PriorityList) Iterator() *Iterator { + return &Iterator{pl: pl, cur: pl.head} +} + +func (pl *PriorityList) CircularIterator() *CircularIterator { + return &CircularIterator{pl: pl, cur: pl.head} +} + +func (pl *PriorityList) Push(pvalue interface{}) { + pl.size++ + pnode := &Node{value: pvalue} + if pl.size == 1 { + pl.head.next = pnode + pl.tail.prev = pnode + pnode.prev = pl.head + pnode.next = pl.tail + return + } + + cur := pl.head + for ; cur.next != pl.tail; cur = cur.next { + if pl.comparator(pvalue, cur.next.value) > 0 { + cnext := cur.next + + cur.next = pnode + cnext.prev = pnode + pnode.prev = cur + pnode.next = cnext + + return + } + } + + cur.next = pnode + pnode.prev = cur + pnode.next = pl.tail + pl.tail.prev = pnode +} + +func (pl *PriorityList) Get(idx int) interface{} { + return pl.GetNode(idx).value +} + +func (pl *PriorityList) GetNode(idx int) *Node { + if idx >= 0 { + cur := pl.head.next + for i := 0; cur != pl.tail; i++ { + if i == idx { + return cur + } + cur = cur.next + } + } else { + cur := pl.tail.prev + for i := -1; cur != pl.head; i-- { + if i == idx { + return cur + } + cur = cur.prev + } + } + return nil +} + +func (pl *PriorityList) RemoveWithIndex(idx int) { + pl.Remove(pl.GetNode(idx)) +} + +func (pl *PriorityList) Remove(node *Node) { + + prev := node.prev + next := node.next + prev.next = next + next.prev = prev + + node.prev = nil + node.next = nil + + pl.size-- +} +func (pl *PriorityList) Values() []interface{} { + values := make([]interface{}, pl.size, pl.size) + for i, cur := 0, pl.head.next; cur != pl.tail; i, cur = i+1, cur.next { + values[i] = cur.value + } + return values +} diff --git a/priority_list/priority_list_test.go b/priority_list/priority_list_test.go new file mode 100644 index 0000000..469533e --- /dev/null +++ b/priority_list/priority_list_test.go @@ -0,0 +1,148 @@ +package plist + +import ( + "testing" + + "github.com/emirpasic/gods/utils" +) + +func TestInsert(t *testing.T) { + pl := New(utils.IntComparator) + for i := 0; i < 10; i++ { + pl.Push(i) + } + + if pl.size != 10 { + t.Error(pl.size) + } + + if pl.String() != "9 8 7 6 5 4 3 2 1 0" { + t.Error(pl.String()) + } + + if pl.RString() != "0 1 2 3 4 5 6 7 8 9" { + t.Error(pl.RString()) + } + + for i := 0; i < 10; i++ { + pl.Push(i) + } + + if pl.String() != "9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0" { + t.Error(pl.String()) + } + + if pl.RString() != "0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9" { + t.Error(pl.RString()) + } +} + +func TestIterator(t *testing.T) { + pl := New(utils.IntComparator) + for i := 0; i < 10; i++ { + pl.Push(i) + } + + iter := pl.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.pl.tail { + t.Error("current point is not equal tail ", iter.pl.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) { + pl := New(utils.IntComparator) + for i := 0; i < 10; i++ { + pl.Push(i) + } + + iter := pl.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 TestGet(t *testing.T) { + pl := New(utils.IntComparator) + for i := 0; i < 10; i++ { + pl.Push(i) + } + + for _, v := range []int{0, 9, 5, 7} { + if pl.Get(v) != (9 - v) { + t.Error(v, "Get == ", pl.Get(v)) + } + } + + func() { + defer func() { + if err := recover(); err == nil { + t.Error("out index, but is not error") + } + }() + + pl.Get(10) + }() +} + +func TestRemove(t *testing.T) { + pl := New(utils.IntComparator) + for i := 0; i < 10; i++ { + pl.Push(i) + } + + pl.RemoveWithIndex(0) + if pl.Get(0).(int) != 8 { + t.Error(pl.Get(0)) + } + + pl.RemoveWithIndex(-1) + if pl.Get(-1).(int) != 1 { + t.Error(pl.Get(-1)) + } + +} diff --git a/priority_queue/iterator.go b/priority_queue/iterator.go new file mode 100644 index 0000000..a281dbd --- /dev/null +++ b/priority_queue/iterator.go @@ -0,0 +1,4 @@ +package pqueue + +type Iterator struct { +} diff --git a/priority_queue/priority_queue.go b/priority_queue/priority_queue.go new file mode 100644 index 0000000..a490fd8 --- /dev/null +++ b/priority_queue/priority_queue.go @@ -0,0 +1,4 @@ +package pqueue + +type PriorityQueue struct { +}