- •линейные списки Ранее - основная компонента структуры данных - массив (обычный или массив
- •Основная особенность: физическое размещение в памяти эл-тов списка не имеет никакого значения, все
- •struct elem // определение структурированного типа
- •Списковые структуры данных - динамические, т.к.:
- •порядок следования эл-тов def последовательностью
- •«обычные» (разомкнутые) списки имеют в качестве
- •Основная операция при работе с элементами массива
- •Список может содержать ограниченное количество
- •Необходимое изменение заголовка можно получить
- •// 3. Используется ссылка на указатель
- •в левой части операции присваивания должно находиться обозначение ячейки, в которую заносится новое
- •2.Адресная интерпретация. Содержимым указателя
- •Присваивание
- •с помощью односвязного списка удобно представ-лять
- •//Включение в начало списка - помещение в стек void PUSH(list *&ph,int v)
- •Чтобы не «мотаться все время» к концу односвяз-ного
- •Дополнительная проверка «крайних ситуаций» -
- •//--- Сортировка односвязного списка вставками
- •Двусвязный список - позволяет двигаться по цепочке
- •Чтобы учесть эти варианты при уже написанном
- •Сравнение операций включения с сохранением
- •Особенности такого списка:
- •Цикл просмотра списка завершается, когда ука-
- •//--- Включение в циклический список с //сохранением
- •Ошибки при работе со списками - могут возникать
линейные списки Ранее - основная компонента структуры данных - массив (обычный или массив указателей).
Построение структуры данных, исходя только из указателей цепочка (последовательность) эл-тов, содержащих указатели друг на друга.
Линейная (список как линейная последовательность элементов, каждый из которых содержит указатели (ссылается) на своих соседей) или ветвящейся (деревья, графы).
Основная особенность: физическое размещение в памяти эл-тов списка не имеет никакого значения, все определяется наличием ссылок на него в других элементах и извне. У массива всегда есть «начало», а у списка по определению отсутствует фиксированная привязка к памяти.
Основной эл-т списка - составная (структурированная) переменная, содержащая собственно хранимые данные и указатели на соседей.
struct elem // определение структурированного типа
{ |
// значение элемента (хранимые данные) |
|||||
int value; |
||||||
elem *next; |
|
|
// единственный указатель или |
|
||
elem *next,*prev; |
// два указателя или |
|
||||
elem *links[10]; |
// ограниченное кол-во указателей |
|||||
elem **plinks; |
|
// (не больше 10) или |
|
|
||
// произвольное кол-во указателей |
|
|||||
}; |
|
// (динамический массив) |
|
|
||
|
|
|
|
|
|
|
Переменная такого типа может содержать 1, 2, не более |
||||||
10 и произвольное (динамический массив) кол-во |
||||||
указателей на аналогичные переменные, хотя это еще не |
||||||
список, а описание его составляющих (эл-тов) как типа |
||||||
данных. Из него следует только, что каждый из них |
||||||
ссылается на аналогичные эл-ты, но никак нельзя |
||||||
определить ни кол-ва таких переменных в структуре |
||||||
данных, |
ни |
характера |
связей |
между |
ними |
|
(последовательный, |
циклический, |
произвольный) |
конкретный тип структуры данных (линейный список,
дерево, граф) зависит от ф-ий, кот. с ней работают.
Списковые структуры данных - динамические, т.к.:
сами переменные таких структур создаются как динамические переменные, т. е. количество их может быть произвольным;кол-во связей между переменными и их характер также
def динамич. в процессе работы программы. В зависимости от связей списки бывают:
односвязные-каждыйэл-т имеет указательнаследующ;двусвязные - каждый эл-т списка имеет указатель на следующий и на предыдущий элементы;циклические - первый и последний эл-ты ссылаются друг на друга и цепочка представляет собой кольцо.
Основные свойства списка:
Эл-т списка доступен в программе через указатель, кот. отражает функцио-нальное назначение эл-та списка в программе: 1, последний, текущий, предыдущий, новый и т.п. Между указателем и эл-том списка имеется такая же связь, как между индексом в массиве и эл-том массива;в программе список задается посредством заголовка – указателя на первый эл-т списка;
порядок следования эл-тов def последовательностью
связей между эл-тами. Изменение порядка следова-ния эл-тов (вставка, удаление) осуществляются изме-нением (переустановкой) указателей на соседние эл-ты.
логический (порядковый) номер эл-та списка также задается его естественной нумерацией в цепочке эл-тов;список – структура данных с последовательным доступом. Для получения n-го по счету эл-та необхо-димо последовательно пройти по цепочке от эл-та, на который имеется указатель (например, от заголовка);список удобен для использования именно как дина-
мическая структура данных: эл-ты списка обычно соз- даются как динамические переменные, а связи меж-ду ними устанавливаются программно (динамически);список обладает свойством локальности изменений: при вставке/удалении эл-та изменения касаются толь-ко текущего и его соседей (в массиве при вставке/уда-лении его эл-тов происходит физическое переме-щение (сдвиг) всех элементов от текущего до конца.
преимущества списков проявляются в таких структурах данных, где операции изменения порядка превалируют
над операциями доступа и поиска.
«обычные» (разомкнутые) списки имеют в качестве
ограничителя последовательности NULL-указательвозможен случай пустого списка, в котором заголовок - указатель на первый элемент также содержит значение NULL.
замкнутый круг: чтобы работать со структурой дан- ных, необходимо ее создать, а при создании нуж-но знать технологические приемы работы с ней.
Работа со списками осуществляется исключи-тельно через указатели. Каждый из них может перемещаться по списку (переустанавливаться с эл-та на эл-т), приобретая одну из смысловых интерпретаций – указатель на 1, последний, текущий, предыдущий, новый и т.п. эл-ты списка. Аналогия с массивом и индексом в нем, но при условии, что индекс меняется линейно, а не произвольно, а текущее количество заполненных эл-тов в массиве задано отдельной переменной. В результате получим следующие выражения, определяющие базовые действия со списком:
|
Список |
Определение |
struct list {int val; |
|
list *next, *prev; }; |
Пустой список |
list *ph=NULL; |
Первый |
list *p; p=ph; |
Следующий |
p->next |
Предыдущий |
p->prev |
К следующему |
p=p->next |
К предыдущему |
p=p->prev |
Просмотр |
for (p=ph; p!=NULL; p=p->next) |
|
…p->val… |
Проверка на последн |
p->next ==NULL |
К последнему |
for (p=ph; p->next!=NULL; |
|
p=p->next); |
Новый |
list *q = new list; |
|
q->val = v; |
Включить последним for (p=ph; p->next!=NULL; |
|
|
p=p->next); |
|
q->next=NULL; p->next=q; |
Включить первым |
q->next=ph; ph=q; |
Массив
int A[100]; int n;
n=0;
Int i=0;
i+1
i-1
i++
i--
for (i=0; i<n; i++) …A[i]…
i==n-1 i=n-1
int v;
A[n++]=v;
for (i=n; i>0; i--)
Основная операция при работе с элементами массива
– « -> » выполняется в контексте: указатель на элемент - поле элемента списка.
сам список представляет собой множество связанных указателями переменных. В простейшем случае (для создания тестовых данных) можно - статический список: его эл-ты - обычные структурированные переменные, связи между ними инициализируются транслятором, и вся структура данных в программный код.
struct list { int val; list *next; }
a={0,NULL}, b={1,&a}, c={2,&b}, *ph = &c;
по условиям def переменных список создается «хвостом вперед». В двусвязном списке проблема «ссылок вперед» на еще неопределенные эл-ты решается: сначала переменные объявляются, как внешние, затем def и инициализируются.
struct list2 { int val; list *next,*prev;};
extern list2 a,b,c; // предварит def эл-тов списка list2 a={0,&b,NULL}, b={1,&c,&a}, c={2,NULL&b}, *ph = &c; // выделены «ссылки вперед»
Список может содержать ограниченное количество |
|||||
эл-тов, взятых из массива. Связи устанавливаются |
|||||
динамически, т. е. программой (используется, когда |
|||||
заданное количество эл-тов образуют несколько |
|||||
различных |
динамических |
структур |
(например, |
||
очередей), переходя из одной в другую). |
|
||||
list |
A[100],*ph; |
// Создать список эл-тов, |
|||
for (i=0; i<99; i++) |
// размещ в статич массиве |
||||
{ |
A[i].next = A+i+1; // Адрес следующ вычисляется |
||||
} |
A[i].val = i; |
|
|
|
|
A[99].next = NULL; |
|
|
|
||
ph = &A[0]; |
вариант |
полезен |
только в |
том случае, |
|
Но!!! Этот |
когда 1 (по счету) эл-т списка остается таковым в процессе работы со списком. Т.к. заголовок передается по значению (как копия), то его изменение никак не сказывается на оригинале – истинном заголовке списка в main.
Необходимое изменение заголовка можно получить
другими способами:
· возврат измененного значения заголовка в виде результата функции; · передача указателя на заголовок списка
(указателя на указатель); · передача ссылки на заголовок. Напомним, что
ссылка - неявный указатель, использующий при работе синтаксис объекта, который «отображается» на соответствующий ему фактический параметр.
//---- включение в начало списка с изменением //заголовка: 1. Измененный указатель возвращается
list *Ins1(list *ph, int v) { list *q=new list;
q->val=v; q->next=ph; ph=q; return ph; }
// 2. Используется указатель на заголовок void Ins2(list **pp, int v)
{ list *q=new list;
q->val=v; q->next=*pp; *pp=q; }