【问题标题】:Breadth first search of C# Control CollectionC#控件集合的广度优先搜索
【发布时间】:2012-03-21 04:11:48
【问题描述】:

我正在尝试为System.Web.UI.Control 编写一个扩展方法,它将在其ControlCollection 中搜索特定Type 的实例,并返回找到的第一个实例。深度优先很简单,但是我想先搜索广度,以便更高的集合优先。

我当前的方法有缺陷,会提前退出,在整个搜索未完成的某些情况下返回 null。我希望我接近正确的解决方案,但需要一些新的眼光。有什么建议吗?

public static T FindFirstControlOfType<T>(this Control rootControl, bool searchRecursively) where T : Control
{
    // list for container controls
    List<Control> controlsWithChildren = new List<Control>();

    // iterate the current control collection first
    foreach (Control child in rootControl.Controls)
    {
        if (child.GetType().IsAssignableFrom(typeof(T)))
        {
            return (T)child;
        }

        // track those controls containing children
        if (child.HasControls())
        {
            controlsWithChildren.Add(child);
        }
    }

    // if recursion is enabled, search the child nodes
    if (searchRecursively)
    {
        foreach (Control control in controlsWithChildren)
        {
            return FindFirstControlOfType<T>(control, true);
        }
    }

    // if never found, return null
    return null;
}

编辑 - 基于标记答案的工作解决方案,适用于任何感兴趣的人:

public static T FindFirstControlOfType<T>(this Control rootControl, bool searchNestedControls) where T : Control
{
    Queue<Control> queue = new Queue<Control>();

    EnqueueChildControls(queue, rootControl);
    while (queue.Count > 0)
    {
        Control current = queue.Dequeue();

        if (current.GetType() == typeof(T))
        {
            return (T)current;
        }

        if (searchNestedControls)
        {
            EnqueueChildControls(queue, current);
        }
    }
    return null;

}

private static void EnqueueChildControls(Queue<Control> queue, Control control)
{
    foreach (Control current in control.Controls)
    {
        queue.Enqueue(current);
    }
}

【问题讨论】:

    标签: c# asp.net recursion queue breadth-first-search


    【解决方案1】:

    广度优先搜索(伪代码)

    Queue fringe 
    
    fringe.Enqueue(rootControl)
    
    while(fringe.Count > 0)
    {
        current = fringe.Dequeue()
        Visit(current)
        fringe.EnqueueAll(current.Children)
    }
    

    广度优先搜索不需要递归。

    Visit 是您想要对当前对象执行的任何操作。如果您正在搜索某些东西,那么Visit 的意思是:if(current matches criteria) return current

    更新: 工作示例:https://gist.github.com/1960108

    【讨论】:

    • 谢谢安德烈 - 我会尝试实现这一点。自从我需要广度优先搜索以来已经有一段时间了,所以我生疏了。
    • 我也是,但最近我需要做一个 BFS,然后我记得它很容易实现。祝你好运,如果您需要帮助,请告诉我们。
    • 附带说明,您可以先将 BFS 更改为深度,只需将队列更改为堆栈,而不是使用递归。您还可以使用优先级队列使其成为最佳优先搜索。如果您对所有这些都有一个通用接口,您可以达到一个简单的配置更改改变整个算法的地步。很酷的东西。
    • @AndreLoker 谢谢 - 我使用您的指导重写了该方法,并且效果很好。点赞+回答。我将使用最终代码更新我的问题,这可能会对将来的某人有所帮助。
    • @Servy 感谢您的建议 - 我实际上很喜欢这种灵活性,并且可能会重构我的扩展方法以支持搜索类型。回到先进先出与......的美好时光:)
    【解决方案2】:

    主要问题在这里:

    foreach (Control control in controlsWithChildren)
    {
        return FindFirstControlOfType<T>(control, true);
    }
    

    您将返回对第一个有孩子的孩子的递归搜索结果,无论该搜索是否成功。广度优先搜索是一个被广泛讨论的话题,但归结为使用队列:

    Queue<Control> controlsWithChildren = new Queue<Control>();
    

    和(通常)循环而不是递归。

    【讨论】:

      猜你喜欢
      • 2011-01-31
      • 1970-01-01
      • 2014-06-02
      • 1970-01-01
      • 1970-01-01
      • 2016-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多