Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по СиАОД.doc
Скачиваний:
54
Добавлен:
27.10.2018
Размер:
759.3 Кб
Скачать

Реализация списков посредством массивов

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

Это представление позволяет легко просматривать содержимое списка и вставлять новые элементы в его конец.

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

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

первый элемент

второй элемент

список

Last

Последний элемент

свободный

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

Первое поле array – это элементы массива, чей размер считается достаточным для хранения списков любой длины, встречающихся в данной программе.

Второе поле целочисленного типа last, указывает на позицию последнего элемента списка в массиве.

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

Структура данных будет иметь вид:

#define MAXLENGTH 100

typedef float element_type;

typedef struct

{

element_type array[MAXLENGTH];

int last;

} LIST;

Реализация списков с помощью указателей

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

В случае пустого списка заголовок имеет указатель null, не указывающий ни на какую ячейку.

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

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

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

Однако ценой за это удобство становится дополнительная память для хранения указателей.

Формально определить структуру элемента однонаправленного списка можно так:

typedef struct

{

char name[20]; /* автор книги */

char title[44]; /* наименование */

int year; /* год издания */

float price; /* цена */

} BOOK;

struct LIST

{

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

BOOK info;

/* указатель на след. элемент */

struct LIST * next;

}

Во многих приложениях возникает необходимость организовать эффективное перемещение по списку как в прямом, так и в обратном направлении.

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

Такой список называется двусвязным.

struct LIST

{

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

BOOK info;

/* указатель на след. элемент */

struct LIST *next;

/* указатель на пред. элемент */

struct LIST *prev;

}

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

Замечание

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

Реализация списков

на основе курсоров

Некоторые языки программирования (например, Фортран и Алгол) не имеют указателей.

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

Для всех элементов списка создадим один массив space (область данных), состоящий из записей.

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

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

Значение space[head].element равно первому элементу списка, значение space[head].next является индексом ячейки массива, содержащей второй элемент списка и т.д.

Нуль в поле next означает, что последующих элементов нет.

space

element next

5

Head L

1

d

7

2

4

3

c

0

1

M

4

6

5

a

8

6

0

7

e

0

9

свободный

8

b

3

9

10

10

2

На рис. показаны два списка L=a.b.c и M=d,e, вложенные в один массив space длиной 10.

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

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

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

А вставляемый элемент помещаем в поле element этой ячейки.

Для удаления элемента из списка, мы помещаем его в начало свободного списка.