Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Динамические структуры данных. Списки (Понятие и классификация языков программирования высокого уровня)

Содержание:

ВВЕДЕНИЕ

Объект исследования данной курсовой работы – языки программирования высокого уровня.

Предметом исследования являются динамические структуры данных, а именно списки, в программировании на языках высокого уровня.

Цель курсовой работы – изучение динамических структур данных на конкретном примере списков, научиться созданию их и проведению других операций добавления или удаления элементов списка.

Актуальность темы состоит главным образом в том, что в современном мире написание функциональных программ без использования динамических структур данных почти невозможно. К тому же необходимо умение оперировать такими структурами, применяя правильный подход в выборе методов решения назначенных задач. Потому изучение этого вопроса является необходимым, как в теории так и на практике.

С помощью линейных списков могут быть реализованы абстрактные типы данных, то есть такие типы данных, которые предоставляют определенные наборы функций для работ с элементами. Эти типы данных изначально были описаны в книге «Искусство программирования», автором которой является Дональд Эрвин Кнут.

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

Эти типы данных помогают достичь модульность программных продуктов и иметь несколько альтернативных взаимозаменяемых вариантов отдельных модулей.

Исходя из всего вышесказанного, выделим задачи рассмотреть основные языки программирования, языки программирования высокого уровня, типы данных, а именно:

  • определение языка программирования;
  • рассмотреть классификации языков программирования;
  • обоснование выбора языка Паскаль;
  • рассмотреть основные понятия и определения;
  • познакомиться с общими понятиями списов в Паскале;
  • рассмотреть основные операции работы со списками.

Глава 1. Языки программирования высокого уровня (ЯПВУ)

1.1 Понятие и классификация языков программирования высокого уровня

Языком программирования называют машинно-независимые языки, использующие понятия и структуры, которые удобны для восприятия человека.

Языки программирования высокого уровня предназначаются прежде всего для удобства и быстроты в написании программ программистами. Высокоуровневые языки программирования стремятся не только облегчать решение каких-либо задач, но и упрощать возможности использования программного обеспечения на разных платформах. Использование различных интерпретаторов и трансляторов обеспечивает связь программы с различным оборудованием и операционной системой, при этом исходный код не изменяется [1].

К недостаткам программы, написанной на языке высокого уровня, отнесится избыточность кода, к которому приводит компиляция. Код содержит в себе разные подпрограммы, а также комментарии к некоторым участкам кода. Программы, при написании которых использовались высокоуровневые языки, просты для понимания, но менее эффективны, чем их аналоги, которые написаны на низкоуровневых (машинных) языках.

Языки программирования высокого уровня разделяются на (рис. 1):

  • процедурные;
  • объектно-ориентированные;
  • функциональные;
  • логические.

Языки программирования

Процедурные

Объектно-ориентированные

Функциональные

Логические

Рисунок 1. Классификация языков программирования высокого уровня

Процедурным языком программирования (Procedural languages) называется язык, в котором все действия над данными описаны последовательностями определенных команд. Написанные на процедурном языке программы являются последовательностями команд, определяющими алгоритмы решения задач. Главной идеей данного программирования является то, что память используется не для хранения данных, а основная команда – операция присвоения, с помощью которой определяется и меняется память в ЭВМ. Программой производится преобразование содержимого памяти, изменяется, тем самым из исходного состояния происходит переход к результирующему [2].

Объектно-ориентированные языки программирования (Object-oriented languages) – это языки, которые при построении программ используют совокупность объектов, которые состоят из данных и операций над ними, и классов объекта. Они в свою очередь имеют возможность обращаться (вызывать) друг друга для последующего выполнения тех или иных операций.

Функциональные языки программирования (Functional languages) – это языки, в которых действия c данными выступают в виде терминов последовательностей команд. Программы, написанные на функциональных языках программирования, являются совокупностью описаний функций и выражений необходимых для вычислений.

Вычисление производится при помощи редукции, то есть серии упрощений. В функциональном языке программирования не используют память в качестве хранилища значений переменных, а операторы присвоения отсутствуют, вследствие этого переменная обозначает объект программы, а не область памяти [3].

Логические языки программирования (Logical languages) – это языки, используемые при построении программ в виде совокупностей логических правил с ранее определенным алгоритмом, которые служат для обработок данных. При использовании логического языка программирования нужно только специфицировать факты, лежащие в основе алгоритмов, а не определять шаги, которые требуются для выполнения [4].

1.2. Наиболее распространенные языки программирования

К наиболее распространенным языкам программирования высокого уровня можно отнести:

  • Ассемблер;
  • Си;
  • Бейсик;
  • Фортран;
  • Паскаль.

Рассмотрим каждый из упомянутых выше языков.

Ассемблер (Assembler) – язык программирования, который расширен при помощи использования конструкций высокоуровневых языков программирования. К достоинствам Ассемблера отнесят самый компактный код, какой только возможен для процессора. На Ассемблере можно написать программу, которая работает на микроуровне, то есть вне операционных систем, например, «прошивка» BIOS или драйверов.

Но в силу машинной ориентации человеку сложнее дается чтение и понимание подобного кода программ, соответственно и усложняется весь процесс программирования и отладки. Также получается невозможным и перенос программ на компьютер с другой архитектурой и набором команд [5].

Си (C) часто называется среднеуровневым языком или даже языком программирования низкого уровня, по причине работы, близкой к машинным устройствам. Тем не менее, в строгой классификации, он непременно является языком программирования высокого уровня. Создавался же этот язык с целью сделать написание программ большого объема более простым, с минимальным числом ошибок с правилами процедурного программирования, тем самым не добавляя в конечный код лишнего для компиляторов, как это всегда делали языки высокого уровня, например Бейсик. Но многие элементы Си относятся к потенциально опасным, а последствия ненадлежащего их использования оказываются непредсказуемы. Керниган в своей книге говорит: «Си – инструмент, острый, как бритва: с его помощью можно создать и элегантную программу, и кровавое месиво». Многие случаи неправильного использования подобных элементов не определяются ни при компиляции, ни во время выполнения. Это часто приводит к дальнейшему непредсказуемому поведению программ. В результате неправильного использования элементов языка Си появляются «дыры» в системах безопасности [6].

Бейсик (BASIC) предназначается для обучения программированию и получил распространение, прежде всего, в качестве языка для домашних компьютеров. Название языка, BASIC, является аббревиатурным сокращением – Beginner’s All-purpose Sybmolic Instruction Code, что в переводе означает универсальный код символических инструкций для начинающих. Синтаксис Бейсика напоминает синтаксис Фортрана, а многие элементы явно заимствованы.

Так как язык задумывался для обучения, его конструкции очень просты – ключевые слова, которые используются в синтаксисе, заимствованы из английского языка. Простота использования Бейсика обернулась трудностями в структурировании. Особенно это видно в ранних версиях языка. Также Бейсик не допускает использования рекурсии, которая позволяет писать короткие, и в то же время довольно эффективные программы. В будущих версиях, например Visual Basic, многие недостатки были устранены [7].

Фортран (Fоrtran) первый высокоуровневый языком, который имеет транслятор. Этот язык используют в первую очередь в различных научных и инженерных вычислениях. Фортран основан на жестких стандартах, поэтому он с легкостью портируется на другие платформы. Многие крупные научно-технические программы написаны на Фортране потому, что он обладает, помимо наличия встроенных математических и тригонометрических функций, важными факторами – переносимость и устойчивост. Также неотъемлемая часть любой программы, которая написана на Фортране, это графическая библиотека, позволяющая использовать графические данные и другие изображения. Главное назначение Фортрана – это быстрый счет в разных научно-технических приложениях. Это та область, в которой Фортран не имеет конкурентов [8].

Язык программирования Паскаль (Pascal) один из первых языков, отличающийся строгой типизацией и наличием средств для структурного (процедурного) программирования. Он представляет собой процедурный язык, который включает в себя большое количество структур и конструкций подобных if, case, while, with и так далее. Но также Паскаль содержит в себе довольно большое количество возможностей для структурирования абстракций и информации (записей, указателей, множеств, определений типов и перечисления).

Паскаль представляет удобную среду для разработки, которая включает функциональный отладчик, доступный программисту в любой момент, обладает большой скоростью компиляции, а также высокой скоростью исполнения откомпилированной программы, имеет встроенные возможност использования вставки на языке ассемблера [9].

1.3 Обоснование выбора языка Паскаль

Выбор Паскаля, как языка программирования в дальнейшем исследовании и построении данных списочных структур, прежде всего, основывается на следующих факторах:

  1. паскаль используется при обучении программированию и является базой для многих других языков программирования;
  2. принимая во внимание строгую типизацию языка, Паскаль содействует дисциплинированному программированию;
  3. есть поддержка указателей, позволяющая использование косвенной адресации (экономии памяти, чтение файлов из памяти, а не загрузка их в ОЗУ) и динамическое управление памятью;
  4. паскаль является языком структурного программирования, в основе которого лежит представления программ в виде иерархических структур. Это дает гарантии того, что при внесении некоторых изменений или исправлений ошибок в одном блоке не выйдет из строя какая-либо уже отлаженная часть программы, оказавшаяся вне поля зрения программиста [10].

Глава 2. Основные принципы построения динамических списочных структур данных

2.1. Однонаправленные списки

Список – упорядоченное множество, которое состоит из n элементов. К элементам списка можно применять такие операции как включение и исключение. Список, элементы которого являются «соседями», имеют последовательность, называется линейным.

Список имеет длину равную количеству элементов в нем. Пустым списком называется список нулевой длины, то есть список, в котором нет элементов. Элементы списков связываются цепочкой с помощью указателей. Каждый элемент списка содержит в себе указатель, указывающий на следующий в списке элемент [11].

Список имеет специфичный элемент, именуемый головой списка (указатель начала списка). В поле указателя последнего элемента однонаправленного списка находится признак NIL, он обозначает конец списка. Однонаправленный список представляет собой структуру данных в виде последовательности элементов, в каждом из которых хранятся значение и указатель на следующий списочный элемент (рис. 2). Если элемент списка является последним, то указатель на следующий элемент будет равен значению NIL.

Рисунок 2.Линейный однонаправленный список

Пример описания списка:

Type ukazatel=^P;

P= record

Inform: integer;

Next: ukazatel;

End;

В языке Паскаль имеется главное правило: прежде чем использовать какой-то объект, его нужно описать. Исключение составляют лишь указатели, которые могут иметь ссылки на еще не объявленные типы.

Формирование списков. Для того, чтобы список начал существовать, нужно определять указатель на его начало. Создается первый элемент в списке:

New (a); {выделяется место в памяти}

a^.Next:= nil; {указатель пустой}

a^.Inf:=3;

Продолжаем формирование списка. Для этого необходимо добавление элемента либо в конец списка, либо в его голову.

А) Добавляем элемент к голове списка. Для этого нужно выполнить следующую последовательность действий:

  • получение памяти для нового элемента;
  • помещение туда информации;
  • присоединение элемента к голове списка.

New(x);

Readln(x^.Inf);

x^.Next:= a;

a:= x;

Б) Добавление элементов в конец списка. Для этого вводится вспомогательная переменная, которая хранит адрес последнего элемента. Например, это будет указатель с именем hvost (хвост) (рис. 3).

x:=hvost;

Рисунок 3. Добавление элемента

New( x^. next); {выделяется память для следующего элемента}

x:=x^.next;

x^.next:=nil;

x^.inf:=5;

hvost:=x;

Просмотр списка:

While a<>nil do

Begin

Writeln (a^.inf);

a:=a^.next;

End;

Удаление элемента из списка.

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

Рисунок 4.Удаление элемента 1

x:=a;

a:=a^.next;

dispose(x);

Б) Удаление элемента из середины списка. Для этого необходимо знать адрес удаляемого элемента и адрес элемента, который стоит перед ним. Например, dig – это значение элемента, который удаляем (рис. 5).

Рисунок 5. Удаление элемента 2

x:=a;

while ( x<> nil) and ( x^. inf<> dig) do

begin

dx:=x;

x:=x^.next;

end;

dx:=x^.next:

dispose(x);

В) Удаление из конца списка. Для этого необходимо найти предпоследний элемент.

x:=a; dx:=a;

while x^.next<>nil do

begin

dx:=x; x:=x^.next;

end;

dx^.next:=nil;

dispose(x);

Прохождение списка. Существенно уметь перебирать все элементы в списке и выполнять над ними какую-то операцию. Например, нужно найти сумму всех элементов в списке:

sum:=0;

x:=a;

while x<>nil do

begin

sum:= sum+x^.inf;

x:=x^.next;

end;

Использование однонаправленного списка при решении некоторых задач может вызывать некоторые трудности. Все дело в том, что по однонаправленным спискам возможно движение только в одном направлении, от его головы к последнему элементу. Но иногда возникают необходимости произведения каких-либо операций с элементами, предшествующими элементу с заданными свойствами. Тем не менее после нахождения элемента с данными свойствами в однонаправленных списках нет возможности получения удобного и быстрого способа доступа к предыдущим элементам [12].

2.2. Двунаправленные списки

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

Двунаправленный список – это сложная динамическая структура данных. Такая структура состоит из последовательности элементов, каждый из них содержит информационную часть и два указателя на элементы по соседству – на следующий элемент списка и на предыдущий (рис. 6).

Рисунок 6. Двунаправленный список

В двунаправленном списке каждый элемент (кроме первого и последнего) связан с предыдущим и следующим элементами. Элементы двунаправленного списка имеют два поля-указателя: одно имеет ссылку на следующий элемент, а другое поле – ссылку на предыдущий соответственно. Третье поле является информационным. Наличие таких ссылок на следующее и на предыдущее звенья списка позволяет двигаться по списку в любом направлении, поэтому такой список и называется двунаправленным [13].

Описать элемент двунаправленного списка можно следующим образом:

Type ukazatel=^P;

P=record

Inform: integer;

Next: ukazatel;

Pred: ukazatel;

End;

В динамические структуры, в нашем случае двунаправленный список, легко добавлять элементы. Для этого достаточно изменить значения адресных полей. Операция вставки реализовывается аналогично функции вставки для однонаправленного списка, только с учетом особенностей двунаправленного списка (рис. 7).

Рисунок 7. Добавление элемента в двунаправленный список

Можно удалять элементы. Данная операция удаления элемента из двунаправленного списка во многом аналогична удалению из однонаправленного списка (рис. 8.).

Рисунок 8.Удаление элемента из двунаправленного списка

Поиск элемента в двунаправленном списке ведется:

  • просмотром всех элементов от начала до конца списка;
  • просмотром элементов от конца списка к его началу;
  • просматром списка в обоих направлениях сразу: от начала к середине списка и от конца к середине, но только если в списке четное или нечетное количество элементов).

Таким образом, двунаправленные списки являются расширением однонаправленных списком, сохраняя при всем своем своеобразии свойства [14].

2.3. Циклические списки

Циклический список отличается тем, что в списке такого типа нет пустых указателей, отсутствует NIL (рис. 9). Как и линейные списки, циклические списки могут быть однонаправленными или двунаправленными.

Рисунок 9. Однонаправленный циклический список

В случае представленном на рисунке 9, последний элемент нашего циклического однонаправленного списка содержит в себе указатель, который связывает его с первым элементом списка. Чтобы полностью «обойти» такой список, достаточно всего лишь иметь указатель на текущий элемент [15].

Что касается двунаправленного циклическом списка, то здесь система указателей полностью аналогична работе указателей линейного двунаправленного (см. рис. 10).

Рисунок 10.Двунаправленный циклический список

Двунаправленные циклические списки позволяют нам достаточно легко осуществлять добавление и удаление элементов справа и слева от текущего элемента. Все элементы циклического списка, В отличие от линейного списка, являются по отношению друг к другу равноправными. Чтобы выделить первый элемент нужно иметь указатель на заголовок. Но все же в большинстве случаев нет такой необходимости, достаточно просто иметь указатель на текущий элемент списка [16].

2.4. Мультисписки

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

Мультисписок состоит из элементов, которые содержат то количество указателей, которое позволяет одновременно организовать их в виде нескольких разных списков. Тем самым мы не допускаем дублирование, вследствие чего более рационально используем память (рис. 11).

Рисунок 11. Объединение двух линейных списков в один мультисписок

Но такая экономия памяти не единственная причина из-за чего используют мультисписки. Большинство структур данных не сводятся к структурам типовым, а составляют их некоторую комбинацию. Комбинируются в мультисписках списки разных типов – циклические и однонаправленные, и двунаправленные [17].

2.5. Очередь и дек

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

Рисунок 12. Организация дека на основе двунаправленного линейного списка

Очередь может быть организована на основе двунаправленного линейного списка (рис. 12). Простая очередь в отличие от дека имеет только один вход и только один выход, а тот элемент, который был добавлен в данную очередь первым, будет удален из нее также первым [18].

Рисунок 13. Дек с ограниченным входом на основе двунаправленного списка

Рисунок 14. Дек с ограниченным выходом на основе двунаправленного линейного списка

Очереди с ограниченными входами или выходами можно организовать на основе двунаправленного линейного списка, так же как дек или очередь.

Деки с ограниченными входами могут быть использованы как простые очереди или стеки [19].

2.6. Стек

Стек представляет собой структуру данных, из которой первым извлекается тот элемент, который был добавлен в нее последним. Стек как динамическую структуру данных легко организовать на основе линейного списка [20].

Для такого списка достаточно хранить указатель вершины стека, который указывает на первый элемент списка. Если стек пуст, то списка не существует и указатель принимает значение NIL.

Рисунок 15. Организация стека на основе линейного списка.

Глава 3. Практическая часть

3.1. Постановка задачи

Для закрепления полученного материала из предыдущих двух глав курсовой работы попробуем сформировать типичную задачу на создание списка.

Задача:

Необходимо сформировать связанный однонаправленный список путем добавления последующих элементов в конец списка. Пусть элементами списка будут являться целые числа: 7, 2, 1 и 9.

3.2. Решение поставленной задачи

Для того чтобы сформировать связанный однонаправленный список сначала определим запись типа element, в котором будут находиться два поля – информационное поле, которое будет содержать в себе данные (по условию задачи это будут целые числа 7, 2, 1 и 9), и адресное поле, в нем будет находиться адрес следующего от текущего элемента списка.

type

pointer = ^element

element = record

data : integer;

next : pointer;

end;

Так мы описали типы с помощью которых будем создавать связанный однонаправленный список.

Следует также заметить то, что все элементы нашего будущего списка взаимосвязаны между собой, поэтому самое главное не потерять начало списка. Для этого мы установим указатель list_header и в процессе выполнения программы построения списка, будем проверять сохранность значения данного указателя.

В решении задачи будем использовать переменные:

var list_header, x: pointer;

где: list_header – указатель на первый элемент списка, его начало; x – указатель, играющий вспомогательную роль при создании следующего элемента списка.

После описания переменных можно приступить к созданию первого элемента списка. Для этого нам необходимо выделить память для переменной типа element, заполнить информационное поле data и адресное поле next первого элемента, а также установим на первый элемент указатель начала (головы) списка list_header:

new(x);

x^.data := 7;

x^.next := nil;

list_header := x

Продолжим формировать наш список. Для этого добавим следующий элемент в конец нашего списка.

new(x^.next);

x := x^.next;

x^.data := 5;

x^.next := nil;

Теперь список содержит два элемента – 7, 5. Добавление оставшихся элементов для построения списка происходит аналогичным образом.

Мы рассмотрели решение простой задачи построения списка из уже определенного числа элементов.

Обладая имеющимися знаниями, напишем пример программы с помощью котором создадим список, в который элементы будут добавляться вводом с клавиатуры.

procedure init(var y : Pointer);

var

x : pointer;

element : integer;

begin

writeln('Введите список ');

y := nil; {Список пуст}

writeln ('Введите элементы. Чтобы закончить ввод символа, введите 0');

read (Element);

if element <> 0

then {Формируем и вставляем первый элемент списка}

begin

new(x);

x^.next := nil;

x^.data := element;

y := x;

read (element);

while Element<>0 do

begin

new(x^.next); {Формируем и вставляем элемент в конец списка}

x := x^.next;

x^.next := Nil;

x^.data := element;

read(element);

end;

end;

writeln;

end;

В представленной программе, в виде процедуры, мы формируем список неизвестной длины. В процессе выполнения мы осуществляем проверку на «0», которым мы заканчиваем формирование списка.

ЗАКЛЮЧЕНИЕ

В данном исследовании мы рассмотрели понятие языка программирования, рассмотрели достоинства и недостатки одних из самых популярных, таких как (Бейсик, Фортран и другие)

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

  • Паскаль способствует «правильному» дисциплинированному программированию;
  • Паскаль в виде структуры-иерархии дает гарантии сохранности уже отлаженной части кода программы при внесении изменений или исправлений ошибок в каком-либо другом блоке программы.
  • Паскаль предлагает среду разработки, обладает не низкой скоростью компиляции, а также высокой скоростью исполнения откомпилированных программ, имеет встроенную возможность вставки на языке assembler.

В данном исследовании рассмотрели динамические структуры данных, а именно списки.

При удалении элемента из двунаправленного списка нужно использовать как указатель на сам удаляемый элемент, так и указатели на предыдущий или следующий за удаляемым элементы.

Но элементы двунаправленного списка имеют два указателя, поэтому при выполнении операций добавления или удаления элемента списка надо менять больше связей по сравнению с однонаправленными списками.

В главе 4 были приведены практические примеры работы со списком – создание однонаправленного списка.

СПИСОК ИСПОЛЬЗУЕМОЙ ЛИТЕРАТУРЫ

  1. М. П. Левин. Параллельное программирование с использованием OpenMP. Издательство: Интернет-университет информационных технологий, Бином. Лаборатория знаний, 2017 – 78 с.
  2. E. Р. Алексеев, О. В. Чеснокова, Т. В. Кучер. Турбо Паскаль. Самоучитель. Издательство: ДМК-пресс, 2018 – 442 с
  3. Язык программирования высокого уровня. [Электронный ресурс]. URL: http://dic.academic.ru/dic.nsf/ruwiki/1219319 (дата обращения 15.10.2019).
  4. Основы ООП. Поколения языков программирования [Электронный ресурс]. URL: http://lib.znate.ru/docs/index-222448.html (дата обращения 15.10.2019).
  5. Логическое программирование. [Электронный ресурс]. URL: https://ru.wikipedia.org/wiki/Функциональное_программирование (дата обращения 15.10.2019).
  6. Логическое программирование. [Электронный ресурс]. URL: http://www.nestor.minsk.by/kg/2000/10/kg01004.html (дата обращения 15.10.2019).
  7. Программирование на языке ассемблера. [Электронный ресурс]. URL: http://natalia.appmat.ru/c&c++/assembler.html (дата обращения 16.10.2019).
  8. Cи (язык программирования). [Электронный ресурс]. URL: https://ru.wikipedia.org/wiki/Си_(язык_программирования) (дата обращения 16.10.2019)
  9. BASIC. [Электронный ресурс]. URL: http://altcode.ru/basic/ (дата обращения 16.10.2019).
  10. Паскаль (язык программирования ) https://ru.wikipedia.org/wiki/ Паскаль_(язык_программирования) (дата обращения 17.10.2019).
  11. Списки. Однонаправленные списки. [Электронный ресурс]. URL: http://it.kgsu.ru/PasDin/dnpas002.html (дата обращения 17.10.2019).
  12. Однонаправленные списки. [Электронный ресурс]. URL: http://inf.1september.ru/2000/6/c/13.htm (дата обращения 17.10.2019).
  13. Информатика. Pascal // Структуры данных в Паскале http://www.uchites.ru/informatika/pascal/struktury_dannyh (дата обращения 17.10.2019).
  14. Самуйлов С.В. Структуры и Алгоритмы Обработки Данных // Циклические [Электронный ресурс]. URL: http://webpnz.narod.ru/student/saod /lections/19 (дата обращения 18.10.2019.
  15. Циклические списки. [Электронный ресурс]. URL: http://www.life-prog.ru/1_17065_tsiklicheskie-spiski.html (дата обращения 18.10.2019).
  16. Мультисписки. [Электронный ресурс]. URL: http://teasoft.ru/data/books/guap/index1.htm (дата обращения 18.10.2019).
  17. Дек. [Электронный ресурс]. URL: http://kvodo.ru/deque.html (дата обращения 19.10.2019).
  18. Дек. [Электронный ресурс]. URL: https://ru.wikipedia.org/ wiki/Дек (дата обращения 19.10.2019).
  19. Стек. [Электронный ресурс]. URL: https://ru.wikipedia.org/wiki/Стек (дата обращения 20.10.2019).