Примеры кода
Бинарный поиск
Эффективный алгоритм поиска в отсортированном массиве.
Сложность: O(log n)
public static int BinarySearch(int[] array, int target)
{
int left = 0;
int right = array.Length - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
// Проверяем, является ли средний элемент искомым
if (array[mid] == target)
return mid;
// Если искомый элемент больше, игнорируем левую половину
if (array[mid] < target)
left = mid + 1;
// Иначе игнорируем правую половину
else
right = mid - 1;
}
// Если элемент не найден
return -1;
}
// Пример использования в Unity
public class BinarySearchExample : MonoBehaviour
{
public int[] numbers = { 2, 5, 8, 12, 16, 23, 38, 56, 72, 91 };
public int targetNumber = 23;
private void Start()
{
int result = BinarySearch(numbers, targetNumber);
if (result != -1)
Debug.Log($"Число {targetNumber} найдено по индексу {result}");
else
Debug.Log($"Число {targetNumber} не найдено в массиве");
}
}
Быстрая сортировка (Quick Sort)
Эффективный алгоритм сортировки с использованием стратегии "разделяй и властвуй".
Сложность: В среднем O(n log n), в худшем случае O(n²)
public static void QuickSort(int[] array, int low, int high)
{
if (low < high)
{
// Получаем индекс опорного элемента
int partitionIndex = Partition(array, low, high);
// Рекурсивно сортируем элементы до и после раздела
QuickSort(array, low, partitionIndex - 1);
QuickSort(array, partitionIndex + 1, high);
}
}
private static int Partition(int[] array, int low, int high)
{
// Выбираем опорный элемент (в данном случае последний элемент)
int pivot = array[high];
// Индекс меньшего элемента
int i = low - 1;
for (int j = low; j < high; j++)
{
// Если текущий элемент меньше или равен опорному
if (array[j] <= pivot)
{
i++;
// Меняем элементы местами
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// Меняем опорный элемент с элементом, который больше опорного
int temp1 = array[i + 1];
array[i + 1] = array[high];
array[high] = temp1;
return i + 1;
}
// Пример использования в Unity
public class QuickSortExample : MonoBehaviour
{
public int[] numbersToSort = { 64, 34, 25, 12, 22, 11, 90 };
private void Start()
{
Debug.Log("До сортировки: " + string.Join(", ", numbersToSort));
QuickSort(numbersToSort, 0, numbersToSort.Length - 1);
Debug.Log("После сортировки: " + string.Join(", ", numbersToSort));
}
}
Представление графа и обход в ширину (BFS)
Реализация графа с использованием списка смежности и алгоритма обхода в ширину.
Сложность BFS: O(V + E), где V - количество вершин, E - количество рёбер
using System.Collections.Generic;
using UnityEngine;
public class Graph
{
private Dictionary> adjacencyList;
private int vertices;
public Graph(int v)
{
vertices = v;
adjacencyList = new Dictionary>();
for (int i = 0; i < v; i++)
{
adjacencyList[i] = new List();
}
}
// Добавление ребра в граф
public void AddEdge(int v, int w)
{
adjacencyList[v].Add(w);
}
// Обход в ширину (BFS) начиная с вершины s
public List BFS(int s)
{
// Массив для отслеживания посещённых вершин
bool[] visited = new bool[vertices];
Queue queue = new Queue();
List result = new List();
// Помечаем текущую вершину как посещённую и ставим в очередь
visited[s] = true;
queue.Enqueue(s);
while (queue.Count > 0)
{
// Извлекаем вершину из очереди и добавляем в результат
s = queue.Dequeue();
result.Add(s);
// Получаем все смежные вершины текущей вершины
foreach (int neighbor in adjacencyList[s])
{
if (!visited[neighbor])
{
visited[neighbor] = true;
queue.Enqueue(neighbor);
}
}
}
return result;
}
}
// Пример использования в Unity
public class GraphExample : MonoBehaviour
{
private void Start()
{
// Создаём граф с 4 вершинами
Graph g = new Graph(4);
// Добавляем рёбра
g.AddEdge(0, 1);
g.AddEdge(0, 2);
g.AddEdge(1, 2);
g.AddEdge(2, 0);
g.AddEdge(2, 3);
g.AddEdge(3, 3);
Debug.Log("Обход в ширину, начиная с вершины 2:");
List bfsResult = g.BFS(2);
Debug.Log("Порядок обхода: " + string.Join(" -> ", bfsResult));
}
}
Алгоритм A* для поиска пути
Эффективный алгоритм поиска пути в графе с эвристической функцией.
Сложность: Зависит от эвристики, в худшем случае O(b^d), где b - коэффициент ветвления, d - глубина решения
using System.Collections.Generic;
using UnityEngine;
public class AStarPathfinding : MonoBehaviour
{
public class Node
{
public Vector2Int Position { get; set; }
public int GCost { get; set; } // Расстояние от старта
public int HCost { get; set; } // Эвристическая оценка до цели
public int FCost => GCost + HCost; // Общая стоимость
public Node Parent { get; set; }
public bool IsWalkable { get; set; } = true;
}
public static List FindPath(Node[,] grid, Vector2Int startPos, Vector2Int targetPos)
{
Node startNode = grid[startPos.x, startPos.y];
Node targetNode = grid[targetPos.x, targetPos.y];
List openSet = new List();
HashSet closedSet = new HashSet();
openSet.Add(startNode);
while (openSet.Count > 0)
{
// Находим узел с наименьшей F-стоимостью
Node currentNode = openSet[0];
for (int i = 1; i < openSet.Count; i++)
{
if (openSet[i].FCost < currentNode.FCost ||
(openSet[i].FCost == currentNode.FCost && openSet[i].HCost < currentNode.HCost))
{
currentNode = openSet[i];
}
}
openSet.Remove(currentNode);
closedSet.Add(currentNode);
// Если достигли цели
if (currentNode == targetNode)
{
return RetracePath(startNode, targetNode);
}
// Проверяем соседей
foreach (Node neighbor in GetNeighbors(currentNode, grid))
{
if (!neighbor.IsWalkable || closedSet.Contains(neighbor))
continue;
int newMovementCostToNeighbor = currentNode.GCost + GetDistance(currentNode, neighbor);
if (newMovementCostToNeighbor < neighbor.GCost || !openSet.Contains(neighbor))
{
neighbor.GCost = newMovementCostToNeighbor;
neighbor.HCost = GetDistance(neighbor, targetNode);
neighbor.Parent = currentNode;
if (!openSet.Contains(neighbor))
openSet.Add(neighbor);
}
}
}
return null; // Путь не найден
}
private static List RetracePath(Node startNode, Node endNode)
{
List path = new List();
Node currentNode = endNode;
while (currentNode != startNode)
{
path.Add(currentNode.Position);
currentNode = currentNode.Parent;
}
path.Reverse();
return path;
}
private static int GetDistance(Node nodeA, Node nodeB)
{
// Манхэттенское расстояние
int dstX = Mathf.Abs(nodeA.Position.x - nodeB.Position.x);
int dstY = Mathf.Abs(nodeA.Position.y - nodeB.Position.y);
return 10 * (dstX + dstY);
}
private static List GetNeighbors(Node node, Node[,] grid)
{
List neighbors = new List();
Vector2Int[] directions = {
new Vector2Int(0, 1), // Вверх
new Vector2Int(1, 0), // Вправо
new Vector2Int(0, -1), // Вниз
new Vector2Int(-1, 0) // Влево
};
foreach (Vector2Int dir in directions)
{
int checkX = node.Position.x + dir.x;
int checkY = node.Position.y + dir.y;
if (checkX >= 0 && checkX < grid.GetLength(0) &&
checkY >= 0 && checkY < grid.GetLength(1))
{
neighbors.Add(grid[checkX, checkY]);
}
}
return neighbors;
}
}
// Пример использования в Unity
public class PathfindingExample : MonoBehaviour
{
public Vector2Int gridSize = new Vector2Int(10, 10);
public Vector2Int startPos = new Vector2Int(0, 0);
public Vector2Int targetPos = new Vector2Int(9, 9);
private void Start()
{
// Создаём сетку
AStarPathfinding.Node[,] grid = new AStarPathfinding.Node[gridSize.x, gridSize.y];
// Инициализируем узлы сетки
for (int x = 0; x < gridSize.x; x++)
{
for (int y = 0; y < gridSize.y; y++)
{
grid[x, y] = new AStarPathfinding.Node
{
Position = new Vector2Int(x, y),
GCost = int.MaxValue,
HCost = 0,
IsWalkable = true
};
}
}
// Добавляем несколько препятствий (для примера)
for (int i = 2; i < 8; i++)
{
grid[i, 5].IsWalkable = false;
}
// Находим путь
List path = AStarPathfinding.FindPath(grid, startPos, targetPos);
if (path != null)
{
string pathString = "";
foreach (Vector2Int pos in path)
{
pathString += $"({pos.x},{pos.y}) -> ";
}
pathString = pathString.TrimEnd("-> ".ToCharArray());
Debug.Log("Найден путь: " + pathString);
}
else
{
Debug.Log("Путь не найден!");
}
}
}
Оптимизация с помощью мемоизации (на примере чисел Фибоначчи)
Использование мемоизации для оптимизации рекурсивных вычислений.
Сложность без мемоизации: O(2^n)
Сложность с мемоизацией: O(n)
Сложность с мемоизацией: O(n)
using System.Collections.Generic;
using UnityEngine;
public class MemoizationExample : MonoBehaviour
{
// Классическая рекурсивная реализация (очень неэффективная)
public static int FibonacciRecursive(int n)
{
if (n <= 1)
return n;
return FibonacciRecursive(n - 1) + FibonacciRecursive(n - 2);
}
// Оптимизированная версия с мемоизацией
private static Dictionary _memo = new Dictionary();
public static long FibonacciMemoized(int n)
{
if (n <= 1)
return n;
// Если значение уже было вычислено, возвращаем его из кэша
if (_memo.ContainsKey(n))
return _memo[n];
// Иначе вычисляем и сохраняем в кэш
long result = FibonacciMemoized(n - 1) + FibonacciMemoized(n - 2);
_memo[n] = result;
return result;
}
// Итеративная версия (самая эффективная по памяти)
public static long FibonacciIterative(int n)
{
if (n <= 1)
return n;
long a = 0, b = 1, result = 0;
for (int i = 2; i <= n; i++)
{
result = a + b;
a = b;
b = result;
}
return result;
}
private void Start()
{
int n = 40; // Для больших значений разница будет очень заметна
// Тестируем рекурсивную версию (очень медленно для n > 40)
// long resultRecursive = FibonacciRecursive(n);
// Debug.Log($"Рекурсивно: Fib({n}) = {resultRecursive}");
// Тестируем версию с мемоизацией
System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
long resultMemoized = FibonacciMemoized(n);
stopwatch.Stop();
Debug.Log($"Мемоизация: Fib({n}) = {resultMemoized} (за {stopwatch.ElapsedMilliseconds} мс)");
// Тестируем итеративную версию
stopwatch.Restart();
long resultIterative = FibonacciIterative(n);
stopwatch.Stop();
Debug.Log($"Итеративно: Fib({n}) = {resultIterative} (за {stopwatch.ElapsedMilliseconds} мс)");
}
}
// Пример использования мемоизации для кэширования результатов тяжёлых вычислений
public class ExpensiveCalculation
{
private Dictionary _cache = new Dictionary();
public float CalculateExpensiveValue(float param1, float param2)
{
// Создаём ключ кэша на основе параметров
string cacheKey = $"{param1}_{param2}";
// Если результат уже есть в кэше, возвращаем его
if (_cache.ContainsKey(cacheKey))
{
Debug.Log("Используем кэшированный результат");
return _cache[cacheKey];
}
// Иначе выполняем тяжёлые вычисления
Debug.Log("Выполняем тяжёлые вычисления...");
// Имитация сложных вычислений
float result = 0;
for (int i = 0; i < 1000000; i++)
{
result = Mathf.Sin(param1) * Mathf.Cos(param2);
}
// Сохраняем результат в кэш
_cache[cacheKey] = result;
return result;
}
}