У меня есть следующая структура:
MyClass {
guid ID
guid ParentID
string Name
}
Я хотел бы создать массив, который содержит элементы в порядке, они должны быть отображены в иерархии (например, согласно их "левым" значениям), а также хеш, который отображает гуид на уровень отступа.
Например:
ID Name ParentID
------------------------
1 Cats 2
2 Animal NULL
3 Tiger 1
4 Book NULL
5 Airplane NULL
Это по существу произвело бы следующие объекты:
// Array is an array of all the elements sorted by the way you would see them in a fully expanded tree
Array[0] = "Airplane"
Array[1] = "Animal"
Array[2] = "Cats"
Array[3] = "Tiger"
Array[4] = "Book"
// IndentationLevel is a hash of GUIDs to IndentationLevels.
IndentationLevel["1"] = 1
IndentationLevel["2"] = 0
IndentationLevel["3"] = 2
IndentationLevel["4"] = 0
IndentationLevel["5"] = 0
Для ясности это - то, на что похожа иерархия:
Airplane
Animal
Cats
Tiger
Book
Я хотел бы выполнить итерации через объекты наименьшего количества суммы возможных времен. Я также не хочу создавать иерархическую структуру данных. Я предпочел бы использовать массивы, хеши, стеки или очереди.
Эти две цели:
Когда я получаю список элементов, они без определенного порядка. Одноуровневые элементы должны быть заказаны их свойством Name.
Обновление: Это может казаться, что я не попытался предложить решение сам и просто хочу, чтобы другие сделали работу для меня. Однако я попытался предложить три различных решения, и я застрял на каждом. Одна причина могла бы состоять в том, что я старался избегать рекурсии (возможно, неправильно так). Я не отправляю частичные решения, которые я имею до сих пор, так как они являются неправильными и могут плохо влиять на решения других.
Сообщение Вонсунги очень помогло, но это скорее для общего графа, чем для дерева. Поэтому я немного изменил его, чтобы создать алгоритм, разработанный специально для дерева:
// Data strcutures:
nodeChildren: Dictionary['nodeID'] = List<Children>;
indentLevel: Dictionary['nodeID'] = Integer;
roots: Array of nodes;
sorted: Array of nodes;
nodes: all nodes
// Step #1: Prepare the data structures for building the tree
for each node in nodes
if node.parentID == NULL
roots.Append(node);
indentLevel[node] = 0;
else
nodeChildren[node.parentID].append(node);
// Step #2: Add elements to the sorted list
roots.SortByABC();
while roots.IsNotEmpty()
root = roots.Remove(0);
rootIndentLevel = indentLevel[root];
sorted.Append(root);
children = nodeChildren[root];
children.SortByABC();
for each child in children (loop backwards)
indentLevel[child] = rootIndentLevel + 1
roots.Prepend(child)
Для иерархических структур вам почти наверняка понадобится рекурсия (если вы допускаете произвольную глубину). Я быстро набросал немного кода на руби, чтобы проиллюстрировать, как вы можете достичь этого (хотя я не сделал отступы):
# setup the data structure
class S < Struct.new(:id, :name, :parent_id);end
class HierarchySorter
def initialize(il)
@initial_list = il
first_level = @initial_list.select{|a| a.parent_id == nil}.sort_by{|a| a.name }
@final_array = subsort(first_level, 0)
end
#recursive function
def subsort(list, indent_level)
result = []
list.each do |item|
result << [item, indent_level]
result += subsort(@initial_list.select{|a| a.parent_id == item.id}.sort_by{|a| a.name }, indent_level + 1)
end
result
end
def sorted_array
@final_array.map &:first
end
def indent_hash
# magick to transform array of structs into hash
Hash[*@final_array.map{|a| [a.first.id, a.last]}.flatten]
end
end
hs = HierarchySorter.new [S.new(1, "Cats", 2), S.new(2, "Animal", nil), S.new(3, "Tiger", 1), S.new(4, "Book", nil),
S.new(5, "Airplane", nil)]
puts "Array:"
puts hs.sorted_array.inspect
puts "\nIndentation hash:"
puts hs.indent_hash.inspect
Если вы не знаете руби, я могу переделать его на что-нибудь другое.
Edit: Я обновил код выше, чтобы вывести обе структуры данных.
Outputs:
Array:
[#<struct S id=5, name="Airplane", parent_id=nil>, #<struct S id=2, name="Animal", parent_id=nil>, #<struct S id=1, name="Cats", parent_id=2>, #<struct S id=3, name="Tiger", parent_id=1>, #<struct S id=4, name="Book", parent_id=nil>]
Indentation hash:
{5=>0, 1=>1, 2=>0, 3=>2, 4=>0}
Мне нужен был похожий алгоритм для сортировки задач по зависимостям (каждая задача могла иметь родительскую задачу, которую нужно было выполнить в первую очередь). Я нашел топологическую сортировку . Вот итеративная реализация на Python с очень подробными комментариями.
Уровень отступа может быть вычислен при топологической сортировке. Просто установите уровень отступа узла равным уровню отступа + 1 его родительского узла, поскольку он добавляется к топологическому порядку.
Обратите внимание, что может существовать множество допустимых топологических порядков. Чтобы гарантировать, что результирующий топологический порядок группирует родительские узлы с дочерними узлами, выберите алгоритм топологической сортировки на основе обхода в глубину графа, созданного с помощью информации частичного упорядочения.
Википедия дает еще два алгоритма топологической сортировки . Обратите внимание, что эти алгоритмы не так хороши, потому что первый - обход в ширину, а второй - рекурсивный.