diff --git a/cut_the_tree/dll/dll.go b/cut_the_tree/dll/dll.go new file mode 100644 index 0000000..af4deb5 --- /dev/null +++ b/cut_the_tree/dll/dll.go @@ -0,0 +1,101 @@ +package dll + +type ListInterface interface { + Len() int // длина списка + Front() *ListItem // первый Item + Back() *ListItem // последний Item + PushFront(v interface{}) *ListItem // добавить значение в начало + PushBack(v interface{}) *ListItem // добавить значение в конец + Remove(i *ListItem) // удалить элемент + MoveToFront(i *ListItem) // переместить элемент в начало +} + +type ListItem struct { + Value interface{} // значение + Next *ListItem // следующий элемент + Prev *ListItem // предыдущий элемент +} + +type List struct { + Info ListItem + len int +} + +func NewList() *List { + return &List{len: 0} +} + +func (l *List) Len() int { + return l.len +} + +func (l *List) Front() *ListItem { + return l.Info.Next +} + +func (l *List) Back() *ListItem { + return l.Info.Prev +} + +func (l *List) PushFront(v interface{}) *ListItem { + e := &ListItem{Value: v} + if l.len != 0 { + e.Prev = l.Info.Next + l.Info.Next.Next = e + } else { + l.Info.Prev = e + } + l.Info.Next = e + l.len++ + return e +} + +func (l *List) PushBack(v interface{}) *ListItem { + e := &ListItem{Value: v} + if l.len != 0 { + e.Next = l.Info.Prev + l.Info.Prev.Prev = e + } else { + l.Info.Next = e + } + l.Info.Prev = e + l.len++ + return e +} + +func (l *List) Remove(i *ListItem) { + if i.Prev == nil { + i.Prev = &ListItem{} + l.Info.Prev = i.Next + } + if i.Next == nil { + i.Next = &ListItem{} + l.Info.Next = i.Prev + } + i.Prev.Next = i.Next + i.Next.Prev = i.Prev + if l.Len() > 1 { + l.Info.Next.Next = nil + l.Info.Prev.Prev = nil + } else { + l.Info = ListItem{} + } + l.len-- +} + +func (l *List) MoveToFront(i *ListItem) { + if l.Info.Next == i { + return + } + if i.Prev != nil { + i.Prev.Next = i.Next + i.Next.Prev = i.Prev + } else { + i.Next.Prev = i.Prev + l.Info.Prev = i.Next + } + i.Prev = l.Front() + l.Front().Next = i + i.Next = nil + l.Info.Next = i +} diff --git a/cut_the_tree/dll/dll_test.go b/cut_the_tree/dll/dll_test.go new file mode 100644 index 0000000..a282d86 --- /dev/null +++ b/cut_the_tree/dll/dll_test.go @@ -0,0 +1,96 @@ +package dll + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestList(t *testing.T) { + t.Run("empty list", func(t *testing.T) { + l := NewList() + + require.Equal(t, l.Len(), 0) + require.Nil(t, l.Front()) + require.Nil(t, l.Back()) + }) + + t.Run("complex", func(t *testing.T) { + l := NewList() + + l.PushFront(10) + require.Equal(t, []int{10}, transpond(l)) + require.Equal(t, 1, l.Len()) + + l.Remove(l.Front()) + require.Equal(t, []int{}, transpond(l)) + require.Equal(t, 0, l.Len()) + + l.PushFront(10) + require.Equal(t, []int{10}, transpond(l)) + require.Equal(t, 1, l.Len()) + + l.PushBack(20) + require.Equal(t, []int{20, 10}, transpond(l)) + require.Equal(t, 2, l.Len()) + + l.PushBack(30) + require.Equal(t, []int{30, 20, 10}, transpond(l)) + require.Equal(t, 3, l.Len()) + + middle := l.Back().Next // 20 + l.Remove(middle) + require.Equal(t, []int{30, 10}, transpond(l)) + require.Equal(t, 2, l.Len()) + + l.PushFront(20) + require.Equal(t, []int{30, 10, 20}, transpond(l)) + require.Equal(t, 3, l.Len()) + + l.Remove(l.Back()) + require.Equal(t, []int{10, 20}, transpond(l)) + require.Equal(t, 2, l.Len()) + + l.PushBack(30) + require.Equal(t, []int{30, 10, 20}, transpond(l)) + require.Equal(t, 3, l.Len()) + + l.Remove(l.Front()) + require.Equal(t, []int{30, 10}, transpond(l)) + require.Equal(t, 2, l.Len()) + + l.PushFront(20) + require.Equal(t, []int{30, 10, 20}, transpond(l)) + require.Equal(t, 3, l.Len()) + + for i, v := range [...]int{40, 50, 60, 70, 80} { + if i%2 == 0 { + l.PushFront(v) + } else { + l.PushBack(v) + } + } + require.Equal(t, []int{70, 50, 30, 10, 20, 40, 60, 80}, transpond(l)) + require.Equal(t, 8, l.Len()) + + l.MoveToFront(l.Front()) + require.Equal(t, []int{70, 50, 30, 10, 20, 40, 60, 80}, transpond(l)) + require.Equal(t, 8, l.Len()) + + l.MoveToFront(l.Back().Next.Next) // 30 + require.Equal(t, []int{70, 50, 10, 20, 40, 60, 80, 30}, transpond(l)) + require.Equal(t, 8, l.Len()) + + l.MoveToFront(l.Back()) + require.Equal(t, []int{50, 10, 20, 40, 60, 80, 30, 70}, transpond(l)) + require.Equal(t, 8, l.Len()) + }) +} + +func transpond(l *List) []int { + elems := make([]int, 0, l.Len()) + for i := l.Back(); i != nil; i = i.Next { + elems = append(elems, i.Value.(int)) + } + return elems +} diff --git a/cut_the_tree/funcs.go b/cut_the_tree/funcs.go index 84a9840..bfe3fb4 100644 --- a/cut_the_tree/funcs.go +++ b/cut_the_tree/funcs.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "maps" + "tests/cut_the_tree/dll" ) type Node struct { @@ -27,7 +28,7 @@ func (root Node) WeighingTreeWithRecursion() map[string]uint16 { return res } -func (root Node) WeighingTreeIdiomaticBFS() map[string]uint16 { +func (root Node) WeighingTreeWithStack() map[string]uint16 { weight := getNodeWeight(root.ID, root.Name) counter := map[string]*WeightedNode{root.ID: { weight: weight, @@ -66,8 +67,45 @@ func (root Node) WeighingTreeIdiomaticBFS() map[string]uint16 { return res } -func (root Node) WeighingTreeIdiomaticDFS() map[string]uint16 { - return nil +func (root Node) WeightingTreeWithDLL() map[string]uint16 { + weight := getNodeWeight(root.ID, root.Name) + counter := map[string]*WeightedNode{root.ID: { + weight: weight, + }} + dllist := dll.NewList() + dllist.PushFront(root) + + for dllist.Len() > 0 { + current := dllist.Front() + dllist.Remove(current) + + nodeWeight := getNodeWeight(current.Value.(Node).ID, current.Value.(Node).Name) + counter[current.Value.(Node).ID].weight = nodeWeight + // Прибавляем вес всем родительским нодам до корня + parentID := counter[current.Value.(Node).ID].parent + for { + currentNode := counter[parentID] + if parentID == "" { + break + } + currentNode.weight = currentNode.weight + nodeWeight + parentID = currentNode.parent + } + + for _, child := range current.Value.(Node).Children { + counter[child.ID] = &WeightedNode{ + parent: current.Value.(Node).ID, + } + dllist.PushFront(*child) + } + } + + var res = make(map[string]uint16) + for k, v := range counter { + res[k] = v.weight + } + + return res } func (root Node) DecomposeTree(weights map[string]uint16, limit int) ([]*Node, error) { diff --git a/cut_the_tree/funcs_bench_test.go b/cut_the_tree/funcs_bench_test.go index 3825d05..11c2ee3 100644 --- a/cut_the_tree/funcs_bench_test.go +++ b/cut_the_tree/funcs_bench_test.go @@ -6,19 +6,20 @@ import ( "github.com/stretchr/testify/assert" ) -func BenchmarkNode_WeighingTreeWithRecursion(b *testing.B) { +func BenchmarkNode_WeighingSmallOrdinaryTreeWithRecursion(b *testing.B) { for i := 0; i < b.N; i++ { - output := map[string]uint16{"node0":1416, "node01":194, "node011":49, "node012":49, "node013":49, "node02":194, "node021":49, "node022":49, "node023":49, "node03":983,"node031":361, "node0311":210, "node03111":53, "node03112":53, "node03113":53, "node0312":51, "node0313":51, "node032":49, "node033":526, "node0331":51, "node0332":375, "node03321":53, "node03322":53, "node03323":218, "node033231":55, "node033232":55, "node033233":55, "node0333":51} - - assert.EqualValues(b, output, testOrdinaryVFS.WeighingTreeWithRecursion()) + assert.EqualValues(b, testOrdinaryVFS.Weghts, testOrdinaryVFS.Tree.WeighingTreeWithRecursion()) } } - -func BenchmarkNode_WeighingTreeIdiomaticBFS(b *testing.B) { +func BenchmarkNode_WeighingSmallOrdinaryTreeWithStack(b *testing.B) { for i := 0; i < b.N; i++ { - output := map[string]uint16{"node0":1416, "node01":194, "node011":49, "node012":49, "node013":49, "node02":194, "node021":49, "node022":49, "node023":49, "node03":983,"node031":361, "node0311":210, "node03111":53, "node03112":53, "node03113":53, "node0312":51, "node0313":51, "node032":49, "node033":526, "node0331":51, "node0332":375, "node03321":53, "node03322":53, "node03323":218, "node033231":55, "node033232":55, "node033233":55, "node0333":51} - - assert.EqualValues(b, output, testOrdinaryVFS.WeighingTreeIdiomaticBFS()) + assert.EqualValues(b, testOrdinaryVFS.Weghts, testOrdinaryVFS.Tree.WeighingTreeWithStack()) + } +} + +func BenchmarkNode_WeighingSmallOrdinaryTreeWithDLL(b *testing.B) { + for i := 0; i < b.N; i++ { + assert.EqualValues(b, testOrdinaryVFS.Weghts, testOrdinaryVFS.Tree.WeightingTreeWithDLL()) } } diff --git a/cut_the_tree/funcs_test.go b/cut_the_tree/funcs_test.go index 2fd83e1..22f0d4a 100644 --- a/cut_the_tree/funcs_test.go +++ b/cut_the_tree/funcs_test.go @@ -12,18 +12,18 @@ func TestNode_WeighingTreeAllAlgo(t *testing.T) { output map[string]uint16 }{ "Ordinary VFS tree":{ - input:testOrdinaryVFS, - output: map[string]uint16{"node0":1416, "node01":194, "node011":49, "node012":49, "node013":49, "node02":194, "node021":49, "node022":49, "node023":49, "node03":983,"node031":361, "node0311":210, "node03111":53, "node03112":53, "node03113":53, "node0312":51, "node0313":51, "node032":49, "node033":526, "node0331":51, "node0332":375, "node03321":53, "node03322":53, "node03323":218, "node033231":55, "node033232":55, "node033233":55, "node0333":51}, + input:testOrdinaryVFS.Tree, + output: testOrdinaryVFS.Weghts, }, "VFS in the form of a pathological tree":{ - input: testPathologicalTree, - output: map[string]uint16{"node0":605, "node01":560, "node012":513, "node0123":464, "node01234":413, "node012345":360, "node0123456":305, "node01234567":248, "node012345678":189, "node0123456789":128, "node0123456789A":65}, + input: testPathologicalTree.Tree, + output: testPathologicalTree.Weghts, }, }{ t.Run(name,func(t *testing.T) { assert.EqualValues(t, tt.output, tt.input.WeighingTreeWithRecursion()) - assert.EqualValues(t, tt.output, tt.input.WeighingTreeIdiomaticBFS()) - //assert.EqualValues(t, tt.output, tt.input.WeighingTreeIdiomaticDFS()) + assert.EqualValues(t, tt.output, tt.input.WeighingTreeWithStack()) + assert.EqualValues(t, tt.output, tt.input.WeightingTreeWithDLL()) }) } } diff --git a/cut_the_tree/test_models.go b/cut_the_tree/test_models.go index 283e39b..44b705c 100644 --- a/cut_the_tree/test_models.go +++ b/cut_the_tree/test_models.go @@ -1,198 +1,208 @@ package main -var testOrdinaryVFS = Node{ - ID: "node0", - Name: "node0", - Children: []*Node{ - { - ID: "node01", - Name: "node01", - Children: []*Node{ - { - ID: "node011", - Name: "node011", - Children: nil, - }, - { - ID: "node012", - Name: "node012", - Children: nil, - }, - { - ID: "node013", - Name: "node013", - Children: nil, - }, - }, - }, - { - ID: "node02", - Name: "node02", - Children: []*Node{ - { - ID: "node021", - Name: "node021", - Children: nil, - }, - { - ID: "node022", - Name: "node022", - Children: nil, - }, - { - ID: "node023", - Name: "node023", - Children: nil, - }, - }, - }, - { - ID: "node03", - Name: "node03", - Children: []*Node{ - { - ID: "node031", - Name: "node031", - Children: []*Node{ - { - ID: "node0311", - Name: "node0311", - Children: []*Node{ - { - ID: "node03111", - Name: "node03111", - Children: nil, - }, - { - ID: "node03112", - Name: "node03112", - Children: nil, - }, - { - ID: "node03113", - Name: "node03113", - Children: nil, - }, - }, - }, - { - ID: "node0312", - Name: "node0312", - Children: nil, - }, - { - ID: "node0313", - Name: "node0313", - Children: nil, - }, +type TestTree struct { + Tree Node + Weghts map[string]uint16 +} + +var testOrdinaryVFS = TestTree{ + Tree: Node{ + ID: "node0", + Name: "node0", + Children: []*Node{ + { + ID: "node01", + Name: "node01", + Children: []*Node{ + { + ID: "node011", + Name: "node011", + Children: nil, + }, + { + ID: "node012", + Name: "node012", + Children: nil, + }, + { + ID: "node013", + Name: "node013", + Children: nil, }, }, - { - ID: "node032", - Name: "node032", - Children: nil, + }, + { + ID: "node02", + Name: "node02", + Children: []*Node{ + { + ID: "node021", + Name: "node021", + Children: nil, + }, + { + ID: "node022", + Name: "node022", + Children: nil, + }, + { + ID: "node023", + Name: "node023", + Children: nil, + }, }, - { - ID: "node033", - Name: "node033", - Children: []*Node{ - { - ID: "node0331", - Name: "node0331", - Children: nil, + }, + { + ID: "node03", + Name: "node03", + Children: []*Node{ + { + ID: "node031", + Name: "node031", + Children: []*Node{ + { + ID: "node0311", + Name: "node0311", + Children: []*Node{ + { + ID: "node03111", + Name: "node03111", + Children: nil, + }, + { + ID: "node03112", + Name: "node03112", + Children: nil, + }, + { + ID: "node03113", + Name: "node03113", + Children: nil, + }, + }, + }, + { + ID: "node0312", + Name: "node0312", + Children: nil, + }, + { + ID: "node0313", + Name: "node0313", + Children: nil, + }, }, - { - ID: "node0332", - Name: "node0332", - Children: []*Node{ - { - ID: "node03321", - Name: "node03321", - Children: nil, - }, - { - ID: "node03322", - Name: "node03322", - Children: nil, - }, - { - ID: "node03323", - Name: "node03323", - Children: []*Node{ - { - ID: "node033231", - Name: "node033231", - Children: nil, - }, - { - ID: "node033232", - Name: "node033232", - Children: nil, - }, - { - ID: "node033233", - Name: "node033233", - Children: nil, + }, + { + ID: "node032", + Name: "node032", + Children: nil, + }, + { + ID: "node033", + Name: "node033", + Children: []*Node{ + { + ID: "node0331", + Name: "node0331", + Children: nil, + }, + { + ID: "node0332", + Name: "node0332", + Children: []*Node{ + { + ID: "node03321", + Name: "node03321", + Children: nil, + }, + { + ID: "node03322", + Name: "node03322", + Children: nil, + }, + { + ID: "node03323", + Name: "node03323", + Children: []*Node{ + { + ID: "node033231", + Name: "node033231", + Children: nil, + }, + { + ID: "node033232", + Name: "node033232", + Children: nil, + }, + { + ID: "node033233", + Name: "node033233", + Children: nil, + }, }, }, }, }, - }, - { - ID: "node0333", - Name: "node0333", - Children: nil, + { + ID: "node0333", + Name: "node0333", + Children: nil, + }, }, }, }, }, }, }, + Weghts: map[string]uint16{"node0": 1416, "node01": 194, "node011": 49, "node012": 49, "node013": 49, "node02": 194, "node021": 49, "node022": 49, "node023": 49, "node03": 983, "node031": 361, "node0311": 210, "node03111": 53, "node03112": 53, "node03113": 53, "node0312": 51, "node0313": 51, "node032": 49, "node033": 526, "node0331": 51, "node0332": 375, "node03321": 53, "node03322": 53, "node03323": 218, "node033231": 55, "node033232": 55, "node033233": 55, "node0333": 51}, } -var testPathologicalTree = Node{ - ID: "node0", - Name: "node0", - Children: []*Node{ - { - ID: "node01", - Name: "node01", - Children: []*Node{ - { - ID: "node012", - Name: "node012", - Children: []*Node{ - { - ID: "node0123", - Name: "node0123", - Children: []*Node{ - { - ID: "node01234", - Name: "node01234", - Children: []*Node{ - { - ID: "node012345", - Name: "node012345", - Children: []*Node{ - { - ID: "node0123456", - Name: "node0123456", - Children: []*Node{ - { - ID: "node01234567", - Name: "node01234567", - Children: []*Node{ - { - ID: "node012345678", - Name: "node012345678", - Children: []*Node{ - { - ID: "node0123456789", - Name: "node0123456789", - Children: []*Node{ - { - ID: "node0123456789A", - Name: "node0123456789A", +var testPathologicalTree = TestTree{ + Tree: Node{ + ID: "node0", + Name: "node0", + Children: []*Node{ + { + ID: "node01", + Name: "node01", + Children: []*Node{ + { + ID: "node012", + Name: "node012", + Children: []*Node{ + { + ID: "node0123", + Name: "node0123", + Children: []*Node{ + { + ID: "node01234", + Name: "node01234", + Children: []*Node{ + { + ID: "node012345", + Name: "node012345", + Children: []*Node{ + { + ID: "node0123456", + Name: "node0123456", + Children: []*Node{ + { + ID: "node01234567", + Name: "node01234567", + Children: []*Node{ + { + ID: "node012345678", + Name: "node012345678", + Children: []*Node{ + { + ID: "node0123456789", + Name: "node0123456789", + Children: []*Node{ + { + ID: "node0123456789A", + Name: "node0123456789A", + }, }, }, }, @@ -213,4 +223,5 @@ var testPathologicalTree = Node{ }, }, }, + Weghts: map[string]uint16{"node0": 605, "node01": 560, "node012": 513, "node0123": 464, "node01234": 413, "node012345": 360, "node0123456": 305, "node01234567": 248, "node012345678": 189, "node0123456789": 128, "node0123456789A": 65}, }