diff --git a/README.md b/README.md
index 58ccc3e7d..6b171db4e 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,8 @@
English
|
日本語
+ |
+ Русский
## 关于本书
diff --git a/en/README.md b/en/README.md
index db534581f..1d444db02 100644
--- a/en/README.md
+++ b/en/README.md
@@ -45,6 +45,8 @@
English
|
日本語
+ |
+ Русский
## The book
diff --git a/ja/README.md b/ja/README.md
index 54f556580..733cc7763 100644
--- a/ja/README.md
+++ b/ja/README.md
@@ -43,6 +43,8 @@
English
|
日本語
+ |
+ Русский
## この本について
diff --git a/mkdocs.yml b/mkdocs.yml
index ccfd7023f..b63dba521 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -80,6 +80,9 @@ extra:
- name: 日本語
link: /ja/
lang: ja
+ - name: Русский
+ link: /ru/
+ lang: ru
social:
- icon: fontawesome/brands/github
link: https://github.com/krahets
diff --git a/ru/README.md b/ru/README.md
new file mode 100644
index 000000000..df8a0e5da
--- /dev/null
+++ b/ru/README.md
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+ Учебник по структурам данных и алгоритмам с анимированными схемами и кодом, готовым к запуску в один клик
+
+
+
+ Читать онлайн
+ |
+ Скачать PDF/EPUB
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 简体中文
+ |
+ 繁體中文
+ |
+ English
+ |
+ 日本語
+ |
+ Русский
+
+
+## О книге
+
+Этот проект призван создать бесплатный, открытый и дружелюбный к начинающим учебник по структурам данных и алгоритмам.
+
+- Книга построена на анимированных схемах, понятном изложении и плавной кривой обучения, помогая начинающим выстроить карту знаний по структурам данных и алгоритмам.
+- Исходный код можно запускать в один клик, чтобы на практике развивать навыки программирования и понимать, как работают алгоритмы и как устроены структуры данных внутри.
+- Мы поддерживаем совместное обучение: задавайте вопросы, делитесь идеями и продвигайтесь вперед через обсуждение.
+
+Если книга оказалась вам полезной, пожалуйста, поставьте Star :star: в правом верхнем углу страницы. Спасибо!
+
+## Рекомендации
+
+> «Понятная вводная книга по структурам данных и алгоритмам, которая направляет читателя к обучению и умом, и руками. Настоятельно рекомендую начинающим изучать алгоритмы именно с нее.»
+>
+> **—— Junhui Deng, профессор факультета компьютерных наук Университета Цинхуа**
+
+> «Если бы у меня была “Hello Algo”, когда я изучал структуры данных и алгоритмы, учиться было бы в десять раз проще!»
+>
+> **—— Mu Li, Senior Principal Scientist, Amazon**
+
+## Благодарности
+
+
+
+
+
+
+[Warp создан для программирования с несколькими AI-агентами.](https://go.warp.dev/hello-algo)
+
+Очень рекомендуем терминал Warp: красивый интерфейс, полезные AI-возможности и отличное общее впечатление от работы.
+
+## Участие
+
+Эта открытая книга продолжает активно развиваться, и мы будем рады вашему участию, чтобы сделать обучение для читателей еще качественнее.
+
+- [Исправление содержания](https://www.hello-algo.com/ru/chapter_appendix/contribution/): помогайте исправлять или указывать в комментариях грамматические ошибки, пропуски в содержании, двусмысленные формулировки, неработающие ссылки и баги в коде.
+- [Перевод кода на другие языки](https://github.com/krahets/hello-algo/issues/15): приглашаем вносить вклад в код на разных языках программирования; сейчас уже поддерживаются Python, Java, C++, Go, JavaScript и другие.
+
+Будем рады вашим замечаниям и предложениям. Если у вас есть вопросы, создайте Issue или свяжитесь через WeChat: `krahets-jyd`.
+
+Мы благодарим каждого участника, работавшего над этой книгой. Именно их самоотверженный вклад делает ее лучше:
+
+
+
+
+
+
+
+## License
+
+The texts, code, images, photos, and videos in this repository are licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/).
diff --git a/ru/codes/Dockerfile b/ru/codes/Dockerfile
new file mode 100644
index 000000000..97d37bbcf
--- /dev/null
+++ b/ru/codes/Dockerfile
@@ -0,0 +1,35 @@
+FROM ubuntu:latest
+
+# Use Ubuntu image from Aliyun
+RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
+ sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
+ sed -i 's/ports.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
+
+RUN apt-get update && apt-get install -y wget
+
+# Install languages environment
+ARG LANGS
+RUN for LANG in $LANGS; do \
+ case $LANG in \
+ python) \
+ apt-get install -y python3.10 && \
+ update-alternatives --install /usr/bin/python python /usr/bin/python3.10 1 ;; \
+ cpp) \
+ apt-get install -y g++ gdb ;; \
+ java) \
+ apt-get install -y openjdk-17-jdk ;; \
+ csharp) \
+ wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \
+ dpkg -i packages-microsoft-prod.deb && \
+ apt-get update && \
+ apt-get install -y dotnet-sdk-8.0 ;; \
+ # More languages...
+ *) \
+ echo "Warning: No installation workflow for $LANG" ;; \
+ esac \
+ done
+
+WORKDIR /codes
+COPY ./ ./
+
+CMD ["/bin/bash"]
diff --git a/ru/codes/c/.gitignore b/ru/codes/c/.gitignore
new file mode 100644
index 000000000..698ee4e21
--- /dev/null
+++ b/ru/codes/c/.gitignore
@@ -0,0 +1,9 @@
+# Ignore all
+*
+# Unignore all with extensions
+!*.*
+# Unignore all dirs
+!*/
+*.dSYM/
+
+build/
diff --git a/ru/codes/c/CMakeLists.txt b/ru/codes/c/CMakeLists.txt
new file mode 100644
index 000000000..bb5f8f6a9
--- /dev/null
+++ b/ru/codes/c/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.10)
+project(hello_algo C)
+
+set(CMAKE_C_STANDARD 11)
+
+include_directories(./include)
+
+add_subdirectory(chapter_computational_complexity)
+add_subdirectory(chapter_array_and_linkedlist)
+add_subdirectory(chapter_stack_and_queue)
+add_subdirectory(chapter_hashing)
+add_subdirectory(chapter_tree)
+add_subdirectory(chapter_heap)
+add_subdirectory(chapter_graph)
+add_subdirectory(chapter_searching)
+add_subdirectory(chapter_sorting)
+add_subdirectory(chapter_divide_and_conquer)
+add_subdirectory(chapter_backtracking)
+add_subdirectory(chapter_dynamic_programming)
+add_subdirectory(chapter_greedy)
diff --git a/ru/codes/c/chapter_array_and_linkedlist/CMakeLists.txt b/ru/codes/c/chapter_array_and_linkedlist/CMakeLists.txt
new file mode 100644
index 000000000..29677a0be
--- /dev/null
+++ b/ru/codes/c/chapter_array_and_linkedlist/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(array array.c)
+add_executable(linked_list linked_list.c)
+add_executable(my_list my_list.c)
\ No newline at end of file
diff --git a/ru/codes/c/chapter_array_and_linkedlist/array.c b/ru/codes/c/chapter_array_and_linkedlist/array.c
new file mode 100644
index 000000000..4f26b5a76
--- /dev/null
+++ b/ru/codes/c/chapter_array_and_linkedlist/array.c
@@ -0,0 +1,114 @@
+/**
+ * File: array.c
+ * Created Time: 2022-12-20
+ * Author: MolDuM (moldum@163.com)
+ */
+
+#include "../utils/common.h"
+
+/* Случайный доступ к элементу */
+int randomAccess(int *nums, int size) {
+ // Случайным образом выбрать число из интервала [0, size)
+ int randomIndex = rand() % size;
+ // Получить и вернуть случайный элемент
+ int randomNum = nums[randomIndex];
+ return randomNum;
+}
+
+/* Увеличить длину массива */
+int *extend(int *nums, int size, int enlarge) {
+ // Инициализировать массив увеличенной длины
+ int *res = (int *)malloc(sizeof(int) * (size + enlarge));
+ // Скопировать все элементы исходного массива в новый массив
+ for (int i = 0; i < size; i++) {
+ res[i] = nums[i];
+ }
+ // Инициализировать расширенное пространство
+ for (int i = size; i < size + enlarge; i++) {
+ res[i] = 0;
+ }
+ // Вернуть новый массив после расширения
+ return res;
+}
+
+/* Вставить элемент num по индексу index в массив */
+void insert(int *nums, int size, int num, int index) {
+ // Сдвинуть элемент с индексом index и все последующие элементы на одну позицию назад
+ for (int i = size - 1; i > index; i--) {
+ nums[i] = nums[i - 1];
+ }
+ // Присвоить num элементу по индексу index
+ nums[index] = num;
+}
+
+/* Удалить элемент по индексу index */
+// Внимание: stdio.h уже использует ключевое слово remove
+void removeItem(int *nums, int size, int index) {
+ // Сдвинуть все элементы после индекса index на одну позицию вперед
+ for (int i = index; i < size - 1; i++) {
+ nums[i] = nums[i + 1];
+ }
+}
+
+/* Обход массива */
+void traverse(int *nums, int size) {
+ int count = 0;
+ // Обход массива по индексам
+ for (int i = 0; i < size; i++) {
+ count += nums[i];
+ }
+}
+
+/* Найти заданный элемент в массиве */
+int find(int *nums, int size, int target) {
+ for (int i = 0; i < size; i++) {
+ if (nums[i] == target)
+ return i;
+ }
+ return -1;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация массива */
+ int size = 5;
+ int arr[5];
+ printf("Массив arr = ");
+ printArray(arr, size);
+
+ int nums[] = {1, 3, 2, 5, 4};
+ printf("Массив nums = ");
+ printArray(nums, size);
+
+ /* Случайный доступ */
+ int randomNum = randomAccess(nums, size);
+ printf("Случайный элемент из nums = %d", randomNum);
+
+ /* Расширение длины */
+ int enlarge = 3;
+ int *res = extend(nums, size, enlarge);
+ size += enlarge;
+ printf("После увеличения длины массива до 8 nums = ");
+ printArray(res, size);
+
+ /* Вставка элемента */
+ insert(res, size, 6, 3);
+ printf("После вставки числа 6 по индексу 3 nums = ");
+ printArray(res, size);
+
+ /* Удаление элемента */
+ removeItem(res, size, 2);
+ printf("После удаления элемента по индексу 2 nums = ");
+ printArray(res, size);
+
+ /* Обход массива */
+ traverse(res, size);
+
+ /* Поиск элемента */
+ int index = find(res, size, 3);
+ printf("Индекс элемента 3 в res = %d\n", index);
+
+ /* Освободить память */
+ free(res);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_array_and_linkedlist/linked_list.c b/ru/codes/c/chapter_array_and_linkedlist/linked_list.c
new file mode 100644
index 000000000..99f595b59
--- /dev/null
+++ b/ru/codes/c/chapter_array_and_linkedlist/linked_list.c
@@ -0,0 +1,89 @@
+/**
+ * File: linked_list.c
+ * Created Time: 2023-01-12
+ * Author: Zero (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Вставить узел P после узла n0 в связном списке */
+void insert(ListNode *n0, ListNode *P) {
+ ListNode *n1 = n0->next;
+ P->next = n1;
+ n0->next = P;
+}
+
+/* Удалить первый узел после узла n0 в связном списке */
+// Внимание: stdio.h уже использует ключевое слово remove
+void removeItem(ListNode *n0) {
+ if (!n0->next)
+ return;
+ // n0 -> P -> n1
+ ListNode *P = n0->next;
+ ListNode *n1 = P->next;
+ n0->next = n1;
+ // Освободить память
+ free(P);
+}
+
+/* Доступ к узлу связного списка по индексу index */
+ListNode *access(ListNode *head, int index) {
+ for (int i = 0; i < index; i++) {
+ if (head == NULL)
+ return NULL;
+ head = head->next;
+ }
+ return head;
+}
+
+/* Найти в связном списке первый узел со значением target */
+int find(ListNode *head, int target) {
+ int index = 0;
+ while (head) {
+ if (head->val == target)
+ return index;
+ head = head->next;
+ index++;
+ }
+ return -1;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация связного списка */
+ // Инициализация всех узлов
+ ListNode *n0 = newListNode(1);
+ ListNode *n1 = newListNode(3);
+ ListNode *n2 = newListNode(2);
+ ListNode *n3 = newListNode(5);
+ ListNode *n4 = newListNode(4);
+ // Построить ссылки между узлами
+ n0->next = n1;
+ n1->next = n2;
+ n2->next = n3;
+ n3->next = n4;
+ printf("Инициализированный связный список\r\n");
+ printLinkedList(n0);
+
+ /* Вставка узла */
+ insert(n0, newListNode(0));
+ printf("Связный список после вставки узла\r\n");
+ printLinkedList(n0);
+
+ /* Удаление узла */
+ removeItem(n0);
+ printf("Связный список после удаления узла\r\n");
+ printLinkedList(n0);
+
+ /* Доступ к узлу */
+ ListNode *node = access(n0, 3);
+ printf("Значение узла по индексу 3 в связном списке = %d\r\n", node->val);
+
+ /* Поиск узла */
+ int index = find(n0, 2);
+ printf("Индекс узла со значением 2 в связном списке = %d\r\n", index);
+
+ // Освободить память
+ freeMemoryLinkedList(n0);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_array_and_linkedlist/my_list.c b/ru/codes/c/chapter_array_and_linkedlist/my_list.c
new file mode 100644
index 000000000..3817de31a
--- /dev/null
+++ b/ru/codes/c/chapter_array_and_linkedlist/my_list.c
@@ -0,0 +1,163 @@
+/**
+ * File: my_list.c
+ * Created Time: 2023-01-12
+ * Author: Zero (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Класс списка */
+typedef struct {
+ int *arr; // Массив (для хранения элементов списка)
+ int capacity; // Вместимость списка
+ int size; // Размер списка
+ int extendRatio; // Коэффициент расширения списка при каждом увеличении
+} MyList;
+
+void extendCapacity(MyList *nums);
+
+/* Конструктор */
+MyList *newMyList() {
+ MyList *nums = malloc(sizeof(MyList));
+ nums->capacity = 10;
+ nums->arr = malloc(sizeof(int) * nums->capacity);
+ nums->size = 0;
+ nums->extendRatio = 2;
+ return nums;
+}
+
+/* Деструктор */
+void delMyList(MyList *nums) {
+ free(nums->arr);
+ free(nums);
+}
+
+/* Получить длину списка */
+int size(MyList *nums) {
+ return nums->size;
+}
+
+/* Получить вместимость списка */
+int capacity(MyList *nums) {
+ return nums->capacity;
+}
+
+/* Доступ к элементу */
+int get(MyList *nums, int index) {
+ assert(index >= 0 && index < nums->size);
+ return nums->arr[index];
+}
+
+/* Обновление элемента */
+void set(MyList *nums, int index, int num) {
+ assert(index >= 0 && index < nums->size);
+ nums->arr[index] = num;
+}
+
+/* Добавление элемента в конец */
+void add(MyList *nums, int num) {
+ if (size(nums) == capacity(nums)) {
+ extendCapacity(nums); // Расширение емкости
+ }
+ nums->arr[size(nums)] = num;
+ nums->size++;
+}
+
+/* Вставка элемента в середину */
+void insert(MyList *nums, int index, int num) {
+ assert(index >= 0 && index < size(nums));
+ // При превышении вместимости по числу элементов запускается расширение
+ if (size(nums) == capacity(nums)) {
+ extendCapacity(nums); // Расширение емкости
+ }
+ for (int i = size(nums); i > index; --i) {
+ nums->arr[i] = nums->arr[i - 1];
+ }
+ nums->arr[index] = num;
+ nums->size++;
+}
+
+/* Удаление элемента */
+// Внимание: stdio.h уже использует ключевое слово remove
+int removeItem(MyList *nums, int index) {
+ assert(index >= 0 && index < size(nums));
+ int num = nums->arr[index];
+ for (int i = index; i < size(nums) - 1; i++) {
+ nums->arr[i] = nums->arr[i + 1];
+ }
+ nums->size--;
+ return num;
+}
+
+/* Расширение списка */
+void extendCapacity(MyList *nums) {
+ // Сначала выделить память
+ int newCapacity = capacity(nums) * nums->extendRatio;
+ int *extend = (int *)malloc(sizeof(int) * newCapacity);
+ int *temp = nums->arr;
+
+ // Скопировать старые данные в новые
+ for (int i = 0; i < size(nums); i++)
+ extend[i] = nums->arr[i];
+
+ // Освободить старые данные
+ free(temp);
+
+ // Обновить новые данные
+ nums->arr = extend;
+ nums->capacity = newCapacity;
+}
+
+/* Преобразовать список в Array для вывода */
+int *toArray(MyList *nums) {
+ return nums->arr;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация списка */
+ MyList *nums = newMyList();
+ /* Добавление элемента в конец */
+ add(nums, 1);
+ add(nums, 3);
+ add(nums, 2);
+ add(nums, 5);
+ add(nums, 4);
+ printf("Список nums = ");
+ printArray(toArray(nums), size(nums));
+ printf("Вместимость = %d, длина = %d\n", capacity(nums), size(nums));
+
+ /* Вставка элемента в середину */
+ insert(nums, 3, 6);
+ printf("После вставки числа 6 по индексу 3 nums = ");
+ printArray(toArray(nums), size(nums));
+
+ /* Удаление элемента */
+ removeItem(nums, 3);
+ printf("После удаления элемента по индексу 3 nums = ");
+ printArray(toArray(nums), size(nums));
+
+ /* Доступ к элементу */
+ int num = get(nums, 1);
+ printf("Элемент по индексу 1: num = %d\n", num);
+
+ /* Обновление элемента */
+ set(nums, 1, 0);
+ printf("После обновления элемента по индексу 1 на 0 nums = ");
+ printArray(toArray(nums), size(nums));
+
+ /* Проверка механизма расширения */
+ for (int i = 0; i < 10; i++) {
+ // При i = 5 длина списка превысит его вместимость, и в этот момент сработает механизм расширения
+ add(nums, i);
+ }
+
+ printf("После расширения список nums = ");
+ printArray(toArray(nums), size(nums));
+ printf("Вместимость = %d, длина = %d\n", capacity(nums), size(nums));
+
+ /* Освободить выделенную память */
+ delMyList(nums);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/CMakeLists.txt b/ru/codes/c/chapter_backtracking/CMakeLists.txt
new file mode 100644
index 000000000..70161b6dd
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_executable(permutations_i permutations_i.c)
+add_executable(permutations_ii permutations_ii.c)
+add_executable(preorder_traversal_i_compact preorder_traversal_i_compact.c)
+add_executable(preorder_traversal_ii_compact preorder_traversal_ii_compact.c)
+add_executable(preorder_traversal_iii_compact preorder_traversal_iii_compact.c)
+add_executable(preorder_traversal_iii_template preorder_traversal_iii_template.c)
+add_executable(subset_sum_i_naive subset_sum_i_naive.c)
+add_executable(subset_sum_i subset_sum_i.c)
+add_executable(subset_sum_ii subset_sum_ii.c)
+add_executable(n_queens n_queens.c)
\ No newline at end of file
diff --git a/ru/codes/c/chapter_backtracking/n_queens.c b/ru/codes/c/chapter_backtracking/n_queens.c
new file mode 100644
index 000000000..929da94a8
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/n_queens.c
@@ -0,0 +1,95 @@
+/**
+ * File : n_queens.c
+ * Created Time: 2023-09-25
+ * Author : lucas (superrat6@gmail.com)
+ */
+
+#include "../utils/common.h"
+
+#define MAX_SIZE 100
+
+/* Алгоритм бэктрекинга: n ферзей */
+void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE],
+ bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) {
+ // Когда все строки уже обработаны, записать решение
+ if (row == n) {
+ res[*resSize] = (char **)malloc(sizeof(char *) * n);
+ for (int i = 0; i < n; ++i) {
+ res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1));
+ strcpy(res[*resSize][i], state[i]);
+ }
+ (*resSize)++;
+ return;
+ }
+ // Обойти все столбцы
+ for (int col = 0; col < n; col++) {
+ // Вычислить главную и побочную диагонали, соответствующие этой клетке
+ int diag1 = row - col + n - 1;
+ int diag2 = row + col;
+ // Отсечение: в столбце, главной диагонали и побочной диагонали этой клетки не должно быть ферзей
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // Попытка: поставить ферзя в эту клетку
+ state[row][col] = 'Q';
+ cols[col] = diags1[diag1] = diags2[diag2] = true;
+ // Перейти к размещению следующей строки
+ backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2);
+ // Откат: восстановить эту клетку как пустую
+ state[row][col] = '#';
+ cols[col] = diags1[diag1] = diags2[diag2] = false;
+ }
+ }
+}
+
+/* Решить задачу о n ферзях */
+char ***nQueens(int n, int *returnSize) {
+ char state[MAX_SIZE][MAX_SIZE];
+ // Инициализировать доску размера n*n, где 'Q' обозначает ферзя, а '#' — пустую клетку
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ state[i][j] = '#';
+ }
+ state[i][n] = '\0';
+ }
+ bool cols[MAX_SIZE] = {false}; // Отмечать, есть ли ферзь в столбце
+ bool diags1[2 * MAX_SIZE - 1] = {false}; // Отмечать наличие ферзя на главной диагонали
+ bool diags2[2 * MAX_SIZE - 1] = {false}; // Отмечать наличие ферзя на побочной диагонали
+
+ char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE);
+ *returnSize = 0;
+ backtrack(0, n, state, res, returnSize, cols, diags1, diags2);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int n = 4;
+ int returnSize;
+ char ***res = nQueens(n, &returnSize);
+
+ printf("Размер входной доски = %d\n", n);
+ printf("Количество способов расстановки ферзей: %d\n", returnSize);
+ for (int i = 0; i < returnSize; ++i) {
+ for (int j = 0; j < n; ++j) {
+ printf("[");
+ for (int k = 0; res[i][j][k] != '\0'; ++k) {
+ printf("%c", res[i][j][k]);
+ if (res[i][j][k + 1] != '\0') {
+ printf(", ");
+ }
+ }
+ printf("]\n");
+ }
+ printf("---------------------\n");
+ }
+
+ // Освободить память
+ for (int i = 0; i < returnSize; ++i) {
+ for (int j = 0; j < n; ++j) {
+ free(res[i][j]);
+ }
+ free(res[i]);
+ }
+ free(res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/permutations_i.c b/ru/codes/c/chapter_backtracking/permutations_i.c
new file mode 100644
index 000000000..5a94d4647
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/permutations_i.c
@@ -0,0 +1,79 @@
+/**
+ * File: permutations_i.c
+ * Created Time: 2023-06-04
+ * Author: Gonglja (glj0@outlook.com), krahets (krahets@163.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположим, что существует не более 1000 перестановок
+#define MAX_SIZE 1000
+
+/* Алгоритм бэктрекинга: все перестановки I */
+void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
+ // Когда длина состояния равна числу элементов, записать решение
+ if (stateSize == choicesSize) {
+ res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
+ for (int i = 0; i < choicesSize; i++) {
+ res[*resSize][i] = state[i];
+ }
+ (*resSize)++;
+ return;
+ }
+ // Перебор всех вариантов выбора
+ for (int i = 0; i < choicesSize; i++) {
+ int choice = choices[i];
+ // Отсечение: нельзя выбирать один и тот же элемент повторно
+ if (!selected[i]) {
+ // Попытка: сделать выбор и обновить состояние
+ selected[i] = true;
+ state[stateSize] = choice;
+ // Перейти к следующему выбору
+ backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ selected[i] = false;
+ }
+ }
+}
+
+/* Все перестановки I */
+int **permutationsI(int *nums, int numsSize, int *returnSize) {
+ int *state = (int *)malloc(numsSize * sizeof(int));
+ bool *selected = (bool *)malloc(numsSize * sizeof(bool));
+ for (int i = 0; i < numsSize; i++) {
+ selected[i] = false;
+ }
+ int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
+ *returnSize = 0;
+
+ backtrack(state, 0, nums, numsSize, selected, res, returnSize);
+
+ free(state);
+ free(selected);
+
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {1, 2, 3};
+ int numsSize = sizeof(nums) / sizeof(nums[0]);
+ int returnSize;
+
+ int **res = permutationsI(nums, numsSize, &returnSize);
+
+ printf("Входной массив nums = ");
+ printArray(nums, numsSize);
+ printf("\nВсе перестановки res = \n");
+ for (int i = 0; i < returnSize; i++) {
+ printArray(res[i], numsSize);
+ }
+
+ // Освободить память
+ for (int i = 0; i < returnSize; i++) {
+ free(res[i]);
+ }
+ free(res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/permutations_ii.c b/ru/codes/c/chapter_backtracking/permutations_ii.c
new file mode 100644
index 000000000..2caa13b50
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/permutations_ii.c
@@ -0,0 +1,81 @@
+/**
+ * File: permutations_ii.c
+ * Created Time: 2023-10-17
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположить, что существует не более 1000 перестановок, а максимальный элемент равен 1000
+#define MAX_SIZE 1000
+
+/* Алгоритм бэктрекинга: все перестановки II */
+void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
+ // Когда длина состояния равна числу элементов, записать решение
+ if (stateSize == choicesSize) {
+ res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
+ for (int i = 0; i < choicesSize; i++) {
+ res[*resSize][i] = state[i];
+ }
+ (*resSize)++;
+ return;
+ }
+ // Перебор всех вариантов выбора
+ bool duplicated[MAX_SIZE] = {false};
+ for (int i = 0; i < choicesSize; i++) {
+ int choice = choices[i];
+ // Отсечение: нельзя выбирать один и тот же элемент повторно и нельзя повторно выбирать равные элементы
+ if (!selected[i] && !duplicated[choice]) {
+ // Попытка: сделать выбор и обновить состояние
+ duplicated[choice] = true; // Записать значения уже выбранных элементов
+ selected[i] = true;
+ state[stateSize] = choice;
+ // Перейти к следующему выбору
+ backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ selected[i] = false;
+ }
+ }
+}
+
+/* Все перестановки II */
+int **permutationsII(int *nums, int numsSize, int *returnSize) {
+ int *state = (int *)malloc(numsSize * sizeof(int));
+ bool *selected = (bool *)malloc(numsSize * sizeof(bool));
+ for (int i = 0; i < numsSize; i++) {
+ selected[i] = false;
+ }
+ int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
+ *returnSize = 0;
+
+ backtrack(state, 0, nums, numsSize, selected, res, returnSize);
+
+ free(state);
+ free(selected);
+
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {1, 1, 2};
+ int numsSize = sizeof(nums) / sizeof(nums[0]);
+ int returnSize;
+
+ int **res = permutationsII(nums, numsSize, &returnSize);
+
+ printf("Входной массив nums = ");
+ printArray(nums, numsSize);
+ printf("\nВсе перестановки res = \n");
+ for (int i = 0; i < returnSize; i++) {
+ printArray(res[i], numsSize);
+ }
+
+ // Освободить память
+ for (int i = 0; i < returnSize; i++) {
+ free(res[i]);
+ }
+ free(res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/preorder_traversal_i_compact.c b/ru/codes/c/chapter_backtracking/preorder_traversal_i_compact.c
new file mode 100644
index 000000000..0dffbb407
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/preorder_traversal_i_compact.c
@@ -0,0 +1,49 @@
+/**
+ * File: preorder_traversal_i_compact.c
+ * Created Time: 2023-05-10
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположить, что длина результата не превышает 100
+#define MAX_SIZE 100
+
+TreeNode *res[MAX_SIZE];
+int resSize = 0;
+
+/* Предварительный обход: пример 1 */
+void preOrder(TreeNode *root) {
+ if (root == NULL) {
+ return;
+ }
+ if (root->val == 7) {
+ // Записать решение
+ res[resSize++] = root;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+}
+
+/* Driver Code */
+int main() {
+ int arr[] = {1, 7, 3, 4, 5, 6, 7};
+ TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
+ printf("\nИнициализация двоичного дерева\n");
+ printTree(root);
+
+ // Предварительный обход
+ preOrder(root);
+
+ printf("\nВывести все узлы со значением 7\n");
+ int *vals = malloc(resSize * sizeof(int));
+ for (int i = 0; i < resSize; i++) {
+ vals[i] = res[i]->val;
+ }
+ printArray(vals, resSize);
+
+ // Освободить память
+ freeMemoryTree(root);
+ free(vals);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c b/ru/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c
new file mode 100644
index 000000000..3b5983e38
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/preorder_traversal_ii_compact.c
@@ -0,0 +1,61 @@
+/**
+ * File: preorder_traversal_ii_compact.c
+ * Created Time: 2023-05-28
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположим, что длина пути и результата не превышает 100
+#define MAX_SIZE 100
+#define MAX_RES_SIZE 100
+
+TreeNode *path[MAX_SIZE];
+TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
+int pathSize = 0, resSize = 0;
+
+/* Предварительный обход: пример 2 */
+void preOrder(TreeNode *root) {
+ if (root == NULL) {
+ return;
+ }
+ // Попытка
+ path[pathSize++] = root;
+ if (root->val == 7) {
+ // Записать решение
+ for (int i = 0; i < pathSize; ++i) {
+ res[resSize][i] = path[i];
+ }
+ resSize++;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // Откат
+ pathSize--;
+}
+
+/* Driver Code */
+int main() {
+ int arr[] = {1, 7, 3, 4, 5, 6, 7};
+ TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
+ printf("\nИнициализация двоичного дерева\n");
+ printTree(root);
+
+ // Предварительный обход
+ preOrder(root);
+
+ printf("\nВывести все пути от корня к узлу 7\n");
+ for (int i = 0; i < resSize; ++i) {
+ int *vals = malloc(MAX_SIZE * sizeof(int));
+ int size = 0;
+ for (int j = 0; res[i][j] != NULL; ++j) {
+ vals[size++] = res[i][j]->val;
+ }
+ printArray(vals, size);
+ free(vals);
+ }
+
+ // Освободить память
+ freeMemoryTree(root);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c b/ru/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c
new file mode 100644
index 000000000..7a4e03adf
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/preorder_traversal_iii_compact.c
@@ -0,0 +1,62 @@
+/**
+ * File: preorder_traversal_iii_compact.c
+ * Created Time: 2023-06-04
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположим, что длина пути и результата не превышает 100
+#define MAX_SIZE 100
+#define MAX_RES_SIZE 100
+
+TreeNode *path[MAX_SIZE];
+TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
+int pathSize = 0, resSize = 0;
+
+/* Предварительный обход: пример 3 */
+void preOrder(TreeNode *root) {
+ // Отсечение
+ if (root == NULL || root->val == 3) {
+ return;
+ }
+ // Попытка
+ path[pathSize++] = root;
+ if (root->val == 7) {
+ // Записать решение
+ for (int i = 0; i < pathSize; i++) {
+ res[resSize][i] = path[i];
+ }
+ resSize++;
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // Откат
+ pathSize--;
+}
+
+/* Driver Code */
+int main() {
+ int arr[] = {1, 7, 3, 4, 5, 6, 7};
+ TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
+ printf("\nИнициализация двоичного дерева\n");
+ printTree(root);
+
+ // Предварительный обход
+ preOrder(root);
+
+ printf("\nВывести все пути от корня к узлу 7, не содержащие узлов со значением 3\n");
+ for (int i = 0; i < resSize; ++i) {
+ int *vals = malloc(MAX_SIZE * sizeof(int));
+ int size = 0;
+ for (int j = 0; res[i][j] != NULL; ++j) {
+ vals[size++] = res[i][j]->val;
+ }
+ printArray(vals, size);
+ free(vals);
+ }
+
+ // Освободить память
+ freeMemoryTree(root);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/preorder_traversal_iii_template.c b/ru/codes/c/chapter_backtracking/preorder_traversal_iii_template.c
new file mode 100644
index 000000000..98ad4a116
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/preorder_traversal_iii_template.c
@@ -0,0 +1,93 @@
+/**
+ * File: preorder_traversal_iii_template.c
+ * Created Time: 2023-06-04
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположим, что длина пути и результата не превышает 100
+#define MAX_SIZE 100
+#define MAX_RES_SIZE 100
+
+TreeNode *path[MAX_SIZE];
+TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
+int pathSize = 0, resSize = 0;
+
+/* Проверить, является ли текущее состояние решением */
+bool isSolution(void) {
+ return pathSize > 0 && path[pathSize - 1]->val == 7;
+}
+
+/* Записать решение */
+void recordSolution(void) {
+ for (int i = 0; i < pathSize; i++) {
+ res[resSize][i] = path[i];
+ }
+ resSize++;
+}
+
+/* Проверить, допустим ли этот выбор в текущем состоянии */
+bool isValid(TreeNode *choice) {
+ return choice != NULL && choice->val != 3;
+}
+
+/* Обновить состояние */
+void makeChoice(TreeNode *choice) {
+ path[pathSize++] = choice;
+}
+
+/* Восстановить состояние */
+void undoChoice(void) {
+ pathSize--;
+}
+
+/* Алгоритм бэктрекинга: пример 3 */
+void backtrack(TreeNode *choices[2]) {
+ // Проверить, является ли текущее состояние решением
+ if (isSolution()) {
+ // Записать решение
+ recordSolution();
+ }
+ // Перебор всех вариантов выбора
+ for (int i = 0; i < 2; i++) {
+ TreeNode *choice = choices[i];
+ // Отсечение: проверить допустимость выбора
+ if (isValid(choice)) {
+ // Попытка: сделать выбор и обновить состояние
+ makeChoice(choice);
+ // Перейти к следующему выбору
+ TreeNode *nextChoices[2] = {choice->left, choice->right};
+ backtrack(nextChoices);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ undoChoice();
+ }
+ }
+}
+
+/* Driver Code */
+int main() {
+ int arr[] = {1, 7, 3, 4, 5, 6, 7};
+ TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
+ printf("\nИнициализация двоичного дерева\n");
+ printTree(root);
+
+ // Алгоритм бэктрекинга
+ TreeNode *choices[2] = {root, NULL};
+ backtrack(choices);
+
+ printf("\nВывести все пути от корня к узлу 7, не содержащие узлов со значением 3\n");
+ for (int i = 0; i < resSize; ++i) {
+ int *vals = malloc(MAX_SIZE * sizeof(int));
+ int size = 0;
+ for (int j = 0; res[i][j] != NULL; ++j) {
+ vals[size++] = res[i][j]->val;
+ }
+ printArray(vals, size);
+ free(vals);
+ }
+
+ // Освободить память
+ freeMemoryTree(root);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/subset_sum_i.c b/ru/codes/c/chapter_backtracking/subset_sum_i.c
new file mode 100644
index 000000000..ec5374445
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/subset_sum_i.c
@@ -0,0 +1,78 @@
+/**
+ * File: subset_sum_i.c
+ * Created Time: 2023-07-29
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+#define MAX_SIZE 100
+#define MAX_RES_SIZE 100
+
+// Состояние (подмножество)
+int state[MAX_SIZE];
+int stateSize = 0;
+
+// Список результатов (список подмножеств)
+int res[MAX_RES_SIZE][MAX_SIZE];
+int resColSizes[MAX_RES_SIZE];
+int resSize = 0;
+
+/* Алгоритм бэктрекинга: сумма подмножеств I */
+void backtrack(int target, int *choices, int choicesSize, int start) {
+ // Если сумма подмножества равна target, записать решение
+ if (target == 0) {
+ for (int i = 0; i < stateSize; ++i) {
+ res[resSize][i] = state[i];
+ }
+ resColSizes[resSize++] = stateSize;
+ return;
+ }
+ // Обойти все варианты выбора
+ // Отсечение 2: начинать обход с start, чтобы избежать генерации повторяющихся подмножеств
+ for (int i = start; i < choicesSize; i++) {
+ // Отсечение 1: если сумма подмножества превышает target, немедленно завершить цикл
+ // Это связано с тем, что массив уже отсортирован, следующие элементы больше, и сумма подмножества точно превысит target
+ if (target - choices[i] < 0) {
+ break;
+ }
+ // Попытка: сделать выбор и обновить target и start
+ state[stateSize] = choices[i];
+ stateSize++;
+ // Перейти к следующему выбору
+ backtrack(target - choices[i], choices, choicesSize, i);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ stateSize--;
+ }
+}
+
+/* Функция сравнения */
+int cmp(const void *a, const void *b) {
+ return (*(int *)a - *(int *)b);
+}
+
+/* Решить задачу суммы подмножеств I */
+void subsetSumI(int *nums, int numsSize, int target) {
+ qsort(nums, numsSize, sizeof(int), cmp); // Отсортировать nums
+ int start = 0; // Стартовая вершина обхода
+ backtrack(target, nums, numsSize, start);
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {3, 4, 5};
+ int numsSize = sizeof(nums) / sizeof(nums[0]);
+ int target = 9;
+
+ subsetSumI(nums, numsSize, target);
+
+ printf("Входной массив nums = ");
+ printArray(nums, numsSize);
+ printf("target = %d\n", target);
+ printf("Все подмножества с суммой %d: \n", target);
+ for (int i = 0; i < resSize; ++i) {
+ printArray(res[i], resColSizes[i]);
+ }
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/subset_sum_i_naive.c b/ru/codes/c/chapter_backtracking/subset_sum_i_naive.c
new file mode 100644
index 000000000..a606a7366
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/subset_sum_i_naive.c
@@ -0,0 +1,69 @@
+/**
+ * File: subset_sum_i_naive.c
+ * Created Time: 2023-07-28
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+#define MAX_SIZE 100
+#define MAX_RES_SIZE 100
+
+// Состояние (подмножество)
+int state[MAX_SIZE];
+int stateSize = 0;
+
+// Список результатов (список подмножеств)
+int res[MAX_RES_SIZE][MAX_SIZE];
+int resColSizes[MAX_RES_SIZE];
+int resSize = 0;
+
+/* Алгоритм бэктрекинга: сумма подмножеств I */
+void backtrack(int target, int total, int *choices, int choicesSize) {
+ // Если сумма подмножества равна target, записать решение
+ if (total == target) {
+ for (int i = 0; i < stateSize; i++) {
+ res[resSize][i] = state[i];
+ }
+ resColSizes[resSize++] = stateSize;
+ return;
+ }
+ // Перебор всех вариантов выбора
+ for (int i = 0; i < choicesSize; i++) {
+ // Отсечение: если сумма подмножества превышает target, пропустить этот выбор
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // Попытка: сделать выбор и обновить элемент и total
+ state[stateSize++] = choices[i];
+ // Перейти к следующему выбору
+ backtrack(target, total + choices[i], choices, choicesSize);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ stateSize--;
+ }
+}
+
+/* Решить задачу суммы подмножеств I (с повторяющимися подмножествами) */
+void subsetSumINaive(int *nums, int numsSize, int target) {
+ resSize = 0; // Инициализировать число решений нулем
+ backtrack(target, 0, nums, numsSize);
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {3, 4, 5};
+ int numsSize = sizeof(nums) / sizeof(nums[0]);
+ int target = 9;
+
+ subsetSumINaive(nums, numsSize, target);
+
+ printf("Входной массив nums = ");
+ printArray(nums, numsSize);
+ printf("target = %d\n", target);
+ printf("Все подмножества с суммой %d: \n", target);
+ for (int i = 0; i < resSize; i++) {
+ printArray(res[i], resColSizes[i]);
+ }
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_backtracking/subset_sum_ii.c b/ru/codes/c/chapter_backtracking/subset_sum_ii.c
new file mode 100644
index 000000000..9ea1c791b
--- /dev/null
+++ b/ru/codes/c/chapter_backtracking/subset_sum_ii.c
@@ -0,0 +1,83 @@
+/**
+ * File: subset_sum_ii.c
+ * Created Time: 2023-07-29
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+#define MAX_SIZE 100
+#define MAX_RES_SIZE 100
+
+// Состояние (подмножество)
+int state[MAX_SIZE];
+int stateSize = 0;
+
+// Список результатов (список подмножеств)
+int res[MAX_RES_SIZE][MAX_SIZE];
+int resColSizes[MAX_RES_SIZE];
+int resSize = 0;
+
+/* Алгоритм бэктрекинга: сумма подмножеств II */
+void backtrack(int target, int *choices, int choicesSize, int start) {
+ // Если сумма подмножества равна target, записать решение
+ if (target == 0) {
+ for (int i = 0; i < stateSize; i++) {
+ res[resSize][i] = state[i];
+ }
+ resColSizes[resSize++] = stateSize;
+ return;
+ }
+ // Обойти все варианты выбора
+ // Отсечение 2: начинать обход с start, чтобы избежать генерации повторяющихся подмножеств
+ // Отсечение 3: начинать обход с start, чтобы избежать повторного выбора одного и того же элемента
+ for (int i = start; i < choicesSize; i++) {
+ // Отсечение 1: если сумма подмножества превышает target, сразу пропустить
+ if (target - choices[i] < 0) {
+ continue;
+ }
+ // Отсечение 4: если этот элемент равен элементу слева, значит ветвь поиска повторяется, ее нужно сразу пропустить
+ if (i > start && choices[i] == choices[i - 1]) {
+ continue;
+ }
+ // Попытка: сделать выбор и обновить target и start
+ state[stateSize] = choices[i];
+ stateSize++;
+ // Перейти к следующему выбору
+ backtrack(target - choices[i], choices, choicesSize, i + 1);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ stateSize--;
+ }
+}
+
+/* Функция сравнения */
+int cmp(const void *a, const void *b) {
+ return (*(int *)a - *(int *)b);
+}
+
+/* Решить задачу суммы подмножеств II */
+void subsetSumII(int *nums, int numsSize, int target) {
+ // Отсортировать nums
+ qsort(nums, numsSize, sizeof(int), cmp);
+ // Начать бэктрекинг
+ backtrack(target, nums, numsSize, 0);
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {4, 4, 5};
+ int numsSize = sizeof(nums) / sizeof(nums[0]);
+ int target = 9;
+
+ subsetSumII(nums, numsSize, target);
+
+ printf("Входной массив nums = ");
+ printArray(nums, numsSize);
+ printf("target = %d\n", target);
+ printf("Все подмножества с суммой %d: \n", target);
+ for (int i = 0; i < resSize; ++i) {
+ printArray(res[i], resColSizes[i]);
+ }
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_computational_complexity/CMakeLists.txt b/ru/codes/c/chapter_computational_complexity/CMakeLists.txt
new file mode 100644
index 000000000..dcfa063c3
--- /dev/null
+++ b/ru/codes/c/chapter_computational_complexity/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(iteration iteration.c)
+add_executable(recursion recursion.c)
+add_executable(time_complexity time_complexity.c)
+add_executable(worst_best_time_complexity worst_best_time_complexity.c)
+add_executable(space_complexity space_complexity.c)
diff --git a/ru/codes/c/chapter_computational_complexity/iteration.c b/ru/codes/c/chapter_computational_complexity/iteration.c
new file mode 100644
index 000000000..00f6a9904
--- /dev/null
+++ b/ru/codes/c/chapter_computational_complexity/iteration.c
@@ -0,0 +1,81 @@
+/**
+ * File: iteration.c
+ * Created Time: 2023-09-09
+ * Author: Gonglja (glj0@outlook.com), MwumLi (mwumli@hotmail.com)
+ */
+
+#include "../utils/common.h"
+
+/* Цикл for */
+int forLoop(int n) {
+ int res = 0;
+ // Циклическое суммирование 1, 2, ..., n-1, n
+ for (int i = 1; i <= n; i++) {
+ res += i;
+ }
+ return res;
+}
+
+/* Цикл while */
+int whileLoop(int n) {
+ int res = 0;
+ int i = 1; // Инициализация условной переменной
+ // Циклическое суммирование 1, 2, ..., n-1, n
+ while (i <= n) {
+ res += i;
+ i++; // Обновить условную переменную
+ }
+ return res;
+}
+
+/* Цикл while (двойное обновление) */
+int whileLoopII(int n) {
+ int res = 0;
+ int i = 1; // Инициализация условной переменной
+ // Циклическое суммирование 1, 4, 10, ...
+ while (i <= n) {
+ res += i;
+ // Обновить условную переменную
+ i++;
+ i *= 2;
+ }
+ return res;
+}
+
+/* Двойной цикл for */
+char *nestedForLoop(int n) {
+ // n * n — это число соответствующих точек, а максимальная длина строки "(i, j), " равна 6+10*2, плюс дополнительное место для завершающего нулевого символа \0
+ int size = n * n * 26 + 1;
+ char *res = malloc(size * sizeof(char));
+ // Цикл по i = 1, 2, ..., n-1, n
+ for (int i = 1; i <= n; i++) {
+ // Цикл по j = 1, 2, ..., n-1, n
+ for (int j = 1; j <= n; j++) {
+ char tmp[26];
+ snprintf(tmp, sizeof(tmp), "(%d, %d), ", i, j);
+ strncat(res, tmp, size - strlen(res) - 1);
+ }
+ }
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int n = 5;
+ int res;
+
+ res = forLoop(n);
+ printf("\nРезультат суммирования в цикле for res = %d\n", res);
+
+ res = whileLoop(n);
+ printf("\nРезультат суммирования в цикле while res = %d\n", res);
+
+ res = whileLoopII(n);
+ printf("\nРезультат суммирования в цикле while (двойное обновление) res = %d\n", res);
+
+ char *resStr = nestedForLoop(n);
+ printf("\nРезультат двойного цикла for %s\r\n", resStr);
+ free(resStr);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_computational_complexity/recursion.c b/ru/codes/c/chapter_computational_complexity/recursion.c
new file mode 100644
index 000000000..55d4d697d
--- /dev/null
+++ b/ru/codes/c/chapter_computational_complexity/recursion.c
@@ -0,0 +1,77 @@
+/**
+ * File: recursion.c
+ * Created Time: 2023-09-09
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Рекурсия */
+int recur(int n) {
+ // Условие завершения
+ if (n == 1)
+ return 1;
+ // Рекурсия: рекурсивный вызов
+ int res = recur(n - 1);
+ // Возврат: вернуть результат
+ return n + res;
+}
+
+/* Имитация рекурсии итерацией */
+int forLoopRecur(int n) {
+ int stack[1000]; // Использовать большой массив для имитации стека
+ int top = -1; // Индекс вершины стека
+ int res = 0;
+ // Рекурсия: рекурсивный вызов
+ for (int i = n; i > 0; i--) {
+ // Имитировать «рекурсию» с помощью операции помещения в стек
+ stack[1 + top++] = i;
+ }
+ // Возврат: вернуть результат
+ while (top >= 0) {
+ // Имитировать «возврат» с помощью операции извлечения из стека
+ res += stack[top--];
+ }
+ // res = 1+2+3+...+n
+ return res;
+}
+
+/* Хвостовая рекурсия */
+int tailRecur(int n, int res) {
+ // Условие завершения
+ if (n == 0)
+ return res;
+ // Хвостовой рекурсивный вызов
+ return tailRecur(n - 1, res + n);
+}
+
+/* Последовательность Фибоначчи: рекурсия */
+int fib(int n) {
+ // Условие завершения: f(1) = 0, f(2) = 1
+ if (n == 1 || n == 2)
+ return n - 1;
+ // Рекурсивный вызов f(n) = f(n-1) + f(n-2)
+ int res = fib(n - 1) + fib(n - 2);
+ // Вернуть результат f(n)
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int n = 5;
+ int res;
+
+ res = recur(n);
+ printf("\nРезультат суммирования в рекурсивной функции res = %d\n", res);
+
+ res = forLoopRecur(n);
+ printf("\nРезультат суммирования с использованием итерации для имитации рекурсии res = %d\n", res);
+
+ res = tailRecur(n, 0);
+ printf("\nРезультат суммирования в хвостовой рекурсии res = %d\n", res);
+
+ res = fib(n);
+ printf("\nЭлемент последовательности Фибоначчи с индексом %d = %d\n", n, res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_computational_complexity/space_complexity.c b/ru/codes/c/chapter_computational_complexity/space_complexity.c
new file mode 100644
index 000000000..2846fdfe9
--- /dev/null
+++ b/ru/codes/c/chapter_computational_complexity/space_complexity.c
@@ -0,0 +1,141 @@
+/**
+ * File: space_complexity.c
+ * Created Time: 2023-04-15
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Функция */
+int func() {
+ // Выполнить некоторые операции
+ return 0;
+}
+
+/* Постоянная сложность */
+void constant(int n) {
+ // Константы, переменные и объекты занимают O(1) памяти
+ const int a = 0;
+ int b = 0;
+ int nums[1000];
+ ListNode *node = newListNode(0);
+ free(node);
+ // Переменные в цикле занимают O(1) памяти
+ for (int i = 0; i < n; i++) {
+ int c = 0;
+ }
+ // Функции в цикле занимают O(1) памяти
+ for (int i = 0; i < n; i++) {
+ func();
+ }
+}
+
+/* Хеш-таблица */
+typedef struct {
+ int key;
+ int val;
+ UT_hash_handle hh; // Реализовано на основе uthash.h
+} HashTable;
+
+/* Линейная сложность */
+void linear(int n) {
+ // Массив длины n занимает O(n) памяти
+ int *nums = malloc(sizeof(int) * n);
+ free(nums);
+
+ // Список длины n занимает O(n) памяти
+ ListNode **nodes = malloc(sizeof(ListNode *) * n);
+ for (int i = 0; i < n; i++) {
+ nodes[i] = newListNode(i);
+ }
+ // Освобождение памяти
+ for (int i = 0; i < n; i++) {
+ free(nodes[i]);
+ }
+ free(nodes);
+
+ // Хеш-таблица длины n занимает O(n) памяти
+ HashTable *h = NULL;
+ for (int i = 0; i < n; i++) {
+ HashTable *tmp = malloc(sizeof(HashTable));
+ tmp->key = i;
+ tmp->val = i;
+ HASH_ADD_INT(h, key, tmp);
+ }
+
+ // Освобождение памяти
+ HashTable *curr, *tmp;
+ HASH_ITER(hh, h, curr, tmp) {
+ HASH_DEL(h, curr);
+ free(curr);
+ }
+}
+
+/* Линейная сложность (рекурсивная реализация) */
+void linearRecur(int n) {
+ printf("Рекурсия n = %d\r\n", n);
+ if (n == 1)
+ return;
+ linearRecur(n - 1);
+}
+
+/* Квадратичная сложность */
+void quadratic(int n) {
+ // Двумерный список занимает O(n^2) памяти
+ int **numMatrix = malloc(sizeof(int *) * n);
+ for (int i = 0; i < n; i++) {
+ int *tmp = malloc(sizeof(int) * n);
+ for (int j = 0; j < n; j++) {
+ tmp[j] = 0;
+ }
+ numMatrix[i] = tmp;
+ }
+
+ // Освобождение памяти
+ for (int i = 0; i < n; i++) {
+ free(numMatrix[i]);
+ }
+ free(numMatrix);
+}
+
+/* Квадратичная сложность (рекурсивная реализация) */
+int quadraticRecur(int n) {
+ if (n <= 0)
+ return 0;
+ int *nums = malloc(sizeof(int) * n);
+ printf("Рекурсия n = %d, длина nums = %d\r\n", n, n);
+ int res = quadraticRecur(n - 1);
+ free(nums);
+ return res;
+}
+
+/* Экспоненциальная сложность (построение полного двоичного дерева) */
+TreeNode *buildTree(int n) {
+ if (n == 0)
+ return NULL;
+ TreeNode *root = newTreeNode(0);
+ root->left = buildTree(n - 1);
+ root->right = buildTree(n - 1);
+ return root;
+}
+
+/* Driver Code */
+int main() {
+ int n = 5;
+ // Постоянная сложность
+ constant(n);
+ // Линейная сложность
+ linear(n);
+ linearRecur(n);
+ // Квадратичная сложность
+ quadratic(n);
+ quadraticRecur(n);
+ // Экспоненциальная сложность
+ TreeNode *root = buildTree(n);
+ printTree(root);
+
+ // Освободить память
+ freeMemoryTree(root);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_computational_complexity/time_complexity.c b/ru/codes/c/chapter_computational_complexity/time_complexity.c
new file mode 100644
index 000000000..56720669b
--- /dev/null
+++ b/ru/codes/c/chapter_computational_complexity/time_complexity.c
@@ -0,0 +1,179 @@
+/**
+ * File: time_complexity.c
+ * Created Time: 2023-01-03
+ * Author: codingonion (coderonion@gmail.com)
+ */
+
+#include "../utils/common.h"
+
+/* Постоянная сложность */
+int constant(int n) {
+ int count = 0;
+ int size = 100000;
+ int i = 0;
+ for (int i = 0; i < size; i++) {
+ count++;
+ }
+ return count;
+}
+
+/* Линейная сложность */
+int linear(int n) {
+ int count = 0;
+ for (int i = 0; i < n; i++) {
+ count++;
+ }
+ return count;
+}
+
+/* Линейная сложность (обход массива) */
+int arrayTraversal(int *nums, int n) {
+ int count = 0;
+ // Число итераций пропорционально длине массива
+ for (int i = 0; i < n; i++) {
+ count++;
+ }
+ return count;
+}
+
+/* Квадратичная сложность */
+int quadratic(int n) {
+ int count = 0;
+ // Число итераций квадратично зависит от размера данных n
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ count++;
+ }
+ }
+ return count;
+}
+
+/* Квадратичная сложность (пузырьковая сортировка) */
+int bubbleSort(int *nums, int n) {
+ int count = 0; // Счетчик
+ // Внешний цикл: неотсортированный диапазон [0, i]
+ for (int i = n - 1; i > 0; i--) {
+ // Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
+ for (int j = 0; j < i; j++) {
+ if (nums[j] > nums[j + 1]) {
+ // Поменять местами nums[j] и nums[j + 1]
+ int tmp = nums[j];
+ nums[j] = nums[j + 1];
+ nums[j + 1] = tmp;
+ count += 3; // Обмен элементов включает 3 элементарные операции
+ }
+ }
+ }
+ return count;
+}
+
+/* Экспоненциальная сложность (итеративная реализация) */
+int exponential(int n) {
+ int count = 0;
+ int bas = 1;
+ // На каждом шаге клетка делится надвое, образуя последовательность 1, 2, 4, 8, ..., 2^(n-1)
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < bas; j++) {
+ count++;
+ }
+ bas *= 2;
+ }
+ // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
+ return count;
+}
+
+/* Экспоненциальная сложность (рекурсивная реализация) */
+int expRecur(int n) {
+ if (n == 1)
+ return 1;
+ return expRecur(n - 1) + expRecur(n - 1) + 1;
+}
+
+/* Логарифмическая сложность (итеративная реализация) */
+int logarithmic(int n) {
+ int count = 0;
+ while (n > 1) {
+ n = n / 2;
+ count++;
+ }
+ return count;
+}
+
+/* Логарифмическая сложность (рекурсивная реализация) */
+int logRecur(int n) {
+ if (n <= 1)
+ return 0;
+ return logRecur(n / 2) + 1;
+}
+
+/* Линейно-логарифмическая сложность */
+int linearLogRecur(int n) {
+ if (n <= 1)
+ return 1;
+ int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);
+ for (int i = 0; i < n; i++) {
+ count++;
+ }
+ return count;
+}
+
+/* Факториальная сложность (рекурсивная реализация) */
+int factorialRecur(int n) {
+ if (n == 0)
+ return 1;
+ int count = 0;
+ for (int i = 0; i < n; i++) {
+ count += factorialRecur(n - 1);
+ }
+ return count;
+}
+
+/* Driver Code */
+int main(int argc, char *argv[]) {
+ // Можно изменить n и запустить программу, чтобы увидеть, как меняется число операций при разных сложностях
+ int n = 8;
+ printf("Размер входных данных n = %d\n", n);
+
+ int count = constant(n);
+ printf("Количество операций постоянной сложности = %d\n", count);
+
+ count = linear(n);
+ printf("Количество операций линейной сложности = %d\n", count);
+ // Выделить память в куче (создать одномерный массив переменной длины: число элементов равно n, тип элементов — int)
+ int *nums = (int *)malloc(n * sizeof(int));
+ count = arrayTraversal(nums, n);
+ printf("Количество операций линейной сложности (обход массива) = %d\n", count);
+
+ count = quadratic(n);
+ printf("Количество операций квадратичной сложности = %d\n", count);
+ for (int i = 0; i < n; i++) {
+ nums[i] = n - i; // [n,n-1,...,2,1]
+ }
+ count = bubbleSort(nums, n);
+ printf("Количество операций квадратичной сложности (пузырьковая сортировка) = %d\n", count);
+
+ count = exponential(n);
+ printf("Количество операций экспоненциальной сложности (итерация) = %d\n", count);
+ count = expRecur(n);
+ printf("Количество операций экспоненциальной сложности (рекурсия) = %d\n", count);
+
+ count = logarithmic(n);
+ printf("Количество операций логарифмической сложности (итерация) = %d\n", count);
+ count = logRecur(n);
+ printf("Количество операций логарифмической сложности (рекурсия) = %d\n", count);
+
+ count = linearLogRecur(n);
+ printf("Количество операций линейно-логарифмической сложности (рекурсия) = %d\n", count);
+
+ count = factorialRecur(n);
+ printf("Количество операций факториальной сложности (рекурсия) = %d\n", count);
+
+ // Освободить память в куче
+ if (nums != NULL) {
+ free(nums);
+ nums = NULL;
+ }
+ getchar();
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_computational_complexity/worst_best_time_complexity.c b/ru/codes/c/chapter_computational_complexity/worst_best_time_complexity.c
new file mode 100644
index 000000000..0ac947c94
--- /dev/null
+++ b/ru/codes/c/chapter_computational_complexity/worst_best_time_complexity.c
@@ -0,0 +1,57 @@
+/**
+ * File: worst_best_time_complexity.c
+ * Created Time: 2023-01-03
+ * Author: codingonion (coderonion@gmail.com)
+ */
+
+#include "../utils/common.h"
+
+/* Создать массив с элементами { 1, 2, ..., n } в случайном порядке */
+int *randomNumbers(int n) {
+ // Выделить память в куче (создать одномерный массив переменной длины: число элементов равно n, тип элементов — int)
+ int *nums = (int *)malloc(n * sizeof(int));
+ // Создать массив nums = { 1, 2, 3, ..., n }
+ for (int i = 0; i < n; i++) {
+ nums[i] = i + 1;
+ }
+ // Случайно перемешать элементы массива
+ for (int i = n - 1; i > 0; i--) {
+ int j = rand() % (i + 1);
+ int temp = nums[i];
+ nums[i] = nums[j];
+ nums[j] = temp;
+ }
+ return nums;
+}
+
+/* Найти индекс числа 1 в массиве nums */
+int findOne(int *nums, int n) {
+ for (int i = 0; i < n; i++) {
+ // Когда элемент 1 находится в начале массива, достигается лучшая временная сложность O(1)
+ // Когда элемент 1 находится в конце массива, достигается худшая временная сложность O(n)
+ if (nums[i] == 1)
+ return i;
+ }
+ return -1;
+}
+
+/* Driver Code */
+int main(int argc, char *argv[]) {
+ // Инициализировать seed генератора случайных чисел
+ srand((unsigned int)time(NULL));
+ for (int i = 0; i < 10; i++) {
+ int n = 100;
+ int *nums = randomNumbers(n);
+ int index = findOne(nums, n);
+ printf("\nПосле перемешивания массива [ 1, 2, ..., n ] nums = ");
+ printArray(nums, n);
+ printf("Индекс числа 1 = %d\n", index);
+ // Освободить память в куче
+ if (nums != NULL) {
+ free(nums);
+ nums = NULL;
+ }
+ }
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_divide_and_conquer/CMakeLists.txt b/ru/codes/c/chapter_divide_and_conquer/CMakeLists.txt
new file mode 100644
index 000000000..e03b1c588
--- /dev/null
+++ b/ru/codes/c/chapter_divide_and_conquer/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(binary_search_recur binary_search_recur.c)
+add_executable(build_tree build_tree.c)
+add_executable(hanota hanota.c)
diff --git a/ru/codes/c/chapter_divide_and_conquer/binary_search_recur.c b/ru/codes/c/chapter_divide_and_conquer/binary_search_recur.c
new file mode 100644
index 000000000..89bdbb8df
--- /dev/null
+++ b/ru/codes/c/chapter_divide_and_conquer/binary_search_recur.c
@@ -0,0 +1,47 @@
+/**
+ * File: binary_search_recur.c
+ * Created Time: 2023-10-01
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Бинарный поиск: задача f(i, j) */
+int dfs(int nums[], int target, int i, int j) {
+ // Если интервал пуст, целевой элемент отсутствует, вернуть -1
+ if (i > j) {
+ return -1;
+ }
+ // Вычислить индекс середины m
+ int m = (i + j) / 2;
+ if (nums[m] < target) {
+ // Рекурсивная подзадача f(m+1, j)
+ return dfs(nums, target, m + 1, j);
+ } else if (nums[m] > target) {
+ // Рекурсивная подзадача f(i, m-1)
+ return dfs(nums, target, i, m - 1);
+ } else {
+ // Целевой элемент найден, вернуть его индекс
+ return m;
+ }
+}
+
+/* Бинарный поиск */
+int binarySearch(int nums[], int target, int numsSize) {
+ int n = numsSize;
+ // Решить задачу f(0, n-1)
+ return dfs(nums, target, 0, n - 1);
+}
+
+/* Driver Code */
+int main() {
+ int target = 6;
+ int nums[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
+ int numsSize = sizeof(nums) / sizeof(nums[0]);
+
+ // Бинарный поиск (двусторонне замкнутый интервал)
+ int index = binarySearch(nums, target, numsSize);
+ printf("Индекс целевого элемента 6 = %d\n", index);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_divide_and_conquer/build_tree.c b/ru/codes/c/chapter_divide_and_conquer/build_tree.c
new file mode 100644
index 000000000..57b59c062
--- /dev/null
+++ b/ru/codes/c/chapter_divide_and_conquer/build_tree.c
@@ -0,0 +1,61 @@
+/**
+ * File : build_tree.c
+ * Created Time: 2023-10-16
+ * Author : lucas (superrat6@gmail.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположить, что все элементы меньше 1000
+#define MAX_SIZE 1000
+
+/* Построить двоичное дерево: разделяй и властвуй */
+TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) {
+ // Завершить при пустом диапазоне поддерева
+ if (r - l < 0)
+ return NULL;
+ // Инициализировать корневой узел
+ TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode));
+ root->val = preorder[i];
+ root->left = NULL;
+ root->right = NULL;
+ // Найти m, чтобы разделить левое и правое поддеревья
+ int m = inorderMap[preorder[i]];
+ // Подзадача: построить левое поддерево
+ root->left = dfs(preorder, inorderMap, i + 1, l, m - 1, size);
+ // Подзадача: построить правое поддерево
+ root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r, size);
+ // Вернуть корневой узел
+ return root;
+}
+
+/* Построить двоичное дерево */
+TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) {
+ // Инициализировать хеш-таблицу для хранения соответствия элементов inorder их индексам
+ int *inorderMap = (int *)malloc(sizeof(int) * MAX_SIZE);
+ for (int i = 0; i < inorderSize; i++) {
+ inorderMap[inorder[i]] = i;
+ }
+ TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorderSize - 1, inorderSize);
+ free(inorderMap);
+ return root;
+}
+
+/* Driver Code */
+int main() {
+ int preorder[] = {3, 9, 2, 1, 7};
+ int inorder[] = {9, 3, 1, 2, 7};
+ int preorderSize = sizeof(preorder) / sizeof(preorder[0]);
+ int inorderSize = sizeof(inorder) / sizeof(inorder[0]);
+ printf("Предварительный обход = ");
+ printArray(preorder, preorderSize);
+ printf("Симметричный обход = ");
+ printArray(inorder, inorderSize);
+
+ TreeNode *root = buildTree(preorder, preorderSize, inorder, inorderSize);
+ printf("Построенное двоичное дерево:\n");
+ printTree(root);
+
+ freeMemoryTree(root);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_divide_and_conquer/hanota.c b/ru/codes/c/chapter_divide_and_conquer/hanota.c
new file mode 100644
index 000000000..8201b948b
--- /dev/null
+++ b/ru/codes/c/chapter_divide_and_conquer/hanota.c
@@ -0,0 +1,74 @@
+/**
+ * File: hanota.c
+ * Created Time: 2023-10-01
+ * Author: Zuoxun (845242523@qq.com), lucas(superrat6@gmail.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположим, что существует не более 1000 перестановок
+#define MAX_SIZE 1000
+
+/* Переместить один диск */
+void move(int *src, int *srcSize, int *tar, int *tarSize) {
+ // Снять диск с вершины src
+ int pan = src[*srcSize - 1];
+ src[*srcSize - 1] = 0;
+ (*srcSize)--;
+ // Положить диск на вершину tar
+ tar[*tarSize] = pan;
+ (*tarSize)++;
+}
+
+/* Решить задачу Ханойской башни f(i) */
+void dfs(int i, int *src, int *srcSize, int *buf, int *bufSize, int *tar, int *tarSize) {
+ // Если в src остался только один диск, сразу переместить его в tar
+ if (i == 1) {
+ move(src, srcSize, tar, tarSize);
+ return;
+ }
+ // Подзадача f(i-1): переместить верхние i-1 дисков из src в buf с помощью tar
+ dfs(i - 1, src, srcSize, tar, tarSize, buf, bufSize);
+ // Подзадача f(1): переместить оставшийся один диск из src в tar
+ move(src, srcSize, tar, tarSize);
+ // Подзадача f(i-1): переместить верхние i-1 дисков из buf в tar с помощью src
+ dfs(i - 1, buf, bufSize, src, srcSize, tar, tarSize);
+}
+
+/* Решить задачу Ханойской башни */
+void solveHanota(int *A, int *ASize, int *B, int *BSize, int *C, int *CSize) {
+ // Переместить верхние n дисков из A в C с помощью B
+ dfs(*ASize, A, ASize, B, BSize, C, CSize);
+}
+
+/* Driver Code */
+int main() {
+ // Хвост списка соответствует вершине столбца
+ int a[] = {5, 4, 3, 2, 1};
+ int b[MAX_SIZE] = {0};
+ int c[MAX_SIZE] = {0};
+
+ int ASize = sizeof(a) / sizeof(a[0]);
+ int BSize = 0;
+ int CSize = 0;
+
+ printf("\nНачальное состояние:");
+ printf("\nA = ");
+ printArray(a, ASize);
+ printf("B = ");
+ printArray(b, BSize);
+ printf("C = ");
+ printArray(c, CSize);
+
+ solveHanota(a, &ASize, b, &BSize, c, &CSize);
+
+ printf("\nПосле завершения перемещения дисков:");
+ printf("A = ");
+ printArray(a, ASize);
+ printf("B = ");
+ printArray(b, BSize);
+ printf("C = ");
+ printArray(c, CSize);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_dynamic_programming/CMakeLists.txt b/ru/codes/c/chapter_dynamic_programming/CMakeLists.txt
new file mode 100644
index 000000000..dd769ebd5
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_executable(climbing_stairs_constraint_dp climbing_stairs_constraint_dp.c)
+add_executable(min_cost_climbing_stairs_dp min_cost_climbing_stairs_dp.c)
+add_executable(min_path_sum min_path_sum.c)
+add_executable(knapsack knapsack.c)
+add_executable(unbounded_knapsack unbounded_knapsack.c)
+add_executable(coin_change coin_change.c)
+add_executable(coin_change_ii coin_change_ii.c)
+add_executable(edit_distance edit_distance.c)
diff --git a/ru/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c
new file mode 100644
index 000000000..2a9c4cac3
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_backtrack.c
@@ -0,0 +1,47 @@
+/**
+ * File: climbing_stairs_backtrack.c
+ * Created Time: 2023-09-22
+ * Author: huawuque404 (huawuque404@163.com)
+ */
+
+#include "../utils/common.h"
+
+/* Бэктрекинг */
+void backtrack(int *choices, int state, int n, int *res, int len) {
+ // Когда подъем достигает n-й ступени, число вариантов увеличивается на 1
+ if (state == n)
+ res[0]++;
+ // Перебор всех вариантов выбора
+ for (int i = 0; i < len; i++) {
+ int choice = choices[i];
+ // Отсечение: нельзя выходить за n-ю ступень
+ if (state + choice > n)
+ continue;
+ // Попытка: сделать выбор и обновить состояние
+ backtrack(choices, state + choice, n, res, len);
+ // Откат
+ }
+}
+
+/* Подъем по лестнице: бэктрекинг */
+int climbingStairsBacktrack(int n) {
+ int choices[2] = {1, 2}; // Можно подняться на 1 или 2 ступени
+ int state = 0; // Начать подъем с 0-й ступени
+ int *res = (int *)malloc(sizeof(int));
+ *res = 0; // Использовать res[0] для хранения числа решений
+ int len = sizeof(choices) / sizeof(int);
+ backtrack(choices, state, n, res, len);
+ int result = *res;
+ free(res);
+ return result;
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsBacktrack(n);
+ printf("Количество способов подняться по лестнице из %d ступеней: %d\n", n, res);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c
new file mode 100644
index 000000000..22b3f6619
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_constraint_dp.c
@@ -0,0 +1,46 @@
+/**
+ * File: climbing_stairs_constraint_dp.c
+ * Created Time: 2023-10-02
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Подъем по лестнице с ограничениями: динамическое программирование */
+int climbingStairsConstraintDP(int n) {
+ if (n == 1 || n == 2) {
+ return 1;
+ }
+ // Инициализация таблицы dp для хранения решений подзадач
+ int **dp = malloc((n + 1) * sizeof(int *));
+ for (int i = 0; i <= n; i++) {
+ dp[i] = calloc(3, sizeof(int));
+ }
+ // Начальное состояние: заранее задать решения наименьших подзадач
+ dp[1][1] = 1;
+ dp[1][2] = 0;
+ dp[2][1] = 0;
+ dp[2][2] = 1;
+ // Переход состояний: постепенное решение больших подзадач через меньшие
+ for (int i = 3; i <= n; i++) {
+ dp[i][1] = dp[i - 1][2];
+ dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
+ }
+ int res = dp[n][1] + dp[n][2];
+ // Освободить память
+ for (int i = 0; i <= n; i++) {
+ free(dp[i]);
+ }
+ free(dp);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsConstraintDP(n);
+ printf("Количество способов подняться по лестнице из %d ступеней: %d\n", n, res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c
new file mode 100644
index 000000000..4cabb613e
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c
@@ -0,0 +1,32 @@
+/**
+ * File: climbing_stairs_dfs.c
+ * Created Time: 2023-09-19
+ * Author: huawuque404 (huawuque404@163.com)
+ */
+
+#include "../utils/common.h"
+
+/* Поиск */
+int dfs(int i) {
+ // dp[1] и dp[2] уже известны, вернуть их
+ if (i == 1 || i == 2)
+ return i;
+ // dp[i] = dp[i-1] + dp[i-2]
+ int count = dfs(i - 1) + dfs(i - 2);
+ return count;
+}
+
+/* Подъем по лестнице: поиск */
+int climbingStairsDFS(int n) {
+ return dfs(n);
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsDFS(n);
+ printf("Количество способов подняться по лестнице из %d ступеней: %d\n", n, res);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c
new file mode 100644
index 000000000..f15c87a32
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dfs_mem.c
@@ -0,0 +1,44 @@
+/**
+ * File: climbing_stairs_dfs_mem.c
+ * Created Time: 2023-09-19
+ * Author: huawuque404 (huawuque404@163.com)
+ */
+
+#include "../utils/common.h"
+
+/* Поиск с мемоизацией */
+int dfs(int i, int *mem) {
+ // dp[1] и dp[2] уже известны, вернуть их
+ if (i == 1 || i == 2)
+ return i;
+ // Если запись dp[i] существует, сразу вернуть ее
+ if (mem[i] != -1)
+ return mem[i];
+ // dp[i] = dp[i-1] + dp[i-2]
+ int count = dfs(i - 1, mem) + dfs(i - 2, mem);
+ // Сохранить dp[i]
+ mem[i] = count;
+ return count;
+}
+
+/* Подъем по лестнице: поиск с мемоизацией */
+int climbingStairsDFSMem(int n) {
+ // mem[i] хранит число способов подняться на i-ю ступень, -1 означает отсутствие записи
+ int *mem = (int *)malloc((n + 1) * sizeof(int));
+ for (int i = 0; i <= n; i++) {
+ mem[i] = -1;
+ }
+ int result = dfs(n, mem);
+ free(mem);
+ return result;
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsDFSMem(n);
+ printf("Количество способов подняться по лестнице из %d ступеней: %d\n", n, res);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c
new file mode 100644
index 000000000..b27f91cee
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c
@@ -0,0 +1,51 @@
+/**
+ * File: climbing_stairs_dp.c
+ * Created Time: 2023-09-19
+ * Author: huawuque404 (huawuque404@163.com)
+ */
+
+#include "../utils/common.h"
+
+/* Подъем по лестнице: динамическое программирование */
+int climbingStairsDP(int n) {
+ if (n == 1 || n == 2)
+ return n;
+ // Инициализация таблицы dp для хранения решений подзадач
+ int *dp = (int *)malloc((n + 1) * sizeof(int));
+ // Начальное состояние: заранее задать решения наименьших подзадач
+ dp[1] = 1;
+ dp[2] = 2;
+ // Переход состояний: постепенное решение больших подзадач через меньшие
+ for (int i = 3; i <= n; i++) {
+ dp[i] = dp[i - 1] + dp[i - 2];
+ }
+ int result = dp[n];
+ free(dp);
+ return result;
+}
+
+/* Подъем по лестнице: динамическое программирование с оптимизацией памяти */
+int climbingStairsDPComp(int n) {
+ if (n == 1 || n == 2)
+ return n;
+ int a = 1, b = 2;
+ for (int i = 3; i <= n; i++) {
+ int tmp = b;
+ b = a + b;
+ a = tmp;
+ }
+ return b;
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsDP(n);
+ printf("Количество способов подняться по лестнице из %d ступеней: %d\n", n, res);
+
+ res = climbingStairsDPComp(n);
+ printf("Количество способов подняться по лестнице из %d ступеней: %d\n", n, res);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_dynamic_programming/coin_change.c b/ru/codes/c/chapter_dynamic_programming/coin_change.c
new file mode 100644
index 000000000..99913f9f1
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/coin_change.c
@@ -0,0 +1,92 @@
+/**
+ * File: coin_change.c
+ * Created Time: 2023-10-02
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Найти минимум */
+int myMin(int a, int b) {
+ return a < b ? a : b;
+}
+
+/* Размен монет: динамическое программирование */
+int coinChangeDP(int coins[], int amt, int coinsSize) {
+ int n = coinsSize;
+ int MAX = amt + 1;
+ // Инициализация таблицы dp
+ int **dp = malloc((n + 1) * sizeof(int *));
+ for (int i = 0; i <= n; i++) {
+ dp[i] = calloc(amt + 1, sizeof(int));
+ }
+ // Переход состояний: первая строка и первый столбец
+ for (int a = 1; a <= amt; a++) {
+ dp[0][a] = MAX;
+ }
+ // Переход состояний: остальные строки и столбцы
+ for (int i = 1; i <= n; i++) {
+ for (int a = 1; a <= amt; a++) {
+ if (coins[i - 1] > a) {
+ // Если целевая сумма превышена, монету i не выбирать
+ dp[i][a] = dp[i - 1][a];
+ } else {
+ // Меньшее из двух решений: не брать или взять монету i
+ dp[i][a] = myMin(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1);
+ }
+ }
+ }
+ int res = dp[n][amt] != MAX ? dp[n][amt] : -1;
+ // Освободить память
+ for (int i = 0; i <= n; i++) {
+ free(dp[i]);
+ }
+ free(dp);
+ return res;
+}
+
+/* Размен монет: динамическое программирование с оптимизацией памяти */
+int coinChangeDPComp(int coins[], int amt, int coinsSize) {
+ int n = coinsSize;
+ int MAX = amt + 1;
+ // Инициализация таблицы dp
+ int *dp = malloc((amt + 1) * sizeof(int));
+ for (int j = 1; j <= amt; j++) {
+ dp[j] = MAX;
+ }
+ dp[0] = 0;
+
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int a = 1; a <= amt; a++) {
+ if (coins[i - 1] > a) {
+ // Если целевая сумма превышена, монету i не выбирать
+ dp[a] = dp[a];
+ } else {
+ // Меньшее из двух решений: не брать или взять монету i
+ dp[a] = myMin(dp[a], dp[a - coins[i - 1]] + 1);
+ }
+ }
+ }
+ int res = dp[amt] != MAX ? dp[amt] : -1;
+ // Освободить память
+ free(dp);
+ return res;
+}
+
+/* Driver code */
+int main() {
+ int coins[] = {1, 2, 5};
+ int coinsSize = sizeof(coins) / sizeof(coins[0]);
+ int amt = 4;
+
+ // Динамическое программирование
+ int res = coinChangeDP(coins, amt, coinsSize);
+ printf("Минимальное количество монет для целевой суммы = %d\n", res);
+
+ // Динамическое программирование с оптимизацией памяти
+ res = coinChangeDPComp(coins, amt, coinsSize);
+ printf("Минимальное количество монет для целевой суммы = %d\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_dynamic_programming/coin_change_ii.c b/ru/codes/c/chapter_dynamic_programming/coin_change_ii.c
new file mode 100644
index 000000000..20e2d0056
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/coin_change_ii.c
@@ -0,0 +1,81 @@
+/**
+ * File: coin_change_ii.c
+ * Created Time: 2023-10-02
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Размен монет II: динамическое программирование */
+int coinChangeIIDP(int coins[], int amt, int coinsSize) {
+ int n = coinsSize;
+ // Инициализация таблицы dp
+ int **dp = malloc((n + 1) * sizeof(int *));
+ for (int i = 0; i <= n; i++) {
+ dp[i] = calloc(amt + 1, sizeof(int));
+ }
+ // Инициализация первого столбца
+ for (int i = 0; i <= n; i++) {
+ dp[i][0] = 1;
+ }
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int a = 1; a <= amt; a++) {
+ if (coins[i - 1] > a) {
+ // Если целевая сумма превышена, монету i не выбирать
+ dp[i][a] = dp[i - 1][a];
+ } else {
+ // Сумма двух решений: не брать или взять монету i
+ dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]];
+ }
+ }
+ }
+ int res = dp[n][amt];
+ // Освободить память
+ for (int i = 0; i <= n; i++) {
+ free(dp[i]);
+ }
+ free(dp);
+ return res;
+}
+
+/* Размен монет II: динамическое программирование с оптимизацией памяти */
+int coinChangeIIDPComp(int coins[], int amt, int coinsSize) {
+ int n = coinsSize;
+ // Инициализация таблицы dp
+ int *dp = calloc(amt + 1, sizeof(int));
+ dp[0] = 1;
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int a = 1; a <= amt; a++) {
+ if (coins[i - 1] > a) {
+ // Если целевая сумма превышена, монету i не выбирать
+ dp[a] = dp[a];
+ } else {
+ // Сумма двух решений: не брать или взять монету i
+ dp[a] = dp[a] + dp[a - coins[i - 1]];
+ }
+ }
+ }
+ int res = dp[amt];
+ // Освободить память
+ free(dp);
+ return res;
+}
+
+/* Driver code */
+int main() {
+ int coins[] = {1, 2, 5};
+ int coinsSize = sizeof(coins) / sizeof(coins[0]);
+ int amt = 5;
+
+ // Динамическое программирование
+ int res = coinChangeIIDP(coins, amt, coinsSize);
+ printf("Количество комбинаций монет для набора целевой суммы = %d\n", res);
+
+ // Динамическое программирование с оптимизацией памяти
+ res = coinChangeIIDPComp(coins, amt, coinsSize);
+ printf("Количество комбинаций монет для набора целевой суммы = %d\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_dynamic_programming/edit_distance.c b/ru/codes/c/chapter_dynamic_programming/edit_distance.c
new file mode 100644
index 000000000..f352a0c45
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/edit_distance.c
@@ -0,0 +1,159 @@
+/**
+ * File: edit_distance.c
+ * Created Time: 2023-10-02
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Найти минимум */
+int myMin(int a, int b) {
+ return a < b ? a : b;
+}
+
+/* Редакционное расстояние: полный перебор */
+int editDistanceDFS(char *s, char *t, int i, int j) {
+ // Если s и t пусты, вернуть 0
+ if (i == 0 && j == 0)
+ return 0;
+ // Если s пусто, вернуть длину t
+ if (i == 0)
+ return j;
+ // Если t пусто, вернуть длину s
+ if (j == 0)
+ return i;
+ // Если два символа равны, сразу пропустить их
+ if (s[i - 1] == t[j - 1])
+ return editDistanceDFS(s, t, i - 1, j - 1);
+ // Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
+ int insert = editDistanceDFS(s, t, i, j - 1);
+ int del = editDistanceDFS(s, t, i - 1, j);
+ int replace = editDistanceDFS(s, t, i - 1, j - 1);
+ // Вернуть минимальное число шагов редактирования
+ return myMin(myMin(insert, del), replace) + 1;
+}
+
+/* Редакционное расстояние: поиск с мемоизацией */
+int editDistanceDFSMem(char *s, char *t, int memCols, int **mem, int i, int j) {
+ // Если s и t пусты, вернуть 0
+ if (i == 0 && j == 0)
+ return 0;
+ // Если s пусто, вернуть длину t
+ if (i == 0)
+ return j;
+ // Если t пусто, вернуть длину s
+ if (j == 0)
+ return i;
+ // Если запись уже есть, сразу вернуть ее
+ if (mem[i][j] != -1)
+ return mem[i][j];
+ // Если два символа равны, сразу пропустить их
+ if (s[i - 1] == t[j - 1])
+ return editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1);
+ // Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
+ int insert = editDistanceDFSMem(s, t, memCols, mem, i, j - 1);
+ int del = editDistanceDFSMem(s, t, memCols, mem, i - 1, j);
+ int replace = editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1);
+ // Сохранить и вернуть минимальное число шагов редактирования
+ mem[i][j] = myMin(myMin(insert, del), replace) + 1;
+ return mem[i][j];
+}
+
+/* Редакционное расстояние: динамическое программирование */
+int editDistanceDP(char *s, char *t, int n, int m) {
+ int **dp = malloc((n + 1) * sizeof(int *));
+ for (int i = 0; i <= n; i++) {
+ dp[i] = calloc(m + 1, sizeof(int));
+ }
+ // Переход состояний: первая строка и первый столбец
+ for (int i = 1; i <= n; i++) {
+ dp[i][0] = i;
+ }
+ for (int j = 1; j <= m; j++) {
+ dp[0][j] = j;
+ }
+ // Переход состояний: остальные строки и столбцы
+ for (int i = 1; i <= n; i++) {
+ for (int j = 1; j <= m; j++) {
+ if (s[i - 1] == t[j - 1]) {
+ // Если два символа равны, сразу пропустить их
+ dp[i][j] = dp[i - 1][j - 1];
+ } else {
+ // Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
+ dp[i][j] = myMin(myMin(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
+ }
+ }
+ }
+ int res = dp[n][m];
+ // Освободить память
+ for (int i = 0; i <= n; i++) {
+ free(dp[i]);
+ }
+ return res;
+}
+
+/* Редакционное расстояние: динамическое программирование с оптимизацией памяти */
+int editDistanceDPComp(char *s, char *t, int n, int m) {
+ int *dp = calloc(m + 1, sizeof(int));
+ // Переход состояний: первая строка
+ for (int j = 1; j <= m; j++) {
+ dp[j] = j;
+ }
+ // Переход состояний: остальные строки
+ for (int i = 1; i <= n; i++) {
+ // Переход состояний: первый столбец
+ int leftup = dp[0]; // Временно сохранить dp[i-1, j-1]
+ dp[0] = i;
+ // Переход состояний: остальные столбцы
+ for (int j = 1; j <= m; j++) {
+ int temp = dp[j];
+ if (s[i - 1] == t[j - 1]) {
+ // Если два символа равны, сразу пропустить их
+ dp[j] = leftup;
+ } else {
+ // Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
+ dp[j] = myMin(myMin(dp[j - 1], dp[j]), leftup) + 1;
+ }
+ leftup = temp; // Обновить до значения dp[i-1, j-1] для следующей итерации
+ }
+ }
+ int res = dp[m];
+ // Освободить память
+ free(dp);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ char *s = "bag";
+ char *t = "pack";
+ int n = strlen(s), m = strlen(t);
+
+ // Полный перебор
+ int res = editDistanceDFS(s, t, n, m);
+ printf("Чтобы заменить %s на %s, требуется минимум %d операций редактирования\n", s, t, res);
+
+ // Поиск с мемоизацией
+ int **mem = malloc((n + 1) * sizeof(int *));
+ for (int i = 0; i <= n; i++) {
+ mem[i] = malloc((m + 1) * sizeof(int));
+ memset(mem[i], -1, (m + 1) * sizeof(int));
+ }
+ res = editDistanceDFSMem(s, t, m + 1, mem, n, m);
+ printf("Чтобы заменить %s на %s, требуется минимум %d операций редактирования\n", s, t, res);
+ // Освободить память
+ for (int i = 0; i <= n; i++) {
+ free(mem[i]);
+ }
+ free(mem);
+
+ // Динамическое программирование
+ res = editDistanceDP(s, t, n, m);
+ printf("Чтобы заменить %s на %s, требуется минимум %d операций редактирования\n", s, t, res);
+
+ // Динамическое программирование с оптимизацией памяти
+ res = editDistanceDPComp(s, t, n, m);
+ printf("Чтобы заменить %s на %s, требуется минимум %d операций редактирования\n", s, t, res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_dynamic_programming/knapsack.c b/ru/codes/c/chapter_dynamic_programming/knapsack.c
new file mode 100644
index 000000000..4f12a5e91
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/knapsack.c
@@ -0,0 +1,137 @@
+/**
+ * File: knapsack.c
+ * Created Time: 2023-10-02
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Найти максимум */
+int myMax(int a, int b) {
+ return a > b ? a : b;
+}
+
+/* Рюкзак 0-1: полный перебор */
+int knapsackDFS(int wgt[], int val[], int i, int c) {
+ // Если все предметы уже рассмотрены или в рюкзаке не осталось места, вернуть стоимость 0
+ if (i == 0 || c == 0) {
+ return 0;
+ }
+ // Если вместимость рюкзака превышена, можно только не класть предмет в рюкзак
+ if (wgt[i - 1] > c) {
+ return knapsackDFS(wgt, val, i - 1, c);
+ }
+ // Вычислить максимальную стоимость для случаев, когда предмет i не кладут и кладут
+ int no = knapsackDFS(wgt, val, i - 1, c);
+ int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1];
+ // Вернуть вариант с большей стоимостью из двух возможных
+ return myMax(no, yes);
+}
+
+/* Рюкзак 0-1: поиск с мемоизацией */
+int knapsackDFSMem(int wgt[], int val[], int memCols, int **mem, int i, int c) {
+ // Если все предметы уже рассмотрены или в рюкзаке не осталось места, вернуть стоимость 0
+ if (i == 0 || c == 0) {
+ return 0;
+ }
+ // Если запись уже есть, вернуть сразу
+ if (mem[i][c] != -1) {
+ return mem[i][c];
+ }
+ // Если вместимость рюкзака превышена, можно только не класть предмет в рюкзак
+ if (wgt[i - 1] > c) {
+ return knapsackDFSMem(wgt, val, memCols, mem, i - 1, c);
+ }
+ // Вычислить максимальную стоимость для случаев, когда предмет i не кладут и кладут
+ int no = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c);
+ int yes = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c - wgt[i - 1]) + val[i - 1];
+ // Сохранить и вернуть вариант с большей стоимостью из двух решений
+ mem[i][c] = myMax(no, yes);
+ return mem[i][c];
+}
+
+/* Рюкзак 0-1: динамическое программирование */
+int knapsackDP(int wgt[], int val[], int cap, int wgtSize) {
+ int n = wgtSize;
+ // Инициализация таблицы dp
+ int **dp = malloc((n + 1) * sizeof(int *));
+ for (int i = 0; i <= n; i++) {
+ dp[i] = calloc(cap + 1, sizeof(int));
+ }
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int c = 1; c <= cap; c++) {
+ if (wgt[i - 1] > c) {
+ // Если вместимость рюкзака превышена, предмет i не выбирать
+ dp[i][c] = dp[i - 1][c];
+ } else {
+ // Большее из двух решений: не брать или взять предмет i
+ dp[i][c] = myMax(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]);
+ }
+ }
+ }
+ int res = dp[n][cap];
+ // Освободить память
+ for (int i = 0; i <= n; i++) {
+ free(dp[i]);
+ }
+ return res;
+}
+
+/* Рюкзак 0-1: динамическое программирование с оптимизацией памяти */
+int knapsackDPComp(int wgt[], int val[], int cap, int wgtSize) {
+ int n = wgtSize;
+ // Инициализация таблицы dp
+ int *dp = calloc(cap + 1, sizeof(int));
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ // Обход в обратном порядке
+ for (int c = cap; c >= 1; c--) {
+ if (wgt[i - 1] <= c) {
+ // Большее из двух решений: не брать или взять предмет i
+ dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
+ }
+ }
+ }
+ int res = dp[cap];
+ // Освободить память
+ free(dp);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int wgt[] = {10, 20, 30, 40, 50};
+ int val[] = {50, 120, 150, 210, 240};
+ int cap = 50;
+ int n = sizeof(wgt) / sizeof(wgt[0]);
+ int wgtSize = n;
+
+ // Полный перебор
+ int res = knapsackDFS(wgt, val, n, cap);
+ printf("Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна %d\n", res);
+
+ // Поиск с мемоизацией
+ int **mem = malloc((n + 1) * sizeof(int *));
+ for (int i = 0; i <= n; i++) {
+ mem[i] = malloc((cap + 1) * sizeof(int));
+ memset(mem[i], -1, (cap + 1) * sizeof(int));
+ }
+ res = knapsackDFSMem(wgt, val, cap + 1, mem, n, cap);
+ printf("Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна %d\n", res);
+ // Освободить память
+ for (int i = 0; i <= n; i++) {
+ free(mem[i]);
+ }
+ free(mem);
+
+ // Динамическое программирование
+ res = knapsackDP(wgt, val, cap, wgtSize);
+ printf("Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна %d\n", res);
+
+ // Динамическое программирование с оптимизацией памяти
+ res = knapsackDPComp(wgt, val, cap, wgtSize);
+ printf("Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна %d\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c b/ru/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c
new file mode 100644
index 000000000..24175e4bb
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/min_cost_climbing_stairs_dp.c
@@ -0,0 +1,62 @@
+/**
+ * File: min_cost_climbing_stairs_dp.c
+ * Created Time: 2023-10-02
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Найти минимум */
+int myMin(int a, int b) {
+ return a < b ? a : b;
+}
+
+/* Минимальная стоимость подъема по лестнице: динамическое программирование */
+int minCostClimbingStairsDP(int cost[], int costSize) {
+ int n = costSize - 1;
+ if (n == 1 || n == 2)
+ return cost[n];
+ // Инициализация таблицы dp для хранения решений подзадач
+ int *dp = calloc(n + 1, sizeof(int));
+ // Начальное состояние: заранее задать решения наименьших подзадач
+ dp[1] = cost[1];
+ dp[2] = cost[2];
+ // Переход состояний: постепенное решение больших подзадач через меньшие
+ for (int i = 3; i <= n; i++) {
+ dp[i] = myMin(dp[i - 1], dp[i - 2]) + cost[i];
+ }
+ int res = dp[n];
+ // Освободить память
+ free(dp);
+ return res;
+}
+
+/* Минимальная стоимость подъема по лестнице: динамическое программирование с оптимизацией памяти */
+int minCostClimbingStairsDPComp(int cost[], int costSize) {
+ int n = costSize - 1;
+ if (n == 1 || n == 2)
+ return cost[n];
+ int a = cost[1], b = cost[2];
+ for (int i = 3; i <= n; i++) {
+ int tmp = b;
+ b = myMin(a, tmp) + cost[i];
+ a = tmp;
+ }
+ return b;
+}
+
+/* Driver Code */
+int main() {
+ int cost[] = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1};
+ int costSize = sizeof(cost) / sizeof(cost[0]);
+ printf("Список стоимостей ступеней = ");
+ printArray(cost, costSize);
+
+ int res = minCostClimbingStairsDP(cost, costSize);
+ printf("Минимальная стоимость подъема по лестнице = %d\n", res);
+
+ res = minCostClimbingStairsDPComp(cost, costSize);
+ printf("Минимальная стоимость подъема по лестнице = %d\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_dynamic_programming/min_path_sum.c b/ru/codes/c/chapter_dynamic_programming/min_path_sum.c
new file mode 100644
index 000000000..5f0db0575
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/min_path_sum.c
@@ -0,0 +1,134 @@
+/**
+ * File: min_path_sum.c
+ * Created Time: 2023-10-02
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположить, что максимальное число строк и столбцов матрицы равно 100
+#define MAX_SIZE 100
+
+/* Найти минимум */
+int myMin(int a, int b) {
+ return a < b ? a : b;
+}
+
+/* Минимальная сумма пути: полный перебор */
+int minPathSumDFS(int grid[MAX_SIZE][MAX_SIZE], int i, int j) {
+ // Если это верхняя левая ячейка, завершить поиск
+ if (i == 0 && j == 0) {
+ return grid[0][0];
+ }
+ // Если индексы строки или столбца выходят за границы, вернуть стоимость +∞
+ if (i < 0 || j < 0) {
+ return INT_MAX;
+ }
+ // Вычислить минимальную стоимость пути из левого верхнего угла до (i-1, j) и (i, j-1)
+ int up = minPathSumDFS(grid, i - 1, j);
+ int left = minPathSumDFS(grid, i, j - 1);
+ // Вернуть минимальную стоимость пути из левого верхнего угла до (i, j)
+ return myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX;
+}
+
+/* Минимальная сумма пути: поиск с мемоизацией */
+int minPathSumDFSMem(int grid[MAX_SIZE][MAX_SIZE], int mem[MAX_SIZE][MAX_SIZE], int i, int j) {
+ // Если это верхняя левая ячейка, завершить поиск
+ if (i == 0 && j == 0) {
+ return grid[0][0];
+ }
+ // Если индексы строки или столбца выходят за границы, вернуть стоимость +∞
+ if (i < 0 || j < 0) {
+ return INT_MAX;
+ }
+ // Если запись уже есть, вернуть сразу
+ if (mem[i][j] != -1) {
+ return mem[i][j];
+ }
+ // Минимальная стоимость пути для левой и верхней ячеек
+ int up = minPathSumDFSMem(grid, mem, i - 1, j);
+ int left = minPathSumDFSMem(grid, mem, i, j - 1);
+ // Сохранить и вернуть минимальную стоимость пути из левого верхнего угла до (i, j)
+ mem[i][j] = myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX;
+ return mem[i][j];
+}
+
+/* Минимальная сумма пути: динамическое программирование */
+int minPathSumDP(int grid[MAX_SIZE][MAX_SIZE], int n, int m) {
+ // Инициализация таблицы dp
+ int **dp = malloc(n * sizeof(int *));
+ for (int i = 0; i < n; i++) {
+ dp[i] = calloc(m, sizeof(int));
+ }
+ dp[0][0] = grid[0][0];
+ // Переход состояний: первая строка
+ for (int j = 1; j < m; j++) {
+ dp[0][j] = dp[0][j - 1] + grid[0][j];
+ }
+ // Переход состояний: первый столбец
+ for (int i = 1; i < n; i++) {
+ dp[i][0] = dp[i - 1][0] + grid[i][0];
+ }
+ // Переход состояний: остальные строки и столбцы
+ for (int i = 1; i < n; i++) {
+ for (int j = 1; j < m; j++) {
+ dp[i][j] = myMin(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
+ }
+ }
+ int res = dp[n - 1][m - 1];
+ // Освободить память
+ for (int i = 0; i < n; i++) {
+ free(dp[i]);
+ }
+ return res;
+}
+
+/* Минимальная сумма пути: динамическое программирование с оптимизацией памяти */
+int minPathSumDPComp(int grid[MAX_SIZE][MAX_SIZE], int n, int m) {
+ // Инициализация таблицы dp
+ int *dp = calloc(m, sizeof(int));
+ // Переход состояний: первая строка
+ dp[0] = grid[0][0];
+ for (int j = 1; j < m; j++) {
+ dp[j] = dp[j - 1] + grid[0][j];
+ }
+ // Переход состояний: остальные строки
+ for (int i = 1; i < n; i++) {
+ // Переход состояний: первый столбец
+ dp[0] = dp[0] + grid[i][0];
+ // Переход состояний: остальные столбцы
+ for (int j = 1; j < m; j++) {
+ dp[j] = myMin(dp[j - 1], dp[j]) + grid[i][j];
+ }
+ }
+ int res = dp[m - 1];
+ // Освободить память
+ free(dp);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int grid[MAX_SIZE][MAX_SIZE] = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}};
+ int n = 4, m = 4; // Емкость матрицы равна MAX_SIZE * MAX_SIZE, число эффективных строк и столбцов — n * m
+
+ // Полный перебор
+ int res = minPathSumDFS(grid, n - 1, m - 1);
+ printf("Минимальная сумма пути из левого верхнего в правый нижний угол = %d\n", res);
+
+ // Поиск с мемоизацией
+ int mem[MAX_SIZE][MAX_SIZE];
+ memset(mem, -1, sizeof(mem));
+ res = minPathSumDFSMem(grid, mem, n - 1, m - 1);
+ printf("Минимальная сумма пути из левого верхнего в правый нижний угол = %d\n", res);
+
+ // Динамическое программирование
+ res = minPathSumDP(grid, n, m);
+ printf("Минимальная сумма пути из левого верхнего в правый нижний угол = %d\n", res);
+
+ // Динамическое программирование с оптимизацией памяти
+ res = minPathSumDPComp(grid, n, m);
+ printf("Минимальная сумма пути из левого верхнего в правый нижний угол = %d\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_dynamic_programming/unbounded_knapsack.c b/ru/codes/c/chapter_dynamic_programming/unbounded_knapsack.c
new file mode 100644
index 000000000..a39e116ff
--- /dev/null
+++ b/ru/codes/c/chapter_dynamic_programming/unbounded_knapsack.c
@@ -0,0 +1,81 @@
+/**
+ * File: unbounded_knapsack.c
+ * Created Time: 2023-10-02
+ * Author: Zuoxun (845242523@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Найти максимум */
+int myMax(int a, int b) {
+ return a > b ? a : b;
+}
+
+/* Полный рюкзак: динамическое программирование */
+int unboundedKnapsackDP(int wgt[], int val[], int cap, int wgtSize) {
+ int n = wgtSize;
+ // Инициализация таблицы dp
+ int **dp = malloc((n + 1) * sizeof(int *));
+ for (int i = 0; i <= n; i++) {
+ dp[i] = calloc(cap + 1, sizeof(int));
+ }
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int c = 1; c <= cap; c++) {
+ if (wgt[i - 1] > c) {
+ // Если вместимость рюкзака превышена, предмет i не выбирать
+ dp[i][c] = dp[i - 1][c];
+ } else {
+ // Большее из двух решений: не брать или взять предмет i
+ dp[i][c] = myMax(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]);
+ }
+ }
+ }
+ int res = dp[n][cap];
+ // Освободить память
+ for (int i = 0; i <= n; i++) {
+ free(dp[i]);
+ }
+ return res;
+}
+
+/* Полный рюкзак: динамическое программирование с оптимизацией памяти */
+int unboundedKnapsackDPComp(int wgt[], int val[], int cap, int wgtSize) {
+ int n = wgtSize;
+ // Инициализация таблицы dp
+ int *dp = calloc(cap + 1, sizeof(int));
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int c = 1; c <= cap; c++) {
+ if (wgt[i - 1] > c) {
+ // Если вместимость рюкзака превышена, предмет i не выбирать
+ dp[c] = dp[c];
+ } else {
+ // Большее из двух решений: не брать или взять предмет i
+ dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
+ }
+ }
+ }
+ int res = dp[cap];
+ // Освободить память
+ free(dp);
+ return res;
+}
+
+/* Driver code */
+int main() {
+ int wgt[] = {1, 2, 3};
+ int val[] = {5, 11, 15};
+ int wgtSize = sizeof(wgt) / sizeof(wgt[0]);
+ int cap = 4;
+
+ // Динамическое программирование
+ int res = unboundedKnapsackDP(wgt, val, cap, wgtSize);
+ printf("Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна %d\n", res);
+
+ // Динамическое программирование с оптимизацией памяти
+ res = unboundedKnapsackDPComp(wgt, val, cap, wgtSize);
+ printf("Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна %d\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_graph/CMakeLists.txt b/ru/codes/c/chapter_graph/CMakeLists.txt
new file mode 100644
index 000000000..28f8470f4
--- /dev/null
+++ b/ru/codes/c/chapter_graph/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(graph_adjacency_matrix graph_adjacency_matrix.c)
+add_executable(graph_adjacency_list_test graph_adjacency_list_test.c)
+add_executable(graph_bfs graph_bfs.c)
+add_executable(graph_dfs graph_dfs.c)
diff --git a/ru/codes/c/chapter_graph/graph_adjacency_list.c b/ru/codes/c/chapter_graph/graph_adjacency_list.c
new file mode 100644
index 000000000..2ee8281a8
--- /dev/null
+++ b/ru/codes/c/chapter_graph/graph_adjacency_list.c
@@ -0,0 +1,171 @@
+/**
+ * File: graph_adjacency_list.c
+ * Created Time: 2023-07-07
+ * Author: NI-SW (947743645@qq.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположим, что максимальное число узлов равно 100
+#define MAX_SIZE 100
+
+/* Структура узла */
+typedef struct AdjListNode {
+ Vertex *vertex; // Вершина
+ struct AdjListNode *next; // Узел-преемник
+} AdjListNode;
+
+/* Класс неориентированного графа на основе списка смежности */
+typedef struct {
+ AdjListNode *heads[MAX_SIZE]; // Массив узлов
+ int size; // Количество узлов
+} GraphAdjList;
+
+/* Конструктор */
+GraphAdjList *newGraphAdjList() {
+ GraphAdjList *graph = (GraphAdjList *)malloc(sizeof(GraphAdjList));
+ if (!graph) {
+ return NULL;
+ }
+ graph->size = 0;
+ for (int i = 0; i < MAX_SIZE; i++) {
+ graph->heads[i] = NULL;
+ }
+ return graph;
+}
+
+/* Деструктор */
+void delGraphAdjList(GraphAdjList *graph) {
+ for (int i = 0; i < graph->size; i++) {
+ AdjListNode *cur = graph->heads[i];
+ while (cur != NULL) {
+ AdjListNode *next = cur->next;
+ if (cur != graph->heads[i]) {
+ free(cur);
+ }
+ cur = next;
+ }
+ free(graph->heads[i]->vertex);
+ free(graph->heads[i]);
+ }
+ free(graph);
+}
+
+/* Найти узел, соответствующий вершине */
+AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) {
+ for (int i = 0; i < graph->size; i++) {
+ if (graph->heads[i]->vertex == vet) {
+ return graph->heads[i];
+ }
+ }
+ return NULL;
+}
+
+/* Вспомогательная функция добавления ребра */
+void addEdgeHelper(AdjListNode *head, Vertex *vet) {
+ AdjListNode *node = (AdjListNode *)malloc(sizeof(AdjListNode));
+ node->vertex = vet;
+ // Вставка в голову
+ node->next = head->next;
+ head->next = node;
+}
+
+/* Добавление ребра */
+void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) {
+ AdjListNode *head1 = findNode(graph, vet1);
+ AdjListNode *head2 = findNode(graph, vet2);
+ assert(head1 != NULL && head2 != NULL && head1 != head2);
+ // Добавить ребро vet1 - vet2
+ addEdgeHelper(head1, vet2);
+ addEdgeHelper(head2, vet1);
+}
+
+/* Вспомогательная функция удаления ребра */
+void removeEdgeHelper(AdjListNode *head, Vertex *vet) {
+ AdjListNode *pre = head;
+ AdjListNode *cur = head->next;
+ // Искать в связном списке узел, соответствующий vet
+ while (cur != NULL && cur->vertex != vet) {
+ pre = cur;
+ cur = cur->next;
+ }
+ if (cur == NULL)
+ return;
+ // Удалить из связного списка узел, соответствующий vet
+ pre->next = cur->next;
+ // Освободить память
+ free(cur);
+}
+
+/* Удаление ребра */
+void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) {
+ AdjListNode *head1 = findNode(graph, vet1);
+ AdjListNode *head2 = findNode(graph, vet2);
+ assert(head1 != NULL && head2 != NULL);
+ // Удалить ребро vet1 - vet2
+ removeEdgeHelper(head1, head2->vertex);
+ removeEdgeHelper(head2, head1->vertex);
+}
+
+/* Добавление вершины */
+void addVertex(GraphAdjList *graph, Vertex *vet) {
+ assert(graph != NULL && graph->size < MAX_SIZE);
+ AdjListNode *head = (AdjListNode *)malloc(sizeof(AdjListNode));
+ head->vertex = vet;
+ head->next = NULL;
+ // Добавить новый список в список смежности
+ graph->heads[graph->size++] = head;
+}
+
+/* Удаление вершины */
+void removeVertex(GraphAdjList *graph, Vertex *vet) {
+ AdjListNode *node = findNode(graph, vet);
+ assert(node != NULL);
+ // Удалить из списка смежности список, соответствующий вершине vet
+ AdjListNode *cur = node, *pre = NULL;
+ while (cur) {
+ pre = cur;
+ cur = cur->next;
+ free(pre);
+ }
+ // Обойти списки других вершин и удалить все ребра, содержащие vet
+ for (int i = 0; i < graph->size; i++) {
+ cur = graph->heads[i];
+ pre = NULL;
+ while (cur) {
+ pre = cur;
+ cur = cur->next;
+ if (cur && cur->vertex == vet) {
+ pre->next = cur->next;
+ free(cur);
+ break;
+ }
+ }
+ }
+ // Сдвинуть вершины после данной вперед, чтобы заполнить образовавшийся пробел
+ int i;
+ for (i = 0; i < graph->size; i++) {
+ if (graph->heads[i] == node)
+ break;
+ }
+ for (int j = i; j < graph->size - 1; j++) {
+ graph->heads[j] = graph->heads[j + 1];
+ }
+ graph->size--;
+ free(vet);
+}
+
+/* Вывести список смежности */
+void printGraph(const GraphAdjList *graph) {
+ printf("Список смежности =\n");
+ for (int i = 0; i < graph->size; ++i) {
+ AdjListNode *node = graph->heads[i];
+ printf("%d: [", node->vertex->val);
+ node = node->next;
+ while (node) {
+ printf("%d, ", node->vertex->val);
+ node = node->next;
+ }
+ printf("]\n");
+ }
+}
diff --git a/ru/codes/c/chapter_graph/graph_adjacency_list_test.c b/ru/codes/c/chapter_graph/graph_adjacency_list_test.c
new file mode 100644
index 000000000..7a2a22674
--- /dev/null
+++ b/ru/codes/c/chapter_graph/graph_adjacency_list_test.c
@@ -0,0 +1,55 @@
+/**
+ * File: graph_adjacency_list_test.c
+ * Created Time: 2023-07-11
+ * Author: NI-SW (947743645@qq.com)
+ */
+
+#include "graph_adjacency_list.c"
+
+/* Driver Code */
+int main() {
+ int vals[] = {1, 3, 2, 5, 4};
+ int size = sizeof(vals) / sizeof(vals[0]);
+ Vertex **v = valsToVets(vals, size);
+ Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}};
+ int egdeSize = sizeof(edges) / sizeof(edges[0]);
+ GraphAdjList *graph = newGraphAdjList();
+ // Добавить все вершины и ребра
+ for (int i = 0; i < size; i++) {
+ addVertex(graph, v[i]);
+ }
+ for (int i = 0; i < egdeSize; i++) {
+ addEdge(graph, edges[i][0], edges[i][1]);
+ }
+ printf("\nПосле инициализации граф имеет вид\n");
+ printGraph(graph);
+
+ /* Добавление ребра */
+ // Вершины 1 и 2 соответствуют v[0] и v[2]
+ addEdge(graph, v[0], v[2]);
+ printf("\nПосле добавления ребра 1-2 граф имеет вид\n");
+ printGraph(graph);
+
+ /* Удаление ребра */
+ // Вершины 1 и 3 соответствуют v[0] и v[1]
+ removeEdge(graph, v[0], v[1]);
+ printf("\nПосле удаления ребра 1-3 граф имеет вид\n");
+ printGraph(graph);
+
+ /* Добавление вершины */
+ Vertex *v5 = newVertex(6);
+ addVertex(graph, v5);
+ printf("\nПосле добавления вершины 6 граф имеет вид\n");
+ printGraph(graph);
+
+ /* Удаление вершины */
+ // Вершина 3 соответствует v[1]
+ removeVertex(graph, v[1]);
+ printf("\nПосле удаления вершины 3 граф имеет вид:\n");
+ printGraph(graph);
+
+ // Освободить память
+ delGraphAdjList(graph);
+ free(v);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_graph/graph_adjacency_matrix.c b/ru/codes/c/chapter_graph/graph_adjacency_matrix.c
new file mode 100644
index 000000000..1ed36a736
--- /dev/null
+++ b/ru/codes/c/chapter_graph/graph_adjacency_matrix.c
@@ -0,0 +1,150 @@
+/**
+ * File: graph_adjacency_matrix.c
+ * Created Time: 2023-07-06
+ * Author: NI-SW (947743645@qq.com)
+ */
+
+#include "../utils/common.h"
+
+// Предположить, что максимальное число вершин равно 100
+#define MAX_SIZE 100
+
+/* Структура неориентированного графа на основе матрицы смежности */
+typedef struct {
+ int vertices[MAX_SIZE];
+ int adjMat[MAX_SIZE][MAX_SIZE];
+ int size;
+} GraphAdjMat;
+
+/* Конструктор */
+GraphAdjMat *newGraphAdjMat() {
+ GraphAdjMat *graph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat));
+ graph->size = 0;
+ for (int i = 0; i < MAX_SIZE; i++) {
+ for (int j = 0; j < MAX_SIZE; j++) {
+ graph->adjMat[i][j] = 0;
+ }
+ }
+ return graph;
+}
+
+/* Деструктор */
+void delGraphAdjMat(GraphAdjMat *graph) {
+ free(graph);
+}
+
+/* Добавление вершины */
+void addVertex(GraphAdjMat *graph, int val) {
+ if (graph->size == MAX_SIZE) {
+ fprintf(stderr, "Количество вершин графа уже достигло максимума\n");
+ return;
+ }
+ // Добавить n-ю вершину и обнулить n-ю строку и столбец
+ int n = graph->size;
+ graph->vertices[n] = val;
+ for (int i = 0; i <= n; i++) {
+ graph->adjMat[n][i] = graph->adjMat[i][n] = 0;
+ }
+ graph->size++;
+}
+
+/* Удаление вершины */
+void removeVertex(GraphAdjMat *graph, int index) {
+ if (index < 0 || index >= graph->size) {
+ fprintf(stderr, "индекс вершины выходит за границы\n");
+ return;
+ }
+ // Удалить вершину с индексом index из списка вершин
+ for (int i = index; i < graph->size - 1; i++) {
+ graph->vertices[i] = graph->vertices[i + 1];
+ }
+ // Удалить строку с индексом index из матрицы смежности
+ for (int i = index; i < graph->size - 1; i++) {
+ for (int j = 0; j < graph->size; j++) {
+ graph->adjMat[i][j] = graph->adjMat[i + 1][j];
+ }
+ }
+ // Удалить столбец с индексом index из матрицы смежности
+ for (int i = 0; i < graph->size; i++) {
+ for (int j = index; j < graph->size - 1; j++) {
+ graph->adjMat[i][j] = graph->adjMat[i][j + 1];
+ }
+ }
+ graph->size--;
+}
+
+/* Добавление ребра */
+// Параметры i и j соответствуют индексам элементов vertices
+void addEdge(GraphAdjMat *graph, int i, int j) {
+ if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) {
+ fprintf(stderr, "индексы ребра выходят за границы или совпадают\n");
+ return;
+ }
+ graph->adjMat[i][j] = 1;
+ graph->adjMat[j][i] = 1;
+}
+
+/* Удаление ребра */
+// Параметры i и j соответствуют индексам элементов vertices
+void removeEdge(GraphAdjMat *graph, int i, int j) {
+ if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) {
+ fprintf(stderr, "индексы ребра выходят за границы или совпадают\n");
+ return;
+ }
+ graph->adjMat[i][j] = 0;
+ graph->adjMat[j][i] = 0;
+}
+
+/* Вывести матрицу смежности */
+void printGraphAdjMat(GraphAdjMat *graph) {
+ printf("Список вершин = ");
+ printArray(graph->vertices, graph->size);
+ printf("Матрица смежности =\n");
+ for (int i = 0; i < graph->size; i++) {
+ printArray(graph->adjMat[i], graph->size);
+ }
+}
+
+/* Driver Code */
+int main() {
+ // Инициализация неориентированного графа
+ GraphAdjMat *graph = newGraphAdjMat();
+ int vertices[] = {1, 3, 2, 5, 4};
+ for (int i = 0; i < 5; i++) {
+ addVertex(graph, vertices[i]);
+ }
+ int edges[][2] = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}};
+ for (int i = 0; i < 6; i++) {
+ addEdge(graph, edges[i][0], edges[i][1]);
+ }
+ printf("\nПосле инициализации граф имеет вид\n");
+ printGraphAdjMat(graph);
+
+ /* Добавление ребра */
+ // Индексы вершин 1 и 2 равны 0 и 2 соответственно
+ addEdge(graph, 0, 2);
+ printf("\nПосле добавления ребра 1-2 граф имеет вид\n");
+ printGraphAdjMat(graph);
+
+ /* Удаление ребра */
+ // Индексы вершин 1 и 3 равны 0 и 1 соответственно
+ removeEdge(graph, 0, 1);
+ printf("\nПосле удаления ребра 1-3 граф имеет вид\n");
+ printGraphAdjMat(graph);
+
+ /* Добавление вершины */
+ addVertex(graph, 6);
+ printf("\nПосле добавления вершины 6 граф имеет вид\n");
+ printGraphAdjMat(graph);
+
+ /* Удаление вершины */
+ // Индекс вершины 3 равен 1
+ removeVertex(graph, 1);
+ printf("\nПосле удаления вершины 3 граф имеет вид\n");
+ printGraphAdjMat(graph);
+
+ // Освободить память
+ delGraphAdjMat(graph);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_graph/graph_bfs.c b/ru/codes/c/chapter_graph/graph_bfs.c
new file mode 100644
index 000000000..8a41b29b3
--- /dev/null
+++ b/ru/codes/c/chapter_graph/graph_bfs.c
@@ -0,0 +1,116 @@
+/**
+ * File: graph_bfs.c
+ * Created Time: 2023-07-11
+ * Author: NI-SW (947743645@qq.com)
+ */
+
+#include "graph_adjacency_list.c"
+
+// Предположим, что максимальное число узлов равно 100
+#define MAX_SIZE 100
+
+/* Структура очереди узлов */
+typedef struct {
+ Vertex *vertices[MAX_SIZE];
+ int front, rear, size;
+} Queue;
+
+/* Конструктор */
+Queue *newQueue() {
+ Queue *q = (Queue *)malloc(sizeof(Queue));
+ q->front = q->rear = q->size = 0;
+ return q;
+}
+
+/* Проверка, пуста ли очередь */
+int isEmpty(Queue *q) {
+ return q->size == 0;
+}
+
+/* Операция добавления в очередь */
+void enqueue(Queue *q, Vertex *vet) {
+ q->vertices[q->rear] = vet;
+ q->rear = (q->rear + 1) % MAX_SIZE;
+ q->size++;
+}
+
+/* Операция извлечения из очереди */
+Vertex *dequeue(Queue *q) {
+ Vertex *vet = q->vertices[q->front];
+ q->front = (q->front + 1) % MAX_SIZE;
+ q->size--;
+ return vet;
+}
+
+/* Проверить, была ли вершина уже посещена */
+int isVisited(Vertex **visited, int size, Vertex *vet) {
+ // Искать узел обходом за O(n) времени
+ for (int i = 0; i < size; i++) {
+ if (visited[i] == vet)
+ return 1;
+ }
+ return 0;
+}
+
+/* Обход в ширину */
+// Использовать список смежности для представления графа, чтобы получить все смежные вершины заданной вершины
+void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) {
+ // Очередь используется для реализации BFS
+ Queue *queue = newQueue();
+ enqueue(queue, startVet);
+ visited[(*visitedSize)++] = startVet;
+ // Начиная с вершины vet, продолжать цикл, пока не будут посещены все вершины
+ while (!isEmpty(queue)) {
+ Vertex *vet = dequeue(queue); // Извлечь головную вершину из очереди
+ res[(*resSize)++] = vet; // Отметить посещенную вершину
+ // Обойти все смежные вершины данной вершины
+ AdjListNode *node = findNode(graph, vet);
+ while (node != NULL) {
+ // Пропустить уже посещенную вершину
+ if (!isVisited(visited, *visitedSize, node->vertex)) {
+ enqueue(queue, node->vertex); // Помещать в очередь только непосещенные вершины
+ visited[(*visitedSize)++] = node->vertex; // Отметить эту вершину как посещенную
+ }
+ node = node->next;
+ }
+ }
+ // Освободить память
+ free(queue);
+}
+
+/* Driver Code */
+int main() {
+ // Инициализация неориентированного графа
+ int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ int size = sizeof(vals) / sizeof(vals[0]);
+ Vertex **v = valsToVets(vals, size);
+ Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]}, {v[2], v[5]}, {v[3], v[4]},
+ {v[3], v[6]}, {v[4], v[5]}, {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}};
+ int egdeSize = sizeof(edges) / sizeof(edges[0]);
+ GraphAdjList *graph = newGraphAdjList();
+ // Добавить все вершины и ребра
+ for (int i = 0; i < size; i++) {
+ addVertex(graph, v[i]);
+ }
+ for (int i = 0; i < egdeSize; i++) {
+ addEdge(graph, edges[i][0], edges[i][1]);
+ }
+ printf("\nПосле инициализации граф имеет вид\n");
+ printGraph(graph);
+
+ // Обход в ширину
+ // Последовательность обхода вершин
+ Vertex *res[MAX_SIZE];
+ int resSize = 0;
+ // Используется для записи уже посещенных вершин
+ Vertex *visited[MAX_SIZE];
+ int visitedSize = 0;
+ graphBFS(graph, v[0], res, &resSize, visited, &visitedSize);
+ printf("\nПоследовательность вершин при обходе в ширину (BFS)\n");
+ printArray(vetsToVals(res, resSize), resSize);
+
+ // Освободить память
+ delGraphAdjList(graph);
+ free(v);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_graph/graph_dfs.c b/ru/codes/c/chapter_graph/graph_dfs.c
new file mode 100644
index 000000000..61cb4ad4a
--- /dev/null
+++ b/ru/codes/c/chapter_graph/graph_dfs.c
@@ -0,0 +1,75 @@
+/**
+ * File: graph_dfs.c
+ * Created Time: 2023-07-13
+ * Author: NI-SW (947743645@qq.com)
+ */
+
+#include "graph_adjacency_list.c"
+
+// Предположим, что максимальное число узлов равно 100
+#define MAX_SIZE 100
+
+/* Проверить, была ли вершина уже посещена */
+int isVisited(Vertex **res, int size, Vertex *vet) {
+ // Искать узел обходом за O(n) времени
+ for (int i = 0; i < size; i++) {
+ if (res[i] == vet) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Вспомогательная функция обхода в глубину */
+void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) {
+ // Отметить посещенную вершину
+ res[(*resSize)++] = vet;
+ // Обойти все смежные вершины данной вершины
+ AdjListNode *node = findNode(graph, vet);
+ while (node != NULL) {
+ // Пропустить уже посещенную вершину
+ if (!isVisited(res, *resSize, node->vertex)) {
+ // Рекурсивно обходить смежные вершины
+ dfs(graph, res, resSize, node->vertex);
+ }
+ node = node->next;
+ }
+}
+
+/* Обход в глубину */
+// Использовать список смежности для представления графа, чтобы получить все смежные вершины заданной вершины
+void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) {
+ dfs(graph, res, resSize, startVet);
+}
+
+/* Driver Code */
+int main() {
+ // Инициализация неориентированного графа
+ int vals[] = {0, 1, 2, 3, 4, 5, 6};
+ int size = sizeof(vals) / sizeof(vals[0]);
+ Vertex **v = valsToVets(vals, size);
+ Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[5]}, {v[4], v[5]}, {v[5], v[6]}};
+ int egdeSize = sizeof(edges) / sizeof(edges[0]);
+ GraphAdjList *graph = newGraphAdjList();
+ // Добавить все вершины и ребра
+ for (int i = 0; i < size; i++) {
+ addVertex(graph, v[i]);
+ }
+ for (int i = 0; i < egdeSize; i++) {
+ addEdge(graph, edges[i][0], edges[i][1]);
+ }
+ printf("\nПосле инициализации граф имеет вид\n");
+ printGraph(graph);
+
+ // Обход в глубину
+ Vertex *res[MAX_SIZE];
+ int resSize = 0;
+ graphDFS(graph, v[0], res, &resSize);
+ printf("\nПоследовательность вершин при обходе в глубину (DFS)\n");
+ printArray(vetsToVals(res, resSize), resSize);
+
+ // Освободить память
+ delGraphAdjList(graph);
+ free(v);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_greedy/CMakeLists.txt b/ru/codes/c/chapter_greedy/CMakeLists.txt
new file mode 100644
index 000000000..b8e6ca425
--- /dev/null
+++ b/ru/codes/c/chapter_greedy/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_executable(coin_change_greedy coin_change_greedy.c)
+add_executable(fractional_knapsack fractional_knapsack.c)
+add_executable(max_capacity max_capacity.c)
+add_executable(max_product_cutting max_product_cutting.c)
+
+if (NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ target_link_libraries(max_product_cutting m)
+endif()
diff --git a/ru/codes/c/chapter_greedy/coin_change_greedy.c b/ru/codes/c/chapter_greedy/coin_change_greedy.c
new file mode 100644
index 000000000..9cb45bb8e
--- /dev/null
+++ b/ru/codes/c/chapter_greedy/coin_change_greedy.c
@@ -0,0 +1,60 @@
+/**
+ * File: coin_change_greedy.c
+ * Created Time: 2023-09-07
+ * Author: lwbaptx (lwbaptx@gmail.com)
+ */
+
+#include "../utils/common.h"
+
+/* Размен монет: жадный алгоритм */
+int coinChangeGreedy(int *coins, int size, int amt) {
+ // Предположить, что список coins упорядочен
+ int i = size - 1;
+ int count = 0;
+ // Циклически выполнять жадный выбор, пока не останется суммы
+ while (amt > 0) {
+ // Найти монету, которая меньше остатка суммы и наиболее к нему близка
+ while (i > 0 && coins[i] > amt) {
+ i--;
+ }
+ // Выбрать coins[i]
+ amt -= coins[i];
+ count++;
+ }
+ // Если допустимое решение не найдено, вернуть -1
+ return amt == 0 ? count : -1;
+}
+
+/* Driver Code */
+int main() {
+ // Жадный подход: гарантирует нахождение глобально оптимального решения
+ int coins1[6] = {1, 5, 10, 20, 50, 100};
+ int amt = 186;
+ int res = coinChangeGreedy(coins1, 6, amt);
+ printf("\ncoins = ");
+ printArray(coins1, 6);
+ printf("amt = %d\n", amt);
+ printf("Минимальное количество монет для набора суммы %d = %d\n", amt, res);
+
+ // Жадный подход: не гарантирует нахождение глобально оптимального решения
+ int coins2[3] = {1, 20, 50};
+ amt = 60;
+ res = coinChangeGreedy(coins2, 3, amt);
+ printf("\ncoins = ");
+ printArray(coins2, 3);
+ printf("amt = %d\n", amt);
+ printf("Минимальное количество монет для набора суммы %d = %d\n", amt, res);
+ printf("На самом деле минимальное количество равно 3, а именно 20 + 20 + 20\n");
+
+ // Жадный подход: не гарантирует нахождение глобально оптимального решения
+ int coins3[3] = {1, 49, 50};
+ amt = 98;
+ res = coinChangeGreedy(coins3, 3, amt);
+ printf("\ncoins = ");
+ printArray(coins3, 3);
+ printf("amt = %d\n", amt);
+ printf("Минимальное количество монет для набора суммы %d = %d\n", amt, res);
+ printf("На самом деле минимальное количество равно 2, а именно 49 + 49\n");
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_greedy/fractional_knapsack.c b/ru/codes/c/chapter_greedy/fractional_knapsack.c
new file mode 100644
index 000000000..1325b2e76
--- /dev/null
+++ b/ru/codes/c/chapter_greedy/fractional_knapsack.c
@@ -0,0 +1,60 @@
+/**
+ * File: fractional_knapsack.c
+ * Created Time: 2023-09-14
+ * Author: xianii (xianyi.xia@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Предмет */
+typedef struct {
+ int w; // Вес предмета
+ int v; // Стоимость предмета
+} Item;
+
+/* Отсортировать по удельной стоимости */
+int sortByValueDensity(const void *a, const void *b) {
+ Item *t1 = (Item *)a;
+ Item *t2 = (Item *)b;
+ return (float)(t1->v) / t1->w < (float)(t2->v) / t2->w;
+}
+
+/* Дробный рюкзак: жадный алгоритм */
+float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) {
+ // Создать список предметов с двумя свойствами: вес и стоимость
+ Item *items = malloc(sizeof(Item) * itemCount);
+ for (int i = 0; i < itemCount; i++) {
+ items[i] = (Item){.w = wgt[i], .v = val[i]};
+ }
+ // Отсортировать по удельной стоимости item.v / item.w в порядке убывания
+ qsort(items, (size_t)itemCount, sizeof(Item), sortByValueDensity);
+ // Циклический жадный выбор
+ float res = 0.0;
+ for (int i = 0; i < itemCount; i++) {
+ if (items[i].w <= cap) {
+ // Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
+ res += items[i].v;
+ cap -= items[i].w;
+ } else {
+ // Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
+ res += (float)cap / items[i].w * items[i].v;
+ cap = 0;
+ break;
+ }
+ }
+ free(items);
+ return res;
+}
+
+/* Driver Code */
+int main(void) {
+ int wgt[] = {10, 20, 30, 40, 50};
+ int val[] = {50, 120, 150, 210, 240};
+ int capacity = 50;
+
+ // Жадный алгоритм
+ float res = fractionalKnapsack(wgt, val, sizeof(wgt) / sizeof(int), capacity);
+ printf("Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна %0.2f\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_greedy/max_capacity.c b/ru/codes/c/chapter_greedy/max_capacity.c
new file mode 100644
index 000000000..7300454c6
--- /dev/null
+++ b/ru/codes/c/chapter_greedy/max_capacity.c
@@ -0,0 +1,49 @@
+/**
+ * File: max_capacity.c
+ * Created Time: 2023-09-15
+ * Author: xianii (xianyi.xia@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Найти минимум */
+int myMin(int a, int b) {
+ return a < b ? a : b;
+}
+/* Найти максимум */
+int myMax(int a, int b) {
+ return a > b ? a : b;
+}
+
+/* Максимальная вместимость: жадный алгоритм */
+int maxCapacity(int ht[], int htLength) {
+ // Инициализировать i и j так, чтобы они располагались по двум концам массива
+ int i = 0;
+ int j = htLength - 1;
+ // Начальная максимальная вместимость равна 0
+ int res = 0;
+ // Выполнять жадный выбор в цикле, пока две доски не встретятся
+ while (i < j) {
+ // Обновить максимальную вместимость
+ int capacity = myMin(ht[i], ht[j]) * (j - i);
+ res = myMax(res, capacity);
+ // Сдвигать внутрь более короткую сторону
+ if (ht[i] < ht[j]) {
+ i++;
+ } else {
+ j--;
+ }
+ }
+ return res;
+}
+
+/* Driver Code */
+int main(void) {
+ int ht[] = {3, 8, 5, 2, 7, 7, 3, 4};
+
+ // Жадный алгоритм
+ int res = maxCapacity(ht, sizeof(ht) / sizeof(int));
+ printf("Максимальная вместимость = %d\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_greedy/max_product_cutting.c b/ru/codes/c/chapter_greedy/max_product_cutting.c
new file mode 100644
index 000000000..19107c18c
--- /dev/null
+++ b/ru/codes/c/chapter_greedy/max_product_cutting.c
@@ -0,0 +1,38 @@
+/**
+ * File: max_product_cutting.c
+ * Created Time: 2023-09-15
+ * Author: xianii (xianyi.xia@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Максимальное произведение разрезания: жадный алгоритм */
+int maxProductCutting(int n) {
+ // Когда n <= 3, обязательно нужно выделить одну 1
+ if (n <= 3) {
+ return 1 * (n - 1);
+ }
+ // Жадно выделить множители 3, где a — число троек, а b — остаток
+ int a = n / 3;
+ int b = n % 3;
+ if (b == 1) {
+ // Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
+ return pow(3, a - 1) * 2 * 2;
+ }
+ if (b == 2) {
+ // Если остаток равен 2, ничего не делать
+ return pow(3, a) * 2;
+ }
+ // Если остаток равен 0, ничего не делать
+ return pow(3, a);
+}
+
+/* Driver Code */
+int main(void) {
+ int n = 58;
+ // Жадный алгоритм
+ int res = maxProductCutting(n);
+ printf("Максимальное произведение после разрезания = %d\n", res);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_hashing/CMakeLists.txt b/ru/codes/c/chapter_hashing/CMakeLists.txt
new file mode 100644
index 000000000..9ac951ae4
--- /dev/null
+++ b/ru/codes/c/chapter_hashing/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(array_hash_map array_hash_map.c)
+add_executable(hash_map_chaining hash_map_chaining.c)
+add_executable(hash_map_open_addressing hash_map_open_addressing.c)
+add_executable(simple_hash simple_hash.c)
diff --git a/ru/codes/c/chapter_hashing/array_hash_map.c b/ru/codes/c/chapter_hashing/array_hash_map.c
new file mode 100644
index 000000000..cb2c2cdb3
--- /dev/null
+++ b/ru/codes/c/chapter_hashing/array_hash_map.c
@@ -0,0 +1,215 @@
+/**
+ * File: array_hash_map.c
+ * Created Time: 2023-03-18
+ * Author: Guanngxu (446678850@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Размер хеш-таблицы по умолчанию */
+#define MAX_SIZE 100
+
+/* Пара ключ-значение int->string */
+typedef struct {
+ int key;
+ char *val;
+} Pair;
+
+/* Набор пар ключ-значение */
+typedef struct {
+ void *set;
+ int len;
+} MapSet;
+
+/* Хеш-таблица на основе массива */
+typedef struct {
+ Pair *buckets[MAX_SIZE];
+} ArrayHashMap;
+
+/* Конструктор */
+ArrayHashMap *newArrayHashMap() {
+ ArrayHashMap *hmap = malloc(sizeof(ArrayHashMap));
+ for (int i=0; i < MAX_SIZE; i++) {
+ hmap->buckets[i] = NULL;
+ }
+ return hmap;
+}
+
+/* Деструктор */
+void delArrayHashMap(ArrayHashMap *hmap) {
+ for (int i = 0; i < MAX_SIZE; i++) {
+ if (hmap->buckets[i] != NULL) {
+ free(hmap->buckets[i]->val);
+ free(hmap->buckets[i]);
+ }
+ }
+ free(hmap);
+}
+
+/* Хеш-функция */
+int hashFunc(int key) {
+ int index = key % MAX_SIZE;
+ return index;
+}
+
+/* Операция поиска */
+const char *get(const ArrayHashMap *hmap, const int key) {
+ int index = hashFunc(key);
+ const Pair *Pair = hmap->buckets[index];
+ if (Pair == NULL)
+ return NULL;
+ return Pair->val;
+}
+
+/* Операция добавления */
+void put(ArrayHashMap *hmap, const int key, const char *val) {
+ Pair *Pair = malloc(sizeof(Pair));
+ Pair->key = key;
+ Pair->val = malloc(strlen(val) + 1);
+ strcpy(Pair->val, val);
+
+ int index = hashFunc(key);
+ hmap->buckets[index] = Pair;
+}
+
+/* Операция удаления */
+void removeItem(ArrayHashMap *hmap, const int key) {
+ int index = hashFunc(key);
+ free(hmap->buckets[index]->val);
+ free(hmap->buckets[index]);
+ hmap->buckets[index] = NULL;
+}
+
+/* Получить все пары ключ-значение */
+void pairSet(ArrayHashMap *hmap, MapSet *set) {
+ Pair *entries;
+ int i = 0, index = 0;
+ int total = 0;
+ /* Подсчитать число действительных пар ключ-значение */
+ for (i = 0; i < MAX_SIZE; i++) {
+ if (hmap->buckets[i] != NULL) {
+ total++;
+ }
+ }
+ entries = malloc(sizeof(Pair) * total);
+ for (i = 0; i < MAX_SIZE; i++) {
+ if (hmap->buckets[i] != NULL) {
+ entries[index].key = hmap->buckets[i]->key;
+ entries[index].val = malloc(strlen(hmap->buckets[i]->val) + 1);
+ strcpy(entries[index].val, hmap->buckets[i]->val);
+ index++;
+ }
+ }
+ set->set = entries;
+ set->len = total;
+}
+
+/* Получить все ключи */
+void keySet(ArrayHashMap *hmap, MapSet *set) {
+ int *keys;
+ int i = 0, index = 0;
+ int total = 0;
+ /* Подсчитать число действительных пар ключ-значение */
+ for (i = 0; i < MAX_SIZE; i++) {
+ if (hmap->buckets[i] != NULL) {
+ total++;
+ }
+ }
+ keys = malloc(total * sizeof(int));
+ for (i = 0; i < MAX_SIZE; i++) {
+ if (hmap->buckets[i] != NULL) {
+ keys[index] = hmap->buckets[i]->key;
+ index++;
+ }
+ }
+ set->set = keys;
+ set->len = total;
+}
+
+/* Получить все значения */
+void valueSet(ArrayHashMap *hmap, MapSet *set) {
+ char **vals;
+ int i = 0, index = 0;
+ int total = 0;
+ /* Подсчитать число действительных пар ключ-значение */
+ for (i = 0; i < MAX_SIZE; i++) {
+ if (hmap->buckets[i] != NULL) {
+ total++;
+ }
+ }
+ vals = malloc(total * sizeof(char *));
+ for (i = 0; i < MAX_SIZE; i++) {
+ if (hmap->buckets[i] != NULL) {
+ vals[index] = hmap->buckets[i]->val;
+ index++;
+ }
+ }
+ set->set = vals;
+ set->len = total;
+}
+
+/* Вывести хеш-таблицу */
+void print(ArrayHashMap *hmap) {
+ int i;
+ MapSet set;
+ pairSet(hmap, &set);
+ Pair *entries = (Pair *)set.set;
+ for (i = 0; i < set.len; i++) {
+ printf("%d -> %s\n", entries[i].key, entries[i].val);
+ }
+ free(set.set);
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация хеш-таблицы */
+ ArrayHashMap *hmap = newArrayHashMap();
+
+ /* Операция добавления */
+ // Добавить пару (key, value) в хеш-таблицу
+ put(hmap, 12836, "Сяо Ха");
+ put(hmap, 15937, "Сяо Ло");
+ put(hmap, 16750, "Сяо Суань");
+ put(hmap, 13276, "Сяо Фа");
+ put(hmap, 10583, "Сяо Я");
+ printf("\nПосле добавления хеш-таблица имеет вид\nКлюч -> Значение\n");
+ print(hmap);
+
+ /* Операция поиска */
+ // Ввести в хеш-таблицу ключ key и получить значение value
+ const char *name = get(hmap, 15937);
+ printf("\nДля студенческого номера 15937 найдено имя %s\n", name);
+
+ /* Операция удаления */
+ // Удалить пару (key, value) из хеш-таблицы
+ removeItem(hmap, 10583);
+ printf("\nПосле удаления 10583 хеш-таблица имеет вид\nКлюч -> Значение\n");
+ print(hmap);
+
+ /* Обход хеш-таблицы */
+ int i;
+
+ printf("\nОтдельный обход пар ключ-значение\n");
+ print(hmap);
+
+ MapSet set;
+
+ keySet(hmap, &set);
+ int *keys = (int *)set.set;
+ printf("\nОтдельный обход ключей\n");
+ for (i = 0; i < set.len; i++) {
+ printf("%d\n", keys[i]);
+ }
+ free(set.set);
+
+ valueSet(hmap, &set);
+ char **vals = (char **)set.set;
+ printf("\nОбход только значений Value\n");
+ for (i = 0; i < set.len; i++) {
+ printf("%s\n", vals[i]);
+ }
+ free(set.set);
+
+ delArrayHashMap(hmap);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_hashing/hash_map_chaining.c b/ru/codes/c/chapter_hashing/hash_map_chaining.c
new file mode 100644
index 000000000..6ff3702fc
--- /dev/null
+++ b/ru/codes/c/chapter_hashing/hash_map_chaining.c
@@ -0,0 +1,213 @@
+/**
+ * File: hash_map_chaining.c
+ * Created Time: 2023-10-13
+ * Author: SenMing (1206575349@qq.com), krahets (krahets@163.com)
+ */
+
+#include
+#include
+#include
+
+// Предположить, что максимальная длина val равна 100
+#define MAX_SIZE 100
+
+/* Пара ключ-значение */
+typedef struct {
+ int key;
+ char val[MAX_SIZE];
+} Pair;
+
+/* Узел связного списка */
+typedef struct Node {
+ Pair *pair;
+ struct Node *next;
+} Node;
+
+/* Хеш-таблица с цепочками */
+typedef struct {
+ int size; // Число пар ключ-значение
+ int capacity; // Вместимость хеш-таблицы
+ double loadThres; // Порог коэффициента загрузки для запуска расширения
+ int extendRatio; // Коэффициент расширения
+ Node **buckets; // Массив корзин
+} HashMapChaining;
+
+/* Конструктор */
+HashMapChaining *newHashMapChaining() {
+ HashMapChaining *hashMap = (HashMapChaining *)malloc(sizeof(HashMapChaining));
+ hashMap->size = 0;
+ hashMap->capacity = 4;
+ hashMap->loadThres = 2.0 / 3.0;
+ hashMap->extendRatio = 2;
+ hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *));
+ for (int i = 0; i < hashMap->capacity; i++) {
+ hashMap->buckets[i] = NULL;
+ }
+ return hashMap;
+}
+
+/* Деструктор */
+void delHashMapChaining(HashMapChaining *hashMap) {
+ for (int i = 0; i < hashMap->capacity; i++) {
+ Node *cur = hashMap->buckets[i];
+ while (cur) {
+ Node *tmp = cur;
+ cur = cur->next;
+ free(tmp->pair);
+ free(tmp);
+ }
+ }
+ free(hashMap->buckets);
+ free(hashMap);
+}
+
+/* Хеш-функция */
+int hashFunc(HashMapChaining *hashMap, int key) {
+ return key % hashMap->capacity;
+}
+
+/* Коэффициент загрузки */
+double loadFactor(HashMapChaining *hashMap) {
+ return (double)hashMap->size / (double)hashMap->capacity;
+}
+
+/* Операция поиска */
+char *get(HashMapChaining *hashMap, int key) {
+ int index = hashFunc(hashMap, key);
+ // Обойти корзину; если найден key, вернуть соответствующее val
+ Node *cur = hashMap->buckets[index];
+ while (cur) {
+ if (cur->pair->key == key) {
+ return cur->pair->val;
+ }
+ cur = cur->next;
+ }
+ return ""; // Если key не найден, вернуть пустую строку
+}
+
+/* Операция добавления */
+void put(HashMapChaining *hashMap, int key, const char *val);
+
+/* Расширить хеш-таблицу */
+void extend(HashMapChaining *hashMap) {
+ // Временно сохранить исходную хеш-таблицу
+ int oldCapacity = hashMap->capacity;
+ Node **oldBuckets = hashMap->buckets;
+ // Инициализация новой хеш-таблицы после расширения
+ hashMap->capacity *= hashMap->extendRatio;
+ hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *));
+ for (int i = 0; i < hashMap->capacity; i++) {
+ hashMap->buckets[i] = NULL;
+ }
+ hashMap->size = 0;
+ // Перенести пары ключ-значение из исходной хеш-таблицы в новую
+ for (int i = 0; i < oldCapacity; i++) {
+ Node *cur = oldBuckets[i];
+ while (cur) {
+ put(hashMap, cur->pair->key, cur->pair->val);
+ Node *temp = cur;
+ cur = cur->next;
+ // Освободить память
+ free(temp->pair);
+ free(temp);
+ }
+ }
+
+ free(oldBuckets);
+}
+
+/* Операция добавления */
+void put(HashMapChaining *hashMap, int key, const char *val) {
+ // Когда коэффициент загрузки превышает порог, выполнить расширение
+ if (loadFactor(hashMap) > hashMap->loadThres) {
+ extend(hashMap);
+ }
+ int index = hashFunc(hashMap, key);
+ // Обойти корзину; если встретился указанный key, обновить соответствующее val и вернуть
+ Node *cur = hashMap->buckets[index];
+ while (cur) {
+ if (cur->pair->key == key) {
+ strcpy(cur->pair->val, val); // Если встретился указанный key, обновить соответствующий val и вернуть
+ return;
+ }
+ cur = cur->next;
+ }
+ // Если такого key нет, добавить пару ключ-значение в голову связного списка
+ Pair *newPair = (Pair *)malloc(sizeof(Pair));
+ newPair->key = key;
+ strcpy(newPair->val, val);
+ Node *newNode = (Node *)malloc(sizeof(Node));
+ newNode->pair = newPair;
+ newNode->next = hashMap->buckets[index];
+ hashMap->buckets[index] = newNode;
+ hashMap->size++;
+}
+
+/* Операция удаления */
+void removeItem(HashMapChaining *hashMap, int key) {
+ int index = hashFunc(hashMap, key);
+ Node *cur = hashMap->buckets[index];
+ Node *pre = NULL;
+ while (cur) {
+ if (cur->pair->key == key) {
+ // Удалить из него пару ключ-значение
+ if (pre) {
+ pre->next = cur->next;
+ } else {
+ hashMap->buckets[index] = cur->next;
+ }
+ // Освободить память
+ free(cur->pair);
+ free(cur);
+ hashMap->size--;
+ return;
+ }
+ pre = cur;
+ cur = cur->next;
+ }
+}
+
+/* Вывести хеш-таблицу */
+void print(HashMapChaining *hashMap) {
+ for (int i = 0; i < hashMap->capacity; i++) {
+ Node *cur = hashMap->buckets[i];
+ printf("[");
+ while (cur) {
+ printf("%d -> %s, ", cur->pair->key, cur->pair->val);
+ cur = cur->next;
+ }
+ printf("]\n");
+ }
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация хеш-таблицы */
+ HashMapChaining *hashMap = newHashMapChaining();
+
+ /* Операция добавления */
+ // Добавить пару (key, value) в хеш-таблицу
+ put(hashMap, 12836, "Сяо Ха");
+ put(hashMap, 15937, "Сяо Ло");
+ put(hashMap, 16750, "Сяо Суань");
+ put(hashMap, 13276, "Сяо Фа");
+ put(hashMap, 10583, "Сяо Я");
+ printf("\nПосле добавления хеш-таблица имеет вид\nКлюч -> Значение\n");
+ print(hashMap);
+
+ /* Операция поиска */
+ // Ввести в хеш-таблицу ключ key и получить значение value
+ char *name = get(hashMap, 13276);
+ printf("\nДля студенческого номера 13276 найдено имя %s\n", name);
+
+ /* Операция удаления */
+ // Удалить пару (key, value) из хеш-таблицы
+ removeItem(hashMap, 12836);
+ printf("\nПосле удаления студенческого номера 12836 хеш-таблица имеет вид\nКлюч -> Значение\n");
+ print(hashMap);
+
+ /* Освободить память хеш-таблицы */
+ delHashMapChaining(hashMap);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_hashing/hash_map_open_addressing.c b/ru/codes/c/chapter_hashing/hash_map_open_addressing.c
new file mode 100644
index 000000000..41eba6053
--- /dev/null
+++ b/ru/codes/c/chapter_hashing/hash_map_open_addressing.c
@@ -0,0 +1,211 @@
+/**
+ * File: hash_map_open_addressing.c
+ * Created Time: 2023-10-6
+ * Author: lclc6 (w1929522410@163.com)
+ */
+
+#include "../utils/common.h"
+
+/* Хеш-таблица с открытой адресацией */
+typedef struct {
+ int key;
+ char *val;
+} Pair;
+
+/* Хеш-таблица с открытой адресацией */
+typedef struct {
+ int size; // Число пар ключ-значение
+ int capacity; // Вместимость хеш-таблицы
+ double loadThres; // Порог коэффициента загрузки для запуска расширения
+ int extendRatio; // Коэффициент расширения
+ Pair **buckets; // Массив корзин
+ Pair *TOMBSTONE; // Удалить метку
+} HashMapOpenAddressing;
+
+// Объявление функции
+void extend(HashMapOpenAddressing *hashMap);
+
+/* Конструктор */
+HashMapOpenAddressing *newHashMapOpenAddressing() {
+ HashMapOpenAddressing *hashMap = (HashMapOpenAddressing *)malloc(sizeof(HashMapOpenAddressing));
+ hashMap->size = 0;
+ hashMap->capacity = 4;
+ hashMap->loadThres = 2.0 / 3.0;
+ hashMap->extendRatio = 2;
+ hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *));
+ hashMap->TOMBSTONE = (Pair *)malloc(sizeof(Pair));
+ hashMap->TOMBSTONE->key = -1;
+ hashMap->TOMBSTONE->val = "-1";
+
+ return hashMap;
+}
+
+/* Деструктор */
+void delHashMapOpenAddressing(HashMapOpenAddressing *hashMap) {
+ for (int i = 0; i < hashMap->capacity; i++) {
+ Pair *pair = hashMap->buckets[i];
+ if (pair != NULL && pair != hashMap->TOMBSTONE) {
+ free(pair->val);
+ free(pair);
+ }
+ }
+ free(hashMap->buckets);
+ free(hashMap->TOMBSTONE);
+ free(hashMap);
+}
+
+/* Хеш-функция */
+int hashFunc(HashMapOpenAddressing *hashMap, int key) {
+ return key % hashMap->capacity;
+}
+
+/* Коэффициент загрузки */
+double loadFactor(HashMapOpenAddressing *hashMap) {
+ return (double)hashMap->size / (double)hashMap->capacity;
+}
+
+/* Найти индекс корзины, соответствующий key */
+int findBucket(HashMapOpenAddressing *hashMap, int key) {
+ int index = hashFunc(hashMap, key);
+ int firstTombstone = -1;
+ // Выполнять линейное пробирование и завершить при встрече с пустой корзиной
+ while (hashMap->buckets[index] != NULL) {
+ // Если встретился key, вернуть соответствующий индекс корзины
+ if (hashMap->buckets[index]->key == key) {
+ // Если ранее встретилась метка удаления, переместить пару ключ-значение на этот индекс
+ if (firstTombstone != -1) {
+ hashMap->buckets[firstTombstone] = hashMap->buckets[index];
+ hashMap->buckets[index] = hashMap->TOMBSTONE;
+ return firstTombstone; // Вернуть индекс корзины после перемещения
+ }
+ return index; // Вернуть индекс корзины
+ }
+ // Записать первую встретившуюся метку удаления
+ if (firstTombstone == -1 && hashMap->buckets[index] == hashMap->TOMBSTONE) {
+ firstTombstone = index;
+ }
+ // Вычислить индекс корзины; при выходе за конец вернуться к началу
+ index = (index + 1) % hashMap->capacity;
+ }
+ // Если key не существует, вернуть индекс точки добавления
+ return firstTombstone == -1 ? index : firstTombstone;
+}
+
+/* Операция поиска */
+char *get(HashMapOpenAddressing *hashMap, int key) {
+ // Найти индекс корзины, соответствующий key
+ int index = findBucket(hashMap, key);
+ // Если пара ключ-значение найдена, вернуть соответствующее val
+ if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) {
+ return hashMap->buckets[index]->val;
+ }
+ // Если пары ключ-значение не существует, вернуть пустую строку
+ return "";
+}
+
+/* Операция добавления */
+void put(HashMapOpenAddressing *hashMap, int key, char *val) {
+ // Когда коэффициент загрузки превышает порог, выполнить расширение
+ if (loadFactor(hashMap) > hashMap->loadThres) {
+ extend(hashMap);
+ }
+ // Найти индекс корзины, соответствующий key
+ int index = findBucket(hashMap, key);
+ // Если пара ключ-значение найдена, перезаписать val и вернуть
+ if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) {
+ free(hashMap->buckets[index]->val);
+ hashMap->buckets[index]->val = (char *)malloc(sizeof(strlen(val) + 1));
+ strcpy(hashMap->buckets[index]->val, val);
+ hashMap->buckets[index]->val[strlen(val)] = '\0';
+ return;
+ }
+ // Если пары ключ-значение нет, добавить ее
+ Pair *pair = (Pair *)malloc(sizeof(Pair));
+ pair->key = key;
+ pair->val = (char *)malloc(sizeof(strlen(val) + 1));
+ strcpy(pair->val, val);
+ pair->val[strlen(val)] = '\0';
+
+ hashMap->buckets[index] = pair;
+ hashMap->size++;
+}
+
+/* Операция удаления */
+void removeItem(HashMapOpenAddressing *hashMap, int key) {
+ // Найти индекс корзины, соответствующий key
+ int index = findBucket(hashMap, key);
+ // Если пара ключ-значение найдена, заменить ее меткой удаления
+ if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) {
+ Pair *pair = hashMap->buckets[index];
+ free(pair->val);
+ free(pair);
+ hashMap->buckets[index] = hashMap->TOMBSTONE;
+ hashMap->size--;
+ }
+}
+
+/* Расширить хеш-таблицу */
+void extend(HashMapOpenAddressing *hashMap) {
+ // Временно сохранить исходную хеш-таблицу
+ Pair **bucketsTmp = hashMap->buckets;
+ int oldCapacity = hashMap->capacity;
+ // Инициализация новой хеш-таблицы после расширения
+ hashMap->capacity *= hashMap->extendRatio;
+ hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *));
+ hashMap->size = 0;
+ // Перенести пары ключ-значение из исходной хеш-таблицы в новую
+ for (int i = 0; i < oldCapacity; i++) {
+ Pair *pair = bucketsTmp[i];
+ if (pair != NULL && pair != hashMap->TOMBSTONE) {
+ put(hashMap, pair->key, pair->val);
+ free(pair->val);
+ free(pair);
+ }
+ }
+ free(bucketsTmp);
+}
+
+/* Вывести хеш-таблицу */
+void print(HashMapOpenAddressing *hashMap) {
+ for (int i = 0; i < hashMap->capacity; i++) {
+ Pair *pair = hashMap->buckets[i];
+ if (pair == NULL) {
+ printf("NULL\n");
+ } else if (pair == hashMap->TOMBSTONE) {
+ printf("TOMBSTONE\n");
+ } else {
+ printf("%d -> %s\n", pair->key, pair->val);
+ }
+ }
+}
+
+/* Driver Code */
+int main() {
+ // Инициализация хеш-таблицы
+ HashMapOpenAddressing *hashmap = newHashMapOpenAddressing();
+
+ // Операция добавления
+ // Добавить пару (key, val) в хеш-таблицу
+ put(hashmap, 12836, "Сяо Ха");
+ put(hashmap, 15937, "Сяо Ло");
+ put(hashmap, 16750, "Сяо Суань");
+ put(hashmap, 13276, "Сяо Фа");
+ put(hashmap, 10583, "Сяо Я");
+ printf("\nПосле добавления хеш-таблица имеет вид\nКлюч -> Значение\n");
+ print(hashmap);
+
+ // Операция поиска
+ // Передать ключ key в хеш-таблицу и получить значение val
+ char *name = get(hashmap, 13276);
+ printf("\nДля студенческого номера 13276 найдено имя %s\n", name);
+
+ // Операция удаления
+ // Удалить пару (key, val) из хеш-таблицы
+ removeItem(hashmap, 16750);
+ printf("\nПосле удаления 16750 хеш-таблица имеет вид\nКлюч -> Значение\n");
+ print(hashmap);
+
+ // Уничтожить хеш-таблицу
+ delHashMapOpenAddressing(hashmap);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_hashing/simple_hash.c b/ru/codes/c/chapter_hashing/simple_hash.c
new file mode 100644
index 000000000..d4107c0ef
--- /dev/null
+++ b/ru/codes/c/chapter_hashing/simple_hash.c
@@ -0,0 +1,68 @@
+/**
+ * File: simple_hash.c
+ * Created Time: 2023-09-09
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Аддитивное хеширование */
+int addHash(char *key) {
+ long long hash = 0;
+ const int MODULUS = 1000000007;
+ for (int i = 0; i < strlen(key); i++) {
+ hash = (hash + (unsigned char)key[i]) % MODULUS;
+ }
+ return (int)hash;
+}
+
+/* Мультипликативное хеширование */
+int mulHash(char *key) {
+ long long hash = 0;
+ const int MODULUS = 1000000007;
+ for (int i = 0; i < strlen(key); i++) {
+ hash = (31 * hash + (unsigned char)key[i]) % MODULUS;
+ }
+ return (int)hash;
+}
+
+/* XOR-хеширование */
+int xorHash(char *key) {
+ int hash = 0;
+ const int MODULUS = 1000000007;
+
+ for (int i = 0; i < strlen(key); i++) {
+ hash ^= (unsigned char)key[i];
+ }
+ return hash & MODULUS;
+}
+
+/* Хеширование с циклическим сдвигом */
+int rotHash(char *key) {
+ long long hash = 0;
+ const int MODULUS = 1000000007;
+ for (int i = 0; i < strlen(key); i++) {
+ hash = ((hash << 4) ^ (hash >> 28) ^ (unsigned char)key[i]) % MODULUS;
+ }
+
+ return (int)hash;
+}
+
+/* Driver Code */
+int main() {
+ char *key = "Hello Algo";
+
+ int hash = addHash(key);
+ printf("Хеш суммы = %d\n", hash);
+
+ hash = mulHash(key);
+ printf("Хеш произведения = %d\n", hash);
+
+ hash = xorHash(key);
+ printf("XOR-хеш = %d\n", hash);
+
+ hash = rotHash(key);
+ printf("Хеш с циклическим сдвигом = %d\n", hash);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_heap/CMakeLists.txt b/ru/codes/c/chapter_heap/CMakeLists.txt
new file mode 100644
index 000000000..357ec702c
--- /dev/null
+++ b/ru/codes/c/chapter_heap/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_executable(my_heap_test my_heap_test.c)
+add_executable(top_k top_k.c)
diff --git a/ru/codes/c/chapter_heap/my_heap.c b/ru/codes/c/chapter_heap/my_heap.c
new file mode 100644
index 000000000..0cf692ce4
--- /dev/null
+++ b/ru/codes/c/chapter_heap/my_heap.c
@@ -0,0 +1,152 @@
+/**
+ * File: my_heap.c
+ * Created Time: 2023-01-15
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+#define MAX_SIZE 5000
+
+/* Максимальная куча */
+typedef struct {
+ // size обозначает фактическое число элементов
+ int size;
+ // Использовать массив с заранее выделенной памятью, чтобы избежать расширения
+ int data[MAX_SIZE];
+} MaxHeap;
+
+// Объявление функции
+void siftDown(MaxHeap *maxHeap, int i);
+void siftUp(MaxHeap *maxHeap, int i);
+int parent(MaxHeap *maxHeap, int i);
+
+/* Конструктор, строящий кучу по срезу */
+MaxHeap *newMaxHeap(int nums[], int size) {
+ // Поместить все элементы в кучу
+ MaxHeap *maxHeap = (MaxHeap *)malloc(sizeof(MaxHeap));
+ maxHeap->size = size;
+ memcpy(maxHeap->data, nums, size * sizeof(int));
+ for (int i = parent(maxHeap, size - 1); i >= 0; i--) {
+ // Выполнить heapify для всех узлов, кроме листовых
+ siftDown(maxHeap, i);
+ }
+ return maxHeap;
+}
+
+/* Деструктор */
+void delMaxHeap(MaxHeap *maxHeap) {
+ // Освободить память
+ free(maxHeap);
+}
+
+/* Получить индекс левого дочернего узла */
+int left(MaxHeap *maxHeap, int i) {
+ return 2 * i + 1;
+}
+
+/* Получить индекс правого дочернего узла */
+int right(MaxHeap *maxHeap, int i) {
+ return 2 * i + 2;
+}
+
+/* Получить индекс родительского узла */
+int parent(MaxHeap *maxHeap, int i) {
+ return (i - 1) / 2; // Округление вниз
+}
+
+/* Поменять элементы местами */
+void swap(MaxHeap *maxHeap, int i, int j) {
+ int temp = maxHeap->data[i];
+ maxHeap->data[i] = maxHeap->data[j];
+ maxHeap->data[j] = temp;
+}
+
+/* Получение размера кучи */
+int size(MaxHeap *maxHeap) {
+ return maxHeap->size;
+}
+
+/* Проверка, пуста ли куча */
+int isEmpty(MaxHeap *maxHeap) {
+ return maxHeap->size == 0;
+}
+
+/* Доступ к элементу на вершине кучи */
+int peek(MaxHeap *maxHeap) {
+ return maxHeap->data[0];
+}
+
+/* Добавление элемента в кучу */
+void push(MaxHeap *maxHeap, int val) {
+ // По умолчанию не следует добавлять так много узлов
+ if (maxHeap->size == MAX_SIZE) {
+ printf("heap is full!");
+ return;
+ }
+ // Добавление узла
+ maxHeap->data[maxHeap->size] = val;
+ maxHeap->size++;
+
+ // Просеивание снизу вверх
+ siftUp(maxHeap, maxHeap->size - 1);
+}
+
+/* Извлечение элемента из кучи */
+int pop(MaxHeap *maxHeap) {
+ // Обработка пустого случая
+ if (isEmpty(maxHeap)) {
+ printf("heap is empty!");
+ return INT_MAX;
+ }
+ // Поменять корневой узел с самым правым листом местами (поменять первый и последний элементы)
+ swap(maxHeap, 0, size(maxHeap) - 1);
+ // Удаление узла
+ int val = maxHeap->data[maxHeap->size - 1];
+ maxHeap->size--;
+ // Просеивание сверху вниз
+ siftDown(maxHeap, 0);
+
+ // Вернуть элемент с вершины кучи
+ return val;
+}
+
+/* Начиная с узла i, выполнить просеивание сверху вниз */
+void siftDown(MaxHeap *maxHeap, int i) {
+ while (true) {
+ // Определить узел с максимальным значением среди i, l и r и обозначить его как max
+ int l = left(maxHeap, i);
+ int r = right(maxHeap, i);
+ int max = i;
+ if (l < size(maxHeap) && maxHeap->data[l] > maxHeap->data[max]) {
+ max = l;
+ }
+ if (r < size(maxHeap) && maxHeap->data[r] > maxHeap->data[max]) {
+ max = r;
+ }
+ // Если узел i уже максимален или индексы l и r вне границ, дальнейшее просеивание не требуется, выйти
+ if (max == i) {
+ break;
+ }
+ // Поменять два узла местами
+ swap(maxHeap, i, max);
+ // Циклическое просеивание вниз
+ i = max;
+ }
+}
+
+/* Начиная с узла i, выполнить просеивание снизу вверх */
+void siftUp(MaxHeap *maxHeap, int i) {
+ while (true) {
+ // Получение родительского узла для узла i
+ int p = parent(maxHeap, i);
+ // Завершить heapify, когда «корневой узел уже пройден» или «узел не требует исправления»
+ if (p < 0 || maxHeap->data[i] <= maxHeap->data[p]) {
+ break;
+ }
+ // Поменять два узла местами
+ swap(maxHeap, i, p);
+ // Циклическое просеивание вверх
+ i = p;
+ }
+}
diff --git a/ru/codes/c/chapter_heap/my_heap_test.c b/ru/codes/c/chapter_heap/my_heap_test.c
new file mode 100644
index 000000000..670358033
--- /dev/null
+++ b/ru/codes/c/chapter_heap/my_heap_test.c
@@ -0,0 +1,41 @@
+/**
+ * File: my_heap_test.c
+ * Created Time: 2023-01-15
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "my_heap.c"
+
+/* Driver Code */
+int main() {
+ /* Инициализация кучи */
+ // Инициализация максимальной кучи
+ int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2};
+ MaxHeap *maxHeap = newMaxHeap(nums, sizeof(nums) / sizeof(int));
+ printf("После построения кучи из входного массива\n");
+ printHeap(maxHeap->data, maxHeap->size);
+
+ /* Получение элемента с вершины кучи */
+ printf("\nВерхний элемент кучи = %d\n", peek(maxHeap));
+
+ /* Добавление элемента в кучу */
+ push(maxHeap, 7);
+ printf("\nПосле добавления элемента 7 в кучу\n");
+ printHeap(maxHeap->data, maxHeap->size);
+
+ /* Извлечение элемента с вершины кучи */
+ int top = pop(maxHeap);
+ printf("\nПосле извлечения верхнего элемента %d из кучи\n", top);
+ printHeap(maxHeap->data, maxHeap->size);
+
+ /* Получение размера кучи */
+ printf("\nКоличество элементов в куче = %d\n", size(maxHeap));
+
+ /* Проверка, пуста ли куча */
+ printf("\nПуста ли куча: %d\n", isEmpty(maxHeap));
+
+ // Освободить память
+ delMaxHeap(maxHeap);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_heap/top_k.c b/ru/codes/c/chapter_heap/top_k.c
new file mode 100644
index 000000000..2f5ce1cd3
--- /dev/null
+++ b/ru/codes/c/chapter_heap/top_k.c
@@ -0,0 +1,73 @@
+/**
+ * File: top_k.c
+ * Created Time: 2023-10-26
+ * Author: krahets (krahets163.com)
+ */
+
+#include "my_heap.c"
+
+/* Добавление элемента в кучу */
+void pushMinHeap(MaxHeap *maxHeap, int val) {
+ // Инвертировать знак элемента
+ push(maxHeap, -val);
+}
+
+/* Извлечение элемента из кучи */
+int popMinHeap(MaxHeap *maxHeap) {
+ // Инвертировать знак элемента
+ return -pop(maxHeap);
+}
+
+/* Доступ к элементу на вершине кучи */
+int peekMinHeap(MaxHeap *maxHeap) {
+ // Инвертировать знак элемента
+ return -peek(maxHeap);
+}
+
+/* Извлечь элементы из кучи */
+int *getMinHeap(MaxHeap *maxHeap) {
+ // Инвертировать все элементы кучи и записать их в массив res
+ int *res = (int *)malloc(maxHeap->size * sizeof(int));
+ for (int i = 0; i < maxHeap->size; i++) {
+ res[i] = -maxHeap->data[i];
+ }
+ return res;
+}
+
+// Функция поиска k наибольших элементов массива на основе кучи
+int *topKHeap(int *nums, int sizeNums, int k) {
+ // Инициализация минимальной кучи
+ // Обратите внимание: мы инвертируем все элементы кучи, чтобы с помощью максимальной кучи имитировать минимальную
+ int *empty = (int *)malloc(0);
+ MaxHeap *maxHeap = newMaxHeap(empty, 0);
+ // Поместить первые k элементов массива в кучу
+ for (int i = 0; i < k; i++) {
+ pushMinHeap(maxHeap, nums[i]);
+ }
+ // Начиная с элемента k+1, поддерживать длину кучи равной k
+ for (int i = k; i < sizeNums; i++) {
+ // Если текущий элемент больше элемента на вершине кучи, извлечь вершину кучи и добавить текущий элемент в кучу
+ if (nums[i] > peekMinHeap(maxHeap)) {
+ popMinHeap(maxHeap);
+ pushMinHeap(maxHeap, nums[i]);
+ }
+ }
+ int *res = getMinHeap(maxHeap);
+ // Освободить память
+ delMaxHeap(maxHeap);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {1, 7, 6, 3, 2};
+ int k = 3;
+ int sizeNums = sizeof(nums) / sizeof(nums[0]);
+
+ int *res = topKHeap(nums, sizeNums, k);
+ printf("Наибольшие %d элементов: ", k);
+ printArray(res, k);
+
+ free(res);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_searching/CMakeLists.txt b/ru/codes/c/chapter_searching/CMakeLists.txt
new file mode 100644
index 000000000..7b2a152d5
--- /dev/null
+++ b/ru/codes/c/chapter_searching/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(binary_search binary_search.c)
+add_executable(two_sum two_sum.c)
+add_executable(binary_search_edge binary_search_edge.c)
+add_executable(binary_search_insertion binary_search_insertion.c)
diff --git a/ru/codes/c/chapter_searching/binary_search.c b/ru/codes/c/chapter_searching/binary_search.c
new file mode 100644
index 000000000..250cb4e8c
--- /dev/null
+++ b/ru/codes/c/chapter_searching/binary_search.c
@@ -0,0 +1,59 @@
+/**
+ * File: binary_search.c
+ * Created Time: 2023-03-18
+ * Author: Guanngxu (446678850@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Бинарный поиск (двусторонне замкнутый интервал) */
+int binarySearch(int *nums, int len, int target) {
+ // Инициализировать двусторонне замкнутый интервал [0, n-1], то есть i и j указывают на первый и последний элементы массива соответственно
+ int i = 0, j = len - 1;
+ // Цикл завершается, когда диапазон поиска пуст (при i > j диапазон пуст)
+ while (i <= j) {
+ int m = i + (j - i) / 2; // Вычислить индекс середины m
+ if (nums[m] < target) // Это означает, что target находится в интервале [m+1, j]
+ i = m + 1;
+ else if (nums[m] > target) // Это означает, что target находится в интервале [i, m-1]
+ j = m - 1;
+ else // Целевой элемент найден, вернуть его индекс
+ return m;
+ }
+ // Целевой элемент не найден, вернуть -1
+ return -1;
+}
+
+/* Бинарный поиск (лево замкнутый, право открытый интервал) */
+int binarySearchLCRO(int *nums, int len, int target) {
+ // Инициализировать лево замкнутый, право открытый интервал [0, n), то есть i и j указывают на первый элемент массива и позицию сразу за последним элементом соответственно
+ int i = 0, j = len;
+ // Цикл завершается, когда диапазон поиска пуст (при i = j диапазон пуст)
+ while (i < j) {
+ int m = i + (j - i) / 2; // Вычислить индекс середины m
+ if (nums[m] < target) // Это означает, что target находится в интервале [m+1, j)
+ i = m + 1;
+ else if (nums[m] > target) // Это означает, что target находится в интервале [i, m)
+ j = m;
+ else // Целевой элемент найден, вернуть его индекс
+ return m;
+ }
+ // Целевой элемент не найден, вернуть -1
+ return -1;
+}
+
+/* Driver Code */
+int main() {
+ int target = 6;
+ int nums[10] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
+
+ /* Бинарный поиск (двусторонне замкнутый интервал) */
+ int index = binarySearch(nums, 10, target);
+ printf("Индекс целевого элемента 6 = %d\n", index);
+
+ /* Бинарный поиск (лево замкнутый, право открытый интервал) */
+ index = binarySearchLCRO(nums, 10, target);
+ printf("Индекс целевого элемента 6 = %d\n", index);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_searching/binary_search_edge.c b/ru/codes/c/chapter_searching/binary_search_edge.c
new file mode 100644
index 000000000..71dbef1bb
--- /dev/null
+++ b/ru/codes/c/chapter_searching/binary_search_edge.c
@@ -0,0 +1,67 @@
+/**
+ * File: binary_search_edge.c
+ * Created Time: 2023-09-09
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Бинарный поиск точки вставки (с повторяющимися элементами) */
+int binarySearchInsertion(int *nums, int numSize, int target) {
+ int i = 0, j = numSize - 1; // Инициализировать двусторонне замкнутый интервал [0, n-1]
+ while (i <= j) {
+ int m = i + (j - i) / 2; // Вычислить индекс середины m
+ if (nums[m] < target) {
+ i = m + 1; // target находится в интервале [m+1, j]
+ } else {
+ j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1]
+ }
+ }
+ // Вернуть точку вставки i
+ return i;
+}
+
+/* Бинарный поиск самого левого target */
+int binarySearchLeftEdge(int *nums, int numSize, int target) {
+ // Эквивалентно поиску точки вставки target
+ int i = binarySearchInsertion(nums, numSize, target);
+ // target не найден, вернуть -1
+ if (i == numSize || nums[i] != target) {
+ return -1;
+ }
+ // Найти target и вернуть индекс i
+ return i;
+}
+
+/* Бинарный поиск самого правого target */
+int binarySearchRightEdge(int *nums, int numSize, int target) {
+ // Преобразовать задачу в поиск самого левого target + 1
+ int i = binarySearchInsertion(nums, numSize, target + 1);
+ // j указывает на самый правый target, а i — на первый элемент больше target
+ int j = i - 1;
+ // target не найден, вернуть -1
+ if (j == -1 || nums[j] != target) {
+ return -1;
+ }
+ // Найти target и вернуть индекс j
+ return j;
+}
+
+/* Driver Code */
+int main() {
+ // Массив с повторяющимися элементами
+ int nums[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15};
+ printf("\nМассив nums = ");
+ printArray(nums, sizeof(nums) / sizeof(nums[0]));
+
+ // Бинарный поиск левой и правой границы
+ int targets[] = {6, 7};
+ for (int i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) {
+ int index = binarySearchLeftEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]);
+ printf("Индекс самого левого элемента %d = %d\n", targets[i], index);
+ index = binarySearchRightEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]);
+ printf("Индекс самого правого элемента %d = %d\n", targets[i], index);
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_searching/binary_search_insertion.c b/ru/codes/c/chapter_searching/binary_search_insertion.c
new file mode 100644
index 000000000..3d99ba0f7
--- /dev/null
+++ b/ru/codes/c/chapter_searching/binary_search_insertion.c
@@ -0,0 +1,68 @@
+/**
+ * File: binary_search_insertion.c
+ * Created Time: 2023-09-09
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Бинарный поиск точки вставки (без повторяющихся элементов) */
+int binarySearchInsertionSimple(int *nums, int numSize, int target) {
+ int i = 0, j = numSize - 1; // Инициализировать двусторонне замкнутый интервал [0, n-1]
+ while (i <= j) {
+ int m = i + (j - i) / 2; // Вычислить индекс середины m
+ if (nums[m] < target) {
+ i = m + 1; // target находится в интервале [m+1, j]
+ } else if (nums[m] > target) {
+ j = m - 1; // target находится в интервале [i, m-1]
+ } else {
+ return m; // Найти target и вернуть точку вставки m
+ }
+ }
+ // target не найден, вернуть точку вставки i
+ return i;
+}
+
+/* Бинарный поиск точки вставки (с повторяющимися элементами) */
+int binarySearchInsertion(int *nums, int numSize, int target) {
+ int i = 0, j = numSize - 1; // Инициализировать двусторонне замкнутый интервал [0, n-1]
+ while (i <= j) {
+ int m = i + (j - i) / 2; // Вычислить индекс середины m
+ if (nums[m] < target) {
+ i = m + 1; // target находится в интервале [m+1, j]
+ } else if (nums[m] > target) {
+ j = m - 1; // target находится в интервале [i, m-1]
+ } else {
+ j = m - 1; // Первый элемент меньше target находится в интервале [i, m-1]
+ }
+ }
+ // Вернуть точку вставки i
+ return i;
+}
+
+/* Driver Code */
+int main() {
+ // Массив без повторяющихся элементов
+ int nums1[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
+ printf("\nМассив nums = ");
+ printArray(nums1, sizeof(nums1) / sizeof(nums1[0]));
+ // Бинарный поиск точки вставки
+ int targets1[] = {6, 9};
+ for (int i = 0; i < sizeof(targets1) / sizeof(targets1[0]); i++) {
+ int index = binarySearchInsertionSimple(nums1, sizeof(nums1) / sizeof(nums1[0]), targets1[i]);
+ printf("Индекс позиции вставки для элемента %d = %d\n", targets1[i], index);
+ }
+
+ // Массив с повторяющимися элементами
+ int nums2[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15};
+ printf("\nМассив nums = ");
+ printArray(nums2, sizeof(nums2) / sizeof(nums2[0]));
+ // Бинарный поиск точки вставки
+ int targets2[] = {2, 6, 20};
+ for (int i = 0; i < sizeof(targets2) / sizeof(int); i++) {
+ int index = binarySearchInsertion(nums2, sizeof(nums2) / sizeof(nums2[0]), targets2[i]);
+ printf("Индекс позиции вставки для элемента %d = %d\n", targets2[i], index);
+ }
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_searching/two_sum.c b/ru/codes/c/chapter_searching/two_sum.c
new file mode 100644
index 000000000..e9b3ce103
--- /dev/null
+++ b/ru/codes/c/chapter_searching/two_sum.c
@@ -0,0 +1,86 @@
+/**
+ * File: two_sum.c
+ * Created Time: 2023-01-19
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Метод 1: полный перебор */
+int *twoSumBruteForce(int *nums, int numsSize, int target, int *returnSize) {
+ for (int i = 0; i < numsSize; ++i) {
+ for (int j = i + 1; j < numsSize; ++j) {
+ if (nums[i] + nums[j] == target) {
+ int *res = malloc(sizeof(int) * 2);
+ res[0] = i, res[1] = j;
+ *returnSize = 2;
+ return res;
+ }
+ }
+ }
+ *returnSize = 0;
+ return NULL;
+}
+
+/* Хеш-таблица */
+typedef struct {
+ int key;
+ int val;
+ UT_hash_handle hh; // Реализовано на основе uthash.h
+} HashTable;
+
+/* Поиск в хеш-таблице */
+HashTable *find(HashTable *h, int key) {
+ HashTable *tmp;
+ HASH_FIND_INT(h, &key, tmp);
+ return tmp;
+}
+
+/* Вставка элемента в хеш-таблицу */
+void insert(HashTable **h, int key, int val) {
+ HashTable *t = find(*h, key);
+ if (t == NULL) {
+ HashTable *tmp = malloc(sizeof(HashTable));
+ tmp->key = key, tmp->val = val;
+ HASH_ADD_INT(*h, key, tmp);
+ } else {
+ t->val = val;
+ }
+}
+
+/* Метод 2: вспомогательная хеш-таблица */
+int *twoSumHashTable(int *nums, int numsSize, int target, int *returnSize) {
+ HashTable *hashtable = NULL;
+ for (int i = 0; i < numsSize; i++) {
+ HashTable *t = find(hashtable, target - nums[i]);
+ if (t != NULL) {
+ int *res = malloc(sizeof(int) * 2);
+ res[0] = t->val, res[1] = i;
+ *returnSize = 2;
+ return res;
+ }
+ insert(&hashtable, nums[i], i);
+ }
+ *returnSize = 0;
+ return NULL;
+}
+
+/* Driver Code */
+int main() {
+ // ======= Test Case =======
+ int nums[] = {2, 7, 11, 15};
+ int target = 13;
+ // ====== Driver Code ======
+ int returnSize;
+ int *res = twoSumBruteForce(nums, sizeof(nums) / sizeof(int), target, &returnSize);
+ // Метод 1
+ printf("Способ 1: res = ");
+ printArray(res, returnSize);
+
+ // Метод 2
+ res = twoSumHashTable(nums, sizeof(nums) / sizeof(int), target, &returnSize);
+ printf("Способ 2: res = ");
+ printArray(res, returnSize);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_sorting/CMakeLists.txt b/ru/codes/c/chapter_sorting/CMakeLists.txt
new file mode 100644
index 000000000..88756b4c9
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_executable(bubble_sort bubble_sort.c)
+add_executable(insertion_sort insertion_sort.c)
+add_executable(quick_sort quick_sort.c)
+add_executable(counting_sort counting_sort.c)
+add_executable(radix_sort radix_sort.c)
+add_executable(merge_sort merge_sort.c)
+add_executable(heap_sort heap_sort.c)
+add_executable(bucket_sort bucket_sort.c)
+add_executable(selection_sort selection_sort.c)
diff --git a/ru/codes/c/chapter_sorting/bubble_sort.c b/ru/codes/c/chapter_sorting/bubble_sort.c
new file mode 100644
index 000000000..f5c48dd80
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/bubble_sort.c
@@ -0,0 +1,61 @@
+/**
+ * File: bubble_sort.c
+ * Created Time: 2022-12-26
+ * Author: Listening (https://github.com/L-Super)
+ */
+
+#include "../utils/common.h"
+
+/* Пузырьковая сортировка */
+void bubbleSort(int nums[], int size) {
+ // Внешний цикл: неотсортированный диапазон [0, i]
+ for (int i = size - 1; i > 0; i--) {
+ // Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
+ for (int j = 0; j < i; j++) {
+ if (nums[j] > nums[j + 1]) {
+ int temp = nums[j];
+ nums[j] = nums[j + 1];
+ nums[j + 1] = temp;
+ }
+ }
+ }
+}
+
+/* Пузырьковая сортировка (оптимизация флагом) */
+void bubbleSortWithFlag(int nums[], int size) {
+ // Внешний цикл: неотсортированный диапазон [0, i]
+ for (int i = size - 1; i > 0; i--) {
+ bool flag = false;
+ // Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
+ for (int j = 0; j < i; j++) {
+ if (nums[j] > nums[j + 1]) {
+ int temp = nums[j];
+ nums[j] = nums[j + 1];
+ nums[j + 1] = temp;
+ flag = true;
+ }
+ }
+ if (!flag)
+ break;
+ }
+}
+
+/* Driver Code */
+int main() {
+ int nums[6] = {4, 1, 3, 1, 5, 2};
+ printf("После пузырьковой сортировки: ");
+ bubbleSort(nums, 6);
+ for (int i = 0; i < 6; i++) {
+ printf("%d ", nums[i]);
+ }
+
+ int nums1[6] = {4, 1, 3, 1, 5, 2};
+ printf("\nПосле оптимизированной пузырьковой сортировки: ");
+ bubbleSortWithFlag(nums1, 6);
+ for (int i = 0; i < 6; i++) {
+ printf("%d ", nums1[i]);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_sorting/bucket_sort.c b/ru/codes/c/chapter_sorting/bucket_sort.c
new file mode 100644
index 000000000..2f54d502a
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/bucket_sort.c
@@ -0,0 +1,57 @@
+/**
+ * File: bucket_sort.c
+ * Created Time: 2023-05-30
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+#define SIZE 10
+
+/* Функция сравнения для qsort */
+int compare(const void *a, const void *b) {
+ float fa = *(const float *)a;
+ float fb = *(const float *)b;
+ return (fa > fb) - (fa < fb);
+}
+
+/* Сортировка корзинами */
+void bucketSort(float nums[], int n) {
+ int k = n / 2; // Инициализировать k = n/2 корзин
+ int *sizes = malloc(k * sizeof(int)); // Записать размер каждой корзины
+ float **buckets = malloc(k * sizeof(float *)); // Массив динамических массивов (корзины)
+ // Предварительно выделить достаточно места для каждой корзины
+ for (int i = 0; i < k; ++i) {
+ buckets[i] = (float *)malloc(n * sizeof(float));
+ sizes[i] = 0;
+ }
+ // 1. Распределить элементы массива по корзинам
+ for (int i = 0; i < n; ++i) {
+ int idx = (int)(nums[i] * k);
+ buckets[idx][sizes[idx]++] = nums[i];
+ }
+ // 2. Выполнить сортировку внутри каждой корзины
+ for (int i = 0; i < k; ++i) {
+ qsort(buckets[i], sizes[i], sizeof(float), compare);
+ }
+ // 3. Объединить отсортированные корзины
+ int idx = 0;
+ for (int i = 0; i < k; ++i) {
+ for (int j = 0; j < sizes[i]; ++j) {
+ nums[idx++] = buckets[i][j];
+ }
+ // Освободить память
+ free(buckets[i]);
+ }
+}
+
+/* Driver Code */
+int main() {
+ // Пусть входные данные — числа с плавающей точкой из диапазона [0, 1)
+ float nums[SIZE] = {0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f};
+ bucketSort(nums, SIZE);
+ printf("После сортировки корзинами nums = ");
+ printArrayFloat(nums, SIZE);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_sorting/counting_sort.c b/ru/codes/c/chapter_sorting/counting_sort.c
new file mode 100644
index 000000000..5aba17388
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/counting_sort.c
@@ -0,0 +1,87 @@
+/**
+ * File: counting_sort.c
+ * Created Time: 2023-03-20
+ * Author: Reanon (793584285@qq.com), Guanngxu (446678850@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Сортировка подсчетом */
+// Простая реализация, не подходит для сортировки объектов
+void countingSortNaive(int nums[], int size) {
+ // 1. Найти максимальный элемент массива m
+ int m = 0;
+ for (int i = 0; i < size; i++) {
+ if (nums[i] > m) {
+ m = nums[i];
+ }
+ }
+ // 2. Подсчитать число появлений каждой цифры
+ // counter[num] обозначает число появлений num
+ int *counter = calloc(m + 1, sizeof(int));
+ for (int i = 0; i < size; i++) {
+ counter[nums[i]]++;
+ }
+ // 3. Обойти counter и заполнить исходный массив nums элементами
+ int i = 0;
+ for (int num = 0; num < m + 1; num++) {
+ for (int j = 0; j < counter[num]; j++, i++) {
+ nums[i] = num;
+ }
+ }
+ // 4. Освободить память
+ free(counter);
+}
+
+/* Сортировка подсчетом */
+// Полная реализация, позволяет сортировать объекты и является стабильной сортировкой
+void countingSort(int nums[], int size) {
+ // 1. Найти максимальный элемент массива m
+ int m = 0;
+ for (int i = 0; i < size; i++) {
+ if (nums[i] > m) {
+ m = nums[i];
+ }
+ }
+ // 2. Подсчитать число появлений каждой цифры
+ // counter[num] обозначает число появлений num
+ int *counter = calloc(m, sizeof(int));
+ for (int i = 0; i < size; i++) {
+ counter[nums[i]]++;
+ }
+ // 3. Вычислить префиксные суммы counter и преобразовать «число появлений» в «конечный индекс»
+ // То есть counter[num]-1 — это индекс последнего появления num в res
+ for (int i = 0; i < m; i++) {
+ counter[i + 1] += counter[i];
+ }
+ // 4. Обойти nums в обратном порядке и поместить элементы в результирующий массив res
+ // Инициализировать массив res для хранения результата
+ int *res = malloc(sizeof(int) * size);
+ for (int i = size - 1; i >= 0; i--) {
+ int num = nums[i];
+ res[counter[num] - 1] = num; // Поместить num по соответствующему индексу
+ counter[num]--; // Уменьшить префиксную сумму на 1, чтобы получить индекс следующего размещения num
+ }
+ // Перезаписать исходный массив nums массивом результата res
+ memcpy(nums, res, size * sizeof(int));
+ // 5. Освободить память
+ free(res);
+ free(counter);
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4};
+ int size = sizeof(nums) / sizeof(int);
+ countingSortNaive(nums, size);
+ printf("После сортировки подсчетом (объекты не поддерживаются) nums = ");
+ printArray(nums, size);
+
+ int nums1[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4};
+ int size1 = sizeof(nums1) / sizeof(int);
+ countingSort(nums1, size1);
+ printf("После сортировки подсчетом nums1 = ");
+ printArray(nums1, size1);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_sorting/heap_sort.c b/ru/codes/c/chapter_sorting/heap_sort.c
new file mode 100644
index 000000000..c5fe1d5a1
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/heap_sort.c
@@ -0,0 +1,60 @@
+/**
+ * File: heap_sort.c
+ * Created Time: 2023-05-30
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Длина кучи равна n; начиная с узла i, выполнить просеивание сверху вниз */
+void siftDown(int nums[], int n, int i) {
+ while (1) {
+ // Определить узел с максимальным значением среди i, l и r и обозначить его как ma
+ int l = 2 * i + 1;
+ int r = 2 * i + 2;
+ int ma = i;
+ if (l < n && nums[l] > nums[ma])
+ ma = l;
+ if (r < n && nums[r] > nums[ma])
+ ma = r;
+ // Если узел i уже максимален или индексы l и r вне границ, дальнейшее просеивание не требуется, выйти
+ if (ma == i) {
+ break;
+ }
+ // Поменять два узла местами
+ int temp = nums[i];
+ nums[i] = nums[ma];
+ nums[ma] = temp;
+ // Циклическое просеивание вниз
+ i = ma;
+ }
+}
+
+/* Сортировка кучей */
+void heapSort(int nums[], int n) {
+ // Построение кучи: выполнить heapify для всех узлов, кроме листовых
+ for (int i = n / 2 - 1; i >= 0; --i) {
+ siftDown(nums, n, i);
+ }
+ // Извлекать максимальный элемент из кучи в течение n-1 итераций
+ for (int i = n - 1; i > 0; --i) {
+ // Поменять корневой узел с самым правым листом местами (поменять первый и последний элементы)
+ int tmp = nums[0];
+ nums[0] = nums[i];
+ nums[i] = tmp;
+ // Начиная с корневого узла, выполнить просеивание сверху вниз
+ siftDown(nums, i, 0);
+ }
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {4, 1, 3, 1, 5, 2};
+ int n = sizeof(nums) / sizeof(nums[0]);
+
+ heapSort(nums, n);
+ printf("После сортировки кучей nums = ");
+ printArray(nums, n);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_sorting/insertion_sort.c b/ru/codes/c/chapter_sorting/insertion_sort.c
new file mode 100644
index 000000000..a30f3b1bf
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/insertion_sort.c
@@ -0,0 +1,36 @@
+/**
+ * File: insertion_sort.c
+ * Created Time: 2022-12-29
+ * Author: Listening (https://github.com/L-Super)
+ */
+
+#include "../utils/common.h"
+
+/* Сортировка вставками */
+void insertionSort(int nums[], int size) {
+ // Внешний цикл: отсортированный диапазон [0, i-1]
+ for (int i = 1; i < size; i++) {
+ int base = nums[i], j = i - 1;
+ // Внутренний цикл: вставить base в правильную позицию отсортированного диапазона [0, i-1]
+ while (j >= 0 && nums[j] > base) {
+ // Сдвинуть nums[j] на одну позицию вправо
+ nums[j + 1] = nums[j];
+ j--;
+ }
+ // Поместить base в правильную позицию
+ nums[j + 1] = base;
+ }
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {4, 1, 3, 1, 5, 2};
+ insertionSort(nums, 6);
+ printf("После сортировки вставками nums = ");
+ for (int i = 0; i < 6; i++) {
+ printf("%d ", nums[i]);
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_sorting/merge_sort.c b/ru/codes/c/chapter_sorting/merge_sort.c
new file mode 100644
index 000000000..274f2a256
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/merge_sort.c
@@ -0,0 +1,63 @@
+/**
+ * File: merge_sort.c
+ * Created Time: 2022-03-21
+ * Author: Guanngxu (446678850@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Объединить левый и правый подмассивы */
+void merge(int *nums, int left, int mid, int right) {
+ // Диапазон левого подмассива: [left, mid], диапазон правого подмассива: [mid+1, right]
+ // Создать временный массив tmp для хранения результата слияния
+ int tmpSize = right - left + 1;
+ int *tmp = (int *)malloc(tmpSize * sizeof(int));
+ // Инициализировать начальные индексы левого и правого подмассивов
+ int i = left, j = mid + 1, k = 0;
+ // Пока в левом и правом подмассивах еще есть элементы, сравнивать их и копировать меньший во временный массив
+ while (i <= mid && j <= right) {
+ if (nums[i] <= nums[j]) {
+ tmp[k++] = nums[i++];
+ } else {
+ tmp[k++] = nums[j++];
+ }
+ }
+ // Скопировать оставшиеся элементы левого и правого подмассивов во временный массив
+ while (i <= mid) {
+ tmp[k++] = nums[i++];
+ }
+ while (j <= right) {
+ tmp[k++] = nums[j++];
+ }
+ // Скопировать элементы временного массива tmp обратно в соответствующий диапазон исходного массива nums
+ for (k = 0; k < tmpSize; ++k) {
+ nums[left + k] = tmp[k];
+ }
+ // Освободить память
+ free(tmp);
+}
+
+/* Сортировка слиянием */
+void mergeSort(int *nums, int left, int right) {
+ // Условие завершения
+ if (left >= right)
+ return; // Завершить рекурсию, когда длина подмассива равна 1
+ // Этап разбиения
+ int mid = left + (right - left) / 2; // Вычислить середину
+ mergeSort(nums, left, mid); // Рекурсивно обработать левый подмассив
+ mergeSort(nums, mid + 1, right); // Рекурсивно обработать правый подмассив
+ // Этап слияния
+ merge(nums, left, mid, right);
+}
+
+/* Driver Code */
+int main() {
+ /* Сортировка слиянием */
+ int nums[] = {7, 3, 2, 6, 0, 1, 5, 4};
+ int size = sizeof(nums) / sizeof(int);
+ mergeSort(nums, 0, size - 1);
+ printf("После сортировки слиянием nums = ");
+ printArray(nums, size);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_sorting/quick_sort.c b/ru/codes/c/chapter_sorting/quick_sort.c
new file mode 100644
index 000000000..289f7c86b
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/quick_sort.c
@@ -0,0 +1,137 @@
+/**
+ * File: quick_sort.c
+ * Created Time: 2023-01-18
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Обмен элементов */
+void swap(int nums[], int i, int j) {
+ int tmp = nums[i];
+ nums[i] = nums[j];
+ nums[j] = tmp;
+}
+
+/* Разбиение с опорными указателями */
+int partition(int nums[], int left, int right) {
+ // Взять nums[left] в качестве опорного элемента
+ int i = left, j = right;
+ while (i < j) {
+ while (i < j && nums[j] >= nums[left]) {
+ j--; // Идти справа налево в поисках первого элемента меньше опорного
+ }
+ while (i < j && nums[i] <= nums[left]) {
+ i++; // Идти слева направо в поисках первого элемента больше опорного
+ }
+ // Поменять эти два элемента местами
+ swap(nums, i, j);
+ }
+ // Переместить опорный элемент на границу двух подмассивов
+ swap(nums, i, left);
+ // Вернуть индекс опорного элемента
+ return i;
+}
+
+/* Быстрая сортировка */
+void quickSort(int nums[], int left, int right) {
+ // Завершить рекурсию, когда длина подмассива равна 1
+ if (left >= right) {
+ return;
+ }
+ // Разбиение с опорными указателями
+ int pivot = partition(nums, left, right);
+ // Рекурсивно обработать левый и правый подмассивы
+ quickSort(nums, left, pivot - 1);
+ quickSort(nums, pivot + 1, right);
+}
+
+// Ниже приведена быстрая сортировка с оптимизацией медианой
+
+/* Выбрать медиану из трех кандидатов */
+int medianThree(int nums[], int left, int mid, int right) {
+ int l = nums[left], m = nums[mid], r = nums[right];
+ if ((l <= m && m <= r) || (r <= m && m <= l))
+ return mid; // m находится между l и r
+ if ((m <= l && l <= r) || (r <= l && l <= m))
+ return left; // l находится между m и r
+ return right;
+}
+
+/* Разбиение с опорными указателями (медиана трех) */
+int partitionMedian(int nums[], int left, int right) {
+ // Выбрать медиану из трех кандидатов
+ int med = medianThree(nums, left, (left + right) / 2, right);
+ // Переместить медиану в крайний левый элемент массива
+ swap(nums, left, med);
+ // Взять nums[left] в качестве опорного элемента
+ int i = left, j = right;
+ while (i < j) {
+ while (i < j && nums[j] >= nums[left])
+ j--; // Идти справа налево в поисках первого элемента меньше опорного
+ while (i < j && nums[i] <= nums[left])
+ i++; // Идти слева направо в поисках первого элемента больше опорного
+ swap(nums, i, j); // Поменять эти два элемента местами
+ }
+ swap(nums, i, left); // Переместить опорный элемент на границу двух подмассивов
+ return i; // Вернуть индекс опорного элемента
+}
+
+/* Быстрая сортировка (медиана трех) */
+void quickSortMedian(int nums[], int left, int right) {
+ // Завершить рекурсию, когда длина подмассива равна 1
+ if (left >= right)
+ return;
+ // Разбиение с опорными указателями
+ int pivot = partitionMedian(nums, left, right);
+ // Рекурсивно обработать левый и правый подмассивы
+ quickSortMedian(nums, left, pivot - 1);
+ quickSortMedian(nums, pivot + 1, right);
+}
+
+// Ниже приведена быстрая сортировка с оптимизацией глубины рекурсии
+
+/* Быстрая сортировка (оптимизация глубины рекурсии) */
+void quickSortTailCall(int nums[], int left, int right) {
+ // Завершить, когда длина подмассива равна 1
+ while (left < right) {
+ // Операция разбиения с опорными указателями
+ int pivot = partition(nums, left, right);
+ // Выполнить быструю сортировку для более короткого из двух подмассивов
+ if (pivot - left < right - pivot) {
+ // Рекурсивно отсортировать левый подмассив
+ quickSortTailCall(nums, left, pivot - 1);
+ // Оставшийся неотсортированный диапазон: [pivot + 1, right]
+ left = pivot + 1;
+ } else {
+ // Рекурсивно отсортировать правый подмассив
+ quickSortTailCall(nums, pivot + 1, right);
+ // Оставшийся неотсортированный диапазон: [left, pivot - 1]
+ right = pivot - 1;
+ }
+ }
+}
+
+/* Driver Code */
+int main() {
+ /* Быстрая сортировка */
+ int nums[] = {2, 4, 1, 0, 3, 5};
+ int size = sizeof(nums) / sizeof(int);
+ quickSort(nums, 0, size - 1);
+ printf("После быстрой сортировки nums = ");
+ printArray(nums, size);
+
+ /* Быстрая сортировка (оптимизация медианным опорным элементом) */
+ int nums1[] = {2, 4, 1, 0, 3, 5};
+ quickSortMedian(nums1, 0, size - 1);
+ printf("После быстрой сортировки (оптимизация медианным опорным элементом) nums = ");
+ printArray(nums1, size);
+
+ /* Быстрая сортировка (оптимизация глубины рекурсии) */
+ int nums2[] = {2, 4, 1, 0, 3, 5};
+ quickSortTailCall(nums2, 0, size - 1);
+ printf("После быстрой сортировки (оптимизация глубины рекурсии) nums = ");
+ printArray(nums1, size);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_sorting/radix_sort.c b/ru/codes/c/chapter_sorting/radix_sort.c
new file mode 100644
index 000000000..994084243
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/radix_sort.c
@@ -0,0 +1,75 @@
+/**
+ * File: radix_sort.c
+ * Created Time: 2023-01-18
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Получить k-й разряд элемента num, где exp = 10^(k-1) */
+int digit(int num, int exp) {
+ // Передача exp вместо k позволяет избежать повторного дорогостоящего вычисления степени
+ return (num / exp) % 10;
+}
+
+/* Сортировка подсчетом (сортировка по k-му разряду nums) */
+void countingSortDigit(int nums[], int size, int exp) {
+ // Разряды десятичной системы лежат в диапазоне 0~9, поэтому нужен массив корзин длины 10
+ int *counter = (int *)malloc((sizeof(int) * 10));
+ memset(counter, 0, sizeof(int) * 10); // Инициализировать нулем для последующего освобождения памяти
+ // Подсчитать число появлений каждой цифры от 0 до 9
+ for (int i = 0; i < size; i++) {
+ // Получить k-й разряд nums[i], обозначив его как d
+ int d = digit(nums[i], exp);
+ // Подсчитать число появлений цифры d
+ counter[d]++;
+ }
+ // Вычислить префиксные суммы и преобразовать «число появлений» в «индекс массива»
+ for (int i = 1; i < 10; i++) {
+ counter[i] += counter[i - 1];
+ }
+ // Выполняя обратный проход, заполнить res элементами по статистике в корзинах
+ int *res = (int *)malloc(sizeof(int) * size);
+ for (int i = size - 1; i >= 0; i--) {
+ int d = digit(nums[i], exp);
+ int j = counter[d] - 1; // Получить индекс j цифры d в массиве
+ res[j] = nums[i]; // Поместить текущий элемент по индексу j
+ counter[d]--; // Уменьшить количество d на 1
+ }
+ // Перезаписать исходный массив nums результатом
+ for (int i = 0; i < size; i++) {
+ nums[i] = res[i];
+ }
+ // Освободить память
+ free(res);
+ free(counter);
+}
+
+/* Поразрядная сортировка */
+void radixSort(int nums[], int size) {
+ // Получить максимальный элемент массива, чтобы определить максимальное число разрядов
+ int max = INT32_MIN;
+ for (int i = 0; i < size; i++) {
+ if (nums[i] > max) {
+ max = nums[i];
+ }
+ }
+ // Проходить разряды от младшего к старшему
+ for (int exp = 1; max >= exp; exp *= 10)
+ // Выполнить сортировку подсчетом по k-му разряду элементов массива
+ // k = 1 -> exp = 1
+ // k = 2 -> exp = 10
+ // то есть exp = 10^(k-1)
+ countingSortDigit(nums, size, exp);
+}
+
+/* Driver Code */
+int main() {
+ // Поразрядная сортировка
+ int nums[] = {10546151, 35663510, 42865989, 34862445, 81883077,
+ 88906420, 72429244, 30524779, 82060337, 63832996};
+ int size = sizeof(nums) / sizeof(int);
+ radixSort(nums, size);
+ printf("После поразрядной сортировки nums = ");
+ printArray(nums, size);
+}
diff --git a/ru/codes/c/chapter_sorting/selection_sort.c b/ru/codes/c/chapter_sorting/selection_sort.c
new file mode 100644
index 000000000..9fb799cb0
--- /dev/null
+++ b/ru/codes/c/chapter_sorting/selection_sort.c
@@ -0,0 +1,37 @@
+/**
+ * File: selection_sort.c
+ * Created Time: 2023-05-31
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Сортировка выбором */
+void selectionSort(int nums[], int n) {
+ // Внешний цикл: неотсортированный диапазон [i, n-1]
+ for (int i = 0; i < n - 1; i++) {
+ // Внутренний цикл: найти минимальный элемент в неотсортированном диапазоне
+ int k = i;
+ for (int j = i + 1; j < n; j++) {
+ if (nums[j] < nums[k])
+ k = j; // Записать индекс минимального элемента
+ }
+ // Поменять этот минимальный элемент местами с первым элементом неотсортированного диапазона
+ int temp = nums[i];
+ nums[i] = nums[k];
+ nums[k] = temp;
+ }
+}
+
+/* Driver Code */
+int main() {
+ int nums[] = {4, 1, 3, 1, 5, 2};
+ int n = sizeof(nums) / sizeof(nums[0]);
+
+ selectionSort(nums, n);
+
+ printf("После сортировки выбором nums = ");
+ printArray(nums, n);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_stack_and_queue/CMakeLists.txt b/ru/codes/c/chapter_stack_and_queue/CMakeLists.txt
new file mode 100644
index 000000000..ed3ba840c
--- /dev/null
+++ b/ru/codes/c/chapter_stack_and_queue/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_executable(array_stack array_stack.c)
+add_executable(linkedlist_stack linkedlist_stack.c)
+add_executable(array_queue array_queue.c)
+add_executable(linkedlist_queue linkedlist_queue.c)
+add_executable(array_deque array_deque.c)
+add_executable(linkedlist_deque linkedlist_deque.c)
diff --git a/ru/codes/c/chapter_stack_and_queue/array_deque.c b/ru/codes/c/chapter_stack_and_queue/array_deque.c
new file mode 100644
index 000000000..1e72fc628
--- /dev/null
+++ b/ru/codes/c/chapter_stack_and_queue/array_deque.c
@@ -0,0 +1,172 @@
+/**
+ * File: array_deque.c
+ * Created Time: 2023-03-13
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Двусторонняя очередь на основе кольцевого массива */
+typedef struct {
+ int *nums; // Массив для хранения элементов очереди
+ int front; // Указатель head, указывающий на первый элемент очереди
+ int queSize; // Указатель хвоста, указывающий на позицию после хвоста
+ int queCapacity; // Вместимость очереди
+} ArrayDeque;
+
+/* Конструктор */
+ArrayDeque *newArrayDeque(int capacity) {
+ ArrayDeque *deque = (ArrayDeque *)malloc(sizeof(ArrayDeque));
+ // Инициализация массива
+ deque->queCapacity = capacity;
+ deque->nums = (int *)malloc(sizeof(int) * deque->queCapacity);
+ deque->front = deque->queSize = 0;
+ return deque;
+}
+
+/* Деструктор */
+void delArrayDeque(ArrayDeque *deque) {
+ free(deque->nums);
+ free(deque);
+}
+
+/* Получить вместимость двусторонней очереди */
+int capacity(ArrayDeque *deque) {
+ return deque->queCapacity;
+}
+
+/* Получение длины двусторонней очереди */
+int size(ArrayDeque *deque) {
+ return deque->queSize;
+}
+
+/* Проверка, пуста ли двусторонняя очередь */
+bool empty(ArrayDeque *deque) {
+ return deque->queSize == 0;
+}
+
+/* Вычислить индекс в кольцевом массиве */
+int dequeIndex(ArrayDeque *deque, int i) {
+ // С помощью операции взятия остатка соединить начало и конец массива
+ // Когда i выходит за хвост массива, вернуться к началу
+ // Когда i выходит за голову массива, вернуться к концу
+ return ((i + capacity(deque)) % capacity(deque));
+}
+
+/* Добавление в голову очереди */
+void pushFirst(ArrayDeque *deque, int num) {
+ if (deque->queSize == capacity(deque)) {
+ printf("Дек заполнен\r\n");
+ return;
+ }
+ // Указатель головы сместить влево на одну позицию
+ // С помощью операции взятия остатка реализовать возврат front к хвосту после выхода за начало массива
+ deque->front = dequeIndex(deque, deque->front - 1);
+ // Добавить num в голову очереди
+ deque->nums[deque->front] = num;
+ deque->queSize++;
+}
+
+/* Добавление в хвост очереди */
+void pushLast(ArrayDeque *deque, int num) {
+ if (deque->queSize == capacity(deque)) {
+ printf("Дек заполнен\r\n");
+ return;
+ }
+ // Вычислить указатель хвоста, указывающий на индекс хвоста + 1
+ int rear = dequeIndex(deque, deque->front + deque->queSize);
+ // Добавить num в хвост очереди
+ deque->nums[rear] = num;
+ deque->queSize++;
+}
+
+/* Доступ к элементу в начале очереди */
+int peekFirst(ArrayDeque *deque) {
+ // Ошибка доступа: двусторонняя очередь пуста
+ assert(empty(deque) == 0);
+ return deque->nums[deque->front];
+}
+
+/* Доступ к элементу в конце очереди */
+int peekLast(ArrayDeque *deque) {
+ // Ошибка доступа: двусторонняя очередь пуста
+ assert(empty(deque) == 0);
+ int last = dequeIndex(deque, deque->front + deque->queSize - 1);
+ return deque->nums[last];
+}
+
+/* Извлечение из головы очереди */
+int popFirst(ArrayDeque *deque) {
+ int num = peekFirst(deque);
+ // Указатель головы сдвигается на одну позицию назад
+ deque->front = dequeIndex(deque, deque->front + 1);
+ deque->queSize--;
+ return num;
+}
+
+/* Извлечение из хвоста очереди */
+int popLast(ArrayDeque *deque) {
+ int num = peekLast(deque);
+ deque->queSize--;
+ return num;
+}
+
+/* Вернуть массив для вывода */
+int *toArray(ArrayDeque *deque, int *queSize) {
+ *queSize = deque->queSize;
+ int *res = (int *)calloc(deque->queSize, sizeof(int));
+ int j = deque->front;
+ for (int i = 0; i < deque->queSize; i++) {
+ res[i] = deque->nums[j % deque->queCapacity];
+ j++;
+ }
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация очереди */
+ int capacity = 10;
+ int queSize;
+ ArrayDeque *deque = newArrayDeque(capacity);
+ pushLast(deque, 3);
+ pushLast(deque, 2);
+ pushLast(deque, 5);
+ printf("Дек deque = ");
+ printArray(toArray(deque, &queSize), queSize);
+
+ /* Доступ к элементу */
+ int peekFirstNum = peekFirst(deque);
+ printf("Элемент в голове peekFirst = %d\r\n", peekFirstNum);
+ int peekLastNum = peekLast(deque);
+ printf("Элемент в хвосте peekLast = %d\r\n", peekLastNum);
+
+ /* Добавление элемента в очередь */
+ pushLast(deque, 4);
+ printf("После вставки элемента 4 в хвост дек = ");
+ printArray(toArray(deque, &queSize), queSize);
+ pushFirst(deque, 1);
+ printf("После вставки элемента 1 в голову дек = ");
+ printArray(toArray(deque, &queSize), queSize);
+
+ /* Извлечение элемента из очереди */
+ int popLastNum = popLast(deque);
+ printf("Извлечен элемент из хвоста = %d, дек после извлечения из хвоста = ", popLastNum);
+ printArray(toArray(deque, &queSize), queSize);
+ int popFirstNum = popFirst(deque);
+ printf("Извлечен элемент из головы = %d, дек после извлечения из головы = ", popFirstNum);
+ printArray(toArray(deque, &queSize), queSize);
+
+ /* Получение длины очереди */
+ int dequeSize = size(deque);
+ printf("Длина дека size = %d\r\n", dequeSize);
+
+ /* Проверка, пуста ли очередь */
+ bool isEmpty = empty(deque);
+ printf("Пуста ли очередь = %s\r\n", isEmpty ? "true" : "false");
+
+ // Освободить память
+ delArrayDeque(deque);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_stack_and_queue/array_queue.c b/ru/codes/c/chapter_stack_and_queue/array_queue.c
new file mode 100644
index 000000000..00e05c0b3
--- /dev/null
+++ b/ru/codes/c/chapter_stack_and_queue/array_queue.c
@@ -0,0 +1,134 @@
+/**
+ * File: array_queue.c
+ * Created Time: 2023-01-28
+ * Author: Zero (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Очередь на основе кольцевого массива */
+typedef struct {
+ int *nums; // Массив для хранения элементов очереди
+ int front; // Указатель head, указывающий на первый элемент очереди
+ int queSize; // Указатель хвоста, указывающий на позицию после хвоста
+ int queCapacity; // Вместимость очереди
+} ArrayQueue;
+
+/* Конструктор */
+ArrayQueue *newArrayQueue(int capacity) {
+ ArrayQueue *queue = (ArrayQueue *)malloc(sizeof(ArrayQueue));
+ // Инициализация массива
+ queue->queCapacity = capacity;
+ queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity);
+ queue->front = queue->queSize = 0;
+ return queue;
+}
+
+/* Деструктор */
+void delArrayQueue(ArrayQueue *queue) {
+ free(queue->nums);
+ free(queue);
+}
+
+/* Получить вместимость очереди */
+int capacity(ArrayQueue *queue) {
+ return queue->queCapacity;
+}
+
+/* Получение длины очереди */
+int size(ArrayQueue *queue) {
+ return queue->queSize;
+}
+
+/* Проверка, пуста ли очередь */
+bool empty(ArrayQueue *queue) {
+ return queue->queSize == 0;
+}
+
+/* Доступ к элементу в начале очереди */
+int peek(ArrayQueue *queue) {
+ assert(size(queue) != 0);
+ return queue->nums[queue->front];
+}
+
+/* Поместить в очередь */
+void push(ArrayQueue *queue, int num) {
+ if (size(queue) == capacity(queue)) {
+ printf("Очередь заполнена\r\n");
+ return;
+ }
+ // Вычислить указатель хвоста, указывающий на индекс хвоста + 1
+ // С помощью операции взятия по модулю вернуть rear к началу после выхода за конец массива
+ int rear = (queue->front + queue->queSize) % queue->queCapacity;
+ // Добавить num в хвост очереди
+ queue->nums[rear] = num;
+ queue->queSize++;
+}
+
+/* Извлечь из очереди */
+int pop(ArrayQueue *queue) {
+ int num = peek(queue);
+ // Указатель head сдвигается на одну позицию назад; если он выходит за конец, то возвращается в начало массива
+ queue->front = (queue->front + 1) % queue->queCapacity;
+ queue->queSize--;
+ return num;
+}
+
+/* Вернуть массив для вывода */
+int *toArray(ArrayQueue *queue, int *queSize) {
+ *queSize = queue->queSize;
+ int *res = (int *)calloc(queue->queSize, sizeof(int));
+ int j = queue->front;
+ for (int i = 0; i < queue->queSize; i++) {
+ res[i] = queue->nums[j % queue->queCapacity];
+ j++;
+ }
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация очереди */
+ int capacity = 10;
+ int queSize;
+ ArrayQueue *queue = newArrayQueue(capacity);
+
+ /* Добавление элемента в очередь */
+ push(queue, 1);
+ push(queue, 3);
+ push(queue, 2);
+ push(queue, 5);
+ push(queue, 4);
+ printf("Очередь queue = ");
+ printArray(toArray(queue, &queSize), queSize);
+
+ /* Доступ к элементу в начале очереди */
+ int peekNum = peek(queue);
+ printf("Элемент в голове peek = %d\r\n", peekNum);
+
+ /* Извлечение элемента из очереди */
+ peekNum = pop(queue);
+ printf("Извлечен элемент из очереди pop = %d, очередь после извлечения = ", peekNum);
+ printArray(toArray(queue, &queSize), queSize);
+
+ /* Получение длины очереди */
+ int queueSize = size(queue);
+ printf("Длина очереди size = %d\r\n", queueSize);
+
+ /* Проверка, пуста ли очередь */
+ bool isEmpty = empty(queue);
+ printf("Пуста ли очередь = %s\r\n", isEmpty ? "true" : "false");
+
+ /* Проверка кольцевого массива */
+ for (int i = 0; i < 10; i++) {
+ push(queue, i);
+ pop(queue);
+ printf("После %d-го цикла enqueue + dequeue queue = ", i);
+ printArray(toArray(queue, &queSize), queSize);
+ }
+
+ // Освободить память
+ delArrayQueue(queue);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/c/chapter_stack_and_queue/array_stack.c b/ru/codes/c/chapter_stack_and_queue/array_stack.c
new file mode 100644
index 000000000..e11bd22f7
--- /dev/null
+++ b/ru/codes/c/chapter_stack_and_queue/array_stack.c
@@ -0,0 +1,103 @@
+/**
+ * File: array_stack.c
+ * Created Time: 2023-01-12
+ * Author: Zero (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+#define MAX_SIZE 5000
+
+/* Стек на основе массива */
+typedef struct {
+ int *data;
+ int size;
+} ArrayStack;
+
+/* Конструктор */
+ArrayStack *newArrayStack() {
+ ArrayStack *stack = malloc(sizeof(ArrayStack));
+ // Инициализировать большую вместимость, чтобы избежать расширения
+ stack->data = malloc(sizeof(int) * MAX_SIZE);
+ stack->size = 0;
+ return stack;
+}
+
+/* Деструктор */
+void delArrayStack(ArrayStack *stack) {
+ free(stack->data);
+ free(stack);
+}
+
+/* Получение длины стека */
+int size(ArrayStack *stack) {
+ return stack->size;
+}
+
+/* Проверка, пуст ли стек */
+bool isEmpty(ArrayStack *stack) {
+ return stack->size == 0;
+}
+
+/* Поместить в стек */
+void push(ArrayStack *stack, int num) {
+ if (stack->size == MAX_SIZE) {
+ printf("Стек заполнен\n");
+ return;
+ }
+ stack->data[stack->size] = num;
+ stack->size++;
+}
+
+/* Доступ к верхнему элементу стека */
+int peek(ArrayStack *stack) {
+ if (stack->size == 0) {
+ printf("стек пуст\n");
+ return INT_MAX;
+ }
+ return stack->data[stack->size - 1];
+}
+
+/* Извлечь из стека */
+int pop(ArrayStack *stack) {
+ int val = peek(stack);
+ stack->size--;
+ return val;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация стека */
+ ArrayStack *stack = newArrayStack();
+
+ /* Помещение элемента в стек */
+ push(stack, 1);
+ push(stack, 3);
+ push(stack, 2);
+ push(stack, 5);
+ push(stack, 4);
+ printf("Стек stack = ");
+ printArray(stack->data, stack->size);
+
+ /* Доступ к верхнему элементу стека */
+ int val = peek(stack);
+ printf("Верхний элемент стека top = %d\n", val);
+
+ /* Извлечение элемента из стека */
+ val = pop(stack);
+ printf("Извлечен элемент из стека pop = %d, стек после извлечения = ", val);
+ printArray(stack->data, stack->size);
+
+ /* Получение длины стека */
+ int size = stack->size;
+ printf("Длина стека size = %d\n", size);
+
+ /* Проверка на пустоту */
+ bool empty = isEmpty(stack);
+ printf("Пуст ли стек = %s\n", empty ? "true" : "false");
+
+ // Освободить память
+ delArrayStack(stack);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_stack_and_queue/linkedlist_deque.c b/ru/codes/c/chapter_stack_and_queue/linkedlist_deque.c
new file mode 100644
index 000000000..eae7185a1
--- /dev/null
+++ b/ru/codes/c/chapter_stack_and_queue/linkedlist_deque.c
@@ -0,0 +1,212 @@
+/**
+ * File: linkedlist_deque.c
+ * Created Time: 2023-03-13
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Узел двусвязного списка */
+typedef struct DoublyListNode {
+ int val; // Значение узла
+ struct DoublyListNode *next; // Узел-преемник
+ struct DoublyListNode *prev; // Узел-предшественник
+} DoublyListNode;
+
+/* Конструктор */
+DoublyListNode *newDoublyListNode(int num) {
+ DoublyListNode *new = (DoublyListNode *)malloc(sizeof(DoublyListNode));
+ new->val = num;
+ new->next = NULL;
+ new->prev = NULL;
+ return new;
+}
+
+/* Деструктор */
+void delDoublyListNode(DoublyListNode *node) {
+ free(node);
+}
+
+/* Двусторонняя очередь на основе двусвязного списка */
+typedef struct {
+ DoublyListNode *front, *rear; // Головной узел front, хвостовой узел rear
+ int queSize; // Длина двусторонней очереди
+} LinkedListDeque;
+
+/* Конструктор */
+LinkedListDeque *newLinkedListDeque() {
+ LinkedListDeque *deque = (LinkedListDeque *)malloc(sizeof(LinkedListDeque));
+ deque->front = NULL;
+ deque->rear = NULL;
+ deque->queSize = 0;
+ return deque;
+}
+
+/* Деструктор */
+void delLinkedListdeque(LinkedListDeque *deque) {
+ // Освободить все узлы
+ for (int i = 0; i < deque->queSize && deque->front != NULL; i++) {
+ DoublyListNode *tmp = deque->front;
+ deque->front = deque->front->next;
+ free(tmp);
+ }
+ // Освободить структуру deque
+ free(deque);
+}
+
+/* Получение длины очереди */
+int size(LinkedListDeque *deque) {
+ return deque->queSize;
+}
+
+/* Проверка, пуста ли очередь */
+bool empty(LinkedListDeque *deque) {
+ return (size(deque) == 0);
+}
+
+/* Поместить в очередь */
+void push(LinkedListDeque *deque, int num, bool isFront) {
+ DoublyListNode *node = newDoublyListNode(num);
+ // Если связный список пуст, пусть front и rear оба указывают на node
+ if (empty(deque)) {
+ deque->front = deque->rear = node;
+ }
+ // Операция добавления в голову очереди
+ else if (isFront) {
+ // Добавить node в голову списка
+ deque->front->prev = node;
+ node->next = deque->front;
+ deque->front = node; // Обновить головной узел
+ }
+ // Операция добавления в хвост очереди
+ else {
+ // Добавить node в хвост списка
+ deque->rear->next = node;
+ node->prev = deque->rear;
+ deque->rear = node;
+ }
+ deque->queSize++; // Обновить длину очереди
+}
+
+/* Добавление в голову очереди */
+void pushFirst(LinkedListDeque *deque, int num) {
+ push(deque, num, true);
+}
+
+/* Добавление в хвост очереди */
+void pushLast(LinkedListDeque *deque, int num) {
+ push(deque, num, false);
+}
+
+/* Доступ к элементу в начале очереди */
+int peekFirst(LinkedListDeque *deque) {
+ assert(size(deque) && deque->front);
+ return deque->front->val;
+}
+
+/* Доступ к элементу в конце очереди */
+int peekLast(LinkedListDeque *deque) {
+ assert(size(deque) && deque->rear);
+ return deque->rear->val;
+}
+
+/* Извлечь из очереди */
+int pop(LinkedListDeque *deque, bool isFront) {
+ if (empty(deque))
+ return -1;
+ int val;
+ // Операция извлечения из головы очереди
+ if (isFront) {
+ val = peekFirst(deque); // Временно сохранить значение головного узла
+ DoublyListNode *fNext = deque->front->next;
+ if (fNext) {
+ fNext->prev = NULL;
+ deque->front->next = NULL;
+ }
+ delDoublyListNode(deque->front);
+ deque->front = fNext; // Обновить головной узел
+ }
+ // Операция извлечения из хвоста очереди
+ else {
+ val = peekLast(deque); // Временно сохранить значение хвостового узла
+ DoublyListNode *rPrev = deque->rear->prev;
+ if (rPrev) {
+ rPrev->next = NULL;
+ deque->rear->prev = NULL;
+ }
+ delDoublyListNode(deque->rear);
+ deque->rear = rPrev; // Обновить хвостовой узел
+ }
+ deque->queSize--; // Обновить длину очереди
+ return val;
+}
+
+/* Извлечение из головы очереди */
+int popFirst(LinkedListDeque *deque) {
+ return pop(deque, true);
+}
+
+/* Извлечение из хвоста очереди */
+int popLast(LinkedListDeque *deque) {
+ return pop(deque, false);
+}
+
+/* Вывести очередь */
+void printLinkedListDeque(LinkedListDeque *deque) {
+ int *arr = malloc(sizeof(int) * deque->queSize);
+ // Скопировать данные связного списка в массив
+ int i;
+ DoublyListNode *node;
+ for (i = 0, node = deque->front; i < deque->queSize; i++) {
+ arr[i] = node->val;
+ node = node->next;
+ }
+ printArray(arr, deque->queSize);
+ free(arr);
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация двусторонней очереди */
+ LinkedListDeque *deque = newLinkedListDeque();
+ pushLast(deque, 3);
+ pushLast(deque, 2);
+ pushLast(deque, 5);
+ printf("Дек deque = ");
+ printLinkedListDeque(deque);
+
+ /* Доступ к элементу */
+ int peekFirstNum = peekFirst(deque);
+ printf("Элемент в голове peekFirst = %d\r\n", peekFirstNum);
+ int peekLastNum = peekLast(deque);
+ printf("Элемент в хвосте peekLast = %d\r\n", peekLastNum);
+
+ /* Добавление элемента в очередь */
+ pushLast(deque, 4);
+ printf("После вставки элемента 4 в хвост дек =");
+ printLinkedListDeque(deque);
+ pushFirst(deque, 1);
+ printf("После вставки элемента 1 в голову дек =");
+ printLinkedListDeque(deque);
+
+ /* Извлечение элемента из очереди */
+ int popLastNum = popLast(deque);
+ printf("Извлечен элемент из хвоста popLast = %d, дек после извлечения из хвоста = ", popLastNum);
+ printLinkedListDeque(deque);
+ int popFirstNum = popFirst(deque);
+ printf("Извлечен элемент из головы popFirst = %d, дек после извлечения из головы = ", popFirstNum);
+ printLinkedListDeque(deque);
+
+ /* Получение длины очереди */
+ int dequeSize = size(deque);
+ printf("Длина дека size = %d\r\n", dequeSize);
+
+ /* Проверка, пуста ли очередь */
+ bool isEmpty = empty(deque);
+ printf("Пуст ли дек = %s\r\n", isEmpty ? "true" : "false");
+
+ // Освободить память
+ delLinkedListdeque(deque);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_stack_and_queue/linkedlist_queue.c b/ru/codes/c/chapter_stack_and_queue/linkedlist_queue.c
new file mode 100644
index 000000000..9ba64740f
--- /dev/null
+++ b/ru/codes/c/chapter_stack_and_queue/linkedlist_queue.c
@@ -0,0 +1,128 @@
+/**
+ * File: linkedlist_queue.c
+ * Created Time: 2023-03-13
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Очередь на основе связного списка */
+typedef struct {
+ ListNode *front, *rear;
+ int queSize;
+} LinkedListQueue;
+
+/* Конструктор */
+LinkedListQueue *newLinkedListQueue() {
+ LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue));
+ queue->front = NULL;
+ queue->rear = NULL;
+ queue->queSize = 0;
+ return queue;
+}
+
+/* Деструктор */
+void delLinkedListQueue(LinkedListQueue *queue) {
+ // Освободить все узлы
+ while (queue->front != NULL) {
+ ListNode *tmp = queue->front;
+ queue->front = queue->front->next;
+ free(tmp);
+ }
+ // Освободить структуру queue
+ free(queue);
+}
+
+/* Получение длины очереди */
+int size(LinkedListQueue *queue) {
+ return queue->queSize;
+}
+
+/* Проверка, пуста ли очередь */
+bool empty(LinkedListQueue *queue) {
+ return (size(queue) == 0);
+}
+
+/* Поместить в очередь */
+void push(LinkedListQueue *queue, int num) {
+ // Добавить node в хвост
+ ListNode *node = newListNode(num);
+ // Если очередь пуста, сделать так, чтобы и head, и tail указывали на этот узел
+ if (queue->front == NULL) {
+ queue->front = node;
+ queue->rear = node;
+ }
+ // Если очередь не пуста, добавить этот узел после хвостового узла
+ else {
+ queue->rear->next = node;
+ queue->rear = node;
+ }
+ queue->queSize++;
+}
+
+/* Доступ к элементу в начале очереди */
+int peek(LinkedListQueue *queue) {
+ assert(size(queue) && queue->front);
+ return queue->front->val;
+}
+
+/* Извлечь из очереди */
+int pop(LinkedListQueue *queue) {
+ int num = peek(queue);
+ ListNode *tmp = queue->front;
+ queue->front = queue->front->next;
+ free(tmp);
+ queue->queSize--;
+ return num;
+}
+
+/* Вывести очередь */
+void printLinkedListQueue(LinkedListQueue *queue) {
+ int *arr = malloc(sizeof(int) * queue->queSize);
+ // Скопировать данные связного списка в массив
+ int i;
+ ListNode *node;
+ for (i = 0, node = queue->front; i < queue->queSize; i++) {
+ arr[i] = node->val;
+ node = node->next;
+ }
+ printArray(arr, queue->queSize);
+ free(arr);
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация очереди */
+ LinkedListQueue *queue = newLinkedListQueue();
+
+ /* Добавление элемента в очередь */
+ push(queue, 1);
+ push(queue, 3);
+ push(queue, 2);
+ push(queue, 5);
+ push(queue, 4);
+ printf("Очередь queue = ");
+ printLinkedListQueue(queue);
+
+ /* Доступ к элементу в начале очереди */
+ int peekNum = peek(queue);
+ printf("Элемент в голове peek = %d\r\n", peekNum);
+
+ /* Извлечение элемента из очереди */
+ peekNum = pop(queue);
+ printf("Извлечен элемент из очереди pop = %d, очередь после извлечения = ", peekNum);
+ printLinkedListQueue(queue);
+
+ /* Получение длины очереди */
+ int queueSize = size(queue);
+ printf("Длина очереди size = %d\r\n", queueSize);
+
+ /* Проверка, пуста ли очередь */
+ bool isEmpty = empty(queue);
+ printf("Пуста ли очередь = %s\r\n", isEmpty ? "true" : "false");
+
+ // Освободить память
+ delLinkedListQueue(queue);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_stack_and_queue/linkedlist_stack.c b/ru/codes/c/chapter_stack_and_queue/linkedlist_stack.c
new file mode 100644
index 000000000..cba0b13c5
--- /dev/null
+++ b/ru/codes/c/chapter_stack_and_queue/linkedlist_stack.c
@@ -0,0 +1,107 @@
+/**
+ * File: linkedlist_stack.c
+ * Created Time: 2023-01-12
+ * Author: Zero (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Стек на основе связного списка */
+typedef struct {
+ ListNode *top; // Использовать головной узел как вершину стека
+ int size; // Длина стека
+} LinkedListStack;
+
+/* Конструктор */
+LinkedListStack *newLinkedListStack() {
+ LinkedListStack *s = malloc(sizeof(LinkedListStack));
+ s->top = NULL;
+ s->size = 0;
+ return s;
+}
+
+/* Деструктор */
+void delLinkedListStack(LinkedListStack *s) {
+ while (s->top) {
+ ListNode *n = s->top->next;
+ free(s->top);
+ s->top = n;
+ }
+ free(s);
+}
+
+/* Получение длины стека */
+int size(LinkedListStack *s) {
+ return s->size;
+}
+
+/* Проверка, пуст ли стек */
+bool isEmpty(LinkedListStack *s) {
+ return size(s) == 0;
+}
+
+/* Поместить в стек */
+void push(LinkedListStack *s, int num) {
+ ListNode *node = (ListNode *)malloc(sizeof(ListNode));
+ node->next = s->top; // Обновить поле указателя нового узла
+ node->val = num; // Обновить поле данных нового узла
+ s->top = node; // Обновить вершину стека
+ s->size++; // Обновить размер стека
+}
+
+/* Доступ к верхнему элементу стека */
+int peek(LinkedListStack *s) {
+ if (s->size == 0) {
+ printf("стек пуст\n");
+ return INT_MAX;
+ }
+ return s->top->val;
+}
+
+/* Извлечь из стека */
+int pop(LinkedListStack *s) {
+ int val = peek(s);
+ ListNode *tmp = s->top;
+ s->top = s->top->next;
+ // Освободить память
+ free(tmp);
+ s->size--;
+ return val;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация стека */
+ LinkedListStack *stack = newLinkedListStack();
+
+ /* Помещение элемента в стек */
+ push(stack, 1);
+ push(stack, 3);
+ push(stack, 2);
+ push(stack, 5);
+ push(stack, 4);
+
+ printf("Стек stack = ");
+ printLinkedList(stack->top);
+
+ /* Доступ к верхнему элементу стека */
+ int val = peek(stack);
+ printf("Верхний элемент стека top = %d\r\n", val);
+
+ /* Извлечение элемента из стека */
+ val = pop(stack);
+ printf("Извлечен элемент из стека pop = %d, стек после извлечения = ", val);
+ printLinkedList(stack->top);
+
+ /* Получение длины стека */
+ printf("Длина стека size = %d\n", size(stack));
+
+ /* Проверка на пустоту */
+ bool empty = isEmpty(stack);
+ printf("Пуст ли стек = %s\n", empty ? "true" : "false");
+
+ // Освободить память
+ delLinkedListStack(stack);
+
+ return 0;
+}
diff --git a/ru/codes/c/chapter_tree/CMakeLists.txt b/ru/codes/c/chapter_tree/CMakeLists.txt
new file mode 100644
index 000000000..9b4e825ff
--- /dev/null
+++ b/ru/codes/c/chapter_tree/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_executable(avl_tree avl_tree.c)
+add_executable(binary_tree binary_tree.c)
+add_executable(binary_tree_bfs binary_tree_bfs.c)
+add_executable(binary_tree_dfs binary_tree_dfs.c)
+add_executable(binary_search_tree binary_search_tree.c)
+add_executable(array_binary_tree array_binary_tree.c)
diff --git a/ru/codes/c/chapter_tree/array_binary_tree.c b/ru/codes/c/chapter_tree/array_binary_tree.c
new file mode 100644
index 000000000..04a08b927
--- /dev/null
+++ b/ru/codes/c/chapter_tree/array_binary_tree.c
@@ -0,0 +1,166 @@
+/**
+ * File: array_binary_tree.c
+ * Created Time: 2023-07-29
+ * Author: Gonglja (glj0@outlook.com)
+ */
+
+#include "../utils/common.h"
+
+/* Структура двоичного дерева в представлении массивом */
+typedef struct {
+ int *tree;
+ int size;
+} ArrayBinaryTree;
+
+/* Конструктор */
+ArrayBinaryTree *newArrayBinaryTree(int *arr, int arrSize) {
+ ArrayBinaryTree *abt = (ArrayBinaryTree *)malloc(sizeof(ArrayBinaryTree));
+ abt->tree = malloc(sizeof(int) * arrSize);
+ memcpy(abt->tree, arr, sizeof(int) * arrSize);
+ abt->size = arrSize;
+ return abt;
+}
+
+/* Деструктор */
+void delArrayBinaryTree(ArrayBinaryTree *abt) {
+ free(abt->tree);
+ free(abt);
+}
+
+/* Вместимость списка */
+int size(ArrayBinaryTree *abt) {
+ return abt->size;
+}
+
+/* Получить значение узла с индексом i */
+int val(ArrayBinaryTree *abt, int i) {
+ // Если индекс выходит за границы, вернуть INT_MAX, обозначающий пустую позицию
+ if (i < 0 || i >= size(abt))
+ return INT_MAX;
+ return abt->tree[i];
+}
+
+/* Получить индекс левого дочернего узла узла с индексом i */
+int left(int i) {
+ return 2 * i + 1;
+}
+
+/* Получить индекс правого дочернего узла узла с индексом i */
+int right(int i) {
+ return 2 * i + 2;
+}
+
+/* Получить индекс родительского узла узла с индексом i */
+int parent(int i) {
+ return (i - 1) / 2;
+}
+
+/* Обход в ширину */
+int *levelOrder(ArrayBinaryTree *abt, int *returnSize) {
+ int *res = (int *)malloc(sizeof(int) * size(abt));
+ int index = 0;
+ // Непосредственно обходить массив
+ for (int i = 0; i < size(abt); i++) {
+ if (val(abt, i) != INT_MAX)
+ res[index++] = val(abt, i);
+ }
+ *returnSize = index;
+ return res;
+}
+
+/* Обход в глубину */
+void dfs(ArrayBinaryTree *abt, int i, char *order, int *res, int *index) {
+ // Если это пустая позиция, вернуть
+ if (val(abt, i) == INT_MAX)
+ return;
+ // Предварительный обход
+ if (strcmp(order, "pre") == 0)
+ res[(*index)++] = val(abt, i);
+ dfs(abt, left(i), order, res, index);
+ // Симметричный обход
+ if (strcmp(order, "in") == 0)
+ res[(*index)++] = val(abt, i);
+ dfs(abt, right(i), order, res, index);
+ // Обратный обход
+ if (strcmp(order, "post") == 0)
+ res[(*index)++] = val(abt, i);
+}
+
+/* Предварительный обход */
+int *preOrder(ArrayBinaryTree *abt, int *returnSize) {
+ int *res = (int *)malloc(sizeof(int) * size(abt));
+ int index = 0;
+ dfs(abt, 0, "pre", res, &index);
+ *returnSize = index;
+ return res;
+}
+
+/* Симметричный обход */
+int *inOrder(ArrayBinaryTree *abt, int *returnSize) {
+ int *res = (int *)malloc(sizeof(int) * size(abt));
+ int index = 0;
+ dfs(abt, 0, "in", res, &index);
+ *returnSize = index;
+ return res;
+}
+
+/* Обратный обход */
+int *postOrder(ArrayBinaryTree *abt, int *returnSize) {
+ int *res = (int *)malloc(sizeof(int) * size(abt));
+ int index = 0;
+ dfs(abt, 0, "post", res, &index);
+ *returnSize = index;
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ // Инициализировать двоичное дерево
+ // Использовать INT_MAX для обозначения пустой позиции NULL
+ int arr[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15};
+ int arrSize = sizeof(arr) / sizeof(arr[0]);
+ TreeNode *root = arrayToTree(arr, arrSize);
+ printf("\nИнициализация двоичного дерева\n");
+ printf("Массивное представление двоичного дерева:\n");
+ printArray(arr, arrSize);
+ printf("Связное представление двоичного дерева:\n");
+ printTree(root);
+
+ ArrayBinaryTree *abt = newArrayBinaryTree(arr, arrSize);
+
+ // Доступ к узлу
+ int i = 1;
+ int l = left(i), r = right(i), p = parent(i);
+ printf("\nТекущий индекс узла = %d, значение = %d\n", i, val(abt, i));
+ printf("Индекс левого дочернего узла = %d, значение = %d\n", l, l < arrSize ? val(abt, l) : INT_MAX);
+ printf("Индекс правого дочернего узла = %d, значение = %d\n", r, r < arrSize ? val(abt, r) : INT_MAX);
+ printf("Индекс родительского узла = %d, значение = %d\n", p, p < arrSize ? val(abt, p) : INT_MAX);
+
+ // Обходить дерево
+ int returnSize;
+ int *res;
+
+ res = levelOrder(abt, &returnSize);
+ printf("\nОбход по уровням: ");
+ printArray(res, returnSize);
+ free(res);
+
+ res = preOrder(abt, &returnSize);
+ printf("Предварительный обход: ");
+ printArray(res, returnSize);
+ free(res);
+
+ res = inOrder(abt, &returnSize);
+ printf("Симметричный обход: ");
+ printArray(res, returnSize);
+ free(res);
+
+ res = postOrder(abt, &returnSize);
+ printf("Обратный обход: ");
+ printArray(res, returnSize);
+ free(res);
+
+ // Освободить память
+ delArrayBinaryTree(abt);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_tree/avl_tree.c b/ru/codes/c/chapter_tree/avl_tree.c
new file mode 100644
index 000000000..1c1e55121
--- /dev/null
+++ b/ru/codes/c/chapter_tree/avl_tree.c
@@ -0,0 +1,259 @@
+/**
+ * File: avl_tree.c
+ * Created Time: 2023-01-15
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Структура AVL-дерева */
+typedef struct {
+ TreeNode *root;
+} AVLTree;
+
+/* Конструктор */
+AVLTree *newAVLTree() {
+ AVLTree *tree = (AVLTree *)malloc(sizeof(AVLTree));
+ tree->root = NULL;
+ return tree;
+}
+
+/* Деструктор */
+void delAVLTree(AVLTree *tree) {
+ freeMemoryTree(tree->root);
+ free(tree);
+}
+
+/* Получить высоту узла */
+int height(TreeNode *node) {
+ // Высота пустого узла равна -1, высота листового узла равна 0
+ if (node != NULL) {
+ return node->height;
+ }
+ return -1;
+}
+
+/* Обновить высоту узла */
+void updateHeight(TreeNode *node) {
+ int lh = height(node->left);
+ int rh = height(node->right);
+ // Высота узла равна высоте более высокого поддерева + 1
+ if (lh > rh) {
+ node->height = lh + 1;
+ } else {
+ node->height = rh + 1;
+ }
+}
+
+/* Получить коэффициент баланса */
+int balanceFactor(TreeNode *node) {
+ // Коэффициент баланса пустого узла равен 0
+ if (node == NULL) {
+ return 0;
+ }
+ // Коэффициент баланса узла = высота левого поддерева - высота правого поддерева
+ return height(node->left) - height(node->right);
+}
+
+/* Операция правого вращения */
+TreeNode *rightRotate(TreeNode *node) {
+ TreeNode *child, *grandChild;
+ child = node->left;
+ grandChild = child->right;
+ // Выполнить правое вращение узла node вокруг child
+ child->right = node;
+ node->left = grandChild;
+ // Обновить высоту узла
+ updateHeight(node);
+ updateHeight(child);
+ // Вернуть корневой узел поддерева после вращения
+ return child;
+}
+
+/* Операция левого вращения */
+TreeNode *leftRotate(TreeNode *node) {
+ TreeNode *child, *grandChild;
+ child = node->right;
+ grandChild = child->left;
+ // Выполнить левое вращение узла node вокруг child
+ child->left = node;
+ node->right = grandChild;
+ // Обновить высоту узла
+ updateHeight(node);
+ updateHeight(child);
+ // Вернуть корневой узел поддерева после вращения
+ return child;
+}
+
+/* Выполнить вращение, чтобы снова сбалансировать поддерево */
+TreeNode *rotate(TreeNode *node) {
+ // Получить коэффициент баланса узла node
+ int bf = balanceFactor(node);
+ // Левосторонне перекошенное дерево
+ if (bf > 1) {
+ if (balanceFactor(node->left) >= 0) {
+ // Правое вращение
+ return rightRotate(node);
+ } else {
+ // Сначала левое вращение, затем правое
+ node->left = leftRotate(node->left);
+ return rightRotate(node);
+ }
+ }
+ // Правосторонне перекошенное дерево
+ if (bf < -1) {
+ if (balanceFactor(node->right) <= 0) {
+ // Левое вращение
+ return leftRotate(node);
+ } else {
+ // Сначала правое вращение, затем левое
+ node->right = rightRotate(node->right);
+ return leftRotate(node);
+ }
+ }
+ // Дерево сбалансировано, вращение не требуется, вернуть сразу
+ return node;
+}
+
+/* Рекурсивная вставка узла (вспомогательная функция) */
+TreeNode *insertHelper(TreeNode *node, int val) {
+ if (node == NULL) {
+ return newTreeNode(val);
+ }
+ /* 1. Найти позицию вставки и вставить узел */
+ if (val < node->val) {
+ node->left = insertHelper(node->left, val);
+ } else if (val > node->val) {
+ node->right = insertHelper(node->right, val);
+ } else {
+ // Повторяющийся узел не вставлять, сразу вернуть
+ return node;
+ }
+ // Обновить высоту узла
+ updateHeight(node);
+ /* 2. Выполнить вращение, чтобы снова сбалансировать поддерево */
+ node = rotate(node);
+ // Вернуть корневой узел поддерева
+ return node;
+}
+
+/* Вставка узла */
+void insert(AVLTree *tree, int val) {
+ tree->root = insertHelper(tree->root, val);
+}
+
+/* Рекурсивное удаление узла (вспомогательная функция) */
+TreeNode *removeHelper(TreeNode *node, int val) {
+ TreeNode *child, *grandChild;
+ if (node == NULL) {
+ return NULL;
+ }
+ /* 1. Найти узел и удалить его */
+ if (val < node->val) {
+ node->left = removeHelper(node->left, val);
+ } else if (val > node->val) {
+ node->right = removeHelper(node->right, val);
+ } else {
+ if (node->left == NULL || node->right == NULL) {
+ child = node->left;
+ if (node->right != NULL) {
+ child = node->right;
+ }
+ // Число дочерних узлов = 0, удалить node и сразу вернуть
+ if (child == NULL) {
+ return NULL;
+ } else {
+ // Число дочерних узлов = 1, удалить node напрямую
+ node = child;
+ }
+ } else {
+ // Число дочерних узлов = 2, удалить следующий по симметричному обходу узел и заменить им текущий узел
+ TreeNode *temp = node->right;
+ while (temp->left != NULL) {
+ temp = temp->left;
+ }
+ int tempVal = temp->val;
+ node->right = removeHelper(node->right, temp->val);
+ node->val = tempVal;
+ }
+ }
+ // Обновить высоту узла
+ updateHeight(node);
+ /* 2. Выполнить вращение, чтобы снова сбалансировать поддерево */
+ node = rotate(node);
+ // Вернуть корневой узел поддерева
+ return node;
+}
+
+/* Удаление узла */
+// Из-за подключения stdio.h здесь нельзя использовать ключевое слово remove
+void removeItem(AVLTree *tree, int val) {
+ TreeNode *root = removeHelper(tree->root, val);
+}
+
+/* Поиск узла */
+TreeNode *search(AVLTree *tree, int val) {
+ TreeNode *cur = tree->root;
+ // Искать в цикле и выйти после прохода за листовой узел
+ while (cur != NULL) {
+ if (cur->val < val) {
+ // Целевой узел находится в правом поддереве cur
+ cur = cur->right;
+ } else if (cur->val > val) {
+ // Целевой узел находится в левом поддереве cur
+ cur = cur->left;
+ } else {
+ // Найти целевой узел и выйти из цикла
+ break;
+ }
+ }
+ // Найти целевой узел и выйти из цикла
+ return cur;
+}
+
+void testInsert(AVLTree *tree, int val) {
+ insert(tree, val);
+ printf("\nПосле вставки узла %d AVL-дерево имеет вид \n", val);
+ printTree(tree->root);
+}
+
+void testRemove(AVLTree *tree, int val) {
+ removeItem(tree, val);
+ printf("\nПосле удаления узла %d AVL-дерево имеет вид \n", val);
+ printTree(tree->root);
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация пустого AVL-дерева */
+ AVLTree *tree = (AVLTree *)newAVLTree();
+ /* Вставка узла */
+ // Обратите внимание, как AVL-дерево сохраняет баланс после вставки узла
+ testInsert(tree, 1);
+ testInsert(tree, 2);
+ testInsert(tree, 3);
+ testInsert(tree, 4);
+ testInsert(tree, 5);
+ testInsert(tree, 8);
+ testInsert(tree, 7);
+ testInsert(tree, 9);
+ testInsert(tree, 10);
+ testInsert(tree, 6);
+
+ /* Вставка повторяющегося узла */
+ testInsert(tree, 7);
+
+ /* Удаление узла */
+ // Обратите внимание, как AVL-дерево сохраняет баланс после удаления узла
+ testRemove(tree, 8); // Удаление узла степени 0
+ testRemove(tree, 5); // Удаление узла степени 1
+ testRemove(tree, 4); // Удаление узла степени 2
+
+ /* Поиск узла */
+ TreeNode *node = search(tree, 7);
+ printf("\nНайденный объект узла, значение узла = %d \n", node->val);
+
+ // Освободить память
+ delAVLTree(tree);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_tree/binary_search_tree.c b/ru/codes/c/chapter_tree/binary_search_tree.c
new file mode 100644
index 000000000..d7c7b4158
--- /dev/null
+++ b/ru/codes/c/chapter_tree/binary_search_tree.c
@@ -0,0 +1,171 @@
+/**
+ * File: binary_search_tree.c
+ * Created Time: 2023-01-11
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Структура двоичного дерева поиска */
+typedef struct {
+ TreeNode *root;
+} BinarySearchTree;
+
+/* Конструктор */
+BinarySearchTree *newBinarySearchTree() {
+ // Инициализировать пустое дерево
+ BinarySearchTree *bst = (BinarySearchTree *)malloc(sizeof(BinarySearchTree));
+ bst->root = NULL;
+ return bst;
+}
+
+/* Деструктор */
+void delBinarySearchTree(BinarySearchTree *bst) {
+ freeMemoryTree(bst->root);
+ free(bst);
+}
+
+/* Получить корневой узел двоичного дерева */
+TreeNode *getRoot(BinarySearchTree *bst) {
+ return bst->root;
+}
+
+/* Поиск узла */
+TreeNode *search(BinarySearchTree *bst, int num) {
+ TreeNode *cur = bst->root;
+ // Искать в цикле и выйти после прохода за листовой узел
+ while (cur != NULL) {
+ if (cur->val < num) {
+ // Целевой узел находится в правом поддереве cur
+ cur = cur->right;
+ } else if (cur->val > num) {
+ // Целевой узел находится в левом поддереве cur
+ cur = cur->left;
+ } else {
+ // Найти целевой узел и выйти из цикла
+ break;
+ }
+ }
+ // Вернуть целевой узел
+ return cur;
+}
+
+/* Вставка узла */
+void insert(BinarySearchTree *bst, int num) {
+ // Если дерево пусто, инициализировать корневой узел
+ if (bst->root == NULL) {
+ bst->root = newTreeNode(num);
+ return;
+ }
+ TreeNode *cur = bst->root, *pre = NULL;
+ // Искать в цикле и выйти после прохода за листовой узел
+ while (cur != NULL) {
+ // Найти повторяющийся узел и сразу вернуть
+ if (cur->val == num) {
+ return;
+ }
+ pre = cur;
+ if (cur->val < num) {
+ // Позиция вставки находится в правом поддереве cur
+ cur = cur->right;
+ } else {
+ // Позиция вставки находится в левом поддереве cur
+ cur = cur->left;
+ }
+ }
+ // Вставка узла
+ TreeNode *node = newTreeNode(num);
+ if (pre->val < num) {
+ pre->right = node;
+ } else {
+ pre->left = node;
+ }
+}
+
+/* Удаление узла */
+// Из-за подключения stdio.h здесь нельзя использовать ключевое слово remove
+void removeItem(BinarySearchTree *bst, int num) {
+ // Если дерево пусто, сразу вернуть
+ if (bst->root == NULL)
+ return;
+ TreeNode *cur = bst->root, *pre = NULL;
+ // Искать в цикле и выйти после прохода за листовой узел
+ while (cur != NULL) {
+ // Найти узел для удаления и выйти из цикла
+ if (cur->val == num)
+ break;
+ pre = cur;
+ if (cur->val < num) {
+ // Удаляемый узел находится в правом поддереве root
+ cur = cur->right;
+ } else {
+ // Удаляемый узел находится в левом поддереве root
+ cur = cur->left;
+ }
+ }
+ // Если узел для удаления отсутствует, сразу вернуть
+ if (cur == NULL)
+ return;
+ // Проверить, есть ли дочерние узлы у удаляемого узла
+ if (cur->left == NULL || cur->right == NULL) {
+ /* Число дочерних узлов = 0 или 1 */
+ // Когда число дочерних узлов = 0 / 1, child = nullptr / этот дочерний узел
+ TreeNode *child = cur->left != NULL ? cur->left : cur->right;
+ // Удалить узел cur
+ if (pre->left == cur) {
+ pre->left = child;
+ } else {
+ pre->right = child;
+ }
+ // Освободить память
+ free(cur);
+ } else {
+ /* Число дочерних узлов = 2 */
+ // Получить следующий узел после cur в симметричном обходе
+ TreeNode *tmp = cur->right;
+ while (tmp->left != NULL) {
+ tmp = tmp->left;
+ }
+ int tmpVal = tmp->val;
+ // Рекурсивно удалить узел tmp
+ removeItem(bst, tmp->val);
+ // Перезаписать cur значением tmp
+ cur->val = tmpVal;
+ }
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация двоичного дерева поиска */
+ int nums[] = {8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15};
+ BinarySearchTree *bst = newBinarySearchTree();
+ for (int i = 0; i < sizeof(nums) / sizeof(int); i++) {
+ insert(bst, nums[i]);
+ }
+ printf("Инициализированное двоичное дерево\n");
+ printTree(getRoot(bst));
+
+ /* Поиск узла */
+ TreeNode *node = search(bst, 7);
+ printf("Значение найденного объекта узла = %d\n", node->val);
+
+ /* Вставка узла */
+ insert(bst, 16);
+ printf("После вставки узла 16 двоичное дерево имеет вид\n");
+ printTree(getRoot(bst));
+
+ /* Удаление узла */
+ removeItem(bst, 1);
+ printf("После удаления узла 1 двоичное дерево имеет вид\n");
+ printTree(getRoot(bst));
+ removeItem(bst, 2);
+ printf("После удаления узла 2 двоичное дерево имеет вид\n");
+ printTree(getRoot(bst));
+ removeItem(bst, 4);
+ printf("После удаления узла 4 двоичное дерево имеет вид\n");
+ printTree(getRoot(bst));
+
+ // Освободить память
+ delBinarySearchTree(bst);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_tree/binary_tree.c b/ru/codes/c/chapter_tree/binary_tree.c
new file mode 100644
index 000000000..5410c858f
--- /dev/null
+++ b/ru/codes/c/chapter_tree/binary_tree.c
@@ -0,0 +1,43 @@
+/**
+ * File: binary_tree.c
+ * Created Time: 2023-01-11
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+/* Driver Code */
+int main() {
+ /* Инициализация двоичного дерева */
+ // Инициализация узла
+ TreeNode *n1 = newTreeNode(1);
+ TreeNode *n2 = newTreeNode(2);
+ TreeNode *n3 = newTreeNode(3);
+ TreeNode *n4 = newTreeNode(4);
+ TreeNode *n5 = newTreeNode(5);
+ // Построить связи между узлами (указатели)
+ n1->left = n2;
+ n1->right = n3;
+ n2->left = n4;
+ n2->right = n5;
+ printf("Инициализация двоичного дерева\n");
+ printTree(n1);
+
+ /* Вставка и удаление узлов */
+ TreeNode *P = newTreeNode(0);
+ // Вставить узел P между n1 -> n2
+ n1->left = P;
+ P->left = n2;
+ printf("После вставки узла P\n");
+ printTree(n1);
+
+ // Удалить узел P
+ n1->left = n2;
+ // Освободить память
+ free(P);
+ printf("После удаления узла P\n");
+ printTree(n1);
+
+ freeMemoryTree(n1);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_tree/binary_tree_bfs.c b/ru/codes/c/chapter_tree/binary_tree_bfs.c
new file mode 100644
index 000000000..cf851844c
--- /dev/null
+++ b/ru/codes/c/chapter_tree/binary_tree_bfs.c
@@ -0,0 +1,73 @@
+/**
+ * File: binary_tree_bfs.c
+ * Created Time: 2023-01-11
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+#define MAX_SIZE 100
+
+/* Обход в ширину */
+int *levelOrder(TreeNode *root, int *size) {
+ /* Вспомогательная очередь */
+ int front, rear;
+ int index, *arr;
+ TreeNode *node;
+ TreeNode **queue;
+
+ /* Вспомогательная очередь */
+ queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_SIZE);
+ // Указатель очереди
+ front = 0, rear = 0;
+ // Добавить корневой узел
+ queue[rear++] = root;
+ // Инициализировать список для хранения последовательности обхода
+ /* Вспомогательный массив */
+ arr = (int *)malloc(sizeof(int) * MAX_SIZE);
+ // Указатель на массив
+ index = 0;
+ while (front < rear) {
+ // Извлечение из очереди
+ node = queue[front++];
+ // Сохранить значение узла
+ arr[index++] = node->val;
+ if (node->left != NULL) {
+ // Поместить левый дочерний узел в очередь
+ queue[rear++] = node->left;
+ }
+ if (node->right != NULL) {
+ // Поместить правый дочерний узел в очередь
+ queue[rear++] = node->right;
+ }
+ }
+ // Обновить значение длины массива
+ *size = index;
+ arr = realloc(arr, sizeof(int) * (*size));
+
+ // Освободить память вспомогательного массива
+ free(queue);
+ return arr;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация двоичного дерева */
+ // Здесь используется функция, напрямую строящая двоичное дерево из массива
+ int nums[] = {1, 2, 3, 4, 5, 6, 7};
+ int size = sizeof(nums) / sizeof(int);
+ TreeNode *root = arrayToTree(nums, size);
+ printf("Инициализация двоичного дерева\n");
+ printTree(root);
+
+ /* Обход в ширину */
+ // Нужно передать длину массива
+ int *arr = levelOrder(root, &size);
+ printf("Последовательность узлов при обходе по уровням = ");
+ printArray(arr, size);
+
+ // Освободить память
+ freeMemoryTree(root);
+ free(arr);
+ return 0;
+}
diff --git a/ru/codes/c/chapter_tree/binary_tree_dfs.c b/ru/codes/c/chapter_tree/binary_tree_dfs.c
new file mode 100644
index 000000000..fcfe0942b
--- /dev/null
+++ b/ru/codes/c/chapter_tree/binary_tree_dfs.c
@@ -0,0 +1,75 @@
+/**
+ * File: binary_tree_dfs.c
+ * Created Time: 2023-01-11
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "../utils/common.h"
+
+#define MAX_SIZE 100
+
+// Вспомогательный массив для хранения последовательности обхода
+int arr[MAX_SIZE];
+
+/* Предварительный обход */
+void preOrder(TreeNode *root, int *size) {
+ if (root == NULL)
+ return;
+ // Порядок обхода: корень -> левое поддерево -> правое поддерево
+ arr[(*size)++] = root->val;
+ preOrder(root->left, size);
+ preOrder(root->right, size);
+}
+
+/* Симметричный обход */
+void inOrder(TreeNode *root, int *size) {
+ if (root == NULL)
+ return;
+ // Порядок обхода: левое поддерево -> корень -> правое поддерево
+ inOrder(root->left, size);
+ arr[(*size)++] = root->val;
+ inOrder(root->right, size);
+}
+
+/* Обратный обход */
+void postOrder(TreeNode *root, int *size) {
+ if (root == NULL)
+ return;
+ // Порядок обхода: левое поддерево -> правое поддерево -> корень
+ postOrder(root->left, size);
+ postOrder(root->right, size);
+ arr[(*size)++] = root->val;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация двоичного дерева */
+ // Здесь используется функция, напрямую строящая двоичное дерево из массива
+ int nums[] = {1, 2, 3, 4, 5, 6, 7};
+ int size = sizeof(nums) / sizeof(int);
+ TreeNode *root = arrayToTree(nums, size);
+ printf("Инициализация двоичного дерева\n");
+ printTree(root);
+
+ /* Предварительный обход */
+ // Инициализация вспомогательного массива
+ size = 0;
+ preOrder(root, &size);
+ printf("Последовательность узлов при предварительном обходе = ");
+ printArray(arr, size);
+
+ /* Симметричный обход */
+ size = 0;
+ inOrder(root, &size);
+ printf("Последовательность узлов при симметричном обходе = ");
+ printArray(arr, size);
+
+ /* Обратный обход */
+ size = 0;
+ postOrder(root, &size);
+ printf("Последовательность узлов при обратном обходе = ");
+ printArray(arr, size);
+
+ freeMemoryTree(root);
+ return 0;
+}
diff --git a/ru/codes/c/utils/CMakeLists.txt b/ru/codes/c/utils/CMakeLists.txt
new file mode 100644
index 000000000..c1ece2e38
--- /dev/null
+++ b/ru/codes/c/utils/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(utils
+ common_test.c
+ common.h print_util.h
+ list_node.h tree_node.h
+ uthash.h)
\ No newline at end of file
diff --git a/ru/codes/c/utils/common.h b/ru/codes/c/utils/common.h
new file mode 100644
index 000000000..8b9adeff7
--- /dev/null
+++ b/ru/codes/c/utils/common.h
@@ -0,0 +1,36 @@
+/**
+ * File: common.h
+ * Created Time: 2022-12-20
+ * Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com)
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "list_node.h"
+#include "print_util.h"
+#include "tree_node.h"
+#include "vertex.h"
+
+// hash table lib
+#include "uthash.h"
+
+#include "vector.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // COMMON_H
diff --git a/ru/codes/c/utils/common_test.c b/ru/codes/c/utils/common_test.c
new file mode 100644
index 000000000..a889b423b
--- /dev/null
+++ b/ru/codes/c/utils/common_test.c
@@ -0,0 +1,35 @@
+/**
+ * File: include_test.c
+ * Created Time: 2023-01-10
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#include "common.h"
+
+void testListNode() {
+ int nums[] = {2, 3, 5, 6, 7};
+ int size = sizeof(nums) / sizeof(int);
+ ListNode *head = arrToLinkedList(nums, size);
+ printLinkedList(head);
+}
+
+void testTreeNode() {
+ int nums[] = {1, 2, 3, INT_MAX, 5, 6, INT_MAX};
+ int size = sizeof(nums) / sizeof(int);
+ TreeNode *root = arrayToTree(nums, size);
+
+ // print tree
+ printTree(root);
+
+ // tree to arr
+ int *arr = treeToArray(root, &size);
+ printArray(arr, size);
+}
+
+int main(int argc, char *argv[]) {
+ printf("==testListNode==\n");
+ testListNode();
+ printf("==testTreeNode==\n");
+ testTreeNode();
+ return 0;
+}
diff --git a/ru/codes/c/utils/list_node.h b/ru/codes/c/utils/list_node.h
new file mode 100644
index 000000000..76775e419
--- /dev/null
+++ b/ru/codes/c/utils/list_node.h
@@ -0,0 +1,59 @@
+/**
+ * File: list_node.h
+ * Created Time: 2023-01-09
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#ifndef LIST_NODE_H
+#define LIST_NODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Структура узла связного списка */
+typedef struct ListNode {
+ int val; // Значение узла
+ struct ListNode *next; // Ссылка на следующий узел
+} ListNode;
+
+/* Конструктор, инициализирующий новый узел */
+ListNode *newListNode(int val) {
+ ListNode *node;
+ node = (ListNode *)malloc(sizeof(ListNode));
+ node->val = val;
+ node->next = NULL;
+ return node;
+}
+
+/* Десериализовать массив в связный список */
+ListNode *arrToLinkedList(const int *arr, size_t size) {
+ if (size <= 0) {
+ return NULL;
+ }
+
+ ListNode *dummy = newListNode(0);
+ ListNode *node = dummy;
+ for (int i = 0; i < size; i++) {
+ node->next = newListNode(arr[i]);
+ node = node->next;
+ }
+ return dummy->next;
+}
+
+/* Освободить память, выделенную под связный список */
+void freeMemoryLinkedList(ListNode *cur) {
+ // Освободить память
+ ListNode *pre;
+ while (cur != NULL) {
+ pre = cur;
+ cur = cur->next;
+ free(pre);
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // LIST_NODE_H
diff --git a/ru/codes/c/utils/print_util.h b/ru/codes/c/utils/print_util.h
new file mode 100644
index 000000000..fd963dd66
--- /dev/null
+++ b/ru/codes/c/utils/print_util.h
@@ -0,0 +1,131 @@
+/**
+ * File: print_util.h
+ * Created Time: 2022-12-21
+ * Author: MolDum (moldum@163.com), Reanon (793584285@qq.com)
+ */
+
+#ifndef PRINT_UTIL_H
+#define PRINT_UTIL_H
+
+#include
+#include
+#include
+
+#include "list_node.h"
+#include "tree_node.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Вывести массив */
+void printArray(int arr[], int size) {
+ if (arr == NULL || size == 0) {
+ printf("[]");
+ return;
+ }
+ printf("[");
+ for (int i = 0; i < size - 1; i++) {
+ printf("%d, ", arr[i]);
+ }
+ printf("%d]\n", arr[size - 1]);
+}
+
+/* Вывести массив */
+void printArrayFloat(float arr[], int size) {
+ if (arr == NULL || size == 0) {
+ printf("[]");
+ return;
+ }
+ printf("[");
+ for (int i = 0; i < size - 1; i++) {
+ printf("%.2f, ", arr[i]);
+ }
+ printf("%.2f]\n", arr[size - 1]);
+}
+
+/* Вывести связный список */
+void printLinkedList(ListNode *node) {
+ if (node == NULL) {
+ return;
+ }
+ while (node->next != NULL) {
+ printf("%d -> ", node->val);
+ node = node->next;
+ }
+ printf("%d\n", node->val);
+}
+
+typedef struct Trunk {
+ struct Trunk *prev;
+ char *str;
+} Trunk;
+
+Trunk *newTrunk(Trunk *prev, char *str) {
+ Trunk *trunk = (Trunk *)malloc(sizeof(Trunk));
+ trunk->prev = prev;
+ trunk->str = (char *)malloc(sizeof(char) * 10);
+ strcpy(trunk->str, str);
+ return trunk;
+}
+
+void showTrunks(Trunk *trunk) {
+ if (trunk == NULL) {
+ return;
+ }
+ showTrunks(trunk->prev);
+ printf("%s", trunk->str);
+}
+
+/**
+ * Вывести двоичное дерево
+ * Этот вывод дерева заимствован из TECHIE DELIGHT
+ * https://www.techiedelight.com/c-program-print-binary-tree/
+ */
+void printTreeHelper(TreeNode *node, Trunk *prev, bool isRight) {
+ if (node == NULL) {
+ return;
+ }
+ char *prev_str = " ";
+ Trunk *trunk = newTrunk(prev, prev_str);
+ printTreeHelper(node->right, trunk, true);
+ if (prev == NULL) {
+ trunk->str = "———";
+ } else if (isRight) {
+ trunk->str = "/———";
+ prev_str = " |";
+ } else {
+ trunk->str = "\\———";
+ prev->str = prev_str;
+ }
+ showTrunks(trunk);
+ printf("%d\n", node->val);
+
+ if (prev != NULL) {
+ prev->str = prev_str;
+ }
+ trunk->str = " |";
+
+ printTreeHelper(node->left, trunk, false);
+}
+
+/* Вывести двоичное дерево */
+void printTree(TreeNode *root) {
+ printTreeHelper(root, NULL, false);
+}
+
+/* Вывести кучу */
+void printHeap(int arr[], int size) {
+ TreeNode *root;
+ printf("Массивное представление кучи:");
+ printArray(arr, size);
+ printf("Древовидное представление кучи:\n");
+ root = arrayToTree(arr, size);
+ printTree(root);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PRINT_UTIL_H
diff --git a/ru/codes/c/utils/tree_node.h b/ru/codes/c/utils/tree_node.h
new file mode 100644
index 000000000..32b9e1c06
--- /dev/null
+++ b/ru/codes/c/utils/tree_node.h
@@ -0,0 +1,107 @@
+/**
+ * File: tree_node.h
+ * Created Time: 2023-01-09
+ * Author: Reanon (793584285@qq.com)
+ */
+
+#ifndef TREE_NODE_H
+#define TREE_NODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#define MAX_NODE_SIZE 5000
+
+/* Структура узла двоичного дерева */
+typedef struct TreeNode {
+ int val; // Значение узла
+ int height; // Высота узла
+ struct TreeNode *left; // Указатель на левый дочерний узел
+ struct TreeNode *right; // Указатель на правый дочерний узел
+} TreeNode;
+
+/* Конструктор */
+TreeNode *newTreeNode(int val) {
+ TreeNode *node;
+
+ node = (TreeNode *)malloc(sizeof(TreeNode));
+ node->val = val;
+ node->height = 0;
+ node->left = NULL;
+ node->right = NULL;
+ return node;
+}
+
+// Правила кодирования сериализации см.:
+// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
+// Массивное представление двоичного дерева:
+// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
+// Связное представление двоичного дерева:
+// /——— 15
+// /——— 7
+// /——— 3
+// | \——— 6
+// | \——— 12
+// ——— 1
+// \——— 2
+// | /——— 9
+// \——— 4
+// \——— 8
+
+/* Десериализовать список в двоичное дерево: рекурсия */
+TreeNode *arrayToTreeDFS(int *arr, int size, int i) {
+ if (i < 0 || i >= size || arr[i] == INT_MAX) {
+ return NULL;
+ }
+ TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode));
+ root->val = arr[i];
+ root->left = arrayToTreeDFS(arr, size, 2 * i + 1);
+ root->right = arrayToTreeDFS(arr, size, 2 * i + 2);
+ return root;
+}
+
+/* Десериализовать список в двоичное дерево */
+TreeNode *arrayToTree(int *arr, int size) {
+ return arrayToTreeDFS(arr, size, 0);
+}
+
+/* Сериализовать двоичное дерево в список: рекурсия */
+void treeToArrayDFS(TreeNode *root, int i, int *res, int *size) {
+ if (root == NULL) {
+ return;
+ }
+ while (i >= *size) {
+ res = realloc(res, (*size + 1) * sizeof(int));
+ res[*size] = INT_MAX;
+ (*size)++;
+ }
+ res[i] = root->val;
+ treeToArrayDFS(root->left, 2 * i + 1, res, size);
+ treeToArrayDFS(root->right, 2 * i + 2, res, size);
+}
+
+/* Сериализовать двоичное дерево в список */
+int *treeToArray(TreeNode *root, int *size) {
+ *size = 0;
+ int *res = NULL;
+ treeToArrayDFS(root, 0, res, size);
+ return res;
+}
+
+/* Освободить память двоичного дерева */
+void freeMemoryTree(TreeNode *root) {
+ if (root == NULL)
+ return;
+ freeMemoryTree(root->left);
+ freeMemoryTree(root->right);
+ free(root);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TREE_NODE_H
diff --git a/ru/codes/c/utils/uthash.h b/ru/codes/c/utils/uthash.h
new file mode 100644
index 000000000..68693bf39
--- /dev/null
+++ b/ru/codes/c/utils/uthash.h
@@ -0,0 +1,1140 @@
+/*
+Copyright (c) 2003-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#define UTHASH_VERSION 2.3.0
+
+#include /* memcmp, memset, strlen */
+#include /* ptrdiff_t */
+#include /* exit */
+
+#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT
+/* This codepath is provided for backward compatibility, but I plan to remove it. */
+#warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead"
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT
+#else
+#include /* uint8_t, uint32_t */
+#endif
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ source) this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)
+#if defined(_MSC_VER) /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#endif
+#elif defined(__MCST__) /* Elbrus C Compiler */
+#define DECLTYPE(x) (__typeof(x))
+#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#else /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE(x)
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ char **_da_dst = (char**)(&(dst)); \
+ *_da_dst = (char*)(src); \
+} while (0)
+#else
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ (dst) = DECLTYPE(dst)(src); \
+} while (0)
+#endif
+
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr) /* free fcn */
+#endif
+#ifndef uthash_bzero
+#define uthash_bzero(a,n) memset(a,'\0',n)
+#endif
+#ifndef uthash_strlen
+#define uthash_strlen(s) strlen(s)
+#endif
+
+#ifndef HASH_FUNCTION
+#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv)
+#endif
+
+#ifndef HASH_KEYCMP
+#define HASH_KEYCMP(a,b,n) memcmp(a,b,n)
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+#endif
+
+#ifndef HASH_NONFATAL_OOM
+#define HASH_NONFATAL_OOM 0
+#endif
+
+#if HASH_NONFATAL_OOM
+/* malloc failures can be recovered from */
+
+#ifndef uthash_nonfatal_oom
+#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0)
+#define IF_HASH_NONFATAL_OOM(x) x
+
+#else
+/* malloc failures result in lost memory, hash tables are unusable */
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1) /* fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory")
+#define IF_HASH_NONFATAL_OOM(x)
+
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhp */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho)))
+
+#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \
+do { \
+ struct UT_hash_handle *_hd_hh_item = (itemptrhh); \
+ unsigned _hd_bkt; \
+ HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ (head)->hh.tbl->buckets[_hd_bkt].count++; \
+ _hd_hh_item->hh_next = NULL; \
+ _hd_hh_item->hh_prev = NULL; \
+} while (0)
+
+#define HASH_VALUE(keyptr,keylen,hashv) \
+do { \
+ HASH_FUNCTION(keyptr, keylen, hashv); \
+} while (0)
+
+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \
+do { \
+ (out) = NULL; \
+ if (head) { \
+ unsigned _hf_bkt; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \
+ if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
+ } \
+ } \
+} while (0)
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ (out) = NULL; \
+ if (head) { \
+ unsigned _hf_hashv; \
+ HASH_VALUE(keyptr, keylen, _hf_hashv); \
+ HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \
+ } \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
+#define HASH_BLOOM_MAKE(tbl,oomed) \
+do { \
+ (tbl)->bloom_nbits = HASH_BLOOM; \
+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
+ if (!(tbl)->bloom_bv) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
+ } \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl) \
+do { \
+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
+
+#define HASH_BLOOM_ADD(tbl,hashv) \
+ HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#define HASH_BLOOM_TEST(tbl,hashv) \
+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl,oomed)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0U
+#endif
+
+#define HASH_MAKE_TABLE(hh,head,oomed) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \
+ if (!(head)->hh.tbl) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
+ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \
+ (head)->hh.tbl->signature = HASH_SIGNATURE; \
+ if (!(head)->hh.tbl->buckets) { \
+ HASH_RECORD_OOM(oomed); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ } else { \
+ uthash_bzero((head)->hh.tbl->buckets, \
+ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (oomed) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ } \
+ ) \
+ } \
+ } \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
+} while (0)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
+} while (0)
+
+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
+} while (0)
+
+#define HASH_APPEND_LIST(hh, head, add) \
+do { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail->next = (add); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+} while (0)
+
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
+do { \
+ do { \
+ if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \
+ break; \
+ } \
+ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
+} while (0)
+
+#ifdef NO_DECLTYPE
+#undef HASH_AKBI_INNER_LOOP
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
+do { \
+ char *_hs_saved_head = (char*)(head); \
+ do { \
+ DECLTYPE_ASSIGN(head, _hs_iter); \
+ if (cmpfcn(head, add) > 0) { \
+ DECLTYPE_ASSIGN(head, _hs_saved_head); \
+ break; \
+ } \
+ DECLTYPE_ASSIGN(head, _hs_saved_head); \
+ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
+} while (0)
+#endif
+
+#if HASH_NONFATAL_OOM
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \
+do { \
+ if (!(oomed)) { \
+ unsigned _ha_bkt; \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \
+ if (oomed) { \
+ HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \
+ HASH_DELETE_HH(hh, head, &(add)->hh); \
+ (add)->hh.tbl = NULL; \
+ uthash_nonfatal_oom(add); \
+ } else { \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+ } \
+ } else { \
+ (add)->hh.tbl = NULL; \
+ uthash_nonfatal_oom(add); \
+ } \
+} while (0)
+
+#else
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \
+do { \
+ unsigned _ha_bkt; \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+} while (0)
+
+#endif
+
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
+do { \
+ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh, add, _ha_oomed); \
+ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \
+ (head) = (add); \
+ IF_HASH_NONFATAL_OOM( } ) \
+ } else { \
+ void *_hs_iter = (head); \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \
+ if (_hs_iter) { \
+ (add)->hh.next = _hs_iter; \
+ if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \
+ HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \
+ } else { \
+ (head) = (add); \
+ } \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \
+ } else { \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ } \
+ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \
+ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \
+} while (0)
+
+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \
+do { \
+ unsigned _hs_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _hs_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
+
+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \
+ HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \
+do { \
+ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (const void*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh, add, _ha_oomed); \
+ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \
+ (head) = (add); \
+ IF_HASH_NONFATAL_OOM( } ) \
+ } else { \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \
+ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \
+} while (0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _ha_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
+
+#define HASH_TO_BKT(hashv,num_bkts,bkt) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1U)); \
+} while (0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+ HASH_DELETE_HH(hh, head, &(delptr)->hh)
+
+#define HASH_DELETE_HH(hh,head,delptrhh) \
+do { \
+ const struct UT_hash_handle *_hd_hh_del = (delptrhh); \
+ if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head) = NULL; \
+ } else { \
+ unsigned _hd_bkt; \
+ if (_hd_hh_del == (head)->hh.tbl->tail) { \
+ (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \
+ } \
+ if (_hd_hh_del->prev != NULL) { \
+ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \
+ } else { \
+ DECLTYPE_ASSIGN(head, _hd_hh_del->next); \
+ } \
+ if (_hd_hh_del->next != NULL) { \
+ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh, head, "HASH_DELETE_HH"); \
+} while (0)
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+do { \
+ unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \
+ HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \
+} while (0)
+#define HASH_ADD_STR(head,strfield,add) \
+do { \
+ unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \
+ HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \
+} while (0)
+#define HASH_REPLACE_STR(head,strfield,add,replaced) \
+do { \
+ unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \
+ HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \
+} while (0)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced) \
+ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out) \
+ HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add) \
+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
+ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#include /* fprintf, stderr */
+#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head,where) \
+do { \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ unsigned _bkt_i; \
+ unsigned _count = 0; \
+ char *_prev; \
+ for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \
+ unsigned _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \
+ (where), (void*)_thh->hh_prev, (void*)_prev); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \
+ (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \
+ (where), (head)->hh.tbl->num_items, _count); \
+ } \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev != (char*)_thh->prev) { \
+ HASH_OOPS("%s: invalid prev %p, actual %p\n", \
+ (where), (void*)_thh->prev, (void*)_prev); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("%s: invalid app item count %u, actual %u\n", \
+ (where), (head)->hh.tbl->num_items, _count); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head,where)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,hashv) \
+do { \
+ unsigned _hb_keylen = (unsigned)keylen; \
+ const unsigned char *_hb_key = (const unsigned char*)(key); \
+ (hashv) = 0; \
+ while (_hb_keylen-- != 0U) { \
+ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \
+ } \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
+ * (archive link: https://archive.is/Ivcan )
+ */
+#define HASH_SAX(key,keylen,hashv) \
+do { \
+ unsigned _sx_i; \
+ const unsigned char *_hs_key = (const unsigned char*)(key); \
+ hashv = 0; \
+ for (_sx_i=0; _sx_i < keylen; _sx_i++) { \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ } \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,hashv) \
+do { \
+ unsigned _fn_i; \
+ const unsigned char *_hf_key = (const unsigned char*)(key); \
+ (hashv) = 2166136261U; \
+ for (_fn_i=0; _fn_i < keylen; _fn_i++) { \
+ hashv = hashv ^ _hf_key[_fn_i]; \
+ hashv = hashv * 16777619U; \
+ } \
+} while (0)
+
+#define HASH_OAT(key,keylen,hashv) \
+do { \
+ unsigned _ho_i; \
+ const unsigned char *_ho_key=(const unsigned char*)(key); \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+} while (0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,hashv) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ unsigned const char *_hj_key=(unsigned const char*)(key); \
+ hashv = 0xfeedbeefu; \
+ _hj_i = _hj_j = 0x9e3779b9u; \
+ _hj_k = (unsigned)(keylen); \
+ while (_hj_k >= 12U) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12U; \
+ } \
+ hashv += (unsigned)(keylen); \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \
+ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \
+ case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \
+ default: ; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+} while (0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,hashv) \
+do { \
+ unsigned const char *_sfh_key=(unsigned const char*)(key); \
+ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \
+ \
+ unsigned _sfh_rem = _sfh_len & 3U; \
+ _sfh_len >>= 2; \
+ hashv = 0xcafebabeu; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0U; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2U*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ break; \
+ default: ; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+} while (0)
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \
+do { \
+ if ((head).hh_head != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ while ((out) != NULL) { \
+ if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \
+ if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \
+ break; \
+ } \
+ } \
+ if ((out)->hh.hh_next != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ } \
+} while (0)
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \
+do { \
+ UT_hash_bucket *_ha_head = &(head); \
+ _ha_head->count++; \
+ (addhh)->hh_next = _ha_head->hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (_ha_head->hh_head != NULL) { \
+ _ha_head->hh_head->hh_prev = (addhh); \
+ } \
+ _ha_head->hh_head = (addhh); \
+ if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \
+ && !(addhh)->tbl->noexpand) { \
+ HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (oomed) { \
+ HASH_DEL_IN_BKT(head,addhh); \
+ } \
+ ) \
+ } \
+} while (0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(head,delhh) \
+do { \
+ UT_hash_bucket *_hd_head = &(head); \
+ _hd_head->count--; \
+ if (_hd_head->hh_head == (delhh)) { \
+ _hd_head->hh_head = (delhh)->hh_next; \
+ } \
+ if ((delhh)->hh_prev) { \
+ (delhh)->hh_prev->hh_next = (delhh)->hh_next; \
+ } \
+ if ((delhh)->hh_next) { \
+ (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \
+ } \
+} while (0)
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
+ sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \
+ if (!_he_new_buckets) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero(_he_new_buckets, \
+ sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \
+ (tbl)->ideal_chain_maxlen = \
+ ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \
+ ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \
+ (tbl)->nonideal_items = 0; \
+ for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \
+ _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh != NULL) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[_he_bkt]); \
+ if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \
+ (tbl)->nonideal_items++; \
+ if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \
+ _he_newbkt->expand_mult++; \
+ } \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head != NULL) { \
+ _he_newbkt->hh_head->hh_prev = _he_thh; \
+ } \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+ (tbl)->num_buckets *= 2U; \
+ (tbl)->log2_num_buckets++; \
+ (tbl)->buckets = _he_new_buckets; \
+ (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \
+ ((tbl)->ineff_expands+1U) : 0U; \
+ if ((tbl)->ineff_expands > 1U) { \
+ (tbl)->noexpand = 1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+ } \
+} while (0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head != NULL) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping != 0U) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p != NULL) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \
+ _hs_psize++; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ if (_hs_q == NULL) { \
+ break; \
+ } \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \
+ if (_hs_psize == 0U) { \
+ _hs_e = _hs_q; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ _hs_qsize--; \
+ } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL) { \
+ _hs_p = ((_hs_p->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
+ } \
+ _hs_psize--; \
+ } else if ((cmpfcn( \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \
+ )) <= 0) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL) { \
+ _hs_p = ((_hs_p->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
+ } \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail != NULL ) { \
+ _hs_tail->next = ((_hs_e != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ if (_hs_e != NULL) { \
+ _hs_e->prev = ((_hs_tail != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \
+ } \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ if (_hs_tail != NULL) { \
+ _hs_tail->next = NULL; \
+ } \
+ if (_hs_nmerges <= 1U) { \
+ _hs_looping = 0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
+ } \
+ _hs_insize *= 2U; \
+ } \
+ HASH_FSCK(hh, head, "HASH_SRT"); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt = NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if ((src) != NULL) { \
+ for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh != NULL; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \
+ _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh != NULL) { \
+ _last_elt_hh->next = _elt; \
+ } \
+ if ((dst) == NULL) { \
+ DECLTYPE_ASSIGN(dst, _elt); \
+ HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (_hs_oomed) { \
+ uthash_nonfatal_oom(_elt); \
+ (dst) = NULL; \
+ continue; \
+ } \
+ ) \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \
+ (dst)->hh_dst.tbl->num_items++; \
+ IF_HASH_NONFATAL_OOM( \
+ if (_hs_oomed) { \
+ HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \
+ HASH_DELETE_HH(hh_dst, dst, _dst_hh); \
+ _dst_hh->tbl = NULL; \
+ uthash_nonfatal_oom(_elt); \
+ continue; \
+ } \
+ ) \
+ HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if ((head) != NULL) { \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head) = NULL; \
+ } \
+} while (0)
+
+#define HASH_OVERHEAD(hh,head) \
+ (((head) != NULL) ? ( \
+ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
+ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
+ sizeof(UT_hash_table) + \
+ (HASH_BLOOM_BYTELEN))) : 0U)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#else
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1u
+#define HASH_BLOOM_SIGNATURE 0xb12220f2u
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+ uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+ uint8_t *bloom_bv;
+ uint8_t bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ const void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/ru/codes/c/utils/vector.h b/ru/codes/c/utils/vector.h
new file mode 100644
index 000000000..a022fa6f2
--- /dev/null
+++ b/ru/codes/c/utils/vector.h
@@ -0,0 +1,259 @@
+/**
+ * File: vector.h
+ * Created Time: 2023-07-13
+ * Author: Zuoxun (845242523@qq.com)、Gonglja (glj0@outlook.com)
+ */
+
+#ifndef VECTOR_H
+#define VECTOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Определить тип вектора */
+typedef struct vector {
+ int size; // Текущий размер вектора
+ int capacity; // Текущая емкость вектора
+ int depth; // Текущая глубина вектора
+ void **data; // Массив указателей на данные
+} vector;
+
+/* Создать вектор */
+vector *newVector() {
+ vector *v = malloc(sizeof(vector));
+ v->size = 0;
+ v->capacity = 4;
+ v->depth = 1;
+ v->data = malloc(v->capacity * sizeof(void *));
+ return v;
+}
+
+/* Создать вектор, указав размер и значение элементов по умолчанию */
+vector *_newVector(int size, void *elem, int elemSize) {
+ vector *v = malloc(sizeof(vector));
+ v->size = size;
+ v->capacity = size;
+ v->depth = 1;
+ v->data = malloc(v->capacity * sizeof(void *));
+ for (int i = 0; i < size; i++) {
+ void *tmp = malloc(sizeof(char) * elemSize);
+ memcpy(tmp, elem, elemSize);
+ v->data[i] = tmp;
+ }
+ return v;
+}
+
+/* Уничтожить вектор */
+void delVector(vector *v) {
+ if (v) {
+ if (v->depth == 0) {
+ return;
+ } else if (v->depth == 1) {
+ for (int i = 0; i < v->size; i++) {
+ free(v->data[i]);
+ }
+ free(v);
+ } else {
+ for (int i = 0; i < v->size; i++) {
+ delVector(v->data[i]);
+ }
+ v->depth--;
+ }
+ }
+}
+
+/* Добавить элемент в конец вектора (копированием) */
+void vectorPushback(vector *v, void *elem, int elemSize) {
+ if (v->size == v->capacity) {
+ v->capacity *= 2;
+ v->data = realloc(v->data, v->capacity * sizeof(void *));
+ }
+ void *tmp = malloc(sizeof(char) * elemSize);
+ memcpy(tmp, elem, elemSize);
+ v->data[v->size++] = tmp;
+}
+
+/* Извлечь элемент из конца вектора */
+void vectorPopback(vector *v) {
+ if (v->size != 0) {
+ free(v->data[v->size - 1]);
+ v->size--;
+ }
+}
+
+/* Очистить вектор */
+void vectorClear(vector *v) {
+ delVector(v);
+ v->size = 0;
+ v->capacity = 4;
+ v->depth = 1;
+ v->data = malloc(v->capacity * sizeof(void *));
+}
+
+/* Получить размер вектора */
+int vectorSize(vector *v) {
+ return v->size;
+}
+
+/* Получить последний элемент вектора */
+void *vectorBack(vector *v) {
+ int n = v->size;
+ return n > 0 ? v->data[n - 1] : NULL;
+}
+
+/* Получить первый элемент вектора */
+void *vectorFront(vector *v) {
+ return v->size > 0 ? v->data[0] : NULL;
+}
+
+/* Получить элемент вектора по индексу pos */
+void *vectorAt(vector *v, int pos) {
+ if (pos < 0 || pos >= v->size) {
+ printf("vectorAt: out of range\n");
+ return NULL;
+ }
+ return v->data[pos];
+}
+
+/* Установить элемент вектора по индексу pos */
+void vectorSet(vector *v, int pos, void *elem, int elemSize) {
+ if (pos < 0 || pos >= v->size) {
+ printf("vectorSet: out of range\n");
+ return;
+ }
+ free(v->data[pos]);
+ void *tmp = malloc(sizeof(char) * elemSize);
+ memcpy(tmp, elem, elemSize);
+ v->data[pos] = tmp;
+}
+
+/* Расширение вектора */
+void vectorExpand(vector *v) {
+ v->capacity *= 2;
+ v->data = realloc(v->data, v->capacity * sizeof(void *));
+}
+
+/* Сжатие вектора */
+void vectorShrink(vector *v) {
+ v->capacity /= 2;
+ v->data = realloc(v->data, v->capacity * sizeof(void *));
+}
+
+/* Вставить элемент по индексу pos в вектор */
+void vectorInsert(vector *v, int pos, void *elem, int elemSize) {
+ if (v->size == v->capacity) {
+ vectorExpand(v);
+ }
+ for (int j = v->size; j > pos; j--) {
+ v->data[j] = v->data[j - 1];
+ }
+ void *tmp = malloc(sizeof(char) * elemSize);
+ memcpy(tmp, elem, elemSize);
+ v->data[pos] = tmp;
+ v->size++;
+}
+
+/* Удалить элемент вектора по индексу pos */
+void vectorErase(vector *v, int pos) {
+ if (v->size != 0) {
+ free(v->data[pos]);
+ for (int j = pos; j < v->size - 1; j++) {
+ v->data[j] = v->data[j + 1];
+ }
+ v->size--;
+ }
+}
+
+/* Обмен элементов вектора */
+void vectorSwap(vector *v, int i, int j) {
+ void *tmp = v->data[i];
+ v->data[i] = v->data[j];
+ v->data[j] = tmp;
+}
+
+/* Пуст ли вектор */
+bool vectorEmpty(vector *v) {
+ return v->size == 0;
+}
+
+/* Заполнен ли вектор */
+bool vectorFull(vector *v) {
+ return v->size == v->capacity;
+}
+
+/* Равны ли векторы */
+bool vectorEqual(vector *v1, vector *v2) {
+ if (v1->size != v2->size) {
+ printf("size not equal\n");
+ return false;
+ }
+ for (int i = 0; i < v1->size; i++) {
+ void *a = v1->data[i];
+ void *b = v2->data[i];
+ if (memcmp(a, b, sizeof(a)) != 0) {
+ printf("data %d not equal\n", i);
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Отсортировать содержимое вектора */
+void vectorSort(vector *v, int (*cmp)(const void *, const void *)) {
+ qsort(v->data, v->size, sizeof(void *), cmp);
+}
+
+/* Функция печати: нужно передать функцию для вывода значения переменной */
+/* В настоящее время поддерживается только вывод vector глубины 1 */
+void printVector(vector *v, void (*printFunc)(vector *v, void *p)) {
+ if (v) {
+ if (v->depth == 0) {
+ return;
+ } else if (v->depth == 1) {
+ if(v->size == 0) {
+ printf("\n");
+ return;
+ }
+ for (int i = 0; i < v->size; i++) {
+ if (i == 0) {
+ printf("[");
+ } else if (i == v->size - 1) {
+ printFunc(v, v->data[i]);
+ printf("]\r\n");
+ break;
+ }
+ printFunc(v, v->data[i]);
+ printf(",");
+ }
+ } else {
+ for (int i = 0; i < v->size; i++) {
+ printVector(v->data[i], printFunc);
+ }
+ v->depth--;
+ }
+ }
+}
+
+/* В настоящее время поддерживается только вывод vector глубины 2 */
+void printVectorMatrix(vector *vv, void (*printFunc)(vector *v, void *p)) {
+ printf("[\n");
+ for (int i = 0; i < vv->size; i++) {
+ vector *v = (vector *)vv->data[i];
+ printf(" [");
+ for (int j = 0; j < v->size; j++) {
+ printFunc(v, v->data[j]);
+ if (j != v->size - 1)
+ printf(",");
+ }
+ printf("],");
+ printf("\n");
+ }
+ printf("]\n");
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // VECTOR_H
diff --git a/ru/codes/c/utils/vertex.h b/ru/codes/c/utils/vertex.h
new file mode 100644
index 000000000..28ca1f2a8
--- /dev/null
+++ b/ru/codes/c/utils/vertex.h
@@ -0,0 +1,49 @@
+/**
+ * File: vertex.h
+ * Created Time: 2023-10-28
+ * Author: krahets (krahets@163.com)
+ */
+
+#ifndef VERTEX_H
+#define VERTEX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Структура вершины */
+typedef struct {
+ int val;
+} Vertex;
+
+/* Конструктор, инициализирующий новый узел */
+Vertex *newVertex(int val) {
+ Vertex *vet;
+ vet = (Vertex *)malloc(sizeof(Vertex));
+ vet->val = val;
+ return vet;
+}
+
+/* Преобразовать массив значений в массив вершин */
+Vertex **valsToVets(int *vals, int size) {
+ Vertex **vertices = (Vertex **)malloc(size * sizeof(Vertex *));
+ for (int i = 0; i < size; ++i) {
+ vertices[i] = newVertex(vals[i]);
+ }
+ return vertices;
+}
+
+/* Преобразовать массив вершин в массив значений */
+int *vetsToVals(Vertex **vertices, int size) {
+ int *vals = (int *)malloc(size * sizeof(int));
+ for (int i = 0; i < size; ++i) {
+ vals[i] = vertices[i]->val;
+ }
+ return vals;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // VERTEX_H
diff --git a/ru/codes/cpp/.gitignore b/ru/codes/cpp/.gitignore
new file mode 100644
index 000000000..dc1ffacf4
--- /dev/null
+++ b/ru/codes/cpp/.gitignore
@@ -0,0 +1,10 @@
+# Ignore all
+*
+# Unignore all with extensions
+!*.*
+# Unignore all dirs
+!*/
+
+*.dSYM/
+
+build/
diff --git a/ru/codes/cpp/CMakeLists.txt b/ru/codes/cpp/CMakeLists.txt
new file mode 100644
index 000000000..1e80bc4d7
--- /dev/null
+++ b/ru/codes/cpp/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.10)
+project(hello_algo CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+
+include_directories(./include)
+
+add_subdirectory(chapter_computational_complexity)
+add_subdirectory(chapter_array_and_linkedlist)
+add_subdirectory(chapter_stack_and_queue)
+add_subdirectory(chapter_hashing)
+add_subdirectory(chapter_tree)
+add_subdirectory(chapter_heap)
+add_subdirectory(chapter_graph)
+add_subdirectory(chapter_searching)
+add_subdirectory(chapter_sorting)
+add_subdirectory(chapter_divide_and_conquer)
+add_subdirectory(chapter_backtracking)
+add_subdirectory(chapter_dynamic_programming)
+add_subdirectory(chapter_greedy)
diff --git a/ru/codes/cpp/chapter_array_and_linkedlist/CMakeLists.txt b/ru/codes/cpp/chapter_array_and_linkedlist/CMakeLists.txt
new file mode 100644
index 000000000..2e933e016
--- /dev/null
+++ b/ru/codes/cpp/chapter_array_and_linkedlist/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(array array.cpp)
+add_executable(linked_list linked_list.cpp)
+add_executable(list list.cpp)
+add_executable(my_list my_list.cpp)
diff --git a/ru/codes/cpp/chapter_array_and_linkedlist/array.cpp b/ru/codes/cpp/chapter_array_and_linkedlist/array.cpp
new file mode 100644
index 000000000..50ca477c8
--- /dev/null
+++ b/ru/codes/cpp/chapter_array_and_linkedlist/array.cpp
@@ -0,0 +1,113 @@
+/**
+ * File: array.cpp
+ * Created Time: 2022-11-25
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Случайный доступ к элементу */
+int randomAccess(int *nums, int size) {
+ // Случайным образом выбрать число из интервала [0, size)
+ int randomIndex = rand() % size;
+ // Получить и вернуть случайный элемент
+ int randomNum = nums[randomIndex];
+ return randomNum;
+}
+
+/* Увеличить длину массива */
+int *extend(int *nums, int size, int enlarge) {
+ // Инициализировать массив увеличенной длины
+ int *res = new int[size + enlarge];
+ // Скопировать все элементы исходного массива в новый массив
+ for (int i = 0; i < size; i++) {
+ res[i] = nums[i];
+ }
+ // Освободить память
+ delete[] nums;
+ // Вернуть новый массив после расширения
+ return res;
+}
+
+/* Вставить элемент num по индексу index в массив */
+void insert(int *nums, int size, int num, int index) {
+ // Сдвинуть элемент с индексом index и все последующие элементы на одну позицию назад
+ for (int i = size - 1; i > index; i--) {
+ nums[i] = nums[i - 1];
+ }
+ // Присвоить num элементу по индексу index
+ nums[index] = num;
+}
+
+/* Удалить элемент по индексу index */
+void remove(int *nums, int size, int index) {
+ // Сдвинуть все элементы после индекса index на одну позицию вперед
+ for (int i = index; i < size - 1; i++) {
+ nums[i] = nums[i + 1];
+ }
+}
+
+/* Обход массива */
+void traverse(int *nums, int size) {
+ int count = 0;
+ // Обход массива по индексам
+ for (int i = 0; i < size; i++) {
+ count += nums[i];
+ }
+}
+
+/* Найти заданный элемент в массиве */
+int find(int *nums, int size, int target) {
+ for (int i = 0; i < size; i++) {
+ if (nums[i] == target)
+ return i;
+ }
+ return -1;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация массива */
+ int size = 5;
+ int *arr = new int[size];
+ cout << "Массив arr = ";
+ printArray(arr, size);
+
+ int *nums = new int[size]{1, 3, 2, 5, 4};
+ cout << "Массив nums = ";
+ printArray(nums, size);
+
+ /* Случайный доступ */
+ int randomNum = randomAccess(nums, size);
+ cout << "Случайный элемент из nums = " << randomNum << endl;
+
+ /* Расширение длины */
+ int enlarge = 3;
+ nums = extend(nums, size, enlarge);
+ size += enlarge;
+ cout << "После увеличения длины массива до 8 nums = ";
+ printArray(nums, size);
+
+ /* Вставка элемента */
+ insert(nums, size, 6, 3);
+ cout << "После вставки числа 6 по индексу 3 nums = ";
+ printArray(nums, size);
+
+ /* Удаление элемента */
+ remove(nums, size, 2);
+ cout << "После удаления элемента по индексу 2 nums = ";
+ printArray(nums, size);
+
+ /* Обход массива */
+ traverse(nums, size);
+
+ /* Поиск элемента */
+ int index = find(nums, size, 3);
+ cout << "Индекс элемента 3 в nums = " << index << endl;
+
+ // Освободить память
+ delete[] arr;
+ delete[] nums;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp b/ru/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp
new file mode 100644
index 000000000..f0f52242a
--- /dev/null
+++ b/ru/codes/cpp/chapter_array_and_linkedlist/linked_list.cpp
@@ -0,0 +1,89 @@
+/**
+ * File: linked_list.cpp
+ * Created Time: 2022-11-25
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Вставить узел P после узла n0 в связном списке */
+void insert(ListNode *n0, ListNode *P) {
+ ListNode *n1 = n0->next;
+ P->next = n1;
+ n0->next = P;
+}
+
+/* Удалить первый узел после узла n0 в связном списке */
+void remove(ListNode *n0) {
+ if (n0->next == nullptr)
+ return;
+ // n0 -> P -> n1
+ ListNode *P = n0->next;
+ ListNode *n1 = P->next;
+ n0->next = n1;
+ // Освободить память
+ delete P;
+}
+
+/* Доступ к узлу связного списка по индексу index */
+ListNode *access(ListNode *head, int index) {
+ for (int i = 0; i < index; i++) {
+ if (head == nullptr)
+ return nullptr;
+ head = head->next;
+ }
+ return head;
+}
+
+/* Найти в связном списке первый узел со значением target */
+int find(ListNode *head, int target) {
+ int index = 0;
+ while (head != nullptr) {
+ if (head->val == target)
+ return index;
+ head = head->next;
+ index++;
+ }
+ return -1;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация связного списка */
+ // Инициализация всех узлов
+ ListNode *n0 = new ListNode(1);
+ ListNode *n1 = new ListNode(3);
+ ListNode *n2 = new ListNode(2);
+ ListNode *n3 = new ListNode(5);
+ ListNode *n4 = new ListNode(4);
+ // Построить ссылки между узлами
+ n0->next = n1;
+ n1->next = n2;
+ n2->next = n3;
+ n3->next = n4;
+ cout << "Инициализированный связный список" << endl;
+ printLinkedList(n0);
+
+ /* Вставка узла */
+ insert(n0, new ListNode(0));
+ cout << "Связный список после вставки узла" << endl;
+ printLinkedList(n0);
+
+ /* Удаление узла */
+ remove(n0);
+ cout << "Связный список после удаления узла" << endl;
+ printLinkedList(n0);
+
+ /* Доступ к узлу */
+ ListNode *node = access(n0, 3);
+ cout << "Значение узла по индексу 3 в связном списке = " << node->val << endl;
+
+ /* Поиск узла */
+ int index = find(n0, 2);
+ cout << "Индекс узла со значением 2 в связном списке = " << index << endl;
+
+ // Освободить память
+ freeMemoryLinkedList(n0);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_array_and_linkedlist/list.cpp b/ru/codes/cpp/chapter_array_and_linkedlist/list.cpp
new file mode 100644
index 000000000..04c7956eb
--- /dev/null
+++ b/ru/codes/cpp/chapter_array_and_linkedlist/list.cpp
@@ -0,0 +1,72 @@
+/**
+ * File: list.cpp
+ * Created Time: 2022-11-25
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Driver Code */
+int main() {
+ /* Инициализация списка */
+ vector nums = {1, 3, 2, 5, 4};
+ cout << "Список nums = ";
+ printVector(nums);
+
+ /* Доступ к элементу */
+ int num = nums[1];
+ cout << "Элемент по индексу 1: num = " << num << endl;
+
+ /* Обновление элемента */
+ nums[1] = 0;
+ cout << "После обновления элемента по индексу 1 на 0 nums = ";
+ printVector(nums);
+
+ /* Очистить список */
+ nums.clear();
+ cout << "После очистки списка nums = ";
+ printVector(nums);
+
+ /* Добавление элемента в конец */
+ nums.push_back(1);
+ nums.push_back(3);
+ nums.push_back(2);
+ nums.push_back(5);
+ nums.push_back(4);
+ cout << "После добавления элемента nums = ";
+ printVector(nums);
+
+ /* Вставка элемента в середину */
+ nums.insert(nums.begin() + 3, 6);
+ cout << "После вставки числа 6 по индексу 3 nums = ";
+ printVector(nums);
+
+ /* Удаление элемента */
+ nums.erase(nums.begin() + 3);
+ cout << "После удаления элемента по индексу 3 nums = ";
+ printVector(nums);
+
+ /* Обходить список по индексам */
+ int count = 0;
+ for (int i = 0; i < nums.size(); i++) {
+ count += nums[i];
+ }
+ /* Непосредственно обходить элементы списка */
+ count = 0;
+ for (int x : nums) {
+ count += x;
+ }
+
+ /* Объединить два списка */
+ vector nums1 = {6, 8, 7, 10, 9};
+ nums.insert(nums.end(), nums1.begin(), nums1.end());
+ cout << "После присоединения списка nums1 к nums nums = ";
+ printVector(nums);
+
+ /* Отсортировать список */
+ sort(nums.begin(), nums.end());
+ cout << "После сортировки списка nums = ";
+ printVector(nums);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_array_and_linkedlist/my_list.cpp b/ru/codes/cpp/chapter_array_and_linkedlist/my_list.cpp
new file mode 100644
index 000000000..4ced0b491
--- /dev/null
+++ b/ru/codes/cpp/chapter_array_and_linkedlist/my_list.cpp
@@ -0,0 +1,171 @@
+/**
+ * File: my_list.cpp
+ * Created Time: 2022-11-25
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Класс списка */
+class MyList {
+ private:
+ int *arr; // Массив (для хранения элементов списка)
+ int arrCapacity = 10; // Вместимость списка
+ int arrSize = 0; // Длина списка (текущее число элементов)
+ int extendRatio = 2; // Коэффициент увеличения списка при каждом расширении
+
+ public:
+ /* Конструктор */
+ MyList() {
+ arr = new int[arrCapacity];
+ }
+
+ /* Метод-деструктор */
+ ~MyList() {
+ delete[] arr;
+ }
+
+ /* Получить длину списка (текущее число элементов) */
+ int size() {
+ return arrSize;
+ }
+
+ /* Получить вместимость списка */
+ int capacity() {
+ return arrCapacity;
+ }
+
+ /* Доступ к элементу */
+ int get(int index) {
+ // Если индекс выходит за границы, выбрасывается исключение; далее аналогично
+ if (index < 0 || index >= size())
+ throw out_of_range("индекс выходит за границы");
+ return arr[index];
+ }
+
+ /* Обновление элемента */
+ void set(int index, int num) {
+ if (index < 0 || index >= size())
+ throw out_of_range("индекс выходит за границы");
+ arr[index] = num;
+ }
+
+ /* Добавление элемента в конец */
+ void add(int num) {
+ // При превышении вместимости по числу элементов запускается расширение
+ if (size() == capacity())
+ extendCapacity();
+ arr[size()] = num;
+ // Обновить число элементов
+ arrSize++;
+ }
+
+ /* Вставка элемента в середину */
+ void insert(int index, int num) {
+ if (index < 0 || index >= size())
+ throw out_of_range("индекс выходит за границы");
+ // При превышении вместимости по числу элементов запускается расширение
+ if (size() == capacity())
+ extendCapacity();
+ // Сдвинуть элемент с индексом index и все следующие элементы на одну позицию назад
+ for (int j = size() - 1; j >= index; j--) {
+ arr[j + 1] = arr[j];
+ }
+ arr[index] = num;
+ // Обновить число элементов
+ arrSize++;
+ }
+
+ /* Удаление элемента */
+ int remove(int index) {
+ if (index < 0 || index >= size())
+ throw out_of_range("индекс выходит за границы");
+ int num = arr[index];
+ // Сдвинуть все элементы после индекса index на одну позицию вперед
+ for (int j = index; j < size() - 1; j++) {
+ arr[j] = arr[j + 1];
+ }
+ // Обновить число элементов
+ arrSize--;
+ // Вернуть удаленный элемент
+ return num;
+ }
+
+ /* Расширение списка */
+ void extendCapacity() {
+ // Создать новый массив длиной в extendRatio раз больше исходного массива
+ int newCapacity = capacity() * extendRatio;
+ int *tmp = arr;
+ arr = new int[newCapacity];
+ // Скопировать все элементы исходного массива в новый массив
+ for (int i = 0; i < size(); i++) {
+ arr[i] = tmp[i];
+ }
+ // Освободить память
+ delete[] tmp;
+ arrCapacity = newCapacity;
+ }
+
+ /* Преобразовать список в Vector для вывода */
+ vector toVector() {
+ // Преобразовывать только элементы списка в пределах фактической длины
+ vector vec(size());
+ for (int i = 0; i < size(); i++) {
+ vec[i] = arr[i];
+ }
+ return vec;
+ }
+};
+
+/* Driver Code */
+int main() {
+ /* Инициализация списка */
+ MyList *nums = new MyList();
+ /* Добавление элемента в конец */
+ nums->add(1);
+ nums->add(3);
+ nums->add(2);
+ nums->add(5);
+ nums->add(4);
+ cout << "Список nums = ";
+ vector vec = nums->toVector();
+ printVector(vec);
+ cout << "Вместимость = " << nums->capacity() << ", длина = " << nums->size() << endl;
+
+ /* Вставка элемента в середину */
+ nums->insert(3, 6);
+ cout << "После вставки числа 6 по индексу 3 nums = ";
+ vec = nums->toVector();
+ printVector(vec);
+
+ /* Удаление элемента */
+ nums->remove(3);
+ cout << "После удаления элемента по индексу 3 nums = ";
+ vec = nums->toVector();
+ printVector(vec);
+
+ /* Доступ к элементу */
+ int num = nums->get(1);
+ cout << "Элемент по индексу 1: num = " << num << endl;
+
+ /* Обновление элемента */
+ nums->set(1, 0);
+ cout << "После обновления элемента по индексу 1 на 0 nums = ";
+ vec = nums->toVector();
+ printVector(vec);
+
+ /* Проверка механизма расширения */
+ for (int i = 0; i < 10; i++) {
+ // При i = 5 длина списка превысит его вместимость, и в этот момент сработает механизм расширения
+ nums->add(i);
+ }
+ cout << "После расширения список nums = ";
+ vec = nums->toVector();
+ printVector(vec);
+ cout << "Вместимость = " << nums->capacity() << ", длина = " << nums->size() << endl;
+
+ // Освободить память
+ delete nums;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_backtracking/CMakeLists.txt b/ru/codes/cpp/chapter_backtracking/CMakeLists.txt
new file mode 100644
index 000000000..6c271e330
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_executable(preorder_traversal_i_compact preorder_traversal_i_compact.cpp)
+add_executable(preorder_traversal_ii_compact preorder_traversal_ii_compact.cpp)
+add_executable(preorder_traversal_iii_compact preorder_traversal_iii_compact.cpp)
+add_executable(preorder_traversal_iii_template preorder_traversal_iii_template.cpp)
+add_executable(permutations_i permutations_i.cpp)
+add_executable(permutations_ii permutations_ii.cpp)
+add_executable(n_queens n_queens.cpp)
+add_executable(subset_sum_i_naive subset_sum_i_naive.cpp)
+add_executable(subset_sum_i subset_sum_i.cpp)
+add_executable(subset_sum_ii subset_sum_ii.cpp)
diff --git a/ru/codes/cpp/chapter_backtracking/n_queens.cpp b/ru/codes/cpp/chapter_backtracking/n_queens.cpp
new file mode 100644
index 000000000..d40a8714f
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/n_queens.cpp
@@ -0,0 +1,65 @@
+/**
+ * File: n_queens.cpp
+ * Created Time: 2023-05-04
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Алгоритм бэктрекинга: n ферзей */
+void backtrack(int row, int n, vector> &state, vector>> &res, vector &cols,
+ vector &diags1, vector &diags2) {
+ // Когда все строки уже обработаны, записать решение
+ if (row == n) {
+ res.push_back(state);
+ return;
+ }
+ // Обойти все столбцы
+ for (int col = 0; col < n; col++) {
+ // Вычислить главную и побочную диагонали, соответствующие этой клетке
+ int diag1 = row - col + n - 1;
+ int diag2 = row + col;
+ // Отсечение: в столбце, главной диагонали и побочной диагонали этой клетки не должно быть ферзей
+ if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
+ // Попытка: поставить ферзя в эту клетку
+ state[row][col] = "Q";
+ cols[col] = diags1[diag1] = diags2[diag2] = true;
+ // Перейти к размещению следующей строки
+ backtrack(row + 1, n, state, res, cols, diags1, diags2);
+ // Откат: восстановить эту клетку как пустую
+ state[row][col] = "#";
+ cols[col] = diags1[diag1] = diags2[diag2] = false;
+ }
+ }
+}
+
+/* Решить задачу о n ферзях */
+vector>> nQueens(int n) {
+ // Инициализировать доску размера n*n, где 'Q' обозначает ферзя, а '#' — пустую клетку
+ vector> state(n, vector(n, "#"));
+ vector cols(n, false); // Отмечать, есть ли ферзь в столбце
+ vector diags1(2 * n - 1, false); // Отмечать наличие ферзя на главной диагонали
+ vector diags2(2 * n - 1, false); // Отмечать наличие ферзя на побочной диагонали
+ vector>> res;
+
+ backtrack(0, n, state, res, cols, diags1, diags2);
+
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int n = 4;
+ vector>> res = nQueens(n);
+
+ cout << "Размер входной доски = " << n << endl;
+ cout << "Количество способов расстановки ферзей: " << res.size() << endl;
+ for (const vector> &state : res) {
+ cout << "--------------------" << endl;
+ for (const vector &row : state) {
+ printVector(row);
+ }
+ }
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_backtracking/permutations_i.cpp b/ru/codes/cpp/chapter_backtracking/permutations_i.cpp
new file mode 100644
index 000000000..64b77a5f0
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/permutations_i.cpp
@@ -0,0 +1,54 @@
+/**
+ * File: permutations_i.cpp
+ * Created Time: 2023-04-24
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Алгоритм бэктрекинга: все перестановки I */
+void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) {
+ // Когда длина состояния равна числу элементов, записать решение
+ if (state.size() == choices.size()) {
+ res.push_back(state);
+ return;
+ }
+ // Перебор всех вариантов выбора
+ for (int i = 0; i < choices.size(); i++) {
+ int choice = choices[i];
+ // Отсечение: нельзя выбирать один и тот же элемент повторно
+ if (!selected[i]) {
+ // Попытка: сделать выбор и обновить состояние
+ selected[i] = true;
+ state.push_back(choice);
+ // Перейти к следующему выбору
+ backtrack(state, choices, selected, res);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ selected[i] = false;
+ state.pop_back();
+ }
+ }
+}
+
+/* Все перестановки I */
+vector> permutationsI(vector nums) {
+ vector state;
+ vector selected(nums.size(), false);
+ vector> res;
+ backtrack(state, nums, selected, res);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ vector nums = {1, 2, 3};
+
+ vector> res = permutationsI(nums);
+
+ cout << "Входной массив nums = ";
+ printVector(nums);
+ cout << "Все перестановки res = ";
+ printVectorMatrix(res);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_backtracking/permutations_ii.cpp b/ru/codes/cpp/chapter_backtracking/permutations_ii.cpp
new file mode 100644
index 000000000..d52b6ed47
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/permutations_ii.cpp
@@ -0,0 +1,56 @@
+/**
+ * File: permutations_ii.cpp
+ * Created Time: 2023-04-24
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Алгоритм бэктрекинга: все перестановки II */
+void backtrack(vector &state, const vector &choices, vector &selected, vector> &res) {
+ // Когда длина состояния равна числу элементов, записать решение
+ if (state.size() == choices.size()) {
+ res.push_back(state);
+ return;
+ }
+ // Перебор всех вариантов выбора
+ unordered_set duplicated;
+ for (int i = 0; i < choices.size(); i++) {
+ int choice = choices[i];
+ // Отсечение: нельзя выбирать один и тот же элемент повторно и нельзя повторно выбирать равные элементы
+ if (!selected[i] && duplicated.find(choice) == duplicated.end()) {
+ // Попытка: сделать выбор и обновить состояние
+ duplicated.emplace(choice); // Записать значения уже выбранных элементов
+ selected[i] = true;
+ state.push_back(choice);
+ // Перейти к следующему выбору
+ backtrack(state, choices, selected, res);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ selected[i] = false;
+ state.pop_back();
+ }
+ }
+}
+
+/* Все перестановки II */
+vector> permutationsII(vector nums) {
+ vector state;
+ vector selected(nums.size(), false);
+ vector> res;
+ backtrack(state, nums, selected, res);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ vector nums = {1, 1, 2};
+
+ vector> res = permutationsII(nums);
+
+ cout << "Входной массив nums = ";
+ printVector(nums);
+ cout << "Все перестановки res = ";
+ printVectorMatrix(res);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp b/ru/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp
new file mode 100644
index 000000000..0e92e7253
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/preorder_traversal_i_compact.cpp
@@ -0,0 +1,39 @@
+/**
+ * File: preorder_traversal_i_compact.cpp
+ * Created Time: 2023-04-16
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+vector res;
+
+/* Предварительный обход: пример 1 */
+void preOrder(TreeNode *root) {
+ if (root == nullptr) {
+ return;
+ }
+ if (root->val == 7) {
+ // Записать решение
+ res.push_back(root);
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+}
+
+/* Driver Code */
+int main() {
+ TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7});
+ cout << "\nИнициализация двоичного дерева" << endl;
+ printTree(root);
+
+ // Предварительный обход
+ preOrder(root);
+
+ cout << "\nВывести все узлы со значением 7" << endl;
+ vector vals;
+ for (TreeNode *node : res) {
+ vals.push_back(node->val);
+ }
+ printVector(vals);
+}
diff --git a/ru/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp b/ru/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp
new file mode 100644
index 000000000..12ccd265e
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/preorder_traversal_ii_compact.cpp
@@ -0,0 +1,46 @@
+/**
+ * File: preorder_traversal_ii_compact.cpp
+ * Created Time: 2023-04-16
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+vector path;
+vector> res;
+
+/* Предварительный обход: пример 2 */
+void preOrder(TreeNode *root) {
+ if (root == nullptr) {
+ return;
+ }
+ // Попытка
+ path.push_back(root);
+ if (root->val == 7) {
+ // Записать решение
+ res.push_back(path);
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // Откат
+ path.pop_back();
+}
+
+/* Driver Code */
+int main() {
+ TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7});
+ cout << "\nИнициализация двоичного дерева" << endl;
+ printTree(root);
+
+ // Предварительный обход
+ preOrder(root);
+
+ cout << "\nВывести все пути от корня к узлу 7" << endl;
+ for (vector &path : res) {
+ vector vals;
+ for (TreeNode *node : path) {
+ vals.push_back(node->val);
+ }
+ printVector(vals);
+ }
+}
diff --git a/ru/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp b/ru/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp
new file mode 100644
index 000000000..09e95ec7f
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/preorder_traversal_iii_compact.cpp
@@ -0,0 +1,47 @@
+/**
+ * File: preorder_traversal_iii_compact.cpp
+ * Created Time: 2023-04-16
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+vector path;
+vector> res;
+
+/* Предварительный обход: пример 3 */
+void preOrder(TreeNode *root) {
+ // Отсечение
+ if (root == nullptr || root->val == 3) {
+ return;
+ }
+ // Попытка
+ path.push_back(root);
+ if (root->val == 7) {
+ // Записать решение
+ res.push_back(path);
+ }
+ preOrder(root->left);
+ preOrder(root->right);
+ // Откат
+ path.pop_back();
+}
+
+/* Driver Code */
+int main() {
+ TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7});
+ cout << "\nИнициализация двоичного дерева" << endl;
+ printTree(root);
+
+ // Предварительный обход
+ preOrder(root);
+
+ cout << "\nВывести все пути от корня к узлу 7, не содержащие узлов со значением 3" << endl;
+ for (vector &path : res) {
+ vector vals;
+ for (TreeNode *node : path) {
+ vals.push_back(node->val);
+ }
+ printVector(vals);
+ }
+}
diff --git a/ru/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp b/ru/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp
new file mode 100644
index 000000000..081c475b9
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/preorder_traversal_iii_template.cpp
@@ -0,0 +1,76 @@
+/**
+ * File: preorder_traversal_iii_template.cpp
+ * Created Time: 2023-04-16
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Проверить, является ли текущее состояние решением */
+bool isSolution(vector &state) {
+ return !state.empty() && state.back()->val == 7;
+}
+
+/* Записать решение */
+void recordSolution(vector &state, vector> &res) {
+ res.push_back(state);
+}
+
+/* Проверить, допустим ли этот выбор в текущем состоянии */
+bool isValid(vector &state, TreeNode *choice) {
+ return choice != nullptr && choice->val != 3;
+}
+
+/* Обновить состояние */
+void makeChoice(vector &state, TreeNode *choice) {
+ state.push_back(choice);
+}
+
+/* Восстановить состояние */
+void undoChoice(vector &state, TreeNode *choice) {
+ state.pop_back();
+}
+
+/* Алгоритм бэктрекинга: пример 3 */
+void backtrack(vector &state, vector &choices, vector> &res) {
+ // Проверить, является ли текущее состояние решением
+ if (isSolution(state)) {
+ // Записать решение
+ recordSolution(state, res);
+ }
+ // Перебор всех вариантов выбора
+ for (TreeNode *choice : choices) {
+ // Отсечение: проверить допустимость выбора
+ if (isValid(state, choice)) {
+ // Попытка: сделать выбор и обновить состояние
+ makeChoice(state, choice);
+ // Перейти к следующему выбору
+ vector nextChoices{choice->left, choice->right};
+ backtrack(state, nextChoices, res);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ undoChoice(state, choice);
+ }
+ }
+}
+
+/* Driver Code */
+int main() {
+ TreeNode *root = vectorToTree(vector{1, 7, 3, 4, 5, 6, 7});
+ cout << "\nИнициализация двоичного дерева" << endl;
+ printTree(root);
+
+ // Алгоритм бэктрекинга
+ vector state;
+ vector choices = {root};
+ vector> res;
+ backtrack(state, choices, res);
+
+ cout << "\nВывести все пути от корня к узлу 7, не содержащие узлов со значением 3" << endl;
+ for (vector &path : res) {
+ vector vals;
+ for (TreeNode *node : path) {
+ vals.push_back(node->val);
+ }
+ printVector(vals);
+ }
+}
diff --git a/ru/codes/cpp/chapter_backtracking/subset_sum_i.cpp b/ru/codes/cpp/chapter_backtracking/subset_sum_i.cpp
new file mode 100644
index 000000000..df3d2e91b
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/subset_sum_i.cpp
@@ -0,0 +1,57 @@
+/**
+ * File: subset_sum_i.cpp
+ * Created Time: 2023-06-21
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Алгоритм бэктрекинга: сумма подмножеств I */
+void backtrack(vector &state, int target, vector &choices, int start, vector> &res) {
+ // Если сумма подмножества равна target, записать решение
+ if (target == 0) {
+ res.push_back(state);
+ return;
+ }
+ // Обойти все варианты выбора
+ // Отсечение 2: начинать обход с start, чтобы избежать генерации повторяющихся подмножеств
+ for (int i = start; i < choices.size(); i++) {
+ // Отсечение 1: если сумма подмножества превышает target, немедленно завершить цикл
+ // Это связано с тем, что массив уже отсортирован, следующие элементы больше, и сумма подмножества точно превысит target
+ if (target - choices[i] < 0) {
+ break;
+ }
+ // Попытка: сделать выбор и обновить target и start
+ state.push_back(choices[i]);
+ // Перейти к следующему выбору
+ backtrack(state, target - choices[i], choices, i, res);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ state.pop_back();
+ }
+}
+
+/* Решить задачу суммы подмножеств I */
+vector> subsetSumI(vector &nums, int target) {
+ vector state; // Состояние (подмножество)
+ sort(nums.begin(), nums.end()); // Отсортировать nums
+ int start = 0; // Стартовая вершина обхода
+ vector> res; // Список результатов (список подмножеств)
+ backtrack(state, target, nums, start, res);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ vector nums = {3, 4, 5};
+ int target = 9;
+
+ vector> res = subsetSumI(nums, target);
+
+ cout << "Входной массив nums = ";
+ printVector(nums);
+ cout << "target = " << target << endl;
+ cout << "Все подмножества с суммой " << target << ": " << endl;
+ printVectorMatrix(res);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp b/ru/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp
new file mode 100644
index 000000000..2cf0b98aa
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp
@@ -0,0 +1,54 @@
+/**
+ * File: subset_sum_i_naive.cpp
+ * Created Time: 2023-06-21
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Алгоритм бэктрекинга: сумма подмножеств I */
+void backtrack(vector &state, int target, int total, vector &choices, vector> &res) {
+ // Если сумма подмножества равна target, записать решение
+ if (total == target) {
+ res.push_back(state);
+ return;
+ }
+ // Перебор всех вариантов выбора
+ for (size_t i = 0; i < choices.size(); i++) {
+ // Отсечение: если сумма подмножества превышает target, пропустить этот выбор
+ if (total + choices[i] > target) {
+ continue;
+ }
+ // Попытка: сделать выбор и обновить элемент и total
+ state.push_back(choices[i]);
+ // Перейти к следующему выбору
+ backtrack(state, target, total + choices[i], choices, res);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ state.pop_back();
+ }
+}
+
+/* Решить задачу суммы подмножеств I (с повторяющимися подмножествами) */
+vector> subsetSumINaive(vector &nums, int target) {
+ vector state; // Состояние (подмножество)
+ int total = 0; // Сумма подмножеств
+ vector> res; // Список результатов (список подмножеств)
+ backtrack(state, target, total, nums, res);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ vector nums = {3, 4, 5};
+ int target = 9;
+
+ vector> res = subsetSumINaive(nums, target);
+
+ cout << "Входной массив nums = ";
+ printVector(nums);
+ cout << "target = " << target << endl;
+ cout << "Все подмножества с суммой " << target << ": " << endl;
+ printVectorMatrix(res);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_backtracking/subset_sum_ii.cpp b/ru/codes/cpp/chapter_backtracking/subset_sum_ii.cpp
new file mode 100644
index 000000000..20af16c3e
--- /dev/null
+++ b/ru/codes/cpp/chapter_backtracking/subset_sum_ii.cpp
@@ -0,0 +1,62 @@
+/**
+ * File: subset_sum_ii.cpp
+ * Created Time: 2023-06-21
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Алгоритм бэктрекинга: сумма подмножеств II */
+void backtrack(vector &state, int target, vector &choices, int start, vector> &res) {
+ // Если сумма подмножества равна target, записать решение
+ if (target == 0) {
+ res.push_back(state);
+ return;
+ }
+ // Обойти все варианты выбора
+ // Отсечение 2: начинать обход с start, чтобы избежать генерации повторяющихся подмножеств
+ // Отсечение 3: начинать обход с start, чтобы избежать повторного выбора одного и того же элемента
+ for (int i = start; i < choices.size(); i++) {
+ // Отсечение 1: если сумма подмножества превышает target, немедленно завершить цикл
+ // Это связано с тем, что массив уже отсортирован, следующие элементы больше, и сумма подмножества точно превысит target
+ if (target - choices[i] < 0) {
+ break;
+ }
+ // Отсечение 4: если этот элемент равен элементу слева, значит ветвь поиска повторяется, ее нужно сразу пропустить
+ if (i > start && choices[i] == choices[i - 1]) {
+ continue;
+ }
+ // Попытка: сделать выбор и обновить target и start
+ state.push_back(choices[i]);
+ // Перейти к следующему выбору
+ backtrack(state, target - choices[i], choices, i + 1, res);
+ // Откат: отменить выбор и восстановить предыдущее состояние
+ state.pop_back();
+ }
+}
+
+/* Решить задачу суммы подмножеств II */
+vector> subsetSumII(vector &nums, int target) {
+ vector state; // Состояние (подмножество)
+ sort(nums.begin(), nums.end()); // Отсортировать nums
+ int start = 0; // Стартовая вершина обхода
+ vector> res; // Список результатов (список подмножеств)
+ backtrack(state, target, nums, start, res);
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ vector nums = {4, 4, 5};
+ int target = 9;
+
+ vector> res = subsetSumII(nums, target);
+
+ cout << "Входной массив nums = ";
+ printVector(nums);
+ cout << "target = " << target << endl;
+ cout << "Все подмножества с суммой " << target << ": " << endl;
+ printVectorMatrix(res);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_computational_complexity/CMakeLists.txt b/ru/codes/cpp/chapter_computational_complexity/CMakeLists.txt
new file mode 100644
index 000000000..ea2845b75
--- /dev/null
+++ b/ru/codes/cpp/chapter_computational_complexity/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(iteration iteration.cpp)
+add_executable(recursion recursion.cpp)
+add_executable(space_complexity space_complexity.cpp)
+add_executable(time_complexity time_complexity.cpp)
+add_executable(worst_best_time_complexity worst_best_time_complexity.cpp)
\ No newline at end of file
diff --git a/ru/codes/cpp/chapter_computational_complexity/iteration.cpp b/ru/codes/cpp/chapter_computational_complexity/iteration.cpp
new file mode 100644
index 000000000..9f3b0296a
--- /dev/null
+++ b/ru/codes/cpp/chapter_computational_complexity/iteration.cpp
@@ -0,0 +1,76 @@
+/**
+ * File: iteration.cpp
+ * Created Time: 2023-08-24
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Цикл for */
+int forLoop(int n) {
+ int res = 0;
+ // Циклическое суммирование 1, 2, ..., n-1, n
+ for (int i = 1; i <= n; ++i) {
+ res += i;
+ }
+ return res;
+}
+
+/* Цикл while */
+int whileLoop(int n) {
+ int res = 0;
+ int i = 1; // Инициализация условной переменной
+ // Циклическое суммирование 1, 2, ..., n-1, n
+ while (i <= n) {
+ res += i;
+ i++; // Обновить условную переменную
+ }
+ return res;
+}
+
+/* Цикл while (двойное обновление) */
+int whileLoopII(int n) {
+ int res = 0;
+ int i = 1; // Инициализация условной переменной
+ // Циклическое суммирование 1, 4, 10, ...
+ while (i <= n) {
+ res += i;
+ // Обновить условную переменную
+ i++;
+ i *= 2;
+ }
+ return res;
+}
+
+/* Двойной цикл for */
+string nestedForLoop(int n) {
+ ostringstream res;
+ // Цикл по i = 1, 2, ..., n-1, n
+ for (int i = 1; i <= n; ++i) {
+ // Цикл по j = 1, 2, ..., n-1, n
+ for (int j = 1; j <= n; ++j) {
+ res << "(" << i << ", " << j << "), ";
+ }
+ }
+ return res.str();
+}
+
+/* Driver Code */
+int main() {
+ int n = 5;
+ int res;
+
+ res = forLoop(n);
+ cout << "\nРезультат суммирования в цикле for res = " << res << endl;
+
+ res = whileLoop(n);
+ cout << "\nРезультат суммирования в цикле while res = " << res << endl;
+
+ res = whileLoopII(n);
+ cout << "\nРезультат суммирования в цикле while (двойное обновление) res = " << res << endl;
+
+ string resStr = nestedForLoop(n);
+ cout << "\nРезультат двойного цикла for " << resStr << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_computational_complexity/recursion.cpp b/ru/codes/cpp/chapter_computational_complexity/recursion.cpp
new file mode 100644
index 000000000..e067c9061
--- /dev/null
+++ b/ru/codes/cpp/chapter_computational_complexity/recursion.cpp
@@ -0,0 +1,78 @@
+/**
+ * File: recursion.cpp
+ * Created Time: 2023-08-24
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Рекурсия */
+int recur(int n) {
+ // Условие завершения
+ if (n == 1)
+ return 1;
+ // Рекурсия: рекурсивный вызов
+ int res = recur(n - 1);
+ // Возврат: вернуть результат
+ return n + res;
+}
+
+/* Имитация рекурсии итерацией */
+int forLoopRecur(int n) {
+ // Использовать явный стек для имитации системного стека вызовов
+ stack stack;
+ int res = 0;
+ // Рекурсия: рекурсивный вызов
+ for (int i = n; i > 0; i--) {
+ // Имитировать «рекурсию» с помощью операции помещения в стек
+ stack.push(i);
+ }
+ // Возврат: вернуть результат
+ while (!stack.empty()) {
+ // Имитировать «возврат» с помощью операции извлечения из стека
+ res += stack.top();
+ stack.pop();
+ }
+ // res = 1+2+3+...+n
+ return res;
+}
+
+/* Хвостовая рекурсия */
+int tailRecur(int n, int res) {
+ // Условие завершения
+ if (n == 0)
+ return res;
+ // Хвостовой рекурсивный вызов
+ return tailRecur(n - 1, res + n);
+}
+
+/* Последовательность Фибоначчи: рекурсия */
+int fib(int n) {
+ // Условие завершения: f(1) = 0, f(2) = 1
+ if (n == 1 || n == 2)
+ return n - 1;
+ // Рекурсивный вызов f(n) = f(n-1) + f(n-2)
+ int res = fib(n - 1) + fib(n - 2);
+ // Вернуть результат f(n)
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ int n = 5;
+ int res;
+
+ res = recur(n);
+ cout << "\nРезультат суммирования в рекурсивной функции res = " << res << endl;
+
+ res = forLoopRecur(n);
+ cout << "\nРезультат суммирования с использованием итерации для имитации рекурсии res = " << res << endl;
+
+ res = tailRecur(n, 0);
+ cout << "\nРезультат суммирования в хвостовой рекурсии res = " << res << endl;
+
+ res = fib(n);
+ cout << "\nЭлемент последовательности Фибоначчи с индексом " << n << " = " << res << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_computational_complexity/space_complexity.cpp b/ru/codes/cpp/chapter_computational_complexity/space_complexity.cpp
new file mode 100644
index 000000000..b105792d8
--- /dev/null
+++ b/ru/codes/cpp/chapter_computational_complexity/space_complexity.cpp
@@ -0,0 +1,107 @@
+/**
+ * File: space_complexity.cpp
+ * Created Time: 2022-11-25
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Функция */
+int func() {
+ // Выполнить некоторые операции
+ return 0;
+}
+
+/* Постоянная сложность */
+void constant(int n) {
+ // Константы, переменные и объекты занимают O(1) памяти
+ const int a = 0;
+ int b = 0;
+ vector nums(10000);
+ ListNode node(0);
+ // Переменные в цикле занимают O(1) памяти
+ for (int i = 0; i < n; i++) {
+ int c = 0;
+ }
+ // Функции в цикле занимают O(1) памяти
+ for (int i = 0; i < n; i++) {
+ func();
+ }
+}
+
+/* Линейная сложность */
+void linear(int n) {
+ // Массив длины n занимает O(n) памяти
+ vector nums(n);
+ // Список длины n занимает O(n) памяти
+ vector nodes;
+ for (int i = 0; i < n; i++) {
+ nodes.push_back(ListNode(i));
+ }
+ // Хеш-таблица длины n занимает O(n) памяти
+ unordered_map map;
+ for (int i = 0; i < n; i++) {
+ map[i] = to_string(i);
+ }
+}
+
+/* Линейная сложность (рекурсивная реализация) */
+void linearRecur(int n) {
+ cout << "Рекурсия n = " << n << endl;
+ if (n == 1)
+ return;
+ linearRecur(n - 1);
+}
+
+/* Квадратичная сложность */
+void quadratic(int n) {
+ // Двумерный список занимает O(n^2) памяти
+ vector> numMatrix;
+ for (int i = 0; i < n; i++) {
+ vector tmp;
+ for (int j = 0; j < n; j++) {
+ tmp.push_back(0);
+ }
+ numMatrix.push_back(tmp);
+ }
+}
+
+/* Квадратичная сложность (рекурсивная реализация) */
+int quadraticRecur(int n) {
+ if (n <= 0)
+ return 0;
+ vector nums(n);
+ cout << "Рекурсия n = " << n << " , длина nums = " << nums.size() << endl;
+ return quadraticRecur(n - 1);
+}
+
+/* Экспоненциальная сложность (построение полного двоичного дерева) */
+TreeNode *buildTree(int n) {
+ if (n == 0)
+ return nullptr;
+ TreeNode *root = new TreeNode(0);
+ root->left = buildTree(n - 1);
+ root->right = buildTree(n - 1);
+ return root;
+}
+
+/* Driver Code */
+int main() {
+ int n = 5;
+ // Постоянная сложность
+ constant(n);
+ // Линейная сложность
+ linear(n);
+ linearRecur(n);
+ // Квадратичная сложность
+ quadratic(n);
+ quadraticRecur(n);
+ // Экспоненциальная сложность
+ TreeNode *root = buildTree(n);
+ printTree(root);
+
+ // Освободить память
+ freeMemoryTree(root);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_computational_complexity/time_complexity.cpp b/ru/codes/cpp/chapter_computational_complexity/time_complexity.cpp
new file mode 100644
index 000000000..4ba231343
--- /dev/null
+++ b/ru/codes/cpp/chapter_computational_complexity/time_complexity.cpp
@@ -0,0 +1,168 @@
+/**
+ * File: time_complexity.cpp
+ * Created Time: 2022-11-25
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Постоянная сложность */
+int constant(int n) {
+ int count = 0;
+ int size = 100000;
+ for (int i = 0; i < size; i++)
+ count++;
+ return count;
+}
+
+/* Линейная сложность */
+int linear(int n) {
+ int count = 0;
+ for (int i = 0; i < n; i++)
+ count++;
+ return count;
+}
+
+/* Линейная сложность (обход массива) */
+int arrayTraversal(vector &nums) {
+ int count = 0;
+ // Число итераций пропорционально длине массива
+ for (int num : nums) {
+ count++;
+ }
+ return count;
+}
+
+/* Квадратичная сложность */
+int quadratic(int n) {
+ int count = 0;
+ // Число итераций квадратично зависит от размера данных n
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ count++;
+ }
+ }
+ return count;
+}
+
+/* Квадратичная сложность (пузырьковая сортировка) */
+int bubbleSort(vector &nums) {
+ int count = 0; // Счетчик
+ // Внешний цикл: неотсортированный диапазон [0, i]
+ for (int i = nums.size() - 1; i > 0; i--) {
+ // Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
+ for (int j = 0; j < i; j++) {
+ if (nums[j] > nums[j + 1]) {
+ // Поменять местами nums[j] и nums[j + 1]
+ int tmp = nums[j];
+ nums[j] = nums[j + 1];
+ nums[j + 1] = tmp;
+ count += 3; // Обмен элементов включает 3 элементарные операции
+ }
+ }
+ }
+ return count;
+}
+
+/* Экспоненциальная сложность (итеративная реализация) */
+int exponential(int n) {
+ int count = 0, base = 1;
+ // На каждом шаге клетка делится надвое, образуя последовательность 1, 2, 4, 8, ..., 2^(n-1)
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < base; j++) {
+ count++;
+ }
+ base *= 2;
+ }
+ // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
+ return count;
+}
+
+/* Экспоненциальная сложность (рекурсивная реализация) */
+int expRecur(int n) {
+ if (n == 1)
+ return 1;
+ return expRecur(n - 1) + expRecur(n - 1) + 1;
+}
+
+/* Логарифмическая сложность (итеративная реализация) */
+int logarithmic(int n) {
+ int count = 0;
+ while (n > 1) {
+ n = n / 2;
+ count++;
+ }
+ return count;
+}
+
+/* Логарифмическая сложность (рекурсивная реализация) */
+int logRecur(int n) {
+ if (n <= 1)
+ return 0;
+ return logRecur(n / 2) + 1;
+}
+
+/* Линейно-логарифмическая сложность */
+int linearLogRecur(int n) {
+ if (n <= 1)
+ return 1;
+ int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);
+ for (int i = 0; i < n; i++) {
+ count++;
+ }
+ return count;
+}
+
+/* Факториальная сложность (рекурсивная реализация) */
+int factorialRecur(int n) {
+ if (n == 0)
+ return 1;
+ int count = 0;
+ // Из одного получается n
+ for (int i = 0; i < n; i++) {
+ count += factorialRecur(n - 1);
+ }
+ return count;
+}
+
+/* Driver Code */
+int main() {
+ // Можно изменить n и запустить программу, чтобы увидеть, как меняется число операций при разных сложностях
+ int n = 8;
+ cout << "Размер входных данных n = " << n << endl;
+
+ int count = constant(n);
+ cout << "Количество операций постоянной сложности = " << count << endl;
+
+ count = linear(n);
+ cout << "Количество операций линейной сложности = " << count << endl;
+ vector arr(n);
+ count = arrayTraversal(arr);
+ cout << "Количество операций линейной сложности (обход массива) = " << count << endl;
+
+ count = quadratic(n);
+ cout << "Количество операций квадратичной сложности = " << count << endl;
+ vector nums(n);
+ for (int i = 0; i < n; i++)
+ nums[i] = n - i; // [n,n-1,...,2,1]
+ count = bubbleSort(nums);
+ cout << "Количество операций квадратичной сложности (пузырьковая сортировка) = " << count << endl;
+
+ count = exponential(n);
+ cout << "Количество операций экспоненциальной сложности (итерация) = " << count << endl;
+ count = expRecur(n);
+ cout << "Количество операций экспоненциальной сложности (рекурсия) = " << count << endl;
+
+ count = logarithmic(n);
+ cout << "Количество операций логарифмической сложности (итерация) = " << count << endl;
+ count = logRecur(n);
+ cout << "Количество операций логарифмической сложности (рекурсия) = " << count << endl;
+
+ count = linearLogRecur(n);
+ cout << "Количество операций линейно-логарифмической сложности (рекурсия) = " << count << endl;
+
+ count = factorialRecur(n);
+ cout << "Количество операций факториальной сложности (рекурсия) = " << count << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp b/ru/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp
new file mode 100644
index 000000000..bfb342011
--- /dev/null
+++ b/ru/codes/cpp/chapter_computational_complexity/worst_best_time_complexity.cpp
@@ -0,0 +1,45 @@
+/**
+ * File: worst_best_time_complexity.cpp
+ * Created Time: 2022-11-25
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Создать массив с элементами { 1, 2, ..., n } в случайном порядке */
+vector randomNumbers(int n) {
+ vector nums(n);
+ // Создать массив nums = { 1, 2, 3, ..., n }
+ for (int i = 0; i < n; i++) {
+ nums[i] = i + 1;
+ }
+ // Использовать системное время для генерации случайного seed
+ unsigned seed = chrono::system_clock::now().time_since_epoch().count();
+ // Случайно перемешать элементы массива
+ shuffle(nums.begin(), nums.end(), default_random_engine(seed));
+ return nums;
+}
+
+/* Найти индекс числа 1 в массиве nums */
+int findOne(vector &nums) {
+ for (int i = 0; i < nums.size(); i++) {
+ // Когда элемент 1 находится в начале массива, достигается лучшая временная сложность O(1)
+ // Когда элемент 1 находится в конце массива, достигается худшая временная сложность O(n)
+ if (nums[i] == 1)
+ return i;
+ }
+ return -1;
+}
+
+/* Driver Code */
+int main() {
+ for (int i = 0; i < 1000; i++) {
+ int n = 100;
+ vector nums = randomNumbers(n);
+ int index = findOne(nums);
+ cout << "\nПосле перемешивания массива [ 1, 2, ..., n ] nums = ";
+ printVector(nums);
+ cout << "Индекс числа 1 = " << index << endl;
+ }
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_divide_and_conquer/CMakeLists.txt b/ru/codes/cpp/chapter_divide_and_conquer/CMakeLists.txt
new file mode 100644
index 000000000..38dfff710
--- /dev/null
+++ b/ru/codes/cpp/chapter_divide_and_conquer/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(binary_search_recur binary_search_recur.cpp)
+add_executable(build_tree build_tree.cpp)
+add_executable(hanota hanota.cpp)
\ No newline at end of file
diff --git a/ru/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp b/ru/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp
new file mode 100644
index 000000000..2281bb66b
--- /dev/null
+++ b/ru/codes/cpp/chapter_divide_and_conquer/binary_search_recur.cpp
@@ -0,0 +1,46 @@
+/**
+ * File: binary_search_recur.cpp
+ * Created Time: 2023-07-17
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Бинарный поиск: задача f(i, j) */
+int dfs(vector &nums, int target, int i, int j) {
+ // Если интервал пуст, целевой элемент отсутствует, вернуть -1
+ if (i > j) {
+ return -1;
+ }
+ // Вычислить индекс середины m
+ int m = (i + j) / 2;
+ if (nums[m] < target) {
+ // Рекурсивная подзадача f(m+1, j)
+ return dfs(nums, target, m + 1, j);
+ } else if (nums[m] > target) {
+ // Рекурсивная подзадача f(i, m-1)
+ return dfs(nums, target, i, m - 1);
+ } else {
+ // Целевой элемент найден, вернуть его индекс
+ return m;
+ }
+}
+
+/* Бинарный поиск */
+int binarySearch(vector &nums, int target) {
+ int n = nums.size();
+ // Решить задачу f(0, n-1)
+ return dfs(nums, target, 0, n - 1);
+}
+
+/* Driver Code */
+int main() {
+ int target = 6;
+ vector nums = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
+
+ // Бинарный поиск (двусторонне замкнутый интервал)
+ int index = binarySearch(nums, target);
+ cout << "Индекс целевого элемента 6 = " << index << endl;
+
+ return 0;
+}
\ No newline at end of file
diff --git a/ru/codes/cpp/chapter_divide_and_conquer/build_tree.cpp b/ru/codes/cpp/chapter_divide_and_conquer/build_tree.cpp
new file mode 100644
index 000000000..f47833d59
--- /dev/null
+++ b/ru/codes/cpp/chapter_divide_and_conquer/build_tree.cpp
@@ -0,0 +1,51 @@
+/**
+ * File: build_tree.cpp
+ * Created Time: 2023-07-17
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Построить двоичное дерево: разделяй и властвуй */
+TreeNode *dfs(vector &preorder, unordered_map &inorderMap, int i, int l, int r) {
+ // Завершить при пустом диапазоне поддерева
+ if (r - l < 0)
+ return NULL;
+ // Инициализировать корневой узел
+ TreeNode *root = new TreeNode(preorder[i]);
+ // Найти m, чтобы разделить левое и правое поддеревья
+ int m = inorderMap[preorder[i]];
+ // Подзадача: построить левое поддерево
+ root->left = dfs(preorder, inorderMap, i + 1, l, m - 1);
+ // Подзадача: построить правое поддерево
+ root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r);
+ // Вернуть корневой узел
+ return root;
+}
+
+/* Построить двоичное дерево */
+TreeNode *buildTree(vector &preorder, vector &inorder) {
+ // Инициализировать хеш-таблицу для хранения соответствия элементов inorder их индексам
+ unordered_map inorderMap;
+ for (int i = 0; i < inorder.size(); i++) {
+ inorderMap[inorder[i]] = i;
+ }
+ TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorder.size() - 1);
+ return root;
+}
+
+/* Driver Code */
+int main() {
+ vector preorder = {3, 9, 2, 1, 7};
+ vector inorder = {9, 3, 1, 2, 7};
+ cout << "Предварительный обход = ";
+ printVector(preorder);
+ cout << "Симметричный обход = ";
+ printVector(inorder);
+
+ TreeNode *root = buildTree(preorder, inorder);
+ cout << "Построенное двоичное дерево:\n";
+ printTree(root);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_divide_and_conquer/hanota.cpp b/ru/codes/cpp/chapter_divide_and_conquer/hanota.cpp
new file mode 100644
index 000000000..15ac1f1ea
--- /dev/null
+++ b/ru/codes/cpp/chapter_divide_and_conquer/hanota.cpp
@@ -0,0 +1,66 @@
+/**
+ * File: hanota.cpp
+ * Created Time: 2023-07-17
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Переместить один диск */
+void move(vector &src, vector &tar) {
+ // Снять диск с вершины src
+ int pan = src.back();
+ src.pop_back();
+ // Положить диск на вершину tar
+ tar.push_back(pan);
+}
+
+/* Решить задачу Ханойской башни f(i) */
+void dfs(int i, vector &src, vector &buf, vector &tar) {
+ // Если в src остался только один диск, сразу переместить его в tar
+ if (i == 1) {
+ move(src, tar);
+ return;
+ }
+ // Подзадача f(i-1): переместить верхние i-1 дисков из src в buf с помощью tar
+ dfs(i - 1, src, tar, buf);
+ // Подзадача f(1): переместить оставшийся один диск из src в tar
+ move(src, tar);
+ // Подзадача f(i-1): переместить верхние i-1 дисков из buf в tar с помощью src
+ dfs(i - 1, buf, src, tar);
+}
+
+/* Решить задачу Ханойской башни */
+void solveHanota(vector &A, vector &B, vector &C) {
+ int n = A.size();
+ // Переместить верхние n дисков из A в C с помощью B
+ dfs(n, A, B, C);
+}
+
+/* Driver Code */
+int main() {
+ // Хвост списка соответствует вершине столбца
+ vector A = {5, 4, 3, 2, 1};
+ vector B = {};
+ vector C = {};
+
+ cout << "Начальное состояние:\n";
+ cout << "A =";
+ printVector(A);
+ cout << "B =";
+ printVector(B);
+ cout << "C =";
+ printVector(C);
+
+ solveHanota(A, B, C);
+
+ cout << "После завершения перемещения дисков:\n";
+ cout << "A =";
+ printVector(A);
+ cout << "B =";
+ printVector(B);
+ cout << "C =";
+ printVector(C);
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/CMakeLists.txt b/ru/codes/cpp/chapter_dynamic_programming/CMakeLists.txt
new file mode 100644
index 000000000..ed185458a
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_executable(climbing_stairs_backtrack climbing_stairs_backtrack.cpp)
+add_executable(climbing_stairs_dfs climbing_stairs_dfs.cpp)
+add_executable(climbing_stairs_dfs_mem climbing_stairs_dfs_mem.cpp)
+add_executable(climbing_stairs_dp climbing_stairs_dp.cpp)
+add_executable(min_cost_climbing_stairs_dp min_cost_climbing_stairs_dp.cpp)
+add_executable(min_path_sum min_path_sum.cpp)
+add_executable(unbounded_knapsack unbounded_knapsack.cpp)
+add_executable(coin_change coin_change.cpp)
+add_executable(coin_change_ii coin_change_ii.cpp)
+add_executable(edit_distance edit_distance.cpp)
\ No newline at end of file
diff --git a/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp
new file mode 100644
index 000000000..b3ee775cb
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_backtrack.cpp
@@ -0,0 +1,43 @@
+
+/**
+ * File: climbing_stairs_backtrack.cpp
+ * Created Time: 2023-06-30
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Бэктрекинг */
+void backtrack(vector &choices, int state, int n, vector &res) {
+ // Когда подъем достигает n-й ступени, число вариантов увеличивается на 1
+ if (state == n)
+ res[0]++;
+ // Перебор всех вариантов выбора
+ for (auto &choice : choices) {
+ // Отсечение: нельзя выходить за n-ю ступень
+ if (state + choice > n)
+ continue;
+ // Попытка: сделать выбор и обновить состояние
+ backtrack(choices, state + choice, n, res);
+ // Откат
+ }
+}
+
+/* Подъем по лестнице: бэктрекинг */
+int climbingStairsBacktrack(int n) {
+ vector choices = {1, 2}; // Можно подняться на 1 или 2 ступени
+ int state = 0; // Начать подъем с 0-й ступени
+ vector res = {0}; // Использовать res[0] для хранения числа решений
+ backtrack(choices, state, n, res);
+ return res[0];
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsBacktrack(n);
+ cout << "Количество способов подняться по лестнице из " << n << " ступеней: " << res << " вариантов" << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp
new file mode 100644
index 000000000..4afdd1af2
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_constraint_dp.cpp
@@ -0,0 +1,37 @@
+/**
+ * File: climbing_stairs_constraint_dp.cpp
+ * Created Time: 2023-07-01
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Подъем по лестнице с ограничениями: динамическое программирование */
+int climbingStairsConstraintDP(int n) {
+ if (n == 1 || n == 2) {
+ return 1;
+ }
+ // Инициализация таблицы dp для хранения решений подзадач
+ vector> dp(n + 1, vector(3, 0));
+ // Начальное состояние: заранее задать решения наименьших подзадач
+ dp[1][1] = 1;
+ dp[1][2] = 0;
+ dp[2][1] = 0;
+ dp[2][2] = 1;
+ // Переход состояний: постепенное решение больших подзадач через меньшие
+ for (int i = 3; i <= n; i++) {
+ dp[i][1] = dp[i - 1][2];
+ dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
+ }
+ return dp[n][1] + dp[n][2];
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsConstraintDP(n);
+ cout << "Количество способов подняться по лестнице из " << n << " ступеней: " << res << " вариантов" << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp
new file mode 100644
index 000000000..f49fc40ea
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs.cpp
@@ -0,0 +1,32 @@
+/**
+ * File: climbing_stairs_dfs.cpp
+ * Created Time: 2023-06-30
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Поиск */
+int dfs(int i) {
+ // dp[1] и dp[2] уже известны, вернуть их
+ if (i == 1 || i == 2)
+ return i;
+ // dp[i] = dp[i-1] + dp[i-2]
+ int count = dfs(i - 1) + dfs(i - 2);
+ return count;
+}
+
+/* Подъем по лестнице: поиск */
+int climbingStairsDFS(int n) {
+ return dfs(n);
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsDFS(n);
+ cout << "Количество способов подняться по лестнице из " << n << " ступеней: " << res << " вариантов" << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp
new file mode 100644
index 000000000..eeb114042
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dfs_mem.cpp
@@ -0,0 +1,39 @@
+/**
+ * File: climbing_stairs_dfs_mem.cpp
+ * Created Time: 2023-06-30
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Поиск с мемоизацией */
+int dfs(int i, vector &mem) {
+ // dp[1] и dp[2] уже известны, вернуть их
+ if (i == 1 || i == 2)
+ return i;
+ // Если запись dp[i] существует, сразу вернуть ее
+ if (mem[i] != -1)
+ return mem[i];
+ // dp[i] = dp[i-1] + dp[i-2]
+ int count = dfs(i - 1, mem) + dfs(i - 2, mem);
+ // Сохранить dp[i]
+ mem[i] = count;
+ return count;
+}
+
+/* Подъем по лестнице: поиск с мемоизацией */
+int climbingStairsDFSMem(int n) {
+ // mem[i] хранит число способов подняться на i-ю ступень, -1 означает отсутствие записи
+ vector mem(n + 1, -1);
+ return dfs(n, mem);
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsDFSMem(n);
+ cout << "Количество способов подняться по лестнице из " << n << " ступеней: " << res << " вариантов" << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp
new file mode 100644
index 000000000..e3db584a8
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/climbing_stairs_dp.cpp
@@ -0,0 +1,49 @@
+/**
+ * File: climbing_stairs_dp.cpp
+ * Created Time: 2023-06-30
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Подъем по лестнице: динамическое программирование */
+int climbingStairsDP(int n) {
+ if (n == 1 || n == 2)
+ return n;
+ // Инициализация таблицы dp для хранения решений подзадач
+ vector dp(n + 1);
+ // Начальное состояние: заранее задать решения наименьших подзадач
+ dp[1] = 1;
+ dp[2] = 2;
+ // Переход состояний: постепенное решение больших подзадач через меньшие
+ for (int i = 3; i <= n; i++) {
+ dp[i] = dp[i - 1] + dp[i - 2];
+ }
+ return dp[n];
+}
+
+/* Подъем по лестнице: динамическое программирование с оптимизацией памяти */
+int climbingStairsDPComp(int n) {
+ if (n == 1 || n == 2)
+ return n;
+ int a = 1, b = 2;
+ for (int i = 3; i <= n; i++) {
+ int tmp = b;
+ b = a + b;
+ a = tmp;
+ }
+ return b;
+}
+
+/* Driver Code */
+int main() {
+ int n = 9;
+
+ int res = climbingStairsDP(n);
+ cout << "Количество способов подняться по лестнице из " << n << " ступеней: " << res << " вариантов" << endl;
+
+ res = climbingStairsDPComp(n);
+ cout << "Количество способов подняться по лестнице из " << n << " ступеней: " << res << " вариантов" << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/coin_change.cpp b/ru/codes/cpp/chapter_dynamic_programming/coin_change.cpp
new file mode 100644
index 000000000..2986dbaf9
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/coin_change.cpp
@@ -0,0 +1,70 @@
+/**
+ * File: coin_change.cpp
+ * Created Time: 2023-07-11
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Размен монет: динамическое программирование */
+int coinChangeDP(vector &coins, int amt) {
+ int n = coins.size();
+ int MAX = amt + 1;
+ // Инициализация таблицы dp
+ vector> dp(n + 1, vector(amt + 1, 0));
+ // Переход состояний: первая строка и первый столбец
+ for (int a = 1; a <= amt; a++) {
+ dp[0][a] = MAX;
+ }
+ // Переход состояний: остальные строки и столбцы
+ for (int i = 1; i <= n; i++) {
+ for (int a = 1; a <= amt; a++) {
+ if (coins[i - 1] > a) {
+ // Если целевая сумма превышена, монету i не выбирать
+ dp[i][a] = dp[i - 1][a];
+ } else {
+ // Меньшее из двух решений: не брать или взять монету i
+ dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1);
+ }
+ }
+ }
+ return dp[n][amt] != MAX ? dp[n][amt] : -1;
+}
+
+/* Размен монет: динамическое программирование с оптимизацией памяти */
+int coinChangeDPComp(vector &coins, int amt) {
+ int n = coins.size();
+ int MAX = amt + 1;
+ // Инициализация таблицы dp
+ vector dp(amt + 1, MAX);
+ dp[0] = 0;
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int a = 1; a <= amt; a++) {
+ if (coins[i - 1] > a) {
+ // Если целевая сумма превышена, монету i не выбирать
+ dp[a] = dp[a];
+ } else {
+ // Меньшее из двух решений: не брать или взять монету i
+ dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1);
+ }
+ }
+ }
+ return dp[amt] != MAX ? dp[amt] : -1;
+}
+
+/* Driver code */
+int main() {
+ vector coins = {1, 2, 5};
+ int amt = 4;
+
+ // Динамическое программирование
+ int res = coinChangeDP(coins, amt);
+ cout << "Минимальное количество монет для целевой суммы = " << res << endl;
+
+ // Динамическое программирование с оптимизацией памяти
+ res = coinChangeDPComp(coins, amt);
+ cout << "Минимальное количество монет для целевой суммы = " << res << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp b/ru/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp
new file mode 100644
index 000000000..a63e1b740
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/coin_change_ii.cpp
@@ -0,0 +1,68 @@
+/**
+ * File: coin_change_ii.cpp
+ * Created Time: 2023-07-11
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Размен монет II: динамическое программирование */
+int coinChangeIIDP(vector &coins, int amt) {
+ int n = coins.size();
+ // Инициализация таблицы dp
+ vector> dp(n + 1, vector(amt + 1, 0));
+ // Инициализация первого столбца
+ for (int i = 0; i <= n; i++) {
+ dp[i][0] = 1;
+ }
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int a = 1; a <= amt; a++) {
+ if (coins[i - 1] > a) {
+ // Если целевая сумма превышена, монету i не выбирать
+ dp[i][a] = dp[i - 1][a];
+ } else {
+ // Сумма двух решений: не брать или взять монету i
+ dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]];
+ }
+ }
+ }
+ return dp[n][amt];
+}
+
+/* Размен монет II: динамическое программирование с оптимизацией памяти */
+int coinChangeIIDPComp(vector &coins, int amt) {
+ int n = coins.size();
+ // Инициализация таблицы dp
+ vector dp(amt + 1, 0);
+ dp[0] = 1;
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int a = 1; a <= amt; a++) {
+ if (coins[i - 1] > a) {
+ // Если целевая сумма превышена, монету i не выбирать
+ dp[a] = dp[a];
+ } else {
+ // Сумма двух решений: не брать или взять монету i
+ dp[a] = dp[a] + dp[a - coins[i - 1]];
+ }
+ }
+ }
+ return dp[amt];
+}
+
+/* Driver code */
+int main() {
+ vector coins = {1, 2, 5};
+ int amt = 5;
+
+ // Динамическое программирование
+ int res = coinChangeIIDP(coins, amt);
+ cout << "Количество комбинаций монет для набора целевой суммы = " << res << endl;
+
+ // Динамическое программирование с оптимизацией памяти
+ res = coinChangeIIDPComp(coins, amt);
+ cout << "Количество комбинаций монет для набора целевой суммы = " << res << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/edit_distance.cpp b/ru/codes/cpp/chapter_dynamic_programming/edit_distance.cpp
new file mode 100644
index 000000000..488e999ec
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/edit_distance.cpp
@@ -0,0 +1,136 @@
+/**
+ * File: edit_distance.cpp
+ * Created Time: 2023-07-13
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Редакционное расстояние: полный перебор */
+int editDistanceDFS(string s, string t, int i, int j) {
+ // Если s и t пусты, вернуть 0
+ if (i == 0 && j == 0)
+ return 0;
+ // Если s пусто, вернуть длину t
+ if (i == 0)
+ return j;
+ // Если t пусто, вернуть длину s
+ if (j == 0)
+ return i;
+ // Если два символа равны, сразу пропустить их
+ if (s[i - 1] == t[j - 1])
+ return editDistanceDFS(s, t, i - 1, j - 1);
+ // Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
+ int insert = editDistanceDFS(s, t, i, j - 1);
+ int del = editDistanceDFS(s, t, i - 1, j);
+ int replace = editDistanceDFS(s, t, i - 1, j - 1);
+ // Вернуть минимальное число шагов редактирования
+ return min(min(insert, del), replace) + 1;
+}
+
+/* Редакционное расстояние: поиск с мемоизацией */
+int editDistanceDFSMem(string s, string t, vector> &mem, int i, int j) {
+ // Если s и t пусты, вернуть 0
+ if (i == 0 && j == 0)
+ return 0;
+ // Если s пусто, вернуть длину t
+ if (i == 0)
+ return j;
+ // Если t пусто, вернуть длину s
+ if (j == 0)
+ return i;
+ // Если запись уже есть, сразу вернуть ее
+ if (mem[i][j] != -1)
+ return mem[i][j];
+ // Если два символа равны, сразу пропустить их
+ if (s[i - 1] == t[j - 1])
+ return editDistanceDFSMem(s, t, mem, i - 1, j - 1);
+ // Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
+ int insert = editDistanceDFSMem(s, t, mem, i, j - 1);
+ int del = editDistanceDFSMem(s, t, mem, i - 1, j);
+ int replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1);
+ // Сохранить и вернуть минимальное число шагов редактирования
+ mem[i][j] = min(min(insert, del), replace) + 1;
+ return mem[i][j];
+}
+
+/* Редакционное расстояние: динамическое программирование */
+int editDistanceDP(string s, string t) {
+ int n = s.length(), m = t.length();
+ vector> dp(n + 1, vector(m + 1, 0));
+ // Переход состояний: первая строка и первый столбец
+ for (int i = 1; i <= n; i++) {
+ dp[i][0] = i;
+ }
+ for (int j = 1; j <= m; j++) {
+ dp[0][j] = j;
+ }
+ // Переход состояний: остальные строки и столбцы
+ for (int i = 1; i <= n; i++) {
+ for (int j = 1; j <= m; j++) {
+ if (s[i - 1] == t[j - 1]) {
+ // Если два символа равны, сразу пропустить их
+ dp[i][j] = dp[i - 1][j - 1];
+ } else {
+ // Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
+ dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
+ }
+ }
+ }
+ return dp[n][m];
+}
+
+/* Редакционное расстояние: динамическое программирование с оптимизацией памяти */
+int editDistanceDPComp(string s, string t) {
+ int n = s.length(), m = t.length();
+ vector dp(m + 1, 0);
+ // Переход состояний: первая строка
+ for (int j = 1; j <= m; j++) {
+ dp[j] = j;
+ }
+ // Переход состояний: остальные строки
+ for (int i = 1; i <= n; i++) {
+ // Переход состояний: первый столбец
+ int leftup = dp[0]; // Временно сохранить dp[i-1, j-1]
+ dp[0] = i;
+ // Переход состояний: остальные столбцы
+ for (int j = 1; j <= m; j++) {
+ int temp = dp[j];
+ if (s[i - 1] == t[j - 1]) {
+ // Если два символа равны, сразу пропустить их
+ dp[j] = leftup;
+ } else {
+ // Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
+ dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1;
+ }
+ leftup = temp; // Обновить до значения dp[i-1, j-1] для следующей итерации
+ }
+ }
+ return dp[m];
+}
+
+/* Driver Code */
+int main() {
+ string s = "bag";
+ string t = "pack";
+ int n = s.length(), m = t.length();
+
+ // Полный перебор
+ int res = editDistanceDFS(s, t, n, m);
+ cout << "Чтобы заменить " << s << " на " << t << " , требуется минимум " << res << " операций редактирования\n";
+
+ // Поиск с мемоизацией
+ vector> mem(n + 1, vector(m + 1, -1));
+ res = editDistanceDFSMem(s, t, mem, n, m);
+ cout << "Чтобы заменить " << s << " на " << t << " , требуется минимум " << res << " операций редактирования\n";
+
+ // Динамическое программирование
+ res = editDistanceDP(s, t);
+ cout << "Чтобы заменить " << s << " на " << t << " , требуется минимум " << res << " операций редактирования\n";
+
+ // Динамическое программирование с оптимизацией памяти
+ res = editDistanceDPComp(s, t);
+ cout << "Чтобы заменить " << s << " на " << t << " , требуется минимум " << res << " операций редактирования\n";
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/knapsack.cpp b/ru/codes/cpp/chapter_dynamic_programming/knapsack.cpp
new file mode 100644
index 000000000..65ebdab48
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/knapsack.cpp
@@ -0,0 +1,109 @@
+#include
+#include
+#include
+
+using namespace std;
+
+/* Рюкзак 0-1: полный перебор */
+int knapsackDFS(vector &wgt, vector &val, int i, int c) {
+ // Если все предметы уже рассмотрены или в рюкзаке не осталось места, вернуть стоимость 0
+ if (i == 0 || c == 0) {
+ return 0;
+ }
+ // Если вместимость рюкзака превышена, можно только не класть предмет в рюкзак
+ if (wgt[i - 1] > c) {
+ return knapsackDFS(wgt, val, i - 1, c);
+ }
+ // Вычислить максимальную стоимость для случаев, когда предмет i не кладут и кладут
+ int no = knapsackDFS(wgt, val, i - 1, c);
+ int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1];
+ // Вернуть вариант с большей стоимостью из двух возможных
+ return max(no, yes);
+}
+
+/* Рюкзак 0-1: поиск с мемоизацией */
+int knapsackDFSMem(vector &wgt, vector &val, vector> &mem, int i, int c) {
+ // Если все предметы уже рассмотрены или в рюкзаке не осталось места, вернуть стоимость 0
+ if (i == 0 || c == 0) {
+ return 0;
+ }
+ // Если запись уже есть, вернуть сразу
+ if (mem[i][c] != -1) {
+ return mem[i][c];
+ }
+ // Если вместимость рюкзака превышена, можно только не класть предмет в рюкзак
+ if (wgt[i - 1] > c) {
+ return knapsackDFSMem(wgt, val, mem, i - 1, c);
+ }
+ // Вычислить максимальную стоимость для случаев, когда предмет i не кладут и кладут
+ int no = knapsackDFSMem(wgt, val, mem, i - 1, c);
+ int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1];
+ // Сохранить и вернуть вариант с большей стоимостью из двух решений
+ mem[i][c] = max(no, yes);
+ return mem[i][c];
+}
+
+/* Рюкзак 0-1: динамическое программирование */
+int knapsackDP(vector &wgt, vector &val, int cap) {
+ int n = wgt.size();
+ // Инициализация таблицы dp
+ vector> dp(n + 1, vector(cap + 1, 0));
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int c = 1; c <= cap; c++) {
+ if (wgt[i - 1] > c) {
+ // Если вместимость рюкзака превышена, предмет i не выбирать
+ dp[i][c] = dp[i - 1][c];
+ } else {
+ // Большее из двух решений: не брать или взять предмет i
+ dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]);
+ }
+ }
+ }
+ return dp[n][cap];
+}
+
+/* Рюкзак 0-1: динамическое программирование с оптимизацией памяти */
+int knapsackDPComp(vector &wgt, vector &val, int cap) {
+ int n = wgt.size();
+ // Инициализация таблицы dp
+ vector dp(cap + 1, 0);
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ // Обход в обратном порядке
+ for (int c = cap; c >= 1; c--) {
+ if (wgt[i - 1] <= c) {
+ // Большее из двух решений: не брать или взять предмет i
+ dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
+ }
+ }
+ }
+ return dp[cap];
+}
+
+/* Driver Code */
+int main() {
+ vector wgt = {10, 20, 30, 40, 50};
+ vector val = {50, 120, 150, 210, 240};
+ int cap = 50;
+ int n = wgt.size();
+
+ // Полный перебор
+ int res = knapsackDFS(wgt, val, n, cap);
+ cout << "Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна " << res << endl;
+
+ // Поиск с мемоизацией
+ vector> mem(n + 1, vector(cap + 1, -1));
+ res = knapsackDFSMem(wgt, val, mem, n, cap);
+ cout << "Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна " << res << endl;
+
+ // Динамическое программирование
+ res = knapsackDP(wgt, val, cap);
+ cout << "Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна " << res << endl;
+
+ // Динамическое программирование с оптимизацией памяти
+ res = knapsackDPComp(wgt, val, cap);
+ cout << "Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна " << res << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp b/ru/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp
new file mode 100644
index 000000000..5de0c080b
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/min_cost_climbing_stairs_dp.cpp
@@ -0,0 +1,53 @@
+/**
+ * File: min_cost_climbing_stairs_dp.cpp
+ * Created Time: 2023-06-30
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Минимальная стоимость подъема по лестнице: динамическое программирование */
+int minCostClimbingStairsDP(vector &cost) {
+ int n = cost.size() - 1;
+ if (n == 1 || n == 2)
+ return cost[n];
+ // Инициализация таблицы dp для хранения решений подзадач
+ vector dp(n + 1);
+ // Начальное состояние: заранее задать решения наименьших подзадач
+ dp[1] = cost[1];
+ dp[2] = cost[2];
+ // Переход состояний: постепенное решение больших подзадач через меньшие
+ for (int i = 3; i <= n; i++) {
+ dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
+ }
+ return dp[n];
+}
+
+/* Минимальная стоимость подъема по лестнице: динамическое программирование с оптимизацией памяти */
+int minCostClimbingStairsDPComp(vector &cost) {
+ int n = cost.size() - 1;
+ if (n == 1 || n == 2)
+ return cost[n];
+ int a = cost[1], b = cost[2];
+ for (int i = 3; i <= n; i++) {
+ int tmp = b;
+ b = min(a, tmp) + cost[i];
+ a = tmp;
+ }
+ return b;
+}
+
+/* Driver Code */
+int main() {
+ vector cost = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1};
+ cout << "Список стоимостей ступеней = ";
+ printVector(cost);
+
+ int res = minCostClimbingStairsDP(cost);
+ cout << "Минимальная стоимость подъема по лестнице = " << res << endl;
+
+ res = minCostClimbingStairsDPComp(cost);
+ cout << "Минимальная стоимость подъема по лестнице = " << res << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp b/ru/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp
new file mode 100644
index 000000000..d5c0bb534
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp
@@ -0,0 +1,116 @@
+/**
+ * File: min_path_sum.cpp
+ * Created Time: 2023-07-10
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Минимальная сумма пути: полный перебор */
+int minPathSumDFS(vector> &grid, int i, int j) {
+ // Если это верхняя левая ячейка, завершить поиск
+ if (i == 0 && j == 0) {
+ return grid[0][0];
+ }
+ // Если индексы строки или столбца выходят за границы, вернуть стоимость +∞
+ if (i < 0 || j < 0) {
+ return INT_MAX;
+ }
+ // Вычислить минимальную стоимость пути из левого верхнего угла до (i-1, j) и (i, j-1)
+ int up = minPathSumDFS(grid, i - 1, j);
+ int left = minPathSumDFS(grid, i, j - 1);
+ // Вернуть минимальную стоимость пути из левого верхнего угла до (i, j)
+ return min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX;
+}
+
+/* Минимальная сумма пути: поиск с мемоизацией */
+int minPathSumDFSMem(vector> &grid, vector> &mem, int i, int j) {
+ // Если это верхняя левая ячейка, завершить поиск
+ if (i == 0 && j == 0) {
+ return grid[0][0];
+ }
+ // Если индексы строки или столбца выходят за границы, вернуть стоимость +∞
+ if (i < 0 || j < 0) {
+ return INT_MAX;
+ }
+ // Если запись уже есть, вернуть сразу
+ if (mem[i][j] != -1) {
+ return mem[i][j];
+ }
+ // Минимальная стоимость пути для левой и верхней ячеек
+ int up = minPathSumDFSMem(grid, mem, i - 1, j);
+ int left = minPathSumDFSMem(grid, mem, i, j - 1);
+ // Сохранить и вернуть минимальную стоимость пути из левого верхнего угла до (i, j)
+ mem[i][j] = min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX;
+ return mem[i][j];
+}
+
+/* Минимальная сумма пути: динамическое программирование */
+int minPathSumDP(vector> &grid) {
+ int n = grid.size(), m = grid[0].size();
+ // Инициализация таблицы dp
+ vector> dp(n, vector(m));
+ dp[0][0] = grid[0][0];
+ // Переход состояний: первая строка
+ for (int j = 1; j < m; j++) {
+ dp[0][j] = dp[0][j - 1] + grid[0][j];
+ }
+ // Переход состояний: первый столбец
+ for (int i = 1; i < n; i++) {
+ dp[i][0] = dp[i - 1][0] + grid[i][0];
+ }
+ // Переход состояний: остальные строки и столбцы
+ for (int i = 1; i < n; i++) {
+ for (int j = 1; j < m; j++) {
+ dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
+ }
+ }
+ return dp[n - 1][m - 1];
+}
+
+/* Минимальная сумма пути: динамическое программирование с оптимизацией памяти */
+int minPathSumDPComp(vector> &grid) {
+ int n = grid.size(), m = grid[0].size();
+ // Инициализация таблицы dp
+ vector dp(m);
+ // Переход состояний: первая строка
+ dp[0] = grid[0][0];
+ for (int j = 1; j < m; j++) {
+ dp[j] = dp[j - 1] + grid[0][j];
+ }
+ // Переход состояний: остальные строки
+ for (int i = 1; i < n; i++) {
+ // Переход состояний: первый столбец
+ dp[0] = dp[0] + grid[i][0];
+ // Переход состояний: остальные столбцы
+ for (int j = 1; j < m; j++) {
+ dp[j] = min(dp[j - 1], dp[j]) + grid[i][j];
+ }
+ }
+ return dp[m - 1];
+}
+
+/* Driver Code */
+int main() {
+ vector> grid = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}};
+ int n = grid.size(), m = grid[0].size();
+
+ // Полный перебор
+ int res = minPathSumDFS(grid, n - 1, m - 1);
+ cout << "Минимальная сумма пути из левого верхнего в правый нижний угол = " << res << endl;
+
+ // Поиск с мемоизацией
+ vector> mem(n, vector(m, -1));
+ res = minPathSumDFSMem(grid, mem, n - 1, m - 1);
+ cout << "Минимальная сумма пути из левого верхнего в правый нижний угол = " << res << endl;
+
+ // Динамическое программирование
+ res = minPathSumDP(grid);
+ cout << "Минимальная сумма пути из левого верхнего в правый нижний угол = " << res << endl;
+
+ // Динамическое программирование с оптимизацией памяти
+ res = minPathSumDPComp(grid);
+ cout << "Минимальная сумма пути из левого верхнего в правый нижний угол = " << res << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp b/ru/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp
new file mode 100644
index 000000000..f40accb04
--- /dev/null
+++ b/ru/codes/cpp/chapter_dynamic_programming/unbounded_knapsack.cpp
@@ -0,0 +1,64 @@
+/**
+ * File: unbounded_knapsack.cpp
+ * Created Time: 2023-07-11
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Полный рюкзак: динамическое программирование */
+int unboundedKnapsackDP(vector &wgt, vector &val, int cap) {
+ int n = wgt.size();
+ // Инициализация таблицы dp
+ vector> dp(n + 1, vector(cap + 1, 0));
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int c = 1; c <= cap; c++) {
+ if (wgt[i - 1] > c) {
+ // Если вместимость рюкзака превышена, предмет i не выбирать
+ dp[i][c] = dp[i - 1][c];
+ } else {
+ // Большее из двух решений: не брать или взять предмет i
+ dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]);
+ }
+ }
+ }
+ return dp[n][cap];
+}
+
+/* Полный рюкзак: динамическое программирование с оптимизацией памяти */
+int unboundedKnapsackDPComp(vector &wgt, vector &val, int cap) {
+ int n = wgt.size();
+ // Инициализация таблицы dp
+ vector dp(cap + 1, 0);
+ // Переход состояний
+ for (int i = 1; i <= n; i++) {
+ for (int c = 1; c <= cap; c++) {
+ if (wgt[i - 1] > c) {
+ // Если вместимость рюкзака превышена, предмет i не выбирать
+ dp[c] = dp[c];
+ } else {
+ // Большее из двух решений: не брать или взять предмет i
+ dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
+ }
+ }
+ }
+ return dp[cap];
+}
+
+/* Driver code */
+int main() {
+ vector wgt = {1, 2, 3};
+ vector val = {5, 11, 15};
+ int cap = 4;
+
+ // Динамическое программирование
+ int res = unboundedKnapsackDP(wgt, val, cap);
+ cout << "Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна " << res << endl;
+
+ // Динамическое программирование с оптимизацией памяти
+ res = unboundedKnapsackDPComp(wgt, val, cap);
+ cout << "Максимальная стоимость предметов, не превышающая вместимость рюкзака, равна " << res << endl;
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_graph/CMakeLists.txt b/ru/codes/cpp/chapter_graph/CMakeLists.txt
new file mode 100644
index 000000000..4a56ce35b
--- /dev/null
+++ b/ru/codes/cpp/chapter_graph/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(graph_bfs graph_bfs.cpp)
+add_executable(graph_dfs graph_dfs.cpp)
+# add_executable(graph_adjacency_list graph_adjacency_list.cpp)
+add_executable(graph_adjacency_list_test graph_adjacency_list_test.cpp)
+add_executable(graph_adjacency_matrix graph_adjacency_matrix.cpp)
diff --git a/ru/codes/cpp/chapter_graph/graph_adjacency_list.cpp b/ru/codes/cpp/chapter_graph/graph_adjacency_list.cpp
new file mode 100644
index 000000000..7a14735fd
--- /dev/null
+++ b/ru/codes/cpp/chapter_graph/graph_adjacency_list.cpp
@@ -0,0 +1,90 @@
+/**
+ * File: graph_adjacency_list.cpp
+ * Created Time: 2023-02-09
+ * Author: what-is-me (whatisme@outlook.jp), krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+
+/* Класс неориентированного графа на основе списка смежности */
+class GraphAdjList {
+ public:
+ // Список смежности, где key — вершина, а value — все смежные ей вершины
+ unordered_map> adjList;
+
+ /* Удалить указанный узел из vector */
+ void remove(vector &vec, Vertex *vet) {
+ for (int i = 0; i < vec.size(); i++) {
+ if (vec[i] == vet) {
+ vec.erase(vec.begin() + i);
+ break;
+ }
+ }
+ }
+
+ /* Конструктор */
+ GraphAdjList(const vector> &edges) {
+ // Добавить все вершины и ребра
+ for (const vector &edge : edges) {
+ addVertex(edge[0]);
+ addVertex(edge[1]);
+ addEdge(edge[0], edge[1]);
+ }
+ }
+
+ /* Получить число вершин */
+ int size() {
+ return adjList.size();
+ }
+
+ /* Добавление ребра */
+ void addEdge(Vertex *vet1, Vertex *vet2) {
+ if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2)
+ throw invalid_argument("вершина не существует");
+ // Добавить ребро vet1 - vet2
+ adjList[vet1].push_back(vet2);
+ adjList[vet2].push_back(vet1);
+ }
+
+ /* Удаление ребра */
+ void removeEdge(Vertex *vet1, Vertex *vet2) {
+ if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2)
+ throw invalid_argument("вершина не существует");
+ // Удалить ребро vet1 - vet2
+ remove(adjList[vet1], vet2);
+ remove(adjList[vet2], vet1);
+ }
+
+ /* Добавление вершины */
+ void addVertex(Vertex *vet) {
+ if (adjList.count(vet))
+ return;
+ // Добавить новый список в список смежности
+ adjList[vet] = vector();
+ }
+
+ /* Удаление вершины */
+ void removeVertex(Vertex *vet) {
+ if (!adjList.count(vet))
+ throw invalid_argument("вершина не существует");
+ // Удалить из списка смежности список, соответствующий вершине vet
+ adjList.erase(vet);
+ // Обойти списки других вершин и удалить все ребра, содержащие vet
+ for (auto &adj : adjList) {
+ remove(adj.second, vet);
+ }
+ }
+
+ /* Вывести список смежности */
+ void print() {
+ cout << "Список смежности =" << endl;
+ for (auto &adj : adjList) {
+ const auto &key = adj.first;
+ const auto &vec = adj.second;
+ cout << key->val << ": ";
+ printVector(vetsToVals(vec));
+ }
+ }
+};
+
+// Тестовые примеры см. в graph_adjacency_list_test.cpp
diff --git a/ru/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp b/ru/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp
new file mode 100644
index 000000000..12dd82ca4
--- /dev/null
+++ b/ru/codes/cpp/chapter_graph/graph_adjacency_list_test.cpp
@@ -0,0 +1,49 @@
+/**
+ * File: graph_adjacency_list_test.cpp
+ * Created Time: 2023-02-09
+ * Author: what-is-me (whatisme@outlook.jp), krahets (krahets@163.com)
+ */
+
+#include "./graph_adjacency_list.cpp"
+
+/* Driver Code */
+int main() {
+ /* Инициализация неориентированного графа */
+ vector v = valsToVets(vector{1, 3, 2, 5, 4});
+ vector> edges = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]},
+ {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}};
+ GraphAdjList graph(edges);
+ cout << "\nПосле инициализации граф имеет вид" << endl;
+ graph.print();
+
+ /* Добавление ребра */
+ // Вершины 1 и 2 соответствуют v[0] и v[2]
+ graph.addEdge(v[0], v[2]);
+ cout << "\nПосле добавления ребра 1-2 граф имеет вид" << endl;
+ graph.print();
+
+ /* Удаление ребра */
+ // Вершины 1 и 3 соответствуют v[0] и v[1]
+ graph.removeEdge(v[0], v[1]);
+ cout << "\nПосле удаления ребра 1-3 граф имеет вид" << endl;
+ graph.print();
+
+ /* Добавление вершины */
+ Vertex *v5 = new Vertex(6);
+ graph.addVertex(v5);
+ cout << "\nПосле добавления вершины 6 граф имеет вид" << endl;
+ graph.print();
+
+ /* Удаление вершины */
+ // Вершина 3 соответствует v[1]
+ graph.removeVertex(v[1]);
+ cout << "\nПосле удаления вершины 3 граф имеет вид" << endl;
+ graph.print();
+
+ // Освободить память
+ for (Vertex *vet : v) {
+ delete vet;
+ }
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp b/ru/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp
new file mode 100644
index 000000000..d524dce66
--- /dev/null
+++ b/ru/codes/cpp/chapter_graph/graph_adjacency_matrix.cpp
@@ -0,0 +1,127 @@
+/**
+ * File: graph_adjacency_matrix.cpp
+ * Created Time: 2023-02-09
+ * Author: what-is-me (whatisme@outlook.jp)
+ */
+
+#include "../utils/common.hpp"
+
+/* Класс неориентированного графа на основе матрицы смежности */
+class GraphAdjMat {
+ vector vertices; // Список вершин: элементы представляют «значения вершин», а индексы — «индексы вершин»
+ vector> adjMat; // Матрица смежности, где индексы строк и столбцов соответствуют «индексам вершин»
+
+ public:
+ /* Конструктор */
+ GraphAdjMat(const vector &vertices, const vector> &edges) {
+ // Добавление вершины
+ for (int val : vertices) {
+ addVertex(val);
+ }
+ // Добавить ребра
+ // Обратите внимание: элементы edges представляют собой индексы вершин, то есть соответствуют индексам элементов vertices
+ for (const vector &edge : edges) {
+ addEdge(edge[0], edge[1]);
+ }
+ }
+
+ /* Получить число вершин */
+ int size() const {
+ return vertices.size();
+ }
+
+ /* Добавление вершины */
+ void addVertex(int val) {
+ int n = size();
+ // Добавить значение новой вершины в список вершин
+ vertices.push_back(val);
+ // Добавить строку в матрицу смежности
+ adjMat.emplace_back(vector(n, 0));
+ // Добавить столбец в матрицу смежности
+ for (vector &row : adjMat) {
+ row.push_back(0);
+ }
+ }
+
+ /* Удаление вершины */
+ void removeVertex(int index) {
+ if (index >= size()) {
+ throw out_of_range("вершина не существует");
+ }
+ // Удалить вершину с индексом index из списка вершин
+ vertices.erase(vertices.begin() + index);
+ // Удалить строку с индексом index из матрицы смежности
+ adjMat.erase(adjMat.begin() + index);
+ // Удалить столбец с индексом index из матрицы смежности
+ for (vector &row : adjMat) {
+ row.erase(row.begin() + index);
+ }
+ }
+
+ /* Добавление ребра */
+ // Параметры i и j соответствуют индексам элементов vertices
+ void addEdge(int i, int j) {
+ // Обработка выхода индекса за границы и случая равенства
+ if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) {
+ throw out_of_range("вершина не существует");
+ }
+ // В неориентированном графе матрица смежности симметрична относительно главной диагонали, то есть выполняется (i, j) == (j, i)
+ adjMat[i][j] = 1;
+ adjMat[j][i] = 1;
+ }
+
+ /* Удаление ребра */
+ // Параметры i и j соответствуют индексам элементов vertices
+ void removeEdge(int i, int j) {
+ // Обработка выхода индекса за границы и случая равенства
+ if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) {
+ throw out_of_range("вершина не существует");
+ }
+ adjMat[i][j] = 0;
+ adjMat[j][i] = 0;
+ }
+
+ /* Вывести матрицу смежности */
+ void print() {
+ cout << "Список вершин = ";
+ printVector(vertices);
+ cout << "Матрица смежности =" << endl;
+ printVectorMatrix(adjMat);
+ }
+};
+
+/* Driver Code */
+int main() {
+ /* Инициализация неориентированного графа */
+ // Обратите внимание: элементы edges представляют индексы вершин, то есть соответствуют индексам элементов vertices
+ vector vertices = {1, 3, 2, 5, 4};
+ vector> edges = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}};
+ GraphAdjMat graph(vertices, edges);
+ cout << "\nПосле инициализации граф имеет вид" << endl;
+ graph.print();
+
+ /* Добавление ребра */
+ // Индексы вершин 1 и 2 равны 0 и 2 соответственно
+ graph.addEdge(0, 2);
+ cout << "\nПосле добавления ребра 1-2 граф имеет вид" << endl;
+ graph.print();
+
+ /* Удаление ребра */
+ // Индексы вершин 1 и 3 равны 0 и 1 соответственно
+ graph.removeEdge(0, 1);
+ cout << "\nПосле удаления ребра 1-3 граф имеет вид" << endl;
+ graph.print();
+
+ /* Добавление вершины */
+ graph.addVertex(6);
+ cout << "\nПосле добавления вершины 6 граф имеет вид" << endl;
+ graph.print();
+
+ /* Удаление вершины */
+ // Индекс вершины 3 равен 1
+ graph.removeVertex(1);
+ cout << "\nПосле удаления вершины 3 граф имеет вид" << endl;
+ graph.print();
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_graph/graph_bfs.cpp b/ru/codes/cpp/chapter_graph/graph_bfs.cpp
new file mode 100644
index 000000000..ac80cb966
--- /dev/null
+++ b/ru/codes/cpp/chapter_graph/graph_bfs.cpp
@@ -0,0 +1,59 @@
+/**
+ * File: graph_bfs.cpp
+ * Created Time: 2023-03-02
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+#include "./graph_adjacency_list.cpp"
+
+/* Обход в ширину */
+// Использовать список смежности для представления графа, чтобы получить все смежные вершины заданной вершины
+vector graphBFS(GraphAdjList &graph, Vertex *startVet) {
+ // Последовательность обхода вершин
+ vector res;
+ // Хеш-множество для хранения уже посещенных вершин
+ unordered_set visited = {startVet};
+ // Очередь используется для реализации BFS
+ queue que;
+ que.push(startVet);
+ // Начиная с вершины vet, продолжать цикл, пока не будут посещены все вершины
+ while (!que.empty()) {
+ Vertex *vet = que.front();
+ que.pop(); // Извлечь головную вершину из очереди
+ res.push_back(vet); // Отметить посещенную вершину
+ // Обойти все смежные вершины данной вершины
+ for (auto adjVet : graph.adjList[vet]) {
+ if (visited.count(adjVet))
+ continue; // Пропустить уже посещенную вершину
+ que.push(adjVet); // Помещать в очередь только непосещенные вершины
+ visited.emplace(adjVet); // Отметить эту вершину как посещенную
+ }
+ }
+ // Вернуть последовательность обхода вершин
+ return res;
+}
+
+/* Driver Code */
+int main() {
+ /* Инициализация неориентированного графа */
+ vector v = valsToVets({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
+ vector> edges = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]},
+ {v[2], v[5]}, {v[3], v[4]}, {v[3], v[6]}, {v[4], v[5]},
+ {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}};
+ GraphAdjList graph(edges);
+ cout << "\nПосле инициализации граф имеет вид\n";
+ graph.print();
+
+ /* Обход в ширину */
+ vector res = graphBFS(graph, v[0]);
+ cout << "\nПоследовательность вершин при обходе в ширину (BFS)" << endl;
+ printVector(vetsToVals(res));
+
+ // Освободить память
+ for (Vertex *vet : v) {
+ delete vet;
+ }
+
+ return 0;
+}
diff --git a/ru/codes/cpp/chapter_graph/graph_dfs.cpp b/ru/codes/cpp/chapter_graph/graph_dfs.cpp
new file mode 100644
index 000000000..034c00da6
--- /dev/null
+++ b/ru/codes/cpp/chapter_graph/graph_dfs.cpp
@@ -0,0 +1,55 @@
+/**
+ * File: graph_dfs.cpp
+ * Created Time: 2023-03-02
+ * Author: krahets (krahets@163.com)
+ */
+
+#include "../utils/common.hpp"
+#include "./graph_adjacency_list.cpp"
+
+/* Вспомогательная функция обхода в глубину */
+void dfs(GraphAdjList &graph, unordered_set &visited, vector &res, Vertex *vet) {
+ res.push_back(vet); // Отметить посещенную вершину
+ visited.emplace(vet); // Отметить эту вершину как посещенную
+ // Обойти все смежные вершины данной вершины
+ for (Vertex *adjVet : graph.adjList[vet]) {
+ if (visited.count(adjVet))
+ continue; // Пропустить уже посещенную вершину
+ // Рекурсивно обходить смежные вершины
+ dfs(graph, visited, res, adjVet);
+ }
+}
+
+/* Обход в глубину */
+// Использовать список смежности для представления графа, чтобы получить все смежные вершины заданной вершины
+vector