Сообщество - Лига программистов
Добавить пост

Лига программистов

1 465 постов 11 288 подписчиков

Популярные теги в сообществе:

AJAZZ AKP153 Stream Controller

Добрый день. Приобрел устройство AJAZZ AKP153. Это аналог Stream deck. Устройство работает на плагинах, плагины написаны на языках Html, js, Json. Так вот, может кто-то подскажет, хотелось бы написать плагин, для того, что бы он выводил информацию о песне и исполнителе из приложения Яндекс музыка.

Алгоритмы: Открытие тайн кода

Здравствуйте, мои маленькие любители алгоритмов. Я покажу вам основные принципы, которые демонстрируют необходимость алгоритмов, обучу вас решению элементарных задач и введу вас в понятие O-нотации. Статья предназначена для тех, кто только начинает свой путь в программировании, так что профессионалам она может показаться менее интересной.

Алгоритмы: Открытие тайн кода Программирование, IT, Технологии, Обучение, Программист, Алгоритм, Гифка, Длиннопост

Что такое алгоритм?

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

Пример задачи бинарного поиска

Начнем с примера задачи бинарного поиска. Допустим, вам нужно отыскать фамилию в телефонном справочнике, который начинается на букву “К”. Вместо того чтобы листать с начала, вы быстрее найдёте нужную страницу, открыв справочник посередине, так как “К” вероятно будет ближе к центру. Такой же подход удобен при поиске слова на букву “О” в словаре - начинать стоит с середины. Бинарный поиск — это универсальный алгоритм, применимый для решения многих задач поиска. Он работает с предварительно отсортированным массивом элементов. Если искомый элемент содержится в массиве, бинарный поиск указывает его позицию. В случае отсутствия элемента алгоритм возвращает значение null. В дальнейшем я объясню, почему важна сортировка массива перед началом поиска.

Как работает метод бинарного поиска

Давайте рассмотрим, как работает метод бинарного поиска на примере игры. Представьте, что я загадал число от 1 до 100, и ваша задача — угадать его, используя минимальное количество попыток. После каждой вашей догадки я буду отвечать: “слишком мало”, “слишком много” или “верно”. Если начать угадывать последовательно, начиная с 1, это будет неэффективно. Например, если я загадал число 99, вам потребуется 99 попыток, чтобы добраться до него. Теперь представим более эффективный метод — бинарный поиск. Начнем с середины диапазона, то есть с 50. Если я скажу, что это “слишком мало”, вы тут же исключите все числа от 1 до 50. Затем попробуйте число 75. Если я скажу “слишком много”, вы исключите числа от 76 до 100. Таким образом, каждый раз вы сокращаете количество возможных вариантов вдвое. Следующая попытка будет 63, что находится посередине между 50 и 75.

Алгоритмы: Открытие тайн кода Программирование, IT, Технологии, Обучение, Программист, Алгоритм, Гифка, Длиннопост

Бинарный поиск эффективен, потому что с каждой попыткой вы исключаете половину оставшихся чисел. Независимо от того, какое число я загадал, вы сможете угадать его за 7 или меньше попыток. Это показывает мощь алгоритмов и как они могут упростить решение задач. В среднем, бинарный поиск в списке из ( n ) элементов находит элемент за (log2n) шагов, в то время как линейный поиск потребует в среднем ( n ) шагов. Это делает бинарный поиск значительно быстрее, особенно для больших списков.

Реализация бинарного поиска на псевдокоде

Алгоритмы: Открытие тайн кода Программирование, IT, Технологии, Обучение, Программист, Алгоритм, Гифка, Длиннопост

Объяснение

  • Устанавливаем начальные границы поиска: Начало и Конец.

  • В цикле Пока проверяем, что начальная граница не превысила конечную.

  • Находим индекс Середина как среднее между Начало и Конец.

  • Сравниваем элемент в середине с искомым элементом:

    • Если они равны, возвращаем индекс Середина.

    • Если элемент в середине меньше искомого, сдвигаем начальную границу за середину.

    • Если элемент в середине больше искомого, сдвигаем конечную границу перед середину.

  • Если элемент не найден, возвращаем -1.

Реализация алгоритма бинарного поиска на c++

Алгоритмы: Открытие тайн кода Программирование, IT, Технологии, Обучение, Программист, Алгоритм, Гифка, Длиннопост

Анализ эффективности алгоритмов

Когда мы анализируем новый алгоритм, важно обсудить его эффективность. Обычно предпочтительнее использовать алгоритм, который оптимизирован по времени или памяти.

Бинарный поиск

Давайте рассмотрим бинарный поиск. Какое преимущество он предоставляет с точки зрения времени? В классическом подходе мы проверяем каждый элемент последовательно. Если у нас есть список из 100 элементов, нам может потребоваться до 100 проверок. В случае списка из 4 миллиардов элементов, число попыток может достигнуть 4 миллиардов. Время выполнения в таком случае растет пропорционально размеру списка, что является примером линейного времени выполнения.

С бинарным поиском ситуация иная. В списке из 100 элементов потребуется всего лишь 7 проверок. А для списка из 4 миллиардов элементов — не более 32 проверок. Довольно впечатляюще, не правда ли? Бинарный поиск работает за логарифмическое время, что делает его значительно более эффективным.

“Большое О”

“Большое О” - это специальная нотация, которая описывает скорость выполнения алгоритма. Это полезно знать, потому что время от времени вам может потребоваться использовать алгоритмы, разработанные другими людьми, и важно понимать, насколько быстро или медленно они работают.

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

Это один из примеров того, как время выполнения двух алгоритмов увеличивается с разной скоростью. Анна пытается выбрать между сортировкой пузырьком и быстрой сортировкой. Ее алгоритм должен работать быстро и правильно. С одной стороны, быстрая сортировка работает быстрее. У Анны есть всего 10 секунд, чтобы отсортировать данные; если она не успеет это сделать, то пользователь может уйти. С другой стороны, сортировка пузырьком проще в написании и вероятность ошибок в нем ниже. Конечно, Анна не хочет допустить ошибку в коде сортировки данных. И тогда, для большей уверенности, Анна решает измерить время выполнения обоих алгоритмов для списка из 100 элементов.

Предположим, проверка одного элемента занимает 1 миллисекунду (мс). При сортировке пузырьком Анне придется проверить 100 элементов, поэтому сортировка займет 100 мс. С другой стороны, при быстрой сортировке достаточно проверить всего 7 элементов (log⁡2100log2100 равно примерно 7), и сортировка займет 7 мс. Но реальный список может содержать более миллиарда элементов. Сколько времени в таком случае потребуется для выполнения сортировки пузырьком? А при быстрой сортировке?

Анна проводит быструю сортировку с 1 миллиардом элементов, и на это уходит 30 мс (log⁡21,000,000,000log21,000,000,000 равно примерно 30). “32 мс! - думает Анна. - Быстрая сортировка в 15 раз быстрее сортировки пузырьком, потому что сортировка пузырьком для 100 элементов заняла 100 мс, а быстрая сортировка заняла 7 мс. Значит, сортировка пузырьком займет 30 х 15 = 450 мс, верно? Гораздо меньше отведенных 10 секунд”. И Анна выбирает сортировку пузырьком. Верен ли ее выбор?

Нет, Анна ошибается. Глубоко ошибается. Время выполнения для сортировки пузырьком с 1 миллиардом элементов составит 1 миллиард миллисекунд, а это 11 дней! Проблема в том, что время выполнения для быстрой сортировки и сортировки пузырьком увеличивается с разной скоростью.

“Большое О” описывает, насколько быстро работает алгоритм. Предположим, имеется список размера n. Сортировка пузырьком должна проверить каждый элемент, поэтому ей придется выполнить n операций. Время выполнения “Большое О” имеет вид O(n).

А теперь другой пример. Для проверки списка размером n быстрой сортировке потребуется log⁡logn операций. Как будет выглядеть “Большое О”? O(log⁡logn).

В общем случае “Большое О” выглядет так: O(f(n)), где f(n) - это количество операций, которые придется выполнить алгоритму. Оно называется “Большое О”, потому что перед количеством операций ставится символ “О” (а большое - потому что в верхнем регистре).

Примеры “Большого О”

Вот пять типичных примеров “О-большого”, которые часто встречаются в алгоритмах и структурах данных. Они представлены в порядке от самого быстрого к самому медленному:

  • O(log⁡logn), или логарифмическое время. Это время, которое обычно требуется для выполнения бинарного поиска.

  • O(n), или линейное время. Это время, которое обычно требуется для выполнения простого поиска.

  • O(n log⁡logn). Это время, которое обычно требуется для выполнения эффективных алгоритмов сортировки, таких как быстрая сортировка.

  • O(2n2). Это время, которое обычно требуется для выполнения медленных алгоритмов сортировки, таких как сортировка выбором.

  • O(n!). Это время, которое обычно требуется для выполнения очень медленных алгоритмов, таких как решение задачи о коммивояжере.

Эффективность алгоритмов определяется не по времени выполнения в секундах

Алгоритмы с временем выполнения O(log n) работают быстрее, чем алгоритмы с временем выполнения O(n). И чем больше размер списка, по которому производится поиск, тем заметнее становится эта разница.

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

Это краткое введение — лишь небольшая часть огромного мира алгоритмов. Надеемся, что оно вдохновит вас на дальнейшее изучение различных алгоритмических подходов и алгоритмов, которые мы рассмотрели.

Реализация алгоритмов

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

Рекомендации по чтению

Вот несколько книг, которые я могу порекомендовать для изучения алгоритмов:

  • “Алгоритмы: построение и анализ” - Томас Х. Кормен, Чарльз Э. Лейзерсон, Рональд Л. Ривест и Клиффорд Штайн. Это классический учебник, который подробно описывает большинство важных алгоритмов.

  • “Алгоритмы на Python” - Майкл Т. Гудрич, Роберто Тамассиа и Майкл Голдвассер. Эта книга объясняет алгоритмы на языке Python, что может быть полезно для тех, кто хочет изучать алгоритмы на конкретном языке программирования.

  • “Структуры данных и алгоритмы в Java” - Майкл Т. Гудрич и Роберто Тамассиа. Это еще одна отличная книга для изучения алгоритмов, особенно если вы предпочитаете Java.

  • “Алгоритмы + структуры данных = программы” - Никлаус Вирт. Это классическая книга, которая объясняет, как алгоритмы и структуры данных работают вместе, чтобы создать эффективные программы.

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

Показать полностью 3

Есть ли какие-нибудь книги, сайт, видеокурс и т.д., с помощью которых можно посмотреть на IT «сверху»?

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

Появилось желание разобраться в том, как работает IT сфера в общем.

К примеру, в таких вопросах, как:

Какие основные направления в ней есть (и, хотя бы поверхностно, покопаться в каждом из них)

На что они подразделяются

Как они связанны между собой

Почему одни направления развиваются, другие же приходят в упадок

Как IT рынок зависит от «реального сектора экономики» и т.д.

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

Куда чаще это видео в стиле «топ 10 способов стать магом после 30-ти» и «почему искусственный интеллект заменит (или не заменит) гребцов через 10 лет».

Платформы для онлайн-обучения: что вы о них думаете? Поделитесь мнением!

Онлайн-курсов становится все больше, и нам интересно собрать статистику. Пожалуйста, пройдите небольшой опрос и поделитесь своим мнением!

Алгоритмы поиска

В программировании одной из наиболее часто встречающихся задач является поиск. При решении таких задач мы исходим из предположения, что группа данных, в которой необходимо найти заданный элемент, является фиксированной.
Пример: Пусть задан массив из n элементов array[0...n-1]. Обычно items описывает запись с некоторым полем, выполняющим роль ключа. Задача заключается в поиске элемента, ключ которого равен заданному аргументу поиска x (a[i], key = x). Полученный в результате i, удовлетворяющий условию a[i] = key = x, обеспечивает доступ к другим полям обнаруженного элемента. Так как нас интересует в первую очередь сам процесс поиска, а не обнаруженные данные, то мы будем считать, что тип item включает только ключ (item = key).
1. Линейный поиск
Линейный поиск заключается в простом, последовательном просмотре массива с увеличением шаг за шагом той его части, где желаемого элемента не обнаружено. Условием окончания поиска является либо нахождение элемента, либо просмотр всего массива без обнаружения совпадений. Этот метод просто проверяет каждый элемент массива по очереди, пока не найдет искомый элемент или не просмотрит весь массив.

Линейный поиск
int i = 0;
const int n = 100;
int arr[n] = {...};
int x;
cin >> x;
while((i < n) && (arr[i] != x)) {
i += 1;
}
2. Линейный поиск с барьером
При линейном поиске на каждом шаге требуется вычислять логические выражения и увеличивать индекс. Чтобы увеличить скорость поиска, можно упростить само логическое выражение. Это можно сделать, если мы гарантируем, что совпадение всегда произойдет. Для этого в конец массива достаточно поместить дополнительный элемент x. Такой вспомогательный элемент называется барьером. Он охраняет нас от перехода за пределы массива. Этот метод упрощает линейный поиск, добавляя искомый элемент в конец массива. Это гарантирует, что поиск всегда будет успешным, и упрощает условие окончания поиска.
const int n = 100;
int arr[n] = {0...n};
int x;
cin >> x;
arr[n] = x;
int i = 0;
while (arr[i] != x) i += 1;
3. Бинарный поиск
Для рассмотрения алгоритма мы будем считать, что массив будет заранее упорядочен, то есть удовлетворяет условию 1 <= k < N; a[k-1] <= a[k]. Основная идея алгоритма выбрать случайно некоторый элемент, предположим a[m] и сравнить его с аргументом поиска x. Если он равен x, то поиск заканчивается. Если меньше x, то мы заключаем, что все элементы с индексами <= m можно исключить из дальнейшего поиска. Это соображение приводит нас к алгоритму, который называется поиск делением пополам. Этот метод использует преимущество отсортированного массива, чтобы ускорить поиск. Он выбирает элемент в середине массива и сравнивает его с искомым. Если элемент в середине меньше искомого, то поиск продолжается в правой половине массива. Если элемент в середине больше искомого, то поиск продолжается в левой половине массива. Этот процесс повторяется, пока не будет найден искомый элемент или пока не останется непроверенных элементов.
int n = 100, left = 0, right = n - 1;
int arr[n] = {0...n};
int x;
bool found = false;

cout << "Введите число для поиска: ";
cin >> x;

while(left <= right && !found) {
int m = (left+right)/2;
if(arr[m] == x) {
found = true;
cout << "Число найдено на позиции: " << m << endl;
}
else if(arr[m] < x) left = m + 1;
else right = m - 1;
}

if(!found) {
cout << "Число не найдено." << endl;
}

Бинарный поиск
Эффективность алгоритма можно несколько улучшить, если поменять местами заголовки условных операторов. Проверку на равенство можно выполнить во вторую очередь, так как она встречается лишь единожды и приводит к окончанию работы.
Поиск в таблице
Поиск в массиве = поиск в таблице. Особенно если ключ сам является составным объектом, таким как массив чисел или массив символов. Часто встречается поиск символов, когда массив символов называют строками или словами. Строковый тип определяется так, string = char arr[0...n-1]. Для установки факта совпадения, мы должны убедиться, что все символы сравниваемых строк соответственно равны один к другому, поэтому сравнение составных операндов сводится к поиску их на неравенство. Если нет не равных частей, то это равенство. Чаще всего используется 2 представления о размерах строк. 1) Размер указывается неявно, благодаря добавлению кольцевого символа, который больше нигде не употребляется. Обычно используется непечатаемый символ со значением 0C ('/0') (минимальный символ из всех символов). 2) Размер явно хранится в качестве первого элемента массива, то есть строка s имеет вид s = s0, s1, ..., s(n-1). Эти символы являются фактическими в строке, а s0 = char(n) (определяет размер строки). Этот метод аналогичен поиску в массиве, но применяется к строкам. Здесь мы ищем подстроку в строке, сравнивая каждый символ подстроки с соответствующим символом строки.
Задача: упорядоченная таблица T (в алфавитном порядке); Аргумент поиска = x (string); T = string arr[0...n-1]. N достаточно велико.
int n = ...; // Замените на конкретное значение
string arr[n] = {...}; // Замените на конкретные значения
string x;
int l = 0, r = n;

cout << "Введите строку для поиска: ";
cin >> x;

while(l < r) {
int m = (l + r) / 2;
if(arr[m] == x) {
cout << "Строка найдена на позиции: " << m << endl;
break;
}
else if(arr[m] < x) l = m + 1;
else r = m;
}

if(l == r) {
cout << "Строка не найдена." << endl;
}

Прямой поиск строки
Пусть задан массив s из n элементов. И задан массив p из m элементов. 0 < m <= n. s: array[0...n-1] of item, p: array[0...n-1] of item. Поиск строки обнаруживает первое вхождение p в s. Обычно item это символы, то есть их можно считать некоторым текстом, а p является образом или словом. Мы хотим найти первое вхождение в слово. Рассмотрим прямолинейный поиск алгоритма. Поиск можно представить, как повторяющие сравнения. Этот метод ищет первое вхождение подстроки в строке, сравнивая каждый символ подстроки с соответствующим символом строки.
string P = "..."; // Замените на искомую строку
string T = "..."; // Замените на текст, в котором ищем
int M = P.size();
int N = T.size();
bool found = false;
for(int i = 0; i <= N - M && !found; i++) {
int j = 0;
while(j < M && T[i + j] == P[j]) {
j++;
}
if(j == M) {
found = true;
cout << "Строка найдена на позиции: " << i << endl;
}
}
if(!found) {
cout << "Строка не найдена." << endl;
}

Алгоритмы поиска Алгоритм, IT, Программист, C++, Гифка, Длиннопост
Алгоритмы поиска Алгоритм, IT, Программист, C++, Гифка, Длиннопост
Показать полностью 2

Используем рекурсию для решения задач на деревья. Ищем максимальную глубину дерева

В прошлой части мы рассмотрели разные подходы рекурсивного обхода дерева. Давайте воспользуемся некоторыми из них для решения довольно известных задач.

Находим максимальную глубину дерева.

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

Используем рекурсию для решения задач на деревья. Ищем максимальную глубину дерева IT, Программист, Telegram (ссылка), Telegram, Java, Длиннопост

Высота данного дерева - пять

Довольно очевидно что самый длинный узел в данном дереве - M и он является пятым по счету если головной является первым.

Как решать данную задачу используя рекурсию.

Если сильно упрощать то нам нужно сделать 2 действия:

  • Обойти все узлы

  • Каким то образом "сохранять" состояния каждый раз когда мы обходим узлы

Но как же сохранять состояния о той глубине на которой мы побывали? Тут есть как минимум два варианта:

  • Использовать возвращемое значение самой рекурсивной функции и "возвращать" её на уровень выше.

  • Иметь какой то объект в котором мы будем сохранять состояния находясь внутри рекурсии

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

  • Рекурсивная функция должна передавать значение сама себе "наверх"

  • Определить какое именно значение должно перебрасываться.

Логика передаваемого "наверх" значения.

  • Самые нижние уровни (те что указывают на null) должны возвращать 0 тк они не включены в расчет глубины данного подграфа

  • Нижний уровень который с листьями имеет лишь null предков должен вернуть 1 тк он является первым уровнем

  • Узел выше чем 1й (те не лист) должен выбирать максимальный уровень из двух его наследников и добавлять 1 тк находится на уровень выше из наибольшего из них.

После данных рассуждений у нас вырисовывается вот такая картина:

Используем рекурсию для решения задач на деревья. Ищем максимальную глубину дерева IT, Программист, Telegram (ссылка), Telegram, Java, Длиннопост

null уровни 0, листья 1 и все остальные узлы - выбирает наибольшее из наследников и добавляют 1.

К чему привели наши рассуждения?

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

И так первая версия кода:

Используем рекурсию для решения задач на деревья. Ищем максимальную глубину дерева IT, Программист, Telegram (ссылка), Telegram, Java, Длиннопост

Версия рабочая но слишком многословная - хотя для собеседования вполне подойдет.

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

Этот код можно было бы улучшить удалив случай когда мы находимся в самом низу - дело в том что если условие истино то возвращаемое значение maxDepth + 1 будет также равно 1.

Спасибо за внимание, всем кому интересна промышленная разработка приглашаю в мой канал.

Показать полностью 2

Типы данных

Тип данных - это набор значений и операций, которые можно проводить над этими значениями.

Концепция типа данных:

  1. Тип данных определяет, какие значения может принимать переменная или выражение.

  2. Мы можем узнать тип данных, просто посмотрев на его описание, без вычислений.

  3. Каждая операция или функция требует аргументов определенного типа.

Простейшие типы данных

Простейшие типы данных - это базовые типы, которые определяются через перечисление значений.

Например, если у нас есть новый тип NAME, мы можем определить его значения как Value1, Value2, и так далее. Это выглядит примерно так: TYPE NAME = (Value1, Value2, ..., ValueN). Это значит, что переменная этого типа может принимать любое из этих значений.

Простейшие и составные стандартные типы данных

Стандартным простым типом данных называют типы данных, которые встроены на большинстве вычислительных машин (целые числа, вещественные числа, логические значения и множество символов).

Целые числа (integer): Это числа без десятичных знаков. Диапазон этих чисел зависит от количества бит, выделенных под их хранение. Если используется n бит, то диапазон будет от −2^(n−1) до 2^(n−1)−1. Операции над данными этого типа точны и следуют арифметическим правилам. Если результат выходит за допустимые пределы, происходит переполнение и вычисления останавливаются.

Вещественные числа (Real): Это числа с десятичной частью. Они используются для представления дробных значений, но при этом могут иметь ошибки округления.

Логический тип (boolean): Имеет только два значения - True (истина) и False (ложь). Используется для логических операций.

Символьный тип (char): Представляет собой одиночные символы. Стандарт ASCII был принят для кодировки этих символов, но сейчас часто используются utf-8, utf-16, utf-64.

Ограниченные типы данных: Это типы данных с определенным диапазоном значений. Например, TYPE T = [MIN, MAX].

Массивы: Это структура данных, которая содержит элементы одного типа. Каждый элемент можно получить по его индексу. Например, TYPE T = ARRAY[N] OF REAL.

Записи (RECORD): Это составные типы данных, которые могут содержать элементы различных типов.

Множества (SET): Это тип данных, который может содержать уникальные элементы определенного типа. Например, TYPE T = SET OF T0.

Последовательности (sequence): Это тип данных, который содержит упорядоченный набор элементов.

Строки (string): Это последовательности символов. Например, Type T = String(T0).

Показать полностью

Какой дизайн лучше?

Когда то я писал что-то вроде web-crm для кастингового агентства. Минимальный набор функционала. По сути просто покажи заявки/отсортируй их.

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

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

Какой дизайн лучше? IT, Программирование, React, CRM

Старая версия сайта

В старой версии не было ничего особенного. Бесконченая лента с подгрузкой старых заявок и возможность отправить заявку в выполненные. Поиск

Какой дизайн лучше? IT, Программирование, React, CRM

Новая версия в процессе разработки. Скрыл детали, чтобы не замазывать

В новой версии я посчитал важным сделать карточки намного меньше (чтобы их больше помещалось на одну страницу). Также партнеры попросили добавить возможность оставлять заметки\комментарии к заявкам (это и есть первопричина по которой я решил взяться за переделку приложения)

Основная идея такова:
Показать только самую важную информацию а всё остальное засунуть в саму карточку и показывать при клике.


p.s если у вас есть идеи, что можно добавить\улучшить как по дизайну, так и по функционалу - буду очень рад почитать

Показать полностью 1
Отличная работа, все прочитано!