C#:如何使这个方法非递归

时间:2009-10-21 23:08:52

标签: c# recursion

我有这个删除空文件夹的递归方法:

    private void DeleteEmpty(DirectoryInfo directory)
    {
        foreach (var d in directory.GetDirectories())
        {
            DeleteEmpty(d);
        }

        if (directory.GetFileSystemInfos().Length == 0)
        {
            try
            {
                directory.Delete();
            }
            catch (Exception)
            {
                // Already gone, no permission, not empty, et cetera
            }
        }
    }

如何重构此方法以使其不是递归的?

6 个答案:

答案 0 :(得分:3)

标准重构是存储您在LIFO(即堆栈)或FIFO队列中传递给函数的数据。请注意,这不会改变渐近空间的使用;你正在使用自己的数据结构而不是调用堆栈。

如果您可以定义“下一个兄弟”功能,则可以访问具有附加空间的节点。这是因为目录图(无文件)由于父指针而基本上是无向的。伪代码:

nextBranchingSibling(sibling):
  while sibling exists
    if sibling has children
      return sibling
    sibling = nextSibling(sibling)
  return null

nextBranch(node):
  if node is marked
      unmark node
  else
      if nextBranchingSibling(firstChild(node)) exists
          return nextBranchingSibling(firstChild(node))
  if nextBranchingSibling(nextSibling(node)) exists
      return nextBranchingSibling(nextSibling(node))
  mark parent(node)
  return parent(node)

prune(node):
  while node exists:
    tmpNode = node
    node    = nextBranch(node)
    if count of tmpNode's children is 0
      delete tmpNode

请注意,您实际上并未使用O(1)空间总数,因为目录结构本身就是O(n)。像DirectoryInfo.GetDirectories这样的方法可以消除nextBranchingSibling中对循环的需求。

答案 1 :(得分:3)

private static Queue<DirectoryInfo> directoryQueue = new Queue<DirectoryInfo>();
private void DeleteEmpty(DirectoryInfo directory)
{
    directoryQueue.Enqueue(directory);
    while (directoryQueue.Count > 0)
    {
        var current = directoryQueue.Dequeue();
        foreach (var d in current.GetDirectories())
        {
            directoryQueue.Enqueue(d);
        }

        if (directory.GetFileSystemInfos().Length == 0)
        {
            try
            {
                directory.Delete();
            }
            catch (Exception)
            {
                // Already gone, no permission, not empty, et cetera
            }
        }
    }
}

答案 2 :(得分:3)

试试这个:

private void DeleteEmpty(string path)
{
    string[] directories = Directory.GetDirectories(
        path, "*", SearchOption.AllDirectories);

    // you should delete deeper directories first
    //      .OrderByDescending(
    //          dir => dir.Split(Path.DirectorySeparatorChar).Length)
    //              .ToArray();

    foreach (string directory in directories)
    {
        DirectoryInfo info = new DirectoryInfo(directory);
        if (info.GetFileSystemInfos().Length == 0)
        {
            info.Delete();
        }
    }

    // If you wanna a LINQ-ish version
    // directories.Where(dir => 
    //     new DirectoryInfo(dir).GetFileSystemInfos().Length == 0)
    //         .ToList().ForEach(dir => Directory.Delete(dir));
}

另一个性能步骤可能是:如果您尝试删除目录并且它包含文件,则应跳过所有父级别,因为它们也会失败。

答案 3 :(得分:1)

当堆栈不为空时,您可以使用本地堆栈和循环。

public void DeleteDirectories(DirectoryInfo directoryInfo, bool deleteFiles)
{
    Stack<DirectoryInfo> directories = new Stack<DirectoryInfo>();
    directories.Push(directoryInfo);

    while (directories.Count > 0)
    {
        var current = directories.Peek();

        foreach (var d in current.GetDirectories())
            directories.Push(d);

        if (current != directories.Peek())
            continue;

        if (deleteFiles)
            foreach (var f in current.GetFiles())
            {
                f.Delete();
            }

        if (current.GetFiles().Length > 0 || current.GetDirectories().Length > 0)
            throw new InvalidOperationException("The directory " + current.FullName + " was not empty and could not be deleted.");

        current.Delete();

        directories.Pop();
    }
}

答案 4 :(得分:1)

我有同样的问题,我创建了一个很好的(imho)解决方案:在根目录中开始,我“递归地”获取子目录并将它们存储在ArrayList对象中。通过这种方式,我创建了一个列表,首先是较高级别的dirs,最后是较深层的嵌套目录。理想情况下,使用存储在LevelList对象级别中的索引将该数组划分为子数组。 这样做,我可以先查看更深层的目录,如果它们是空的则删除它们,然后逐级返回到根目录。

例如:

    private void directoryCleanup(string root)
    {
        try
        {
            // Create directory "tree"
            ArrayList dirs = new ArrayList();
            // Beginning and ending indexes for each level
            ArrayList levels = new ArrayList();
            int start = 0;
            dirs.Add(root);
            while (start < dirs.Count)
            {
                ArrayList temp = new ArrayList();
                for (int i = start; i < dirs.Count; i++)
                {
                    DirectoryInfo dinfo = new DirectoryInfo((string)dirs[i]);
                    DirectoryInfo[] children = dinfo.GetDirectories();
                    for (int j = 0; j < children.Length; j++)
                    {
                        temp.Add(children[j].FullName);
                    }
                    Array.Clear(children, 0, children.Length);
                    children = null;
                    dinfo = null;
                }
                start = dirs.Count;
                levels.Add(dirs.Count);
                dirs.AddRange(temp);
                temp.Clear();
                temp = null;
            }
            levels.Reverse();
            // Navigate the directory tree level by level, starting with the deepest one
            for (int i = 0; i < levels.Count - 1; i++)
            {
                int end = (int)levels[i] - 1;
                int begin = (int)levels[i + 1];
                for (int j = end; j >= begin; j--)
                {
                    string path = (string)dirs[j];
                    if (Directory.GetFileSystemEntries(path).Length == 0)
                    {
                        Directory.Delete(path);
                    }
                }
            }
            levels.Clear();
            levels = null;
            dirs.Clear();
            dirs = null;
        }
        catch (IOException ioex)
        {
            // Manage exception
            return;
        }
        catch (Exception e)
        {
            // Manage exception
            return;
        }
    }

答案 5 :(得分:0)

创建一个包含起始目录中所有目录的队列,然后当它不为空时,取下一个项目,检查目录是否为空,如果是,则删除它,如果没有将所有子目录添加到队列中。< / p>

我不知道C#,但是如果没有标准的队列类型,链接列表或可变数组类型的东西也可以正常工作。

伪代码;

directories = empty queue
until directories is not empty
    next = directories.shift
    if next is an empty folder
        delete it
    or else
        add all the subdiretories to the queue