Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие часть 1.doc
Скачиваний:
66
Добавлен:
24.09.2019
Размер:
6.98 Mб
Скачать

2.3.4. Очереди с приоритетами

В реальных задачах часто возникает необходимость организации очередей, принцип работы которых отличен от FIFO. Порядок выборки элементов в них определяется приоритетами элементов. Приоритет — это некоторое числовое значение, связанное с элементами очереди, в простейшем случае, это может быть само значение элемента. При выборке элемента из очереди каждый раз выбирается элемент с наибольшим приоритетом.

Различают два способа реализации очередей с приоритетами.

1. Очереди с приоритетным включением. В этом случае очередь всё время поддерживается упорядоченной, т.е. каждый новый элемент помещается в то место очереди, которое определяется его приоритетом.

2. Очереди с приоритетным исключением. Новые элементы помещаются в конец очереди, при исключении элемента ищется элемент с максимальным приоритетом.

Реализация очередей с приоритетами на базе отсортированных массивов или списков используется при небольшом количестве элементов такой очереди. При небольшом количестве приоритетов может использоваться так называемый корзинный способ. Наиболее удобной формой реализации больших очередей с приоритетами является реализация с помощью частично упорядоченных деревьев. Но об этом речь пойдет в следующих разделах.

2.3.5. Пример программы с использованием очереди

Лабиринт представляет собой клетчатое поле NxM клеток, некоторые из них пустые, другие закрашены - через них проходить нельзя. За один шаг можно перейти из текущей клетки в одну из свободных соседних. Требуется найти длину кратчайший путь из левого верхнего угла в правый нижний. Лабиринт считывается из текстового файла input.txt, где в первой строке записаны числа N и M (через пробел), а в каждой из последующих N строк находятся M символов информации о клетках лабиринта. При этом символ пробела обозначает пустую клетку, символ 'x' - закрашенную. Результат работы программы выводится на экран.

Идея решения состоит в следующем («метод волны»). Для начала модифицируем созданный ранее класс очереди. Пусть элементы очереди представляют собой структуру из трёх полей - координаты клетки и число шагов до неё от левого верхнего угла:

struct cell

{ int x,y,l;

};

typedef cell type_of_data;

После чтения информации о лабиринте создаем очередь и помещаем в нее первый элемент с координатами левого верхнего угла и числом шагов 0.

Алгоритм «метода волны» состоит в следующем. На каждом шаге извлекаем очередной элемент, смотрим, в какие соседние клетки можно переместиться из текущей, и помещаем в очередь элементы, соответствующие этим клеткам. При этом для них делаем количество шагов на единицу больше, чем для извлечённого элемента. После этого необходимо пометить текущую клетку как закрашенную, чтобы не возвращаться в нее в дальнейшем, и перейти к извлечению нового элемента из очереди.

В конце концов либо окажется, что очередь пуста (это означает, что все возможные пути пройдены, и все свободные клетки помечены), либо очередной из извлеченных элементов окажется с координатами правого нижнего угла. В последнем случае количество шагов в структуре и будет решением задачи, т.е. длиной кратчайшего пути. Это объясняется тем, что элементы в очередь помещаются по возрастанию количества шагов.

#include <fstream.h>

#include "queue.cpp"

main() {

ifstream fi("input.txt");

int N,M;

fi>>N>>M; fi.get(); //Считывание размеров лабиринта

char s[100]; int a[10][10], i, j;

for (i=0; i<N; i++) { //Чтение информации о клетках

fi.getline(s,100);

for (j=0; j<M; j++) {

if (s[j]=='.') a[i][j]=0;

else a[i][j]=1;

}

}

queue q; // Очередь абсцисс, ординат и длин путей

cell c;

// Заносим в очередь первую клетку:

c.x=0; c.y=0; c.l=0; q.enqueue(c);

int len;

while (!q.isnull())

{ c=q.dequeue();

i=c.y; j=c.x; len=c.l;

if ((i==N-1)&&(j==M-1)) break; // Путь найден

// Проверяем соседние с (i,j) клетки:

c.l++; //увеличиваем путь на 1

if ((i>0)&&(!a[i-1][j])) { c.y--; q.enqueue(c); c.y++; }

if ((i<N-1)&&(!a[i+1][j])) { c.y++; q.enqueue(c); c.y--; }

if ((j>0)&&(!a[i][j-1])) { c.x--; q.enqueue(c); c.x++; }

if ((j<M-1)&&(!a[i][j+1])) { c.x++; q.enqueue(c); c.x--; }

a[i][j]=1;

}

if ((i==N-1)&&(j==M-1)) cout<<"Длина кратчайшего пути равна "<<len;

else {

q.makenull();

cout<<"Путь в лабиринте не найден.";

}

cin.get(); return 0;

}

Стеки и очереди являются частными случаями более универсальной структуры данных— линейных списков.