Skip to content

Просто алгоритмы из курсы лекций "Структуры и алгоритмы обработки данных"

Notifications You must be signed in to change notification settings

artemilin-2023/Algorithms

Repository files navigation

Алгоритмы и структуры данных GitHub Repo stars

Посчитал репозиторий полезным? Поставь звезду

Данный репозиторий содержит мою реализацию структрур и алгоритмов обработки данных из соответствующего курса лекций МАИ, а так же мое вольное описание принципа работы Алгоритмов/структур, на достоверность не претендую.

Навигация

Алгоритмы обработки данных

Я буду рассматривать только алгоритмы поиска и сортировки, алгоритмы оптимизации оставлю на потом.

Поиск

Глобально алгоритмы поиска элемента в массиве делятся на линейные и бинарные. Преимущество линейного поиска заклчается в том, что для его работы нет ограничения на входные данные: поиск может осуществляться как в отсортированом массиве, так и не отсортированном. Бинарный поиск работает быстрее (в худшем случае), но требует, чтобы исходный массив был отсортирован.

Сравнение алгоритмов поиска

Тип Название лучший случай худший случай память
Линейный LinearSearch $\Omega(1)$ $O(n)$ $O(1)$
Линейный SentialSearch $\Omega(1)$ $O(n)$ $O(1)$
Бинарный BinarySearch $\Omega(\log(n))$ $O(\log(n))$ $O(1)$

Линейный поиск

Реализация тут

Классический линейный поиск. Принцип работы заключается в том, что в цикле поэлементно сравнивается текущее значение множества с искомым. Если мы перебрали весь исходный массив, а искомый элемент так и не нашелся, возвращается значение -1.

Пограничный поиск

Реализация тут

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

Двоичный поиск

Реализация тут

Идея заключается в том, что мы "сужаем" диапазон, в котором может находиться искомый элемент, учитывая то, что мы знаем, с какого индекса находятся элементы меньше искомого, а с какого - больше.

Алгоритм:

  1. Определяется значение элемента в середине массива. Полученное значение сравнивается с искомым элементом.
  2. Если искомый элемент меньше значения середины, то поиск осуществляется в первой половине элементов, иначе — во второй.
  3. Поиск сводится к тому, что вновь определяется значение серединного элемента в выбранной половине и сравнивается с искомым элеменом.
  4. Процесс продолжается до тех пор, пока не будет найден элемент со значением искомого или не станет пустым интервал для поиска.

binsearch

Сортировка

Алгоритмов сортировки существует множество, я рассмотрю только несколько наиболее популярных.

Сравнение алгоритмов сортировки

Название Средний случай Худший случай Память
Сортировка пузырьком $O(n^2)$ $O(n^2)$ $O(1)$
Сортировка вставками $O(n^2)$ $O(n^2)$ $O(1)$
Сортировка выбором $O(n^2)$ $O(n^2)$ $O(1)$
Сортировка слиянием $O(n*log(n))$ $O(n*log(n))$ $O(n)$
Быстрая сортировка $O(n*log(n))$ $O(n^2)$ $O(log(n))$
Параллельная сортировка слиянием $O(n)$ $O(n)$ $O(n)$

Сортировка пузырьком

Реализация тут

Простейшая для реализации и понимания сортирровка. Принцип работы заключается в выполнении нескольких проходов по массиву: начиная c 0 элемента, перебираются соседние пары. Если левый элемент пары больше правого, то они меняются местами (при сортировке по возрастанию).

Псевдокод:

function bubble_sort(array)
    for i from 0 to length(array) - 1 do
        for j from 0 to length(array) - 1 - i do
            if array[j] > array[j + 1]
              swap(array[j - 1], array[j])
            end if

bubble sort

Сортировка вставками

Реализация тут

Изначально массив разделяется на отсортированную часть и неотсортированную, затем берется первый элемент из неотсортированной части и вставляется в нужное место в отсортированной. Это действие продолжается до тех пор, пока в неостортированной части не закончатся элементы.

Псевдокод:

function insert_sort(array)
    for i from 0 to length(array) - 1 do
        insert(array, i, sorted + 1)

function insert(array, endOfSortedPartIndex, itemIndex)
    // получение индекса вставки может быть реализовано разными способами, например - через бинарный поиск
    insertToIndex = getInsertToIndex(endSortedPartIndex, array[itemIndex])

    for i from itemIndex to insertToIndex step -1 do
        swap(array[i], array[i - 1])

insert sort

Сортировка выбором

Реализация тут

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

Псевдокод:

function selection_sort(array)
    for i from 0 to length(array) - 1 do
        min_index = I
        for j from i + 1 to length(array) do
            if array[j] < array[min_index]
                min_index = j
            end if
        swap(array[i], array[min_index])

selection sort selection sort points

Сортировка слиянием

Реализация тут

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

Алгоритм работает по приципу "разделяй и властвуй": исходное множество делится на две части, каждая из которых так же делится на две части. Когда часть множества состоит всего из одного (или нуля) элемента и делить дальше уже некуда, мы получаем отсортированное множество (множество из одного элемента по умолчанию отсортированно, а пустое - тем более). Затем наступает этап слияния двух уже отсортированных подможеств в одно большей размерности.

Псевдокод:

function mergesort(m)
    var list left, right, result
    if length(m) ≤ 1
        return m
    else
        middle = length(m) / 2
        for each x in m up to middle
            add x to left
        for each x in m after middle
            add x to right
        left = mergesort(left)
        right = mergesort(right)
        result = merge(left, right)
        return result
    end if

function merge(left,right)
    var list result
    while length(left) > 0 and length(right) > 0
        if first(left) ≤ first(right)
            append first(left) to result
            left = rest(left)
        else
            append first(right) to result
            right = rest(right)
        end if
    while length(left) > 0 
        append first(left) to result
        left = rest(left)
    while length(right) > 0 
        append first(right) to result
        right = rest(right)
    return result

merge sort

Быстрая сортировка

Реализация тут

Общая идея алгоритма состоит в следующем:

  • Выбрать из массива элемент, называемый опорным. Это может быть любой из элементов массива. От выбора опорного элемента не зависит корректность алгоритма, но в отдельных случаях может сильно зависеть его эффективность. Сравнить все остальные элементы с опорным и переставить их в массиве так, чтобы разбить массив на три непрерывных отрезка, следующих друг за другом: элементы "меньшие" опорного, "равные" и "большие".
  • Для отрезков "меньших" и "больших" значений выполнить рекурсивно ту же последовательность операций, если длина отрезка больше единицы. На практике массив обычно делят не на три, а на две части: например, "меньшие опорного" и "равные и большие"; такой подход в общем случае эффективнее, так как упрощает алгоритм разделения. В своей реализации я поступил именно так.

Псевдокод:

function quick_sort(array, left, right)
    if left >= right
        return
    end if

    var i, j, = left, right
    var pivot = array[left]
    while i <= j do
        while array[i] < pivot do
            i++
        end while
        while array[j] > pivot do
            j--
        end while

        if i <= j
            swap(array[i], array[j])
            i++
            j--
        end if
    end while

    quick_sort(array, left, j)
    quick_sort(array, i, right)

quick sort

Параллельная сортировка слиянием

Реализация тут

Одно из преимуществ сортировки слиянием залкючается в том, что ее очень просто распараллелить: достаточно рекурсивные вызовы делать в новых потоках, а затем дожидаться выполнения этих вызовов и производить слияние в вызывающем потоке.

parallel merge sort

Структуры данных

В процессе оформления

Список

в процессе оформления

Очередь

в процессе оформления

Хеш-таблица

в процессе оформления

Бинароне дерево поиска

в процессе реализации

Красно-чёрное дерево поиска

в процессе реализации

About

Просто алгоритмы из курсы лекций "Структуры и алгоритмы обработки данных"

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages