Диплом, курсовая, контрольная работа
Помощь в написании студенческих работ

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

КурсоваяПомощь в написанииУзнать стоимостьмоей работы

В таком списке каждый элемент (кроме первого и последнего) связан с предыдущим и следующим за ним элементами. Каждый элемент двунаправленного списка имеет два поля с указателями: одно поле содержит ссылку на следующий элемент, другое поле — ссылку на предыдущий элемент и третье поле — информационное. Наличие ссылок на следующее звено и на предыдущее позволяет двигаться по списку от каждого звена… Читать ещё >

Однонаправленные и двунаправленные списки (реферат, курсовая, диплом, контрольная)

1. Однонаправленные (односвязные) списки

1.1 Описание однонаправленных списков

1.2 Создание однонаправленного списка

1.3 Печать (просмотр) однонаправленного списка

1.3.1 Вставка элемента в однонаправленный список

1.3.2 Удаление элемента из однонаправленного списка

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

1.5 Удаление однонаправленного списка

2. Двунаправленные (двусвязные) списки

2.1 Создание двунаправленного списка

2.2 Печать (просмотр) двунаправленного списка

2.3 Вставка элемента в двунаправленный список

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

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

2.6 Проверка пустоты двунаправленного списка

2.7 Удаление двунаправленного списка

3. Реализация однонаправленных и двунаправленных списков Заключение Список источников

Введение

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

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

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

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

· однонаправленные (односвязные) списки;

· двунаправленные (двусвязные) списки;

· циклические (кольцевые) списки.

В основном они отличаются видом взаимосвязи элементов и/или допустимыми операциями.

1. Однонаправленные (односвязные) списки

1.1 Описание однонаправленных списков

Наиболее простой динамической структурой является однонаправленный список, элементами которого служат объекты структурного типа.

Однонаправленный (односвязный) список — это структура данных, представляющая собой последовательность элементов, в каждом из которых хранится значение и указатель на следующий элемент списка (Рис.1). В последнем элементе указатель на следующий элемент равен NULL.

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

struct имя_типа { информационное поле; адресное поле; };

где информационное поле — это поле любого, ранее объявленного или стандартного, типа;

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

Например:

struct Node {

int key;//информационное поле

Node*next;//адресное поле

};

Информационных полей может быть несколько.

Например:

struct point {

char*name;//информационное поле

int age;//информационное поле

point*next;//адресное поле

};

Каждый элемент списка содержит ключ, который идентифицирует этот элемент. Ключ обычно бывает либо целым числом, либо строкой.

Основными операциями, осуществляемыми с однонаправленными списками, являются:

· создание списка;

· печать (просмотр) списка;

· вставка элемента в список;

· удаление элемента из списка;

· поиск элемента в списке

· проверка пустоты списка;

· удаление списка.

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

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

Для описания алгоритмов этих основных операций используется следующее объявление:

struct Single_List {//структура данных

int Data; //информационное поле

Single_List *Next; //адресное поле

};

.. .. .. ... .

Single_List *Head; //указатель на первый элемент списка

.. .. .. ... .

Single_List *Current;

//указатель на текущий элемент списка (при необходимости)

1.2 Создание однонаправленного списка

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

//создание однонаправленного списка (добавления в конец)

void Make_Single_List (int n, Single_List** Head){

if (n > 0) {

(*Head) = new Single_List ();

//выделяем память под новый элемент

cout << «Введите значение «;

cin >> (*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) {

cout << Head->Data << «t» ;

Print_Single_List (Head->Next);

//переход к следующему элементу

}

else cout << «n» ;

}

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

1.3.1 Вставка элемента в однонаправленный список

/*вставка элемента с заданным номером в однонаправленный список*/

Single_List* Insert_Item_Single_List (Single_List* Head,

int Number, int DataItem){

Number—;

Single_List *NewItem=new (Single_List);

NewItem->Data=DataItem;

NewItem->Next = NULL;

if (Head == NULL) {//список пуст

Head = NewItem;//создаем первый элемент списка

}

else {//список не пуст

Single_List *Current=Head;

for (int i=1; i < Number && Current->Next≠NULL; i++)

Current=Current->Next;

if (Number == 0){

//вставляем новый элемент на первое место

NewItem->Next = Head;

Head = NewItem;

}

else {//вставляем новый элемент на непервое место

if (Current->Next ≠ NULL)

NewItem->Next = Current->Next;

Current->Next = NewItem;

}

}

return Head;

}

Рис. 2. Вставка элемента в однонаправленный список

1.3.2 Удаление элемента из однонаправленного списка

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

Алгоритмы удаления первого и последующих элементов списка отличаются друг от друга. Поэтому в функции, реализующей данную операцию, осуществляется проверка, какой элемент удаляется. Далее реализуется соответствующий алгоритм удаления (Рис. 3).

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

/*удаление элемента с заданным номером из однонаправленного списка*/

Single_List* Delete_Item_Single_List (Single_List* Head,

int Number){

Single_List *ptr;//вспомогательный указатель

Single_List *Current = Head;

for (int i = 1; i < Number && Current ≠ NULL; i++)

Current = Current->Next;

if (Current ≠ NULL){//проверка на корректность

if (Current == Head){//удаляем первый элемент

Head = Head->Next;

delete (Current);

Current = Head;

}

else {//удаляем непервый элемент

ptr = Head;

while (ptr->Next ≠ Current)

ptr = ptr->Next;

ptr->Next = Current->Next;

delete (Current);

Current=ptr;

}

}

return Head;

}

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

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

//поиск элемента в однонаправленном списке

bool Find_Item_Single_List (Single_List* Head, int DataItem){

Single_List *ptr; //вспомогательным указатель

ptr = Head;

while (ptr ≠ NULL){//пока не конец списка

if (DataItem == ptr->Data) return true;

else ptr = ptr->Next;

}

return false;

}

1.5 Удаление однонаправленного списка

Операция удаления списка заключается в освобождении динамической памяти. Для данной операции организуется функция, в которой нужно переставлять указатель на следующий элемент списка до тех пор, пока указатель не станет равен NULL, то есть не будет достигнут конец списка. Реализуем рекурсивную функцию.

/*освобождение памяти, выделенной под однонаправленный список*/

void Delete_Single_List (Single_List* Head){

if (Head ≠ NULL){

Delete_Single_List (Head->Next);

delete Head;

}

}

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

2. Двунаправленные (двусвязные) списки

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

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

В таком списке каждый элемент (кроме первого и последнего) связан с предыдущим и следующим за ним элементами. Каждый элемент двунаправленного списка имеет два поля с указателями: одно поле содержит ссылку на следующий элемент, другое поле — ссылку на предыдущий элемент и третье поле — информационное. Наличие ссылок на следующее звено и на предыдущее позволяет двигаться по списку от каждого звена в любом направлении: от звена к концу списка или от звена к началу списка, поэтому такой список называют двунаправленным.

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

struct имя_типа {

информационное поле;

адресное поле 1;

адресное поле 2;

};

где информационное поле — это поле любого, ранее объявленного или стандартного, типа;

адресное поле 1 — это указатель на объект того же типа, что и определяемая структура, в него записывается адрес следующего элемента списка ;

адресное поле 2 — это указатель на объект того же типа, что и определяемая структура, в него записывается адрес предыдущего элемента списка.

Например:

struct list {

type elem ;

list *next, *pred ;

}

list *headlist ;

где type — тип информационного поля элемента списка;

*next, *pred — указатели на следующий и предыдущий элементы этой структуры соответственно.

Переменная-указатель headlist задает список как единый программный объект, ее значение — указатель на первый (или заглавный) элемент списка.

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

Рассмотрим основные операции, осуществляемые с двунаправленными списками, такие как:

1) создание списка;

2) печать (просмотр) списка;

3) вставка элемента в список;

4) удаление элемента из списка;

5) поиск элемента в списке;

6) проверка пустоты списка;

7) удаление списка.

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

Для описания алгоритмов этих основных операций используется следующее объявление:

struct Double_List {//структура данных

int Data; //информационное поле

Double_List *Next, //адресное поле

*Prior; //адресное поле

};

.. .. .. ... .

Double_List *Head; //указатель на первый элемент списка

.. .. .. ... .

Double_List *Current;

//указатель на текущий элемент списка (при необходимости)

2.1 Создание двунаправленного списка

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

//создание двунаправленного списка (добавления в конец)

void Make_Double_List (int n, Double_List** Head,

Double_List* Prior){

if (n > 0) {

(*Head) = new Double_List ();

//выделяем память под новый элемент

cout << «Введите значение «;

cin >> (*Head)->Data;

//вводим значение информационного поля

(*Head)->Prior = Prior;

(*Head)->Next=NULL;//обнуление адресного поля

Make_Double_List (n-1,&((*Head)->Next),(*Head));

}

else (*Head) = NULL;

}

2.2 Печать (просмотр) двунаправленного списка

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

//печать двунаправленного списка

void Print_Double_List (Double_List* Head) {

if (Head ≠ NULL) {

cout << Head->Data << «t» ;

Print_Double_List (Head->Next);

//переход к следующему элементу

}

else cout << «n» ;

}

2.3 Вставка элемента в двунаправленный список

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

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

//вставка элемента с заданным номером в двунаправленный список

Double_List* Insert_Item_Double_List (Double_List* Head,

int Number, int DataItem){

Number—;

Double_List *NewItem=new (Double_List);

NewItem->Data=DataItem;

NewItem->Prior=NULL;

NewItem->Next = NULL;

if (Head == NULL) {//список пуст

Head = NewItem;

}

else {//список не пуст

Double_List *Current=Head;

for (int i=1; i < Number && Current->Next≠NULL; i++)

Current=Current->Next;

if (Number == 0){

//вставляем новый элемент на первое место

NewItem->Next = Head;

Head->Prior = NewItem;

Head = NewItem;

}

else {//вставляем новый элемент на непервое место

if (Current->Next ≠ NULL) Current->Next->Prior = NewItem;

NewItem->Next = Current->Next;

Current->Next = NewItem;

NewItem->Prior = Current;

Current = NewItem;

}

}

return Head;

}

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

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

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

/*удаление элемента с заданным номером из двунаправленного списка*/

Double_List* Delete_Item_Double_List (Double_List* Head,

int Number){

Double_List *ptr;//вспомогательный указатель

Double_List *Current = Head;

for (int i = 1; i < Number && Current ≠ NULL; i++)

Current = Current->Next;

if (Current ≠ NULL){//проверка на корректность

if (Current->Prior == NULL){//удаляем первый элемент

Head = Head->Next;

delete (Current);

Head->Prior = NULL;

Current = Head;

}

else {//удаляем непервый элемент

if (Current->Next == NULL) {

//удаляем последний элемент

Current->Prior->Next = NULL;

delete (Current);

Current = Head;

}

else {//удаляем непервый и непоследний элемент

ptr = Current->Next;

Current->Prior->Next =Current->Next;

Current->Next->Prior =Current->Prior;

delete (Current);

Current = ptr;

}

}

}

return Head;

}

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

Операция поиска элемента в двунаправленном списке реализуется абсолютно аналогично соответствующей функции для однонаправленного списка. Поиск элемента в двунаправленном списке можно вести:

а) просматривая элементы от начала к концу списка;

б) просматривая элементы от конца списка к началу;

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

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

bool Find_Item_Double_List (Double_List* Head,

int DataItem){

Double_List *ptr; //вспомогательный указатель

ptr = Head;

while (ptr ≠ NULL){//пока не конец списка

if (DataItem == ptr->Data) return true;

else ptr = ptr->Next;

}

return false;

}

2.6 Проверка пустоты двунаправленного списка

Операция проверки двунаправленного списка на пустоту осуществляется аналогично проверки однонаправленного списка.

//проверка пустоты двунаправленого списка

bool Empty_Double_List (Double_List* Head){

if (Head≠NULL) return false;

else return true;

}

2.7 Удаление двунаправленного списка

Операция удаления двунаправленного списка реализуется аналогично удалению однонаправленного списка.

//освобождение памяти, выделенной под двунаправленный список

void Delete_Double_List (Double_List* Head){

if (Head ≠ NULL){

Delete_Double_List (Head->Next);

delete Head;

}

}

3. Реализация однонаправленных и двунаправленных списков

Пример 1. Nнатуральных чисел являются элементами двунаправленного списка L, вычислить: X1*Xn+X2*Xn-1+…+Xn*X1. Вывести на экран каждое произведение и итоговую сумму.

Алгоритм:

1. Создаём структуру.

2. Формируем список целых чисел.

3. Продвигаемся по списку: от начала к концу и от конца к началу в одном цикле, перемножаем данные, содержащиеся в соответствующих элементах списка.

4. Суммируем полученные результаты.

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

//поиск последнего элемента списка

Double_List* Find_End_Item_Double_List (Double_List* Head){

Double_List *ptr; //дополнительный указатель

ptr = Head;

while (ptr->Next ≠ NULL){

ptr = ptr->Next;

}

return ptr;

}

//итоговая сумма произведений

void Total_Sum (Double_List* Head) {

Double_List* lel = Head;

Double_List* mel = Find_End_Item_Double_List (Head);

int mltp, sum=0;

while (lel ≠ NULL) {

mltp = (lel->Data)*(mel->Data);//умножение элементов

printf («nn%d * %d = %d», lel->Data, mel->Data, mltp);

sum = sum + mltp;//суммирование произведений

lel = lel->Next;

//идем по списку из первого элемента в последний

mel = mel->Prior;

//идем по списку из последнего элемента в первый

}

printf («nn Итоговая сумма равна %d», sum);

}

Заключение

однонаправленный двунаправленный список

Список является динамической структурой, для элементов которого определены операции включения, исключения.

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

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

В однонаправленном (односвязном) списке каждый из элементов содержит информационную часть и указатель на следующий элемент списка. Адресное поле последнего элемента имеет значение NULL.

Каждый элемент списка содержит ключ, который идентифицирует этот элемент.

Основными операциями с однонаправленными списками, являются: создание списка; печать (просмотр) списка; вставка элемента в список; удаление элемента из списка; поиск элемента в списке; проверка пустоты списка; удаление списка.

В двунаправленном (двусвязном) списке каждый из элементов содержит информационную часть и два указателя на соседние элементы.

Основные операции, выполняемые над двунаправленным списком, те же, что и для однонаправленного списка.

Список источников

http://www.intuit.ru/studies/courses/648/504/lecture/11 456?page=5

Лекция 30: Динамические структуры данных: однонаправленные и двунаправленные списки

Показать весь текст
Заполнить форму текущей работой