Шпаргалка для технического собеседования

Эта шпаргалка поможет вам подготовиться к техническому собеседованию, чтобы вы могли освежить в памяти ключевые вещи. По сути, это содержание курса по информатике безо всяких подробностей.
Основы структур данных
Массив
Определение:
- Хранит элементы данных на основе последовательного индекса, чаще всего с нулевой базой.
- В его основе лежат кортежи из теории множеств.
- Массив — одна из старейших и наиболее используемых структур данных.
Что вам нужно знать:
- Массив оптимален для индексирования; плох для поиска, вставки и удаления (если не делать этого в самом конце массива).
- Основная разновидность — линейные массивы, или одноразмерные.
- Их размер статичен, то есть при объявлении линейного массива задаётся фиксированный размер.
- При заполнении динамического массива его содержимое копируется в массив большего размер.
Эффективность («О» большое):
- Индексирование: линейный массив — O(1), динамический массив — O(1).
- Поиск: линейный массив — O(n), динамический массив — O(n).
- Оптимизированный поиск: линейный массив — O(log n), динамический массив — O(log n).
- Вставка: линейный массив — недопустимо, динамический массив — O(n).
Связный список
Определение:
- Данные хранятся в узлах, указывающих на другие узлы.
- Узел содержит один элемент данных и одну ссылку (на другой узел).
- Связный список соединяет узлы друг с другом с помощью ссылок от одного узла к другому.
Что вам нужно знать:
- Связный список разработан для оптимизирования вставки и удаления. Медленно работает при индексировании и поиске.
- Двусвязный список содержит узлы, которые ссылаются на предыдущие узлы.
- Кольцевой связный список — это простой связный список, хвост которого (последний узел) ссылается на голову (первый узел).
- Стек обычно реализуется с помощью связных списков, но может быть создан и из массивов.
- Стеки — это LIFO-структуры данных (last in, first out).
- Голова связного списка, лежащего в основе стека, единственное место для вставки и удаления элементов.
- Очереди — это FIFO-структуры данных (first in, first out).
- Очередь представляет собой двусвязный список, в котором элементы удаляются из головы, а добавляются в хвост.
Эффективность («О» большое):
- Индексирование: связный список — O(n).
- Поиск: связный список — O(n).
- Оптимизированный поиск: связный список — O(n).
- Вставка: связный список — O(1).
Хэш-таблица
Определение:
- Данные хранятся в виде пар ключ-значение.
- Хэш-функции принимают ключ и возвращают выходные данные, соответствующие только этому ключу.
- Этот процесс называется хэшированием: однозначным сопоставлением друг другу входных и выходных данных.
- Хэш-функции возвращают для данных уникальные адреса в памяти.
Что вам нужно знать:
- Хэш-функции разработаны для оптимизирования поиска, вставки и удаления.
- Хэш-коллизиями называются ситуации, когда для двух разных входных данных функция возвращает одинаковые выходные данные.
- Эта проблема свойственна всем хэш-функциям.
- Часто она решается с помощью увеличения хэш-таблиц до огромного размера.
Эффективность («О» большое):
- Индексирование: хэш-таблицы — O(1).
- Поиск: хэш-таблицы — O(1).
- Вставка: хэш-таблицы — O(1).
Двоичное дерево
Определение:
- Двоичное дерево — это такая структура данных, в которой каждый узел имеет максимум два дочерних элемента.
- Дочерние элементы бывают левым и правым.
Что вам нужно знать:
- Деревья разработаны для оптимизирования списка и сортировки.
- Вырожденное дерево — это несбалансированное дерево. Если оно полностью одностороннее, то представляет собой, по сути, связный список.
- Деревья относительно просты в реализации по сравнению с другими структурами данных.
- Используются для создания двоичных деревьев поиска.
- Двоичное дерево с помощью сравнивания ключей решает, в каком направлении следовать к дочернему узлу.
- Ключ левого дочернего узла меньше, чем у родительского.
- Ключ правого дочернего узла больше, чем у родительского.
- Не может быть дублирующих узлов.
- В связи с вышесказанным такое дерево чаще используется как структура данных, чем двоичное дерево.
Эффективность («О» большое):
- Индексирование: двоичное дерево поиска — O(log n).
- Поиск: двоичное дерево поиска — O(log n).
- Вставка: двоичное дерево поиска — O(log n).
Поиск
Поиск в ширину
Определение:
- Поиск в ширину — это алгоритм, ищущий по дереву (или графу), просматривая по уровням начиная с корня.
- Алгоритм находит все узлы текущего уровня, обычно двигаясь слева направо.
- В ходе этого процесса он регистрирует все дочерние узлы, связанные с узлами на текущем уровне.
- По завершении поиска на текущем уровне, алгоритм переходит на крайний левый узел следующего уровня.
- Последним анализируется крайний правый узел самого нижнего уровня.
Что вам нужно знать:
- Поиск в ширину оптимален для поиска по дереву, чья ширина превышает глубину.
- Во время хождения по дереву, алгоритм сохраняет информацию о нём в очереди.
- В связи с использованием очереди такой метод поиска потребляет больше памяти, чем поиск в глубину.
- Очередь использует память для хранения указателей.
Эффективность («О» большое):
- Поиск: поиск в ширину — O(|E| + |V|).
- E — количество рёбер (граней?).
- V — количество вершин.
Поиск в глубину
Определение:
- Поиск в глубину — это алгоритм, ищущий по дереву (или графу) сначала в глубину начиная с корня.
- Алгоритм идёт по дереву, переходя между уровнями по левым дочерним узлам, пока не дойдёт до самого низа.
- Завершив проход по ветви, алгоритм возвращается обратно, просматривая правые дочерние узлы этой ветви. Причём, если возможно, выбирает самые левые из узлов, расположенных справа от предыдущего маршрута.
- Завершив просмотр всей ветви, алгоритм переходит к узлу, расположенному справа от корня, и снова идёт по левым дочерним узлам до самого дна.
- Последним анализируется крайний правый узел (расположенный справа от всех своих предшественников).
Что вам нужно знать:
- Алгоритм оптимален для поиска по дереву, чья глубина превышает ширину.
- Для работы алгоритма используется стек.
- Поскольку стек является LIFO-структурой, ему не нужно отслеживать указатели узлов, поэтому потребляется меньше памяти, чем в случае с поиском в ширину.
- Когда алгоритм не может дальше идти по левой стороне, он начинает анализировать стек.
Эффективность («О» большое):
- Поиск: поиск в глубину — O(|E| + |V|).
- E — количество рёбер (граней?).
- V — количество вершин.
Сравнение поисков в ширину и в глубину
- Выбирайте тип поиска в соответствии с размером и формой дерева.
- Для широких, мелких деревьев используйте поиск в ширину.
- Для глубоких, узких деревьев используйте поиск в глубину.
Нюансы:
- Поскольку поиск в ширину использует очереди для хранения информации об узлах и их детях, то он может занять больше памяти, чем доступно на вашем компьютере. (Но вам вряд ли придётся об этом беспокоиться.)
- Если применять поиск в глубину по очень глубокому дереву, то алгоритм может уходить слишком далеко вниз. Подробнее об этом читайте здесь.
- Поиск в ширину — циклический алгоритм.
- Поиск в глубину — рекурсивный алгоритм.
Эффективная сортировка
Сортировка слиянием
Определение:
- Сравнение данных с помощью алгоритма сортировки:
- Весь набор данных делится минимум на две группы.
- Пары значений сравниваются между собой, наименьшее перемещается влево.
- После сортировки внутри всех пар, сравниваются левые значения двух левых пар. Таким образом, создаётся группа из четырёх значений: два наименьшие — слева, наибольшие — справа.
- Процесс повторяется до тех пор, пока не останется только один набор.
Что вам нужно знать:
- Это один из фундаментальных алгоритмов сортировки.
- Данные делятся на как можно более маленькие наборы, которые потом сравниваются.
Эффективность («О» большое):
- Наилучший вариант сортировки: сортировка слиянием — O(n).
- Средний вариант сортировки: сортировка слиянием — O(n log n).
- Худший вариант сортировки: сортировка слиянием — O(n log n).
Быстрая сортировка
Определение:
- Алгоритм сортировки на основе сравнения.
- Весь набор данных делится пополам путём выбора среднего элемента и перемещения всех, кто меньше него, влево.
- Затем такая же процедура итерационно выполняется с левой частью до тех пор, пока не останутся только два элемента. В результате левая часть окажется отсортированной.
- Затем всё то же самое делается с правой частью.
Что вам нужно знать:
- Хотя «О» большое здесь имеет те же значения (а в ряде случаев — хуже), что и у многих других алгоритмов сортировки, но на практике этот алгоритм зачастую работает быстрее, например, той же сортировки слиянием.
- Данные будут последовательно делиться пополам, пока не будут целиком отсортированы.
Эффективность («О» большое):
- Наилучший вариант сортировки: быстрая сортировка — O(n).
- Средний вариант сортировки: быстрая сортировка — O(n log n).
- Худший вариант сортировки: быстрая сортировка — O(n^2).
Пузырьковая сортировка
Определение:
- Алгоритм сортировки на основе сравнения.
- Итерирует слева направо, сравнивая значения внутри каждой пары и перемещая наименьшее влево.
- Процесс повторяется до тех пор, пока ни одно значение уже не может быть перемещено.
Что вам нужно знать:
- Алгоритм очень прост в реализации, но наименее эффективен из всех трёх, описанных здесь.
- Сравнив два значения и переместив наименьшее влево, алгоритм переходит на одну позицию вправо.
Эффективность («О» большое):
- Наилучший вариант сортировки: пузырьковая сортировка — O(n).
- Средний вариант сортировки: пузырьковая сортировка — O(n^2).
- Худший вариант сортировки: пузырьковая сортировка — O(n^2).
Сравнение алгоритмов сортировки слиянием и быстрой сортировки
- Быстрая сортировка на практике зачастую эффективнее.
- Сортировка слиянием сразу делит набор данных на наименьшие возможные группы, а затем восстанавливает набор, инкрементально сортируя и укрупняя группы.
- Быстрая сортировка последовательно делит набор по среднему значению, пока он не будет отсортирован рекурсивно.
Основные типы алгоритмов
Рекурсивные алгоритмы
Определение:
- Как следует из определения, этот алгоритм вызывает самого себя.
- Рекурсивный сценарий — когда условный оператор используется для запуска рекурсии.
- Базовый сценарий — когда условный оператор используется для прерывания рекурсии.
Что вам нужно знать:
- Слишком глубокий уровень стека и переполнение стека.
- Если при работе рекурсивного алгоритма вы столкнулись с чем-то из перечисленного, значит, вы всё испортили.
- Это означает, что базовый сценарий не был ни разу запущен из-за ошибок, либо проблема была столь серьёзной, что у вас кончилась память, прежде чем рекурсия была прервана.
- Знание того, сможете ли вы достичь базового сценария, является неотъемлемой частью правильного использования рекурсии.
- Такие алгоритмы часто используются при поиске в глубину.
Итеративные алгоритмы
Определение:
- Итеративным называется алгоритм, вызываемый неоднократно, но ограниченное количество раз. Каждый вызов является отдельной итерацией.
- Часто применяются для инкрементального прохождения по набору данных.
Что вам нужно знать:
- Обычно итерации представлены в виде циклов, выражений for , while и until .
- Итерация — это однократный проход по набору данных.
- Такие алгоритмы часто применяются для обработки массивов.
Сравнение рекурсивности и итеративности
- Отличить рекурсивность от итеративности бывает сложно, поскольку обе они используются для реализации друг друга. Однако:
- Рекурсивность обычно более выразительна и проста в реализации.
- Итеративность потребляет меньше памяти.
Псевдокод прохождения по массиву (вот почему для этого применяется итеративность)
Рекурсивность | Итеративность ----------------------------------|---------------------------------- recursive method (array, n) | iterative method (array) if array[n] is not nil | for n from 0 to size of array print array[n] | print(array[n]) recursive method(array, n+1) | else | exit loop |Жадные алгоритмы
Определение:
- Жадными называют алгоритмы, выбирающие только ту информацию, которая удовлетворяет определённым критериям.
- Жадный алгоритм содержит пять основных компонентов:
- Набор кандидатов (candidate set), на основе которого создаётся решение.
- Функция выбора, которая решает, какой лучший кандидат будет добавлен в решение.
- Функция обоснования (feasibility function), которая решает, может ли кандидат внести вклад в решение.
- Целевая функция (objective function), которая присваивает значение решению или частичному значению.
- Функция решения (solution function), которая сигнализирует о том, что мы нашли полное решение.
Что вам нужно знать:
- Жадные алгоритмы используются для поиска оптимального решения данной проблемы.
- Обычно они применяются к наборам данных, в которых лишь небольшая порция обработанной информации даёт желаемый результат.
- Часто жадные алгоритмы могут помочь в уменьшении «О» большого другого алгоритма.
Псевдокод жадного алгоритма для поиска самой большой разницы между двумя числами в массиве
greedy algorithm (array) var largest difference = 0 var new difference = find next difference (array[n], array[n+1]) largest difference = new difference if new difference is > largest difference repeat above two steps until all differences have been found return largest differenceЭтому алгоритму не нужно сравнивать друг с другом все разницы, что экономит нам целую итерацию.
FIFO, LIFO ИЛИ FEFO? Какой из методов выбытия запасов используете вы?
Sorry, this entry is only available in Russian and Ukrainian.

Подготовлено:
Бенькович Анастасия, помощник аудитора Департамент аудита, МСФО и консалтинга
К нам поступил вопрос от предприятия, которое составляет отчетность по Международным стандартам финансовой отчетности:
- Возможно ли применение метода FEFO, как метода списания запасов в производство или для других целей?
Давайте разберемся в правильности и целесообразности применения различных методов выбытия или перемещения запасов, а так же в последствиях их изменения.
В соответствии со стандартами учета, будь то П(С)БУ или МСФО, предприятие самостоятельно выбирает метод выбытия запасов, с учетом особенности своей деятельности и обязательно отмечает его в приказе об учетной политике. При этом для всех запасов одинакового характера и использования применяется одна и та же формула себестоимости.
Все существующие методы выбытия и логистической ротации запасов мы собрали и представили для вас в схематическом виде с предоставлением основных их характеристик:

Согласно п. 16 П(С)БУ № 9 «Запасы» предусмотрено 5 методов выбытия запасов:
- идентифицированной себестоимости соответствующей единицы запасов;
- средневзвешенной себестоимости;
- себестоимости первых по времени поступления запасов (FIFO)
- нормативных затрат;
- цены продажи.
Согласно п. 23, 25 МСБУ № 2 «Запасы» выделяют 3 метода выбытия запасов (или формулы себестоимости):
- метод идентифицированной индивидуальной себестоимости;
- метод средневзвешенной себестоимости;
- метод FIFO.
Согласно Generally Accepted Accounting Principles (GAAP US) предприятие может использовать метод выбытия запасов LIFO. Однако данный метод был исключен из методов выбытия запасов по МСФО и П(С)БУ, поскольку LIFO не соответствует нормам этих стандартов – не отражает точного потока запасов в большинстве случаев и, таким образом, приводит к несоответствию доходов и расходов.
Вернемся к методам ротации запасов: FEFO, FPFO и BBD.
Метод FPFO учитывает дату выпуска продукции и предусматривает, что более «старая» продукция, то есть та, которая была изготовлена или приобретена ранее, отгружается в первую очередь, а та продукция, которая изготовлена или приобретена позже, отпускается последней. Такой подход, по нашему мнению, по своей сути, сопоставим с методом FIFO (см. Определение метода приведено в п.27 МСФО).
Метод BBD, по нашему мнению, является разновидностью метода FEFO так как в основе лежит определение срока годности запаса, в методе BBD – это рекомендуемый срок годности, в методе FEFO – минимальный остаточный срок.
Отметим, что методы FEFO, FPFO и BBD в качестве методов определения себестоимости запасов не определены ни в положениях П(С)БУ, ни в МСФО, ни в GAAP US.
Таким образом, по нашему мнению, если предприятию необходимо применить метод FEFO или BBD, то в учетной политике целесообразно его отметить как метод идентифицированной индивидуальной себестоимости, а если необходимо использовать метод FPFO – то отметить как метод FIFO.
Изменение метода выбытия запасов считается изменением учетной политики. Согласно П(С)БУ 6 «Исправление ошибок и изменения в финансовых отчетах» и МСБУ 8 «Учетная политика, изменения в бухгалтерских оценках и ошибки» изменение учетной политики применяется ретроспективно. То есть корректируется остаток каждого компонента собственного капитала на начало периода, на который это изменение влияет, за первый представленный предыдущий период, а также корректируются другие сравниваемые суммы, информация о которых раскрыта за каждый поданный предыдущий период, так, будто новая учетная политика применялась всегда.
Итак, суммируя все вышеизложенное, отметим:
- Предприятие самостоятельно выбирает метод выбытия запасов и отмечает его в учетной политике.
- Существует 5 методов выбытия запасов по П(С)БУ и 3 основных метода по МСБУ.
- В соответствии с МСФО, П(С)БУ и GAAP US методы FEFO, FPFO и BBD не применяются.
- При необходимости применения метода FEFO, целесообразно его отметить в учетной политике как метод идентифицированной индивидуальной себестоимости, при необходимости применения метода FPFO – как метод FIFO.
- Изменение метода выбытия запасов считается изменением учетной политики, применяется ретроспективно.
Остались вопросы? Свяжитесь с нами прямо сейчас!
〉 097 338 12 88, 050 340 11 17
〉 af@audit-invest.com.ua
Be the first to know!
Subscribe to the newsletter and receive
only the most useful information for your business!Родовые Контейнеры
Библиотека Qt предоставляет набор основанных на шаблонах классов-контейнеров. Эти классы могут использоваться для хранения элементов указанного типа. Например, если Вам необходим массив изменяемого размера, содержащий QString, используйте QVector.
Эти классы-контейнеры разработаны для прозрачного, безопасного и простого использования вместо контейнеров STL. Если Вы не знакомы с STL, или предпочитаете работать «только с Qt», то можете использовать эти классы вместо классов STL.
Данные классы-контейнеры используют неявное совместное использование данных, они монопоточны, а также они оптимизированы для быстрого исполнения, низкого потребления памяти и минимального раздувания кода (inline), в результате получились классы с минимальным потреблением ресурсов.
Для перебора элементов, хранящихся в контейнере, Вы можете использовать итераторы двух типов: итераторы в стиле Java и итераторы в стиле STL. Итераторы в стиле Java легче в использовании и предоставляют высокоуровневую функциональность, тогда как итераторы в стиле STL немного эффективней и могут использоваться совместно с родовыми алгоритмами Qt и STL.
Qt также предоставляет конструкцию foreach, которая позволяет очень просто перебрать все элементы, хранящиеся в контейнере.
Классы-Контейнеры
Qt предоставляет следующие классы-контейнеры:
С помощью QList::append() и QList::prepend(), элементы могут быть добавлены в оба конца списка, а с помощью QList::insert(), вставлены в середину списка. QList наиболее оптимизирован для расширения, минимизации кода и скорости исполнения, чем другие классы-контейнеры. QStringList является наследником QList.
Контейнеры могут быть вложенными. Например, вполне возможно использование QMap>, где типом ключа является QString, а типом значения — QList. Единственный нюанс — это то, что Вы должны оставить пространство между закрывающими угловыми скобками (>); иначе, компилятор C++ воспримет два символа > как оператор сдвига вправо (>>) и сообщит об ошибке.
Значения, хранящиеся в различных контейнерах, должны иметь один из присваиваемых типов данных. Для этого, тип должен предоставлять, конструктор по умолчанию, конструктор копирования и оператор присваивания. Этому соответствуют большинство типов данных, которые Вы, вероятно, захотите поместить в контейнер, включая базовые типы, такие как int и double, типы указателей и типы данных Qt, такие как QString, QDate и QTime, но не соответствуют типы QObject или некоторые из подклассов QObject (QWidget, QDialog, QTimer, и т.д.). Если Вы сделаете попытку создания QList, то компилятор сообщит, что не доступен конструктор копирования и оператор присваивания QWidget. Если Вы хотите поместить объекты этих видов в контейнер, то поместите указатели на них, например: QList.
Здесь приведен пример определения класса, удовлетворяющего требованиям, предъявляемым к присваиваемому типу данных:
class Employee < public: Employee() <>Employee(const Employee &other); Employee &operator=(const Employee &other); private: QString myName; QDate myDateOfBirth; >;
Если Вы не предоставляете конструктора копирования и оператора присваивания, C++ предоставляет реализацию по умолчанию, выполняющую копирование объекта почленно. Определение класса в вышеприведенном примере достаточно. Также, если Вы не предоставляете конструкторов, то C++ предоставляет конструктор по умолчанию, который инициализирует члены класса с помощью их конструкторов по умолчанию. Следующий тип данных может быть помещен в контейнер, хотя и не предоставляет явно конструктора и оператора присваивания:
struct Movie < QString title; QDate releaseDate; >;
Некоторые из контейнеров предъявляют дополнительные требования к типам данных, которые они могут хранить. Например, для типа Key контейнера QMap должен быть реализован operator. Такие особые требования описаны в подробных описаниях классов. В некоторых случаях, определенные функции предъявляют специальные требования; они указаны в описаниях функций. Если эти требования не выполняются, компилятор обязательно сообщит об ошибке.
Контейнеры Qt предоставляют operator>(), и, поэтому, они могут легко читать из и записывать в QDataStream. Это означает, что типы данных, помещаемых в контейнер, также должны поддерживать operator>(). Поддержку этих операторов нужно реализовать напрямую, как это сделано для структуры Movie в следующем примере:
inline QDataStream &operator <<(QDataStream &out, const Movie &movie) < out << (quint32)movie.id << movie.title << (quint32)movie.year; return out; >inline QDataStream &operator>>(QDataStream &in, Movie &movie) < quint32 id; quint32 year; in >> id >> movie.title >> year; movie.id = (int)id; movie.year = (int)year; return in; >
Описания некоторых функций классов-контейнеров ссылаются на значения по умолчанию; например, QVector автоматически инициализирует свои элементами их значениями по умолчанию, а QMap::value() возвращает значение по умолчанию, если указанный ключ не встречается в карте. Для большинства типов значений это просто означает, что они созданы с помощью конструктора по умолчанию (например, пустая строка для QString). Но для примитивных типов, подобных int и double, как и для типов указателей, язык C++ не предусматривает инициализации; в этих случаях, контейнеры Qt автоматически инициализируют значения нулем.
Классы-Итераторы
Итераторы предоставляют однородные средства доступа к элементам контейнера. Классы контейнеров Qt предоставляют два типа итераторов: итераторы в стиле Java и итераторы в стиле STL.
Итераторы в стиле Java
Итераторы в стиле Java добавлены в Qt 4.0 и очень часто используются в приложениях Qt. Они более удобны в использовании, чем итераторы в стиле STL, но они немного менее эффективны. Их API сделан по образцу классов-итераторов Java.
Для каждого класса-контейнера определено два типа итераторов в стиле Java: один из них предоставляет доступ только-для-чтения, а другой предоставляет доступ для чтения-записи.
Контейнеры Итератор только-для-чтения Итератор для чтения-записи QList, QQueue QListIterator QMutableListIterator QLinkedList QLinkedListIterator QMutableLinkedListIterator QVector, QStack QVectorIterator QMutableVectorIterator QSet QSetIterator N/A QMap, QMultiMap QMapIterator QMutableMapIterator QHash, QMultiHash QHashIterator QMutableHashIterator В этом обсуждении мы сконцентрируем наше внимание на QList и QMap. Типы итераторов для QLinkedList, QVector и QSet имеют точно такой-же интерфейс, что и итераторы QList; точно также, типы итераторов для QHash имеют тот-же интерфейс, что и итераторы QMap.
В отличие от итераторов в стиле STL (описанных ниже), итераторы в стиле Java указывают на ячейку памяти между элементами, а не на сами элементы. По этому они указывают либо на начало контейнера (перед первым элементом), либо на конец контейнера (после последнего элемента), либо между двумя элементами. На диаграмме ниже красными стрелками показаны возможные позиции итератора в списке, содержащем четыре элемента:

Вот типичный пример цикла для перебора всех элементов QList по порядку и вывода их в консоль:
QList list; list << "A" << "B" << "C" << "D"; QListIteratori(list); while (i.hasNext()) qDebug()
Данный код работает следующим образом: Перебираемый QList передается в конструктор QListIterator. В этот момент итератор позиционирован на начало первого элемента в списке (перед элементом "A"). Потом мы вызываем hasNext() для проверки, существует-ли какой-либо элемент после позиции итератора. Если это так, мы вызываем next() для перемещения к следующему элементу. Функция next() возвращает элемент, через который перепрыгнул итератор. Для QList это элементы типа QString.
В следующем примере показано, как перебрать элементы QList в обратном порядке:
QListIterator i(list); i.toBack(); while (i.hasPrevious()) qDebug()
Этот код симметричен перебору по порядку, за исключения того, что сначала мы вызываем toBack() для перемещения на позицию, после последнего элемента в списке.
Диаграмма, приведенная ниже, показывает эффект от вызовов функций итератора next() и previous():

В следующей таблице дается резюме API QListIterator:
Функция Поведение toFront() Перемещает итератор в начало списка (перед первым элементом) toBack() Перемещает итератор в конец списка (после последнего элемента) hasNext() Возвращает true, если итератор не позиционирован на конец списка next() Возвращает следующий элемент и перемещает итератор на одну позицию вперед peekNext() Возвращает следующий элемент без перемещения итератора hasPrevious() Возвращает true, если итератор не позиционирован на начало списка previous() Возвращает предыдущий элемент и перемещает итератор на одну позицию назад peekPrevious() Возвращает предыдущий элемент без перемещения итератора QListIterator не предоставляет функций для вставки и удаления элементов перебираемого списка. Для того, чтобы сделать это, нужно использовать QMutableListIterator. В следующем примере мы, с помощью QMutableListIterator, удаляем все элементы с нечетными значениями из QList:
QMutableListIterator i(list); while (i.hasNext())next() вызывается в каждой итерации цикла. Она перемещает итератор к следующему элементу списка. Функция remove() удаляет последний элемент списка, через который перепрыгнул итератор. Вызов remove() не делает итератор недействительным, и он остается пригодным для дальнейшего использования. При переборе элементов в обратном порядке, эта функция работает точно также:
QMutableListIterator i(list); i.toBack(); while (i.hasPrevious())Если Вы желаете лишь изменить значение существующего элемента, то можете использовать функцию setValue(). В нижеприведенном примере, мы заменяем значения, большие 128, на 128:
QMutableListIterator i(list); while (i.hasNext()) < if (i.next() >128) i.setValue(128); >
Точно также, как и remove(), setValue() работает с последнем элементом, который мы перескочили. Если Вы перебираете элементы по порядку, то это элемент, расположенный прямо перед итератором, если Вы перебираете элементы в обратном порядке, то это элемент, расположенный сразу за итератором.
Функция next() возвращает неконстантный указатель на элемент списка. Для простых операций нам даже не требуется setValue():
QMutableListIterator i(list); while (i.hasNext()) i.next() *= 2;
Как было сказано выше, итераторы классов QLinkedList, QVector и QSet имеют API, сходный с QList. Теперь обратимся к итератору QMapIterator, который несколько отличен, так как служит для перебора пар (ключ, значение).
Как и QListIterator, QMapIterator предоставляет toFront(), toBack(), hasNext(), next(), peekNext(), hasPrevious(), previous() и peekPrevious(). Ключ и значение могут быть получены с помощью вызова key() и value() для объекта, возвращенного next(), peekNext(), previous() или peekPrevious().
В следующем примере удаляются все пары (столица, государство), в которых название столицы оканчивается на "City":
QMap map; map.insert("Paris", "France"); map.insert("Guatemala City", "Guatemala"); map.insert("Mexico City", "Mexico"); map.insert("Moscow", "Russia"); . QMutableMapIterator i(map); while (i.hasNext())
QMapIterator также предоставляет функции key() и value(), которые работают напрямую с итератором и возвращают ключ и значение последнего элемента, который перепрыгнул итератор. Например, в следующем коде производится копирование содержимого QMap в QHash:
QMap map; QHash hash; QMapIterator i(map); while (i.hasNext())
Если Вы хотите перебрать все элементы, содержащие одно и то-же значение, то можете использовать findNext() или findPrevious(). В следующем примере будут удалены все элементы, содержащие определенное значение:
QMutableMapIterator i(map); while (i.findNext(widget)) i.remove();
Итераторы в стиле STL
Итераторы в стиле STL стали доступны, начиная с Qt 2.0. Они совместимы с родовыми алгоритмами Qt и STL и оптимизированы по скорости.
Для каждого класса-контейнера есть два типа итераторов в стиле STL: один из них предоставляет доступ только-для-чтения, а другой - доступ для чтения-записи. Итераторы только-для-чтения должны использоваться везде, где это только возможно, так как они быстрее, чем итераторы для чтения-записи.
Контейнеры Итераторы только-для-чтения Итераторы для чтения-записи QList, QQueue QList::const_iterator QList::iterator QLinkedList QLinkedList::const_iterator QLinkedList::iterator QVector, QStack QVector::const_iterator QVector::iterator QSet QSet::const_iterator N/A QMap, QMultiMap QMap::const_iterator QMap::iterator QHash, QMultiHash QHash::const_iterator QHash::iterator API итераторов в стиле STL сделан по образцу указателей в массиве. Например, оператор ++ перемещает итератор к следующему элементу, а оператор * возвращает элемент, на который позиционирован итератор. Фактически, для QVector и QStack, хранящих свои элементы в смежных ячейках памяти, тип iterator - это всего лишь typedef для T *, а тип const_iterator - всего лишь typedef для const T *.
В данном обсуждении мы сконцентрируем свое внимание на QList и QMap. Типы итераторов для QLinkedList, QVector и QSet имеют точно такой-же интерфейс, что и итераторы для QList; а типы итераторов для QHash имеют интерфейс, подобный интерфейсу итераторов для QMap.
Здесь показан типичный способ организации цикла для перебора по порядку всех элементов QList и преобразования их к нижнему регистру:
QList list; list << "A" << "B" << "C" << "D"; QList::iterator i; for (i = list.begin(); i != list.end(); ++i) *i = (*i).toLower();
В отличие от итераторов в стиле Java, итераторы в стиле STL указывают прямо на элемент. Функция контейнера begin() возвращает итератор, указывающий на первый элемент контейнера. Функция контейнера end() возвращает итератор, указывающий на воображаемый элемент, находящийся в позиции, следующей за последним элементом контейнера. end() обозначает несуществующую позицию; он никогда не должен разименовываться. Обычно, он используется, как условие выхода из цикла. Если список пуст, то begin() эквивалентно end(), поэтому цикл никогда не выполнится.
На нижеприведенной диаграмме, красными стрелками показаны возможные позиции итератора в контейнере vector, содержащем четыре элемента:

При переборе элементов в обратном порядке с помощью итераторов в стиле STL, требуется, чтобы оператор декремента использовался перед обращения к элементу. Вот требуемый цикл while:
QList list; list << "A" << "B" << "C" << "D"; QList::iterator i = list.end(); while (i != list.begin())
В этих фрагментах кода, для восстановления значения элемента (типа QString), хранящегося в некоторой позиции итератора, мы использовали унарный оператор *, а затем для него вызывали QString::toLower(). Большинство компиляторов C++ (но не все) также позволяют писать i->toLower().
Для доступа к элементам только-для-чтения, можно использовать const_iterator, constBegin() и constEnd(). Например:
QList::const_iterator i; for (i = list.constBegin(); i != list.constEnd(); ++i) qDebug()
В следующей таблице дается резюме API итераторов в стиле STL:
Выражение Действие *i Возвращает текущий элемент ++i Перемещает итератор к следующему элементу i += n Перемещает итератор вперед на n элементов --i Перемещает итератор на один элемент назад i -= n Перемещает итератор назад на n элементов i - j Возвращает количество элементов, находящихся между позицией итератора i и позицией итератора j Оба оператора ++ и -- могут использоваться как префиксы (++i, --i) и постфиксы (i++, i--). Префиксная версия изменяет итератор, и возвращает ссылку на новый элемент; постфиксная версия, перед изменением итератора, запоминает элемент, на который тот позиционирован, и возвращает на ссылку на него. В выражениях, в которых возвращаемое значение игнорируется, мы рекомендуем использовать префиксную версию (++i, --i), так как она несколько быстрее.
Значение, возвращаемое унарным оператором *, примененным к итератору неконстантного типа, может использоваться с левой стороны, от оператора присваивания.
Для QMap и QHash, оператор * возвращает компонент значения элемента. Если Вы хотите получить ключ, вызовите для итератора key(). Для симметрии, типы итераторов предоставляют также функцию value(), восстанавливающую значение. Для примера здесь показано, как можно вывести все элементы QMap в консоль:
QMap map; . QMap::const_iterator i; for (i = map.constBegin(); i != map.constEnd(); ++i) qDebug()
Благодаря неявному совместному использованию данных, использование значений контейнера весьма недорого. В API Qt содержится множество функций, возвращающих QList или QStringList со значениями (например, QSplitter::sizes()). Если Вы хотите перебрать эти значения с помощью итератора в стиле STL, то всегда должны иметь копию контейнера и перебирать ее элементы. Например:
// ПРАВИЛЬНО const QList sizes = splitter->sizes(); QList::const_iterator i; for (i = sizes.begin(); i != sizes.end(); ++i) . // НЕ ПРАВИЛЬНО QList::const_iterator i; for (i = splitter->sizes().begin(); i != splitter->sizes().end(); ++i) .
Эта проблема не должна возникать при использовании функций, возвращающих неконстантный указатель на контейнер.
Неявное совместное разделение данных имеет и другое влияние на использованиние итераторов в стиле STL: Вы не должны делать копии контейнера, если для него активны неконстантные итераторы. Итераторы в стиле Java не страдают от этого ограничения.
Конструкция foreach
Если Вы хотите перебрать все элементы контейнера по порядку, то можете использовать конструкцию Qt foreach. Данная конструкция - это дополнение Qt к языку C++, реализованное с помощью средств препроцессора.
Ее синтаксис: foreach (variable, container) statement. В следующем примере показано использование конструкции foreach для перебора всех элементов контейнера QLinkedList:
QLinkedList list; . QString str; foreach (str, list) qDebug()
Код с использованием конструкции foreach значительно короче аналогичного кода, использующего итераторы:
QLinkedList list; . QLinkedListIterator i(list); while (i.hasNext()) qDebug()
Также, как в цикле for языка C++, переменная, используемая для перебора элементов контейнера может быть определена внутри выражения foreach:
QLinkedList list; . foreach (QString str, list) qDebug()
И подобно любому циклу C++, Вы можете заключить тело цикла foreach в фигурные скобки и использовать break для прерывания цикла:
QLinkedList list; . foreach (QString str, list)При использовании с QMap и QHash, foreach предоставляет доступ к парам значений (key, value). Если Вы хотите перебрать ключи и значения, то можете использовать итераторы (это работает быстрее) или написать код, подобный следующему:
QMap map; . foreach (QString str, map.keys()) qDebug()
Для многосвязных карт:
QMultiMap map; . foreach (QString str, map.uniqueKeys()) foreach (int i, map.values(str)) qDebug()
При запуске foreach, Qt автоматически делает копию контейнера. Если Вы изменяете контейнер, который перебираете, это не будет влиять на выполнение цикла. (Если Вы не изменяли контейнер, копирование все еще имеет место, но, благодаря неявному совместному использованию данных, копирование контейнера осуществляется очень быстро.)
В дополнение к foreach, Qt также предоставляет псевдоключевое слово forever, обозначающее бесконечный цикл:
foreverЕсли Вас беспокоит засорение пространства имен, то Вы можете отключить использование этих макросов, добавив в .pro-файл следующую строку:
CONFIG += no_keywords
Другие Контейнероподобные Классы
Qt включает три класса-шаблона, которые в каком-то отношении напоминают контейнеры. Эти классы не предоставляют итераторов и не могут использоваться в конструкции foreach.
- QVarLengthArrayпредоставляет низкоуровневый массив переменной длины. Он может использоваться вместо QVector в тех местах кода, в которых особенно важна скорость выполнения.
- QCacheпредоставляет кэш для хранения объектов некоторого типа T, ассоциированных с ключами типа Key.
- QPairхранит пары элементов.
Дополнительные нешаблонные типы, дополняющие контейнерные шаблоны Qt, это - QBitArray, QByteArray, QString и QStringList.
Сложности Алгоритмизации
Сложности алгоритмизации заключаются в определении насколько быстра (или медленна) каждая из функций при большом количестве элементов в контейнере. Например, вставка элемента в QLinkedList - чрезвычайно быстрая операция назависимо от количества элементов в QLinkedList. В тоже время, вставка элемента в середину QVector, если он содержит очень много элементов, потенциально очень дорога, потому, что половину элементов придется переместить на одну позицию в памяти.
Для описание алгоритмической сложности мы используем следующую терминологию, основанную на нотации "большого O":
- Постоянное время: O(1). Мы говорим, что функция выполняется за постоянное время, если она требует для выполнения одного и того-же времени, независимо от того, сколько элементов содержится в контейнере. В качестве примера можно привести QLinkedList::insert().
- Логарифмическое время: O(log n). Функция, выполняющаяся за логарифмическое время, - это функция, время выполнения которой пропорционально логарифму от количества элементов в контейнере. В качестве примера можно привести qBinaryFind().
- Линейное время: O(n). Функция, выполняющаяся за линейное время, - это функция, время выполнения которой прямо пропорционально количеству элементов, хранящихся в контейнере. В качестве примера можно привести QVector::insert().
- Линейно-логарифмическое время: O(n log n). Функция, выполняющаяся за линейно-логарифмическое время, - это функция, время выполнения которой ассимптотически больше времени выполнения линейной функции, но меньше квадратичной функции.
- Квадратичное время: O(nІ). Функция, выполняющаяся за квадратичное время, - это функция, время выполнения которой пропорционально квадрату количества элементов, хранящихся в контейнере.
В следующей таблице приведена алгоритмическая сложность простых классов-контейнеров Qt:
Доступ по индексу Вставка в середину Добавление в начало Добавление в конец QLinkedList O(n) O(1) O(1) O(1) QList O(1) O(n) Amort. O(1) Amort. O(1) QVector O(1) O(n) O(n) Amort. O(1) В этой таблице, "Amort." указано для обозначения "усредненного поведения". Например, "Amort. O(1)" обозначает, что, если Вы вызываете данную функцию единожды, то можете получить время выполнения, равное O(n), но при многократном вызове (например, n раз), усредненная величина будет равна O(1).
В следующей таблице приведена алгоритмическая сложность ассоциативных контейнеров и наборов Qt:
Просмотр ключа Вставка Среднее значение Худший случай Среднее значение Худший случай QMap O(log n) O(log n) O(log n) O(log n) QHash Amort. O(1) O(n) Amort. O(1) O(n) QSet Amort. O(1) O(n) Amort. O(1) O(n) В QVector, QHash и QSet, время выполнения усредняется до O(log n). С помощью вызова QVector::reserve(), QHash::reserve() или QSet::reserve(), с ожидаемым количество элементов в списке, до вставка элементов, оно может быть сведено к O(1). В следующем разделе этот вопрос обсуждается более глубоко.
Стратегии Увеличения Размера
QVector, QString и QByteArray хранят свои значения в смежных ячейках памяти; QList, для обеспечения быстрого доступа по индексу, содержит массив указателей на элементы, которые он содержит (если T не является типом указателя или базовым типом с размером, равным размеру указателя, в этом случае в массив помещается само значение); QHash содержит хэш-таблицу, чей размер пропорционален количеству элементов. Во избежание перераспределения данных каждый раз при добавлении элемента в конец контейнера, эти классы обычно занимают больше памяти, чем требуется.
Рассмотрите следующий код, который строит QString от другого QString:
QString onlyLetters(const QString &in) < QString out; for (int j = 0; j < in.size(); ++j) < if (in[j].isLetter()) out += in[j]; >return out; >
Мы строим out динамически, добавляя по одному символу в конец строки. Давайте предположим, что добавляем 15000 символов в конец строки QString. Тогда следующие 18 перераспределений данных (из возможных 15000) произойдут, когда QString исчерпает место под 4, 8, 12, 16, 20, 52, 116, 244, 500, 1012, 2036, 4084, 6132, 8180, 10228, 12276, 14324, 16372 символа. В конце концов QString будет иметь возможность размещения 16372 символов Unicode, из которых 15000 будут заняты.
Чтобы приведенные значения не казались странными, приводим принцип размещения:
- QString занимает по 4 символа за один раз, пока не достигнет размера 20.
- От 20 до 4084 символов он каждый раз удваивает свой размер. Если быть более точными, то он удваивает свой размер и увеличивает его еще на 12. (Некоторые менеджеры памяти работают хуже, когда требуется точное увеличение занимаемой памяти в два раза из-за того, что они используют дополнительные байты для подсчета.)
- Начиная с размера 4084 он занимает память блоками по 2048 символов (4096 bytes). Это имеет смысл потому, что современные операционные системы не копируют полные страницы при перераспределении буфера, просто повторно назначаются физические страницы памяти, а скопированы должны быть лишь данные первой и последней страниц.
QByteArray и QList используют более или менее похожие алгоритмы размещения, что и QString.
QVector также использует этот алгоритм для типов данных, которые могут быть перемещены с помощью memcpy() (включая базовые типы C++, типы указателей и общие классы Qt), но для других типов данных, которые могут быть перемещены только с помощью вызовов конструкторов копирования и деструкторов, использует другой алгоритм. Так как стоимость перемещения в таком случае становится выше, QVector уменьшает количество перераспределений, всегда удваивая память.
QHash является польностью отличным случаем. Внутренняя хэш-таблица QHash всегда увеличивается в два раза, элементы, перераспределенные в новом ковше, вычисляются, как qHash(key) % QHash::capacity() (количество ковшей). Это замечание также относится и к QSet и к QCache.
Для большинства приложений вполне подходит алгоритм увеличения размера, предоставляемый Qt по умолчанию. Если Вам требуется больший контроль, то QVector, QHash, QSet, QString и QByteArray предоставляют три функции, позволяющие контролировать и задавать столько памяти, сколько Вам нужно для размещения элементов:
- capacity() возвращает количество элементов, которые могут быть размещены в уже занятой памяти (для QHash и QSet - это количество котлов в хэш-таблице).
- reserve(size) явно занимает память для size элементов.
- squeeze() освобождает память, которая не используется для хранения элементов.
Если Вы заранее приблизительно знаете, сколько элементов Вы разместите в контейнере, то сперва можете вызвать reserve(), затем, заполнить контейнер, а затем вызвать squeeze() для освобождения дополнительной занятой памяти.
Стратегии отбора товаров на складе: какие бывают и как выбрать

Отлаженная складская логистика — важное конкурентное преимущество для бизнеса, который занимается производством или реализацией продукции. От того, как товары движутся через склад, как организован учет, какой метод учета используется, зависит эффективность складских процессов. Цифровизация логистики привела к изменениям в работе складов и логистических центров. Сейчас для учета товаров используют системы управления складом (Warehouse Management System, сокращенно WMS), которые позволяют вести точный учет товаров, контролировать сроки хранения, содержат информацию об их расположении, оптимизируют использование складского пространства. Больше информации о том, что такое WMS, вы можете узнать в другой нашей статье.
Однако упорядоченной структуры на складе недостаточно. Чтобы эффективно работать с системами управления складом, необходимо определиться с принципами обработки товара. Для этого в складской логистике используют 3 стратегии — FIFO, FEFO и LIFO. В этой статье вы узнаете, что это за методы отбора, для каких групп товаров используют каждый из них, и как выбрать подходящий для конкретного склада.
Принципы FIFO и FEFO в складской логистике
Самые распространенные подходы к отбору товара — FIFO и FEFO. Это акронимы от англоязычных понятий. FIFO расшифровывается как “First In, First Out” (“первым пришел — первым ушел”), а FEFO — “First Expire, First Out” (“первый истекает — первый выходит”).

Принцип FIFO на складе подразумевает отгрузку в первую очередь товара, который поступил на склад раньше всего, затем грузов, которые поступили на хранение вторыми и т.д. Например, на склад поступили две партии консерв. Одна — в первых числах месяца, вторая — в последних. Согласно принципу FIFO, когда поступит заказ, первыми будут отгружать консервы, которые поступили на склад в первых числах месяца.
При использовании правила FEFO, фактором при выборе приоритетности отгрузки является не время прибытия груза на склад, а срок годности. Т.е. товар, чей срок годности истекает первым, отгружают первым. Например, на склад поступили две партии товаров. Первая — поступила в первых числах месяца со сроком годности 30 дней. Вторая — в последних числах месяца со сроком годности 15 дней. Когда на складе используют подход FEFO, первой отгрузят партию со сроком годности 15 дней, несмотря на то, что она поступила на склад второй.
Эти методы похожи по своему принципу приоритетности, ведь в большинстве случаев товар, который только поступил на склад, имеет больший срок годности, чем тот, который находится там уже длительное время.

Эти подходы используют для тех групп товаров, которые нельзя долго хранить. Это продукты питания, лекарства, товары повседневного спроса и другая продукция, которую нужно реализовать в короткие сроки. В эту категорию также относят сезонные товары, fashion-сегмент и электронику, поскольку чем дольше они лежат на складе, тем выше вероятность того, что они потеряют свою актуальность и не будут реализованы.
Главные преимущества стратегий ФЕФО и ФИФО
- Сокращение устаревших запасов. Если продукт устаревает и теряет свою привлекательность для потребителей, бизнес может понести серьезные убытки. Подходы FEFO и FIFO снижают вероятность того, что товар не будет продан до истечения срока годности и потери актуальности на рынке.
- Снижение затрат на складскую логистику. Если товар не будет долго находиться на складе, стоимость его хранения будет соответственно ниже.
- Повышение уровня удовлетворенности клиентов. Поскольку оборачиваемость запасов на складе высокая, клиенты с меньшей вероятностью получат просроченную или некачественную продукцию. Это повышает уровень доверия потребителей.
- Снижение влияния инфляции на бизнес. Инфляция — постоянный процесс. Стоимость производства растет, а отправка запасов, которые были произведены раньше и чья себестоимость ниже, способствует повышению прибыльности бизнеса.
Недостатки FIFO и FEFO: что нужно учесть
- Необходим точный учет товаров. Внедрение FIFO и FEFO означает целенаправленное отслеживание складских запасов. Нужна грамотная система отслеживания всех товаров которые поступают и отгружаются со склада.
- Необходимо правильно организовать складское пространство. Чтобы эффективно реализовать стратегии FIFO и FEFO, склад должен быть тщательно организован. Продукцию, которая давно поступила на склад, необходимо размещать рядом с зоной отгрузки. Для этого нужно иметь достаточно складских площадей и специализированное погрузочно-разгрузочное и стеллажное оборудование.
- Сложно масштабировать бизнес. Когда бизнес начинает расширяться, ему нужно больше складских помещений. Вносить большое количество информации о товарах и контролировать их сроки годности на разных складах, может быть сложно.
- Более высокие налоги. Поскольку использование FIFO и FEFO минимизирует инфляцию, то это влияет на рост прибыли, и соответственно на увеличение налогов.
Принцип LIFO: что это и для каких товаров он подходит
Подход LIFO (от англ. Last In, First Out — “последним пришёл — первым вышел”) противоположен принципам FIFO и FEFO. В этом случае товары, которые поступили на склад последними, должны быть обработаны и отгружены в первую очередь. Принцип метода схож с принципом стопки тарелок, когда можно взять тарелку из середины стопки, только сняв те, которые лежат сверху.

Такой подход широко используется для хрупкой продукции, которую нежелательно лишний раз перемещать по складу, и для однородных товаров, например, сыпучих стройматериалов. У них длительный срок хранения, и они не подвержены устареванию. Это позволяет рационально использовать все пространство склада (особенно, если оно ограничено) и экономит время на погрузочно-разгрузочных работах.
В чем выгода использования метода ЛИФО
- Оптимизация складского пространства. При подходе LIFO, новая партия товаров размещается поверх уже имеющихся запасов на складе. Таким образом, нужно меньше складского пространства, поскольку ротация запасов не требуется.
- Сравнение затрат и доходов. LIFO позволяет легко сопоставить расходы и доходы за последнее время. Когда растут расходы на производство и, следовательно, увеличивается себестоимость товара. Так, у бизнеса есть четкое понимание о своих доходах.
- Сокращение налогов. Благодаря более точной оценке актуальной прибыли, ниже вероятность того, что компания будет переоценивать свои доходы и недооценивать себестоимость товаров. Поэтому сумма доходов будет соответствовать действительности, и налоги на прибыль будут меньше.
- Меньше списаний. Если рыночная стоимость продукции колеблется, то при падении цен выгоднее продавать товары, которые были произведены ранее. Таким образом, убытки сокращаются.
Недостатки принципа LIFO
- Складские запасы остаются. Есть вероятность, что устаревшие запасы никогда не будут реализованы. Поэтому этот метод рекомендуется только для товаров с длительным сроком годности.
- Снижение доходов в период инфляции. Во время инфляции, компания, которая использует метод LIFO, уменьшает заявленную в отчетах прибыль. Это может снизить привлекательность компании в глазах инвесторов.
- Занижение реального количества запасов. Количество запасов в отчетах обычно занижается, поскольку оно основано на более старых расходах. Из-за этого состояние оборотного капитала компании может казаться хуже, чем оно есть на самом деле.
- Сложности при выходе на международный рынок. Хотя сейчас LIFO — распространенная практика, не во всех странах она является разрешенной. Поэтому компаниям, которые стремятся к масштабированию в международных масштабах, стоит использовать метод ФИФО.
Как организовать работу склада с помощью разных стратегий
Прежде всего необходимо правильно разместить товар на складе. FIFO предполагает размещения грузов, которые поступили на склад раньше остальных, рядом с зоной отгрузки. Используя метод FEFO, компании ставят вперед товары с истекающим сроком годности, а грузы с длительным сроком хранения размещают в глубине склада. Все это требует больших складских пространств и грамотной их организации. При LIFO стратегия размещения продукции, которая только что прибыла, значительно проще — ее помещают поверх старых запасов. Таким образом, зоны погрузки и отгрузки находятся рядом.

Чтобы складские процессы осуществлялись эффективно, бизнесу необходимо приобрести специальное оборудование. Для методов FEFO и FIFO используют стеллажи гравитационного типа. Под действием силы тяжести грузы перемещают от места разгрузки до места выгрузки. Это позволяет оптимизировать использование грузоподъемной техники. При подходе LIFO подходят шаттловые стеллажи, которые позволяют максимально использовать пространство склада и необходимы при работе с большими объемами товаров.
Для LIFO подходят набивные стеллажи, когда паллетные грузы размещают в глубину конструкции. Однако, стоит учитывать, что при отгрузке продукция может повредиться, поэтому это решение подходит не для всех групп товаров. Push Back конструкции, которые совмещают в себе принципы въездных и гравитационных стеллажей, как правило, используются при подходе LIFO, но они, как и набивное оборудование, могут применяться и при использовании стратегий FIFO и FEFO.
Сравнительная таблица методов FEFO, FIFO и LIFO в складской логистике
В предыдущих разделах мы описали как работают FEFO, FIFO и LIFO на практике, какие плюсы и минусы каждого принципа и для каких товаров лучше выбрать тот или иной метод. Далее вы можете ознакомиться со сравнительной таблицей, где представлена расшифровка и суть понятий, а также в каких случаях их стоит использовать.
Название FEFO FIFO LIFO Расшифровка названия First Expire, First Out — “первый истекает — первый выходит” First In, First Out — “первым пришел — первым ушел” Last In, First Out — “последним пришёл — первым ушёл” Суть метода Сначала производят отгрузку товаров, чей срок годности подходит к концу. В приоритете отгрузка товара, который поступил на склад первым. В первую очередь осуществляется отгрузка того товара, который поступил на склад последним. Когда используется Метод подходит для обработки скоропортящихся товаров разных партий. Применяется в случаях, когда товар подвержен устареванию, сезонности, имеет ограниченный срок хранения. Принцип используется при работе с однородным товаром, чаще всего речь идет о больших партиях грузов. Для какой товарной группы подходит Продукты питания, медикаменты, косметика, любая продукция с ограниченным сроком годности. Та же, что и для FEFO, + электроника, сезонные товары, fashion-сегмент; любая продукция, которая быстро устаревает/теряет актуальность. Стройматериалы, химикаты, сыпучие материалы, сырье; продукция с неограниченным или длительным сроком хранения. Если ваша компания еще не определилась, какой подход к отбору товара на складе ей подходит, ей стоит передать эти задачи на аутсорсинг. Логистические провайдеры профессионально решают вопросы, связанные с хранением и обработкой грузов. WareTeka сотрудничает с 30+ поставщиками логистических услуг по всей Украине. Мы подберем для вашей компании провайдера, который работает с нужным товарным сегментом, имеет налаженную логистику и организует эффективную обработку грузов с учетом их специфики, товарного соседства и задач бизнеса.
Часто задаваемые вопросы по стратегиям отбора товаров
Какая из стратегий дает более эффективную оценку складских запасов?
FIFO и FEFO показывают более точную оценку своих запасов, поскольку при таких методах реализуют в первую очередь товары, которые были приобретены раньше. Запасы, которые только поступили на склад, закуплены по актуальным ценам на рынке. Это дает четкое понимание о прибыли компании. При подходе LIFO, это сложнее сделать из-за того, что продукция, которая была закуплена давно, была приобретена по цене ниже актуальной на рынке.
Почему метод LIFO не подходит для компаний, которые выходят на международный уровень?
Метод LIFO запрещен Международными стандартами финансовой отчетности (МСФО), поскольку он искажает данные о реальном состоянии доходности компании. Поэтому страны, которые приняли стандарты МСФО, могут отказаться от сотрудничества с компаниями, которые используют этот подход.
Какие методы ротации товаров на складе еще используют?
Кроме вышеперечисленных методов, используют также подходы BBD (“Best Before Day” — “первым истекает рекомендуемый срок — первым выходит”) и FPFO (“First Product, First Out” — “первый произведен — первый выходит”). Как и предыдущие стратегии, использование подобных решений подходит для работы с определенными группами товаров.
Выводы: FEFO, FIFO или LIFO — какую стратегию отбора товара выбрать
Не существует универсального решения для управления складом. Однако есть факторы, на которые нужно обратить внимание при выборе стратегии. Один из самых важных — тип товара. Для товаров, у которых ограниченный срок годности и которые нужно быстро реализовать, подойдут стратегии FEFO и FIFO. Иначе порчи товаров и убытков не избежать. Для продукции, у которой длительный срок хранения, как правило, оптимален подход LIFO. Кроме того, нужно проанализировать потребительский спрос и ситуацию на рынке. Методы FEFO и FIFO используют при обработке товаров, стоимость и спрос на которые относительно стабильны, а LIFO — для продукции, цена которых изменчива и новые единицы которых раскупают быстрее.

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