Интерфейс отображений
Предположим, необходимо создать отображение, в котором и ключ, и значение являются целыми числами. Ниже представлен заголовочный файл с интерфейсом такого АТД:
#ifndef _INTEGER_MAP_HPP_
#define _INTEGER_MAP_HPP_
// Форвардное объявление структуры-отображения
struct IntegerMap;
// Создание нового объекта-отображения
IntegerMap * IntegerMapCreate ();
// Уничтожение объекта-отображения
void IntegerMapDestroy ( IntegerMap * _pMap );
// Очистка отображения
void IntegerMapClear ( IntegerMap & _map );
// Проверка отображения на пустоту
bool IntegerMapIsEmpty ( const IntegerMap & _map );
// Проверка отображения на содержание указанного ключа
bool IntegerMapHasKey ( const IntegerMap & _map, int _key );
// Поиск значения по указанному ключу в отображении
int IntegerMapFind ( const IntegerMap & _map, int _key );
// Вставка пары ключ-значение в отображение
void IntegerMapInsertKey ( IntegerMap & _map, int _key, int _value );
// Удаление элемента с указанным ключом из отображения
void IntegerMapRemoveKey ( IntegerMap & _map, int _key );
#endif // _INTEGER_MAP_HPP_
Можно представить простую тестовую программу, использующую отображения данного типа:
#include "integer_map.hpp"
#include <cassert>
int main ()
{
// Создаем объект-отображение
IntegerMap * pMap = IntegerMapCreate();
// Вставляем в отображение 3 пары ключ-значение
IntegerMapInsertKey( * pMap, 10, 100 );
IntegerMapInsertKey( * pMap, 20, 200 );
IntegerMapInsertKey( * pMap, 30, 300 );
// Проверяем корректность поиска в отображении
assert( IntegerMapFind( * pMap, 20 ) == 200 );
// Удаляем элемент по одному из ключей
IntegerMapRemoveKey( * pMap, 30 );
// Убеждаемся, что такого элемента больше нет в отображении
assert( ! IntegerMapHasKey( * pMap, 30 ) );
// Уничтожаем объект-отображение
IntegerMapDestroy( pMap );
}
Реализация отображений при помощи векторов
Самой простой, но не слишком эффективный, способ реализации отображений состоит в использовании векторов и простого алгоритма поиска ключей, основанного на полном переборе. Представляется возможным создать вектор, хранящий пары ключ-значение в виде несложной вложенной структуры:
struct IntegerPairVector
{
int m_nUsed;
int m_nAllocated;
struct Cell
{
int m_key;
int m_value;
};
Cell * m_pData;
};
Однако такое решение потребует очередной реализации всех функций для работы с вектором с учетом нового типа хранимых данных в виде пары ключ-значение, что без специальных языковых средств, таких как шаблоны (template), может быть утомительным.
Простое и элегантное решение, повторно использующее наработки из предыдущих лекций, можно построить задействовав отдельные вектора для ключей и значений. Если при вставке пар ключ-значение одновременно вносить данные в каждый из векторов, до в дальнейшем, при поиске значения по ключу, их позиции в обоих векторах совпадут. В случае удаления элемента ключ и значение также следует удалять из векторов одновременно, чтобы не нарушить порядок позиций всех остальных пар ключ-значение:

При вставке очередной пары ключ-значение следует проверять существует ли уже в отображении такой же ключ, и в случае его наличия, можно перезаписать существующую позицию новым значением.
Ниже представлена реализация отображения, основанная на предложенной идее:
#include "integer_map.hpp"
#include "integer_vector.hpp"
#include <cassert>
// Структура для отображения из двух отдельных векторов для ключей и значений
struct IntegerMap
{
IntegerVector m_keys;
IntegerVector m_values;
};
// Создание объекта-отображения
IntegerMap * IntegerMapCreate ()
{
// Создаем объект-отображение в динамической памяти
IntegerMap * pMap = new IntegerMap;
// Инициализируем оба внутренних вектора
IntegerVectorInit( pMap->m_keys );
IntegerVectorInit( pMap->m_values );
// Возвращаем объект во внешний код
return pMap;
}
// Уничтожение объекта-отображения
void IntegerMapDestroy ( IntegerMap * _pMap )
{
// Освобождаем ресурсы внутренних векторов
IntegerVectorDestroy( _pMap->m_keys );
IntegerVectorDestroy( _pMap->m_values );
// Удаляем сам объект-отображение
delete _pMap;
}
// Очистка отображения
void IntegerMapClear ( IntegerMap & _map )
{
// Очищаем оба внутренних вектора
IntegerVectorClear( _map.m_keys );
IntegerVectorClear( _map.m_values );
}
// Проверка отображения на пустоту
bool IntegerMapIsEmpty ( const IntegerMap & _map )
{
// Отображение пусто, если пусты внутренние векторы.
// Поскольку векторы модифицируются всегда одновременно,
// достаточно проверить один из векторов на пустоту, например, вектор ключей
return IntegerVectorIsEmpty( _map.m_keys );
}
// Вспомогательная функция для поиска позиции ключа в отображении
int IntegerMapFindKeyPosition ( const IntegerMap & _map, int _key )
{
// Ищем позицию ключа в векторе ключей путем полного перебора
for ( int i = 0; i < _map.m_keys.m_nUsed; i++ )
if ( _map.m_keys.m_pData[ i ] == _key )
// Позиция найдена
return i;
// Позиция не найдена
return -1;
}
// Проверка отображения на содержание указанного ключа
bool IntegerMapHasKey ( const IntegerMap & _map, int _key )
{
// Ключ есть в отображении, если поиск его позиции дает корректный ответ
return IntegerMapFindKeyPosition( _map, _key ) != -1;
}
// Поиск значения по указанному ключу в отображении
int IntegerMapFind ( const IntegerMap & _map, int _key )
{
// Ищем позицию ключа в векторе ключей
int position = IntegerMapFindKeyPosition( _map, _key );
// Ключ должен существовать!
assert( position != -1 );
// Используем найденную позицию для извлечения значения из вектора значений
return _map.m_values.m_pData[ position ];
}
// Вставка пары ключ-значение в отображение
void IntegerMapInsertKey ( IntegerMap & _map, int _key, int _value )
{
// Ищем позицию ключа в векторе ключей. Возможно такой ключ уже существует
int position = IntegerMapFindKeyPosition( _map, _key );
// Если такого ключа нет, добавляем ключ и значение в соответствующие вектора
if ( position == - 1 )
{
IntegerVectorPushBack( _map.m_keys, _key );
IntegerVectorPushBack( _map.m_values, _value );
}
else
// В противном случае обновляем значение по существующему ключу
_map.m_values.m_pData[ position ] = _value;
}
// Удаление элемента с указанным ключом из отображения
void IntegerMapRemoveKey ( IntegerMap & _map, int _key )
{
// Ищем позицию ключа в векторе ключей
int position = IntegerMapFindKeyPosition( _map, _key );
// Ключ должен существовать!
assert( position != -1 );
// Одновременно удаляем ключ и значение из векторов в найденной позиции
IntegerVectorDeleteAt( _map.m_keys, position );
IntegerVectorDeleteAt( _map.m_values, position );
}
