Читайте дальше...
Рекурсивный обход для подсчета суммы всех элементов
// Рекурсивный подсчет суммы числовых значений во всех узлах дерева
Процедура РекурсивныйПодсчетСуммы()
// Создаем тестовое дерево
Дерево = СоздатьТестовоеДерево();
// Вычисляем общую сумму рекурсивно
ОбщаяСумма = ПодсчитатьСуммуРекурсивно(Дерево.Строки);
Сообщить("Общая сумма всех элементов: " + ОбщаяСумма);
// Вычисляем количество узлов
КоличествоУзлов = ПодсчитатьКоличествоУзлов(Дерево.Строки);
Сообщить("Общее количество узлов: " + КоличествоУзлов);
КонецПроцедуры
Функция СоздатьТестовоеДерево()
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Наименование");
Дерево.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Корень1 = Дерево.Строки.Добавить();
Корень1.Наименование = "Категория 1";
Корень1.Сумма = 1000;
Дочерний1 = Корень1.Строки.Добавить();
Дочерний1.Наименование = "Товар 1.1";
Дочерний1.Сумма = 500;
Дочерний2 = Корень1.Строки.Добавить();
Дочерний2.Наименование = "Товар 1.2";
Дочерний2.Сумма = 300;
Внук1 = Дочерний2.Строки.Добавить();
Внук1.Наименование = "Подтовар 1.2.1";
Внук1.Сумма = 200;
Корень2 = Дерево.Строки.Добавить();
Корень2.Наименование = "Категория 2";
Корень2.Сумма = 2000;
Дочерний3 = Корень2.Строки.Добавить();
Дочерний3.Наименование = "Товар 2.1";
Дочерний3.Сумма = 800;
Возврат Дерево;
КонецФункции
Функция ПодсчитатьСуммуРекурсивно(СтрокиДерева)
Сумма = 0;
Для Каждого Строка Из СтрокиДерева Цикл
// Добавляем сумму текущего узла
Сумма = Сумма + Строка.Сумма;
// Рекурсивно обрабатываем дочерние узлы
Если Строка.Строки.Количество() > 0 Тогда
Сумма = Сумма + ПодсчитатьСуммуРекурсивно(Строка.Строки);
КонецЕсли;
КонецЦикла;
Возврат Сумма;
КонецФункции
Функция ПодсчитатьКоличествоУзлов(СтрокиДерева)
Количество = 0;
Для Каждого Строка Из СтрокиДерева Цикл
Количество = Количество + 1;
Если Строка.Строки.Количество() > 0 Тогда
Количество = Количество + ПодсчитатьКоличествоУзлов(Строка.Строки);
КонецЕсли;
КонецЦикла;
Возврат Количество;
КонецФункции
Рекурсивный обход с накоплением информации о пути
// Обход дерева с формированием полного пути к каждому узлу
Процедура ОбходСФормированиемПути()
Дерево = СоздатьМногоуровневоеДерево();
// Формируем пути для всех узлов
Пути = Новый Соответствие;
СформироватьПутиРекурсивно(Дерево.Строки, "", Пути);
// Выводим все пути
Сообщить("Пути к узлам дерева:");
Для Каждого Пара Из Пути Цикл
Сообщить(Пара.Ключ + " -> " + Пара.Значение);
КонецЦикла;
// Находим узел по пути
НайденныйУзел = НайтиУзелПоПути(Дерево, "Категория 1/Товар 1.1");
Если НайденныйУзел <> Неопределено Тогда
Сообщить("Найден узел: " + НайденныйУзел.Наименование);
КонецЕсли;
КонецПроцедуры
Функция СоздатьМногоуровневоеДерево()
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Наименование");
Корень1 = Дерево.Строки.Добавить();
Корень1.Наименование = "Категория 1";
Уровень2 = Корень1.Строки.Добавить();
Уровень2.Наименование = "Товар 1.1";
Уровень3 = Уровень2.Строки.Добавить();
Уровень3.Наименование = "Подтовар 1.1.1";
Корень2 = Дерево.Строки.Добавить();
Корень2.Наименование = "Категория 2";
Возврат Дерево;
КонецФункции
Процедура СформироватьПутиРекурсивно(СтрокиДерева, ТекущийПуть, Пути)
Для Каждого Строка Из СтрокиДерева Цикл
НовыйПуть = ?(ТекущийПуть = "", Строка.Наименование, ТекущийПуть + "/" + Строка.Наименование);
Пути.Вставить(НовыйПуть, Строка);
Если Строка.Строки.Количество() > 0 Тогда
СформироватьПутиРекурсивно(Строка.Строки, НовыйПуть, Пути);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция НайтиУзелПоПути(Дерево, Путь)
ЧастиПути = СтрРазделить(Путь, "/");
ТекущиеСтроки = Дерево.Строки;
НайденныйУзел = Неопределено;
Для Каждого Часть Из ЧастиПути Цикл
НайденоНаУровне = Ложь;
Для Каждого Строка Из ТекущиеСтроки Цикл
Если Строка.Наименование = Часть Тогда
НайденныйУзел = Строка;
ТекущиеСтроки = Строка.Строки;
НайденоНаУровне = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Если Не НайденоНаУровне Тогда
Возврат Неопределено;
КонецЕсли;
КонецЦикла;
Возврат НайденныйУзел;
КонецФункции
Рекурсивный поиск узлов по условию
// Поиск всех узлов, удовлетворяющих заданному условию
Процедура РекурсивныйПоискПоУсловию()
Дерево = СоздатьДеревоСотрудников();
// Поиск сотрудников с зарплатой более 60000
Результат1 = НайтиПоУсловию(Дерево.Строки, "Зарплата > 60000");
Сообщить("Сотрудники с зарплатой > 60000:");
Для Каждого Сотрудник Из Результат1 Цикл
Сообщить(" " + Сотрудник.Наименование + " - " + Сотрудник.Зарплата);
КонецЦикла;
// Поиск сотрудников с возрастом менее 30 лет
Результат2 = НайтиПоУсловию(Дерево.Строки, "Возраст < 30");
Сообщить("Сотрудники младше 30 лет:");
Для Каждого Сотрудник Из Результат2 Цикл
Сообщить(" " + Сотрудник.Наименование + " - " + Сотрудник.Возраст + " лет");
КонецЦикла;
// Поиск с составным условием
Результат3 = НайтиПоУсловию(Дерево.Строки, "Зарплата > 50000 И Возраст < 35");
Сообщить("Сотрудники с зарплатой > 50000 и возрастом < 35:");
Для Каждого Сотрудник Из Результат3 Цикл
Сообщить(" " + Сотрудник.Наименование + " - зарплата: " +
Сотрудник.Зарплата + ", возраст: " + Сотрудник.Возраст);
КонецЦикла;
КонецПроцедуры
Функция СоздатьДеревоСотрудников()
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Наименование");
Дерево.Колонки.Добавить("Зарплата", Новый ОписаниеТипов("Число"));
Дерево.Колонки.Добавить("Возраст", Новый ОписаниеТипов("Число"));
Отдел1 = Дерево.Строки.Добавить();
Отдел1.Наименование = "Отдел продаж";
Сотр1 = Отдел1.Строки.Добавить();
Сотр1.Наименование = "Иванов"; Сотр1.Зарплата = 70000; Сотр1.Возраст = 35;
Сотр2 = Отдел1.Строки.Добавить();
Сотр2.Наименование = "Петров"; Сотр2.Зарплата = 55000; Сотр2.Возраст = 28;
Отдел2 = Дерево.Строки.Добавить();
Отдел2.Наименование = "Отдел разработки";
Сотр3 = Отдел2.Строки.Добавить();
Сотр3.Наименование = "Сидоров"; Сотр3.Зарплата = 80000; Сотр3.Возраст = 32;
Сотр4 = Отдел2.Строки.Добавить();
Сотр4.Наименование = "Кузнецов"; Сотр4.Зарплата = 60000; Сотр4.Возраст = 25;
Возврат Дерево;
КонецФункции
Функция НайтиПоУсловию(СтрокиДерева, Условие)
Результат = Новый Массив;
Для Каждого Строка Из СтрокиДерева Цикл
// Проверяем условие для текущего узла
Если ВыполнитьУсловие(Строка, Условие) Тогда
Результат.Добавить(Строка);
КонецЕсли;
// Рекурсивно проверяем дочерние узлы
Если Строка.Строки.Количество() > 0 Тогда
ДочерниеРезультаты = НайтиПоУсловию(Строка.Строки, Условие);
Для Каждого Дочерний Из ДочерниеРезультаты Цикл
Результат.Добавить(Дочерний);
КонецЦикла;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Функция ВыполнитьУсловие(Строка, Условие)
// Упрощенная проверка (в реальном коде используйте Вычислить или Строка.Зарплата > 60000)
Если Условие = "Зарплата > 60000" Тогда
Возврат Строка.Зарплата > 60000;
ИначеЕсли Условие = "Возраст < 30" Тогда
Возврат Строка.Возраст < 30;
ИначеЕсли Условие = "Зарплата > 50000 И Возраст < 35" Тогда
Возврат Строка.Зарплата > 50000 И Строка.Возраст < 35;
КонецЕсли;
Возврат Ложь;
КонецФункции
Рекурсивное копирование и преобразование дерева
// Создание глубокой копии дерева с возможным преобразованием данных
Процедура РекурсивноеКопированиеДерева()
Исходное = СоздатьТестовоеДерево();
Сообщить("Исходное дерево:");
ВывестиДерево(Исходное.Строки, "");
// Копирование с преобразованием (увеличиваем сумму на 10%)
Копия = СкопироватьДеревоСПреобразованием(Исходное);
Сообщить("Копия с увеличенной на 10% суммой:");
ВывестиДерево(Копия.Строки, "");
// Копирование только узлов, удовлетворяющих условию
Фильтрованное = СкопироватьУзлыПоУсловию(Исходное, "Сумма > 500");
Сообщить("Фильтрованное дерево (сумма > 500):");
ВывестиДерево(Фильтрованное.Строки, "");
КонецПроцедуры
Функция СкопироватьДеревоСПреобразованием(ИсходноеДерево)
НовоеДерево = Новый ДеревоЗначений;
// Копируем структуру колонок
Для Каждого Колонка Из ИсходноеДерево.Колонки Цикл
НовоеДерево.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
КонецЦикла;
// Копируем строки с преобразованием
СкопироватьВеткуСПреобразованием(ИсходноеДерево.Строки, НовоеДерево.Строки);
Возврат НовоеДерево;
КонецФункции
Процедура СкопироватьВеткуСПреобразованием(ИсходныеСтроки, НовыеСтроки)
Для Каждого ИсходнаяСтрока Из ИсходныеСтроки Цикл
НоваяСтрока = НовыеСтроки.Добавить();
// Копируем значения с преобразованием
НоваяСтрока.Наименование = ИсходнаяСтрока.Наименование;
НоваяСтрока.Сумма = ИсходнаяСтрока.Сумма * 1.1; // Увеличиваем на 10%
// Рекурсивно копируем дочерние элементы
Если ИсходнаяСтрока.Строки.Количество() > 0 Тогда
СкопироватьВеткуСПреобразованием(ИсходнаяСтрока.Строки, НоваяСтрока.Строки);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция СкопироватьУзлыПоУсловию(ИсходноеДерево, Условие)
НовоеДерево = Новый ДеревоЗначений;
Для Каждого Колонка Из ИсходноеДерево.Колонки Цикл
НовоеДерево.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
КонецЦикла;
СкопироватьУзлыПоУсловиюРекурсивно(ИсходноеДерево.Строки, НовоеДерево.Строки, Условие);
Возврат НовоеДерево;
КонецФункции
Процедура СкопироватьУзлыПоУсловиюРекурсивно(ИсходныеСтроки, НовыеСтрокиРодителя, Условие)
Для Каждого ИсходнаяСтрока Из ИсходныеСтроки Цикл
// Проверяем условие для текущего узла
Если ВыполнитьУсловиеНаСтроке(ИсходнаяСтрока, Условие) Тогда
НоваяСтрока = НовыеСтрокиРодителя.Добавить();
НоваяСтрока.Наименование = ИсходнаяСтрока.Наименование;
НоваяСтрока.Сумма = ИсходнаяСтрока.Сумма;
// Рекурсивно копируем дочерние узлы (они попадут в фильтрацию)
Если ИсходнаяСтрока.Строки.Количество() > 0 Тогда
СкопироватьУзлыПоУсловиюРекурсивно(ИсходнаяСтрока.Строки, НоваяСтрока.Строки, Условие);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция ВыполнитьУсловиеНаСтроке(Строка, Условие)
Если Условие = "Сумма > 500" Тогда
Возврат Строка.Сумма > 500;
КонецЕсли;
Возврат Истина;
КонецФункции
Процедура ВывестиДерево(Строки, Отступ)
Для Каждого Строка Из Строки Цикл
Сообщить(Отступ + Строка.Наименование + " (сумма: " + Строка.Сумма + ")");
Если Строка.Строки.Количество() > 0 Тогда
ВывестиДерево(Строка.Строки, Отступ + " ");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Рекурсивный расчет агрегированных показателей
// Расчет итогов для каждого узла на основе дочерних элементов
Процедура РасчетАгрегированныхПоказателей()
Дерево = СоздатьДеревоПродаж();
Сообщить("Исходное дерево:");
ВывестиДеревоПродаж(Дерево.Строки, "");
// Рассчитываем итоги для каждого узла
РассчитатьИтогиРекурсивно(Дерево.Строки);
Сообщить("Дерево после расчета итогов:");
ВывестиДеревоСИтогами(Дерево.Строки, "");
КонецПроцедуры
Функция СоздатьДеревоПродаж()
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Наименование");
Дерево.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Дерево.Колонки.Добавить("ИтоговаяСумма", Новый ОписаниеТипов("Число"));
Дерево.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
Дерево.Колонки.Добавить("ИтоговоеКоличество", Новый ОписаниеТипов("Число"));
Год2024 = Дерево.Строки.Добавить();
Год2024.Наименование = "2024 год";
Год2024.Сумма = 0;
Год2024.Количество = 0;
Квартал1 = Год2024.Строки.Добавить();
Квартал1.Наименование = "1 квартал";
Квартал1.Сумма = 0;
Квартал1.Количество = 0;
Январь = Квартал1.Строки.Добавить();
Январь.Наименование = "Январь";
Январь.Сумма = 50000;
Январь.Количество = 10;
Февраль = Квартал1.Строки.Добавить();
Февраль.Наименование = "Февраль";
Февраль.Сумма = 60000;
Февраль.Количество = 12;
Квартал2 = Год2024.Строки.Добавить();
Квартал2.Наименование = "2 квартал";
Квартал2.Сумма = 0;
Квартал2.Количество = 0;
Март = Квартал2.Строки.Добавить();
Март.Наименование = "Март";
Март.Сумма = 70000;
Март.Количество = 14;
Возврат Дерево;
КонецФункции
Функция РассчитатьИтогиРекурсивно(СтрокиДерева)
Для Каждого Строка Из СтрокиДерева Цикл
Если Строка.Строки.Количество() > 0 Тогда
// Рекурсивно рассчитываем итоги для детей
РассчитатьИтогиРекурсивно(Строка.Строки);
// Суммируем данные детей
ИтогСумма = 0;
ИтогКоличество = 0;
Для Каждого Ребенок Из Строка.Строки Цикл
// Если у ребенка уже есть итоговые значения, берем их, иначе берем собственные
Если Ребенок.ИтоговаяСумма > 0 Или Ребенок.Строки.Количество() > 0 Тогда
ИтогСумма = ИтогСумма + Ребенок.ИтоговаяСумма;
ИтогКоличество = ИтогКоличество + Ребенок.ИтоговоеКоличество;
Иначе
ИтогСумма = ИтогСумма + Ребенок.Сумма;
ИтогКоличество = ИтогКоличество + Ребенок.Количество;
КонецЕсли;
КонецЦикла;
// Добавляем собственную сумму, если есть
Строка.ИтоговаяСумма = ИтогСумма + Строка.Сумма;
Строка.ИтоговоеКоличество = ИтогКоличество + Строка.Количество;
Иначе
// Для листовых узлов итоги равны собственным значениям
Строка.ИтоговаяСумма = Строка.Сумма;
Строка.ИтоговоеКоличество = Строка.Количество;
КонецЕсли;
КонецЦикла;
КонецФункции
Процедура ВывестиДеревоПродаж(Строки, Отступ)
Для Каждого Строка Из Строки Цикл
Сообщить(Отступ + Строка.Наименование +
" (собственные: сумма=" + Строка.Сумма + ", кол-во=" + Строка.Количество + ")");
Если Строка.Строки.Количество() > 0 Тогда
ВывестиДеревоПродаж(Строка.Строки, Отступ + " ");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура ВывестиДеревоСИтогами(Строки, Отступ)
Для Каждого Строка Из Строки Цикл
Сообщить(Отступ + Строка.Наименование +
" | собственные: с=" + Строка.Сумма + ", к=" + Строка.Количество +
" | итого: с=" + Строка.ИтоговаяСумма + ", к=" + Строка.ИтоговоеКоличество);
Если Строка.Строки.Количество() > 0 Тогда
ВывестиДеревоСИтогами(Строка.Строки, Отступ + " ");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Рекурсивное построение дерева из плоского списка
// Построение иерархического дерева из плоской таблицы с использованием рекурсии
Процедура ПостроениеДереваИзСписка()
// Исходный плоский список
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("ID", Новый ОписаниеТипов("Число"));
Таблица.Колонки.Добавить("ParentID", Новый ОписаниеТипов("Число"));
Таблица.Колонки.Добавить("Наименование");
Таблица.Колонки.Добавить("Значение", Новый ОписаниеТипов("Число"));
Таблица.Добавить().ID = 1; Таблица[0].ParentID = 0; Таблица[0].Наименование = "Корень 1"; Таблица[0].Значение = 0;
Таблица.Добавить().ID = 2; Таблица[1].ParentID = 1; Таблица[1].Наименование = "Узел 1.1"; Таблица[1].Значение = 100;
Таблица.Добавить().ID = 3; Таблица[2].ParentID = 1; Таблица[2].Наименование = "Узел 1.2"; Таблица[2].Значение = 200;
Таблица.Добавить().ID = 4; Таблица[3].ParentID = 2; Таблица[3].Наименование = "Лист 1.1.1"; Таблица[3].Значение = 50;
Таблица.Добавить().ID = 5; Таблица[4].ParentID = 2; Таблица[4].Наименование = "Лист 1.1.2"; Таблица[4].Значение = 75;
Таблица.Добавить().ID = 6; Таблица[5].ParentID = 0; Таблица[5].Наименование = "Корень 2"; Таблица[5].Значение = 0;
Таблица.Добавить().ID = 7; Таблица[6].ParentID = 6; Таблица[6].Наименование = "Узел 2.1"; Таблица[6].Значение = 300;
// Создаем соответствие для быстрого доступа к строкам по ID
ТаблицаПоИд = Новый Соответствие;
Для Каждого СтрокаТаблицы Из Таблица Цикл
ТаблицаПоИд.Вставить(СтрокаТаблицы.ID, СтрокаТаблицы);
КонецЦикла;
// Создаем дерево
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Наименование");
Дерево.Колонки.Добавить("Значение", Новый ОписаниеТипов("Число"));
Дерево.Колонки.Добавить("ID", Новый ОписаниеТипов("Число"));
// Добавляем корневые элементы
Для Каждого СтрокаТаблицы Из Таблица Цикл
Если СтрокаТаблицы.ParentID = 0 Тогда
ДобавитьВеткуРекурсивно(Дерево.Строки, СтрокаТаблицы, ТаблицаПоИд);
КонецЕсли;
КонецЦикла;
// Выводим результат
Сообщить("Построенное иерархическое дерево:");
ВывестиПостроенноеДерево(Дерево.Строки, "");
КонецПроцедуры
Процедура ДобавитьВеткуРекурсивно(РодительскиеСтроки, СтрокаДанных, ТаблицаПоИд)
// Добавляем текущую строку в дерево
НоваяСтрока = РодительскиеСтроки.Добавить();
НоваяСтрока.ID = СтрокаДанных.ID;
НоваяСтрока.Наименование = СтрокаДанных.Наименование;
НоваяСтрока.Значение = СтрокаДанных.Значение;
// Ищем и добавляем дочерние элементы
Для Каждого КлючЗначение Из ТаблицаПоИд Цикл
ПотенциальныйРебенок = КлючЗначение.Значение;
Если ПотенциальныйРебенок.ParentID = СтрокаДанных.ID Тогда
ДобавитьВеткуРекурсивно(НоваяСтрока.Строки, ПотенциальныйРебенок, ТаблицаПоИд);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура ВывестиПостроенноеДерево(Строки, Отступ)
Для Каждого Строка Из Строки Цикл
Сообщить(Отступ + Строка.Наименование + " (ID: " + Строка.ID + ", значение: " + Строка.Значение + ")");
Если Строка.Строки.Количество() > 0 Тогда
ВывестиПостроенноеДерево(Строка.Строки, Отступ + " ");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Рекурсивное удаление узлов по условию
// Удаление всех узлов, удовлетворяющих условию (с возможным удалением родителей)
Процедура РекурсивноеУдалениеУзлов()
Дерево = СоздатьДеревоДляУдаления();
Сообщить("Исходное дерево:");
ВывестиДеревоСПометками(Дерево.Строки, "");
// Удаляем узлы с суммой = 0 (пустые категории) без удаления родителей
УдалитьУзлыПоУсловию(Дерево.Строки, "Сумма = 0", Ложь);
Сообщить("После удаления узлов с суммой = 0 (без удаления родителей):");
ВывестиДеревоСПометками(Дерево.Строки, "");
// Удаляем узлы с меткой "Удалить" и их родителей, если они остались пустыми
УдалитьПустыеРодители(Дерево.Строки);
Сообщить("После удаления пустых родителей:");
ВывестиДеревоСПометками(Дерево.Строки, "");
КонецПроцедуры
Функция СоздатьДеревоДляУдаления()
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Наименование");
Дерево.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Дерево.Колонки.Добавить("ПометкаУдаления", Новый ОписаниеТипов("Булево"));
Корень1 = Дерево.Строки.Добавить();
Корень1.Наименование = "Категория 1"; Корень1.Сумма = 0; Корень1.ПометкаУдаления = Истина;
Ребенок1 = Корень1.Строки.Добавить();
Ребенок1.Наименование = "Товар 1"; Ребенок1.Сумма = 100; Ребенок1.ПометкаУдаления = Ложь;
Корень2 = Дерево.Строки.Добавить();
Корень2.Наименование = "Категория 2"; Корень2.Сумма = 0; Корень2.ПометкаУдаления = Истина;
Ребенок2 = Корень2.Строки.Добавить();
Ребенок2.Наименование = "Пустой товар"; Ребенок2.Сумма = 0; Ребенок2.ПометкаУдаления = Истина;
Возврат Дерево;
КонецФункции
Функция УдалитьУзлыПоУсловию(СтрокиДерева, Условие, УдалятьРодителей = Истина)
Для Инд = СтрокиДерева.Количество() - 1 По 0 Шаг -1 Цикл
Строка = СтрокиДерева[Инд];
// Рекурсивно обрабатываем дочерние элементы
Если Строка.Строки.Количество() > 0 Тогда
УдалитьУзлыПоУсловию(Строка.Строки, Условие, УдалятьРодителей);
КонецЕсли;
// Проверяем условие удаления
Если ВыполнитьУсловиеУдаления(Строка, Условие) Тогда
// Если узел нужно удалить
УдалитьРодительскуюСтроку(СтрокиДерева, Инд);
КонецЕсли;
КонецЦикла;
КонецФункции
Процедура УдалитьПустыеРодители(СтрокиДерева)
Для Инд = СтрокиДерева.Количество() - 1 По 0 Шаг -1 Цикл
Строка = СтрокиДерева[Инд];
// Сначала обрабатываем дочерние элементы
Если Строка.Строки.Количество() > 0 Тогда
УдалитьПустыеРодители(Строка.Строки);
// Если после удаления детей родитель остался пустым, удаляем его
Если Строка.Строки.Количество() = 0 И Строка.Сумма = 0 Тогда
УдалитьРодительскуюСтроку(СтрокиДерева, Инд);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура УдалитьРодительскуюСтроку(СтрокиДерева, Индекс)
Попытка
СтрокиДерева.Удалить(Индекс);
Исключение
// Игнорируем ошибки удаления
КонецПопытки;
КонецПроцедуры
Функция ВыполнитьУсловиеУдаления(Строка, Условие)
Если Условие = "Сумма = 0" Тогда
Возврат Строка.Сумма = 0;
КонецЕсли;
Возврат Ложь;
КонецФункции
Процедура ВывестиДеревоСПометками(Строки, Отступ)
Для Каждого Строка Из Строки Цикл
Сообщить(Отступ + Строка.Наименование +
" (сумма: " + Строка.Сумма +
", удалить: " + ?(Строка.ПометкаУдаления, "Да", "Нет") + ")");
Если Строка.Строки.Количество() > 0 Тогда
ВывестиДеревоСПометками(Строка.Строки, Отступ + " ");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Примечания
// Важные особенности рекурсивного обхода дерева значений:
// 1. Всегда проверяйте условие выхода из рекурсии (termination condition)
// 2. При рекурсивном обходе будьте внимательны с глубиной рекурсии (ограничение ~128-256 уровней)
// 3. Для очень глубоких деревьев используйте итеративный обход с использованием стека
// 4. При удалении узлов обходите дерево с конца (шаг -1) для избежания смещения индексов
// 5. Для кэширования результатов обхода используйте соответствие или массив
// 6. При формировании пути используйте разделитель и накапливайте строку на каждом уровне
// 7. Для агрегации данных (суммы, количества) обрабатывайте снизу вверх (post-order traversal)
// 8. При рекурсивном копировании не забудьте скопировать структуру колонок
// 9. Для поиска по дереву используйте ранний выход при нахождении первого элемента
// 10. Избегайте модификации дерева во время итерации (используйте копию или обратный проход)
// 11. Для отладки рекурсии выводите текущий уровень и имя узла
// 12. Потребление памяти при рекурсии растет с глубиной - учитывайте это для больших деревьев








