From 9b947bf937859c7b8b331c59458c92bac2eef572 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=98=D0=B2=D0=B0?=
 =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= <andre.ivanov@vk.team>
Date: Thu, 12 Sep 2024 13:02:41 +0300
Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?=
 =?UTF-8?q?=D0=BE=D0=B1=D1=85=D0=BE=D0=B4=20=D0=B2=20=D0=B3=D0=BB=D1=83?=
 =?UTF-8?q?=D0=B1=D0=B8=D0=BD=D1=83=20=D1=81=20DLL=20=D0=B2=D0=BC=D0=B5?=
 =?UTF-8?q?=D1=81=D1=82=D0=BE=20=D1=81=D1=82=D0=B5=D0=BA=D0=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 cut_the_tree/dll/dll.go          | 101 +++++++++
 cut_the_tree/dll/dll_test.go     |  96 ++++++++
 cut_the_tree/funcs.go            |  44 +++-
 cut_the_tree/funcs_bench_test.go |  19 +-
 cut_the_tree/funcs_test.go       |  12 +-
 cut_the_tree/test_models.go      | 365 ++++++++++++++++---------------
 6 files changed, 442 insertions(+), 195 deletions(-)
 create mode 100644 cut_the_tree/dll/dll.go
 create mode 100644 cut_the_tree/dll/dll_test.go

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},
 }