当递归遍历目录结构时,如果文件比目录多,最有效的算法是什么?我注意到使用深度优先遍历时,当给定目录中有很多文件时,它似乎需要更长的时间。在这种情况下,广度优先遍历是否更有效?目前我无法对这两种算法进行分析,因此非常欢迎您的见解。
编辑:针对 alphazero 的评论,我正在 Linux 机器上使用 PHP。
当递归遍历目录结构时,如果文件比目录多,最有效的算法是什么?我注意到使用深度优先遍历时,当给定目录中有很多文件时,它似乎需要更长的时间。在这种情况下,广度优先遍历是否更有效?目前我无法对这两种算法进行分析,因此非常欢迎您的见解。
编辑:针对 alphazero 的评论,我正在 Linux 机器上使用 PHP。
由于您拥有的文件比目录更多,因此看起来您处理的不是非常深层嵌套的目录,这会使DFS占用更多的内存(因此需要更长的时间)比BFS。基本上,BFS和DFS都做同样的事情(即访问图的每个节点),因此通常它们的速度不应该有任何显着差异。
很难确定为什么您的DFS较慢,没有实际查看您的实现。您确定由于文件系统中的链接/快捷方式而不止一次访问相同的节点吗?如果使用基于显式堆栈的DFS而不是递归,则可能会获得显着的加速。
stat
查看它们是文件还是目录。因此,我建议从先序深度优先搜索开始作为起点,因为它最容易实现,并且最有可能具有良好的缓存/搜索性能。
总之 - 先序深度优先 - 进入目录时,列出其内容,处理该目录中的任何文件,并保存子目录名称列表。然后依次进入每个子目录。除非你知道自己有非常深的目录结构,否则只需使用程序的调用堆栈作为堆栈即可。
使用BFS(如Igor所提到的)遍历目录结构。
当您到达一个目录时,启动一个线程来列出该目录中的所有文件。
并在完成列表/遍历文件后终止该线程。
因此,每个目录都将有一个单独的线程来列出文件。
例子:
root
- d1
- d1.1
- d1.2
- f1.1 ... f1.100
- d2
- d2.1
- d2.2
- d2.3
- f2.1 ... f2.200
- d3
....
输出结果可能会像这样 ->
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
因此,当你返回遍历目录的深度时,获取文件的线程将已经完成(几乎)其工作。
希望这对你有所帮助。
这在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;
};