2012-03-02 133 views
2

我想我以前知道如何做到這一點,但似乎我已經忘記了。消除遞歸刪除空目錄算法

我有一個遞歸算法刪除所有空目錄的目錄樹:

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<>類,但我想不出一個好辦法,回到基本路徑,並採取了子目錄已經確定的行動。我想解開非尾遞歸需要多一點努力。

+0

你爲什麼要用另一個堆棧(你的)替換一個堆棧(IL堆棧)?你從中獲得什麼? – zmbq 2012-03-02 19:38:50

+0

知識。沒有人說我要在生產代碼中這樣做。 – palswim 2012-03-02 19:40:07

+0

@zmbq - 這就是我的想法。這實際上是遞歸的一個很好的用法,因爲它使用調用堆棧來爬取樹。試圖用自己的堆棧做同樣的事情只會使代碼更長,更難以理解,並且不會提供任何性能優勢。 – 2012-03-02 19:44:38

回答

1

因爲加布使用遞歸的時候我的靈感,使這項工作沒有它提到一個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來完成此操作)。

我想有更有效的方法,但這並不算太壞,它似乎在我的測試工作。

更新: 我擺脫了顛倒的字典,贊成第二堆棧。不過,我仍然使用字典進行快速查找。

+0

爲什麼downvote? – 2012-03-02 22:57:22

+0

你絕對不應該依賴'Dictionary'中的任何一種排序。在某些情況下,它會按照插入它們的順序返回它們,但不應該依賴它,因爲它並不總是有效。 – svick 2012-03-02 22:57:24

+0

@svick - 很高興知道。我只是放棄了顛倒的字典。 – 2012-03-02 23:03:54

1

無sans遞歸版本要困難得多,效率也不高,所以我只是建議保持遞歸。此外,這裏有一個版本,這是一個少許清潔劑:

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; 
} 
+0

* *有點乾淨。儘管如此,你仍然必須找到一個地方,在必要時將remove設置爲'false'('remove&=(d。GetFiles()。Length == 0)'somewhere)。 – palswim 2012-03-02 19:53:22

+0

@palswim:好點,完成。 – Ryan 2012-03-02 19:54:11