Скачать презентацию Динамические структуры данных язык Паскаль Стеки очереди деки Скачать презентацию Динамические структуры данных язык Паскаль Стеки очереди деки

Стеки, очереди, деки.ppt

  • Количество слайдов: 42

Динамические структуры данных (язык Паскаль) Стеки, очереди, деки Динамические структуры данных (язык Паскаль) Стеки, очереди, деки

2 Стек – это линейная структура данных, в которой добавление и удаление элементов возможно 2 Стек – это линейная структура данных, в которой добавление и удаление элементов возможно только с одного конца (вершины стека). Stack = кипа, куча, стопка (англ. ) LIFO = Last In – First Out «Последним пришел – первым ушел» . Операции со стеком: 1) добавить элемент на вершину (Push = втолкнуть); 2) снять элемент с вершины (Pop = вылететь со звуком).

3 Пример задачи Задача: вводится символьная строка, в которой записано выражение со скобками трех 3 Пример задачи Задача: вводится символьная строка, в которой записано выражение со скобками трех типов: [], {} и (). Определить, верно ли расставлены скобки (не обращая внимания на остальные символы). Примеры: [()]{} ][ [({)]} Упрощенная задача: то же самое, но с одним видом скобок. Решение: счетчик вложенности скобок. Последовательность правильная, если в конце счетчик равен нулю и проходе не разу не становился отрицательным. ( ( ) ) ( ) 1 2 1 0 ? ( ( ) ) ) ( 1 2 1 0 -1 0 Можно ли решить исходную задачу так же, но с тремя счетчиками? ( ( ) ) ( 1 2 1 0 1 [ ( { ) ] } (: 0 1 0 [: 0 1 0 {: 0 1 0

4 Решение задачи со скобками [ [ ( ( [ ( ) ) ( 4 Решение задачи со скобками [ [ ( ( [ ( ) ) ( ( [ ( [ [ ] { } [ { { Алгоритм: 1) в начале стек пуст; 2) в цикле просматриваем все символы строки по порядку; 3) если очередной символ – открывающая скобка, заносим ее на вершину стека; 4) если символ – закрывающая скобка, проверяем вершину стека: там должна быть соответствующая открывающая скобка (если это не так, то ошибка); 5) если в конце стек не пуст, выражение неправильное.

5 Реализация стека (список) Структура узла: type PNode = ^Node; { указатель на узел 5 Реализация стека (список) Структура узла: type PNode = ^Node; { указатель на узел } Node = record data: char; { данные } next: PNode; { указатель на след. элемент } end; Добавление элемента: procedure Push( var Head: PNode; x: char); var New. Node: PNode; begin New(New. Node); { выделить память } New. Node^. data : = x; { записать символ } New. Node^. next : = Head; { сделать первым узлом } Head : = New. Node; end;

6 Реализация стека (список) Снятие элемента с вершины: function Pop ( var Head: PNode 6 Реализация стека (список) Снятие элемента с вершины: function Pop ( var Head: PNode ): char; var q: PNode; begin if Head = nil then begin { стек пуст } Pop : = char(255); { неиспользуемый символ } Exit; end; Pop : = Head^. data; { взять верхний символ } { запомнить вершину } q : = Head; Head : = Head^. next; { удалить вершину из стека } { удалить из памяти } Dispose(q); end; ? Можно ли переставлять операторы?

7 Реализация стека (список) Пустой или нет? function is. Empty ( S: Stack ): 7 Реализация стека (список) Пустой или нет? function is. Empty ( S: Stack ): Boolean; begin is. Empty : = (S = nil); end; Изменения в основной программе: var S: Stack; . . . S. size : = 0; ! var S: PNode; S : = nil; Больше ничего не меняется!

8 Вычисление арифметических выражений Как вычислять автоматически: (a + b) / (c + d 8 Вычисление арифметических выражений Как вычислять автоматически: (a + b) / (c + d – 1) Инфиксная запись (знак операции между операндами) необходимы скобки! Префиксная запись (знак операции до операндов) / + a b - c + - 1 a + c d 1 + d польская нотация, Jan Łukasiewicz (1920) скобки не нужны, можно однозначно вычислить! Постфиксная запись (знак операции после операндов) a b + c d + - 1 / + b c + d 1 - обратная польская нотация, F. L. Bauer and E. W. Dijkstra

9 Запишите в постфиксной форме (32*6 -5)*(2*3+4)/(3+7*2) (2*4+3*5)*(2*3+18/3*2)*(12 -3) (4 -2*3)*(3 -12/3/4)*(24 -3*12) 9 Запишите в постфиксной форме (32*6 -5)*(2*3+4)/(3+7*2) (2*4+3*5)*(2*3+18/3*2)*(12 -3) (4 -2*3)*(3 -12/3/4)*(24 -3*12)

10 Вычисление выражений Постфиксная форма: X = a b + c d + d 10 Вычисление выражений Постфиксная форма: X = a b + c d + d b a a 1 - 1 c a+b / c c+d c+d-1 a+b a+b a+b X Алгоритм: 1) взять очередной элемент; 2) если это не знак операции, добавить его в стек; 3) если это знак операции, то • взять из стека два операнда; • выполнить операцию и записать результат в стек; 4) перейти к шагу 1.

11 Системный стек (Windows – 1 Мб) Используется для 1) размещения локальных переменных; 2) 11 Системный стек (Windows – 1 Мб) Используется для 1) размещения локальных переменных; 2) хранения адресов возврата (по которым переходит программа после выполнения функции или процедуры); 3) передачи параметров в функции и процедуры; 4) временного хранения данных (в программах на языке Ассмеблер). Переполнение стека (stack overflow): 1) слишком много локальных переменных (выход – использовать динамические массивы); 2) очень много рекурсивных вызовов функций и процедур (выход – переделать алгоритм так, чтобы уменьшить глубину рекурсии или отказаться от нее вообще).

12 Очередь 6 5 4 3 2 1 Очередь – это линейная структура данных, 12 Очередь 6 5 4 3 2 1 Очередь – это линейная структура данных, в которой добавление элементов возможно только с одного конца (конца очереди), а удаление элементов – только с другого конца (начала очереди). FIFO = First In – First Out «Кто первым вошел, тот первым вышел» . Операции с очередью: 1) добавить элемент в конец очереди (Push. Tail = втолкнуть в конец); 2) удалить элемент с начала очереди (Pop).

13 Реализация очереди (списки) Структура узла: type PNode = ^Node; Node = record data: 13 Реализация очереди (списки) Структура узла: type PNode = ^Node; Node = record data: integer; next: PNode; end; Тип данных «очередь» : type Queue = record head, tail: PNode; end;

14 Реализация очереди (списки) Добавление элемента: procedure Push. Tail( var Q: Queue; x: integer 14 Реализация очереди (списки) Добавление элемента: procedure Push. Tail( var Q: Queue; x: integer ); var New. Node: PNode; создаем begin новый узел New(New. Node); если в списке уже New. Node^. data : = x; что-то было, New. Node^. next : = nil; добавляем в конец if Q. tail <> nil then Q. tail^. next : = New. Node; Q. tail : = New. Node; { новый узел – в конец} if Q. head = nil then Q. head : = Q. tail; end; если в списке ничего не было, …

15 Реализация очереди (списки) Выборка элемента: function Pop ( var S: Queue ): integer; 15 Реализация очереди (списки) Выборка элемента: function Pop ( var S: Queue ): integer; var top: PNode; если список пуст, … begin if Q. head = nil then begin Pop : = Max. Int; запомнили Exit; первый элемент end; если в списке top : = Q. head; ничего не Pop : = top^. data; осталось, … Q. head : = top^. next; if Q. head = nil then Q. tail : = nil; Dispose(top); освободить end; память

16 Дек (deque = double ended queue, очередь с двумя концами) – это линейная 16 Дек (deque = double ended queue, очередь с двумя концами) – это линейная структура данных, в которой добавление и удаление элементов возможно с обоих концов. 6 4 2 1 3 5 Операции с деком: 1) добавление элемента в начало (Push); 2) удаление элемента с начала (Pop); 3) добавление элемента в конец (Push. Tail); 4) удаление элемента с конца (Pop. Tail). Реализация: двусвязный список.

17 Задания « 4» : В файле input. dat находится список чисел (или слов). 17 Задания « 4» : В файле input. dat находится список чисел (или слов). Переписать его в файл output. dat в обратном порядке. « 5» : Составить программу, которая вычисляет значение арифметического выражения, записанного в постфиксной форме, с помощью стека. Выражение правильное, допускаются только однозначные числа и знаки +, -, *, /. « 6» : То же самое, что и на « 5» , но допускаются многозначные числа.

Динамические структуры данных (язык Паскаль) Тема 6. Деревья Динамические структуры данных (язык Паскаль) Тема 6. Деревья

19 Деревья директор гл. инженер гл. бухгалтер инженер бухгалтер ? Что общего во всех 19 Деревья директор гл. инженер гл. бухгалтер инженер бухгалтер ? Что общего во всех примерах?

20 Деревья Дерево – это структура данных, состоящая из узлов и соединяющих их направленных 20 Деревья Дерево – это структура данных, состоящая из узлов и соединяющих их направленных ребер (дуг), причем в каждый узел (кроме корневого) ведет ровно одна дуга. 2 Корень – это начальный узел дерева. Лист – это узел, из которого не выходит ни одной дуги. 5 корень 1 6 1 4 3 1 3 2 10 9 1 2 8 7 Какие структуры – не деревья? 1 4 3 2 3 6 3 2 5 4

21 Деревья ! С помощью деревьев изображаются отношения подчиненности (иерархия, «старший – младший» , 21 Деревья ! С помощью деревьев изображаются отношения подчиненности (иерархия, «старший – младший» , «родитель – ребенок» ). Предок узла x – это узел, из которого существует путь по стрелкам в узел x. Потомок узла x – это узел, в который существует путь по стрелкам из узла x. Родитель узла x – это узел, из которого существует дуга непосредственно в узел x. 1 2 3 4 5 6 Сын узла x – это узел, в который существует дуга непосредственно из узла x. Брат узла x (sibling) – это узел, у которого тот же родитель, что и у узла x. Высота дерева – это наибольшее расстояние от корня до листа (количество дуг).

22 Дерево – рекурсивная структура данных Рекурсивное определение: 1 1. Пустая структура – это 22 Дерево – рекурсивная структура данных Рекурсивное определение: 1 1. Пустая структура – это дерево. 2 3 2. Дерево – это корень и несколько связанных с ним деревьев. 4 5 Двоичное (бинарное) дерево – это 6 дерево, в котором каждый узел имеет не более двух сыновей. 1. Пустая структура – это двоичное дерево. 2. Двоичное дерево – это корень и два связанных с ним двоичных дерева (левое и правое поддеревья).

23 Двоичные деревья Применение: 1) поиск данных в специально построенных деревьях (базы данных); 2) 23 Двоичные деревья Применение: 1) поиск данных в специально построенных деревьях (базы данных); 2) сортировка данных; 3) вычисление арифметических выражений; 4) кодирование (метод Хаффмана). Структура узла: type PNode = ^Node; { указатель на узел } Node = record data: integer; { полезные данные } left, right: PNode; { ссылки на левого и правого сыновей } end;

24 Двоичные деревья поиска Ключ – это характеристика узла, по которой выполняется поиск (чаще 24 Двоичные деревья поиска Ключ – это характеристика узла, по которой выполняется поиск (чаще всего – одно из полей структуры). ? 59 30 16 98 45 76 125 Какая закономерность? Слева от каждого узла находятся узлы с меньшими ключами, а справа – с бóльшими. Как искать ключ, равный x: 1) 2) 3) 4) если дерево пустое, ключ не найден; если ключ узла равен x, то стоп. если ключ узла больше x, то искать x в левом поддереве; если ключ узла меньше x, то искать x в правом поддереве. ? Сведение задачи к такой же задаче меньшей размерности – это …?

25 Двоичные деревья поиска Поиск в массиве (N элементов): 59 98 76 125 30 25 Двоичные деревья поиска Поиск в массиве (N элементов): 59 98 76 125 30 45 16 При каждом сравнении отбрасывается 1 элемент. Число сравнений – N. Поиск по дереву (N элементов): 59 30 16 98 45 76 125 При каждом сравнении отбрасывается половина оставшихся элементов. Число сравнений ~ log 2 N. быстрый поиск 1) нужно заранее построить дерево; 2) желательно, чтобы дерево было минимальной высоты.

26 Реализация алгоритма поиска { Функция Search – поиск по дереву Вход: Tree - 26 Реализация алгоритма поиска { Функция Search – поиск по дереву Вход: Tree - адрес корня, x - что ищем Выход: адрес узла или nil (не нашли) } function Search(Tree: PNode; x: integer): PNode; begin дерево пустое: if Tree = nil then begin ключ не нашли… Search: = nil; Exit; end; нашли, if x = Tree^. data then возвращаем Result : = Tree искать в адрес корня else левом поддереве if x < Tree^. data then Search: = Search(Tree^. left, x) else Search: = Search(Tree^. right, x); end; искать в правом поддереве

27 Как построить дерево поиска? { Процедура Add. To. Tree – добавить элемент Вход: 27 Как построить дерево поиска? { Процедура Add. To. Tree – добавить элемент Вход: Tree - адрес корня, x - что добавляем } procedure Add. To. Tree( var Tree: PNode; x: integer ); begin адрес корня может if Tree = nil then begin измениться New(Tree); Tree^. data : = x; дерево пустое: создаем Tree^. left : = nil; новый узел (корень) Tree^. right : = nil; Exit; добавляем к левому end; или правому if x < Tree^. data then Add. To. Tree(Tree^. left, x) поддереву else Add. To. Tree(Tree^. right, x); end; ! Минимальная высота не гарантируется!

28 Обход дерева – это перечисление всех узлов в определенном порядке. 59 30 Обход 28 Обход дерева – это перечисление всех узлов в определенном порядке. 59 30 Обход ЛКП ( «левый – корень – правый» ): 16 30 45 59 76 98 16 125 Обход ПКЛ ( «правый – корень – левый» ): 125 98 76 59 45 30 16 Обход КЛП ( «корень – левый – правый» ): 59 30 16 45 98 76 125 Обход ЛПК ( «левый – правый – корень» ): 16 45 30 76 125 98 59 98 45 76 125

29 Обход дерева – реализация { Процедура LKP – обход дерева в порядке ЛКП 29 Обход дерева – реализация { Процедура LKP – обход дерева в порядке ЛКП (левый – корень – правый) Вход: Tree - адрес корня } procedure LKP(Tree: PNode); обход этой ветки закончен begin if Tree = nil then Exit; обход левого LKP(Tree^. left); поддерева write(' ', Tree^. data); LKP(Tree^. right); end; ! вывод данных корня обход правого поддерева Для рекурсивной структуры удобно применять рекурсивную обработку!

30 Разбор арифметических выражений Как вычислять автоматически: / (a + b) / (c + 30 Разбор арифметических выражений Как вычислять автоматически: / (a + b) / (c + d – 1) Инфиксная запись, обход ЛКП (знак операции между операндами) + a b a + b / c + d – 1 + c 1 d необходимы скобки! Префиксная запись, КЛП (знак операции до операндов) польская нотация, / + a b - + c d 1 Jan Łukasiewicz (1920) скобки не нужны, можно однозначно вычислить! Постфиксная запись, ЛПК (знак операции после операндов) a b + c d + 1 - / обратная польская нотация, F. L. Bauer and E. W. Dijkstra

31 Вычисление выражений Задача: в символьной строке записано правильное арифметическое выражение, которое может содержать 31 Вычисление выражений Задача: в символьной строке записано правильное арифметическое выражение, которое может содержать только однозначные числа и знаки операций +-*. Вычислить это выражение. Алгоритм: 1) ввести строку; 2) построить дерево; 3) вычислить выражение по дереву. Ограничения: 1) 2) 3) 4) ошибки не обрабатываем; многозначные числа не разрешены; дробные числа не разрешены; скобки не разрешены.

32 Построение дерева k first k-1 last k+1 5 + 7 * 6 - 32 Построение дерева k first k-1 last k+1 5 + 7 * 6 - 3 * 2 Алгоритм: 1) если first=last (остался один символ – число), то создать новый узел и записать в него этот элемент; иначе. . . 2) среди элементов от first до last включительно найти последнюю операцию (элемент с номером k); 3) создать новый узел (корень) и записать в него знак операции; 4) рекурсивно применить этот алгоритм два раза: • построить левое поддерево, разобрав выражение из элементов массива с номерами от first до k-1; • построить правое поддерево, разобрав выражение из элементов массива с номерами от k+1 до last.

33 Как найти последнюю операцию? 5 + 7 * 6 - 3 * 2 33 Как найти последнюю операцию? 5 + 7 * 6 - 3 * 2 Порядок выполнения операций • умножение и деление; • сложение и вычитание. Приоритет (старшинство) – число, определяющее последовательность выполнения операций: раньше выполняются операции с большим приоритетом: • умножение и деление (приоритет 2); • сложение и вычитание (приоритет 1). ! Нужно искать последнюю операцию с наименьшим приоритетом!

34 Приоритет операции { Функция Priority – приоритет операции Вход: символ операции Выход: приоритет 34 Приоритет операции { Функция Priority – приоритет операции Вход: символ операции Выход: приоритет или 100, если не операция } function Priority ( c: char ): integer; сложение и begin вычитание: case ( c ) of приоритет 1 '+', '-': Priority : = 1; '*', '/': Priority : = 2; else Priority : = 100; умножение и деление: end; приоритет 2 end; это вообще не операция

35 Номер последней операции { Функция Last. Operation – номер последней операции Вход: строка, 35 Номер последней операции { Функция Last. Operation – номер последней операции Вход: строка, номера первого и последнего символов рассматриваемой части Выход: номер символа - последней операции } function Last. Operation ( Expr: string; first, last: integer): integer; var Min. Prt, i, k, prt: integer; проверяем все begin Min. Prt : = 100; символы for i: =first to last do begin prt : = Priority ( Expr[i] ); if prt <= Min. Prt then begin Min. Prt : = prt; нашли операцию с k : = i; минимальным end; приоритетом end; Result : = k; вернуть номер end; символа

36 Построение дерева Структура узла type PNode = ^Node; Node = record data: char; 36 Построение дерева Структура узла type PNode = ^Node; Node = record data: char; left, right: PNode; end; Создание узла для числа (без потомков) function Number. Node(c: char): PNode; begin один символ, New(Number. Node); число Number. Node^. data : = c; Number. Node^. left : = nil; Number. Node^. right : = nil; возвращает адрес end; созданного узла

37 Построение дерева { Функция Make. Tree – построение дерева Вход: строка, номера первого 37 Построение дерева { Функция Make. Tree – построение дерева Вход: строка, номера первого и последнего символов рассматриваемой части Выход: адрес построенного дерева } function Make. Tree ( Expr: string; first, last: integer): PNode; var k: integer; осталось begin только число if first = last then begin Result : = Number. Node ( Expr[first] ); Exit; end; k : = Last. Operation ( Expr, first, last ); New(Result); новый узел: операция Result^. data : = Expr[k]; Result^. left : = Make. Tree ( Expr, first, k-1 ); Result^. right : = Make. Tree ( Expr, k+1, last ); end;

38 Вычисление выражения по дереву { Функция Calc. Tree – вычисление по дереву Вход: 38 Вычисление выражения по дереву { Функция Calc. Tree – вычисление по дереву Вход: адрес дерева Выход: значение выражения } function Calc. Tree(Tree: PNode): integer; var num 1, num 2: integer; вернуть число, begin если это лист if Tree^. left = nil then begin Result : = Ord(Tree^. data) - Ord('0'); Exit; вычисляем end; операнды num 1 : = Calc. Tree(Tree^. left); num 2 : = Calc. Tree(Tree^. right); (поддеревья) case Tree^. data of '+': Result : = num 1+num 2; выполняем '-': Result : = num 1 -num 2; '*': Result : = num 1*num 2; операцию '/': Result : = num 1 div num 2; else Result : = Max. Int; некорректная end; операция end;

39 Основная программа { Ввод и вычисление выражения с помощью дерева } program qq; 39 Основная программа { Ввод и вычисление выражения с помощью дерева } program qq; var Tree: PNode; Expr: string; { процедуры и функции } begin write('Введите выражение > '); readln( Expr ); Tree : = Make. Tree( Expr, 1, Length(Expr) ); writeln(' = ', Calc. Tree(Tree) ); end.

40 Дерево игры Задача. Перед двумя игроками лежат две кучки камней, в первой из 40 Дерево игры Задача. Перед двумя игроками лежат две кучки камней, в первой из которых 3, а во второй – 2 камня. У каждого игрока неограниченно много камней. Игроки ходят по очереди. Ход состоит в том, что игрок или увеличивает в 3 раза число камней в какой-то куче, или добавляет 1 камень в какую-то кучу. Выигрывает игрок, после хода которого общее число камней в двух кучах становится не менее 16. Кто выигрывает при безошибочной игре – игрок, делающий первый ход, или игрок, делающий второй ход? Как должен ходить выигрывающий игрок?

41 Дерево игры игрок 1 игрок 2 9, 2 27, 2 3, 6 игрок 41 Дерево игры игрок 1 игрок 2 9, 2 27, 2 3, 6 игрок 1 3, 18 12, 2 4, 2 36, 2 4, 6 выиграл игрок 1 4, 18 5, 2 игрок 2 15, 2 12, 2 36, 2 4, 6 12, 6 5, 3 15, 3 4, 4 12, 4 9, 3 ключевой ход 27, 3 4, 3 3, 3 ! При правильной игре выиграет игрок 2! 2

42 Задания « 4» : «Собрать» программу для вычисления правильного арифметического выражения, включающего только 42 Задания « 4» : «Собрать» программу для вычисления правильного арифметического выражения, включающего только однозначные числа и знаки операций +, -, * , /. « 5» : То же самое, но допускаются также многозначные числа и скобки. « 6» : То же самое, что и на « 5» , но с обработкой ошибок (должно выводиться сообщение).