用于将分层平面数据(w / ParentID)转换为具有缩进级别的排序平面列表的算法

时间:2010-05-20 00:11:13

标签: algorithm language-agnostic hierarchical-data

我有以下结构:

MyClass {
  guid ID
  guid ParentID
  string Name
}

我想创建一个数组,其中包含应按层次结构显示顺序的元素(例如根据其“左”值),以及将guid映射到缩进级别的哈希。

例如:

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

我想尽可能少地遍历这些项目。我也不想创建分层数据结构。我更喜欢使用数组,散列,堆栈或队列。

这两个目标是:

  1. 将ID的哈希值存储到缩进级别。
  2. 根据左侧值对所有对象进行排序。
  3. 当我得到元素列表时,它们没有特定的顺序。兄弟姐妹应该通过他们的姓名属性来订购。

    更新:看起来我似乎没有尝试过自己提出解决方案,只是希望别人为我做这项工作。但是,我尝试了三种不同的解决方案,而且我已经陷入了困境。一个原因可能是我试图避免递归(可能是错误的)。我不会发布我到目前为止的部分解决方案,因为它们不正确,可能会严重影响其他人的解决方案。

3 个答案:

答案 0 :(得分:3)

我需要一个类似的算法来对具有依赖关系的任务进行排序(每个任务都可以有一个需要先完成的父任务)。我找到拓扑排序。这是iterative implementation in Python,其中包含非常详细的评论。

可以在进行拓扑排序时计算缩进级别。只需将节点的缩进级别设置为其父节点的缩进级别+ 1,因为它已添加到拓扑排序中。

注意可能存在许多有效的拓扑排序。为了确保生成的拓扑顺序将具有子节点的父节点分组,请选择基于由部分排序信息生成的图的深度优先遍历的拓扑排序算法。

维基百科提供two more algorithms for topological sort。注意这些算法不是很好,因为第一个是广度优先遍历,第二个是递归。

答案 1 :(得分:2)

对于层次结构,您几乎肯定需要递归(如果允许任意深度)。我很快就修改了一些ruby代码来说明你如何实现这个目标(虽然我还没有完成缩进):

# 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

如果你不说红宝石,我可以用别的东西重新制作它。

编辑:我更新了上面的代码,输出了两个数据结构。

输出:

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}

答案 2 :(得分:1)

Wonsungi的帖子帮了很多忙,不过那是一张通用图而不是树。所以我修改了很多东西来创建一个专门为树设计的算法:

// 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)