如何遍历像Windows结构的Windows资源管理器

时间:2009-06-11 18:48:24

标签: c++ visual-c++ mfc

我正在设计一个FTP客户端,需要一些设计功能的帮助,其唯一目的是能够遍历树状结构并上传用户选择的文件夹和文件。

基本上,用户可以从本地文件和文件夹View中一次选择多个文件夹和文件。然后,我需要能够遍历选定的每个文件夹以搜索和定位子文件夹,直到所有文件夹和子文件夹和文件都已上载到FTP服务器。

我对Tree like数据结构和使用递归遍历二叉树有点熟悉。但是在我的情况下,用户可能有多个/几个子文件夹和所有路径的文件,并认为使用递归的开销很大。我也意识到使用do / while循环结构可以完成任何递归操作。

任何建议或意见将不胜感激。如果已有现成的代码,请提供链接。

由于

5 个答案:

答案 0 :(得分:2)

http://en.wikipedia.org/wiki/Tree_traversal

基本上你有预购,有序,后序和等级订单。每个人都有他们自己的专业人士,但最后,他们做同样的事情。

wikipage获得了算法的递归和迭代版本的伪代码实现。我不打算在这里重新做,但如果你需要帮助来理解它们,请在这里发帖,我将逐步介绍它们:P

[编辑] Eeek,yikes等! :D好像我的触发手指在那里太快了。该链接仅提供遍历二叉树的算法。但是,原则保持不变,除了在给定节点上代替“左”和“右”子项,您现在有0到多个孩子。

假设您在每个节点上都有一个具有任意数量子节点的树,您可以使用某种In / Out数据结构(基本上是队列或堆栈)来遍历它。您为此选择的那个将决定您搜索树的顺序。在此示例中,使用队列:

public void TraverseTree(Node rootNode)
{
   Queue nodeQueue();
   nodeQueue.push(rootNode);   //The first node to examine is the rootNode
   Node currentNode;

   //Okay, here we go. We will continue till the queue is empty. The queue will
   //be empty when we've seen all children of all children :)
   while (!nodeQueue.IsEmpty())   
   {
       currentNode = nodeQueue.Pop();   //Get the next node to examine

       DoStuffToNode(currentNode);      //Do whatever we want to the node (in your case
                                        //do some FTP stuff to the node (aka. the file)

       //Okay, we're done with this node. Now, let's add all the children of this node
       //To the queue of nodes we want to examine
       foreach(Node child in currentNode.children)   
          nodeQueue.push(child);
   }
}

如果你愿意的话,你可以用一个数组来做这个,但它会带来一些骗局,很可能是无效的,而且不是很直观。

假设您要将C:转移到FTP站点(为了便于说明)

使用堆栈,您将在前往大孩子之前遍历当前节点的所有子节点。所以,你首先创建一个名为“C:”的文件夹,然后是“Program Files”,然后是“Windows” - 然后你会进入“程序文件”并创建“Adobe”,然后创建“Microsoft”等。等。

使用队列,您将在前往下一个孩子之前遍历孩子的所有祖先。然后我们首先创建“程序文件”,然后是“Adobe”,然后是“Microsoft”等,然后创建“Windows”。

我真的希望我在这里清楚明白:)用一个动画解释起来要容易得多。

基本算法是:

  1. 转到队列中的下一个节点或堆栈
  2. 将内容添加到当前节点
  3. 将当前节点的所有子节点添加到队列或堆栈
  4. 转到1
  5. 哦,顺便说一下,我对MFC没有任何经验,但你不能使用std :: queue<>而不是CArray? :)

答案 1 :(得分:0)

对于这样的操作,我建议创建一个函数来解析一个文件夹,该文件夹将文件夹中的所有文件添加到某种数组或哈希中。如果该函数遇到目录中的文件夹,那么它将自行调用它。这里有一些伪代码。

static void processFolder(string folderName, string[][] array)
{
    if (FolderLibs::hasChildFolders(folderName))
    {
        foreach(string childFolderName in FolderLibs::getChildFolderNames(folderName))
        {
            processFolder(childFolderName, array);
        }
    }

    foreach(string fileName in FolderLibs::getChildFileNames)
    {
        array[folderName].push(fileName);
    }    
}

void main()
{
    string rootFolder = "/";
    processFolder(rootFolder);
}

无论如何,这是我对伪代码的悲惨尝试:)希望它有所帮助。

答案 2 :(得分:0)

我确实对维基百科提供的伪代码有一些疑问。这是使用interations的伪代码之一。我的评论就在每一行旁边。

public void traverseTree(Node root)
{
   Stack nodes = new Stack();          // this can be a CArray ?
   nodes.push(root);                   // are we adding to back or front of CArray ?
   Node currentNode;                   // OK
   while (! nodes.isEmpty()){          // OK
      currentNode = nodes.pop();       // pop from front of CArray ?
      Node right = currentNode.right(); // don't know what is node to right ? parent ?
      if (right != null)
         nodes.push(right);
      Node left = currentNode.left();   // don't know what is node to left ? child ?
      if (left != null)
         nodes.push(left);      
      System.out.println("Node data: "+currentNode.data);
   }
}

答案 3 :(得分:0)

你太习惯了你需要避免递归的想法。没有必要 - 递归会使算法变得无比简单。

当我们只有16 MB的RAM时,我们正在集中使用递归。当然有今天的技嘉机器,内存非常丰富。

以另一种方式看待它:通常一个文件夹最多只有5个深度。让我们假设它更深 - 比如说1000.并说你通常有5个局部变量,例如大小,例如长 - 4 x 2 = 8字节。对于深度为1000的文件夹,堆栈上需要8,000个字节 - 8MB。

假设你有4 GB的主内存 - 在递归算法中你只会使用0.0007%的内存。因此,不要担心递归的“低效率” - 效率低下的是制作一个冗长,不优雅,容易出错的非递归版本的基本递归算法。

答案 4 :(得分:0)

为了向您展示使用递归处理树的简单方法,这里是递归算法,用于生成随机树,递归打印出来,并递归打印出所选节点。

模块模块1

Private randGen As New Random()

''' <summary>
''' Max depth of the tree.
''' </summary>
Private MAX_DEPTH As Integer = 3

Private MAX_CHILDREN As Integer = 5

''' <summary>
''' Node of a tree with any number of children.
''' Leave Nodes have no children.
''' </summary>
''' <remarks></remarks>
Private Class Node
    Public children As List(Of Node) = Nothing
    Public name As String
    Public isSelected As Boolean = False
End Class

''' <summary>
''' Create a random tree, of at most depth, and return
''' the root.
''' </summary>
Private Function createTreeAux(ByVal depth As Integer) As Node
    Dim node As New Node
    ' generate random name
    node.name = "leaf" & randGen.Next().ToString
    ' randomly select it or not.
    node.isSelected = randGen.Next(0, 100) > 50

    ' create leaf node
    If depth = 0 Then
        Return node
    End If

    ' create children
    Dim numChildren As Integer = randGen.Next(1, MAX_CHILDREN)
    node.children = New List(Of Node)
    For i As Integer = 0 To numChildren - 1
        node.children.Add(createTreeAux(depth - 1))
    Next
    Return node
End Function

''' <summary>
''' Top level function for creating a random tree.
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Private Function createTree() As Node
    Return createTreeAux(MAX_DEPTH)
End Function

''' <summary>
''' Auxiliary function for printing out the selected nodes of
''' the tree. if selectedOnly is true, then only the selected nodes
''' are printed out. Otherwise the selected nodes have their name
''' pre-pended with an asterix "*".
''' </summary>
''' <param name="node"></param>
''' <param name="depth"></param>
Private Sub printTreeAux(ByVal node As Node, ByVal depth As Integer, _
                             ByVal selectedOnly As Boolean)
    If node Is Nothing Then
        Return
    End If

    ' indent
    For i As Integer = 0 To depth - 1
        Console.Write("  ")
    Next
    ' print node label
    If Not selectedOnly Or node.isSelected Then
        If node.isSelected Then
            Console.Write("*")
        End If
        Console.WriteLine(node.name)
    End If
    If Not node.children Is Nothing Then
        For Each child In node.children
            printTreeAux(child, depth + 1, selectedOnly)
        Next
    End If
End Sub

''' <summary>
''' Print out the selected nodes of the tree rooted 
''' at root.
''' </summary>
''' <param name="root">root of the tree to be printed</param>
Private Sub printSelected(ByVal root As Node)
    Console.WriteLine("Printing Selected Nodes Only")
    printTreeAux(root, 0, True)
End Sub

''' <summary>
''' Prints out the entire tree 
''' </summary>
''' <param name="root">root of the tree to be printed</param>
Private Sub printTree(ByVal root As Node)
    Console.WriteLine("Printing Entire Tree")
    printTreeAux(root, 0, False)
End Sub

''' <summary>
''' Do a test run by generating a random tree and then printing out
''' the entire tree, then just the selected nodes.
''' </summary>
Private Sub test()
    Dim tree As Node = createTree()
    printTree(tree)
    printSelected(tree)

End Sub

Sub Main()
    ' just do 5 test runs
    For i As Integer = 1 To 1
        Console.WriteLine("Run " & i.ToString())
        test()
    Next
    Console.ReadLine()
End Sub

结束模块