Skip to content

Лабораторна робота №4

Умова

  • Реалізуйте стек за допомогою лінійного зв'язаного списку L. Операції PUSH і POP мають виконуватися за час O(1).

  • Реалізуйте чергу за допомогою лінійного зв'язаного списку L. Операції ENQUEUE і DEQUEUE мають виконуватися за час О(1).

Аналіз

Обидві структури мають дуже схожу реалізацію через зв'язний список. Єдиним чим вони відрізняються, це тим звідки ми беремо значення. У стеку з кінця, а в черзі з початку.

Для Pop та Dequeue я буду використовувати Try- паттерн. Метод буде повертати true та встановлювати реальне значення out параметру, коли коллекція має елементи. Інакше функція повертає false та out параметр має значення за замовчуванням.

Це використовується так, наприклад:

cs
if(stack.TryPop(out var value)) {
  Console.WriteLine("Value exist")
}
if(stack.TryPop(out var value)) {
  Console.WriteLine("Value exist")
}

Структура основних вхідних та вихідних даних

Для Push та Enqueue це значення. Для TryPop та TryDequeue це out параметр (змінна в яку буде записане значення в функції)

Текст програми

Program.cs

cs
using Lab4;

Console.WriteLine("Stack");
var stack = new RomanStack<int>();
Track.Add("Push", stack.Push, 4);
Track.Add("Push", stack.Push, 1);
Track.Add("Push", stack.Push, 3);
Track.Remove<int>("Pop", stack.TryPop);
Track.Add("Push", stack.Push, 8);
Track.Remove<int>("Pop", stack.TryPop);

Console.WriteLine();

Console.WriteLine("Queue");
var queue = new RomanQueue<int>();
Track.Add("Enqueue", queue.Enqueue, 4);
Track.Add("Enqueue", queue.Enqueue, 1);
Track.Add("Enqueue", queue.Enqueue, 3);
Track.Remove<int>("Dequeue", queue.TryDequeue);
Track.Add("Enqueue", queue.Enqueue, 8);
Track.Remove<int>("Dequeue", queue.TryDequeue);
using Lab4;

Console.WriteLine("Stack");
var stack = new RomanStack<int>();
Track.Add("Push", stack.Push, 4);
Track.Add("Push", stack.Push, 1);
Track.Add("Push", stack.Push, 3);
Track.Remove<int>("Pop", stack.TryPop);
Track.Add("Push", stack.Push, 8);
Track.Remove<int>("Pop", stack.TryPop);

Console.WriteLine();

Console.WriteLine("Queue");
var queue = new RomanQueue<int>();
Track.Add("Enqueue", queue.Enqueue, 4);
Track.Add("Enqueue", queue.Enqueue, 1);
Track.Add("Enqueue", queue.Enqueue, 3);
Track.Remove<int>("Dequeue", queue.TryDequeue);
Track.Add("Enqueue", queue.Enqueue, 8);
Track.Remove<int>("Dequeue", queue.TryDequeue);

Track.cs

cs
namespace Lab4;

public delegate bool RemoveDelegate<T>(out T value);

/// <summary>
/// Track addition and removing from stack/queue like methods.
/// </summary>
public static class Track
{
    public static void Add<T>(string message, Action<T> action, T value)
    {
        action(value);
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine($"{message}: {value}");
        Console.ForegroundColor = ConsoleColor.White;
    }

    public static void Remove<T>(string message, RemoveDelegate<T> action)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        if (action(out var value))
        {
            Console.WriteLine($"{message}: {value}");
        }
        else
        {
            Console.WriteLine($"Nothing can be {message}");
        }
        Console.ForegroundColor = ConsoleColor.White;
    }
}
namespace Lab4;

public delegate bool RemoveDelegate<T>(out T value);

/// <summary>
/// Track addition and removing from stack/queue like methods.
/// </summary>
public static class Track
{
    public static void Add<T>(string message, Action<T> action, T value)
    {
        action(value);
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine($"{message}: {value}");
        Console.ForegroundColor = ConsoleColor.White;
    }

    public static void Remove<T>(string message, RemoveDelegate<T> action)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        if (action(out var value))
        {
            Console.WriteLine($"{message}: {value}");
        }
        else
        {
            Console.WriteLine($"Nothing can be {message}");
        }
        Console.ForegroundColor = ConsoleColor.White;
    }
}

RomanStack.cs

cs
using System.Diagnostics.CodeAnalysis;

namespace Lab4;

public class RomanStack<T>
{
    private readonly LinkedList<T> data = new();

    public void Push(T item)
    {
        data.AddLast(item);
    }

    public bool TryPop([NotNullWhen(true)] out T? value)
    {
        var last = data.Last;
        if (last == null || last.Value == null)
        {
            value = default;
            return false;
        }

        value = last.Value;
        data.RemoveLast();
        return true;
    }
}
using System.Diagnostics.CodeAnalysis;

namespace Lab4;

public class RomanStack<T>
{
    private readonly LinkedList<T> data = new();

    public void Push(T item)
    {
        data.AddLast(item);
    }

    public bool TryPop([NotNullWhen(true)] out T? value)
    {
        var last = data.Last;
        if (last == null || last.Value == null)
        {
            value = default;
            return false;
        }

        value = last.Value;
        data.RemoveLast();
        return true;
    }
}

RomanQueue.cs

cs
using System.Diagnostics.CodeAnalysis;

namespace Lab4;

public class RomanQueue<T>
{
    private readonly LinkedList<T> data = new();

    public void Enqueue(T item)
    {
        data.AddLast(item);
    }

    public bool TryDequeue([NotNullWhen(true)] out T? value)
    {
        var first = data.First;
        if (first == null || first.Value == null)
        {
            value = default;
            return false;
        }

        value = first.Value;
        data.RemoveFirst();
        return true;
    }
}
using System.Diagnostics.CodeAnalysis;

namespace Lab4;

public class RomanQueue<T>
{
    private readonly LinkedList<T> data = new();

    public void Enqueue(T item)
    {
        data.AddLast(item);
    }

    public bool TryDequeue([NotNullWhen(true)] out T? value)
    {
        var first = data.First;
        if (first == null || first.Value == null)
        {
            value = default;
            return false;
        }

        value = first.Value;
        data.RemoveFirst();
        return true;
    }
}

Результат

Стек та Черга це "умовні" структури, бо найчастіше вони не змінюють шлях, як ми структуруємо данні в пам'яті. Вони лише визначають/обмежують можливі дії з данними. Теоритично ми можемо створити 1 структуру данних й обмежувати її за допомогою інтерфейсів та отримати схожий результат.

Стек та Черга є зручними в багатьох випадках, для певних задач.