
- •Конспект лекций по курсу
- •Абстрактные структуры данных
- •Определение
- •Базовые структуры данных
- •Очереди и стеки
- •Деревья
- •Внутренние структуры данных
- •Отображение абстрактных структур данных на внутренние
- •Строка-вектор
- •1. Функция сцепления двух строк
- •2. Функция поэлементного сравнения двух строк
- •3. Функция разбиения строки.
- •4. Функция нахождения подстроки в строке
- •Строка-список
- •1. Сцепление двух строк
- •2. Поэлементное сравнение двух строк
- •3. Разбиение строки на части
- •4. Функция нахождения подстроки в строке
- •Стек-вектор
- •Стек-список
- •Очереди
- •Очередь-вектор
- •Очередь-список
- •Деревья
- •Классификация таблиц
- •Способ работы с таблицей
- •Способ доступа к таблице
- •Просматриваемые таблицы
- •Статическая просматриваемая таблица-вектор
- •Динамическая просматриваемая таблица-вектор
- •Просматриваемая таблица-список
- •Упорядоченные таблицы
- •Упорядоченная таблица-вектор
- •Динамическая упорядоченная таблица – вектор
- •Упорядоченная таблица – двоичное дерево
- •Перемешанные таблицы
- •Открытое перемешивание
- •Перемешивание сцеплением
Просматриваемая таблица-список
При отображении просматриваемой таблицы списком не играет роли, какую – статическую или динамическую – таблицу использовать; структура элемента таблицы (списка) будет одинакова. Поиск в таблице осуществляется простым просмотром списка и не вызывает каких-либо затруднений. При включении в таблицу новый элемент может быть размещен в любом месте таблицы (списка), поэтому удобнее (эффективнее) включать его в начало списка, используя соответствующий алгоритм стека. Здесь эта функция не приводится.
Некоторые осложнения могут возникнуть при удалении элемента из таблицы.
Если некоторый элемент удаляется из списка, действия, выполняемые при этом, могут быть разными в зависимости от того, какой элемент удаляется (рис. II–38).
Рис. II–38
Если удаляется первый элемент списка, в этом случае должен быть модифицирован указатель на начало списка (таблицы). Если же удаляется некоторый элемент из середины списка, тогда модифицируется поле указателя предыдущего элемента (рис. II–38).
Таким образом, при удалении элемента надо:
иметь доступ к предшествующему элементу списка,
различать вид предшествующего элемента.
Поскольку операция поиска возвращает искомый элемент и не сообщает о месте его размещения в списке, при реализации операции удаления элемента из списка приходится либо (a) повторять поиск элемента, либо (b) следует несколько изменить реализацию операции поиска. Способ (b) удобно реализуется средствами языка С/С++. Рассмотрим оба способа.
Алгоритм удаления элемента из просматриваемой таблицы-списка по варианту a) приведен на рис. II–39.
В приведенном алгоритме отдельно рассматриваются удаление первого и промежуточного элементов таблицы, поскольку это приводит к необходимости модифицировать разные структуры: указатель на начало таблицы при удалении первого элемента и поле указателя в элементе списка при удалении промежуточного элемента. В результате в блоке C2 сравниваются ключи первого элемента таблицы и удаляемого элемента и, при их совпадении, в блоке D2 переопределяется указатель на начало таблицы.
Рис. II–39
Если удаляется не первый элемент, осуществляется в цикле (блоки C3, D3, E3) поиск удаляемого элемента. При этом используются два указателя – на текущий (cur) и на предыдущий (prev) элементы таблицы. При совпадении ключей переопределяется поле ссылки в предыдущем элементе (на который указываетprev), в результате чего элемент удаляется из таблицы-списка.
Ниже приводится текст программы, соответствующий приведенному алгоритму.
struct Item{
int key;
Type info;
Item *next;
};
Item *ptab; /*указатель на начало таблицы */
int del1(int k)
{
Item *cur, *prev;
cur = ptab;
/*проверяем, есть ли в таблице элементы */
if(!cur)
return -1; /*таблица пуста – отказ */
/*возможно, требуется удалить первый элемент таблицы */
if(cur->key == k){
/* удаляем первый элемент */
ptab = cur->next;
delInfo(cur->info);
delete cur;
return 0;
}
/* ищем удаляемый элемент среди других элементов таблицы */
while(cur->next){ /* есть другие элементы */
prev = cur;
cur = cur->next;
if(cur->key == k){
/* нашли элемент, который надо удалить */
prev->next = cur->next;
delInfo(cur->info);
delete cur;
return 0;
}
}
/* естественный выход из цикла – в таблице нет элемента с ключом k */
return -1;
}
Эту функцию можно сократить за счет использования данных типа "указатель на указатель на ...". Введя такую переменную, мы можем записать в нее адрес указателя на элемент списка, что позволит совместить удаление первого и промежуточных элементов таблицы. Текст функции приводится ниже.
struct Item{
int key;
Type info;
Item *next;
};
Item *ptab; /*указатель на начало таблицы */
int del2(int k)
{
Item *cur, **pptr;
pptr = &ptab; /* указатель на указатель на первый элемент таблицы */
/* ищем удаляемый элемент среди всех элементов таблицы */
while(*pptr){ /* еще есть элементы */
if((*pptr)->key == k){
/* нашли элемент, который надо удалить */
cur = *pptr; /* указатель на удаляемый элемент */
*pptr = cur->next;
delInfo(cur->info);
delete cur;
return 0;
}
/* продвигаемся к следующему элементу таблицы */
pptr = &(*pptr)->next;
}
/* естественный выход из цикла – в таблице нет элемента с ключом k */
return -1;
}
Чтобы понять, как работает данная функция, рассмотрим следующую иллюстрацию (рис. II–40).
Рис. II–40
В исходном состоянии переменная pptrуказывает на элементptab– на указатель на указатель на первый элемент таблицы (списка). Таким образом, значениемpptrявляется значение указателя на переменнуюptab (адреса этой переменной); значением (результатом вычисления выражения)*pptr– значение самой переменнойptab, т.е. значение указателя на первый элемент таблицы; наконец, результатом вычисления выражения(*pptr)->keyбудет ключ первого элемента таблицы.
Если ключ элемента совпадает с заданным, значит, найден удаляемый элемент. Тогда: переменной curприсваивается значение*pptr, т.е. значение указателя на этот элемент таблицы;cur->nextопределяет следующий элемент таблицы, указатель на который должен быть записан вptab, т.е. в*pptr. Указатели переопределяются, и найденный элемент удаляется из списка.
Если ключи не совпадают, надо продолжить поиск в таблице. Для этого переменной pptr присваивается значение указателя на полеnextтекущего элемента. Этим обеспечивается, что*pptrуказывает на следующий элемент таблицы, и все вычисления выполняются аналогично рассмотренным выше (*pptrуказывает на второй элемент таблицы,(*pptr)->keyопределяет ключ второго элемента,(*pptr)->next– указатель на следующий, третий элемент таблицы, и так далее).
Тексты функций приведены также в файле tab1lst.cpp.
Если разработать функцию поиска элемента в таблице, которая возвращает указатель на указатель на найденный элемент, ее можно будет использовать в функции удаления элемента из таблицы. Реализацию данного варианта функций предлагаю в качестве самостоятельной работы.