§ 21. Структуры данных
Сайт: | Профильное обучение |
Курс: | Информатика. 10 класс (Повышенный уровень) |
Книга: | § 21. Структуры данных |
Напечатано:: | Гость |
Дата: | Воскресенье, 22 Декабрь 2024, 08:14 |
21.1. Понятие о структурах данных
В информатике структура данных — программная единица, позволяющая хранить и обрабатывать данные, а также обеспечивающая их эффективное использование. Данные при этом должны быть однотипными или логически связанными. Различные виды структур данных подходят для различных задач; некоторые из них имеют узкую специализацию, другие являются универсальными (пример 21.1). При разработке программного обеспечения сложность реализации и качество работы программ существенно зависят не только от выбора алгоритма, но и от правильного выбора структур данных. Одни и те же данные можно сохранить в структурах, требующих различного объема памяти, а алгоритмы работы с разными структурами данных могут иметь различную эффективность. Структура данных, наиболее подходящая для решения конкретной задачи, позволяет выполнять большое количество различных операций, используя как можно меньший объем ресурсов. Ни одна профессиональная программа сегодня не пишется без использования структур данных, поэтому многие из них содержатся в стандартных библиотеках современных языков программирования (например, STL для С++). Структура данных представляет собой набор значений данных, отношения между ними, а также функции и (или) операции, которые могут быть применены к данным. Структуры данных классифицируют по разным признакам. В примере 21.2 приведена классификация структур данных по организации взаимосвязей между элементами. Далее будут рассмотрены некоторые линейные структуры данных. |
Пример 21.1. Примеры некоторых структур данных: 1. Массив (вектор в C++) — самая простая и широко используемая структура данных. 2. Запись (структура в С++) — совокупность элементов данных разного типа. Содержит постоянное количество элементов, которые называют полями. 3. Связный список (Linked List) представляет набор связанных узлов, каждый из которых хранит собственно данные и ссылку на следующий узел. В реальной жизни связный список можно представить в виде поезда, каждый вагон которого может содержать некоторый груз или пассажиров и при этом может быть связан с другим вагоном. 4. Граф — совокупность вершин (узлов) и связей между ними (ребер). В реальной жизни по такому принципу устроены социальные сети: узлы — это люди, а ребра — их отношения. 5. Множество — совокупность данных, значения которых не повторяются, реализация математического объекта множество, за исключением того, что множество в программировании конечно. Пример 21.2. Классификация структур данных. Линейные:
Иерархические:
Сетевые:
Табличные:
|
21.2. Список
Список — динамическая линейная структура данных, хранящая конечную последовательность элементов, порядок которых определяется с помощью ссылок. Список является динамической линейной структурой данных, в которой каждый элемент ссылается либо только на следующий — однонаправленный линейный список (пример 21.3), либо на предыдущий и следующий за ним — двунаправленный линейный список (пример 21.4). Список представляет собой последовательность связанных структур. Каждая структура содержит ссылку, связывающую ее с другой структурой, входящей в список. Обычно структура состоит из двух смысловых частей — информационной и дополнительной. Информационная часть содержит данные, в дополнительной находятся указатели на последующую или предыдущую структуру списка. Порядок элементов определяется ссылкой на первый элемент. Такая организация структуры данных позволяет последовательно перемещаться по ссылкам от одного элемента списка к другому. Основные операции, производимые над списком, перечислены в примере 21.5. Добавление в список элемента, следующего за заданным, включает в себя следующие этапы:
Удаление из списка элемента, следующего за заданным, включает в себя следующие этапы:
Поиск элемента x в линейном списке осуществляется последовательным просмотром элементов. Он заканчивается либо при обнаружении искомого элемента, либо при достижении конца списка. Поиск в списке аналогичен поиску в линейном массиве, только обращение к элементам осуществляется не по индексу, а по указателю. Если воспользоваться структурой данных list, входящей в STL, то все моменты, связанные с хранением элементов списка, а также с выделением и освобождением памяти, будут решаться самой структурой данных. Для пользователя операции доступны в виде функций, присущих данному классу. Для работы со списком необходимо подключить библиотеку <list>. Описание списка: list <тип данных> имя переменной; В библиотеке list реализован двунаправленный список, который является более общим, чем однонаправленный, и поддерживает большее количество операций. Если необходима работа с однонаправленным списком, можно использовать тип данных forward_list с подключением одноименной библиотеки. В примере 21.8 перечислены некоторые операции, доступные для работы со списком. С другими можно ознакомиться в Приложение к главе 1.1. Многие функции аналогичны функциям, определенным для типа данных vector. Пример 21.9. В файле хранится список фамилий. Написать программу, которая отсортирует фамилии в списке, найдет номер человека по фамилии Королев, вставит перед ним фамилию Иванов и удалит фамилию после Королева (Королев не последний в списке). Преобразованный список вывести в файл. Этапы выполнения задания I. Исходные данные: переменные n — количество фамилий в списке, spis — список. II. Результат: номер Королева и преобразованный список. III. Алгоритм решения задачи. 1. Ввод исходных данных. Будем считывать строку из файла и добавлять ее в конец списка, используя функцию push_back. 2.1. Для сортировки списка используем функцию sort. Поскольку список нужно сортировать по возрастанию, компаратор не потребуется. 3. Вывод результата. IV. Описание переменных: n, n1, – int, spis – list<string>. |
Списки отличаются от массивов тем, что доступ к их элементам осуществляется последовательно, в то время как массивы – структура данных произвольного доступа (предоставляет возможность в любой момент времени обратиться к любому элементу по его индексу). Элементы связанного списка (в отличие от элементов массива) не обязаны располагаться в памяти друг за другом. Связные списки иногда называют самоссылочными (self-referent) структурами. Хотя ссылка элемента обычно указывает на другой элемент, возможны и ссылки на себя (прямо или косвенно, через другие элементы), поэтому связные списки могут представлять собой циклические (cyclic) структуры. Пример 21.5. Основные операции над списком: 1. Инициализация списка. Пример 21.6. Добавление элемента в однонаправленный список: Пример 21.7. Удаление элемента из однонаправленного списка: Пример 21.8. Некоторые операции для типа данных list.
Списки оптимизированы для выполнения операций по добавлению и удалению элементов. Если мы знаем месторасположение элемента, то для вставки или удаления элемента достаточно изменить значения указателей. Сами элементы при этом не перемещаются (в отличие от вставки и удаления элементов в массиве). Для поиска элемента x в списке понадобится (в худшем случае) порядка n операций сравнения. V. Программа:
VI. Тестирование. VII. Анализ результатов. В итоговом списке Королев имеет номер 40, поскольку перед ним вставили Иванова. До вставки номер был 39. |
21.3. Стек
Стек — динамическая линейная структура данных, хранящая последовательность элементов, в которой размещение новых и удаление существующих происходит с одного конца, называемого вершиной стека (top). Стек организован по принципу LIFO (англ. last in — first out, «последним пришел — первым ушел»). Для стека определены операции добавления элемента и удаления элемента (пример 21.10). По своей организации стек является линейным списком, добавление и удаление элементов в который допускается только в начало списка. В STL для работы со стеком определен тип данных stack, для чего подключается одноименная библиотека. Описание стека: stack <тип данных> имя переменной; В примере 21.11 перечислены операции, доступные для работы со стеком. Большинство функций для работы со стеком имеют аналоги для типа данных list. Пример 21.12. Правильная скобочная последовательность — последовательность, состоящая из символов — «скобок», в которой каждой открывающейся скобке соответствует закрывающаяся скобка такого же типа, что и открывающаяся скобка. Проверить, является ли заданная скобочная последовательность правильной. Этапы выполнения задания I. Исходные данные: переменная s — строка, содержащая скобочную последовательность. II. Результат: ответ «да» или «нет». III. Алгоритм решения задачи. 1. Ввод исходных данных. 2.1. Если текущий символ — открывающаяся скобка, то этот символ заносится в стек. 3. Вывод результата. IV. Описание переменных: s – string, st – stack<char>. |
Принцип работы стека часто сравнивают со стопкой тарелок: чтобы взять тарелку из середины стопки, нужно снять верхние. Стек также можно сравнить с железнодорожным путём: вагон, который попал туда последним, заберут первым. Пример 21.10. Добавление и удаление элемента в вершине стека: Пример 21.11. Операции для типа данных stack.
Правильные скобочные последовательности: [([])((([[[]]])))]{()}, ()((()))[[]]. Неправильные скобочные последовательности: [[)) (несоответствие типа закрывающихся скобок типу открывающихся), }{ (закрывающая скобка стоит раньше открывающейся), [[{{}}] (не каждой открывающейся скобке соответствует закрывающаяся). V. Программа:
VI. Тестирование. VII. Анализ результатов. Рассмотрим пример для правильной скобочной последовательности [([])(([]))]. В первой строке — текущий символ, во второй — состояние стека.
|
21.4. Очередь
Очередь — динамическая линейная структура данных, хранящая последовательность элементов, в которой добавление новых элементов происходит в конец очереди (хвост, tail), а удаление — из начала очереди (головы, head). Очередь организована по принципу FIFO (англ. first in — first out, «первым пришел — первым ушел»). Для очереди определены операции добавления элемента и удаления элемента (пример 21.13). По своей организации очередь является линейным списком, добавление в который допускается только в конец списка, а удаление только из начала. В STL для работы со стеком определен тип данных queue, для работы с которым подключается одноименная библиотека. Описание стека: queue <тип данных> имя переменной; В примере 21.14 перечислены операции, доступные для работы с очередью. Большинство функций для работы с очередью имеют аналоги для типа данных list. Пример 21.15. Задана строка, состоящая из прописных и строчных латинских букв. Вывести буквы парами. В каждой паре первая буква прописная, а вторая строчная. Пары образуются в том порядке, в котором следуют буквы в строке. (В реальности это могут быть, например, фамилии пациентов, ожидающих определенное лекарство, и аптеки, в которые это лекарство поступило.) Этапы выполнения задания I. Исходные данные: переменная s — строка, содержащая прописные и строчные буквы. II. Результат: пары букв и набор букв, оставшийся без пары. III. Алгоритм решения задачи. 1. Ввод исходных данных. 2.1. В очередь будем заносить буквы одного регистра. 3. Если в конце очередь не пуста, то в ней остались символы, которым не хватило пары. Для того чтобы вывести все символы из очереди, будем после вывода удалять символ, пока очередь не станет пустой. IV. Описание переменных: s – string, q – queue<char>. Часто используется структура дек — (англ. deque — double ended queue, «двухсторонняя очередь»). Дек можно определять как двухстороннюю очередь, или как стек, имеющий два конца. Это означает, что данный вид списка позволяет добавлять элементы в начало и в конец, и то же самое справедливо для операции извлечения. Эта структура данных одновременно работает по двум способам организации данных: FIFO и LIFO (пример 21.16). |
Структуру данных очередь можно представить себе как очередь к врачу, где новые пациенты встают в конец очереди, а прием пациентов осуществляется с начала очереди. На основе очереди организована обработка событий в Windows. Когда пользователь оказывает какое-либо действие на приложение, то сообщение об этом ставится в очередь, и только когда будут обработаны сообщения, пришедшие ранее, приложение выполнит необходимое действие. Пример 21.13. Добавление и удаление элемента в очереди: Пример 21.14. Операции для типа данных queue.
Если на вход подана строка ZHJKqwertASDyuiopQWERTYUIOPasdf, то должны получиться следующие пары: Zq Hw Je Kr At Sy Du Qi Wo Ep Ra Ts Yd Uf. Буквы I O P останутся без пары. V. Программа:
VI. Тестирование. VII. Анализ результатов. Рассмотрим пример для строки wwerTyGJLqTT. В первой строке — текущий символ, во второй — состояние очереди.
Пример 21.16. Добавление и удаление элемента в деке: В STL для работы с деком определен тип данных deque, для работы с которым подключается одноименная библиотека. Функции для работы с деком можно посмотреть в см. Приложение к главе 1.1. |
Вопросы к параграфу
1. Что понимают под структурой данных? 2. Как добавить элемент в список? 3. Как удалить элемент из списка? 4. Как устроен стек? 5. Какие операции определены для очереди? |
Упражнения
1. В файле хранится список фамилий. Написать программу, которая определит, есть ли в списке одинаковые фамилии, и, если есть, удалит их. Преобразованный список вывести в файл.
2. Дано целое число x и список, состоящий из целых чисел. Удалить из списка все элементы со значением x.
3. Осуществить циклический сдвиг элементов двусвязного списка на k позиций вправо.
4. По заданному текстовому файлу получите новый текстовый файл, в котором слова из первого файла расположены в обратном порядке. Используйте структуру данных стек.
5. В файле хранятся целые числа. Разместить числа в два стека. В первый стек — положительные числа, а во второй — отрицательные. Если количество чисел в стеках одинаково, то в итоговый файл вывести попарные произведения чисел: один множитель из первого стека, другой — из второго, иначе в итоговый файл записать значения вершины каждого стека.
6. В стеке хранятся целые числа. Найдите минимальное число в стеке.
7. В очереди хранятся целые числа. Найдите максимальное число в очереди.
8. Дана величина a строкового типа из четного количества символов. Получить и вывести величину b, состоящую из символов первой половины величины a, записанных в обратном порядке, после которых идут символы второй половины величины a, также записанные в обратном порядке. Например, при а = «привет» b должно быть равно «ирптев».
9. Имеется n черных и белых карточек, сложенных в стопку. Карточки раскладываются на стол в одну линию следующим образом: первая кладется на стол, вторая — под низ стопки, третья — на стол, четвертая — под низ стопки и т. д., пока все карточки не будут выложены на стол. Каким должно быть исходное расположение карточек в стопке, чтобы разложенные на столе карточки чередовались по цвету: белая, черная, белая, черная и т. д.? Подсказка: используйте очередь.