Top.Mail.Ru
График: 5/2, full-time
Формат: удаленный/офис
Вакансия «1С-программист»

Обход дерева без рекурсии

В очередной раз увидел, как обход дерева рекурсией приводит к бесконечной вложенности и завершению работы по ошибке. Поэтому предлагаю шпаргалку для отказа от рекурсии за счет циклов. Если дерево замкнуто, то и в моем алгоритме тоже может возникнуть бесконечный цикл. Как от него избавляться каждый решает самостоятельно: проверка уровня вложенности, проверка всех узлов на зацикленность или что-то еще.

Чтобы отказаться от рекурсии важно обходить дерево в ширину, плоско по уровням:

Например, у нас есть дерево, где одному руководителю назначается несколько сотрудников, а в результате мы хотим получить плоскую таблицу, в которой будут: Руководитель, его Сотрудник, Уровень руководителя и Уровень сотрудника. В этом случае код будет следующий:

Функция РазложитьДеревоВТаблицуСУровнемИерархии(Знач Дерево) Экспорт 
	
	ТаблицаИерархии = Новый ТаблицаЗначений;
	ТаблицаИерархии.Колонки.Добавить("УровеньРуководителя");
	ТаблицаИерархии.Колонки.Добавить("УровеньСотрудника");
	ТаблицаИерархии.Колонки.Добавить("Сотрудник");
	ТаблицаИерархии.Колонки.Добавить("Руководитель");
	
	МассивСтрок = Новый Массив;
	Для Каждого СтрДерева Из Дерево.Строки Цикл
		МассивСтрок.Добавить(СтрДерева);
	КонецЦикла;
	
	Пока МассивСтрок.Количество() > 0 Цикл
		// тут можно вызвать общий запрос для всего уровня дерева... 
		
		МассивСтрокНовый = Новый Массив;
		Для Каждого СтрДерева Из МассивСтрок Цикл
			// обработка данных элемента дерева ....
			
			Если ЗначениеЗаполнено(СтрДерева.Сотрудник) Тогда
				
				Родитель = СтрДерева.Родитель;
				Пока Родитель <> Неопределено Цикл
					
					НоваяСтрока = ТаблицаИерархии.Добавить();
					НоваяСтрока.УровеньРуководителя = Родитель.Уровень();
					НоваяСтрока.УровеньСотрудника = СтрДерева.Уровень();
					НоваяСтрока.Сотрудник = СтрДерева.Сотрудник;
					НоваяСтрока.Руководитель = Родитель.Сотрудник;
					
					Родитель = Родитель.Родитель;
					
				КонецЦикла;

				Если Родитель = Неопределено И СтрДерева.Родитель = Неопределено Тогда
					НоваяСтрока = ТаблицаИерархии.Добавить();
					НоваяСтрока.УровеньРуководителя = 0;
					НоваяСтрока.УровеньСотрудника = 0;
					НоваяСтрока.Сотрудник = СтрДерева.Сотрудник;
					НоваяСтрока.Руководитель = Справочники.Сотрудники.ПустаяСсылка();
				КонецЕсли;
				
			КонецЕсли;

			// Тут делаем псевдо рекурсию
			Для Каждого СтрДереваНовый Из СтрДерева.Строки Цикл
				МассивСтрокНовый.Добавить(СтрДереваНовый);
			КонецЦикла;
		КонецЦикла;
		
		МассивСтрок = Новый Массив;
		Для Каждого СтрДерева Из МассивСтрокНовый Цикл
			МассивСтрок.Добавить(СтрДерева);
		КонецЦикла;
	КонецЦикла;
	
	Возврат ТаблицаИерархии;
	
КонецФункции

В сети много подобных описаний, но хочется все иметь в оном месте, поэтому добавляю данную статью в нашу библиотеку разработчика.

Полезные материалы

Вывод дерева в табличный документ СКД (первый комментарий)
Поиск в ширину
Поиск в глубину

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar