Презентация на тему: Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1

Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1
1/26
Средняя оценка: 4.2/5 (всего оценок: 43)
Код скопирован в буфер обмена
Скачать (222 Кб)
1

Первый слайд презентации

Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1

Изображение слайда
2

Слайд 2

Лекция 31.10.11г. 2 Динамически расширяемые массивы Массивы — простейший способ группировки данных; вовсе не случайно большинство языков имеют эффективные и удобные индексируемые массивы и даже представляют строки в виде массивов символов. Массивы просты в использовании, обладают фиксированным временем доступа к любому элементу, хорошо работают с двоичным поиском и быстрой сортировкой, а также почти совсем не тратят лишних ресурсов. Для наборов данных фиксированного размера, которые могут быть созданы даже во время компиляции, или же для гарантированно небольших объемов данных массивы подходят идеально. Однако хранение меняющегося набора значений в массиве может быть весьма ресурсоемким, поэтому, если количество элементов непредсказуемо и потенциально неограниченно, может оказаться удобнее использовать другую структуру данных.

Изображение слайда
3

Слайд 3

Лекция 31.10.11г. 3 Структура данных «вектор» ( Vector) Сконструируем абстракцию «вектор», которая во многом будет похожа на массив (состоит из однотипных элементов и обеспечивает прямой доступ к элементам по индексу), но допускает изменение размеров во время работы и неограниченное добавление новых элементов. Основные идеи для реализации этой абстракции: 1-я идея – использовать структуру языка С для представления вектора, членами которой будут: массив достаточного размера для хранения элементов вектора, число, равное количеству элементов вектора, число, равное текущему размеру массива. typedef struct Vector { int sz; double* elem; int space; } Vector; elem[0] elem[1] elem[…] elem[sz-1] … … … sz space

Изображение слайда
4

Слайд 4

Лекция 31.10.11г. 4 Структура данных «вектор» ( Vector) Выводы по предыдущему рисунку: а) «пустой вектор» – это: sz = 0, space = 0, elem = NULL. б) «полный вектор» – к вектору невозможно добавить новый элемент, т.к. нет свободного пространства: sz = space. 2-я идея – если при попытке добавления нового элемента обнаруживается состояние «полный вектор», то массив, в котором хранится вектор, увеличивает свой размер в два раза. Такое увеличение происходит за 3 шага: выделяется новая область памяти размером 2* space, «старый» массив переписывается в эту область, прежняя область освобождается и устанавливается space = 2* space.

Изображение слайда
5

Слайд 5

Лекция 31.10.11г. 5 Структура данных «вектор» ( Vector) Теперь начнем разработку функций, поддерживающих абстракцию «вектор». Первая из функций - init должна выполнять инициализацию вектора, т.е. создвать пустой вектор: void init(Vector *v) { v->sz = v->space = 0; v->elem = NULL; } Вторая функция - push_back добавляет к вектору новый элемент: void push_back(Vector *v, double d) { if (v->sz == 0) // память ещё не выделялась reserve (v, 8); else if (v->sz == v->space) // нет свободного пространства reserve (v, 2*v->space); v->elem[v->sz] = d; // добавляем новый элемент в конец ++v->sz; // инкрементируем счетчик } Что делает функция reserve ?

Изображение слайда
6

Слайд 6

Лекция 31.10.11г. 6 Структура данных «вектор» ( Vector) Функция reserve играет вспомогательную роль – перемещает вектор на новое место, размер которого больше, чем существующий размер: void reserve (Vector *v, int newalloc) { if (newalloc <= v->space) return; double* p = (double*)calloc(newalloc, sizeof(double)); int i; for (i=0; i<v->sz; ++i) p[i] = v->elem[i]; // копируем... if(v->elem) free(v->elem); // освобождаем память... v->elem = p; v->space = newalloc; } Наконец, ещё одна полезная функция изменения размера вектора resize : void resize (Vector *v, int newsize) { int i; reserve(v, newsize); for(i = v->sz; i<newsize; ++i) v->elem[i] = 0; v->sz = newsize; }

Изображение слайда
7

Слайд 7

Лекция 31.10.11г. 7 Структура данных «вектор» ( Vector) Проанализируем работу функции resize на примерах. Исходное состояние: sz = 10, space = 16. 12 9 7 23 35 4 8 11 45 6 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 После вызова resize(v, 14) : sz = 14, space = 16. 12 9 7 23 35 4 8 11 45 6 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 После вызова resize(v, 7) : sz = 7, space = 16. 12 9 7 23 35 4 8 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 После вызова resize(v, 18) : sz = 18, space = 18. 12 9 7 23 35 4 8 11 45 6 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 0 16 17

Изображение слайда
8

Слайд 8

Лекция 31.10.11г. 8 Структура данных «вектор» ( Vector) Теперь можно приступить к конструированию модульной структуры приложения, использующего абстракцию «вектор». Прежде всего, сформируем интерфейс, в который поместим описание структуры Vector, а также описания клиентских функций для работы с векторами: init, push_back и resize. Как отмечалось ранее, интерфейс помещается в заголовочный файл, который назовем Vector.h : // Vector.h typedef struct Vector { int sz; double* elem; int space; } Vector; void init(Vector*); void resize(Vector*, int); void push_back(Vector*, double); Реализация всех функций, как интерфейсных, так и вспомогательных, размещается в одном файле Vector.c :

Изображение слайда
9

Слайд 9

Лекция 31.10.11г. 9 // Vector.c #include <stdlib.h> #include "Vector.h" void init (Vector *v) { v->sz = v->space = 0; v->elem = NULL; } void reserve (Vector *v, int newalloc) { if (newalloc <= v->space) return; double* p = (double*)calloc(newalloc, sizeof(double)); int i; for (i=0; i<v->sz; ++i) p[i] = v->elem[i]; if(v->elem) free(v->elem); v->elem = p; v->space = newalloc; } void resize (Vector *v, int newsize) { int i; reserve(v, newsize); for(i = v->sz; i<newsize; ++i) v->elem[i] = 0; v->sz = newsize; } void push_back (Vector *v, double d) { if (v->sz == 0) reserve(v, 8); else if (v->sz == v->space) reserve(v, 2*v->space); v->elem[v->sz] = d; ++v->sz; }

Изображение слайда
10

Слайд 10

Лекция 31.10.11г. 10 Структура данных «вектор» ( Vector) Покажем, как можно использовать абстракцию Vector : #include <stdio.h> #include <stdlib.h> #include "Vector.h" void Print(Vector *v) { int i; printf("sz=%d, space=%d\nelem: ", v->sz, v->space); for(i = 0; i < v->sz; ++i) printf("%5.1f ", v->elem[i]); printf("\n----------------\n"); } int main() { Vector z; init (&z); int i; for(i = 0; i < 20; ++i) push_back (&z, (double)i*i); Print(&z); resize (&z, 25); Print(&z); resize (&z, 5); Print(&z); push_back (&z, 123.45); Print(&z); system("PAUSE"); return 0; }

Изображение слайда
11

Слайд 11

Лекция 31.10.11г. 11 Расширение функциональности Vector Модифицируем функцию быстрой сортировки для структуры данных Vector и включим ее в интерфейс: // эту строку помещаем в Vector.h void qsortv (Vector*, int, int); // определения функций помещаем в Vector.c void swapv (Vector *v, int i, int j) { double x; x = v->elem[i]; v->elem[i] = v->elem[j]; v->elem[j] = x; } void qsortv (Vector *v, int left, int right) { int i, last; if (left >= right) return; swapv(v, left, (left + right)/2); last = left; for (i = left + 1; i <= right; i++) if (v->elem[i] < v->elem[left]) swapv(v, ++last, i); swapv(v, left, last); qsortv(v, left, last-1); qsortv(v, last+1, right); }

Изображение слайда
12

Слайд 12

Лекция 31.10.11г. 12 Пример сортировки Vector #include <stdio.h> #include <stdlib.h> #include "Vector.h" void Print(Vector *v) { int i; printf("sz=%d, space=%d\nelem: ", v->sz, v->space); for(i = 0; i < v->sz; ++i) printf("%5.1f ", v->elem[i]); printf("\n----------------\n"); } int main() { Vector z; init(&z); int i; for(i = 0; i < 20; ++i) push_back(&z, (double)(20-i)*(20-i)); Print(&z); qsortv (&z, 0, 19); Print(&z); system("PAUSE"); return 0; }

Изображение слайда
13

Слайд 13

Лекция 31.10.11г. 13 Структура данных Vector. Выводы В рассмотренных выше примерах структура данных Vector предназначалась для хранения данных типа double. Очевидно, что не составит особого труда приспособить её для хранения данных других типов, например, структур: typedef struct Nameval { char *name; int value; } Nameval; typedef struct VectorNV { int sz; Nameval * elem; int space; } VectorNV; void init(VectorNV*); void resize(VectorNV*, int); void push_back(VectorNV*, Nameval ); void qsortv(VectorNV*, int, int); Второй вывод: на базе абстракции Vector можно конструировать другие динамические структуры данных, поддерживающие другие стратегии добавления и исключения элементов, например: стеки, очереди, кучи и хэш-таблицы.

Изображение слайда
14

Слайд 14

Лекция 31.10.11г. 14 Структура данных «стек» ( Stack) Стек – это одна из простейших динамических структур данных со специфическими правилами включения и удаления элементов. Стек работает по принципу «последним пришёл – первым ушёл» ( last-in, first-out – сокращённо LIFO ). Включение новых элементов в стек всегда производится в конце последовательности элементов (в вершине стека). Получить (выбрать) элемент из стека можно также только из вершины, прямого доступа к элементам стека по индексу нет. Для размещения стека удобно использовать массив, а еще лучше – вектор, т.к. стек – это динамическая структура. typedef struct Stack{ int top; Vector v; } Stack; elem[0] elem[1] elem[…] elem[top-1] … … … top

Изображение слайда
15

Слайд 15

Лекция 31.10.11г. 15 Структура данных «стек» ( Stack) Теперь начнем разработку функций, поддерживающих абстракцию «стек». Первая из функций – init_s должна выполнять инициализацию стека, т.е. создавать пустой стек: void init_s (Stack *st) { st->top = 0; init(&st->v); } Вторая функция - push_s добавляет к стеку новый элемент: void push_s (Stack *st, double d) { push_back(&st->v, d); // добавляем ++st->top; // инкрементируем вершину } Третья функция - pop_s извлекает из стека элемент: int pop_s (Stack *st, double *d) { if(st->top == 0) return 0; // если стек пуст *d = st->v.elem[--st->top]; --st->v.sz; return 1; } адрес вектора! результат – через аргумент!

Изображение слайда
16

Слайд 16

Лекция 31.10.11г. 16 Структура данных «стек» ( Stack) Теперь займемся конструированием модульной структуры приложения, использующего абстракцию «стек». По аналогии с абстракцией «вектор» сначала сформируем интерфейс, в который поместим описание структуры Stack, а также описания клиентских функций для работы со стеками: init_s, push_s и pop_s. Как отмечалось ранее, интерфейс помещается в заголовочный файл, который назовем Stack.h : // Stack.h #include "Vector.h" typedef struct Stack{ int top; Vector v;} Stack ; void init_s (Stack*); void push_s (Stack*, double); int pop_s (Stack*, double*);

Изображение слайда
17

Слайд 17

Лекция 31.10.11г. 17 // Stack.c #include "stack.h" void init_s (Stack *st) { st->top = 0; init(&st->v); } void push_s (Stack *st, double d) { push_back(&st->v, d); // добавляем ++st->top; // инкрементируем вершину } int pop_s (Stack *st, double *d) { if(st->top == 0) return 0; // если стек пуст *d = st->v.elem[--st->top]; --st->v.sz; return 1; // операция извлечения элемента завершилась } Структура данных «стек» ( Stack) Реализацию интерфейсных функций разместим в одном файле Stack.c :

Изображение слайда
18

Слайд 18

Лекция 31.10.11г. 18 #include <stdio.h> #include <stdlib.h> #include "Stack.h" int main() { Stack s; init_s(&s); int i; for(i = 0; i < 20; ++i) push_s(&s, (double)i*i); double x; for(i = 0; i < 20; ++i) { pop_s(&s, &x); printf("s[%d] = %5.1f\n", s.top, x); } system("PAUSE"); return 0; } Структура данных «стек» ( Stack) Покажем, как можно использовать абстракцию «стек»: определяем стек инициализируем заполняем… извлекаем…

Изображение слайда
19

Слайд 19

Лекция 31.10.11г. 19 Структура данных «очередь» ( queue) Очередь – также одна из простейших динамических структур данных со своими правилами включения и удаления элементов. Очередь работает по принципу «первым пришёл – первым ушёл» ( first-in, first-out – сокращённо FIFO ). Включение новых элементов в очередь всегда производится в конце последовательности элементов (в «хвосте» - tail ). Получить (выбрать) элемент из очереди можно только из «головы» ( head) очереди; прямого доступа к элементам очереди по индексу также нет. Для размещения очереди в памяти компьютера можно использовать массив, но лучше – вектор, т.к. очередь – это также динамическая структура. typedef struct Queue{ int head, tail; Vector v; } Queue; elem[head] elem[…] elem[tail-1] … … … tail … … head

Изображение слайда
20

Слайд 20

Лекция 31.10.11г. 20 Структура данных «очередь» ( queue) Разработаем функции, поддерживающих абстракцию «очередь». Первая из функций – init_q должна выполнять инициализацию очереди, т.е. создавать пустую очередь : void init_q(Queue *q) { q->head = q->tail = 0; init(&q->v); } Вторая функция - enqueue добавляет к очереди новый элемент: void enqueue(Queue *q, double d) { push_back(&q->v, d); // добавляем в «хвост» ++q->tail; // инкрементируем «хвост» } Третья функция - dequeue извлекает из очереди элемент: int dequeue(Queue *q, double *d) { if(q->head == q->tail) return 0; // если очередь пуста *d = q->v.elem[q->head++]; // извлекаем из «головы» return 1; }

Изображение слайда
21

Слайд 21

Лекция 31.10.11г. 21 Структура данных «очередь» ( queue) Теперь займемся конструированием модульной структуры приложения, использующего абстракцию «очередь». По аналогии с абстракциями «вектор» и «стек» сначала сформируем интерфейс, в который поместим описание структуры Queue, а также описания клиентских функций для работы с очередями: init_q, enqueue и dequeue. Как отмечалось ранее, интерфейс помещается в заголовочный файл, который назовем Queue.h : // Queue.h #include "Vector.h" typedef struct Queue { int head, tail; Vector v;} Queue ; void init_q (Queue*); void enqueue (Queue*, double); int dequeue (Queue*, double*);

Изображение слайда
22

Слайд 22

Лекция 31.10.11г. 22 Структура данных «очередь» ( queue) // Queue.c #include "Queue.h" void init_q (Queue *q) { q->head = q->tail = 0; init(&q->v); } void enqueue (Queue *q, double d) { push_back(&q->v, d); // добавляем в «хвост» ++ q->tail; // инкрементируем «хвост» } int dequeue (Queue *q, double *d) { if(q->head == q->tail) return 0; // если очередь пуста * d = q->v.elem[q->head++]; // извлекаем из «головы» return 1; } Реализацию интерфейсных функций поместим в один файл Queue.c :

Изображение слайда
23

Слайд 23

Лекция 31.10.11г. 23 Структура данных «очередь» ( queue) #include <stdio.h> #include <stdlib.h> #include "Queue.h" int main() { Queue q; init_q(&q); int i; for(i = 0; i < 20; ++i) enqueue(&q, (double)i*i); double x; for(i = 0; i < 20; ++i) { dequeue(&q, &x); printf("s[%d] = %5.1f\n", q.head-1, x); } system("PAUSE"); return 0; } Покажем, как можно использовать абстракцию «очередь»: определяем очередь инициализируем заполняем… извлекаем…

Изображение слайда
24

Слайд 24

Лекция 31.10.11г. 24 Структура данных «очередь» ( queue) Сделаем важное замечание относительно абстракции «очередь». Если при работе с очередью количество операций включения примерно такое же как и количество операция извлечения, то размер очереди не будет большим, однако размер массива, в котором она размещается, будет непрерывно возрастать в силу того, что очередь постоянно «ползёт» от начала массива к его концу. Следовательно, необходимо применять специальные меры для полезного использования пустых элементов массива, расположенных левее «головы» очереди (т.е. необходима своеобразная «чистка мусора»). Одно из часто используемых решений – «закольцевать» массив, в котором размещается очередь, т.е. считать, что сразу за последним элементом массива расположен первый элемент. Однако при этом решение проблемы переполнения «кольца» несколько усложняется. Другое решение – периодически перемещать очередь к началу массива…

Изображение слайда
25

Слайд 25

Лекция 31.10.11г. 25 Структура данных «куча» ( heap) Вектор как динамически расширяемый массив

Изображение слайда
26

Последний слайд презентации: Тема Алгоритмы и структуры данных (продолжение) Лекция 31.10.11г. 1

Лекция 31.10.11г. 26 Структура данных «хэш-таблица» ( hash table) Вектор как динамически расширяемый массив

Изображение слайда