当递归遍历目录结构时,如果文件多于目录,那么最有效的算法是什么?我注意到,当使用深度优先遍历时,当给定目录中存在大量文件时,似乎需要更长时间。在这种情况下,广度优先遍历是否更有效?我目前无法对这两种算法进行分析,因此非常欢迎您的见解。
编辑:为了回应alphazero的评论,我在Linux机器上使用PHP。
答案 0 :(得分:2)
由于您拥有的文件多于目录,因此它看起来并不像处理非常深层嵌套的目录那样会使DFS占用更多内存(因此比BFS更多)。从本质上讲,BFS和DFS都做同样的事情(即访问图中的每个节点),因此一般来说它们的速度不应有任何显着的差异。
很难说为什么在没有实际看到你的实现的情况下你的DFS会更慢。由于文件系统中的链接/快捷方式,您确定不会多次访问相同的节点吗?如果使用基于显式堆栈的DFS而不是递归,您也可能获得显着的加速。
答案 1 :(得分:1)
广度优先将更好地运作是有道理的。输入根文件夹后,您将创建需要处理的项目列表。其中一些是文件,一些是目录。
如果你使用广度优先,你将处理目录中的文件并忘记它们,然后再转到其中一个子目录。
如果您使用深度优先,则需要继续增加一个文件列表,以便在您深入钻取时进行处理。这将使用更多内存来维护您要处理的文件列表,可能导致更多页面错误等...
另外,您还需要查看新项目列表,以确定哪些是您可以深入研究的目录。当你已经到了处理文件的时候,你需要再次查看相同的列表(减去目录)。
答案 2 :(得分:1)
您可能只希望每个目录扫描一次目录中的内容,因此processing order - 无论您在访问其他目录之前还是之后处理目录的内容,可能比您是否正在深入处理更重要 - 第一次或广度优先搜索。根据您的文件系统,处理文件节点的速度可能比stat
更早,而不是晚于它们,以查看它们是文件还是目录。因此,我建议将预订深度优先搜索作为起点,最容易实现,并且最有可能具有良好的缓存/搜索性能。
总结 - 预订深度优先 - 在进入目录时,列出其内容,处理该目录中的所有文件,并保存子目录名称列表。然后依次输入每个子目录。只需将程序的调用堆栈用作堆栈,除非您知道目录结构非常深。
答案 3 :(得分:0)
使用BFS进行Travse目录结构(如Igor所述)。
当你到达目录时,启动一个线程列出目录中的所有文件。
一旦完成列出/追踪文件,就杀掉它。
因此,每个目录都有单独的线程来列出文件。
实施例
root
- d1
- d1.1
- d1.2
- f1.1 ... f1.100
- d2
- d2.1
- d2.2
- d2.3
- f2.1 ... f2.200
- d3
....
OUTPUT 可能如下所示 - >
got d1
started thread to get files of d1
got d2
started thread to get files of d1
done with files in d1
got d3
started thread to get files of d1
got d1.1
started thread to get files of d1.1
got d1.2
started thread to get files of d1.2
所以当你回到目的地的深处时 获取文件的线程已经完成(几乎)它的工作。
希望这有帮助。
答案 4 :(得分:0)
这在Windows(类DirectoryTreeReader)中最有效,它首先使用呼吸并存储每个目录。
static const uint64 DIRECTORY_INDICATOR = -1;//std::numeric_limits <uint64>::max();
class DirectoryContent {
public:
DirectoryContent(const CString& path)
: mIndex(-1)
{
CFileFind finder;
finder.FindFile(path + L"\\*.*");
BOOL keepGoing = FALSE;
do {
keepGoing = finder.FindNextFileW();
if (finder.IsDots()) {
// Do nothing...
} else if (finder.IsDirectory()) {
mPaths.push_back(finder.GetFilePath());
mSizes.push_back(DIRECTORY_INDICATOR);
} else {
mPaths.push_back(finder.GetFilePath());
mSizes.push_back(finder.GetLength());
}
} while(keepGoing);
}
bool OutOfRange() const {
return mIndex >= mPaths.size();
}
void Advance() {
++mIndex;
}
bool IsDirectory() const {
return mSizes[mIndex] == DIRECTORY_INDICATOR;
}
const CString& GetPath() const {
return mPaths[mIndex];
}
uint64 GetSize() const {
return mSizes[mIndex];
}
private:
CStrings mPaths;
std::vector <uint64> mSizes;
size_t mIndex;
};
class DirectoryTreeReader{
DirectoryTreeReader& operator=(const DirectoryTreeReaderRealtime& other) {};
DirectoryTreeReader(const DirectoryTreeReaderRealtime& other) {};
public:
DirectoryTreeReader(const CString& startPath)
: mStartPath(startPath){
Reset();
}
void Reset() {
// Argh!, no clear() in std::stack
while(!mDirectoryContents.empty()) {
mDirectoryContents.pop();
}
mDirectoryContents.push( DirectoryContent(mStartPath) );
Advance();
}
void Advance() {
bool keepGoing = true;
while(keepGoing) {
if (mDirectoryContents.empty()){
return;
}
mDirectoryContents.top().Advance();
if (mDirectoryContents.top().OutOfRange()){
mDirectoryContents.pop();
} else if ( mDirectoryContents.top().IsDirectory() ){
mDirectoryContents.push( DirectoryContent(mDirectoryContents.top().GetPath()) );
} else {
keepGoing = false;
}
}
}
bool OutOfRange() const {
return mDirectoryContents.empty();
}
const CString& GetPath() const {
return mDirectoryContents.top().GetPath();
}
uint64 GetSize() const {
return mDirectoryContents.top().GetSize();
}
private:
const CString mStartPath;
std::stack <DirectoryContent> mDirectoryContents;
};