mirror of
http://bgp.hk.skcks.cn:10086/https://github.com/krahets/hello-algo
synced 2026-04-20 12:50:51 +08:00
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:
@@ -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>
|
||||
|
||||
## 关于本书
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
## この本について
|
||||
|
||||
@@ -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
101
ru/README.md
Normal 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¢er=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
35
ru/codes/Dockerfile
Normal 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
9
ru/codes/c/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# Ignore all
|
||||
*
|
||||
# Unignore all with extensions
|
||||
!*.*
|
||||
# Unignore all dirs
|
||||
!*/
|
||||
*.dSYM/
|
||||
|
||||
build/
|
||||
20
ru/codes/c/CMakeLists.txt
Normal file
20
ru/codes/c/CMakeLists.txt
Normal 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)
|
||||
3
ru/codes/c/chapter_array_and_linkedlist/CMakeLists.txt
Normal file
3
ru/codes/c/chapter_array_and_linkedlist/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
add_executable(array array.c)
|
||||
add_executable(linked_list linked_list.c)
|
||||
add_executable(my_list my_list.c)
|
||||
114
ru/codes/c/chapter_array_and_linkedlist/array.c
Normal file
114
ru/codes/c/chapter_array_and_linkedlist/array.c
Normal 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;
|
||||
}
|
||||
89
ru/codes/c/chapter_array_and_linkedlist/linked_list.c
Normal file
89
ru/codes/c/chapter_array_and_linkedlist/linked_list.c
Normal 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;
|
||||
}
|
||||
163
ru/codes/c/chapter_array_and_linkedlist/my_list.c
Normal file
163
ru/codes/c/chapter_array_and_linkedlist/my_list.c
Normal 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;
|
||||
}
|
||||
10
ru/codes/c/chapter_backtracking/CMakeLists.txt
Normal file
10
ru/codes/c/chapter_backtracking/CMakeLists.txt
Normal 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)
|
||||
95
ru/codes/c/chapter_backtracking/n_queens.c
Normal file
95
ru/codes/c/chapter_backtracking/n_queens.c
Normal 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;
|
||||
}
|
||||
79
ru/codes/c/chapter_backtracking/permutations_i.c
Normal file
79
ru/codes/c/chapter_backtracking/permutations_i.c
Normal 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;
|
||||
}
|
||||
81
ru/codes/c/chapter_backtracking/permutations_ii.c
Normal file
81
ru/codes/c/chapter_backtracking/permutations_ii.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
78
ru/codes/c/chapter_backtracking/subset_sum_i.c
Normal file
78
ru/codes/c/chapter_backtracking/subset_sum_i.c
Normal 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;
|
||||
}
|
||||
69
ru/codes/c/chapter_backtracking/subset_sum_i_naive.c
Normal file
69
ru/codes/c/chapter_backtracking/subset_sum_i_naive.c
Normal 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;
|
||||
}
|
||||
83
ru/codes/c/chapter_backtracking/subset_sum_ii.c
Normal file
83
ru/codes/c/chapter_backtracking/subset_sum_ii.c
Normal 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;
|
||||
}
|
||||
@@ -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)
|
||||
81
ru/codes/c/chapter_computational_complexity/iteration.c
Normal file
81
ru/codes/c/chapter_computational_complexity/iteration.c
Normal 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;
|
||||
}
|
||||
77
ru/codes/c/chapter_computational_complexity/recursion.c
Normal file
77
ru/codes/c/chapter_computational_complexity/recursion.c
Normal 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;
|
||||
}
|
||||
141
ru/codes/c/chapter_computational_complexity/space_complexity.c
Normal file
141
ru/codes/c/chapter_computational_complexity/space_complexity.c
Normal 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;
|
||||
}
|
||||
179
ru/codes/c/chapter_computational_complexity/time_complexity.c
Normal file
179
ru/codes/c/chapter_computational_complexity/time_complexity.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
3
ru/codes/c/chapter_divide_and_conquer/CMakeLists.txt
Normal file
3
ru/codes/c/chapter_divide_and_conquer/CMakeLists.txt
Normal 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)
|
||||
47
ru/codes/c/chapter_divide_and_conquer/binary_search_recur.c
Normal file
47
ru/codes/c/chapter_divide_and_conquer/binary_search_recur.c
Normal 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;
|
||||
}
|
||||
61
ru/codes/c/chapter_divide_and_conquer/build_tree.c
Normal file
61
ru/codes/c/chapter_divide_and_conquer/build_tree.c
Normal 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;
|
||||
}
|
||||
74
ru/codes/c/chapter_divide_and_conquer/hanota.c
Normal file
74
ru/codes/c/chapter_divide_and_conquer/hanota.c
Normal 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;
|
||||
}
|
||||
8
ru/codes/c/chapter_dynamic_programming/CMakeLists.txt
Normal file
8
ru/codes/c/chapter_dynamic_programming/CMakeLists.txt
Normal 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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
32
ru/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c
Normal file
32
ru/codes/c/chapter_dynamic_programming/climbing_stairs_dfs.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
51
ru/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c
Normal file
51
ru/codes/c/chapter_dynamic_programming/climbing_stairs_dp.c
Normal 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;
|
||||
}
|
||||
92
ru/codes/c/chapter_dynamic_programming/coin_change.c
Normal file
92
ru/codes/c/chapter_dynamic_programming/coin_change.c
Normal 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;
|
||||
}
|
||||
81
ru/codes/c/chapter_dynamic_programming/coin_change_ii.c
Normal file
81
ru/codes/c/chapter_dynamic_programming/coin_change_ii.c
Normal 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;
|
||||
}
|
||||
159
ru/codes/c/chapter_dynamic_programming/edit_distance.c
Normal file
159
ru/codes/c/chapter_dynamic_programming/edit_distance.c
Normal 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;
|
||||
}
|
||||
137
ru/codes/c/chapter_dynamic_programming/knapsack.c
Normal file
137
ru/codes/c/chapter_dynamic_programming/knapsack.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
134
ru/codes/c/chapter_dynamic_programming/min_path_sum.c
Normal file
134
ru/codes/c/chapter_dynamic_programming/min_path_sum.c
Normal 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;
|
||||
}
|
||||
81
ru/codes/c/chapter_dynamic_programming/unbounded_knapsack.c
Normal file
81
ru/codes/c/chapter_dynamic_programming/unbounded_knapsack.c
Normal 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;
|
||||
}
|
||||
4
ru/codes/c/chapter_graph/CMakeLists.txt
Normal file
4
ru/codes/c/chapter_graph/CMakeLists.txt
Normal 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)
|
||||
171
ru/codes/c/chapter_graph/graph_adjacency_list.c
Normal file
171
ru/codes/c/chapter_graph/graph_adjacency_list.c
Normal 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");
|
||||
}
|
||||
}
|
||||
55
ru/codes/c/chapter_graph/graph_adjacency_list_test.c
Normal file
55
ru/codes/c/chapter_graph/graph_adjacency_list_test.c
Normal 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;
|
||||
}
|
||||
150
ru/codes/c/chapter_graph/graph_adjacency_matrix.c
Normal file
150
ru/codes/c/chapter_graph/graph_adjacency_matrix.c
Normal 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;
|
||||
}
|
||||
116
ru/codes/c/chapter_graph/graph_bfs.c
Normal file
116
ru/codes/c/chapter_graph/graph_bfs.c
Normal 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;
|
||||
}
|
||||
75
ru/codes/c/chapter_graph/graph_dfs.c
Normal file
75
ru/codes/c/chapter_graph/graph_dfs.c
Normal 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;
|
||||
}
|
||||
8
ru/codes/c/chapter_greedy/CMakeLists.txt
Normal file
8
ru/codes/c/chapter_greedy/CMakeLists.txt
Normal 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()
|
||||
60
ru/codes/c/chapter_greedy/coin_change_greedy.c
Normal file
60
ru/codes/c/chapter_greedy/coin_change_greedy.c
Normal 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;
|
||||
}
|
||||
60
ru/codes/c/chapter_greedy/fractional_knapsack.c
Normal file
60
ru/codes/c/chapter_greedy/fractional_knapsack.c
Normal 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;
|
||||
}
|
||||
49
ru/codes/c/chapter_greedy/max_capacity.c
Normal file
49
ru/codes/c/chapter_greedy/max_capacity.c
Normal 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;
|
||||
}
|
||||
38
ru/codes/c/chapter_greedy/max_product_cutting.c
Normal file
38
ru/codes/c/chapter_greedy/max_product_cutting.c
Normal 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;
|
||||
}
|
||||
4
ru/codes/c/chapter_hashing/CMakeLists.txt
Normal file
4
ru/codes/c/chapter_hashing/CMakeLists.txt
Normal 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)
|
||||
215
ru/codes/c/chapter_hashing/array_hash_map.c
Normal file
215
ru/codes/c/chapter_hashing/array_hash_map.c
Normal 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;
|
||||
}
|
||||
213
ru/codes/c/chapter_hashing/hash_map_chaining.c
Normal file
213
ru/codes/c/chapter_hashing/hash_map_chaining.c
Normal 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;
|
||||
}
|
||||
211
ru/codes/c/chapter_hashing/hash_map_open_addressing.c
Normal file
211
ru/codes/c/chapter_hashing/hash_map_open_addressing.c
Normal 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;
|
||||
}
|
||||
68
ru/codes/c/chapter_hashing/simple_hash.c
Normal file
68
ru/codes/c/chapter_hashing/simple_hash.c
Normal 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;
|
||||
}
|
||||
2
ru/codes/c/chapter_heap/CMakeLists.txt
Normal file
2
ru/codes/c/chapter_heap/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
add_executable(my_heap_test my_heap_test.c)
|
||||
add_executable(top_k top_k.c)
|
||||
152
ru/codes/c/chapter_heap/my_heap.c
Normal file
152
ru/codes/c/chapter_heap/my_heap.c
Normal 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;
|
||||
}
|
||||
}
|
||||
41
ru/codes/c/chapter_heap/my_heap_test.c
Normal file
41
ru/codes/c/chapter_heap/my_heap_test.c
Normal 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;
|
||||
}
|
||||
73
ru/codes/c/chapter_heap/top_k.c
Normal file
73
ru/codes/c/chapter_heap/top_k.c
Normal 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;
|
||||
}
|
||||
4
ru/codes/c/chapter_searching/CMakeLists.txt
Normal file
4
ru/codes/c/chapter_searching/CMakeLists.txt
Normal 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)
|
||||
59
ru/codes/c/chapter_searching/binary_search.c
Normal file
59
ru/codes/c/chapter_searching/binary_search.c
Normal 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;
|
||||
}
|
||||
67
ru/codes/c/chapter_searching/binary_search_edge.c
Normal file
67
ru/codes/c/chapter_searching/binary_search_edge.c
Normal 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;
|
||||
}
|
||||
68
ru/codes/c/chapter_searching/binary_search_insertion.c
Normal file
68
ru/codes/c/chapter_searching/binary_search_insertion.c
Normal 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;
|
||||
}
|
||||
86
ru/codes/c/chapter_searching/two_sum.c
Normal file
86
ru/codes/c/chapter_searching/two_sum.c
Normal 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;
|
||||
}
|
||||
9
ru/codes/c/chapter_sorting/CMakeLists.txt
Normal file
9
ru/codes/c/chapter_sorting/CMakeLists.txt
Normal 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)
|
||||
61
ru/codes/c/chapter_sorting/bubble_sort.c
Normal file
61
ru/codes/c/chapter_sorting/bubble_sort.c
Normal 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;
|
||||
}
|
||||
57
ru/codes/c/chapter_sorting/bucket_sort.c
Normal file
57
ru/codes/c/chapter_sorting/bucket_sort.c
Normal 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;
|
||||
}
|
||||
87
ru/codes/c/chapter_sorting/counting_sort.c
Normal file
87
ru/codes/c/chapter_sorting/counting_sort.c
Normal 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;
|
||||
}
|
||||
60
ru/codes/c/chapter_sorting/heap_sort.c
Normal file
60
ru/codes/c/chapter_sorting/heap_sort.c
Normal 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;
|
||||
}
|
||||
36
ru/codes/c/chapter_sorting/insertion_sort.c
Normal file
36
ru/codes/c/chapter_sorting/insertion_sort.c
Normal 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;
|
||||
}
|
||||
63
ru/codes/c/chapter_sorting/merge_sort.c
Normal file
63
ru/codes/c/chapter_sorting/merge_sort.c
Normal 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;
|
||||
}
|
||||
137
ru/codes/c/chapter_sorting/quick_sort.c
Normal file
137
ru/codes/c/chapter_sorting/quick_sort.c
Normal 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;
|
||||
}
|
||||
75
ru/codes/c/chapter_sorting/radix_sort.c
Normal file
75
ru/codes/c/chapter_sorting/radix_sort.c
Normal 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);
|
||||
}
|
||||
37
ru/codes/c/chapter_sorting/selection_sort.c
Normal file
37
ru/codes/c/chapter_sorting/selection_sort.c
Normal 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;
|
||||
}
|
||||
6
ru/codes/c/chapter_stack_and_queue/CMakeLists.txt
Normal file
6
ru/codes/c/chapter_stack_and_queue/CMakeLists.txt
Normal 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)
|
||||
172
ru/codes/c/chapter_stack_and_queue/array_deque.c
Normal file
172
ru/codes/c/chapter_stack_and_queue/array_deque.c
Normal 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;
|
||||
}
|
||||
134
ru/codes/c/chapter_stack_and_queue/array_queue.c
Normal file
134
ru/codes/c/chapter_stack_and_queue/array_queue.c
Normal 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;
|
||||
}
|
||||
103
ru/codes/c/chapter_stack_and_queue/array_stack.c
Normal file
103
ru/codes/c/chapter_stack_and_queue/array_stack.c
Normal 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;
|
||||
}
|
||||
212
ru/codes/c/chapter_stack_and_queue/linkedlist_deque.c
Normal file
212
ru/codes/c/chapter_stack_and_queue/linkedlist_deque.c
Normal 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;
|
||||
}
|
||||
128
ru/codes/c/chapter_stack_and_queue/linkedlist_queue.c
Normal file
128
ru/codes/c/chapter_stack_and_queue/linkedlist_queue.c
Normal 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;
|
||||
}
|
||||
107
ru/codes/c/chapter_stack_and_queue/linkedlist_stack.c
Normal file
107
ru/codes/c/chapter_stack_and_queue/linkedlist_stack.c
Normal 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;
|
||||
}
|
||||
6
ru/codes/c/chapter_tree/CMakeLists.txt
Normal file
6
ru/codes/c/chapter_tree/CMakeLists.txt
Normal 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)
|
||||
166
ru/codes/c/chapter_tree/array_binary_tree.c
Normal file
166
ru/codes/c/chapter_tree/array_binary_tree.c
Normal 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;
|
||||
}
|
||||
259
ru/codes/c/chapter_tree/avl_tree.c
Normal file
259
ru/codes/c/chapter_tree/avl_tree.c
Normal 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;
|
||||
}
|
||||
171
ru/codes/c/chapter_tree/binary_search_tree.c
Normal file
171
ru/codes/c/chapter_tree/binary_search_tree.c
Normal 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;
|
||||
}
|
||||
43
ru/codes/c/chapter_tree/binary_tree.c
Normal file
43
ru/codes/c/chapter_tree/binary_tree.c
Normal 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;
|
||||
}
|
||||
73
ru/codes/c/chapter_tree/binary_tree_bfs.c
Normal file
73
ru/codes/c/chapter_tree/binary_tree_bfs.c
Normal 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;
|
||||
}
|
||||
75
ru/codes/c/chapter_tree/binary_tree_dfs.c
Normal file
75
ru/codes/c/chapter_tree/binary_tree_dfs.c
Normal 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;
|
||||
}
|
||||
5
ru/codes/c/utils/CMakeLists.txt
Normal file
5
ru/codes/c/utils/CMakeLists.txt
Normal 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
36
ru/codes/c/utils/common.h
Normal 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
|
||||
35
ru/codes/c/utils/common_test.c
Normal file
35
ru/codes/c/utils/common_test.c
Normal 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;
|
||||
}
|
||||
59
ru/codes/c/utils/list_node.h
Normal file
59
ru/codes/c/utils/list_node.h
Normal 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
|
||||
131
ru/codes/c/utils/print_util.h
Normal file
131
ru/codes/c/utils/print_util.h
Normal 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
Reference in New Issue
Block a user