Lab_4

реклама
Лабораторная работа № 4.
Динамические структуры данных. Однонаправленный список
1. Цель работы
Целью работы является получение навыков описания и обработки информационнологических структур, принципиально реализуемых динамически: списков, стека, дека, очереди. Основой для моделирования перечисленных структур может служить однонаправленный список как структура с максимально широкими возможностями.
Понятие списка и его реализация средствами языка Паскаль известны из курса «Алгоритмические языки и программирование», поэтому в данной работе основной акцент делается на особенности обработки списков в Си.
2. Основные положения
2.1. Специфика реализации динамических структур
 Перечисленные в п. 1 информационно-логические структуры являются структурами многочисленных и весьма важных классов задач, однако статическая реализация этих
структур неэффективна или вообще невозможна (в отличие, например, от массивов). Поэтому соответствующие типы данных отсутствуют в языке и для их моделирования используется динамическая организация памяти. Поэтому за этими структурами традиционно закрепилось название «динамические». Язык предоставляет не готовые структуры данных, а базовые средства для их конструирования.
Для упомянутых структур данных функциональное и логическое описание не
совпадает с физическим, и для каждой из абстрактных структур при ее реализации как
ссылочной необходимы все три уровня описания. Акцент переносится на логический уровень, т.е. появляется необходимость в выборе или конструировании базовых структур и
операций, которые позволят перейти от функционального уровня к кодированию. Как следствие, программная реализация неоднозначна: одну и ту же функциональную операцию
можно смоделировать по-разному.
 Процедуры и схемы работы с однонаправленным списком лежат в основе работы
практически со всеми информационно-логическими структурами, реализуемыми динамически.
2.2. Информационно-логическая структура «однонаправленный список»
 Однонаправленный список – это упорядоченная совокупность элементов, в которой возможен последовательный доступ к любому элементу, удаление или добавление элемента в любое место списка.
 Операции абстрактного уровня (функциональная спецификация) определяют
структуру «список» через действия с этой структурой независимо от физической реализации.
Однонаправленным списком является структура, над которой определены следующие
операции.
1.
2.
3.
4.
5.
Сделать список пустым.
Проверить, список пуст/не пуст.
Установить указатель в начало списка.
Передвинуть указатель вперед (переход к следующему элементу).
Добавить элемент: а) в начало; б) в конец; в) в середину.
43
6. Взять (прочесть) элемент: а) из начала; б) с конца; в) из середины.
7. Удалить элемент: а) из начала; б) с конца; в) из середины.
Операции добавления/удаления из середины списка могут иметь модификации, которые оговариваются позже.
 Логическое описание определяет структуру списка с учетом реализации его в памяти компьютера как динамической структуры. Это означает, что память под элементы будет выделяться динамически, т.е. выделенные участки в общем случае расположены независимо, и упорядочены элементы могут быть только за счет организации ссылок между ними.
Тогда, во-первых, элемент списка должен включать информационную часть, так как
список создается для хранения информации.
Во-вторых, все элементы списка создаются динамически по отдельности, но должны
быть связаны в упорядоченную (один за другим) цепочку. Для этого каждый элемент должен
ссылаться на следующий, т.е. в его структуре должно быть предусмотрено поле ссылки, содержащее адрес следующего элемента.
Изобразим соответствующую структуру элемента в общем виде, сразу дав имена полям элемента, типам этих полей и типу элемента в целом.
Информация
inf
Имена полей
элемента
next
<информационная
часть>
<адресная
часть>
type_inf
Типы полей
элемента
ptr
elem
Ссылка на следующий элемент
Тип элемента
Рис. 4.1
Пусть an – адрес начала списка, т.е. адрес его первого элемента. Поскольку каждый
элемент списка, кроме последнего, ссылается на следующий за ним, достаточно задать адрес
начала списка, чтобы последовательно добраться до любого элемента.
За последним элементом ничего не следует, поэтому содержимое поля ссылки этого
элемента должно быть равно NULL.
Тогда структуру списка в целом можно изобразить следующим образом:
an
......
<next>
<next>
<next>
Рис. 4.2
44
NULL
Итак, однонаправленный список задается:
- адресом первого элемента (обозначенным здесь an);
- типом элемента (этот тип назван elem); при этом элемент обязательно содержит по
крайней мере два поля – информационное и поле ссылки на следующий элемент.
 Принципы реализации в языке Си (на исполнительном уровне)
Тип type_inf информационной части списка может быть любым, кроме файлового.
Чтобы не ограничивать общности описания, будем полагать, что в каждом конкретном случае этот тип подлежит отдельному определению, а тип элемента включает всегда два поля,
представленных на рис. 4.1. Очевидно, это тип-структура.
Замечание. Тип второго поля должен быть в таком случае типом-указателем на тип
элемента в целом, то есть определяться раньше, чем тип элемента. В языке Си такие опережающие описания запрещены (в отличие от Паскаля, где для указанное противоречие разрешается так: для единственного типа «указатель» делается исключение и допускается описание типа «указатель» на еще не описанный тип). Поэтому следует дать имя шаблону
структуры и вместо указателя на тип элемента описать указатель на структуру с этим именем.
Для определенности опишем общий случай, когда информационная часть элемента
также представлена структурой.
//типы полей и элемента списка
typedef //тип информационной части
struct {
<описание полей информационной части>
} type_inf;
typedef //тип элемента списка
struct elem {
type_inf inf;
//информационная часть элемента
struct elem* next; //поле ссылки на следующий элемент
} elem;
typedef //тип-указатель на тип элемента списка
elem* ptr;
//переменная – указатель на элемент списка
ptr an; //адрес начала списка
Описав далее операции функциональной спецификации с помощью средств Си, получим реализацию в Си информационно-логической структуры «однонаправленный список».
Чтобы на уровне вызывающей программы каждая абстрактная операция реализовывалась как одна операция языка программирования, оформим абстрактные операции в виде
процедур.
Замечание. По функциональным возможностям описываемая реализация (описание
данных и процедуры-операции) может рассматриваться как тип данных «список», созданный нами, хотя средствами процедурного языка описать такой тип формально нельзя. Но это
уже база для описания класса «список», что вплотную подводит нас к объектноориентированной парадигме.
45
3. Реализация алгоритмов и функций обработки списков
 Общие замечания
Состав процедур и их описание определены в курсе «Алгоритмические языки и программирование» и подробно описаны в [3]. Поэтому здесь интересно только оформление
этих процедур в виде функций Си и описание интерфейсных частей этих функций, представляемое заголовками функций. Кодирование собственно алгоритмов функций является
технической задачей и легко может быть выполнено самостоятельно.
При описании процедур будем использовать введенные выше типы и имена.
Тип элементов списка должен быть глобальным по отношению ко всем процедурамоперациям.
«Передача списка» в функцию означает задание начального адреса списка как входного параметра.
Каждая абстрактная операция оформлена как функция, хотя для некоторых операций
это сделано в чисто методических целях.
 Некоторые дополнительные обозначения
Далее нам неоднократно потребуются некоторые переменные, используемые везде в
одном и том же смысле. Введем сразу имена этих переменных.
Пусть:
- an,ak – адреса начала и конца списка;
- am – некоторый заданный адрес элемента;
тип ptr
- l – адрес элемента,предшествующего элементу с адресом am;
- k – текущий адрес элемента списка;
- val – переменная для записи\чтения информационного поля;
возвращается или передается в процедуру как параметр;
тип type_inf
- val_loc – локальная переменная процедур для обработки
информационного поля.
Проверка предположений, необходимых для работы функций (пустота или непустота
списка, наличие искомого элемента и т.д.), а также технические детали (фиксация результатов проверки, сравнение структурированных элементов и т.д.) непосредственного отношения к работе со списком как с динамической структурой не имеют и потому опущены. В реальных задачах, разумеется, все эти действия должны быть выполнены!
 Основные процедуры для работы с однонаправленным списком: реализация
операций функциональной спецификации
Специфика кодирования в Си
 Выходные значения через список параметров должны быть переданы по указателю. Во избежание смешения типов формируемых функцией выходных переменных и типов
соотпараметров функции (указателей на соответствующие типы) приняты следующие обозначения: если функция передает значение некоторой переменной как выходной по указателю через список параметров, то имя соответствующего параметра-указателя снабжено цифрой «1» в конце. Например, если функция меняет адрес an начала списка (описание: ptr an),
то соответствующий формальный параметр функции имеет вид: ptr* an1.
 Из-за отсутствия в языке логического типа целесообразно в качестве эквивалента
использовать целый тип; предлагается кодировать логическое значение «истина» как «1»,
логическое значение «ложь» как «0».
Замечания
 Функции представлены их заголовками.
 При кодировании сохранена преемственность с предшествующим курсом. Алгоритмы, оформленные в виде процедур общего вида (procedure) в Паскале, здесь оформлены
как функции типа void. Это дает возможность безошибочной перекодировки с одного языка
на другой без дополнительных разъяснений.
46
 Адрес an начала списка в ряде функций не используется и мог бы быть опущен.
Однако во всех приведенных процедурах он присутствует в списке параметров из
концептуальных соображений, чтобы подчеркнуть: речь идет об информационно-логической
структуре “список” с начальным адресом an, а не о нескольких полях памяти, связанных
ссылками.
 В функции удаления элементов для реальных задач следует добавить операции
освобождения памяти.
 При составлении программ лучше все используемые функции описать в отдельном
файле.
 Приведенный ниже список заголовков легко преобразуется в список прототипов
функций.
 По желанию можно список прототипов функций разместить в отдельном заголовочном файле либо поместить в функцию main. Сравнить эти варианты и обосновать свой
выбор!
//Функции обработки однонаправленного списка
// Тип type_inf информационного поля может быть любым, кроме файлового
// an,ak – адреса начала и конца списка
// am – некоторый заданный адрес элемента
// l – адрес элемента, предшествующего элементу с адресом am
// k – текущий адрес элемента списка
//val – переменная для записи\чтения информационного поля
//val_loc – локальная переменная процедур для обработки информационного поля
//-------------------------------------------------------------------------------------------------------------//типы полей и элемента списка
typedef //тип информационной части
struct {
<описание полей информационной части>
} type_inf;
typedef //тип элемента списка
struct elem {
type_inf inf;
//информационная часть элемента
struct elem* next; //поле ссылки на следующий элемент
} elem;
typedef //тип-указатель на тип элемента списка
elem* ptr;
//Заголовки функций
//------------------------Вспомогательные процедуры обработки -----------------------------------// найти адрес последнего элемента
void adr_last (ptr an , ptr* ak1 )
// найти адрес l элемента,предшествующего элементу с адресом am
void pre_am (ptr an, ptr am , ptr* l1 )
//найти адрес am элемента с инф. полем val; flag-индикатор наличия элемента
void val_inf (ptr an , type_inf val, ptr* am1 , int* flag1)
//создать список с начальным адресом an из файла f добавлением элемента в начало
void creat_beg (FILE* f, ptr* an1 )
//вывести список с начальным адресом an в файл r
void print_list (FILE r, ptr an )
//-------------------------------- Абстрактные операции 1-4 ------------------------------------------void del_list (ptr* an )
// сделать список пустым
int empty (ptr an )
// проверить, пуст ли список
void beg_list (an , ptr* k1 ) // установить указатель в начало
47
void next_(ptr* k1 )
// установить указатель на следующий элемент
//--------------- Абстрактная операция 5: процедуры добавления в список --------------------//------------------------элемента с информационным полем val -----------------------------------void add_beg (ptr * an1 , type_inf val)
// в начало
void add_end (ptr an , type_inf val, ptr * ak1 )
// в конец
void add_middle (ptr an, ptr am , type_inf val)
// после элемента с адресом am
//------------------------------------ Абстрактная операция 6: ------------------------------------------//---------- процедуры чтения информационной части элемента в переменную val ---------void take_beg (ptr an, type_inf* val1)
// из начала
void take_end (ptr an,type_inf* val1)
// из конца
void take_middle (ptr an, ptr am , type_inf* val1) // из элемента с адресом am
//--------------- Абстрактная операция 7: процедуры удаления элемента------------------------void del_beg (ptr * an1 )
// из начала
void del_end (ptr an , ptr * ak1 )
// из конца
void del_middle (ptr an, ptr am )
// после элемента с адресом am
4. Пример обработки однонаправленного списка
Представлена программа создания и вывода списка с использованием соответствующих функций. Этого достаточно для иллюстрации кодирования функций и самостоятельного
решения содержательных задач.
 Задача.
Имеется некоторое множество ссылок на книги (сведений о книгах), содержащих
информацию об авторе, заглавии книги, годе ее издания и месте издания. Общее число ссылок заранее неизвестно.
Сформировать однонаправленный список сведений о книгах, считав информацию из
входного файла, и вывести этот список в выходной файл.
 Входные данные
 Пусть исходные данные записаны в файле a:\dat.dat; соответствующий указатель
на файл - dat.
 Поскольку число ссылок на книги не оговорено, вводить ссылки следует до их исчерпания, т.е. до конца входного файла. Ссылки целесообразно в таком случае представлять
простой переменной, значения которой поочередно вводятся и обрабатываются. Пусть
val_loc – такая переменная.
 Пусть описание книги имеет следующую структуру.
val_loc
книга
author
автор
title
заглавие
Строки до 30 символов
refdat
year
год изд.
city
место изд.
Число
Строка до 10 символов
от 1 до 2000
Очевидно, такое описание представляется агрегированным типом “структура”.
48
 Разместим значение каждого признака в отдельной строке файла.
Тогда входная форма:
< val_loc.author>
< val_loc.title>
< val_loc.year>
< val_loc.city>
< val_loc.author>
< val_loc.title>
< val_loc.year>
< val_loc.city>
...........
и т.д. для каждой ссылки до исчерпания ссылок
После ввода полей очередной ссылки значение переменной val_loc должно быть записано в информационную часть очередного элемента списка.
 Выходные данные
 Пусть выходные данные записываются в файл a:\res.res; соответствующий указатель на файл - res.
 Для вывода ссылок используем ту же переменную val_loc. Перед выводом
очередной ссылки значение информационной части очередного элемента списка должна
быть записано в эту переменную.
 Разместим значение каждого признака в отдельной строке файла. Тогда выходная
форма будет совпадать со входной.
 Метод, алгоритм, программа
//Пример обработки однонаправленного списка
#include <stdio.h>
#include <alloc.h>
typedef
// тип информационной части элемента
struct {
char author[31];
char title [31];
int year;
char city [11];
} type_inf;
typedef
// тип элемента
struct elem {
type_inf inf;
// информационная часть
struct elem* next; // адрес следующего элемента
} elem;
typedef // тип ссылки на элемент
elem* ptr;
//------------------------------------------------
49
//------ Функция создания списка из файла добавлением в начало
//an1- указатель на указатель начала списка (т.е. *an1 - указатель на начало)
//f - указатель на входной поток
void creat_beg (FILE* f, ptr* an1)
{
ptr k;
type_inf val_loc;
*an1=NULL; //сделать список пустым
while (!(feof(f)))
{ //----- считывание по полям элемента перечня книг
fgets(val_loc.author,31,f);
fgets(val_loc.title,31,f);
fscanf(f,"%d\n",&(val_loc.year));
fgets(val_loc.city,11,f);
//----- выделение памяти под элемент списка
k=(elem*)malloc(sizeof(elem));
//----- заполнение полей списка
(*k).next=*an1;
(*k).inf=val_loc;
*an1=k;
//созданный элемент стал первым
}
}
//------------------------------------------------//------- Функция вывода списка с указателем an на начало в поток с указателем r
void print_list(FILE *r,ptr an)
{
ptr k;
type_inf val_loc;
k=an;
while (k!=NULL)
{
val_loc = (*k).inf;
fprintf(r,"%s \n %s \n %d \n %s \n",
val_loc.author,val_loc.title,val_loc.year,
val_loc.city);
k=(*k).next;
}
}
//------------------------------------------------//------- Главная функция
void main(int argc, char* argv[])
{
FILE *dat,*res;
ptr an;
dat=fopen(argv[1],"r"); res=fopen(argv[2],"w");
creat_beg(dat,&an);
print_list(res,an);
fclose(dat); fclose(res);
}
//-------------------------------------------------50
5. Задание
1. Задано множество объектов, описываемых совокупностью некоторых признаков.
Требуется:
- выбрать объекты, признаки которых удовлетворяют заданным условиям;
- распечатать заданную совокупность признаков этих объектов.
Выполнить задание в следующей модификации:
- из файла входных данных сформировать однонаправленный список;
- вывести этот список в выходной файл согласно выходной форме;
- обработав созданный исходный список, сформировать список элементов, удовлетворящих запросу;
- вывести результирующий список в выходной файл.
Варианты заданий взять из [3] (задание к лабораторной работе № 1, с. 9-11) или из
[4], п. 3.2, с. 21-24.
2. [4], задание 4.1, с. 27. Структуру информационного поля элемента списка брать в
соответствии с условием задачи; если ничего не оговорено, то произвольно.
6. Контрольные вопросы
1. Почему в функциях изменения.списка путем удаления или добавления элемента в
середину отсутствуют выходные значения?
2. Почему необходимо давать имя шаблону структуры при описании элемента списка?
3. Просмотреть список функций типа void и функции, имеющие одно выходное значение, перекодировать в фукции соответствующего типа.
4. Какими достаточно универсальными вспомогательными процедурами можно, по
Вашему мнению, дополнить предложенный список?
5. Какие модификации процедур вставки/удаления могли бы Вы предложить?
ЛИТЕРАТУРА
1. Касаткин А.И., Вальвачев А.Н. Профессиональное программирование на языке СИ.
От TURBO C к BORLAND C++. Минск: Вышейшая школа, 1992. 239 с.
2. Перевезенцева Е.С., Сорокин П.М. Нисходящее проектирование алгоритмов
с использованием процедур. М.: Изд-во МЭИ, 1997. 64 с.
3. Перевезенцева Е.С., Шамаева О.Ю. Обработка данных сложной структуры в языке
Паскаль. Лабораторные работы по курсу «Алгоритмические языки и программирование».
М.: Изд-во МЭИ, 1999. 48 с.
4. Методические указания по курсу «Основы программирования». Сборник заданий
к практическим и лабораторным занятиям. /Чуркина Л.В., Луканина В.И., Шевченко А.Г.
М.: Изд-во МЭИ, 1994. 40 с.
5. Бочков С.О., Субботин Д.М. Язык программирования СИ для персонального компьютера. М.: Радио и связь, 1990. 382 с.
6. Бруно Бабэ. Просто и ясно о BORLAND C++. М.: Бином, 1996. 408 с.
51
СОДЕРЖАНИЕ
Лабораторная работа № 1
Символьные строки (ASCIIZ – строки)...………………………………..................33
1. Цель работы ............................... ...................................................................33
2. Основные положения ............................... ....................................................33
3
2.1. Определение строки ……………………………………………………3
3
2.2. Способы описания строк ………………………………………………3
5
2.3. Использование литералов ……………………………………………...5
6
2.4. Обработка строк ………………………………………………………..6
8
3. Пример обработки строк ….......................................................................…8
15
4. Задание ....................................................................................….................
5. Контрольные вопросы .............................. .......................................…...... 15
Лабораторная работа № 2
Массивы и указатели. Моделирование одномерных массивов
16
с использованием динамической памяти ………….………………………………16
16
1. Цель работы ............................... .......................….......................................16
16
2. Основные положения …………………………………………………..….16
18
3. Одномерный динамический массив (вектор): сравнение моделей ……..18
19
4. Пример-схема работы с динамическим вектором ……………………….19
25
5. Задание .................................................................................….....................25
26
6. Контрольные вопросы .............................. ......................................….........26
Лабораторная работа № 3.
Массивы и указатели. Моделирование двумерных массивов
c использованием динамической памяти ................................................................ 27
27
1. Цель работы ............................... ..................................................................27
27
2. Основные положения ............................... ...................................................27
28
3. Возможные варианты представления двумерного массива .....................28
31
4. Пример-схема обработки двумерного динамического массива ………...31
41
5. Задание ...........................................................................................................40
42
6. Контрольные вопросы .............................. ...................................................41
Лабораторная работа № 4
43
Динамические структуры данных. Однонаправленный список ............................42
1. Цель работы ............................... .........................................................…..... 43
42
2. Основные положения ............................... ......................................... .........43
42
43
2.1. Специфика реализации динамических структур данных …..........42
2.2. Информационно-логическая структура
«однонаправленный список» ……………………………………..42
43
3. Реализация алгоритмов и процедур обработки списков ...........................45
46
4. Пример обработки однонаправленного списка ……………………………47
48
5. Задание ...........................................................................................................50
51
6. Контрольные вопросы .................................................................................50
51
Литература .......................................................................................................................50
51
52
Скачать