11. Очередь. Queue(T). Поиск кратчайшего пути в двумерном массиве.
1. Очередь (fifo).
Продолжаем говорить о структурах данных.
Одна из самых полезных структур, которая позволяет описать и запрограммировать огромное количество реальных задач – очередь.
Очередь — это структура данных, представляющая собой специализированным образом организованный список элементов. Доступ к элементам очереди осуществляется по принципу FIFO (First In First Out) — первым пришел, первым вышел. Добавление элемента возможно лишь в конец очереди, выборка — только из начала очереди, при этом выбранный элемент из очереди удаляется.
Принцип работы данной структуры данных схож с обычной живой очередью в больнице. Кто раньше пришел, тот раньше зайдет на прием. Все новые пациенты выстраиваются в конец очереди по мере поступления.
Очередь в программировании используется, когда нужно совершить какие-то действия в порядке их поступления, выполнив их последовательно. Примером может служить очередность атаки юнитов в пошаговой стратегии.
Класс Queue<T> представляет обычную очередь, работающую по алгоритму FIFO ("первый вошел - первый вышел").
Произвольный доступ к элементам очереди, как и у стека, невозможен. Новые элементы всегда добавляются в конец очереди. Элемент в начале очереди можно либо получить с удалением, либо прочитать без удаления. Соответственно существуют три основные операции:
Enqueue — добавление
Dequeue — получение с удалением
Peek — чтение без удаления
Сложность алгоритмов добавления и удаления из очереди составляет O(1).
2. Использование очереди. Пример.
class Person
{
public string Name { get; set; }
}
public static void Main(string[] args)
{
Queue<int> numbers = new Queue<int>();
numbers.Enqueue(3); // очередь 3
numbers.Enqueue(5); // очередь 3, 5
numbers.Enqueue(8); // очередь 3, 5, 8
// получаем первый элемент очереди
int queueElement = numbers.Dequeue(); //теперь очередь 5, 8
Console.WriteLine(queueElement);
Queue<Person> persons = new Queue<Person>();
persons.Enqueue(new Person() { Name = "Tom" });
persons.Enqueue(new Person() { Name = "Bill" });
persons.Enqueue(new Person() { Name = "John" });
// получаем первый элемент без его извлечения
Person pp = persons.Peek();
Console.WriteLine(pp.Name);
Console.WriteLine("Сейчас в очереди "+ persons.Count+" человек");
// теперь в очереди Tom, Bill, John
foreach (Person p in persons)
{
Console.WriteLine(p.Name);
}
Console.WriteLine();
// Извлекаем первый элемент в очереди – Tom
Person person = persons.Dequeue(); // теперь в очереди Bill, John
Console.WriteLine(person.Name);
}3. Поиск кратчайшего пути в двумерном массиве (принц и принцесса).
Открыв глаза, Принц Персии обнаружил, что находится на нижнем уровне подземного лабиринта Джаффара. Лабиринт представляет собой прямоугольную площадку, разбитую на m*n участков. На некоторых участках стоят колонны, поддерживающие потолок, на такие участки Принц заходить не может.
Принц может перемещаться с одного участка на другой соседний свободный участок того же уровня. Любое перемещение занимает у Принца 5 секунд.
На одном из участков нижнего уровня Принца ждет Принцесса. Помогите Принцу найти Принцессу, потратив на это как можно меньше времени.
В исходном двумерном содержатся m строк, по n символов в каждой: «.» обозначает свободный участок, «о» (английская маленькая буква) обозначает участок с колонной, «1» обозначает свободный участок, в котором оказался Принц в начале своего путешествия, «2» обозначает свободный участок, на котором томится Принцесса. Символы «1» и «2» встречаются во входном файле ровно по одному разу.
Выведите минимальное время в секундах, необходимое Принцу, чтобы найти Принцессу или слово «Бесконечность…», если принц не может этого сделать.
class Coordinates
{
public int X { get; set; }
public int Y { get; set; }
}
private static string[] _array =
{
"1....",
".o.o.",
".oo..",
"oo...",
"..o.2"
};
public static void Main(string[] args)
{
int n = _array.Length;
int m = _array[0].Length;
Queue<Coordinates> points = new Queue<Coordinates>();
int[,] labirint = new int[n + 2, m + 2];
int xPrincess = 0;
int yPrincess = 0;
for (int i = 0; i <= n + 1; i++)
{
for (int j = 0; j <= m + 1; j++)
{
labirint[i, j] = -1;
}
}
for (int i = 0; i < n; i++)
{
string str = _array[i];
for (int j = 0; j < m; j++)
{
if (str[j] == '1')
{
labirint[i + 1, j + 1] = -10;
points.Enqueue(new Coordinates() {X = i + 1, Y = j + 1});
}
else if (str[j] == '.')
{
labirint[i + 1, j + 1] = 0;
}
else if (str[j] == '2')
{
xPrincess = i + 1;
yPrincess = j + 1;
labirint[i + 1, j + 1] = 0;
}
}
}
Print(labirint);
int step = 1;
int start = 1;
int end = 1;
int count = 1;
while (true)
{
for (int i = start; i <= end; i++)
{
Coordinates currentCoordinates = points.Dequeue();
int x = currentCoordinates.X;
int y = currentCoordinates.Y;
if (labirint[x - 1, y] == 0)
{
labirint[x - 1, y] = step;
points.Enqueue(new Coordinates() {X = x - 1, Y = y});
count++;
}
if (labirint[x + 1, y] == 0)
{
labirint[x + 1, y] = step;
points.Enqueue(new Coordinates() {X = x + 1, Y = y});
count++;
}
if (labirint[x, y - 1] == 0)
{
labirint[x, y - 1] = step;
points.Enqueue(new Coordinates() {X = x, Y = y - 1});
count++;
}
if (labirint[x, y + 1] == 0)
{
labirint[x, y + 1] = step;
points.Enqueue(new Coordinates() {X = x, Y = y + 1});
count++;
}
}
if (points.Count == 0)
{
break;
}
start = end + 1;
end = count;
step++;
}
Print(labirint);
if (labirint[xPrincess, yPrincess] == 0)
{
Console.WriteLine("Бесконечность...");
}
else if (labirint[xPrincess, yPrincess] > 0)
{
Console.WriteLine(labirint[xPrincess,yPrincess]*5+" секунд");
}
else
{
Console.WriteLine("Некорректные данные!");
}
}
private static void Print(int[,] array)
{
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
Console.Write(quot;{array[i, j]} \t");
}
Console.WriteLine();
}
Console.WriteLine();
}Задание*: Попробуйте самостоятельно вывести в консоль маршрут принца (координаты точек в нужном порядке), по которому должен пройти принц, чтобы спасти принцессу, потратив на это как можно меньше времени.
Итоги занятия.
Очередь – очень часто используемая структура, которая позволяет за О(1) добавлять и извлекать элементы в определенном порядке. Можно применять во всех задачах, где требуется совершить какие-то действия в порядке их поступления, выполнив их последовательно. А таких задач очень много. В том числе в играх…
А на следующем занятии с теми, кто еще способен соображать после принца и принцессы, мы сделаем практически невозможное! Научим компьютер искать элементы в достаточно больших структурах данных за О(1)! И попробуем разрулить возникающие споры и проблемы (коллизии)… В общем, все как в жизни…