Algorithm 一种将无序列表排序为树状结构的算法
我正在寻找一种算法,将无序的项目列表排序到树结构中,使用尽可能少的“is-child”比较操作 关于我的具体案例的一些背景知识,但我想我只是在寻找一种我一直找不到的通用排序算法(这是一个很难改进的搜索术语) 我有一个无序的等高线列表,它只是描述闭合多边形的坐标列表。我想创建一棵树来表示这些轮廓之间的关系,最外层是根,下一层的每个轮廓都是子轮廓,以此类推。因此,每个节点具有零到多个子节点的树结构 该算法的一个关键要求是将确定一个轮廓是否是另一个轮廓的子轮廓的测试保持在最低限度,因为此操作非常昂贵。等高线可以(而且经常)共享多个顶点,但不应相交。这些共享顶点通常出现在达到贴图限制的地方-在贴图的直边上画一组同心半圆。如果我需要在得到一个明确的答案之前,在直线上运行很多点,那么多边形中的点测试是很慢的 这是我到目前为止提出的算法。毫无疑问,这很幼稚,但它确实有效。可能有一些启发式方法可能会有所帮助-例如,一个轮廓很可能是另一个轮廓的子轮廓,深度在一定范围内-但我想先确定基本算法。第一个危险信号是它是指数型的Algorithm 一种将无序列表排序为树状结构的算法,algorithm,sorting,Algorithm,Sorting,我正在寻找一种算法,将无序的项目列表排序到树结构中,使用尽可能少的“is-child”比较操作 关于我的具体案例的一些背景知识,但我想我只是在寻找一种我一直找不到的通用排序算法(这是一个很难改进的搜索术语) 我有一个无序的等高线列表,它只是描述闭合多边形的坐标列表。我想创建一棵树来表示这些轮廓之间的关系,最外层是根,下一层的每个轮廓都是子轮廓,以此类推。因此,每个节点具有零到多个子节点的树结构 该算法的一个关键要求是将确定一个轮廓是否是另一个轮廓的子轮廓的测试保持在最低限度,因为此操作非常昂贵。
for each candidate_contour in all_contours
for each contour in all_contours
// note already contains means "is direct or indirect child of"
if contour == candidate_contour or contour already contains(candidate_contour)
continue
else
list contours_to_check
contours_to_check.add(candidate_contour)
contour parent_contour = candidate_contour.parent
while (parent_contour != null)
contours_to_check.add(parent_contour)
parent_contour = parent_contour.parent
for each possible_move_candidate in contours_to_check (REVERSE ITERATION)
if possible_move_candidate is within contour
// moving a contour moves the contour and all of its children
move possible_move_candidate to direct child of contour
break
因此,这是可行的——或者至少看起来是可行的——但对于数量不多的等高线(想想——几百到可能几千条)来说,它变得非常缓慢
有没有更好的方法来实现这一点,或者说,有没有已知的算法能够准确地处理这一点?如前所述-在我的案例中,关键是将“is轮廓在”比较保持在最小值
根据下面Jim的答案编辑以添加解决方案-谢谢Jim强>
这是第一次迭代-产生了良好的(10倍)改进。迭代2见下文。
当轮廓集变得非常大时,此代码比我的原始算法快10倍以上。请参见下面的图像,它现在在几秒钟内排序(v的30多秒之前),并按顺序渲染。我认为通过一些附加的启发式方法还有进一步改进的余地——例如,现在原始列表是根据区域排序的,那么每个新候选对象都必须是树中某个地方的叶节点。困难在于确定要遍历哪些分支来测试现有的叶子-如果有许多分支/叶子,那么通过检查顶部的分支来缩减搜索空间可能会更快。。还有更多的事情要考虑
public static iso_area sort_iso_areas(List<iso_area> areas, iso_area root)
{
if (areas.Count == 0)
return null;
areas.Sort(new iso_comparer_descending());
foreach (iso_area area in areas)
{
if (root.children.Count == 0)
root.children.Add(area);
else
{
bool found_child = false;
foreach (iso_area child in root.children)
{
// check if this iso_area is within the child
// if it is, follow the children down to find the insertion position
if (child.try_add_child(area))
{
found_child = true;
break;
}
}
if (!found_child)
root.children.Add(area);
}
}
return root;
}
// try and add the provided child to this area
// if it fits, try adding to a subsequent child
// keep trying until failure - then add to the previous child in which it fitted
bool try_add_child(iso_area area)
{
if (within(area))
{
// do a recursive search through all children
foreach (iso_area child in children)
{
if (child.try_add_child(area))
return true;
}
area.move_to(this);
return true;
}
else
return false;
}
公共静态iso\u区域排序\u iso\u区域(列表区域,iso\u区域根)
{
如果(areas.Count==0)
返回null;
排序(新的iso_比较器_降序());
foreach(面积中的iso_面积)
{
if(root.children.Count==0)
root.children.Add(面积);
其他的
{
bool found_child=false;
foreach(根目录中的iso_区域子项.children)
{
//检查此iso_区域是否在子对象内
//如果是,请跟随子项查找插入位置
if(child.try_添加_child(区域))
{
found_child=true;
打破
}
}
如果(!找到子项)
root.children.Add(面积);
}
}
返回根;
}
//尝试将提供的子项添加到此区域
//如果合适,请尝试添加到后续子级
//继续尝试,直到失败-然后添加到安装它的前一个子项
bool try_add_child(iso_区域)
{
如果(在(区域内))
{
//对所有子项执行递归搜索
foreach(iso_区域儿童中的儿童)
{
if(child.try_添加_child(区域))
返回true;
}
区域。移动到(此);
返回true;
}
其他的
返回false;
}
第二次迭代-仅与现有叶片进行比较
根据我之前的想法,新的轮廓只能适合现有的叶子,我突然想到,事实上这会快得多,,因为多边形中多边形测试会在除目标叶子以外的所有叶子的第一次边界检查中失败。第一个解决方案涉及遍历一个分支以找到目标,根据定义,沿途的每个多边形都将通过边界检查,并进行完整的多边形对多边形测试,直到没有发现更多的叶子
继Jim的评论和对代码的重新检查之后,不幸的是,第二个解决方案没有起作用。我想知道在分支之前查看树中较低的元素是否还有一些好处,因为poly-in-poly测试应该很快失败,并且您知道如果您找到一个接受候选元素的叶子,那么就没有更多的poly需要检查了
重访第二次迭代
虽然不是等高线只能适合树叶,而是它们几乎总是这样,而且它们通常会适合等高线有序列表中最近的前辈。最后更新的代码是迄今为止最快的,并且完全放弃了树遍历。它只是在最近较大的多边形中向后走,然后尝试每个多边形-来自其他分支的多边形很可能会在边界检查中通过多边形中多边形测试,并且找到的第一个多边形包围了候选多边形
public static iso_area sort_iso_areas(List<iso_area> areas)
{
if (areas.Count == 0)
return null;
areas.Sort(new iso_comparer_descending());
for (int i = 0; i < areas.Count; ++i)
{
for (int j = i - 1; j >= 0; --j)
{
if (areas[j].try_add_child(areas[i]))
break;
}
}
return areas[0];
}
boolean tryAddToNode(Node possibleParent, Node toAdd)
{
if not toAdd.isChildOf(possibleParent)
return false
for each child in possibleParent.children
if(tryAddToNode(child, toAdd))
return true
// not a child of any of my children, but
// it is a child of me.
possibleParent.children.add(toAdd)
return true
}
A.x1 < B.x1
A.y1 < B.y1
A.x2 > B.x2
A.y2 > B.y2
PolygonNode
{
Polygon poly
PolygonNode[] Children
}
Polygon[] sortedPolygons
PolygonNode[] theTree
iso_area last_area = null; // <============
foreach (iso_area area in areas)
{
if (root.children.Count == 0)
root.children.Add(area);
else if (!last_area.try_add_child(area)) // <=======
{
bool found_child = false;
foreach (iso_area child in root.children)
{
// check if this iso_area is within the child
// if it is, follow the children down to find the insertion position
if (child.try_add_child(area))
{
found_child = true;
break;
}
}
if (!found_child)
root.children.Add(area);
}
last_area = area; // <============
}
return root;