logo search
Metod_ukazanija C_1-8

Краткая теоретическая справка и пример решения задачи

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

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

Для описания данной структуры в языке Си обычно используют два варианта записи. В первом варианте, представленном ниже, вводится только тип структуры без определения типа данных «указатель на структуру» (пример в проекте 8_1).

typedef struct TList { // структура типа TList

char D[80]; // информационное поле структуры (данные)

struct TList *P; // адресное поле структуры (адрес)

} TList;

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

struct TList *slova = NULL; // Первый элемент списка слов

struct TList *razd = NULL; // Первый элемент списка разделителей

Для добавления элемента списка в начало списка введем функцию AddFirst.

// 1. Функция добавления элемента в начало списка

struct TList * AddFirst(struct TList* First, char *s)

{ struct TList *tmp; // временная переменная

tmp = (struct TList*)malloc(sizeof(struct TList));

strcpy(tmp->D, s); // заполнить поле данных

tmp->P = First; // поставить перед первым

return tmp; // вернуть значение

}

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

Теперь можно изменить функцию выделения из предложения слов или разделительных знаков (обратите внимание на список передаваемых в функцию параметров).

// 2. Выделение из предложения (слов или разделителей)

struct TList* Select(const char *predl, struct TList *First, char *sel)

{ char *p, *s;

s=strdup (predl); // Сделать копию исходного предложения

p = strtok (s, sel);

while (p)

{

First = AddFirst(First,p); // добавить элемент в список

p = strtok (NULL, sel);

}

free (s);

return First;

}

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

// 3. Распечатать список

void Print (struct TList *First)

{

while (First != NULL)

{

puts(First->D); // вывод данных

First = First->P; //перевод указателя на следующий элемент

}

}

Распечатав односвязный список, можете заметить, что элементы в нем располагается в порядке, обратном вводимым данным. Если требуется восстановить исходный порядок, достаточно воспользоваться функцией Reverse, которая создает новый список, элементы которого будут добавляться в новый список также функцией AddFirst, то есть в обратной последовательности. Обратите внимание на использование статической переменной

// 4. Сделать список в обратной последовательности

struct TList * Reverse(struct TList * First)

{ static struct TList * tmp = NULL;

while (First!=NULL)

{

tmp = AddFirst(tmp,First->D);

First = First->P;

}

return tmp;

}

Аналогично тому, что делалось в примере к работе по теме №6, надо собрать результирующее предложение таким образом, чтобы слова следовали в обратной последовательности, а разделительные знаки – в прямой.

// 5. Сборка предложения

char *Constructor(struct TList * slova, struct TList * razd)

{

char res[80];

char *s;

*res=0; // Начальное значение результата пустое

while ((slova!=NULL)&&(razd!=NULL)) // пока есть слова и разделители

{

if (slova!=NULL)

{

strcat (res, slova->D); // Добавление слова

slova = slova->P;

}

if (razd!=NULL)

{

strcat (res, razd->D); // Добавление знаков

razd = razd->P;

}

}

s = strdup (res);

return s;

}

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

// 6. Удаление всего списка

struct TList* FreeList(struct TList *First)

{ struct TList * tmp;

while (First != NULL)

{

tmp = First; // запомнить указатель на первый элемент

First = First->P; // переметить укзатель на следующий элемент

free(tmp); // удалить элемент

}

return NULL;

}