Add ru version (#1865)

* Add Russian docs site baseline

* Add Russian localized codebase

* Polish Russian code wording

* Update ru code translation.

* Update code translation and chapter covers.

* Fix pythontutor extraction.

* Add README and landing page.

* placeholder of profiles

* Use figures of English version

* Remove chapter paperbook
This commit is contained in:
Yudong Jin
2026-03-28 04:24:07 +08:00
committed by GitHub
parent 2ca570cc33
commit 772183705e
1958 changed files with 108186 additions and 0 deletions

View File

@@ -45,6 +45,8 @@
<a href="https://github.com/krahets/hello-algo/blob/main/en/README.md">English</a>
<a href="https://github.com/krahets/hello-algo/blob/main/ja/README.md">日本語</a>
<a href="https://github.com/krahets/hello-algo/blob/main/ru/README.md">Русский</a>
</p>
## 关于本书

View File

@@ -45,6 +45,8 @@
English
<a href="https://github.com/krahets/hello-algo/blob/main/ja/README.md">日本語</a>
<a href="https://github.com/krahets/hello-algo/blob/main/ru/README.md">Русский</a>
</p>
## The book

View File

@@ -43,6 +43,8 @@
<a href="https://github.com/krahets/hello-algo/blob/main/en/README.md">English</a>
日本語
<a href="https://github.com/krahets/hello-algo/blob/main/ru/README.md">Русский</a>
</p>
## この本について

View File

@@ -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

101
ru/README.md Normal file
View File

@@ -0,0 +1,101 @@
<p align="center">
<a href="https://www.hello-algo.com/ru/">
<img src="https://www.hello-algo.com/index.assets/hello_algo_header.png" width="450"></a>
</p>
<p align="center">
<img style="height: 60px;" src="https://readme-typing-svg.demolab.com?font=Roboto&weight=400&duration=3500&pause=2000&color=21C8B8&center=true&vCenter=true&random=false&width=260&lines=Hello%2C+%D0%90%D0%BB%D0%B3%D0%BE!" alt="hello-algo-typing-svg" />
</br>
Учебник по структурам данных и алгоритмам с анимированными схемами и кодом, готовым к запуску в один клик
</p>
<p align="center">
<a href="https://www.hello-algo.com/ru/"><strong>Читать онлайн</strong></a>
<a href="https://github.com/krahets/hello-algo/releases"><strong>Скачать PDF/EPUB</strong></a>
</p>
<p align="center">
<img src="https://www.hello-algo.com/index.assets/animation.gif" width="395">
<img src="https://www.hello-algo.com/index.assets/running_code.gif" width="395">
</p>
<p align="center">
<img src="https://img.shields.io/badge/Python-snow?logo=python&logoColor=3776AB" alt="" />
<img src="https://img.shields.io/badge/Java-snow?logo=coffeescript&logoColor=FC4C02" alt="" />
<img src="https://img.shields.io/badge/C%2B%2B-snow?logo=c%2B%2B&logoColor=00599C" alt="" />
<img src="https://img.shields.io/badge/C-snow?logo=c&logoColor=A8B9CC" alt="" />
<img src="https://img.shields.io/badge/C%23-snow?logo=csharp&logoColor=512BD4" alt="" />
<img src="https://img.shields.io/badge/JavaScript-snow?logo=javascript&logoColor=E9CE30" alt="" />
<img src="https://img.shields.io/badge/Go-snow?logo=go&logoColor=00ADD8" alt="" />
<img src="https://img.shields.io/badge/Swift-snow?logo=swift&logoColor=F05138" alt="" />
<img src="https://img.shields.io/badge/Rust-snow?logo=rust&logoColor=000000" alt="" />
<img src="https://img.shields.io/badge/Ruby-snow?logo=ruby&logoColor=CC342D" alt="" />
<img src="https://img.shields.io/badge/Kotlin-snow?logo=kotlin&logoColor=7F52FF" alt="" />
<img src="https://img.shields.io/badge/TypeScript-snow?logo=typescript&logoColor=3178C6" alt="" />
<img src="https://img.shields.io/badge/Dart-snow?logo=dart&logoColor=0175C2" alt="" />
</p>
<p align="center">
<a href="https://github.com/krahets/hello-algo">简体中文</a>
<a href="https://github.com/krahets/hello-algo/blob/main/zh-hant/README.md">繁體中文</a>
<a href="https://github.com/krahets/hello-algo/blob/main/en/README.md">English</a>
<a href="https://github.com/krahets/hello-algo/blob/main/ja/README.md">日本語</a>
Русский
</p>
## О книге
Этот проект призван создать бесплатный, открытый и дружелюбный к начинающим учебник по структурам данных и алгоритмам.
- Книга построена на анимированных схемах, понятном изложении и плавной кривой обучения, помогая начинающим выстроить карту знаний по структурам данных и алгоритмам.
- Исходный код можно запускать в один клик, чтобы на практике развивать навыки программирования и понимать, как работают алгоритмы и как устроены структуры данных внутри.
- Мы поддерживаем совместное обучение: задавайте вопросы, делитесь идеями и продвигайтесь вперед через обсуждение.
Если книга оказалась вам полезной, пожалуйста, поставьте Star :star: в правом верхнем углу страницы. Спасибо!
## Рекомендации
> «Понятная вводная книга по структурам данных и алгоритмам, которая направляет читателя к обучению и умом, и руками. Настоятельно рекомендую начинающим изучать алгоритмы именно с нее.»
>
> **—— Junhui Deng, профессор факультета компьютерных наук Университета Цинхуа**
> «Если бы у меня была “Hello Algo”, когда я изучал структуры данных и алгоритмы, учиться было бы в десять раз проще!»
>
> **—— Mu Li, Senior Principal Scientist, Amazon**
## Благодарности
<p align="left">
<a href="https://go.warp.dev/hello-algo">
<img src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-02.png" alt="Warp-Github-LG-02" width="500"></a>
</p>
[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`.
Мы благодарим каждого участника, работавшего над этой книгой. Именно их самоотверженный вклад делает ее лучше:
<p align="left">
<a href="https://github.com/krahets/hello-algo/graphs/contributors">
<img width="770" src="https://contrib.rocks/image?repo=krahets/hello-algo&max=300&columns=16" />
</a>
</p>
## 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/).

35
ru/codes/Dockerfile Normal file
View File

@@ -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"]

9
ru/codes/c/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
# Ignore all
*
# Unignore all with extensions
!*.*
# Unignore all dirs
!*/
*.dSYM/
build/

20
ru/codes/c/CMakeLists.txt Normal file
View File

@@ -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)

View File

@@ -0,0 +1,3 @@
add_executable(array array.c)
add_executable(linked_list linked_list.c)
add_executable(my_list my_list.c)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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");
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -0,0 +1,213 @@
/**
* File: hash_map_chaining.c
* Created Time: 2023-10-13
* Author: SenMing (1206575349@qq.com), krahets (krahets@163.com)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Предположить, что максимальная длина 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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,2 @@
add_executable(my_heap_test my_heap_test.c)
add_executable(top_k top_k.c)

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,5 @@
add_executable(utils
common_test.c
common.h print_util.h
list_node.h tree_node.h
uthash.h)

36
ru/codes/c/utils/common.h Normal file
View File

@@ -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 <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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

Some files were not shown because too many files have changed in this diff Show More