Табличные структуры
[Оглавление] [<< страница] [>>страница]


   5.3. Основные операции и возможная структура древовидной таблицы
 

Основными функциональными операциями над древовидными таблицами являются:

Эти операции по выполняемым функциям схожи с аналогичными операциями над бинарными деревьями поиска, однако aлгоритмы реализации их существенно различны. Это вызвано тем, каждый узел бинарного дерева поиска размещается в динамической памяти, получаемой при включении узла и освобождаемой при его удалении. Узлы же бинарного дерева в древовидной таблице размещаются в массиве (в векторной памяти). Это вызывает необходимость ведения списков занятых и свободных элементов массива. Список занятых элементов определяется указателями в узлах, начиная с корня, для ведения списка свободных элементов можно использовать один из указателей в свободных элементах. Для каждого списка необходим указатель на его начало.

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

Исходя из этого можно предложить такую структуру таблицы, в которой первые две строки заняты управляющей информацией. Массив имеет n элементов, первые два из которых содержат управляющую информацию, остальные m=n-2 предназначены для элементов двоичного дерева поиска.
Таблица.
Указатель на корень Пусто Указатель списка свободных элементов
Счетчик занятых Пусто Размер таблицы m
Левый указатель Ключ + данные Правый указатель/ Указатель свободного элемента
... ... ...
... ... ...

Инициализация таблицы. При инициализации таблицы обнуляются все левые указатели, включая указатель на корень и счетчик занятых элементов, очищаются поля ключей и данных. В указатель списка свободных элементов заносится индекс первой свободной строки, т.е. 2. Размер таблицы m устанавливается равным либо m = n (число строк массива), либо m = n - 2 (максимальное число узлов дерева). Правые указатели, начиная с третьей строки массива (с индексом i = 2), должны показывать на последующие строки (3, 4, ..., n). Правый указатель в последней строке можно обнулять, так как возможность включения нового элемента определяется не концом списка свободных элементов, а счетчиком занятых элементов. В качестве примера рассмотрим табл., в которой поле «Ключ+данные» представлено только ключом, имеющим положительные целочисленные значения, n= 15, m = 13. Таблица 6.2
0 0 2
0 0 13
0 0 3
0 0 4
... ... ...
0 0 i+1
... ... ...
0 0 14
0 0 15

Пусть элементы таблицы имеют структуру
struct KR
{ int L;     /* Левый указатель */
int key;    /* Поле ключа */
int R;       /* Правый указатель */
} Т[15];     /* Массив под таблицу */

Включение элемента в таблицу. Примем, что запись с дублирующим ключом в таблицу не включается.

Исходные данные для включения — таблица Т и ключ key >0.
Состояние таблицы в момент включения:

Состояние а) — таблица полная, следовательно, счетчик занятых элементов равен размеру таблицы: T[1].L=T[1].R. Возврат – 2.
Состояние б) — таблица пустая, т.е. Т[1].L =0.
Состояние в) — таблица заполнена частично.
Поиск элемента по ключу. Исходные данные: таблица Т и ключ key. Состояния таблицы: а) таблица пустая, б) не пустая. Удаление элемента. Исходные данные: таблица Т и ключ key. При каждом движении по дереву в поисках записи с заданным ключом запоминаем адрес предыдущего указателя. Состояния таблицы: таблица пуста или не пуста. Если табли¬ца пуста, T.e.T[l].L= =0, то возврат — 2. Если таблица не пуста, то осуществляется поиск ключа. Если не найден, то возврат — 1.
Ключ найден в строке j, возможные положения узла в дереве:

а) Узел не имеет потомков.
Если узел является корнем дерева, то:

Можно было поступить проще. Так как с удалением корня дерево становится пустым, достаточно заново инициализировать таблицу. Если же узел является листом дерева, то обнуляем предыдущий указатель, затем выполняем пункты 2—5.

б) Узел дерева с двумя потомками.

в) Узел дерева с одним потомком.
Если удаляемый узел является корнем дерева, то в указатель списка занятых заносим индекс следующего элемента (правого или левого) по дереву, затем выполняем пункты а) 2—5.

Если же удаляемый элемент является промежуточным узлом дерева, то в указатель предыдущего элемента заносим индекс следующего элемента, затем опять-таки выполняем пункты а) 2-

Отметим, что при удалении любого узла выполняются пункты а) 2—5.
Сохранение таблицы в файле . Исходными данными являю таблица Т и имя файла. Естественно, сохраняются только не пустые таблицы.

Восстановление таблицы из файла . Исходные данные файла. Возвращаемое значение — указатель на динамическую таблицу либо NULL в случае неудачи.