Пример создания связанного списка имен, упорядоченных по алфавиту
Включение узла в двунаправленный связанный список после элемента с адресом р (рис. 2.6).
Определение количества узлов в линейном однонаправленном списке (рис. 2.5).
Перемещение на следующий элемент списка Однонаправленный список
Удаление узла
ЦИКЛИЧЕСКИЙ ДВУНАПРАВЛЕННЫЙ СПИСОК
Ptrn2
Info
Ptrnl
Ptm2
Info
Ptrnl
ptrnl | info | ptrn2 |
ptrnl -NULL | 1 | ■ | ptrn2=NULL | ||||||||||
ptrnl | info | ptrn2 | —•■ ■*— | ptrnl | info | ptrn2 | —* —»■ *— -*— | ptrnl | info | ptrn2 | |||
. . | |||||||||||||
Рис. 2.4. Графическое представление двунаправленных связанных списков
Двунаправленные списки имеют следующее удобное свойство [11]: если р — указатель на элемент, то
left(right(p))=p=right(left(p)),
где right(p) — ссылка на адрес следующего элемента; \ей(р) — ссылка на адрес предыдущего элемента.
Операция, которую можно выполнить над двунаправленным списком, но нельзя над обычным — это удаление элемента с заданным адресом.
Пример удаления элемент с заданным адресом/?#•
if( ptr == NULL )
printf("Удаление запрещено."); else
{
х = info(ptr); g = left(ptr); r = right(ptr); right(g) = r; left(r) = g; freenode(ptr);
Реализация списков на языке Си
1. Узел однонаправленного списка
struct NODE | |
{ | |
<тип> info; | |
struct NODE | *ptrn; |
}; |
2. Узел двунаправленного списка
St { | ruct NODE | ||
struct | NODE | *ptrnl; | |
<тип> | info; | ||
struct | NODE | *ptrn2; | |
}; |
3. Указатель на список
struct NODE *lst; struct NODE *p;
4. Выделение блока оперативной памяти под узел
p=(struct NODE*)malloc( sizeof(struct NODE) );
5. Обращения к полям узла Однонаправленный список
p->ptrn=...; p->infо=...;
Двунаправленный список
р- | ->ptrnl=. | • • г |
р- | ->info=.. | • г |
р- | ->ptrn2=. | • • г |
free(p);
р= | =Р" | ->ptrn; | |||
Двунаправленный список | |||||
Р= Р= | =Р" =Р" | ->ptrnl; ->ptrn2; | /^перемещение /^перемещение | влево вправе | */ */ |
Примеры работы со списками
p=lst;
k=0;
while(p!=NULL)
{
k++; p=p->ptrn;
}
printf("\nB списке %о! узлов. ",k);
1st—^ | ptrn=NULL | |||||
info | ptrn | ► ... *■ | info | ptrn | ||
Рис. 2.5. Линейный однонаправленный список
p2=(struct NODE*)malloc(sizeof(struct NODE));
p3=p->ptrn2; p->ptrn2=p2; p2->ptrnl=p; p2->ptrn2=p3; p3->ptrnl=p2; p2->info=x;
Рис. 2.6
3. Удаление узла с адресом р из двунаправленного связанного списка (рис. 2.7).
x=p->infо; q=p->ptrnl; r=p->ptrn2; q->ptrn2=r; r->ptrnl=q; free(p);
Рис. 2.7 4. Создание линейного двунаправленного списка, состоящего из п узлов.
lst=(struct NODE*)malloc(sizeof(struct NODE;
lst->ptrnl=NULL;
lst->info=...;
pl=lst;
for(i=l;i<=n-l;i++)
{
pl->ptrn2=(struct NODE*)
malloc(sizeof(struct NODE)); p2=pl;
pl=pl->ptrn2; pl->info=...; pl->ptrnl=p2;
} pl->ptrn2=NULL;
Пример.Рассмотрим реализацию списка с двойными связями. Каждый узел содержит три поля: обратный указатель В, прямой указатель F и данные info (рис. 2.8). Рассматриваемый список является циклическим.
+ | ||||||||||
В | info | F | —*■ | В | info | F | В | info | F | |
+ | ||||||||||
Рис. 2.8. Пример Определим структуру, представляющую узел списка.
Узел списка
struct NODE
{
struct listnode *pred;
/^обратный указатель*/ struct listnode *succ;
/*прямой указатель*/ char data[NAME_SIZE];
/*поле данных*/ };
Разработаем программу, которая упорядочивает имена в поле info по алфавиту. Функция main в диалоге запрашивает вводимые имена и вызывает функцию insert, которая образует узел для очередного имени и включает его в соответствующее место списка.
Чтобы сохранить информацию о положении начального узла списка создадим особый узел, называемый головой списка. Прямому и обратному указателям головы присваивается адрес его начала, тем самым образуется пустой список.
Функция insert просматривает список, пока не найдет место для имени.
#include <stdio.h> #define NAME_SIZE 30
struct NODE
{
struct listnode*pred;
struct listnode*succ;
char data[NAME_SIZE]; };
main()
{
char name [NAME_SIZE];
struct NODE *root; /^Голова списка.*/
struct NODE *np; /^Используется при просмотре.*/
/*3анять блок памяти для головы списка и иницилизировать его так, чтобы он показывал сам на себя.*/ root=malloc(sizeof(struct NODE) ) ;
root->pred= root->succ=root; root->data[0]='\0' ;
/^Создать связанный список имен.*/ for ( ; ; )
{
printf ("имя:");
gets (name);
if (strcmp(name), "конец")==0)
break; if (insert(root, name)==0)
{
printf ("не хватает динамической памяти \п"); exit(1); } } /^Изобразить содержимое списка.*/
for (np=root->succ; np!=np->succ) printf ("имя=%з \n", np->data);
printf ("работа закончена \п"); }
/^Зарезервировать память для нового узла списка; скопировать в него имя и вставить новый узел в список в алфавитном порядке. Возвратить либо указатель на новый узел, либо NULL, если для создания узла не хватило памяти.*/
struct NODE *insert (node, name); struct NODE *node; /*Голова списка.*/ char *name;
{
NODE *np; /*Узел списка.*/ NODE *newnode;
/*Узел, вставленный в список.*/
/*Просматривать список, пока не обнаружится узел, поле info которого имеет значение, большее введенного имени или равное ему.*/
for (np=node->succe; (np!=node) &&
strcmp (name, np->data)>0); np=np->succe);
/^Зарезервировать память для нового узла; поместить введеное имя в его поле info и вставить новый узел перед тем, на который показывает указатель пр.*/
if( (newnode=malloc(sizeof(struct NODE)))!=0 ) {
strncpy(newnode->data, name, №\ME_SIZE);
newnode->succ=np;
newnode->pred=np->pred;
/^Изменить прямой указатель в узле, предшествующем вставленному (теперь он должен показывать на вставленный узел) и изменить обратный указатель в узле, следующем за вставленным.*/
(newnode->pred)->succ=newnode; np->pred=newnode; }
return (newnode); }