Массив меток и заголовок со списками дочерних узлов.

 

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

 

Эта структура также предполагает изначальную фиксацию количества узлов в дереве.

 

structTree

{

// Количество узлов в дереве

intm_nNodes;

 

// Массив меток узлов, тип выбирается в зависимости от задачи

char* m_pNodeLabels;

 

// Структура, представляющая элемент связного списка индексов дочерних узлов

structChildIndexElement

{

intm_childIndex;

ChildIndexElement * m_pNext;

};

 

// Заголовок - указатели на первые элементы список дочерних узлов

ChildIndexElement ** m_pHeader;

};

 

Ниже показано графическое представление состояния реализованной структуры данных после формирования дерева-примера.

 

 

Функция создания дерева из N узлов реализуется следующим образом (за исключением заголовка вместо массива индексов родительских узлов - идентично предыдущей структуре данных:

 

Tree * TreeCreate ( int_nNodes )

{

// Число узлов должно быть положительным

assert( _nNodes > 0 );

 

// Создаем объект-дерево в динамической памяти

Tree * pTree = newTree;

pTree->m_nNodes = _nNodes;

 

// Создаем массив меток

pTree->m_pNodeLabels = newchar[ _nNodes ];

memset( pTree->m_pNodeLabels, 0, _nNodes );

 

// Создаем массив указателей на списки узлов-сыновей

pTree->m_pHeader = newTree::ChildIndexElement * [ _nNodes ];

memset( pTree->m_pHeader, 0, sizeof( Tree::ChildIndexElement * ) * _nNodes );

 

// Возвращаем созданное дерево

returnpTree;

}

 

Функция освобождения памяти чуть усложняется из-за работы с динамически выделяемыми элементами списков индексов дочерних узлов:

 

voidTreeDestroy ( Tree * _pTree )

{

// Уничтожаем метки узлов

delete[] _pTree->m_pNodeLabels;

 

// Уничтожаем списки узлов-сыновей

for( inti = 0; i < _pTree->m_nNodes; i++ )

{

Tree::ChildIndexElement * pCurrent = _pTree->m_pHeader[ i ];

while( pCurrent )

{

Tree::ChildIndexElement * pTemp = pCurrent->m_pNext;

deletepCurrent;

pCurrent = pTemp;

}

}

 

// Уничтожаем массив указателей на списки узлов-сыновей

delete[] _pTree->m_pHeader;

 

// Уничтожаем само дерево

delete_pTree;

}

 

Функции получения индекса корневого узла и работы с метками узлов реализуются полностью идентично структуре с массивом индексов родительских узлов.

 

Данная структура уменьшает вычислительную сложность реализации операции LEFTMOST_CHILD с линейной до константной за счет специальных структурных связей от родителя к дочерним узлам:

 

intTreeGetLeftmostChildIndex ( constTree & _tree, int_nodeIndex )

{

assert( _nodeIndex < _tree.m_nNodes );

 

Tree::ChildIndexElement * pCurrent = _tree.m_pHeader[ _nodeIndex ];

returnpCurrent ? pCurrent->m_childIndex : -1;

}

 

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

 

Напротив, обратная задача получения индекса родительского узла по индексу дочернего в данной реализации усложняется от константной вычислительной сложности до линейной, поскольку прямых структурных связей более не предусмотрено, и поиск будет осуществляться сканирование списков дочерних узлов всех вышестоящих по иерархии узлов-родителей:

 

intTreeGetParentIndex ( constTree & _tree, int_nodeIndex )

{

// Проверяем корректность индекса

assert( _nodeIndex < _tree.m_nNodes );

 

// Ищем родительский узел, перебирая все списки сыновей

for( inti = 0; i < _nodeIndex; i++ )

{

Tree::ChildIndexElement * pCurrent = _tree.m_pHeader[ i ];

while( pCurrent )

{

if( pCurrent->m_childIndex == _nodeIndex )

// Искомый узел найден, соответственно, это был список узла-родителя

returni;

 

pCurrent = pCurrent->m_pNext;

}

}

 

// Родительский узел не найден

return-1;

}

 

Аналогично, не имея индекса родительского узла, получение индекса ближайшего правого братского узла также усложняется до линейного поиска:

 

intTreeGetRightSiblingIndex ( constTree & _tree, int_nodeIndex )

{

// Проверяем корректность индекса

assert( _nodeIndex < _tree.m_nNodes );

 

// Ищем родительский узел, перебирая все списки сыновей

for( inti = 0; i < _nodeIndex; i++ )

{

Tree::ChildIndexElement * pCurrent = _tree.m_pHeader[ i ];

while( pCurrent )

{

if( pCurrent->m_childIndex == _nodeIndex )

{

// Искомый узел найден, соответственно, это был список узла-родителя.

// Следующий узел-сын известен по непосредственной структурной связи

Tree::ChildIndexElement * pNext = pCurrent->m_pNext;

returnpNext ? pNext->m_childIndex : -1;

}

pCurrent = pCurrent->m_pNext;

}

}

 

// Братский узел не найден

return-1;

}

 

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

 

voidTreeSetParentIndex ( Tree & _tree, int_nodeIndex, int_parentIndex )

{

// Проверка корректности индексов

assert( _nodeIndex < _tree.m_nNodes );

assert( _parentIndex < _nodeIndex );

 

// Создаем новый узел-сын

Tree::ChildIndexElement * pNewElement = newTree::ChildIndexElement;

pNewElement->m_childIndex = _nodeIndex;

pNewElement->m_pNext = nullptr;

 

// Является ли этот узел первым сыном для родительского узла?

Tree::ChildIndexElement * pCurrent = _tree.m_pHeader[ _parentIndex ];

if( ! pCurrent )

{

// Да, формируем первый элемент списка узлов-сыноей

_tree.m_pHeader[ _parentIndex ] = pNewElement;

return;

}

 

// В противном случае, прикрепляем новый узел к концу списка

while( pCurrent->m_pNext )

pCurrent = pCurrent->m_pNext;

pCurrent->m_pNext = pNewElement;

}

 

Поскольку интерфейс функций, реализующих АТД “Дерево”, идентичен по сравнению с предыдущей рассмотренной структурой данных, то сформировать и обойти рассматриваемое дерево-пример способна та же самая тестовая программа без малейшей модификации.

 








Дата добавления: 2016-01-29; просмотров: 678;


Поиск по сайту:

При помощи поиска вы сможете найти нужную вам информацию.

Поделитесь с друзьями:

Если вам перенёс пользу информационный материал, или помог в учебе – поделитесь этим сайтом с друзьями и знакомыми.
helpiks.org - Хелпикс.Орг - 2014-2024 год. Материал сайта представляется для ознакомительного и учебного использования. | Поддержка
Генерация страницы за: 0.02 сек.