йцук. Однонаправленные (односвязные) списки. 1 Описание однонаправленных списков. 2 Создание однонаправленного списка. 3 Печать (просмотр) однонаправленного списка
Скачать 2.17 Mb.
|
Оглавление Введение . Однонаправленные (односвязные) списки .1 Описание однонаправленных списков .2 Создание однонаправленного списка .3 Печать (просмотр) однонаправленного списка .3.1 Вставка элемента в однонаправленный список .3.2 Удаление элемента из однонаправленного списка .4 Поиск элемента в однонаправленном списке .5 Удаление однонаправленного списка . Двунаправленные (двусвязные) списки .1 Создание двунаправленного списка .2 Печать (просмотр) двунаправленного списка .3 Вставка элемента в двунаправленный список .4 Удаление элемента из двунаправленного списка .5 Поиск элемента в двунаправленном списке .6 Проверка пустоты двунаправленного списка .7 Удаление двунаправленного списка . Реализация однонаправленных и двунаправленных списков Заключение Список источников Введение Списком называется упорядоченное множество, состоящее из переменного числа элементов, к которым применимы операции включения, исключения. Список, отражающий отношения соседства между элементами, называется линейным. Длина списка равна числу элементов, содержащихся в списке, список нулевой длины называется пустым списком. Списки представляют собой способ организации структуры данных, при которой элементы некоторого типа образуют цепочку. Для связывания элементов в списке используют систему указателей. В минимальном случае, любой элемент линейного списка имеет один указатель, который указывает на следующий элемент в списке или является пустым указателем, что интерпретируется как конец списка. Структура, элементами которой служат записи с одним и тем же форматом, связанные друг с другом с помощью указателей, хранящихся в самих элементах, называют связанным списком. В связанном списке элементы линейно упорядочены, но порядок определяется не номерами, как в массиве, а указателями, входящими в состав элементов списка. Каждый список имеет особый элемент, называемый указателем начала списка (головой списка), который обычно по содержанию отличен от остальных элементов. В поле указателя последнего элемента списка находится специальный признак NULL, свидетельствующий о конце списка. Линейные связные списки являются простейшими динамическими структурами данных. Из всего многообразия связанных списков можно выделить следующие основные: · однонаправленные (односвязные) списки; · двунаправленные (двусвязные) списки; · циклические (кольцевые) списки. В основном они отличаются видом взаимосвязи элементов и/или допустимыми операциями. 1. Однонаправленные (односвязные) списки1.1 Описание однонаправленных списковНаиболее простой динамической структурой является однонаправленный список, элементами которого служат объекты структурного типа. Однонаправленный (односвязный) список - это структура данных, представляющая собой последовательность элементов, в каждом из которых хранится значение и указатель на следующий элемент списка (Рис.1). В последнем элементе указатель на следующий элемент равен NULL. Рис. 1. Линейный однонаправленный список Описание простейшего элемента такого списка выглядит следующим образом:имя_типа { информационное поле; адресное поле; }; где информационное поле - это поле любого, ранее объявленного или стандартного, типа; адресное поле - это указатель на объект того же типа, что и определяемая структура, в него записывается адрес следующего элемента списка. Например: struct Node {key;//информационное поле*next;//адресное поле }; Информационных полей может быть несколько. Например: struct point {*name;//информационное полеage;//информационное поле*next;//адресное поле }; Каждый элемент списка содержит ключ, который идентифицирует этот элемент. Ключ обычно бывает либо целым числом, либо строкой. Основными операциями, осуществляемыми с однонаправленными списками, являются: · создание списка; · печать (просмотр) списка; · вставка элемента в список; · удаление элемента из списка; · поиск элемента в списке · проверка пустоты списка; · удаление списка. Особое внимание следует обратить на то, что при выполнении любых операций с линейным однонаправленным списком необходимо обеспечивать позиционирование какого-либо указателя на первый элемент. В противном случае часть или весь список будет недоступен. Рассмотрим подробнее каждую из приведенных операций. Для описания алгоритмов этих основных операций используется следующее объявление: struct Single_List {//структура данныхData; //информационное поле Single_List *Next; //адресное поле }; . . . . . . . . . ._List *Head; //указатель на первый элемент списка . . . . . . . . . ._List *Current; //указатель на текущий элемент списка (при необходимости) 1.2 Создание однонаправленного спискаДля того, чтобы создать список, нужно создать сначала первый элемент списка, а затем при помощи функции добавить к нему остальные элементы. При относительно небольших размерах списка наиболее изящно и красиво использование рекурсивной функции. Добавление может выполняться как в начало, так и в конец списка. //создание однонаправленного списка (добавления в конец) void Make_Single_List(int n,Single_List** Head){(n > 0) { (*Head) = new Single_List(); //выделяем память под новый элемент<< "Введите значение ";>> (*Head)->Data; //вводим значение информационного поля (*Head)->Next=NULL;//обнуление адресного поля Make_Single_List(n-1,&((*Head)->Next)); } } 1.3 Печать (просмотр) однонаправленного спискаОперация печати списка заключается в последовательном просмотре всех элементов списка и выводе их значений на экран. Для обработки списка организуется функция, в которой нужно переставлять указатель на следующий элемент списка до тех пор, пока указатель не станет равен NULL, то есть будет достигнут конец списка. Реализуем данную функцию рекурсивно. //печать однонаправленного списка void Print_Single_List(Single_List* Head) { if (Head != NULL) {<< Head->Data << "\t";_Single_List(Head->Next); //переход к следующему элементу }cout << "\n"; } Вставка элемента в однонаправленный список В динамические структуры легко добавлять элементы, так как для этого достаточно изменить значения адресных полей. Вставка первого и последующих элементов списка отличаются друг от друга. Поэтому в функции, реализующей данную операцию, сначала осуществляется проверка, на какое место вставляется элемент. Далее реализуется соответствующий алгоритм добавления (рис.2). 1.3.1 Вставка элемента в однонаправленный список/*вставка элемента с заданным номером в однонаправленный список*/ Single_List* Insert_Item_Single_List(Single_List* Head,Number, int DataItem){-;_List *NewItem=new(Single_List);>Data=DataItem;>Next = NULL; if (Head == NULL) {//список пуст= NewItem;//создаем первый элемент списка }{//список не пуст_List *Current=Head;(int i=1; i < Number && Current->Next!=NULL; i++)=Current->Next;(Number == 0){ //вставляем новый элемент на первое место NewItem->Next = Head;= NewItem; }{//вставляем новый элемент на непервое место if (Current->Next != NULL)>Next = Current->Next;>Next = NewItem; } }Head; } Рис. 2. Вставка элемента в однонаправленный список 1.3.2Удаление элемента из однонаправленного спискаИз динамических структур можно удалять элементы, так как для этого достаточно изменить значения адресных полей. Операция удаления элемента однонаправленного списка осуществляет удаление элемента, на который установлен указатель текущего элемента. После удаления указатель текущего элемента устанавливается на предшествующий элемент списка или на новое начало списка, если удаляется первый. Алгоритмы удаления первого и последующих элементов списка отличаются друг от друга. Поэтому в функции, реализующей данную операцию, осуществляется проверка, какой элемент удаляется. Далее реализуется соответствующий алгоритм удаления ( Рис. 3). Рис. 3. Удаление элемента из однонаправленного списка /*удаление элемента с заданным номером из однонаправленного списка*/ Single_List* Delete_Item_Single_List(Single_List* Head,Number){_List *ptr;//вспомогательный указатель_List *Current = Head;(int i = 1; i < Number && Current != NULL; i++)= Current->Next; if (Current != NULL){//проверка на корректность if (Current == Head){//удаляем первый элемент= Head->Next;(Current);= Head; }{//удаляем непервый элемент= Head;(ptr->Next != Current)= ptr->Next;>Next = Current->Next;(Current);=ptr; } }Head; } 1.4 Поиск элемента в однонаправленном спискеОперация поиска элемента в списке заключается в последовательном просмотре всех элементов списка до тех пор, пока текущий элемент не будет содержать заданное значение или пока не будет достигнут конец списка. В последнем случае фиксируется отсутствие искомого элемента в списке (функция принимает значение false ). //поиск элемента в однонаправленном спискеFind_Item_Single_List(Single_List* Head, int DataItem){ Single_List *ptr; //вспомогательным указатель= Head;(ptr != NULL){//пока не конец списка if (DataItem == ptr->Data) return true;ptr = ptr->Next; }false; } 1.5 Удаление однонаправленного спискаОперация удаления списка заключается в освобождении динамической памяти. Для данной операции организуется функция, в которой нужно переставлять указатель на следующий элемент списка до тех пор, пока указатель не станет равен NULL, то есть не будет достигнут конец списка. Реализуем рекурсивную функцию. /*освобождение памяти, выделенной под однонаправленный список*/ void Delete_Single_List(Single_List* Head){ if (Head != NULL){_Single_List(Head->Next); delete Head; } } Таким образом, однонаправленный список имеет только один указатель в каждом элементе. Это позволяет минимизировать расход памяти на организацию такого списка. Одновременно это позволяет осуществлять переходы между элементами только в одном направлении, что зачастую увеличивает время, затрачиваемое на обработку списка. Например, для перехода к предыдущему элементу необходимо осуществить просмотр списка с начала до элемента, указатель которого установлен на текущий элемент. 2. Двунаправленные (двусвязные) спискиДля ускорения многих операций целесообразно применять переходы между элементами списка в обоих направлениях. Это реализуется с помощью двунаправленных списков, которые являются сложной динамической структурой. Двунаправленный (двусвязный) список - это структура данных, состоящая из последовательности элементов, каждый из которых содержит информационную часть и два указателя на соседние элементы (Рис. 4). При этом два соседних элемента должны содержать взаимные ссылки друг на друга. В таком списке каждый элемент (кроме первого и последнего) связан с предыдущим и следующим за ним элементами. Каждый элемент двунаправленного списка имеет два поля с указателями: одно поле содержит ссылку на следующий элемент, другое поле - ссылку на предыдущий элемент и третье поле - информационное. Наличие ссылок на следующее звено и на предыдущее позволяет двигаться по списку от каждого звена в любом направлении: от звена к концу списка или от звена к началу списка, поэтому такой список называют двунаправленным. Рис. 4. Двунаправленный список Описание простейшего элемента такого списка выглядит следующим образом:имя_типа { информационное поле; адресное поле 1; адресное поле 2; }; где информационное поле - это поле любого, ранее объявленного или стандартного, типа; адресное поле 1 - это указатель на объект того же типа, что и определяемая структура, в него записывается адрес следующего элемента списка ; адресное поле 2 - это указатель на объект того же типа, что и определяемая структура, в него записывается адрес предыдущего элемента списка. Например:list {elem ;*next, *pred ; }*headlist ; где type - тип информационного поля элемента списка; *next, *pred - указатели на следующий и предыдущий элементы этой структуры соответственно. Переменная-указатель headlist задает список как единый программный объект, ее значение - указатель на первый (или заглавный) элемент списка. Основные операции, выполняемые над двунаправленным списком, те же, что и для однонаправленного списка. Так как двунаправленный список более гибкий, чем однонаправленный, то при включении элемента в список, нужно использовать указатель как на элемент, за которым происходит включение, так и указатель на элемент, перед которым происходит включение. При исключении элемента из списка нужно использовать как указатель на сам исключаемый элемент, так и указатели на предшествующий или следующий за исключаемым элементы. Но так как элемент двунаправленного списка имеет два указателя, то при выполнении операций включения/исключения элемента надо изменять больше связей, чем в однонаправленном списке. Рассмотрим основные операции, осуществляемые с двунаправленными списками, такие как: ) создание списка; ) печать (просмотр) списка; ) вставка элемента в список; ) удаление элемента из списка; ) поиск элемента в списке; ) проверка пустоты списка; ) удаление списка. Особое внимание следует обратить на то, что в отличие от однонаправленного списка здесь нет необходимости обеспечивать позиционирование какого-либо указателя именно на первый элемент списка, так как благодаря двум указателям в элементах можно получить доступ к любому элементу списка из любого другого элемента, осуществляя переходы в прямом или обратном направлении. Однако по правилам хорошего тона программирования указатель желательно ставить на заголовок списка. Для описания алгоритмов этих основных операций используется следующее объявление:Double_List {//структура данныхData; //информационное поле_List *Next, //адресное поле *Prior; //адресное поле }; . . . . . . . . . ._List *Head; //указатель на первый элемент списка . . . . . . . . . ._List *Current; //указатель на текущий элемент списка (при необходимости) 2.1 Создание двунаправленного спискаДля того, чтобы создать список, нужно создать сначала первый элемент списка, а затем при помощи функции добавить к нему остальные элементы. Добавление может выполняться как в начало, так и в конец списка. Реализуем рекурсивную функцию. //создание двунаправленного списка (добавления в конец) void Make_Double_List(int n,Double_List** Head, Double_List* Prior){(n > 0) { (*Head) = new Double_List(); //выделяем память под новый элемент<< "Введите значение ";>> (*Head)->Data; //вводим значение информационного поля (*Head)->Prior = Prior; (*Head)->Next=NULL;//обнуление адресного поля_Double_List(n-1,&((*Head)->Next),(*Head)); }(*Head) = NULL; } 2.2 Печать (просмотр) двунаправленного спискаОперация печати списка для двунаправленного списка реализуется абсолютно аналогично соответствующей функции для однонаправленного списка. Просматривать двунаправленный список можно в обоих направлениях. //печать двунаправленного списка void Print_Double_List(Double_List* Head) { if (Head != NULL) {<< Head->Data << "\t";_Double_List(Head->Next); //переход к следующему элементу }cout << "\n"; } 2.3 Вставка элемента в двунаправленный списокВ динамические структуры легко добавлять элементы, так как для этого достаточно изменить значения адресных полей. Операция вставки реализовывается аналогично функции вставки для однонаправленного списка, только с учетом особенностей двунаправленного списка (Рис. 5). Рис. 5. Добавление элемента в двунаправленный список //вставка элемента с заданным номером в двунаправленный список Double_List* Insert_Item_Double_List(Double_List* Head,Number, int DataItem){-;_List *NewItem=new(Double_List);>Data=DataItem;>Prior=NULL;>Next = NULL;(Head == NULL) {//список пуст= NewItem; }{//список не пуст_List *Current=Head;(int i=1; i < Number && Current->Next!=NULL; i++)=Current->Next;(Number == 0){ //вставляем новый элемент на первое место NewItem->Next = Head;>Prior = NewItem; Head = NewItem; }{//вставляем новый элемент на непервое место if (Current->Next != NULL) Current->Next->Prior = NewItem;>Next = Current->Next;>Next = NewItem;>Prior = Current;= NewItem; } }Head; } |