tests/weighting_n_cutting_the_tree/cutting.go

261 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package main
import (
"github.com/pkg/errors"
"slices"
)
func DecomposeTree(tree map[string]*WeightedNode, threshold uint16) ([]SubTree, error) {
var res []SubTree
// Рабочий цикл
for len(tree) > 0 {
// Индексируем tree по weight и сортируем индекс
var index = make([]TreeIndex, len(tree))
i := 0
for k, v := range tree {
index[i] = TreeIndex{
id: k,
weight: v.weight,
}
i += 1
}
slices.SortFunc(index, func(a, b TreeIndex) int {
return int(b.weight) - int(a.weight)
})
// Ищем ветку подходящую под лимит
var branchID string
for _, v := range index {
if v.weight < threshold {
branchID = v.id
break
}
}
if branchID == "" {
return nil, errors.New("[GetNodePath]: threshold too small")
}
// Определяем путь к найденной ветке
nodeID := tree[branchID].node.ID
var path string
if _, ok := tree[nodeID]; !ok {
return nil, errors.New("[GetNodePath] node not exists")
}
if tree[nodeID].parent == nil {
path = "/"
} else {
nodeID = tree[nodeID].parent.ID
path = tree[nodeID].node.Name
for path != "/" {
if tree[nodeID].parent.Name == "/" {
path = "/" + path
break
}
nodeID = tree[nodeID].parent.ID
path = tree[nodeID].node.Name + "/" + path
}
}
// Вычитаем ветку из веса предков
currentID := branchID
if tree[currentID].parent != nil {
currentID = tree[currentID].parent.ID
for {
tree[currentID].weight -= tree[branchID].weight
if tree[currentID].parent == nil {
break
}
currentID = tree[currentID].parent.ID
}
}
// Вырезаем её из среза вместе со всеми потомками
// Удаляем ссылки на ноду из родительской ноды
if tree[branchID].parent != nil {
parent := tree[branchID].parent
m := slices.IndexFunc(parent.Children, func(node *Node) bool {
return node.ID == branchID
})
if m >= 0 {
parent.Children[m] = parent.Children[len(parent.Children)-1]
parent.Children = parent.Children[:len(parent.Children)-1]
//parent.Children = append(parent.Children[:m], parent.Children[m+1:]...)
}
}
node := tree[branchID].node
// Удаляем ветви вместе со всеми потомками
stack := []string{branchID}
for len(stack) > 0 {
current := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if _, ok := tree[current]; ok {
for _, v := range tree[current].children {
stack = append(stack, v.ID)
}
delete(tree, current)
}
}
// Добавляем найденную ветку к res
res = append(res, SubTree{
path: path,
tree: node,
})
}
return res, nil
}
type TreeIndex struct {
id string
weight uint16
}
// GetTreeIndex индексирует дерево по weight и сортирует индекс по уменьшению weight
func GetTreeIndex(tree map[string]*WeightedNode) []TreeIndex {
var index = make([]TreeIndex, len(tree))
i := 0
for k, v := range tree {
index[i] = TreeIndex{
id: k,
weight: v.weight,
}
i += 1
}
slices.SortFunc(index, func(a, b TreeIndex) int {
return int(b.weight) - int(a.weight)
})
return index
}
// GetBranch возвращает первую найденную ветвь дерева, суммарный вес которой меньше threshold
// Для работы необходим актуальный индекс мапы, отсортированный по убыванию
func GetBranch(tree map[string]*WeightedNode, index []TreeIndex, threshold uint16) (string, uint16) {
var branchID string
for _, v := range index {
if v.weight < threshold {
branchID = v.id
break
}
}
if branchID == "" {
return "", 0
}
return tree[branchID].node.ID, tree[branchID].weight
}
// GetNodePath возвращает полный путь ноды от корня
func GetNodePath(tree map[string]*WeightedNode, nodeID string) (string, error) {
if _, ok := tree[nodeID]; !ok {
return "", errors.New("node not exists")
}
if tree[nodeID].parent == nil {
return "/", nil
}
nodeID = tree[nodeID].parent.ID
path := tree[nodeID].node.Name
for path != "/" {
if tree[nodeID].parent.Name == "/" {
path = "/" + path
break
}
nodeID = tree[nodeID].parent.ID
path = tree[nodeID].node.Name + "/" + path
}
return path, nil
}
// RecalculateParents вычитает вес вырезанной ветки из всех родительских веток
func RecalculateParents(tree map[string]*WeightedNode, currentID string, nodeWeight uint16) {
if tree[currentID].parent == nil {
return
}
currentID = tree[currentID].parent.ID
for {
tree[currentID].weight -= nodeWeight
if tree[currentID].parent == nil {
break
}
currentID = tree[currentID].parent.ID
}
}
func GetNode(tree map[string]*WeightedNode, index []TreeIndex, threshold uint16) (string, string, error) {
// Для работы необходим актуальный индекс мапы, отсортированный по убыванию
var branchID string
for _, v := range index {
if v.weight < threshold {
branchID = v.id
break
}
}
if branchID == "" {
return "", "", errors.New("threshold too small")
}
currentID := tree[branchID].node.ID
nodeWeight := tree[branchID].weight
// GetNodePath возвращает полный путь ноды от корня
if _, ok := tree[currentID]; !ok {
return "", "", errors.New("node not exists")
}
if tree[currentID].parent == nil {
return tree[currentID].node.ID, "/", nil
}
currentID = tree[currentID].parent.ID
path := tree[currentID].node.Name
for {
if tree[currentID].parent.Name == "/" {
path = "/" + path
break
}
currentID = tree[currentID].parent.ID
path = tree[currentID].node.Name + "/" + path
}
// RecalculateParents вычитает вес вырезанной ветки из всех родительских веток
if tree[currentID].parent == nil {
return "", "", errors.New("it's the root node")
}
currentID = tree[currentID].parent.ID
for {
tree[currentID].weight -= nodeWeight
if tree[currentID].parent == nil {
break
}
currentID = tree[currentID].parent.ID
}
return tree[branchID].node.ID, path, nil
}
// CutBranch удаляет из дерева ветку со всеми потомками, а так же удаляет ссылку на себя из родительской ноды
func CutBranch(tree map[string]*WeightedNode, nodeID string) *Node {
// Удаление ссылки на ноду из родительской ноды
if tree[nodeID].parent != nil {
parent := tree[nodeID].parent
m := slices.IndexFunc(parent.Children, func(node *Node) bool {
return node.ID == nodeID
})
if m >= 0 {
parent.Children[m] = parent.Children[len(parent.Children)-1]
parent.Children = parent.Children[:len(parent.Children)-1]
//parent.Children = append(parent.Children[:m], parent.Children[m+1:]...)
}
}
node := tree[nodeID].node
// Удаление ветви вместе со всеми потомками
stack := []string{nodeID}
for len(stack) > 0 {
current := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if _, ok := tree[current]; ok {
for _, v := range tree[current].children {
stack = append(stack, v.ID)
}
delete(tree, current)
}
}
return node
}