LogoData Structures & Algorithms

Деревья: обход файловой системы (BFS, очередь)

Файловая система – это дерево: каталоги – узлы, файлы – листья. Обход в ширину (BFS) идёт по уровням, используя очередь, и собирает пути ко всем файлам. Подходит для задач вроде: вывести имена всех файлов в каталоге pics, включая все подкаталоги.
Сложность:

Время – O(N) по числу записей (файлы+каталоги), память – O(W), где W – максимальная ширина уровня.

walkDirBfs.ts
1import { readdir, stat } from 'node:fs/promises';
2import type { Dirent } from 'node:fs';
3import { join, basename } from 'node:path';
4
5type WalkOptions = {
6 /** Следовать по симлинкам (по умолчанию false) */
7 followSymlinks?: boolean;
8};
9
10/**
11 * Обходит дерево каталогов в ширину (BFS) и возвращает список файлов.
12 *
13 * Требование: вывести имена всех файлов в каталоге `pics`,
14 * включая все его подкаталоги.
15 *
16 * Пример:
17 * (async () => {
18 * const files = await walkDirBfs('pics'); // корень обхода
19 * for (const f of files) console.log(basename(f)); // печать только имён
20 * })().catch(console.error);
21 */
22export async function walkDirBfs(root: string, opts: WalkOptions = {}): Promise<string[]> {
23 const files: string[] = [];
24 const queue: string[] = [root];
25
26 while (queue.length) {
27 const dir = queue.shift()!;
28 let entries: Dirent[];
29 try {
30 entries = await readdir(dir, { withFileTypes: true });
31 } catch {
32 continue; // не каталог / нет доступа - пропускаем
33 }
34
35 for (const entry of entries) {
36 const full = join(dir, entry.name);
37
38 if (entry.isDirectory()) {
39 queue.push(full);
40 } else if (entry.isFile()) {
41 files.push(full);
42 } else if (opts.followSymlinks && entry.isSymbolicLink()) {
43 try {
44 const s = await stat(full);
45 if (s.isDirectory()) queue.push(full);
46 else if (s.isFile()) files.push(full);
47 } catch (err) {
48 const code = (err as NodeJS.ErrnoException)?.code;
49 // игнорируем типичные «безопасные» кейсы симлинков
50 if (code === 'ENOENT' || code === 'EACCES' || code === 'EPERM') {
51 // no-op
52 } else {
53 throw err; // всё остальное - настоящая ошибка
54 }
55 }
56 }
57 }
58 }
59
60 return files;
61}