Лекция 17. Очереди.Стеки.

22
Лекция 17. Очереди. Стеки Очереди Очередь это упорядоченная последовательность элементов, в которой операции включения и исключения элементов выполняются по принципу FIFO (First-In-First- Out) - “первым пришел – первым ушел”, т.е. включение всегда происходит в конец очереди, а исключение – из начала очереди (рис. 17.1). Рис. 17.1. Очередь Очереди широко используются в операционных системах, например, может возникнуть очередь программ на загрузку в оперативную память, на выполнение к процессору, на обработку файлов и т.д. Основные операции над очередью: 1. Инициализация (создание пустой очереди). 2. Включение элемента в очередь. 3. Исключение элемента из очереди. 1 Q[1] Q[2] ... Q[k] Исключение элементов Включение элементов Начало Конец

Transcript of Лекция 17. Очереди.Стеки.

Page 1: Лекция 17. Очереди.Стеки.

Лекция 17. Очереди. Стеки

Очереди

Очередь – это упорядоченная последовательность элементов, в которой

операции включения и исключения элементов выполняются по принципу

FIFO (First-In-First-Out) - “первым пришел – первым ушел”, т.е. включение

всегда происходит в конец очереди, а исключение – из начала очереди (рис.

17.1).

Рис. 17.1. Очередь

Очереди широко используются в операционных системах, например,

может возникнуть очередь программ на загрузку в оперативную память, на

выполнение к процессору, на обработку файлов и т.д.

Основные операции над очередью:

1. Инициализация (создание пустой очереди).

2. Включение элемента в очередь.

3. Исключение элемента из очереди.

Очередь может храниться в виде циклического вектора или в виде

списка.

Представление очереди в виде циклического вектора

В циклическом (кольцевом) векторе за последним элементом следует

первый (с индексом 0). Очередь хранится в циклическом векторе с

указателями начала и конца (рис. 17.2).

1

Q[1] Q[2] . . . Q[k]

Исключение

элементовВключение

элементов

Начало Конец

Page 2: Лекция 17. Очереди.Стеки.

0 Свободная 0 .Очередь 1 ..память

Начало Очередь Конец ..Свободная j. .памят ь Конец Свободная Начало .Очередь N память N

а) Индекс начала очереди < индекса б) Индекс начала > индекса конца конца очереди

Рис. 17.2. Хранение очереди в виде циклического вектора(показаны два состояния)

Часть вектора занимает очередь, остальная часть – свободная память.

Указатель начала очереди равен индексу первого элемента очереди, а

указатель конца – индексу первого свободного элемента в конце очереди (так

проще программировать операции над очередью). Для пустой очереди

значения указателей равны между собой.

Включение элемента происходит в конец очереди, при этом изменяется

указатель конца: ему присваивается индекс следующего свободного элемента

вектора. Исключение элемента происходит из начала очереди, при этом

указателю начала присваивается индекс следующего элемента очереди (за N

следует 0).

Чтобы отличить полную очередь от пустой, один элемент вектора

всегда оставляют свободным.

Программирование операций на языке С (C++)

1. Описание очереди

#define N 50 /* максимальное число элементов в очереди */тип_элемента Och [N+1]; /* вектор, содержащий очередь */int un, uk; /* указатели начала и конца очереди */

Q[1] …

Q[k]

Q[k]

Q[1]

Q[2]

2

Page 3: Лекция 17. Очереди.Стеки.

2. Инициализация (создание пустой очереди)

un = uk = 0;

3. Исключение элемента из очереди с присвоением его значения переменной X

if (un != uk) /* очередь не пустая*/{ X = Och [un]; if (un < N) un++; else un = 0;} илиif (un != uk) { X = Och [un++]; if (un > N) un = 0;}

4. Включение в очередь элемента X

int i; /* значение указателя конца после включения элемента */ if (uk<N) i = uk +1;else i = 0;if (i != uk) /* есть место в векторе */{ Och [uk] = X; uk = i;}else … /* очередь переполнена */

Представление очереди в виде списка

Очередь можно хранить в виде списка с двумя указателями: начала и

конца очереди (рис. 17.3).

1-й эл-т 2-й эл-т n-й эл-т

указатель указательначала конца

Рис. 17.3. Очередь в виде спискаПрограммирование операций на языке С (C++)

3

... 0

Page 4: Лекция 17. Очереди.Стеки.

1. Описание очереди

/* тип элемента очереди */struct EL_OCH{ тип_элемента inf; /* значение элемента (поле информации) */ struct EL_OCH * sled; /* ссылка на следующий элемент */};struct EL_OCH *un, *uk; /* указатели начала и конца очереди */

2. Инициализация (создание пустой очереди)

un = uk = NULL;

3. Исключение элемента из очереди с присвоением его значения

переменной X

а) Если в очереди больше одного элемента, нужно переменной X

присвоить значение 1-го элемента, указателю начала присвоить адрес 2-го

элемента и освободить память, занятую 1-м элементом (рис. 17.4).

inf sled

un uk

Рис. 17.4. Исключение элемента из очереди, состоящей из двух и более элементов

б) Если в очереди всего один элемент, нужно X присвоить значение

этого элемента и освободить память, выделенную под элемент, затем

обнулить оба указателя (рис. 17.5).

inf sled

un uk

Рис. 17.5. Исключение элемента из очереди, состоящей из одного элементаОперацию исключения элемента из очереди оформим как отдельную

функцию./* Вызов функции*/

... 0

4

0

0 0

Page 5: Лекция 17. Очереди.Стеки.

if ( !Iskluch (&un, &uk, &X) ) puts(“Очередь была пустая”);

/* Функция исключения элемента из очереди*/int Iskluch(struct EL_OCH **p_un, struct EL_OCH **p_uk, тип_элемента *pX)

/* p_un – указатель на un *//* p_uk – указатель на uk *//* pX – указатель на X *//* Функция возвращает 1 в случае успешного выполнения и 0, если очередь была пустая.*/

{ struct EL_OCH *i; if (*p_un != NULL) /* очередь не пустая*/ { i = *p_un;

*pX = i -> inf; *p_un = i -> sled;

free (i);if (*p_un == NULL) /* в очереди был один элемент */ *p_uk = NULL;return 1;

} return 0;}

4. Включение в очередь элемента, равного X .

Нужно выделить память под новый элемент и записать в его поле

информации значение X, а в поле ссылки – 0. Затем добавить новый элемент

в конец очереди и изменить указатель конца (рис. 17.6). Если очередь была

пустой, то нужно изменить и указатель начала, чтобы он тоже ссылался на

новый элемент (рис. 17.7).

inf sled новый эл-т

un uk i

Рис. 17.6. Включение нового элемента в непустую очередь

новый эл-т

5

... 0 0

0

0 0

Page 6: Лекция 17. Очереди.Стеки.

un uk

Рис. 17.7. Включение в пустую очередь нового элемента

Операцию включения элемента в очередь оформим как отдельную функцию.

/* Вызов функции*/Vkluch (&un, &uk, X);

/* Функция включения элемента в очередь*/void Vskluch (struct EL_OCH **p_un, struct EL_OCH **p_uk, тип_элемента X)

/* p_un – указатель на un *//* p_uk – указатель на uk *//* X – значение включаемого элемента */

{ struct EL_OCH *i; /* указатель на новый элемент */i = (struct EL_OCH *) malloc (sizeof (struct EL_OCH));i -> inf = X;i -> sled = NULL;if (*p_un != NULL) /* очередь не пустая*/ (*p_uk) -> sled = i;else *p_un = i;*p_uk = i;

}

Представление очереди в виде списка удобно для очереди трудно

предсказуемого размера и для очереди с приоритетами. В очереди с

приоритетами исключение элементов происходит не в порядке поступления,

а по приоритету (старшинству). Для этого элементы упорядочиваются в

очереди по убыванию приоритета, т.е. включаются не обязательно в конец,

что удобно делать в списке. Каждый элемент очереди содержит, кроме полей

информации и указателя на следующий элемент, поле приоритета (целое

число).

Стеки

6

Page 7: Лекция 17. Очереди.Стеки.

Стек (stack) - это упорядоченная последовательность элементов, в

которой выполняются операции включения и исключения элемента по

принципу LIFO (Last-In-First-Out) - "последним пришел - первым ушел", т. е.

включение и исключение всегда происходят в одном конце (рис. 17.8). Этот

конец называют верхом, противоположный - дном стека. Другие названия

стека: магазин (по аналогии с магазином пистолета или автомата), очередь

типа LIFO.

S[1] S[2] … S[k]

Дно ВерхВключение иисключениеэлементов

Рис. 17.8. Стек

Примеры стека: заднее сиденье легкового автомобиля, когда посадка и

высадка происходит с одной стороны; стопка подносов в столовой, где

подносы берутся и кладутся только сверху.

Назовем некоторые из многочисленных применений стека.

1. Переупорядочивание данных для обработки в порядке,

отличающемся от порядка поступления.

2. Запоминание подзадач некоторой задачи с последующим решением

подзадач в порядке, обратном порядку их возникновения (см. алгоритм

обхода дерева в глубину).

3. Области локальных данных (включая параметры и адреса возврата)

вложенных вызовов подпрограмм.

4. Области локальных данных вложенных блоков программы.

5. Трансляция скобочных структур: выражений, вложенных ветвлений,

циклов, блоков и всей программы.

6. ЭВМ и микрокалькуляторы с безадресной магазинной памятью.

7

Page 8: Лекция 17. Очереди.Стеки.

7. Стек в мозге человека (7 ± 2 элемента). Психологи обнаружили, что

человек может воспринимать именно такую глубину вложенности, например,

придаточных предложений фразы или такое количество рассматриваемых

вместе дел, понятий и т. п.

Типовые операции над стеком:

1. Инициализация (создание, подготовка к работе);

2. Вталкивание (включение) элемента - PUSH;

3. Выталкивание (исключение) элемента - POP;

4. Проверка пустоты стека;

5. Проверка переполнения стека;

6. Доступ к вершине (получение / изменение значения последнего

поступившего элемента).

Представление стека в виде вектора

Чаще всего стек представляется в виде вектора с указателем (рис. 17.9).

Рис. 17.9. Хранение стека в виде вектора

Часть вектора занимает стек, часть - свободное пространство.

Указателем стека служит индекс последнего из поступивших элементов

(либо индекс первой свободной позиции). Указатель стека увеличивается на

единицу при вталкивании элемента и уменьшается при выталкивании (или

наоборот, если стек расположен в конце вектора).

8

Page 9: Лекция 17. Очереди.Стеки.

Два стека удобно хранить в одном векторе, заполняя их навстречу друг

другу (рис. 17.10).

Рис. 17.10. Хранение двух стеков в одном векторе

Этот способ позволяет расти любому из двух стеков, пока есть хотя бы

одна свободная ячейка. Максимальные размеры каждого стека и их суммы

равны длине вектора. Для большего количества стеков подобного метода не

существует.

Программирование операций на языке С (С++)

1. Описание данных для представления стека вектором

#define N 100 /* Максимальная длина стека */ тип-элемента st[N]; /* Отображающий вектор стека */ int ist; /* Указатель стека (индекс последнего элемента) */

2. Инициализация (создание) пустого стека:

ist = -1;

Как и в очереди, при невозможности выполнения операции действия

зависят от решаемой задачи. Обычно пустота стека используется как условие

окончания алгоритма, а переполнение означает ошибку.

9

A[1]. . .

A[k]

B[m]. . .

B[1]

Дно

Верх

Стек A

Свободная память

Указатель стека A

Верх

Дно

Стек B

Указатель стека B

Page 10: Лекция 17. Очереди.Стеки.

3. Выталкивание из стека элемента и присваивание его величине X

(обозначим эту операцию Стек ==> X; без запоминания элемента: Стек ==>):

тип-элемента X; /* Значение вытолкнутого элемента */ . . .

if (ist >= 0) /* В стеке есть элементы */{ X = st[ist]; /* Получение значения */ ist--; /* Выталкивание элемента */ } else . . . /* Стек пуст */

4. Вталкивание в стек элемента, равного X (обозначим эту операцию

Стек <== X):

тип-элемента X; /* Вталкиваемое значение */ . . . if (ist < N-1) /* Есть место в стеке */ { ist++; /* Увеличение стека */ st[ist] = X; /* Запись x в вершину стека */ } else . . . /* Стек переполнен */

Задача. Дано арифметическое выражение длиной до 80 символов,

оканчивающееся пробелом. Выражение содержит целые числа без знака и

знаки операций +, -, *, /. Вычислить значение выражения.

Например, входной текст: 130+25*3-160/20*6 .

Результат: 157.

Метод решения задачи основан на использовании стека операндов,

операций и приоритетов. Операции * и / имеют одинаковый приоритет,

причем более высокий, чем операции + и - . При просмотре выражения

происходит выделение очередного операнда (числа), преобразование его из

символьной формы в целочисленную и запись в стек полученного числа,

следующей за ним операции и присвоенного ей приоритета. Пока в стеке

более одного элемента и приоритет последней операции оказывается не

выше приоритета предыдущей операции в стеке, происходит

10

Page 11: Лекция 17. Очереди.Стеки.

выполнение соответствующих операций над двумя последними

операндами стека и удаление ненужных элементов из стека. В конце

концов, когда просмотр выражения закончится, первый элемент стека и

будет искомым результатом.

На рис. 17.11 показано, как меняется содержимое стека.

операнд приор-т оп-ция ┌───┬────┬───┐ ┌───┬─┬─┐ ┌───┬─┬─┐ ┌───┬─┬─┐ ┌───┬─┬─┐ ┌───┬─┬─┐ │130│ + │ 1 │ │130│+│1│ │205│-│1│ │205│-│1│ │205│-│1│ │157│ │0│ │ 25│ * │ 2 │ │ 75│-│1│ │160│/│2│ │ 8│*│2│ │ 48│ │0│ │ │ │ │ │ 3│ - │ 1 │ │ │ │ │ │ 20│*│2│ │ 6│ │0│ │ │ │ │ │ │ │ │ └───┴────┴───┘ └───┴─┴─┘ └───┴─┴─┘ └───┴─┴─┘ └───┴─┴─┘ └───┴─┴─┘

Рис. 17.11. Пример заполнения стека операндов, операций и приоритетов.

В программе стек реализован в виде трех параллельных массивов.

Программа:

#include <stdio.h> #include <conio.h> /*──────────────────────────────────*/ /* Функция выделения числа и преобразования его из */ /* символьной формы представления в целочисленную */ /*──────────────────────────────────*/ int Сhislo ( char text [], int *i ) /* Входные данные: */ /* text - символьная строка, */ /* *i - индекс первой цифры числа в строке text. */ /* Выходные данные: */ /* *i - индекс следующего после числа символа. */ /* Функция возвращает целое число */ { int c=0; /* возвращаемое значение */ while (text[*i]>='0' && text[*i]<='9') { c=c*10+(text[*i]-'0'); (*i)++; } return c; }

11

Page 12: Лекция 17. Очереди.Стеки.

/*───────────────────*/ /* Главная функция */ /*───────────────────*/

main() { char text [80]; /* вх. текст - арифм. выражение */ int i ; /* индекс массива text */ float opd [3]; /* стек операндов */ char opc [3]; /* стек операций */ int pr [3] ; /* стек приоритетов */ int j = -1 ; /* указатель стека */ printf ("\nВведите арифметическое выражение.\n"); gets (text); i=-1; do { i++; /* запись числа, операции и ее приоритета в стек */ opd[++j] = Сhislo(text,&i); opc[j] = text[i]; switch (text[i]) { case '+': case '-': pr[j]=1; break; case '*': case '/': pr[j]=2; break; case ' ' /* пробел */: pr[j]=0; } while (j>0 && pr[j]<=pr[j-1]) { /* выполнение соответствующей операции */ switch (opc[j-1]) { case '+' : opd[j-1] = opd[j-1] + opd[j]; break; case '-' : opd[j-1] = opd[j-1] - opd[j]; break; case '*' : opd[j-1] = opd[j-1] * opd[j]; break; case '/' : opd[j-1] = opd[j-1] / opd[j]; } /* удаление выполненной операции из стека */ opc[j-1]=opc[j]; pr[j-1]=pr[j]; j=j-1; } } while (text[i]!=' '); printf ("Результат: %.2f\n",opd[0]); getch(); }

12

Page 13: Лекция 17. Очереди.Стеки.

Результаты тестирования:

Введите арифметическое выражение. 9/4-12 Результат: -9.75

Введите арифметическое выражение. 10*2/5+6/3 Результат: 6.00

Введите арифметическое выражение. 130+25*3-160/20*6 Результат: 157.00

Введите арифметическое выражение. 2345 Результат: 2345.00

Представление стека в виде списка

В виде списка удобно хранить стек с элементами переменной длины

или трудно предсказуемым количеством элементов. Вершина стека

размещается в начале списка, где выполнять операции включения и

исключения элемента проще, чем в конце (рис. 17.12).

Error: Reference source not foundРис. 17.12. Стек в виде списка

Контрольные вопросы и упражнения

1. Что такое очередь?

2. Как можно хранить очередь?

3. Дан циклический вектор, содержащий очередь:

Индекс 0 1 2 3 4 5 6 7

Значение 10 8 11 17 13 5 12 0

13

S[k] S[1] 0 . . .

Верх Дно

Указатель списка

Page 14: Лекция 17. Очереди.Стеки.

и значения указателей начала un=6 и конца uk=3 очереди. Описать

очередь на языке С.

Перечислите значения элементов очереди в порядке их поступления.

Как изменятся значения вектора и указателей после включения числа 15 в

очередь.

4. Очередь хранится в циклическом векторе из 10 элементов с

указателями начала и конца, равными 8 и 2 соответственно. Какие значения

примут указатели после удаления 2-х и добавления 3-х элементов.

Опишите очередь на языке С, если элементами ее являются отдельные

символы.

5. Опишите функцию, определяющую, есть ли еще место в данной

очереди, представленной циклическим вектором. Функция должна вернуть

значение 1, если есть место, и 0 – в противном случае. Элементами очереди

являются целые числа. Приведите пример вызова функции.

6. Дана очередь в виде списка. Значением элемента очереди является

строка длиной до 20 символов. Опишите очередь на языке С.

7. Дана очередь с приоритетами в виде списка. Значением элемента

очереди является номер задачи (целое число). Опишите очередь. Опишите

функцию включения в очередь нового элемента с заданным номером задачи

и заданным приоритетом. Привести пример вызова этой функции.

8. Что такое стек?

9. Как можно хранить стек?

10. Опишите стек, состоящий из целых чисел, в виде списка. Опишите

отдельные функции вталкивания элемента в стек и выталкивания элемента из

стека. Приведите примеры их вызова.

Выполнение контрольных заданий

1. Получите у преподавателя индивидуальное задание.

14

Page 15: Лекция 17. Очереди.Стеки.

2. Составьте программу на языке С и подберите тесты для проверки

программы на компьютере.

3. Отладьте программу на компьютере.

5. Оформите и сдайте отчет.

Контрольные задания

1. Дано арифметическое выражение длиной до 20 символов,

оканчивающееся пробелом. Выражение содержит однобуквенные

идентификаторы и знаки операций +, -, *, /. Преобразовать выражение в

обратную польскую запись, используя стек операций и приоритетов.

Пример.

Вх. текст: a*b+c/d-e

Результат: ab*cd/+e-

2. Дано арифметическое выражение в постфиксной (обратной

польской) записи длиной до 20 символов, оканчивающееся пробелом.

Получить эквивалентную последовательность элементарных присваиваний,

содержащих одну арифметическую операцию (используя стек операндов).

а) В левой части операторов присваивания использовать

вспомогательные переменные R1,R2,R3,...

Пример.

Вх. текст: abc*+def+/-

Результат: R1:=b*c

R2:=a+R1

R3:=e+f

R4:=d/R3

R5:=R2-R4

15

Page 16: Лекция 17. Очереди.Стеки.

б) В левой части операторов присваивания использовать

вспомогательные переменные R1,R2,R3,..., сократив до минимума число этих

переменных.

Пример.

Вх. текст: abc*+def+/-

Результат: R1:=b*c

R1:=a+R1

R2:=e+f

R2:=d/R2

R2:=R1-R2

3. Дано арифметическое выражение длиной до 30 символов,

оканчивающееся знаком равенства. Выражение содержит знаки операций +,

-, *, / и однозначные целые числа и представлено в обратной польской

записи. Вычислить значение выражения, используя стек операндов.

Пример.

Вх. текст: 345+2*63/-+= 19 результат

4. Дано скобочное выражение длиной до 50 символов,

оканчивающееся пробелом. Напечатать попарно порядковые номера

соответствующих открывающих и закрывающих скобок в выражении.

Пример.

1 4 8 10 14 19 27

Вх. текст: (a+(b+c)/(a-b)*(x+(y+2)**3))/2

Результат: 4 810 1419 2316 271 28

Указание. Для запоминания номеров открывающих скобок

использовать стек.

16

Page 17: Лекция 17. Очереди.Стеки.

5. Дана последовательность чисел, оканчивающаяся нулем.

а) Напечатать только отрицательные числа из этой

последовательности, причем, если подряд идет несколько отрицательных

чисел, печатать их в обратном порядке. Например:

Вх. последовательность: 5 -1 -20 -8 10 14 -3 5 -2 -13 -7 0

Результат: -8 -20 -1 -3 -7 -13 -2

Указание. Последовательность подряд расположенных отрицательных

чисел помещать в стек.

б) Напечатать только положительные числа из этой

последовательности, причем, если подряд идет несколько положительных

чисел, печатать их в обратном порядке. Например:

Вх. последовательность: -2 5 10 20 15 -4 -6 2 -10 3 1 4 -5 0

Результат: 15 20 10 5 2 4 1 3

Указание. Последовательность подряд расположенных

положительных чисел помещать в стек.

6. Дана последовательность из n слов (n<=20) в алфавитном порядке.

Длина каждого слова не более 10 букв. Напечатать слова, начинающиеся с

одной и той же буквы в обратном порядке, используя стек. Например:

Вх. последовательность: Результат:

Август апрельАпрель августМай мореМарт мартМоре майЛето лето

17