
- •Основні положення теорії алгоритмів та її застосування
- •Введення до теорії алгоритмів
- •Загальні риси алгоритмів
- •Машина Поста
- •Машина т’юринга
- •Основи лямбда-числення та функціонального програмування
- •Теза Черча-т’юринга про алгоритмічну розв’язність задачі
- •Проблема розв’язності (зависання)
- •Алгоритмічно нерозв’язні задачі
- •Проблема відсутності загального методу вирішення задачі
- •Проблема інформаційної невизначеності задачі
- •Проблема логічної нерозв’язності задачі
- •Побудова машини т’юринга для обчислення деяких простих функцій
- •Введення до оцінки складності алгоритмів
- •Визначення порядку складності алгоритму
- •Оптимізація алгоритмів
- •Структури даних в алгоритмічній мові програмування
- •Визначення алгоритмічної мови програмування
- •Базові елементи сучасної мови програмування: типи даних; екземпляри даних; вирази; оператори; функції; класи.
- •Поняття типу даних
- •Прості типи: числові; символьні; логічні.
- •Тип даних рядок
- •Структуровані типи даних: масиви, записи, множини
- •Типи даних за значенням і за посиланням
- •Сумісність типів та перетворення між типами даних
- •Екземпляри даних: змінні, константи
- •Видимість даних
- •Управління ходом виконання та структурування програм в алгоритмічній мові програмування
- •Вирази, операнди та операції
- •Основні операції: арифметичні; логічні; бульові; з використанням рядків
- •Поняття оператора
- •Прості оператори: присвоювання; виклику функції
- •Оператори управління ходом виконання: розгалуження; вибору; цикли; переходу
- •Структурування програм: функції та класи
- •Параметри функцій: вхідні, результуючі.
- •Рекурсивні функції
- •Файли: текстові, бінарні
- •Алгоритми чисельних методів, апроксимації функцій, інтегрування та вирішення рівнянь з одним невідомим
- •Чисельні методи
- •Особливості вирішення задач чисельними методами, точність та коректність рішень
- •Апроксимації функцій: лінійна інтерполяція; інтерполяційний многочлен Ньютона.
- •Чисельне інтегрування: метод трапецій; метод Сімпсона; метод Сімпсона з оцінкою погрішності.
- •Вирішення рівнянь з одним невідомим: метод простих ітерацій; метод Ньютона; метод парабол.
- •Алгоритми вирішення системи лінійних рівнянь, пошуку екстремуму функції
- •Вирішення системи лінійних рівнянь методом Гауса
- •Пошук екстремуму функцій одної змінної: метод золотого перетину; метод парабол.
- •Пошук екстремуму функцій багатьох змінних: метод координатного спуску; метод найскорішого спуску.
- •Алгоритми обробки масивів
- •Визначення масивів
- •Операції над масивами
- •Упорядкування масивів: сортування вибором; сортування вставкою; бульбашкове сортування; сортування методом Шелла; метод швидкого сортування.
- •Вибір методів сортування
- •Пошук в упорядкованих масивах методом половинного поділу, інтерполяційним методом
- •Застосування індексів для пошуку у невпорядкованих даних
- •Алгоритми обробки даних на основі списків та дерев
- •Визначення списку
- •Види списків: незалежні списки, однозв’язані списки; двозв’язані списки; кільцеві списки; упорядковані списки
- •Основні операції над списками: включення елементу до списку; видалення елементу; перехід між елементами; ітератор для списку
- •Упорядкування та пошук в списках
- •Похідні структури даних: черга, стек, дек
- •Визначення дерева
- •Впорядковані дерева
- •Бінарні дерева
- •Основні операції з бінарними деревами: включення елементу; видалення елементу; обхід дерева
- •Балансування дерева
- •Алгоритми обробки текстових даних на основі регулярних виразів
- •Введення до теорії кінцевих автоматів
- •Графічне представлення кінцевих автоматів
- •Використання кінцевого автомату: синтаксичний аналіз.
- •Реалізація синтаксичного аналізу файлу з розділяючими комами
- •Детерміновані та недетерміновані кінцеві автомати
- •Регулярні вирази
- •Форма Бекуса-Наура для запису регулярних виразів
- •Синтаксичний аналіз регулярних виразів
- •Компіляція регулярних виразів
- •Інструменти для спрощення роботи з регулярними виразами
- •Зіставлення рядків з регулярними виразами.
- •Алгоритми систем числення
- •Введення до систем числення
- •Двійкова система числення
- •Шістнадцяткова система числення
- •Системи числення з нетрадиційними основами
- •Перетворення між різними системами числення
- •Арифметика чисел з плаваючою комою
- •Точність операцій з плаваючою комою
- •Арифметика великих чисел
- •Алгоритми криптографії та хешування
- •Значення випадкових чисел у програмуванні
- •Алгоритми генерації рівномірно розподілених псевдовипадкових чисел
- •Перевірка якості випадкових чисел
- •Кодування з виправленням помилок
- •Стиснення даних
- •Стиснення даних зі словником
- •Алгоритм стиснення даних Лемпела-Зіва
- •Введення до криптографії
- •Елементи теорії порівнянь
- •Шифрування за допомогою випадкових чисел
- •Створення таємного ключа по Діффі-Хеллману
- •Система rsa
- •Алгоритми цифрового підпису
- •Введення до хешування
- •Функції хешування
- •Проста функція хешування рядків
- •Функції хешування з використанням рандомізації
- •Вирішення конфліктів за допомогою лінійного зондування
- •Псевдовипадкове зондування
- •Подвійне хешування
Впорядковані дерева
Дерево вважається орієнтованим, якщо воно задовольняє наступним вимогам:
у дерева існує один корінь;
кожен вузол дерева може бути досягнутий із кореня.
Дерево вважається впорядкованим, якщо порядок його елементів є фіксованим.
Наприклад, структура папок і файлів є орієнтованим впорядкованим деревом.
Бінарні дерева
Бінарне дерево - це динамічна структура даних, що складається з вузлів (елементів), кожен з яких містить, окрім даних, не більше двох посилань на інші бінарні дерева. На кожен вузол припадає рівно одне посилання. Початковий вузол називається коренем дерева (рисунок 6.7). Якщо дерево організоване таким чином, що для кожного вузла всі ключі його лівого піддерева менші за ключ цього вузла, а всі ключі його правого піддерева - більші, воно називається деревом пошуку. Однакові ключі в деревах пошуку не допускаються.
В дереві пошуку можна знайти елемент за ключем, рухаючись від кореня і переходячи на ліве або праве піддерево в залежності від значення ключа в кожному вузлі. Такий спосіб набагато ефективніший пошуку по списку, так як час виконання операції пошуку визначається висотою дерева. Дерево є рекурсивною структурою даних, так як кожне піддерево є також деревом. Дії з такими структурами даних простіше всього описувати за допомогою рекурсивних алгоритмів.
Рисунок 7.11 – Бінарне дерево
Основні операції з бінарними деревами: включення елементу; видалення елементу; обхід дерева
Існують три види таких обходів, кожний яких визначається рекурсивно:
Прямий порядок (preorder) наступної послідовності:
відвідати корінь
відвідати ліве піддерево
відвідати праве піддерево
Тобто, в такому порядку обходу кожна вершина відвідується до того, як будуть відвідані її діти.
Зворотний порядок (postorder) наступної послідовності:
відвідати ліве піддерево
відвідати праве піддерево
відвідати корінь
Тобто, в такому порядку кожна вершина відвідується лише після того, як будуть відвідані її діти.
Центрований (центральний) порядок (inorder) наступної послідовності:
відвідати ліве піддерево
відвідати корінь
відвідати праве піддерево
В такому порядку кожна вершина відвідується між відвіданням лівої та правої дитини. Такий порядок особливо часто застосовується в бінарних деревах пошуку, тому що дає можливість обходу вершин у порядку збільшення їхніх порядкових номерів.
На рисунку зображено, яким чином здійснюється обхід бінарного дерева:
Рисунок 7.12 – Різні варіанти обходу бінарного дерева
Прямий обхід (префіксний обхід, обхід в глибину «зверху вниз»):
Почати з кореня дерева.
Помітити поточну вершину
Здійснити прямий обхід лівого піддерева
Здійснити прямий обхід правого піддерева.
Прямий обхід подано на рисунку.
Рисунок 7.13 – Прямий обхід дерева
Зворотній обхід (постфіксний обхід, обхід в глибину «знизу вгору»):
Почати з кореня дерева.
Здійснити зворотній обхід лівого піддерева.
Здійснити зворотній обхід правого піддерева.
Помітити поточну вершину.
Зворотній обхід подано на рисунку.
Рисунок 7.14 – Зворотній обхід
Центрований обхід (синтаксичний обхід, інфіксний обхід, обхід «зліва направо»):
Почати з кореня дерева
Здійснити прямий обхід лівого піддерева
Помітити поточну вершину
Здійснити прямий обхід правого піддерева
Центрований обхід подано на рисунку.
Рисунок 7.15 – Центрований обхід
Обхід в ширину:
Помітити корінь дерева
Помітити всі вершини 1-го рівня (зліва направо)
Помітити всі вершини 2-го рівня
…
Варіант алгоритму обходу в ширину:
Занести (і помітити) в чергу корінь дерева
Доки черга не стане пустою повторювати наступні дії:
2.1. Видалити перший елементи із голови черги.
2.2. Додати (і помітити) в хвіст черги всіх потомків видаленої черги
Включення елементу до дерева
Розглянемо приклад реалізації бінарного дерева
using System;
using System.Collections.Generic;
using System.Text;
namespace Tree
{
class Node
{
public String Data = null;
public Node Parent = null;
public Node LeftChild = null;
public Node RightChild = null;
public void SetLeftChild(Node aLeftChild)
{
this.LeftChild = aLeftChild;
if (aLeftChild != null)
{
aLeftChild.Parent = this;
}
}
public void SetRightChild(Node aRightChild)
{
this.RightChild = aRightChild;
if (aRightChild != null)
{
aRightChild.Parent = this;
}
}
//Конструктор для вершин з дітьми
public Node(Node aLeftChild, Node aRightChild)
{
this.SetLeftChild(aLeftChild);
this.SetRightChild(aRightChild);
}
// Конструктор для вершин без дітей
public Node()
{
Parent = null;
this.SetLeftChild(null);
this.SetRightChild(null);
}
}
class Program
{
static void Main(string[] args)
{
Node a = new Node();
Node b = new Node();
Node c = new Node(a, b);
}
}
}
Рисунок 7.16 – Приклад дерева
Включення елементу d між елементами c та b:
class Program
{
static void Main(string[] args)
{
Node a = new Node();
Node b = new Node();
Node c = new Node(a, b);
Node d = new Node();
d.SetLeftChild(b);
c.SetRightChild(d);
}
}
Рисунок 7.17 – Приклад включення елемента
Прямий обхід дерева:
// Прямий обхід
public static void PreorderTravel(Node current)
{
if (current != null)
{
Console.WriteLine(current.Data);
PreorderTravel(current.LeftChild);
PreorderTravel(current.RightChild);
}
}
Створення дерева і виконання обходу:
Node a = new Node();
a.Data = "5";
Node b = new Node();
b.Data = "11";
Node c = new Node(a, b);
c.Data = "6";
Node d = new Node();
d.Data = "2";
a = new Node(d, c);
a.Data = "7";
c = new Node();
c.Data = "4";
d = new Node(c, null);
d.Data = "9";
c = new Node(null, d);
c.Data = "5";
b = new Node(a, c);
b.Data = "2";
PreorderTravel(b);
Результат:
Рисунок 7.16 – Результат виконання прямого обходу
Центрований обхід:
// Центрований обхід
public static void CenterOrderTravel(Node current)
{
if (current != null)
{
CenterOrderTravel(current.LeftChild);
Console.WriteLine(current.Data);
CenterOrderTravel(current.RightChild);
}
}
На тому ж дереві:
CenterOrderTravel(b);
Результат:
Рисунок 7.17 – Результат виконання центрованого обходу
Зворотній обхід:
// Зворотній обхід
public static void ReverseOrderTravel(Node current)
{
if (current != null)
{
ReverseOrderTravel(current.LeftChild);
ReverseOrderTravel(current.RightChild);
Console.WriteLine(current.Data);
}
}
На тому ж дереві:
ReverseOrderTravel(b);
Рисунок 7.17 – Результат виконання зворотнього обходу
Пошук по дереву з включенням
Основна сфера використання бінарних дерев – реалізація алгоритмів бінарного пошуку (такі дерева мають назву бінарних дерев пошуку, BST).
Побудова бінарного дерева пошуку здійснюється шляхом виконання процедури, яка має назву «пошук по дереву з включенням» - у її реалізації здійснюється пошук елемента в дереві, і якщо його не знайдено, то елемент розміщується у необхідній позиції на дереві.
Рисунок 7.18 – Бінарне дерево пошуку
Видалення із дерева:
class Program
{
static void Main(string[] args)
{
Node a = new Node();
Node b = new Node();
Node c = new Node(a, b);
// Включення вузла d
Node d = new Node();
d.SetLeftChild(b);
c.SetRightChild(d);
// Повернення до попереднього варіанту дерева
c.SetRightChild(b);
}
}