- •Тема 1. Основные этапы решения задач на эвм 5
- •Тема 2. Жизненный цикл программы. Критерии качества программы. 15
- •Тема 3. Схемы алгоритмов, данных, программ 29
- •Тема 1. Основные этапы решения задач на эвм Постановка задачи разработки программного обеспечения
- •Анализ формальной постановки задачи
- •Выбор или разработка математической модели и метода решения
- •Разработка алгоритма
- •Базовые структуры алгоритма
- •3.2. Цикл с постусловием.
- •Тема 2. Жизненный цикл программы. Критерии качества программы.
- •Техническое задание и спецификация программы
- •Разработка проекта программной системы
- •Программирование (кодирование) или программная реализация алгоритмов
- •Тестирование и отладка
- •Эксплуатация и сопровождение
- •Критерии качества программного обеспечения
- •Тема 3. Схемы алгоритмов, данных, программ
- •Символы данных
- •Отображает данные, вводимые в ручную, во время обработки с устройств любого типа (клавиатура, переключатели, кнопки, световое перо, полоски со штрих кодом и т.Д.).
- •Символы процесса
- •Символы линий
- •Специальные символы
- •Правила применения символов в схемах
- •Правила выполнения соединений
- •Специальные условные обозначения
- •Тема 4. Язык программирования высокого уровня Си Общие сведения о языке Си
- •Алфавит языка Си
- •Грамматика для описания языка, синтаксические диаграммы
- •Структура программы на языке Си
- •Void main() //функция main
- •Имена объектов в программе
- •Выражения, операции и приоритеты
- •Тема 5. Стандартные типы данных
- •Тема 6. Составные типы данных Данные регулярного типа (массивы)
- •Int b [n]; // вектор из 10 целых элементов
- •9 Strcpy(s1,&s2[k]); //копирует правую подстроку из s2 в s1
- •9 Strncpy(s1,&s[2],n); //копирует среднюю подстроку из s2 в s1
- •Void main() /*пример функции*/
- •If(strcmp(s, "пароль"))
- •If(!strсmp("quit", s)) break;
- •Данные комбинированного типа (структуры)
- •Int month;
- •Int year;
- •Перечисления
- •Объединения
- •Указатели
- •Void *addres;
- •Int arrey[25];
- •Тема 7. Представление основных управляющих структур программирования Оператор присваивания
- •Составной оператор
- •Оператор перехода Goto
- •Условный оператор If
- •Оператор выбора switch
- •Операторы цикла while, do – while, for
- •Int I,j,imax,jmax,imin,jmin;
- •Операторы прерывания циклов
- •If (!flag) printf("Отрицательных чисел нет"); Форматированный ввод данных
- •Форматированный вывод данных
- •Преобразование типов
- •Инициализация данных
- •Тема 8. Функции
- •Определение функций в языке Си
- •Int rus (unsigned char r)
- •Void change (int X, int y)
- •Void change (int *X, int *y)
- •Вызов функций в языке Си
- •Int *fun (intx,int *y);
- •Int main()
- •Рекурсивные функции
- •Int nodWhile (int m, int n)
- •Int nodWhile (int m, int n)
- •Int main()
- •Int fCalculated[nFib];
- •Int FibDinam (int n)
- •Int main()
- •Int Summa(int n, int a[100])
- •Int main()
- •Тема 9. Файлы
- •Int fseek(file *fp, long count, int access);
- •Int ferror(file *fp);
- •Int remove(char *file_name);
- •Void rewind(file *fp);
- •Int main()
- •Тема 10. Приемы программирования. Примеры алгоритмов Алгоритмы сортировки
- •Исходный массив
- •Void SortBubble (int count, int* pArr)
- •Исходный массив
- •Void SortSelect(int count, int* pArr)
- •Int i1,temp;
- •Int jmax;
- •Void SortInsert (int count, int* pArr)
- •Int temp, j;
- •Алгоритмы поиска
- •Int bfSearch(char *s, char *p)
- •Int bmtarr[255];
- •Int bmSearch(int startpos, char *s, char *p)
- •Int BinarySearch (int lb, int ub, int key, int* pArr)
- •Динамические структуры данных
- •Линейные списки
- •Int value; // значение элемента
- •Void PrintSearchList (list head, int val)
- •If (lfound) printf("Элемент в списке найден!");
- •Стек, очередь, дек
- •Int prior(char);
- •Void main(void)
- •Int k, point;
- •Int prior(char a)
- •Деревья
- •Int info; //информационное поле
- •Приложение 1. Стандартные библиотеки языка Си
- •Приложение 2. Примеры реализации алгоритмов
- •Int main()
- •Int arr[10]; // Массив arr из 10 целочисленных элементов
- •Int I; // Счетчик для циклов
- •Int main()
- •Int main()
- •Int main()
- •Int Temp;
- •Int CurrentYear, Diff, Day1, Day2, Month1, Month2, I, Visokos;
- •Int main()
- •InsertSort(d, max); // Сортируем массив b методом вставок
- •Int number;
- •Int main()
- •Не рекурсивный алгоритм решения задачи Ханойская башня.
- •Int main()
- •Рекурсивный алгоритм решения задачи Ханойская башня.
- •Void move(int I, int j, int d)
- •Void hanoy(int I, int j, int k, int d)
- •Int main()
- •Int Cubic(double *X,double a,double b,double c);
- •Int Cubic (double *X, double a, double b, double c)
- •Void lu_backsub (double **a, int n, int *indx, double *b)
- •Void lu_invert (double **a, int n, int *indx, double **inv, double *col)
- •Int BracketRoot (double x0, double *a, double *b, double d0, double di, double dmax, double (*fun)(double));
- •Int BracketRoot (double x0, double *a, double *b, double d0,
- •Int main()
- •Int expo, I;
- •If (expo & 1)
- •Int main()
- •Приложение 3. Лабораторные работы Лабораторная работа №1
- •Лабораторная работа №2
- •Лабораторная работа №3
- •Лабораторная работа №4
- •Лабораторная работа №5
- •Лабораторная работа №6
- •Лабораторная работа №7
- •Лабораторная работа №8
- •Лабораторная работа №9
- •Лабораторная работа №10
- •Лабораторная работа №11
- •Лабораторная работа №12
- •Список литературы
Линейные списки
Списком называется упорядоченное множество, состоящее из переменного числа элементов, к которым применимы операции включения, исключения. Список, отражающий отношения соседства между элементами, называется линейным. Длина списка равна числу элементов, содержащихся в списке, список нулевой длины называется пустым списком. Линейные связные списки являются простейшими динамическими структурами данных.
Графически связи в списках удобно изображать с помощью стрелок, как было показано в разделе описания указателей. Если элемент списка не связан с любым другим, то в поле указателя записывают значение, не указывающее ни на какой элемент (пустой указатель). Такая ссылка в Паскале обозначается nil, а на схеме обозначается перечеркнутым прямоугольником. Ниже приведена структура односвязного списка (рис.40), т.е. связь идет от одного элемента списка к другому в одном направлении. Здесь поле D – это информационное поле, в котором находятся данные (любой допустимый в языке Паскаль тип), поле N (NEXT) – указатель на следующий элемент списка.
Каждый список должен иметь следующие указатели: Head – голова списка, Cur – текущий элемент списка, иногда в односвязных списках также используется указатель Tail – хвост списка (в двухсвяхных списках он используется всегда). Поле указателя последнего элемента списка является пустым, в нем находится специальный признак nil, свидетельствующий о конце списка и обозначается перечеркнутым квадратом.
Двухсвязный линейный список отличается от односвязного наличием в каждом узле списка ещё одного указателя B (Back), ссылающегося на предыдущий элемент списка (рис.41).
Структура данных, соответствующая двухсвязному линейному списку описывается на языке Си следующим образом:
struct list
{
Int value; // значение элемента
struct list *next; // указатель на следующий элемент
struct list *back; // указатель на предыдующий элемент
}
При работе с линейными списками используются следующие основные операции:
-
создание списка;
-
добавление нового элемента в список: в голову списка, в хвост списка, после текущего элемента списка, перед текущим элементом списка;
-
удаление элемента из списка;
-
обход списка с целью его обработки;
-
поиск узла в списке;
-
переход на узел в списке по номеру от начала и другие операции.
Создание списка
При создании списка нужно выполнить следующие действия (рис. 42):
-
выделить память;
-
«обнулить» указатели на предыдущий и последующий элементы;
-
определить указатели на голову, хвост и текущий элемент списка;
-
внести в информационную часть списка данные.
Добавление нового узла в список
Для добавления нового узла в голову списка нужно выполнить последоватеьность действий, указанных в табл.15.
Таблица 15.
Добавление нового узла в голову списка
№ |
Действие |
Иллюстрация |
1. |
Выделить память под новый узел списка: list* ptr = new list. |
ptr
|
2. |
Заполнить информационную часть узла и обнулить указатели в нем |
данные ptr
|
3. |
Если список пуст, то этот узел сделать головой списка: head = ptr. |
данные head |
4. |
Если список не пуст, то |
|
4.1. |
Для нового узла следующий указатель установить на голову текущего списка: ptr -> next = head. |
ptr
head
|
4.2. |
Для головы списка предыдущий указатель установить на новый узел: head -> back = ptr. |
ptr
head
|
4.3. |
Установить на новый узел указатель на голову списка: head = ptr. |
ptr
head
|
int val;
list head;
list* ptr = new list;
ptr->value = val;
ptr->next = NULL;
ptr->back = NULL;
if (head == Nil) head = ptr //список пустой?
else
{
Ptr->next = head;
Head->back = ptr;
Head = ptr;
}
Добавление в хвост списка и после текущего узла списка (или перед текущим) делается аналогично:
int s
list tail;
list* ptr = new list;
ptr->value = val;
ptr->next = NULL;
ptr->back = NULL;
if (tail == NULL) tail = ptr //список пустой?
else
{
tail->next = ptr;
prt->back = tail;
tail = ptr;
}
Удаление узла из списка
При удалении узла из списка нужно рассмотреть следующие ситуации:
-
Удаляемый узел является головой списка.
-
Удаляемый узел является хвостом списка.
-
Удаляемый узел единственный в списке.
-
Удаляемый узел не является ни головой, ни хвостом, ни единственным в списке.
В конце процедуры удаления обязательно необходимо освободить ранее выделенную динамическую память для удаляемого узла с помощью стандартной процедуры free(). Разберем подробно последний случай 4, когда удаляется узел не с краев, а внутри списка (табл.16).
Таблица 16.
Удаление узла внутри списка
№ |
Действие |
Иллюстрация |
1. |
На текущий узел указывает предыдущий узел. Этот указатель нужно «перекинуть» на следующий за текущим узел: cur->back->next =cur->next;
|
cur->back->next Cur->next
Cur->back
cur
cur->next->back
|
2. |
С
cur->back->next
cur->next->back = cur->back;
|
Cur->next
Cur->back
cur
cur->next->back back
|
3. |
З
cur->back->next
|
Cur->next
Cur->back
cur
cur->next->back
tail
|
4. |
Т
cur->back->next
cur = cur->next;
|
Cur->next
Cur->back
cur
cur->next->back
tail
|
5. |
Освобождаем память, выделенную динамически для удаляемого узла списка: free(tail); |
cur
|
//удаление узла в списке
list head,tail,cur, temp;
// проверяем является ли узел единственным в списке?
if ((cur->next == NULL) && (cur->back = NULL))
{
head = NULL;
tail = NULL; // делаем список пустым
free(cur);
cur = NULL; //освобождаем память
}
else
if (cur->next == NULL) // текущий узел является хвостом?
{
tail = tail->back; // Хвостом теперь станет предпоследний узел
tail->next = NULL;
free (cur);
cur = tail;
}
else
if (cur->back == NULL) // текущий узел является головой?
{
head = head->next; // головой становится второй узел списка
head->back = NULL;
free (cur);
cur = head;
}
else //узел ни голова, ни хвост, ни единственный в списке
{
cur->back->next = cur->next;
cur->next->back = cur->back;
temp = cur;
cur = cur->next;
free (temp);
};
}
Обход списка и переход к узлу по номеру от головы списка
Обход списка осуществляется с целью анализа, обработки и поиска данных находящихся в списке. Заметим, что переход к следующему узлу осуществляется присваиванием указателю C на текущий узел значения указателя на следующий узел cur->next. Но по определению списка, последний узел в списке содержит пустой указатель на следующий узел. А значит это и является признаком конца обхода списка. Таким образом, стандартный алгоритм обхода списка прост:
-
Выполнять в цикле:
-
Обработать текущий узел.
-
Перейти к следующему узлу.
-
До тех пор пока указатель на следующий узел станет пустым.
Пример 34. Напишем процедуру обохода списока с целью вывода его содержимого на экран и поиска совпадающей строки, а также функцию перехода к узлу списка по номеру.