
- •Конспект лекций по курсу
- •Абстрактные структуры данных
- •Определение
- •Базовые структуры данных
- •Очереди и стеки
- •Деревья
- •Внутренние структуры данных
- •Отображение абстрактных структур данных на внутренние
- •Строка-вектор
- •1. Функция сцепления двух строк
- •2. Функция поэлементного сравнения двух строк
- •3. Функция разбиения строки.
- •4. Функция нахождения подстроки в строке
- •Строка-список
- •1. Сцепление двух строк
- •2. Поэлементное сравнение двух строк
- •3. Разбиение строки на части
- •4. Функция нахождения подстроки в строке
- •Стек-вектор
- •Стек-список
- •Очереди
- •Очередь-вектор
- •Очередь-список
- •Деревья
- •Классификация таблиц
- •Способ работы с таблицей
- •Способ доступа к таблице
- •Просматриваемые таблицы
- •Статическая просматриваемая таблица-вектор
- •Динамическая просматриваемая таблица-вектор
- •Просматриваемая таблица-список
- •Упорядоченные таблицы
- •Упорядоченная таблица-вектор
- •Динамическая упорядоченная таблица – вектор
- •Упорядоченная таблица – двоичное дерево
- •Перемешанные таблицы
- •Открытое перемешивание
- •Перемешивание сцеплением
4. Функция нахождения подстроки в строке
Прототип функции может иметь следующий вид:
char * substr(char *string1, char *string2);
Функция возвращает указатель на первое вхождение подстроки string2в строкуstring1илиNULL, если подстрокаstring2не входит в строкуstring1. Схема алгоритма функции приведена на рис.II–17.
Рис. II–17
Нужно обратить внимание на следующее: так как внутренний цикл может закончиться по любому из двух условий (блоки D2 – найден конец второй строки (строки 2) или D3 – обнаружены несовпадающие символы строк), на выходе требуется дополнительная проверка (блок E2). Если цикл закончился по условию обнаружения конца строки 2, это означает, что все символы строки 2 совпадали с соответствующими символами первой (строки 1), а следовательно, строка 2 является подстрокой строки 1, и функция завершается. Если же внутренний цикл закончился по другому условию, значит, нужно продолжать проверку.
Можно было сократить количество итераций внешнего цикла, если проверять не просто нуль-байт в конце первой строки, а учитывать длину второй строки; тогда, если вторая строка окажется длиннее первой, цикл не будет выполнен ни разу (такой вариант функции, конечно, предпочтительнее).
Текст функции приводится ниже и в файле stringv.cpp.
char *substr(char *string1, char *string2)
{
char *ptr;
int i, l = strlen(string2) - 1;
for(ptr = string1; *(ptr + l); ptr++){
for(i = 0; string2[i] && ptr[i] == string2[i]; i++)
;
if(string2[i] == '\0')
return ptr;
}
return NULL;
}
Строка-список
Строка может быть отображена линейным односвязным или двусвязным списком, что наиболее соответствует смыслу строки. В этом случае каждый элемент списка отображает один элемент строки. Если элемент строки имеет фиксированный размер, тогда он (элемент) может быть размещен непосредственно в элементе списка (пример – строка символов). Если же элементы строки имеют переменный размер, тогда в элементе списка размещается указатель на элемент строки. В этом случае информация о длине такого элемента может быть включена или в элемент списка, или в сам элемент строки. Примером может служить строка слов, в которой слова имеют разные длины и каждое слово представляется нуль ограниченным массивом символов. Представление строки списком может усложнить или, наоборот, упростить отдельные алгоритмы обработки строк (например, сравнение строк)
Рассмотрим реализацию тех же функций обработки строк:
сцепление двух строк,
поэлементное сравнение двух строк,
разбиение строки на части,
нахождение подстроки.
1. Сцепление двух строк
Исходные строки заданы списками; в результате сцепления получаем объединенный список, в котором вторая строка добавлена в конец первой. Функция возвращает указатель на результирующую строку-список.
Возможны два способа реализации данной функции.
Первый способ: вторая строка непосредственно подцепляется к концу первой строки (рис. II–18). Если первая строка задана пустым списком, результатом будет вторая строка. В результате работы функции вторая строка перестает существовать, что необходимо учитывать в вызывающих программах. Во избежание возможных недоразумений было бы полезно так реализовать функцию, чтобы она модифицировала указатель на второй список.
Рис. II–18
Алгоритм функции приведен на рис. II–19.
Рис. II–19
В результате работы функции меняются указатели и на первую строку, и на вторую, поэтому оба параметра целесообразно определить как ссылки.
Второй способ: в конец первой строки добавляются (копируются) символы из второй строки, при этом вторая строка сохраняется. В этом случае возможно изменение только указателя на первую строку, поэтому только один (первый) параметр определяется как ссылка.
Тексты функции приводятся ниже и в файле stringl.cpp.
struct Item{
char s;
Item *next;
};
Item *concat1(Item *&str1, Item *&str2)
{
Item *cur;
/* сначала проверим, что первый список не пуст */
if(!str1)
str1 = str2;
else{
/* формируем указатель на последний элемент первого списка */
for(cur = str1; cur->next; cur = cur->next)
;
/* включаем второй список в конец первого */
cur->next = str2;
}
str2 = NULL;
return str1;
}
Используя возможности языка С/С++, эту функцию можно реализовать более компактно, не проверяя специально условие, когда первый список пуст:
Item *concat1m(Item *&str1, Item *&str2)
{
Item **cur;
/* формируем указатель на поле указателя в последнем элементе списка */
for(pcur = &str1; *cur; cur = &(*cur)->next)
;
/* включаем второй список в конец или вместо первого */
*cur = str2;
str2 = NULL;
return str1;
}
Item *concat2(Item *&str1, Item *str2)
{
Item **cur;
/* формируем указатель на поле указателя в последнем элементе списка */
for(cur = &str1; *cur; cur = &(*cur)->next)
;
/* добавляем элементы второй строки в конец или вместо первого */
for(; str2; str2 = str2->next){
*cur = new Item;
(*cur)->s = str2->s;
(*cur)->next = NULL;
cur = &(*cur)->next;
}
return str1;
}