从删除空目录算法中消除递归

时间:2012-03-02 19:36:39

标签: c# .net algorithm recursion stack

我想我以前知道怎么做,但似乎我已经忘记了。

我有一个递归算法来删除目录树中的所有空目录:

static bool DeleteDirectoriesRecursive(string path)
{
    var remove = true;
    foreach (var dir in System.IO.Directory.GetDirectories(path))
    {
        remove &= DeleteDirectoriesRecursive(dir);
    }
    if (remove &= (System.IO.Directory.GetFiles(path).Length == 0))
        System.IO.Directory.Delete(path);
    return remove;
}

我正在尝试消除此算法的递归,而不是“修复”算法(即the similar question不使用remove变量,但我想保留它)。

我已经使用Stack<>类启动了一个新函数,但我想不出一个返回基本路径并采取子目录已确定的操作的好方法。我想解开非尾递归需要更多的努力。

2 个答案:

答案 0 :(得分:1)

sans-recursion版本要困难得多,效率也不高,所以我建议保留你的递归。此外,这是一个更清洁的版本:

static bool DeleteDirectoriesRecursive(DirectoryInfo d)
{
    bool remove = true;

    foreach (DirectoryInfo c in d.GetDirectories())
    {
        remove &= DeleteDirectoriesRecursive(c);
    }

    if (remove && d.GetFiles().Length == 0) {
        d.Delete();
        return true;
    }

    return false;
}

答案 1 :(得分:1)

由于Gabe在使用递归时提到了StackOverflowException的可能性,所以我受到了启发,没有它就可以完成这项工作。我使用代码here作为起点。这就是我想出的......

public static bool DeleteDirectories(string root)
{
    bool removed = false;

    var dirs = new Stack<string>();
    var emptyDirStack = new Stack<string>();
    var emptyDirs = new Dictionary<string, int>();

    if (!System.IO.Directory.Exists(root))
    {
        throw new ArgumentException();
    }
    dirs.Push(root);

    while (dirs.Count > 0)
    {
        string currentDir = dirs.Pop();

        string[] subDirs;
        try
        {
            subDirs = System.IO.Directory.GetDirectories(currentDir);
        }
        catch (UnauthorizedAccessException e)
        {
            Console.WriteLine(e.Message);
            continue;
        }
        catch (System.IO.DirectoryNotFoundException e)
        {
            Console.WriteLine(e.Message);
            continue;
        }

        if (Directory.GetFiles(currentDir).Length == 0)
        {
            emptyDirStack.Push(currentDir);
            emptyDirs.Add(currentDir, subDirs.Length); // add directory path and number of sub directories
        }

        // Push the subdirectories onto the stack for traversal.
        foreach (string str in subDirs)
            dirs.Push(str);
    }

    while (emptyDirStack.Count > 0)
    {   
        string currentDir = emptyDirStack.Pop();
        if (emptyDirs[currentDir] == 0)
        {
            string parentDir = Directory.GetParent(currentDir).FullName;
            Console.WriteLine(currentDir); // comment this line
            //Directory.Delete(currentDir); // uncomment this line to delete
            if (emptyDirs.ContainsKey(parentDir))
            {
                emptyDirs[parentDir]--; // decrement number of subdirectories of parent
            }
            removed = true;
        }
    }

    return removed;
}

一些注意事项:

  1. 在没有递归的情况下爬树没有太难以实现,并且在我上面链接的MSDN文章中完成了。但是这个特殊的例子比他们的要复杂得多。
  2. 我正在存储emptyDirs字典中不包含任何文件的每个目录,以及它包含的子目录数。
  3. 由于删除目录的顺序很关键,我必须以相反的顺序运行emptyDirs字典(我使用Linq来完成此操作)。
  4. 我想有更有效的方法,但这并不算太糟糕,而且它似乎在我的测试中起作用。

    更新: 我摆脱了颠倒的字典而支持第二个堆栈。不过,我仍然使用字典进行快速查找。