7-Динамические структуры данных.ppt
- Количество слайдов: 43
Динамические структуры данных
Во многих программах работают с большими заранее неизвестными объемами данных. Использование статических массивов в этих случаях неэффективно: Во-первых, не всегда можно определить верхнюю границу объема данных. Во-вторых, если такая граница существует, то реально будет наблюдаться перерасход памяти.
Для эффективного хранения информации используются динамические структуры данных, память под которые выделяется и освобождается по мере необходимости. Область свободной памяти, доступной для выделения, называется кучей ( heap). Куча расположена между памятью программы и стеком.
В языке Си для выделения памяти используются библиотечные функции malloc(), calloc(), realloc() (stdlib. h), а в языке Си++ добавляется операция new. Если данные становятся ненужными, то память освобождается c помощью функции free()(в языке Си) или операции delete (в языке Си++).
Формирование массивов динамической памяти с помощью библиотечных функций: Функция Описание функции malloc void *malloc(unsigned s) – возвращает указатель на начало блока динамической памяти длиной s байтов. calloc void *сalloc(unsigned n, unsigned m) –возвращает указатель на начало блока динамической памяти для n элементов длиной m байтов каждый.
Функция Описание функции realloc void *realloc(void *bl, unsigned s) – изменяет размер блока ранее выделенной динамической памяти до размера s байт( bl – адрес начала изменяемого блока) free void *free(void *bl) – освобождает ранее выделенный блок динамической памяти (bl – адрес первого байта)
Функции malloc(), calloc(), realloc() возвращают NULL, если память не выделена.
Создадим динамический целочисленный массив: void main () { int *X; // указатель на массив int N; // кол-во элементов массива int i; // индекс эл-та массива /* Запрашиваем кол-во эл-тов массива и создаем массив заданной длины */ printf("n. Input mass size="); scanf("%d", &N); X =(int *)calloc(N, sizeof(int)); // X =(int *)malloc(N*sizeof(int));
// Заполняем массив значениями for(i=0; i < N; i++) X[i]= i; // Распечатываем содержимое массива for(i= 0; i < N; i++) { printf("%d ", X[i]); } // Уничтожаем массив free(X); }
Функция sizeof()возвращает кол-во байт, которое занимает тип данных или переменная. Формат записи функции: sizeof(<тип данных>) или sizeof(<переменная>)
Операции new и delete С помощью операции new происходит выделение памяти; если по каким-либо причинам память не может быть выделена, то операция возвращает NULL.
Операция имеет два формата – один для скалярных типов данных, а другой для массивов: <указатель> = new <тип данных> <указ-ль> =new <тип данных>[<кол-во эл-в>]
Для освобождения памяти используется операция delete. Она также имеет два формата: delete <указатель> delete[] <указатель> // для массивов
void main() {int *Mass; // указатель на первый элемент массива int Size; // кол-во элементов массива int i; // индекс эл-та массива /* Запрашиваем кол-во эл-тов массива и создаем массив */ cout<<"n. Input size= "; cin>>Size; Mass= new int[Size]; // Заполняем массив значениями for(i= 0; i < Size; i++) Mass[i]= i; // Распечатываем содержимое массива for(i= 0; i < Size; i++) cout << Mass[i]; // Уничтожаем массив delete[] Mass; }
Из примера видно, что операция new имеет преимущества перед библиотечными функциями malloc() и calloc(), т. к. операция сама определяет размер типа данных и возвращает типизированный указатель. Динамический массив может изменять свои размеры в процессе работы программы. В языке Си для этого используют функцию realloc(), которая выделяет новый блок памяти, переписывает в него данные из старого блока памяти, а затем освобождает память, занимаемую старым блоком памяти.
Пример программы с realloc : int *Mas; // указатель на первый элемент массива int N; // кол-во элементов массива int i; /* Запрашиваем кол-во эл-тов массива и создаем массив заданной длины */ cout << "n. Input mass size= "; cin >> N; Mas=(int *)calloc(N, sizeof(int)); // Заполняем массив значениями for(i= 0; i < N; i++) Mas[i]= i;
/* Запрашиваем новое кол-во эл-тов массива cout << "n. Input mass size= "; cin >> N; Mas=(int*)realloc(Mas, N*sizeof(int)); // Распечатываем содержимое массива for(i= 0; i < N; i++) cout << Mas[i]; // Уничтожаем массив free(Mass);
В языке Си++ нет операции, аналогичной realloc(), поэтому программист должен самостоятельно реализовать действия, выполняемые функцией realloc(): int *X; // указатель на первый элемент массива int N; // кол-во элементов массива int *Old. X; // указатель на старый блок памяти int Old. N; // старое кол-во элементов массива int i; /* Запрашиваем кол-во эл-тов массива и создаем массив заданной длины */ cout <<"n. Input mass size= "; cin >> N; X = new int[N];
// Заполняем массив значениями for(i= 0; i < N; i++) X[i]= i; // Запрашиваем новое кол-во эл-тов массива Old. N = N; cout << "n. Input mass size= "; cin >> N; // Выдяеляем для массива новый блок памяти заданной длины Old. X = X; X = new int[N]; /* Переписываем в новый блок памяти содержимое массива из старого блока памяти */ for(i= 0; i < min(Old. N, N); i++) X[i]= Old. X[i]; // Освобождаем старый блок памяти delete[] Old. X;
// Распечатываем содержимое массива for(i= 0; i < N; i++) cout << X[i]); // Уничтожаем массив delete[] X;
Списки. Типы списков
К динамическим структурам данных относятся такие структуры, размер которых невозможно предсказать заранее. Они формируются в процессе работы программы. К ним относятся списки и деревья. Список — это набор элементов, размещаемых в динамической памяти и связанных между собой указателями на эти элементы.
Списки бывают линейными и кольцевыми, односвязными и двусвязными. Элемент односвязного списка содержит кроме непосредственно "полезной" информации также информацию о следующем элементе списка. Элемент двусвязного списка содержит информацию, как о следующем, так и о предыдущем элементах. Последний элемент кольцевого списка содержит указатель на первый элемент списка.
Линейный односвязный список представляет собой связанную структуру данных. Каждый элемент списка состоит из двух частей: информационной (данные) и адресной (указатель на следующий элемент списка):
Линейный односвязный список
Самыми распространёнными случаями линейного односвязного списка служат очередь и стек. Очередь — это список с таким способом связи между элементами, при котором новые элементы добавляются строго в конец списка, а извлекаются для обработки строго из начала ("Первым пришел — первым ушел). Указатель последнего элемента содержит NULL.
Стек — это список с таким способом связи между элементами, при котором новые элементы добавляются строго в начало списка и извлекаются для обработки также строго из начала списка ("первым пришел — последним ушел" ).
Реализация стека в С++
Приведем пример программы, в которой реализован список (стек) людей (ФИО) и соответствующей им информации. #include <stdlib. h> #include <stdio. h> #include <string. h> #include <iostream. h>
// Структура, описывающая человека: ФИО, дата рожд. struct MAN { char F[15], I[15], O[15], DR[10]; }; // Структура элемента стека struct STACK { MAN M; STACK *next; };
// Описание функций: // Добавление элементов в стек void add(STACK **head); // Проверка наличия элементов int iden(STACK *head, MAN *w); // Ввод информации о человеке void input(struct MAN *w); // Вывод содержимого стека void display(struct STACK *w. K);
// Вывод на экран одного человека STACK *print(STACK *h); // Удаление последнего элемента void del(STACK **head); // **** Главная программа ***** void main(void) { struct STACK *head=NULL;
while(1) { cout<<"Операции: "<<endl; cout<<"a -добавить элемент"<<endl; cout<<"p -вывод содержимого стека“ << endl; cout<<"d -удалить последний элемент“ <<endl; cout<<"q -выход"<<endl; fflush(stdin); /*очистка буфера ввода от ненужной информации */
switch(getch()) { case 'a': case 'A': add(&head); break; case 'p': case 'P': display(head); break; case 'd': case 'D': del(&head); break; case 'q': case 'Q': return; default: cout<<"Ошибка. Повторввода"<<endl; } cout<<"Нажмите любую клавишу"<<endl; getch(); } }
// Добавление элемента в стек void add(struct STACK **head) { struct STACK *tmp; struct MAN W; input(&W); // Ввод информации о человеке // Если человека в списке нет if(iden(*head, &W)==0) { tmp=new STACK; if(tmp==NULL) { cout<<"Нет свободной памяти"; return; }
else // Если память выделилась { tmp->M = W; //запоминаем информацию о человеке tmp->next=(*head); /* новый элемент указывает на головной*/ (*head)=tmp; /* новый элемент становится головным */ } } else cout <<"Такой элемент cуществует“ <<endl; return; }
/**** Проверка наличия элемента в стеке ***/ int iden(STACK *head, MAN *w) { while(head) //пока не достигнем конца стека { if(strcmp(head->M. F, w->F)==0 && strcmp(head->M. I, w->I)==0 && strcmp(head->M. O, w->O)==0 && strcmp(head->M. DR, w->DR)==0) return 1; // Если элемент присутствует head=head->next; /* Если отстутствует, двигаемся по стеку */ } return 0; /* head==NULL – элемент не встретился*/ }
/****** Ввод информации о человеке *****/ void input(MAN *w) { сout <<"Введите ФИО Дату_рождения" <<endl; fflush(stdin); Cin>> w->F>> w->I>> w->O>> w->DR; }
/******* Вывод содержимого стека *******/ void display(STACK *h) { cout<<"В стеке содержится: "<<endl; if(!h) { cout<<"Список пуст"<<endl; return; } do { print(h); // Вывод элемента h=h->next; // Смещение по стеку } while(h); cout<<" "<<endl; return; }
/* Вывод на экран одного элемента */ STACK *print(STACK *wk) { cout<< wk->M. F<< " "<< wk->M. I<< " "<< wk->M. O<< " "<< wk->M. DR<< endl; return wk; }
/***** Удаление последнего элемента стека ******/ void del(struct STACK **head) { struct STACK *current; if(!*head) return; // Если стека нет current=*head; // Копия указателя на начало cтека *head=current->next; /* Второй элемент становится головным*/ delete current; // удаляем первый элемент cout<<"Элемент удалён"<<endl; return; }
7-Динамические структуры данных.ppt