feat: Traditional Chinese version (#1163)

* First commit

* Update mkdocs.yml

* Translate all the docs to traditional Chinese

* Translate the code files.

* Translate the docker file

* Fix mkdocs.yml

* Translate all the figures from SC to TC

* 二叉搜尋樹 -> 二元搜尋樹

* Update terminology.

* Update terminology

* 构造函数/构造方法 -> 建構子
异或 -> 互斥或

* 擴充套件 -> 擴展

* constant - 常量 - 常數

* 類	-> 類別

* AVL -> AVL 樹

* 數組 -> 陣列

* 係統 -> 系統
斐波那契數列 -> 費波那契數列
運算元量 -> 運算量
引數 -> 參數

* 聯絡 -> 關聯

* 麵試 -> 面試

* 面向物件 -> 物件導向
歸併排序 -> 合併排序
范式 -> 範式

* Fix 算法 -> 演算法

* 錶示 -> 表示
反碼 -> 一補數
補碼 -> 二補數
列列尾部 -> 佇列尾部
區域性性 -> 區域性
一摞 -> 一疊

* Synchronize with main branch

* 賬號 -> 帳號
推匯 -> 推導

* Sync with main branch

* First commit

* Update mkdocs.yml

* Translate all the docs to traditional Chinese

* Translate the code files.

* Translate the docker file

* Fix mkdocs.yml

* Translate all the figures from SC to TC

* 二叉搜尋樹 -> 二元搜尋樹

* Update terminology

* 构造函数/构造方法 -> 建構子
异或 -> 互斥或

* 擴充套件 -> 擴展

* constant - 常量 - 常數

* 類	-> 類別

* AVL -> AVL 樹

* 數組 -> 陣列

* 係統 -> 系統
斐波那契數列 -> 費波那契數列
運算元量 -> 運算量
引數 -> 參數

* 聯絡 -> 關聯

* 麵試 -> 面試

* 面向物件 -> 物件導向
歸併排序 -> 合併排序
范式 -> 範式

* Fix 算法 -> 演算法

* 錶示 -> 表示
反碼 -> 一補數
補碼 -> 二補數
列列尾部 -> 佇列尾部
區域性性 -> 區域性
一摞 -> 一疊

* Synchronize with main branch

* 賬號 -> 帳號
推匯 -> 推導

* Sync with main branch

* Update terminology.md

* 操作数量(num. of operations)-> 操作數量

* 字首和->前綴和

* Update figures

* 歸 -> 迴
記憶體洩漏 -> 記憶體流失

* Fix the bug of the file filter

* 支援 -> 支持
Add zh-Hant/README.md

* Add the zh-Hant chapter covers.
Bug fixes.

* 外掛 -> 擴充功能

* Add the landing page for zh-Hant version

* Unify the font of the chapter covers for the zh, en, and zh-Hant version

* Move zh-Hant/ to zh-hant/

* Translate terminology.md to traditional Chinese
This commit is contained in:
Yudong Jin
2024-04-06 02:30:11 +08:00
committed by GitHub
parent 33d7f8a2e5
commit 5f7385c8a3
1875 changed files with 102923 additions and 18 deletions

View File

@@ -8,64 +8,64 @@
style="position: absolute; width: auto; height: 26.445%; left: 28.211%; top: 54.145%;">
<img src="assets/hero/links.png" alt=""
style="position: absolute; width: auto; height: 78.751%; left: 10.545%; top: 7.326%;">
<a href="/chapter_introduction/">
<a href="chapter_introduction/">
<img src="assets/hero/astronaut.png" alt="" style="height: 46.673%; left: 35.413%; top: 24.343%;">
<span style="left: 52.244%; top: 20.919%;">初识算法</span>
</a>
<a href="/chapter_computational_complexity/">
<a href="chapter_computational_complexity/">
<img src="assets/hero/chapter_computational_complexity.png" alt=""
style="height: 12.347%; left: 36.267%; top: 37.653%;">
<span style="left: 39.244%; top: 33.919%;">复杂度</span>
</a>
<a href="/chapter_array_and_linkedlist/">
<a href="chapter_array_and_linkedlist/">
<img src="assets/hero/chapter_array_and_linkedlist.png" alt=""
style="height: 22.242%; left: 73.242%; top: 52.481%;">
<span style="left: 90.897%; top: 76.259%;">数组与链表</span>
</a>
<a href="/chapter_stack_and_queue/">
<a href="chapter_stack_and_queue/">
<img src="assets/hero/chapter_stack_and_queue.png" alt=""
style="height: 14.302%; left: 62.646%; top: 77.875%;">
<span style="left: 77.571%; top: 91.25%;">栈与队列</span>
</a>
<a href="/chapter_hashing/">
<a href="chapter_hashing/">
<img src="assets/hero/chapter_hashing.png" alt="" style="height: 15.266%; left: 63.281%; top: 27.933%;">
<span style="left: 68.862%; top: 46.292%;">哈希表</span>
</a>
<a href="/chapter_tree/">
<a href="chapter_tree/">
<img src="assets/hero/chapter_tree.png" alt="" style="height: 19.615%; left: 80.137%; top: 26.678%;">
<span style="left: 96.159%; top: 44.8%;"></span>
</a>
<a href="/chapter_heap/">
<a href="chapter_heap/">
<img src="assets/hero/chapter_heap.png" alt="" style="height: 10.566%; left: 77.226%; top: 11.559%;">
<span style="left: 88.103%; top: 15.422%;"></span>
</a>
<a href="/chapter_graph/">
<a href="chapter_graph/">
<img src="assets/hero/chapter_graph.png" alt="" style="height: 16.112%; left: 51.854%; top: 5.575%;">
<span style="left: 71.195%; top: 6.503%;"></span>
</a>
<a href="/chapter_searching/">
<a href="chapter_searching/">
<img src="assets/hero/chapter_searching.png" alt="" style="height: 15.149%; left: 18.185%; top: 16.404%;">
<span style="left: 14.556%; top: 20.876%;">搜索</span>
</a>
<a href="/chapter_sorting/">
<a href="chapter_sorting/">
<img src="assets/hero/chapter_sorting.png" alt="" style="height: 9.574%; left: 25.409%; top: 40.747%;">
<span style="left: 28.805%; top: 53.808%;">排序</span>
</a>
<a href="/chapter_divide_and_conquer/">
<a href="chapter_divide_and_conquer/">
<img src="assets/hero/chapter_divide_and_conquer.png" alt=""
style="height: 18.681%; left: 32.721%; top: 4.816%;">
<span style="left: 31.42%; top: 8.679%;">分治</span>
</a>
<a href="/chapter_backtracking/">
<a href="chapter_backtracking/">
<img src="assets/hero/chapter_backtracking.png" alt="" style="height: 17.338%; left: 4.875%; top: 32.925%;">
<span style="left: 4.742%; top: 50.113%;">回溯</span>
</a>
<a href="/chapter_dynamic_programming/">
<a href="chapter_dynamic_programming/">
<img src="assets/hero/chapter_dynamic_programming.png" alt=""
style="height: 15.47%; left: 9.406%; top: 57.472%;">
<span style="left: 8.561%; top: 75.351%;">动态规划</span>
</a>
<a href="/chapter_greedy/">
<a href="chapter_greedy/">
<img src="assets/hero/chapter_greedy.png" alt="" style="height: 14.127%; left: 23.132%; top: 75.803%;">
<span style="left: 21.619%; top: 86.85%;">贪心</span>
</a>
@@ -80,7 +80,7 @@
<p style="margin-top: max(-1vh, -2vw); margin-bottom: min(2vh, 3.5vw);">
动画图解、一键运行的数据结构与算法教程
</p>
<a href="/chapter_hello_algo/" class="rounded-button">
<a href="chapter_hello_algo/" class="rounded-button">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path
@@ -140,7 +140,7 @@
<!-- ipad height = 280.6mm -->
<!-- iphone height = 160.7mm -->
<div class="media-block">
<a href="/chapter_paperbook/">
<a href="chapter_paperbook/">
<div style="height: 8.17%;"></div>
<img class="device-on-hover" style="height: 66.83%;" src="assets/hero/cover_render.png" alt="Cover">
<div class="text-button">
@@ -153,7 +153,7 @@
</a>
</div>
<div class="media-block">
<a href="/chapter_hello_algo/">
<a href="chapter_hello_algo/">
<div style="height: 4.34%;"></div>
<img class="device-on-hover" style="height: 66.31%;" src="assets/hero/web_mac_iphone.png" alt="">
<div style="height: 4.34%;"></div>

View File

@@ -68,9 +68,12 @@ theme:
extra:
alternate:
- name: 中文
- name: 简体中文
link: /
lang: zh
- name: 繁體中文
link: /zh-hant/
lang: zh-Hant
- name: English
link: /en/
lang: en

82
zh-hant/README.md Normal file
View File

@@ -0,0 +1,82 @@
<p align="center">
<a href="https://www.hello-algo.com/">
<img src="https://www.hello-algo.com/index.assets/hello_algo_header.png" width="450"></a>
</p>
<p align="center">
<img src="https://readme-typing-svg.demolab.com?font=Noto+Sans+SC&weight=500&duration=3500&pause=2000&color=21C8B8&center=true&vCenter=true&random=false&width=200&lines=Hello%2C+%E7%AE%97%E6%B3%95+!" alt="hello-algo-typing-svg" />
</br>
動畫圖解、一鍵執行的資料結構與演算法教程
</p>
<p align="center">
<a href="https://www.hello-algo.com/">
<img src="https://www.hello-algo.com/index.assets/btn_read_online_dark.svg" width="145"></a>
<a href="https://github.com/krahets/hello-algo/releases">
<img src="https://www.hello-algo.com/index.assets/btn_download_pdf_dark.svg" width="145"></a>
<a href="https://github.com/krahets/hello-algo/blob/main/en/README.md">
<img src="https://www.hello-algo.com/index.assets/btn_english_edition_dark.svg" width="145"></a>
</p>
<p align="center">
<img src="https://www.hello-algo.com/index.assets/animation.gif" width="396">
<img src="https://www.hello-algo.com/index.assets/running_code.gif" width="396">
</p>
<p align="center">
<img src="https://img.shields.io/badge/Python-snow?logo=python&logoColor=3776AB">
<img src="https://img.shields.io/badge/C%2B%2B-snow?logo=c%2B%2B&logoColor=00599C">
<img src="https://img.shields.io/badge/Java-snow?logo=coffeescript&logoColor=FC4C02">
<img src="https://img.shields.io/badge/C%23-snow?logo=csharp&logoColor=512BD4">
<img src="https://img.shields.io/badge/Go-snow?logo=go&logoColor=00ADD8">
<img src="https://img.shields.io/badge/Swift-snow?logo=swift&logoColor=F05138">
<img src="https://img.shields.io/badge/JavaScript-snow?logo=javascript&logoColor=E9CE30">
<img src="https://img.shields.io/badge/TypeScript-snow?logo=typescript&logoColor=3178C6">
<img src="https://img.shields.io/badge/Dart-snow?logo=dart&logoColor=0175C2">
<img src="https://img.shields.io/badge/Rust-snow?logo=rust&logoColor=000000">
<img src="https://img.shields.io/badge/C-snow?logo=c&logoColor=A8B9CC">
<img src="https://img.shields.io/badge/Zig-snow?logo=zig&logoColor=F7A41D">
<img src="https://img.shields.io/badge/Stay%20Tuned-snow">
</p>
## 關於本書
本專案旨在打造一本開源免費、新手友好的資料結構與演算法入門教程。
- 全書採用動畫圖解,內容清晰易懂、學習曲線平滑,引導初學者探索資料結構與演算法的知識地圖。
- 源程式碼可一鍵執行,幫助讀者在練習中提升程式設計技能,瞭解演算法工作原理和資料結構底層實現。
- 鼓勵讀者互助學習,提問與評論通常可在兩日內得到回覆。
若本書對您有所幫助,請在頁面右上角點個 Star :star: 支持一下,謝謝!
## 推薦語
> “一本通俗易懂的資料結構與演算法入門書,引導讀者手腦並用地學習,強烈推薦演算法初學者閱讀。”
>
> **—— 鄧俊輝,清華大學計算機系教授**
> “如果我當年學資料結構與演算法的時候有《Hello 演算法》,學起來應該會簡單 10 倍!”
>
> **—— 李沐,亞馬遜資深首席科學家**
## 貢獻
本開源書仍在持續更新之中,歡迎您參與本專案,一同為讀者提供更優質的學習內容。
- [內容修正](https://www.hello-algo.com/chapter_appendix/contribution/):請您協助修正或在評論區指出語法錯誤、內容缺失、文字歧義、無效連結或程式碼 bug 等問題。
- [程式碼轉譯](https://github.com/krahets/hello-algo/issues/15):期待您貢獻各種語言程式碼,已支持 Python、Java、C++、Go、JavaScript 等 12 門程式語言。
- [中譯英](https://github.com/krahets/hello-algo/issues/914):誠邀您加入我們的翻譯小組,成員主要來自計算機相關專業、英語專業和英文母語者。
歡迎您提出寶貴意見和建議,如有任何問題請提交 Issues 或微信關聯 `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
zh-hant/codes/Dockerfile Normal file
View File

@@ -0,0 +1,35 @@
FROM ubuntu:latest
# Use Ubuntu image from Aliyun
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/ports.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
RUN apt-get update && apt-get install -y wget
# Install languages environment
ARG LANGS
RUN for LANG in $LANGS; do \
case $LANG in \
python) \
apt-get install -y python3.10 && \
update-alternatives --install /usr/bin/python python /usr/bin/python3.10 1 ;; \
cpp) \
apt-get install -y g++ gdb ;; \
java) \
apt-get install -y openjdk-17-jdk ;; \
csharp) \
wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \
dpkg -i packages-microsoft-prod.deb && \
apt-get update && \
apt-get install -y dotnet-sdk-8.0 ;; \
# More languages...
*) \
echo "Warning: No installation workflow for $LANG" ;; \
esac \
done
WORKDIR /codes
COPY ./ ./
CMD ["/bin/bash"]

9
zh-hant/codes/c/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.10)
project(hello_algo C)
set(CMAKE_C_STANDARD 11)
include_directories(./include)
add_subdirectory(chapter_computational_complexity)
add_subdirectory(chapter_array_and_linkedlist)
add_subdirectory(chapter_stack_and_queue)
add_subdirectory(chapter_hashing)
add_subdirectory(chapter_tree)
add_subdirectory(chapter_heap)
add_subdirectory(chapter_graph)
add_subdirectory(chapter_searching)
add_subdirectory(chapter_sorting)
add_subdirectory(chapter_divide_and_conquer)
add_subdirectory(chapter_backtracking)
add_subdirectory(chapter_dynamic_programming)
add_subdirectory(chapter_greedy)

View File

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

View File

@@ -0,0 +1,114 @@
/**
* File: array.c
* Created Time: 2022-12-20
* Author: MolDuM (moldum@163.com)
*/
#include "../utils/common.h"
/* 隨機訪問元素 */
int randomAccess(int *nums, int size) {
// 在區間 [0, size) 中隨機抽取一個數字
int randomIndex = rand() % size;
// 獲取並返回隨機元素
int randomNum = nums[randomIndex];
return randomNum;
}
/* 擴展陣列長度 */
int *extend(int *nums, int size, int enlarge) {
// 初始化一個擴展長度後的陣列
int *res = (int *)malloc(sizeof(int) * (size + enlarge));
// 將原陣列中的所有元素複製到新陣列
for (int i = 0; i < size; i++) {
res[i] = nums[i];
}
// 初始化擴展後的空間
for (int i = size; i < size + enlarge; i++) {
res[i] = 0;
}
// 返回擴展後的新陣列
return res;
}
/* 在陣列的索引 index 處插入元素 num */
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("在索引 3 處插入數字 6 ,得到 nums = ");
printArray(res, size);
/* 刪除元素 */
removeItem(res, size, 2);
printf("刪除索引 2 處的元素,得到 nums = ");
printArray(res, size);
/* 走訪陣列 */
traverse(res, size);
/* 查詢元素 */
int index = find(res, size, 3);
printf("在 res 中查詢元素 3 ,得到索引 = %d\n", index);
/* 釋放記憶體 */
free(res);
return 0;
}

View File

@@ -0,0 +1,89 @@
/**
* File: linked_list.c
* Created Time: 2023-01-12
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 在鏈結串列的節點 n0 之後插入節點 P */
void insert(ListNode *n0, ListNode *P) {
ListNode *n1 = n0->next;
P->next = n1;
n0->next = P;
}
/* 刪除鏈結串列的節點 n0 之後的首個節點 */
// 注意stdio.h 佔用了 remove 關鍵詞
void removeItem(ListNode *n0) {
if (!n0->next)
return;
// n0 -> P -> n1
ListNode *P = n0->next;
ListNode *n1 = P->next;
n0->next = n1;
// 釋放記憶體
free(P);
}
/* 訪問鏈結串列中索引為 index 的節點 */
ListNode *access(ListNode *head, int index) {
for (int i = 0; i < index; i++) {
if (head == NULL)
return NULL;
head = head->next;
}
return head;
}
/* 在鏈結串列中查詢值為 target 的首個節點 */
int find(ListNode *head, int target) {
int index = 0;
while (head) {
if (head->val == target)
return index;
head = head->next;
index++;
}
return -1;
}
/* Driver Code */
int main() {
/* 初始化鏈結串列 */
// 初始化各個節點
ListNode *n0 = newListNode(1);
ListNode *n1 = newListNode(3);
ListNode *n2 = newListNode(2);
ListNode *n3 = newListNode(5);
ListNode *n4 = newListNode(4);
// 構建節點之間的引用
n0->next = n1;
n1->next = n2;
n2->next = n3;
n3->next = n4;
printf("初始化的鏈結串列為\r\n");
printLinkedList(n0);
/* 插入節點 */
insert(n0, newListNode(0));
printf("插入節點後的鏈結串列為\r\n");
printLinkedList(n0);
/* 刪除節點 */
removeItem(n0);
printf("刪除節點後的鏈結串列為\r\n");
printLinkedList(n0);
/* 訪問節點 */
ListNode *node = access(n0, 3);
printf("鏈結串列中索引 3 處的節點的值 = %d\r\n", node->val);
/* 查詢節點 */
int index = find(n0, 2);
printf("鏈結串列中值為 2 的節點的索引 = %d\r\n", index);
// 釋放記憶體
freeMemoryLinkedList(n0);
return 0;
}

View File

@@ -0,0 +1,163 @@
/**
* File: my_list.c
* Created Time: 2023-01-12
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 串列類別 */
typedef struct {
int *arr; // 陣列(儲存串列元素)
int capacity; // 串列容量
int size; // 串列大小
int extendRatio; // 串列每次擴容的倍數
} MyList;
void extendCapacity(MyList *nums);
/* 建構子 */
MyList *newMyList() {
MyList *nums = malloc(sizeof(MyList));
nums->capacity = 10;
nums->arr = malloc(sizeof(int) * nums->capacity);
nums->size = 0;
nums->extendRatio = 2;
return nums;
}
/* 析構函式 */
void delMyList(MyList *nums) {
free(nums->arr);
free(nums);
}
/* 獲取串列長度 */
int size(MyList *nums) {
return nums->size;
}
/* 獲取串列容量 */
int capacity(MyList *nums) {
return nums->capacity;
}
/* 訪問元素 */
int get(MyList *nums, int index) {
assert(index >= 0 && index < nums->size);
return nums->arr[index];
}
/* 更新元素 */
void set(MyList *nums, int index, int num) {
assert(index >= 0 && index < nums->size);
nums->arr[index] = num;
}
/* 在尾部新增元素 */
void add(MyList *nums, int num) {
if (size(nums) == capacity(nums)) {
extendCapacity(nums); // 擴容
}
nums->arr[size(nums)] = num;
nums->size++;
}
/* 在中間插入元素 */
void insert(MyList *nums, int index, int num) {
assert(index >= 0 && index < size(nums));
// 元素數量超出容量時,觸發擴容機制
if (size(nums) == capacity(nums)) {
extendCapacity(nums); // 擴容
}
for (int i = size(nums); i > index; --i) {
nums->arr[i] = nums->arr[i - 1];
}
nums->arr[index] = num;
nums->size++;
}
/* 刪除元素 */
// 注意stdio.h 佔用了 remove 關鍵詞
int removeItem(MyList *nums, int index) {
assert(index >= 0 && index < size(nums));
int num = nums->arr[index];
for (int i = index; i < size(nums) - 1; i++) {
nums->arr[i] = nums->arr[i + 1];
}
nums->size--;
return num;
}
/* 串列擴容 */
void extendCapacity(MyList *nums) {
// 先分配空間
int newCapacity = capacity(nums) * nums->extendRatio;
int *extend = (int *)malloc(sizeof(int) * newCapacity);
int *temp = nums->arr;
// 複製舊資料到新資料
for (int i = 0; i < size(nums); i++)
extend[i] = nums->arr[i];
// 釋放舊資料
free(temp);
// 更新新資料
nums->arr = extend;
nums->capacity = newCapacity;
}
/* 將串列轉換為 Array 用於列印 */
int *toArray(MyList *nums) {
return nums->arr;
}
/* Driver Code */
int main() {
/* 初始化串列 */
MyList *nums = newMyList();
/* 在尾部新增元素 */
add(nums, 1);
add(nums, 3);
add(nums, 2);
add(nums, 5);
add(nums, 4);
printf("串列 nums = ");
printArray(toArray(nums), size(nums));
printf("容量 = %d ,長度 = %d\n", capacity(nums), size(nums));
/* 在中間插入元素 */
insert(nums, 3, 6);
printf("在索引 3 處插入數字 6 ,得到 nums = ");
printArray(toArray(nums), size(nums));
/* 刪除元素 */
removeItem(nums, 3);
printf("刪除索引 3 處的元素,得到 nums = ");
printArray(toArray(nums), size(nums));
/* 訪問元素 */
int num = get(nums, 1);
printf("訪問索引 1 處的元素,得到 num = %d\n", num);
/* 更新元素 */
set(nums, 1, 0);
printf("將索引 1 處的元素更新為 0 ,得到 nums = ");
printArray(toArray(nums), size(nums));
/* 測試擴容機制 */
for (int i = 0; i < 10; i++) {
// 在 i = 5 時,串列長度將超出串列容量,此時觸發擴容機制
add(nums, i);
}
printf("擴容後的串列 nums = ");
printArray(toArray(nums), size(nums));
printf("容量 = %d ,長度 = %d\n", capacity(nums), size(nums));
/* 釋放分配記憶體 */
delMyList(nums);
return 0;
}

View File

@@ -0,0 +1,10 @@
add_executable(permutations_i permutations_i.c)
add_executable(permutations_ii permutations_ii.c)
add_executable(preorder_traversal_i_compact preorder_traversal_i_compact.c)
add_executable(preorder_traversal_ii_compact preorder_traversal_ii_compact.c)
add_executable(preorder_traversal_iii_compact preorder_traversal_iii_compact.c)
add_executable(preorder_traversal_iii_template preorder_traversal_iii_template.c)
add_executable(subset_sum_i_naive subset_sum_i_naive.c)
add_executable(subset_sum_i subset_sum_i.c)
add_executable(subset_sum_ii subset_sum_ii.c)
add_executable(n_queens n_queens.c)

View File

@@ -0,0 +1,95 @@
/**
* File : n_queens.c
* Created Time: 2023-09-25
* Author : lucas (superrat6@gmail.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
/* 回溯演算法n 皇后 */
void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE],
bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) {
// 當放置完所有行時,記錄解
if (row == n) {
res[*resSize] = (char **)malloc(sizeof(char *) * n);
for (int i = 0; i < n; ++i) {
res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1));
strcpy(res[*resSize][i], state[i]);
}
(*resSize)++;
return;
}
// 走訪所有列
for (int col = 0; col < n; col++) {
// 計算該格子對應的主對角線和次對角線
int diag1 = row - col + n - 1;
int diag2 = row + col;
// 剪枝:不允許該格子所在列、主對角線、次對角線上存在皇后
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
// 嘗試:將皇后放置在該格子
state[row][col] = 'Q';
cols[col] = diags1[diag1] = diags2[diag2] = true;
// 放置下一行
backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2);
// 回退:將該格子恢復為空位
state[row][col] = '#';
cols[col] = diags1[diag1] = diags2[diag2] = false;
}
}
}
/* 求解 n 皇后 */
char ***nQueens(int n, int *returnSize) {
char state[MAX_SIZE][MAX_SIZE];
// 初始化 n*n 大小的棋盤,其中 'Q' 代表皇后,'#' 代表空位
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
state[i][j] = '#';
}
state[i][n] = '\0';
}
bool cols[MAX_SIZE] = {false}; // 記錄列是否有皇后
bool diags1[2 * MAX_SIZE - 1] = {false}; // 記錄主對角線上是否有皇后
bool diags2[2 * MAX_SIZE - 1] = {false}; // 記錄次對角線上是否有皇后
char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE);
*returnSize = 0;
backtrack(0, n, state, res, returnSize, cols, diags1, diags2);
return res;
}
/* Driver Code */
int main() {
int n = 4;
int returnSize;
char ***res = nQueens(n, &returnSize);
printf("輸入棋盤長寬為%d\n", n);
printf("皇后放置方案共有 %d 種\n", returnSize);
for (int i = 0; i < returnSize; ++i) {
for (int j = 0; j < n; ++j) {
printf("[");
for (int k = 0; res[i][j][k] != '\0'; ++k) {
printf("%c", res[i][j][k]);
if (res[i][j][k + 1] != '\0') {
printf(", ");
}
}
printf("]\n");
}
printf("---------------------\n");
}
// 釋放記憶體
for (int i = 0; i < returnSize; ++i) {
for (int j = 0; j < n; ++j) {
free(res[i][j]);
}
free(res[i]);
}
free(res);
return 0;
}

View File

@@ -0,0 +1,79 @@
/**
* File: permutations_i.c
* Created Time: 2023-06-04
* Author: Gonglja (glj0@outlook.com), krahets (krahets@163.com)
*/
#include "../utils/common.h"
// 假設最多有 1000 個排列
#define MAX_SIZE 1000
/* 回溯演算法:全排列 I */
void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
// 當狀態長度等於元素數量時,記錄解
if (stateSize == choicesSize) {
res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
for (int i = 0; i < choicesSize; i++) {
res[*resSize][i] = state[i];
}
(*resSize)++;
return;
}
// 走訪所有選擇
for (int i = 0; i < choicesSize; i++) {
int choice = choices[i];
// 剪枝:不允許重複選擇元素
if (!selected[i]) {
// 嘗試:做出選擇,更新狀態
selected[i] = true;
state[stateSize] = choice;
// 進行下一輪選擇
backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
// 回退:撤銷選擇,恢復到之前的狀態
selected[i] = false;
}
}
}
/* 全排列 I */
int **permutationsI(int *nums, int numsSize, int *returnSize) {
int *state = (int *)malloc(numsSize * sizeof(int));
bool *selected = (bool *)malloc(numsSize * sizeof(bool));
for (int i = 0; i < numsSize; i++) {
selected[i] = false;
}
int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
*returnSize = 0;
backtrack(state, 0, nums, numsSize, selected, res, returnSize);
free(state);
free(selected);
return res;
}
/* Driver Code */
int main() {
int nums[] = {1, 2, 3};
int numsSize = sizeof(nums) / sizeof(nums[0]);
int returnSize;
int **res = permutationsI(nums, numsSize, &returnSize);
printf("輸入陣列 nums = ");
printArray(nums, numsSize);
printf("\n所有排列 res = \n");
for (int i = 0; i < returnSize; i++) {
printArray(res[i], numsSize);
}
// 釋放記憶體
for (int i = 0; i < returnSize; i++) {
free(res[i]);
}
free(res);
return 0;
}

View File

@@ -0,0 +1,81 @@
/**
* File: permutations_ii.c
* Created Time: 2023-10-17
* Author: krahets (krahets@163.com)
*/
#include "../utils/common.h"
// 假設最多有 1000 個排列,元素最大為 1000
#define MAX_SIZE 1000
/* 回溯演算法:全排列 II */
void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
// 當狀態長度等於元素數量時,記錄解
if (stateSize == choicesSize) {
res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
for (int i = 0; i < choicesSize; i++) {
res[*resSize][i] = state[i];
}
(*resSize)++;
return;
}
// 走訪所有選擇
bool duplicated[MAX_SIZE] = {false};
for (int i = 0; i < choicesSize; i++) {
int choice = choices[i];
// 剪枝:不允許重複選擇元素 且 不允許重複選擇相等元素
if (!selected[i] && !duplicated[choice]) {
// 嘗試:做出選擇,更新狀態
duplicated[choice] = true; // 記錄選擇過的元素值
selected[i] = true;
state[stateSize] = choice;
// 進行下一輪選擇
backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
// 回退:撤銷選擇,恢復到之前的狀態
selected[i] = false;
}
}
}
/* 全排列 II */
int **permutationsII(int *nums, int numsSize, int *returnSize) {
int *state = (int *)malloc(numsSize * sizeof(int));
bool *selected = (bool *)malloc(numsSize * sizeof(bool));
for (int i = 0; i < numsSize; i++) {
selected[i] = false;
}
int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
*returnSize = 0;
backtrack(state, 0, nums, numsSize, selected, res, returnSize);
free(state);
free(selected);
return res;
}
/* Driver Code */
int main() {
int nums[] = {1, 1, 2};
int numsSize = sizeof(nums) / sizeof(nums[0]);
int returnSize;
int **res = permutationsII(nums, numsSize, &returnSize);
printf("輸入陣列 nums = ");
printArray(nums, numsSize);
printf("\n所有排列 res = \n");
for (int i = 0; i < returnSize; i++) {
printArray(res[i], numsSize);
}
// 釋放記憶體
for (int i = 0; i < returnSize; i++) {
free(res[i]);
}
free(res);
return 0;
}

View File

@@ -0,0 +1,49 @@
/**
* File: preorder_traversal_i_compact.c
* Created Time: 2023-05-10
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
// 假設結果長度不超過 100
#define MAX_SIZE 100
TreeNode *res[MAX_SIZE];
int resSize = 0;
/* 前序走訪:例題一 */
void preOrder(TreeNode *root) {
if (root == NULL) {
return;
}
if (root->val == 7) {
// 記錄解
res[resSize++] = root;
}
preOrder(root->left);
preOrder(root->right);
}
/* Driver Code */
int main() {
int arr[] = {1, 7, 3, 4, 5, 6, 7};
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
printf("\n初始化二元樹\n");
printTree(root);
// 前序走訪
preOrder(root);
printf("\n輸出所有值為 7 的節點\n");
int *vals = malloc(resSize * sizeof(int));
for (int i = 0; i < resSize; i++) {
vals[i] = res[i]->val;
}
printArray(vals, resSize);
// 釋放記憶體
freeMemoryTree(root);
free(vals);
return 0;
}

View File

@@ -0,0 +1,61 @@
/**
* File: preorder_traversal_ii_compact.c
* Created Time: 2023-05-28
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
// 假設路徑和結果長度不超過 100
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
TreeNode *path[MAX_SIZE];
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
int pathSize = 0, resSize = 0;
/* 前序走訪:例題二 */
void preOrder(TreeNode *root) {
if (root == NULL) {
return;
}
// 嘗試
path[pathSize++] = root;
if (root->val == 7) {
// 記錄解
for (int i = 0; i < pathSize; ++i) {
res[resSize][i] = path[i];
}
resSize++;
}
preOrder(root->left);
preOrder(root->right);
// 回退
pathSize--;
}
/* Driver Code */
int main() {
int arr[] = {1, 7, 3, 4, 5, 6, 7};
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
printf("\n初始化二元樹\n");
printTree(root);
// 前序走訪
preOrder(root);
printf("\n輸出所有根節點到節點 7 的路徑\n");
for (int i = 0; i < resSize; ++i) {
int *vals = malloc(MAX_SIZE * sizeof(int));
int size = 0;
for (int j = 0; res[i][j] != NULL; ++j) {
vals[size++] = res[i][j]->val;
}
printArray(vals, size);
free(vals);
}
// 釋放記憶體
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,62 @@
/**
* File: preorder_traversal_iii_compact.c
* Created Time: 2023-06-04
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
// 假設路徑和結果長度不超過 100
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
TreeNode *path[MAX_SIZE];
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
int pathSize = 0, resSize = 0;
/* 前序走訪:例題三 */
void preOrder(TreeNode *root) {
// 剪枝
if (root == NULL || root->val == 3) {
return;
}
// 嘗試
path[pathSize++] = root;
if (root->val == 7) {
// 記錄解
for (int i = 0; i < pathSize; i++) {
res[resSize][i] = path[i];
}
resSize++;
}
preOrder(root->left);
preOrder(root->right);
// 回退
pathSize--;
}
/* Driver Code */
int main() {
int arr[] = {1, 7, 3, 4, 5, 6, 7};
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
printf("\n初始化二元樹\n");
printTree(root);
// 前序走訪
preOrder(root);
printf("\n輸出所有根節點到節點 7 的路徑,要求路徑中不包含值為 3 的節點\n");
for (int i = 0; i < resSize; ++i) {
int *vals = malloc(MAX_SIZE * sizeof(int));
int size = 0;
for (int j = 0; res[i][j] != NULL; ++j) {
vals[size++] = res[i][j]->val;
}
printArray(vals, size);
free(vals);
}
// 釋放記憶體
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,93 @@
/**
* File: preorder_traversal_iii_template.c
* Created Time: 2023-06-04
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
// 假設路徑和結果長度不超過 100
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
TreeNode *path[MAX_SIZE];
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
int pathSize = 0, resSize = 0;
/* 判斷當前狀態是否為解 */
bool isSolution(void) {
return pathSize > 0 && path[pathSize - 1]->val == 7;
}
/* 記錄解 */
void recordSolution(void) {
for (int i = 0; i < pathSize; i++) {
res[resSize][i] = path[i];
}
resSize++;
}
/* 判斷在當前狀態下,該選擇是否合法 */
bool isValid(TreeNode *choice) {
return choice != NULL && choice->val != 3;
}
/* 更新狀態 */
void makeChoice(TreeNode *choice) {
path[pathSize++] = choice;
}
/* 恢復狀態 */
void undoChoice(void) {
pathSize--;
}
/* 回溯演算法:例題三 */
void backtrack(TreeNode *choices[2]) {
// 檢查是否為解
if (isSolution()) {
// 記錄解
recordSolution();
}
// 走訪所有選擇
for (int i = 0; i < 2; i++) {
TreeNode *choice = choices[i];
// 剪枝:檢查選擇是否合法
if (isValid(choice)) {
// 嘗試:做出選擇,更新狀態
makeChoice(choice);
// 進行下一輪選擇
TreeNode *nextChoices[2] = {choice->left, choice->right};
backtrack(nextChoices);
// 回退:撤銷選擇,恢復到之前的狀態
undoChoice();
}
}
}
/* Driver Code */
int main() {
int arr[] = {1, 7, 3, 4, 5, 6, 7};
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
printf("\n初始化二元樹\n");
printTree(root);
// 回溯演算法
TreeNode *choices[2] = {root, NULL};
backtrack(choices);
printf("\n輸出所有根節點到節點 7 的路徑,要求路徑中不包含值為 3 的節點\n");
for (int i = 0; i < resSize; ++i) {
int *vals = malloc(MAX_SIZE * sizeof(int));
int size = 0;
for (int j = 0; res[i][j] != NULL; ++j) {
vals[size++] = res[i][j]->val;
}
printArray(vals, size);
free(vals);
}
// 釋放記憶體
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,78 @@
/**
* File: subset_sum_i.c
* Created Time: 2023-07-29
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
// 狀態(子集)
int state[MAX_SIZE];
int stateSize = 0;
// 結果串列(子集串列)
int res[MAX_RES_SIZE][MAX_SIZE];
int resColSizes[MAX_RES_SIZE];
int resSize = 0;
/* 回溯演算法:子集和 I */
void backtrack(int target, int *choices, int choicesSize, int start) {
// 子集和等於 target 時,記錄解
if (target == 0) {
for (int i = 0; i < stateSize; ++i) {
res[resSize][i] = state[i];
}
resColSizes[resSize++] = stateSize;
return;
}
// 走訪所有選擇
// 剪枝二:從 start 開始走訪,避免生成重複子集
for (int i = start; i < choicesSize; i++) {
// 剪枝一:若子集和超過 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 的子集 res = \n", target);
for (int i = 0; i < resSize; ++i) {
printArray(res[i], resColSizes[i]);
}
return 0;
}

View File

@@ -0,0 +1,69 @@
/**
* File: subset_sum_i_naive.c
* Created Time: 2023-07-28
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
// 狀態(子集)
int state[MAX_SIZE];
int stateSize = 0;
// 結果串列(子集串列)
int res[MAX_RES_SIZE][MAX_SIZE];
int resColSizes[MAX_RES_SIZE];
int resSize = 0;
/* 回溯演算法:子集和 I */
void backtrack(int target, int total, int *choices, int choicesSize) {
// 子集和等於 target 時,記錄解
if (total == target) {
for (int i = 0; i < stateSize; i++) {
res[resSize][i] = state[i];
}
resColSizes[resSize++] = stateSize;
return;
}
// 走訪所有選擇
for (int i = 0; i < choicesSize; i++) {
// 剪枝:若子集和超過 target ,則跳過該選擇
if (total + choices[i] > target) {
continue;
}
// 嘗試:做出選擇,更新元素和 total
state[stateSize++] = choices[i];
// 進行下一輪選擇
backtrack(target, total + choices[i], choices, choicesSize);
// 回退:撤銷選擇,恢復到之前的狀態
stateSize--;
}
}
/* 求解子集和 I包含重複子集 */
void subsetSumINaive(int *nums, int numsSize, int target) {
resSize = 0; // 初始化解的數量為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 的子集 res = \n", target);
for (int i = 0; i < resSize; i++) {
printArray(res[i], resColSizes[i]);
}
return 0;
}

View File

@@ -0,0 +1,83 @@
/**
* File: subset_sum_ii.c
* Created Time: 2023-07-29
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
// 狀態(子集)
int state[MAX_SIZE];
int stateSize = 0;
// 結果串列(子集串列)
int res[MAX_RES_SIZE][MAX_SIZE];
int resColSizes[MAX_RES_SIZE];
int resSize = 0;
/* 回溯演算法:子集和 II */
void backtrack(int target, int *choices, int choicesSize, int start) {
// 子集和等於 target 時,記錄解
if (target == 0) {
for (int i = 0; i < stateSize; i++) {
res[resSize][i] = state[i];
}
resColSizes[resSize++] = stateSize;
return;
}
// 走訪所有選擇
// 剪枝二:從 start 開始走訪,避免生成重複子集
// 剪枝三:從 start 開始走訪,避免重複選擇同一元素
for (int i = start; i < choicesSize; i++) {
// 剪枝一:若子集和超過 target ,則直接跳過
if (target - choices[i] < 0) {
continue;
}
// 剪枝四:如果該元素與左邊元素相等,說明該搜尋分支重複,直接跳過
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 的子集 res = \n", target);
for (int i = 0; i < resSize; ++i) {
printArray(res[i], resColSizes[i]);
}
return 0;
}

View File

@@ -0,0 +1,5 @@
add_executable(iteration iteration.c)
add_executable(recursion recursion.c)
add_executable(time_complexity time_complexity.c)
add_executable(worst_best_time_complexity worst_best_time_complexity.c)
add_executable(space_complexity space_complexity.c)

View File

@@ -0,0 +1,81 @@
/**
* File: iteration.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com), MwumLi (mwumli@hotmail.com)
*/
#include "../utils/common.h"
/* for 迴圈 */
int forLoop(int n) {
int res = 0;
// 迴圈求和 1, 2, ..., n-1, n
for (int i = 1; i <= n; i++) {
res += i;
}
return res;
}
/* while 迴圈 */
int whileLoop(int n) {
int res = 0;
int i = 1; // 初始化條件變數
// 迴圈求和 1, 2, ..., n-1, n
while (i <= n) {
res += i;
i++; // 更新條件變數
}
return res;
}
/* while 迴圈(兩次更新) */
int whileLoopII(int n) {
int res = 0;
int i = 1; // 初始化條件變數
// 迴圈求和 1, 4, 10, ...
while (i <= n) {
res += i;
// 更新條件變數
i++;
i *= 2;
}
return res;
}
/* 雙層 for 迴圈 */
char *nestedForLoop(int n) {
// n * n 為對應點數量,"(i, j), " 對應字串長最大為 6+10*2加上最後一個空字元 \0 的額外空間
int size = n * n * 26 + 1;
char *res = malloc(size * sizeof(char));
// 迴圈 i = 1, 2, ..., n-1, n
for (int i = 1; i <= n; i++) {
// 迴圈 j = 1, 2, ..., n-1, n
for (int j = 1; j <= n; j++) {
char tmp[26];
snprintf(tmp, sizeof(tmp), "(%d, %d), ", i, j);
strncat(res, tmp, size - strlen(res) - 1);
}
}
return res;
}
/* Driver Code */
int main() {
int n = 5;
int res;
res = forLoop(n);
printf("\nfor 迴圈的求和結果 res = %d\n", res);
res = whileLoop(n);
printf("\nwhile 迴圈的求和結果 res = %d\n", res);
res = whileLoopII(n);
printf("\nwhile 迴圈(兩次更新)求和結果 res = %d\n", res);
char *resStr = nestedForLoop(n);
printf("\n雙層 for 迴圈的走訪結果 %s\r\n", resStr);
free(resStr);
return 0;
}

View File

@@ -0,0 +1,77 @@
/**
* File: recursion.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 遞迴 */
int recur(int n) {
// 終止條件
if (n == 1)
return 1;
// 遞:遞迴呼叫
int res = recur(n - 1);
// 迴:返回結果
return n + res;
}
/* 使用迭代模擬遞迴 */
int forLoopRecur(int n) {
int stack[1000]; // 藉助一個大陣列來模擬堆疊
int top = -1; // 堆疊頂索引
int res = 0;
// 遞:遞迴呼叫
for (int i = n; i > 0; i--) {
// 透過“入堆疊操作”模擬“遞”
stack[1 + top++] = i;
}
// 迴:返回結果
while (top >= 0) {
// 透過“出堆疊操作”模擬“迴”
res += stack[top--];
}
// res = 1+2+3+...+n
return res;
}
/* 尾遞迴 */
int tailRecur(int n, int res) {
// 終止條件
if (n == 0)
return res;
// 尾遞迴呼叫
return tailRecur(n - 1, res + n);
}
/* 費波那契數列:遞迴 */
int fib(int n) {
// 終止條件 f(1) = 0, f(2) = 1
if (n == 1 || n == 2)
return n - 1;
// 遞迴呼叫 f(n) = f(n-1) + f(n-2)
int res = fib(n - 1) + fib(n - 2);
// 返回結果 f(n)
return res;
}
/* Driver Code */
int main() {
int n = 5;
int res;
res = recur(n);
printf("\n遞迴函式的求和結果 res = %d\n", res);
res = forLoopRecur(n);
printf("\n使用迭代模擬遞迴求和結果 res = %d\n", res);
res = tailRecur(n, 0);
printf("\n尾遞迴函式的求和結果 res = %d\n", res);
res = fib(n);
printf("\n費波那契數列的第 %d 項為 %d\n", n, res);
return 0;
}

View File

@@ -0,0 +1,141 @@
/**
* File: space_complexity.c
* Created Time: 2023-04-15
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 函式 */
int func() {
// 執行某些操作
return 0;
}
/* 常數階 */
void constant(int n) {
// 常數、變數、物件佔用 O(1) 空間
const int a = 0;
int b = 0;
int nums[1000];
ListNode *node = newListNode(0);
free(node);
// 迴圈中的變數佔用 O(1) 空間
for (int i = 0; i < n; i++) {
int c = 0;
}
// 迴圈中的函式佔用 O(1) 空間
for (int i = 0; i < n; i++) {
func();
}
}
/* 雜湊表 */
typedef struct {
int key;
int val;
UT_hash_handle hh; // 基於 uthash.h 實現
} HashTable;
/* 線性階 */
void linear(int n) {
// 長度為 n 的陣列佔用 O(n) 空間
int *nums = malloc(sizeof(int) * n);
free(nums);
// 長度為 n 的串列佔用 O(n) 空間
ListNode **nodes = malloc(sizeof(ListNode *) * n);
for (int i = 0; i < n; i++) {
nodes[i] = newListNode(i);
}
// 記憶體釋放
for (int i = 0; i < n; i++) {
free(nodes[i]);
}
free(nodes);
// 長度為 n 的雜湊表佔用 O(n) 空間
HashTable *h = NULL;
for (int i = 0; i < n; i++) {
HashTable *tmp = malloc(sizeof(HashTable));
tmp->key = i;
tmp->val = i;
HASH_ADD_INT(h, key, tmp);
}
// 記憶體釋放
HashTable *curr, *tmp;
HASH_ITER(hh, h, curr, tmp) {
HASH_DEL(h, curr);
free(curr);
}
}
/* 線性階(遞迴實現) */
void linearRecur(int n) {
printf("遞迴 n = %d\r\n", n);
if (n == 1)
return;
linearRecur(n - 1);
}
/* 平方階 */
void quadratic(int n) {
// 二維串列佔用 O(n^2) 空間
int **numMatrix = malloc(sizeof(int *) * n);
for (int i = 0; i < n; i++) {
int *tmp = malloc(sizeof(int) * n);
for (int j = 0; j < n; j++) {
tmp[j] = 0;
}
numMatrix[i] = tmp;
}
// 記憶體釋放
for (int i = 0; i < n; i++) {
free(numMatrix[i]);
}
free(numMatrix);
}
/* 平方階(遞迴實現) */
int quadraticRecur(int n) {
if (n <= 0)
return 0;
int *nums = malloc(sizeof(int) * n);
printf("遞迴 n = %d 中的 nums 長度 = %d\r\n", n, n);
int res = quadraticRecur(n - 1);
free(nums);
return res;
}
/* 指數階(建立滿二元樹) */
TreeNode *buildTree(int n) {
if (n == 0)
return NULL;
TreeNode *root = newTreeNode(0);
root->left = buildTree(n - 1);
root->right = buildTree(n - 1);
return root;
}
/* Driver Code */
int main() {
int n = 5;
// 常數階
constant(n);
// 線性階
linear(n);
linearRecur(n);
// 平方階
quadratic(n);
quadraticRecur(n);
// 指數階
TreeNode *root = buildTree(n);
printTree(root);
// 釋放記憶體
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,179 @@
/**
* File: time_complexity.c
* Created Time: 2023-01-03
* Author: codingonion (coderonion@gmail.com)
*/
#include "../utils/common.h"
/* 常數階 */
int constant(int n) {
int count = 0;
int size = 100000;
int i = 0;
for (int i = 0; i < size; i++) {
count++;
}
return count;
}
/* 線性階 */
int linear(int n) {
int count = 0;
for (int i = 0; i < n; i++) {
count++;
}
return count;
}
/* 線性階(走訪陣列) */
int arrayTraversal(int *nums, int n) {
int count = 0;
// 迴圈次數與陣列長度成正比
for (int i = 0; i < n; i++) {
count++;
}
return count;
}
/* 平方階 */
int quadratic(int n) {
int count = 0;
// 迴圈次數與資料大小 n 成平方關係
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
count++;
}
}
return count;
}
/* 平方階(泡沫排序) */
int bubbleSort(int *nums, int n) {
int count = 0; // 計數器
// 外迴圈:未排序區間為 [0, i]
for (int i = n - 1; i > 0; i--) {
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交換 nums[j] 與 nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
count += 3; // 元素交換包含 3 個單元操作
}
}
}
return count;
}
/* 指數階(迴圈實現) */
int exponential(int n) {
int count = 0;
int bas = 1;
// 細胞每輪一分為二,形成數列 1, 2, 4, 8, ..., 2^(n-1)
for (int i = 0; i < n; i++) {
for (int j = 0; j < bas; j++) {
count++;
}
bas *= 2;
}
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
return count;
}
/* 指數階(遞迴實現) */
int expRecur(int n) {
if (n == 1)
return 1;
return expRecur(n - 1) + expRecur(n - 1) + 1;
}
/* 對數階(迴圈實現) */
int logarithmic(int n) {
int count = 0;
while (n > 1) {
n = n / 2;
count++;
}
return count;
}
/* 對數階(遞迴實現) */
int logRecur(int n) {
if (n <= 1)
return 0;
return logRecur(n / 2) + 1;
}
/* 線性對數階 */
int linearLogRecur(int n) {
if (n <= 1)
return 1;
int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);
for (int i = 0; i < n; i++) {
count++;
}
return count;
}
/* 階乘階(遞迴實現) */
int factorialRecur(int n) {
if (n == 0)
return 1;
int count = 0;
for (int i = 0; i < n; i++) {
count += factorialRecur(n - 1);
}
return count;
}
/* Driver Code */
int main(int argc, char *argv[]) {
// 可以修改 n 執行,體會一下各種複雜度的操作數量變化趨勢
int n = 8;
printf("輸入資料大小 n = %d\n", n);
int count = constant(n);
printf("常數階的操作數量 = %d\n", count);
count = linear(n);
printf("線性階的操作數量 = %d\n", count);
// 分配堆積區記憶體(建立一維可變長陣列:陣列中元素數量為 n ,元素型別為 int
int *nums = (int *)malloc(n * sizeof(int));
count = arrayTraversal(nums, n);
printf("線性階(走訪陣列)的操作數量 = %d\n", count);
count = quadratic(n);
printf("平方階的操作數量 = %d\n", count);
for (int i = 0; i < n; i++) {
nums[i] = n - i; // [n,n-1,...,2,1]
}
count = bubbleSort(nums, n);
printf("平方階(泡沫排序)的操作數量 = %d\n", count);
count = exponential(n);
printf("指數階(迴圈實現)的操作數量 = %d\n", count);
count = expRecur(n);
printf("指數階(遞迴實現)的操作數量 = %d\n", count);
count = logarithmic(n);
printf("對數階(迴圈實現)的操作數量 = %d\n", count);
count = logRecur(n);
printf("對數階(遞迴實現)的操作數量 = %d\n", count);
count = linearLogRecur(n);
printf("線性對數階(遞迴實現)的操作數量 = %d\n", count);
count = factorialRecur(n);
printf("階乘階(遞迴實現)的操作數量 = %d\n", count);
// 釋放堆積區記憶體
if (nums != NULL) {
free(nums);
nums = NULL;
}
getchar();
return 0;
}

View File

@@ -0,0 +1,57 @@
/**
* File: worst_best_time_complexity.c
* Created Time: 2023-01-03
* Author: codingonion (coderonion@gmail.com)
*/
#include "../utils/common.h"
/* 生成一個陣列,元素為 { 1, 2, ..., n },順序被打亂 */
int *randomNumbers(int n) {
// 分配堆積區記憶體(建立一維可變長陣列:陣列中元素數量為 n ,元素型別為 int
int *nums = (int *)malloc(n * sizeof(int));
// 生成陣列 nums = { 1, 2, 3, ..., n }
for (int i = 0; i < n; i++) {
nums[i] = i + 1;
}
// 隨機打亂陣列元素
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
return nums;
}
/* 查詢陣列 nums 中數字 1 所在索引 */
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[]) {
// 初始化隨機數種子
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 ] 被打亂後 = ");
printArray(nums, n);
printf("數字 1 的索引為 %d\n", index);
// 釋放堆積區記憶體
if (nums != NULL) {
free(nums);
nums = NULL;
}
}
return 0;
}

View File

@@ -0,0 +1,3 @@
add_executable(binary_search_recur binary_search_recur.c)
add_executable(build_tree build_tree.c)
add_executable(hanota hanota.c)

View File

@@ -0,0 +1,47 @@
/**
* File: binary_search_recur.c
* Created Time: 2023-10-01
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 二分搜尋:問題 f(i, j) */
int dfs(int nums[], int target, int i, int j) {
// 若區間為空,代表無目標元素,則返回 -1
if (i > j) {
return -1;
}
// 計算中點索引 m
int m = (i + j) / 2;
if (nums[m] < target) {
// 遞迴子問題 f(m+1, j)
return dfs(nums, target, m + 1, j);
} else if (nums[m] > target) {
// 遞迴子問題 f(i, m-1)
return dfs(nums, target, i, m - 1);
} else {
// 找到目標元素,返回其索引
return m;
}
}
/* 二分搜尋 */
int binarySearch(int nums[], int target, int numsSize) {
int n = numsSize;
// 求解問題 f(0, n-1)
return dfs(nums, target, 0, n - 1);
}
/* Driver Code */
int main() {
int target = 6;
int nums[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
int numsSize = sizeof(nums) / sizeof(nums[0]);
// 二分搜尋(雙閉區間)
int index = binarySearch(nums, target, numsSize);
printf("目標元素 6 的索引 = %d\n", index);
return 0;
}

View File

@@ -0,0 +1,61 @@
/**
* File : build_tree.c
* Created Time: 2023-10-16
* Author : lucas (superrat6@gmail.com)
*/
#include "../utils/common.h"
// 假設所有元素都小於 1000
#define MAX_SIZE 1000
/* 構建二元樹:分治 */
TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) {
// 子樹區間為空時終止
if (r - l < 0)
return NULL;
// 初始化根節點
TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode));
root->val = preorder[i];
root->left = NULL;
root->right = NULL;
// 查詢 m ,從而劃分左右子樹
int m = inorderMap[preorder[i]];
// 子問題:構建左子樹
root->left = dfs(preorder, inorderMap, i + 1, l, m - 1, size);
// 子問題:構建右子樹
root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r, size);
// 返回根節點
return root;
}
/* 構建二元樹 */
TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) {
// 初始化雜湊表,儲存 inorder 元素到索引的對映
int *inorderMap = (int *)malloc(sizeof(int) * MAX_SIZE);
for (int i = 0; i < inorderSize; i++) {
inorderMap[inorder[i]] = i;
}
TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorderSize - 1, inorderSize);
free(inorderMap);
return root;
}
/* Driver Code */
int main() {
int preorder[] = {3, 9, 2, 1, 7};
int inorder[] = {9, 3, 1, 2, 7};
int preorderSize = sizeof(preorder) / sizeof(preorder[0]);
int inorderSize = sizeof(inorder) / sizeof(inorder[0]);
printf("前序走訪 = ");
printArray(preorder, preorderSize);
printf("中序走訪 = ");
printArray(inorder, inorderSize);
TreeNode *root = buildTree(preorder, preorderSize, inorder, inorderSize);
printf("構建的二元樹為:\n");
printTree(root);
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,74 @@
/**
* File: hanota.c
* Created Time: 2023-10-01
* Author: Zuoxun (845242523@qq.com), lucas(superrat6@gmail.com)
*/
#include "../utils/common.h"
// 假設最多有 1000 個排列
#define MAX_SIZE 1000
/* 移動一個圓盤 */
void move(int *src, int *srcSize, int *tar, int *tarSize) {
// 從 src 頂部拿出一個圓盤
int pan = src[*srcSize - 1];
src[*srcSize - 1] = 0;
(*srcSize)--;
// 將圓盤放入 tar 頂部
tar[*tarSize] = pan;
(*tarSize)++;
}
/* 求解河內塔問題 f(i) */
void dfs(int i, int *src, int *srcSize, int *buf, int *bufSize, int *tar, int *tarSize) {
// 若 src 只剩下一個圓盤,則直接將其移到 tar
if (i == 1) {
move(src, srcSize, tar, tarSize);
return;
}
// 子問題 f(i-1) :將 src 頂部 i-1 個圓盤藉助 tar 移到 buf
dfs(i - 1, src, srcSize, tar, tarSize, buf, bufSize);
// 子問題 f(1) :將 src 剩餘一個圓盤移到 tar
move(src, srcSize, tar, tarSize);
// 子問題 f(i-1) :將 buf 頂部 i-1 個圓盤藉助 src 移到 tar
dfs(i - 1, buf, bufSize, src, srcSize, tar, tarSize);
}
/* 求解河內塔問題 */
void solveHanota(int *A, int *ASize, int *B, int *BSize, int *C, int *CSize) {
// 將 A 頂部 n 個圓盤藉助 B 移到 C
dfs(*ASize, A, ASize, B, BSize, C, CSize);
}
/* Driver Code */
int main() {
// 串列尾部是柱子頂部
int a[] = {5, 4, 3, 2, 1};
int b[MAX_SIZE] = {0};
int c[MAX_SIZE] = {0};
int ASize = sizeof(a) / sizeof(a[0]);
int BSize = 0;
int CSize = 0;
printf("\n初始狀態下:");
printf("\nA = ");
printArray(a, ASize);
printf("B = ");
printArray(b, BSize);
printf("C = ");
printArray(c, CSize);
solveHanota(a, &ASize, b, &BSize, c, &CSize);
printf("\n圓盤移動完成後:");
printf("A = ");
printArray(a, ASize);
printf("B = ");
printArray(b, BSize);
printf("C = ");
printArray(c, CSize);
return 0;
}

View File

@@ -0,0 +1,8 @@
add_executable(climbing_stairs_constraint_dp climbing_stairs_constraint_dp.c)
add_executable(min_cost_climbing_stairs_dp min_cost_climbing_stairs_dp.c)
add_executable(min_path_sum min_path_sum.c)
add_executable(knapsack knapsack.c)
add_executable(unbounded_knapsack unbounded_knapsack.c)
add_executable(coin_change coin_change.c)
add_executable(coin_change_ii coin_change_ii.c)
add_executable(edit_distance edit_distance.c)

View File

@@ -0,0 +1,47 @@
/**
* File: climbing_stairs_backtrack.c
* Created Time: 2023-09-22
* Author: huawuque404 (huawuque404@163.com)
*/
#include "../utils/common.h"
/* 回溯 */
void backtrack(int *choices, int state, int n, int *res, int len) {
// 當爬到第 n 階時,方案數量加 1
if (state == n)
res[0]++;
// 走訪所有選擇
for (int i = 0; i < len; i++) {
int choice = choices[i];
// 剪枝:不允許越過第 n 階
if (state + choice > n)
continue;
// 嘗試:做出選擇,更新狀態
backtrack(choices, state + choice, n, res, len);
// 回退
}
}
/* 爬樓梯:回溯 */
int climbingStairsBacktrack(int n) {
int choices[2] = {1, 2}; // 可選擇向上爬 1 階或 2 階
int state = 0; // 從第 0 階開始爬
int *res = (int *)malloc(sizeof(int));
*res = 0; // 使用 res[0] 記錄方案數量
int len = sizeof(choices) / sizeof(int);
backtrack(choices, state, n, res, len);
int result = *res;
free(res);
return result;
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsBacktrack(n);
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
return 0;
}

View File

@@ -0,0 +1,46 @@
/**
* File: climbing_stairs_constraint_dp.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 帶約束爬樓梯:動態規劃 */
int climbingStairsConstraintDP(int n) {
if (n == 1 || n == 2) {
return 1;
}
// 初始化 dp 表,用於儲存子問題的解
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(3, sizeof(int));
}
// 初始狀態:預設最小子問題的解
dp[1][1] = 1;
dp[1][2] = 0;
dp[2][1] = 0;
dp[2][2] = 1;
// 狀態轉移:從較小子問題逐步求解較大子問題
for (int i = 3; i <= n; i++) {
dp[i][1] = dp[i - 1][2];
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
}
int res = dp[n][1] + dp[n][2];
// 釋放記憶體
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
free(dp);
return res;
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsConstraintDP(n);
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
return 0;
}

View File

@@ -0,0 +1,32 @@
/**
* File: climbing_stairs_dfs.c
* Created Time: 2023-09-19
* Author: huawuque404 (huawuque404@163.com)
*/
#include "../utils/common.h"
/* 搜尋 */
int dfs(int i) {
// 已知 dp[1] 和 dp[2] ,返回之
if (i == 1 || i == 2)
return i;
// dp[i] = dp[i-1] + dp[i-2]
int count = dfs(i - 1) + dfs(i - 2);
return count;
}
/* 爬樓梯:搜尋 */
int climbingStairsDFS(int n) {
return dfs(n);
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsDFS(n);
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
return 0;
}

View File

@@ -0,0 +1,44 @@
/**
* File: climbing_stairs_dfs_mem.c
* Created Time: 2023-09-19
* Author: huawuque404 (huawuque404@163.com)
*/
#include "../utils/common.h"
/* 記憶化搜尋 */
int dfs(int i, int *mem) {
// 已知 dp[1] 和 dp[2] ,返回之
if (i == 1 || i == 2)
return i;
// 若存在記錄 dp[i] ,則直接返回之
if (mem[i] != -1)
return mem[i];
// dp[i] = dp[i-1] + dp[i-2]
int count = dfs(i - 1, mem) + dfs(i - 2, mem);
// 記錄 dp[i]
mem[i] = count;
return count;
}
/* 爬樓梯:記憶化搜尋 */
int climbingStairsDFSMem(int n) {
// mem[i] 記錄爬到第 i 階的方案總數,-1 代表無記錄
int *mem = (int *)malloc((n + 1) * sizeof(int));
for (int i = 0; i <= n; i++) {
mem[i] = -1;
}
int result = dfs(n, mem);
free(mem);
return result;
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsDFSMem(n);
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
return 0;
}

View File

@@ -0,0 +1,51 @@
/**
* File: climbing_stairs_dp.c
* Created Time: 2023-09-19
* Author: huawuque404 (huawuque404@163.com)
*/
#include "../utils/common.h"
/* 爬樓梯:動態規劃 */
int climbingStairsDP(int n) {
if (n == 1 || n == 2)
return n;
// 初始化 dp 表,用於儲存子問題的解
int *dp = (int *)malloc((n + 1) * sizeof(int));
// 初始狀態:預設最小子問題的解
dp[1] = 1;
dp[2] = 2;
// 狀態轉移:從較小子問題逐步求解較大子問題
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
int result = dp[n];
free(dp);
return result;
}
/* 爬樓梯:空間最佳化後的動態規劃 */
int climbingStairsDPComp(int n) {
if (n == 1 || n == 2)
return n;
int a = 1, b = 2;
for (int i = 3; i <= n; i++) {
int tmp = b;
b = a + b;
a = tmp;
}
return b;
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsDP(n);
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
res = climbingStairsDPComp(n);
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
return 0;
}

View File

@@ -0,0 +1,88 @@
/**
* 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 = calloc(amt + 1, sizeof(int));
dp[0] = 0;
// 狀態轉移
for (int i = 1; i <= n; i++) {
for (int a = 1; a <= amt; a++) {
if (coins[i - 1] > a) {
// 若超過目標金額,則不選硬幣 i
dp[a] = dp[a];
} else {
// 不選和選硬幣 i 這兩種方案的較小值
dp[a] = myMin(dp[a], dp[a - coins[i - 1]] + 1);
}
}
}
int res = dp[amt] != MAX ? dp[amt] : -1;
// 釋放記憶體
free(dp);
return res;
}
/* Driver code */
int main() {
int coins[] = {1, 2, 5};
int coinsSize = sizeof(coins) / sizeof(coins[0]);
int amt = 4;
// 動態規劃
int res = coinChangeDP(coins, amt, coinsSize);
printf("湊到目標金額所需的最少硬幣數量為 %d\n", res);
// 空間最佳化後的動態規劃
res = coinChangeDPComp(coins, amt, coinsSize);
printf("湊到目標金額所需的最少硬幣數量為 %d\n", res);
return 0;
}

View File

@@ -0,0 +1,81 @@
/**
* File: coin_change_ii.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 零錢兌換 II動態規劃 */
int coinChangeIIDP(int coins[], int amt, int coinsSize) {
int n = coinsSize;
// 初始化 dp 表
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(amt + 1, sizeof(int));
}
// 初始化首列
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
// 狀態轉移
for (int i = 1; i <= n; i++) {
for (int a = 1; a <= amt; a++) {
if (coins[i - 1] > a) {
// 若超過目標金額,則不選硬幣 i
dp[i][a] = dp[i - 1][a];
} else {
// 不選和選硬幣 i 這兩種方案之和
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]];
}
}
}
int res = dp[n][amt];
// 釋放記憶體
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
free(dp);
return res;
}
/* 零錢兌換 II空間最佳化後的動態規劃 */
int coinChangeIIDPComp(int coins[], int amt, int coinsSize) {
int n = coinsSize;
// 初始化 dp 表
int *dp = calloc(amt + 1, sizeof(int));
dp[0] = 1;
// 狀態轉移
for (int i = 1; i <= n; i++) {
for (int a = 1; a <= amt; a++) {
if (coins[i - 1] > a) {
// 若超過目標金額,則不選硬幣 i
dp[a] = dp[a];
} else {
// 不選和選硬幣 i 這兩種方案之和
dp[a] = dp[a] + dp[a - coins[i - 1]];
}
}
}
int res = dp[amt];
// 釋放記憶體
free(dp);
return res;
}
/* Driver code */
int main() {
int coins[] = {1, 2, 5};
int coinsSize = sizeof(coins) / sizeof(coins[0]);
int amt = 5;
// 動態規劃
int res = coinChangeIIDP(coins, amt, coinsSize);
printf("湊出目標金額的硬幣組合數量為 %d\n", res);
// 空間最佳化後的動態規劃
res = coinChangeIIDPComp(coins, amt, coinsSize);
printf("湊出目標金額的硬幣組合數量為 %d\n", res);
return 0;
}

View File

@@ -0,0 +1,159 @@
/**
* File: edit_distance.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 求最小值 */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* 編輯距離:暴力搜尋 */
int editDistanceDFS(char *s, char *t, int i, int j) {
// 若 s 和 t 都為空,則返回 0
if (i == 0 && j == 0)
return 0;
// 若 s 為空,則返回 t 長度
if (i == 0)
return j;
// 若 t 為空,則返回 s 長度
if (j == 0)
return i;
// 若兩字元相等,則直接跳過此兩字元
if (s[i - 1] == t[j - 1])
return editDistanceDFS(s, t, i - 1, j - 1);
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
int insert = editDistanceDFS(s, t, i, j - 1);
int del = editDistanceDFS(s, t, i - 1, j);
int replace = editDistanceDFS(s, t, i - 1, j - 1);
// 返回最少編輯步數
return myMin(myMin(insert, del), replace) + 1;
}
/* 編輯距離:記憶化搜尋 */
int editDistanceDFSMem(char *s, char *t, int memCols, int **mem, int i, int j) {
// 若 s 和 t 都為空,則返回 0
if (i == 0 && j == 0)
return 0;
// 若 s 為空,則返回 t 長度
if (i == 0)
return j;
// 若 t 為空,則返回 s 長度
if (j == 0)
return i;
// 若已有記錄,則直接返回之
if (mem[i][j] != -1)
return mem[i][j];
// 若兩字元相等,則直接跳過此兩字元
if (s[i - 1] == t[j - 1])
return editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1);
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
int insert = editDistanceDFSMem(s, t, memCols, mem, i, j - 1);
int del = editDistanceDFSMem(s, t, memCols, mem, i - 1, j);
int replace = editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1);
// 記錄並返回最少編輯步數
mem[i][j] = myMin(myMin(insert, del), replace) + 1;
return mem[i][j];
}
/* 編輯距離:動態規劃 */
int editDistanceDP(char *s, char *t, int n, int m) {
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(m + 1, sizeof(int));
}
// 狀態轉移:首行首列
for (int i = 1; i <= n; i++) {
dp[i][0] = i;
}
for (int j = 1; j <= m; j++) {
dp[0][j] = j;
}
// 狀態轉移:其餘行和列
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s[i - 1] == t[j - 1]) {
// 若兩字元相等,則直接跳過此兩字元
dp[i][j] = dp[i - 1][j - 1];
} else {
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
dp[i][j] = myMin(myMin(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
}
}
}
int res = dp[n][m];
// 釋放記憶體
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
return res;
}
/* 編輯距離:空間最佳化後的動態規劃 */
int editDistanceDPComp(char *s, char *t, int n, int m) {
int *dp = calloc(m + 1, sizeof(int));
// 狀態轉移:首行
for (int j = 1; j <= m; j++) {
dp[j] = j;
}
// 狀態轉移:其餘行
for (int i = 1; i <= n; i++) {
// 狀態轉移:首列
int leftup = dp[0]; // 暫存 dp[i-1, j-1]
dp[0] = i;
// 狀態轉移:其餘列
for (int j = 1; j <= m; j++) {
int temp = dp[j];
if (s[i - 1] == t[j - 1]) {
// 若兩字元相等,則直接跳過此兩字元
dp[j] = leftup;
} else {
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
dp[j] = myMin(myMin(dp[j - 1], dp[j]), leftup) + 1;
}
leftup = temp; // 更新為下一輪的 dp[i-1, j-1]
}
}
int res = dp[m];
// 釋放記憶體
free(dp);
return res;
}
/* Driver Code */
int main() {
char *s = "bag";
char *t = "pack";
int n = strlen(s), m = strlen(t);
// 暴力搜尋
int res = editDistanceDFS(s, t, n, m);
printf("將 %s 更改為 %s 最少需要編輯 %d 步\n", s, t, res);
// 記憶化搜尋
int **mem = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
mem[i] = malloc((m + 1) * sizeof(int));
memset(mem[i], -1, (m + 1) * sizeof(int));
}
res = editDistanceDFSMem(s, t, m + 1, mem, n, m);
printf("將 %s 更改為 %s 最少需要編輯 %d 步\n", s, t, res);
// 釋放記憶體
for (int i = 0; i <= n; i++) {
free(mem[i]);
}
free(mem);
// 動態規劃
res = editDistanceDP(s, t, n, m);
printf("將 %s 更改為 %s 最少需要編輯 %d 步\n", s, t, res);
// 空間最佳化後的動態規劃
res = editDistanceDPComp(s, t, n, m);
printf("將 %s 更改為 %s 最少需要編輯 %d 步\n", s, t, res);
return 0;
}

View File

@@ -0,0 +1,137 @@
/**
* File: knapsack.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 求最大值 */
int myMax(int a, int b) {
return a > b ? a : b;
}
/* 0-1 背包:暴力搜尋 */
int knapsackDFS(int wgt[], int val[], int i, int c) {
// 若已選完所有物品或背包無剩餘容量,則返回價值 0
if (i == 0 || c == 0) {
return 0;
}
// 若超過背包容量,則只能選擇不放入背包
if (wgt[i - 1] > c) {
return knapsackDFS(wgt, val, i - 1, c);
}
// 計算不放入和放入物品 i 的最大價值
int no = knapsackDFS(wgt, val, i - 1, c);
int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1];
// 返回兩種方案中價值更大的那一個
return myMax(no, yes);
}
/* 0-1 背包:記憶化搜尋 */
int knapsackDFSMem(int wgt[], int val[], int memCols, int **mem, int i, int c) {
// 若已選完所有物品或背包無剩餘容量,則返回價值 0
if (i == 0 || c == 0) {
return 0;
}
// 若已有記錄,則直接返回
if (mem[i][c] != -1) {
return mem[i][c];
}
// 若超過背包容量,則只能選擇不放入背包
if (wgt[i - 1] > c) {
return knapsackDFSMem(wgt, val, memCols, mem, i - 1, c);
}
// 計算不放入和放入物品 i 的最大價值
int no = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c);
int yes = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c - wgt[i - 1]) + val[i - 1];
// 記錄並返回兩種方案中價值更大的那一個
mem[i][c] = myMax(no, yes);
return mem[i][c];
}
/* 0-1 背包:動態規劃 */
int knapsackDP(int wgt[], int val[], int cap, int wgtSize) {
int n = wgtSize;
// 初始化 dp 表
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(cap + 1, sizeof(int));
}
// 狀態轉移
for (int i = 1; i <= n; i++) {
for (int c = 1; c <= cap; c++) {
if (wgt[i - 1] > c) {
// 若超過背包容量,則不選物品 i
dp[i][c] = dp[i - 1][c];
} else {
// 不選和選物品 i 這兩種方案的較大值
dp[i][c] = myMax(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]);
}
}
}
int res = dp[n][cap];
// 釋放記憶體
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
return res;
}
/* 0-1 背包:空間最佳化後的動態規劃 */
int knapsackDPComp(int wgt[], int val[], int cap, int wgtSize) {
int n = wgtSize;
// 初始化 dp 表
int *dp = calloc(cap + 1, sizeof(int));
// 狀態轉移
for (int i = 1; i <= n; i++) {
// 倒序走訪
for (int c = cap; c >= 1; c--) {
if (wgt[i - 1] <= c) {
// 不選和選物品 i 這兩種方案的較大值
dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
}
}
}
int res = dp[cap];
// 釋放記憶體
free(dp);
return res;
}
/* Driver Code */
int main() {
int wgt[] = {10, 20, 30, 40, 50};
int val[] = {50, 120, 150, 210, 240};
int cap = 50;
int n = sizeof(wgt) / sizeof(wgt[0]);
int wgtSize = n;
// 暴力搜尋
int res = knapsackDFS(wgt, val, n, cap);
printf("不超過背包容量的最大物品價值為 %d\n", res);
// 記憶化搜尋
int **mem = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
mem[i] = malloc((cap + 1) * sizeof(int));
memset(mem[i], -1, (cap + 1) * sizeof(int));
}
res = knapsackDFSMem(wgt, val, cap + 1, mem, n, cap);
printf("不超過背包容量的最大物品價值為 %d\n", res);
// 釋放記憶體
for (int i = 0; i <= n; i++) {
free(mem[i]);
}
free(mem);
// 動態規劃
res = knapsackDP(wgt, val, cap, wgtSize);
printf("不超過背包容量的最大物品價值為 %d\n", res);
// 空間最佳化後的動態規劃
res = knapsackDPComp(wgt, val, cap, wgtSize);
printf("不超過背包容量的最大物品價值為 %d\n", res);
return 0;
}

View File

@@ -0,0 +1,62 @@
/**
* File: min_cost_climbing_stairs_dp.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 求最小值 */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* 爬樓梯最小代價:動態規劃 */
int minCostClimbingStairsDP(int cost[], int costSize) {
int n = costSize - 1;
if (n == 1 || n == 2)
return cost[n];
// 初始化 dp 表,用於儲存子問題的解
int *dp = calloc(n + 1, sizeof(int));
// 初始狀態:預設最小子問題的解
dp[1] = cost[1];
dp[2] = cost[2];
// 狀態轉移:從較小子問題逐步求解較大子問題
for (int i = 3; i <= n; i++) {
dp[i] = myMin(dp[i - 1], dp[i - 2]) + cost[i];
}
int res = dp[n];
// 釋放記憶體
free(dp);
return res;
}
/* 爬樓梯最小代價:空間最佳化後的動態規劃 */
int minCostClimbingStairsDPComp(int cost[], int costSize) {
int n = costSize - 1;
if (n == 1 || n == 2)
return cost[n];
int a = cost[1], b = cost[2];
for (int i = 3; i <= n; i++) {
int tmp = b;
b = myMin(a, tmp) + cost[i];
a = tmp;
}
return b;
}
/* Driver Code */
int main() {
int cost[] = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1};
int costSize = sizeof(cost) / sizeof(cost[0]);
printf("輸入樓梯的代價串列為:");
printArray(cost, costSize);
int res = minCostClimbingStairsDP(cost, costSize);
printf("爬完樓梯的最低代價為 %d\n", res);
res = minCostClimbingStairsDPComp(cost, costSize);
printf("爬完樓梯的最低代價為 %d\n", res);
return 0;
}

View File

@@ -0,0 +1,134 @@
/**
* File: min_path_sum.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
// 假設矩陣最大行列數為 100
#define MAX_SIZE 100
/* 求最小值 */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* 最小路徑和:暴力搜尋 */
int minPathSumDFS(int grid[MAX_SIZE][MAX_SIZE], int i, int j) {
// 若為左上角單元格,則終止搜尋
if (i == 0 && j == 0) {
return grid[0][0];
}
// 若行列索引越界,則返回 +∞ 代價
if (i < 0 || j < 0) {
return INT_MAX;
}
// 計算從左上角到 (i-1, j) 和 (i, j-1) 的最小路徑代價
int up = minPathSumDFS(grid, i - 1, j);
int left = minPathSumDFS(grid, i, j - 1);
// 返回從左上角到 (i, j) 的最小路徑代價
return myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX;
}
/* 最小路徑和:記憶化搜尋 */
int minPathSumDFSMem(int grid[MAX_SIZE][MAX_SIZE], int mem[MAX_SIZE][MAX_SIZE], int i, int j) {
// 若為左上角單元格,則終止搜尋
if (i == 0 && j == 0) {
return grid[0][0];
}
// 若行列索引越界,則返回 +∞ 代價
if (i < 0 || j < 0) {
return INT_MAX;
}
// 若已有記錄,則直接返回
if (mem[i][j] != -1) {
return mem[i][j];
}
// 左邊和上邊單元格的最小路徑代價
int up = minPathSumDFSMem(grid, mem, i - 1, j);
int left = minPathSumDFSMem(grid, mem, i, j - 1);
// 記錄並返回左上角到 (i, j) 的最小路徑代價
mem[i][j] = myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX;
return mem[i][j];
}
/* 最小路徑和:動態規劃 */
int minPathSumDP(int grid[MAX_SIZE][MAX_SIZE], int n, int m) {
// 初始化 dp 表
int **dp = malloc(n * sizeof(int *));
for (int i = 0; i < n; i++) {
dp[i] = calloc(m, sizeof(int));
}
dp[0][0] = grid[0][0];
// 狀態轉移:首行
for (int j = 1; j < m; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
// 狀態轉移:首列
for (int i = 1; i < n; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
// 狀態轉移:其餘行和列
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
dp[i][j] = myMin(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
}
}
int res = dp[n - 1][m - 1];
// 釋放記憶體
for (int i = 0; i < n; i++) {
free(dp[i]);
}
return res;
}
/* 最小路徑和:空間最佳化後的動態規劃 */
int minPathSumDPComp(int grid[MAX_SIZE][MAX_SIZE], int n, int m) {
// 初始化 dp 表
int *dp = calloc(m, sizeof(int));
// 狀態轉移:首行
dp[0] = grid[0][0];
for (int j = 1; j < m; j++) {
dp[j] = dp[j - 1] + grid[0][j];
}
// 狀態轉移:其餘行
for (int i = 1; i < n; i++) {
// 狀態轉移:首列
dp[0] = dp[0] + grid[i][0];
// 狀態轉移:其餘列
for (int j = 1; j < m; j++) {
dp[j] = myMin(dp[j - 1], dp[j]) + grid[i][j];
}
}
int res = dp[m - 1];
// 釋放記憶體
free(dp);
return res;
}
/* Driver Code */
int main() {
int grid[MAX_SIZE][MAX_SIZE] = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}};
int n = 4, m = 4; // 矩陣容量為 MAX_SIZE * MAX_SIZE ,有效行列數為 n * m
// 暴力搜尋
int res = minPathSumDFS(grid, n - 1, m - 1);
printf("從左上角到右下角的最小路徑和為 %d\n", res);
// 記憶化搜尋
int mem[MAX_SIZE][MAX_SIZE];
memset(mem, -1, sizeof(mem));
res = minPathSumDFSMem(grid, mem, n - 1, m - 1);
printf("從左上角到右下角的最小路徑和為 %d\n", res);
// 動態規劃
res = minPathSumDP(grid, n, m);
printf("從左上角到右下角的最小路徑和為 %d\n", res);
// 空間最佳化後的動態規劃
res = minPathSumDPComp(grid, n, m);
printf("從左上角到右下角的最小路徑和為 %d\n", res);
return 0;
}

View File

@@ -0,0 +1,81 @@
/**
* File: unbounded_knapsack.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 求最大值 */
int myMax(int a, int b) {
return a > b ? a : b;
}
/* 完全背包:動態規劃 */
int unboundedKnapsackDP(int wgt[], int val[], int cap, int wgtSize) {
int n = wgtSize;
// 初始化 dp 表
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(cap + 1, sizeof(int));
}
// 狀態轉移
for (int i = 1; i <= n; i++) {
for (int c = 1; c <= cap; c++) {
if (wgt[i - 1] > c) {
// 若超過背包容量,則不選物品 i
dp[i][c] = dp[i - 1][c];
} else {
// 不選和選物品 i 這兩種方案的較大值
dp[i][c] = myMax(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]);
}
}
}
int res = dp[n][cap];
// 釋放記憶體
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
return res;
}
/* 完全背包:空間最佳化後的動態規劃 */
int unboundedKnapsackDPComp(int wgt[], int val[], int cap, int wgtSize) {
int n = wgtSize;
// 初始化 dp 表
int *dp = calloc(cap + 1, sizeof(int));
// 狀態轉移
for (int i = 1; i <= n; i++) {
for (int c = 1; c <= cap; c++) {
if (wgt[i - 1] > c) {
// 若超過背包容量,則不選物品 i
dp[c] = dp[c];
} else {
// 不選和選物品 i 這兩種方案的較大值
dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
}
}
}
int res = dp[cap];
// 釋放記憶體
free(dp);
return res;
}
/* Driver code */
int main() {
int wgt[] = {1, 2, 3};
int val[] = {5, 11, 15};
int wgtSize = sizeof(wgt) / sizeof(wgt[0]);
int cap = 4;
// 動態規劃
int res = unboundedKnapsackDP(wgt, val, cap, wgtSize);
printf("不超過背包容量的最大物品價值為 %d\n", res);
// 空間最佳化後的動態規劃
res = unboundedKnapsackDPComp(wgt, val, cap, wgtSize);
printf("不超過背包容量的最大物品價值為 %d\n", res);
return 0;
}

View File

@@ -0,0 +1,4 @@
add_executable(graph_adjacency_matrix graph_adjacency_matrix.c)
add_executable(graph_adjacency_list_test graph_adjacency_list_test.c)
add_executable(graph_bfs graph_bfs.c)
add_executable(graph_dfs graph_dfs.c)

View File

@@ -0,0 +1,171 @@
/**
* File: graph_adjacency_list.c
* Created Time: 2023-07-07
* Author: NI-SW (947743645@qq.com)
*/
#include "../utils/common.h"
// 假設節點最大數量為 100
#define MAX_SIZE 100
/* 節點結構體 */
typedef struct AdjListNode {
Vertex *vertex; // 頂點
struct AdjListNode *next; // 後繼節點
} AdjListNode;
/* 基於鄰接表實現的無向圖類別 */
typedef struct {
AdjListNode *heads[MAX_SIZE]; // 節點陣列
int size; // 節點數量
} GraphAdjList;
/* 建構子 */
GraphAdjList *newGraphAdjList() {
GraphAdjList *graph = (GraphAdjList *)malloc(sizeof(GraphAdjList));
if (!graph) {
return NULL;
}
graph->size = 0;
for (int i = 0; i < MAX_SIZE; i++) {
graph->heads[i] = NULL;
}
return graph;
}
/* 析構函式 */
void delGraphAdjList(GraphAdjList *graph) {
for (int i = 0; i < graph->size; i++) {
AdjListNode *cur = graph->heads[i];
while (cur != NULL) {
AdjListNode *next = cur->next;
if (cur != graph->heads[i]) {
free(cur);
}
cur = next;
}
free(graph->heads[i]->vertex);
free(graph->heads[i]);
}
free(graph);
}
/* 查詢頂點對應的節點 */
AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) {
for (int i = 0; i < graph->size; i++) {
if (graph->heads[i]->vertex == vet) {
return graph->heads[i];
}
}
return NULL;
}
/* 新增邊輔助函式 */
void addEdgeHelper(AdjListNode *head, Vertex *vet) {
AdjListNode *node = (AdjListNode *)malloc(sizeof(AdjListNode));
node->vertex = vet;
// 頭插法
node->next = head->next;
head->next = node;
}
/* 新增邊 */
void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) {
AdjListNode *head1 = findNode(graph, vet1);
AdjListNode *head2 = findNode(graph, vet2);
assert(head1 != NULL && head2 != NULL && head1 != head2);
// 新增邊 vet1 - vet2
addEdgeHelper(head1, vet2);
addEdgeHelper(head2, vet1);
}
/* 刪除邊輔助函式 */
void removeEdgeHelper(AdjListNode *head, Vertex *vet) {
AdjListNode *pre = head;
AdjListNode *cur = head->next;
// 在鏈結串列中搜索 vet 對應節點
while (cur != NULL && cur->vertex != vet) {
pre = cur;
cur = cur->next;
}
if (cur == NULL)
return;
// 將 vet 對應節點從鏈結串列中刪除
pre->next = cur->next;
// 釋放記憶體
free(cur);
}
/* 刪除邊 */
void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) {
AdjListNode *head1 = findNode(graph, vet1);
AdjListNode *head2 = findNode(graph, vet2);
assert(head1 != NULL && head2 != NULL);
// 刪除邊 vet1 - vet2
removeEdgeHelper(head1, head2->vertex);
removeEdgeHelper(head2, head1->vertex);
}
/* 新增頂點 */
void addVertex(GraphAdjList *graph, Vertex *vet) {
assert(graph != NULL && graph->size < MAX_SIZE);
AdjListNode *head = (AdjListNode *)malloc(sizeof(AdjListNode));
head->vertex = vet;
head->next = NULL;
// 在鄰接表中新增一個新鏈結串列
graph->heads[graph->size++] = head;
}
/* 刪除頂點 */
void removeVertex(GraphAdjList *graph, Vertex *vet) {
AdjListNode *node = findNode(graph, vet);
assert(node != NULL);
// 在鄰接表中刪除頂點 vet 對應的鏈結串列
AdjListNode *cur = node, *pre = NULL;
while (cur) {
pre = cur;
cur = cur->next;
free(pre);
}
// 走訪其他頂點的鏈結串列,刪除所有包含 vet 的邊
for (int i = 0; i < graph->size; i++) {
cur = graph->heads[i];
pre = NULL;
while (cur) {
pre = cur;
cur = cur->next;
if (cur && cur->vertex == vet) {
pre->next = cur->next;
free(cur);
break;
}
}
}
// 將該頂點之後的頂點向前移動,以填補空缺
int i;
for (i = 0; i < graph->size; i++) {
if (graph->heads[i] == node)
break;
}
for (int j = i; j < graph->size - 1; j++) {
graph->heads[j] = graph->heads[j + 1];
}
graph->size--;
free(vet);
}
/* 列印鄰接表 */
void printGraph(const GraphAdjList *graph) {
printf("鄰接表 =\n");
for (int i = 0; i < graph->size; ++i) {
AdjListNode *node = graph->heads[i];
printf("%d: [", node->vertex->val);
node = node->next;
while (node) {
printf("%d, ", node->vertex->val);
node = node->next;
}
printf("]\n");
}
}

View File

@@ -0,0 +1,55 @@
/**
* File: graph_adjacency_list_test.c
* Created Time: 2023-07-11
* Author: NI-SW (947743645@qq.com)
*/
#include "graph_adjacency_list.c"
/* Driver Code */
int main() {
int vals[] = {1, 3, 2, 5, 4};
int size = sizeof(vals) / sizeof(vals[0]);
Vertex **v = valsToVets(vals, size);
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}};
int egdeSize = sizeof(edges) / sizeof(edges[0]);
GraphAdjList *graph = newGraphAdjList();
// 新增所有頂點和邊
for (int i = 0; i < size; i++) {
addVertex(graph, v[i]);
}
for (int i = 0; i < egdeSize; i++) {
addEdge(graph, edges[i][0], edges[i][1]);
}
printf("\n初始化後,圖為\n");
printGraph(graph);
/* 新增邊 */
// 頂點 1, 2 即 v[0], v[2]
addEdge(graph, v[0], v[2]);
printf("\n新增邊 1-2 後,圖為\n");
printGraph(graph);
/* 刪除邊 */
// 頂點 1, 3 即 v[0], v[1]
removeEdge(graph, v[0], v[1]);
printf("\n刪除邊 1-3 後,圖為\n");
printGraph(graph);
/* 新增頂點 */
Vertex *v5 = newVertex(6);
addVertex(graph, v5);
printf("\n新增頂點 6 後,圖為\n");
printGraph(graph);
/* 刪除頂點 */
// 頂點 3 即 v[1]
removeVertex(graph, v[1]);
printf("\n刪除頂點 3 後,圖為:\n");
printGraph(graph);
// 釋放記憶體
delGraphAdjList(graph);
free(v);
return 0;
}

View File

@@ -0,0 +1,150 @@
/**
* File: graph_adjacency_matrix.c
* Created Time: 2023-07-06
* Author: NI-SW (947743645@qq.com)
*/
#include "../utils/common.h"
// 假設頂點數量最大為 100
#define MAX_SIZE 100
/* 基於鄰接矩陣實現的無向圖結構體 */
typedef struct {
int vertices[MAX_SIZE];
int adjMat[MAX_SIZE][MAX_SIZE];
int size;
} GraphAdjMat;
/* 建構子 */
GraphAdjMat *newGraphAdjMat() {
GraphAdjMat *graph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat));
graph->size = 0;
for (int i = 0; i < MAX_SIZE; i++) {
for (int j = 0; j < MAX_SIZE; j++) {
graph->adjMat[i][j] = 0;
}
}
return graph;
}
/* 析構函式 */
void delGraphAdjMat(GraphAdjMat *graph) {
free(graph);
}
/* 新增頂點 */
void addVertex(GraphAdjMat *graph, int val) {
if (graph->size == MAX_SIZE) {
fprintf(stderr, "圖的頂點數量已達最大值\n");
return;
}
// 新增第 n 個頂點,並將第 n 行和列置零
int n = graph->size;
graph->vertices[n] = val;
for (int i = 0; i <= n; i++) {
graph->adjMat[n][i] = graph->adjMat[i][n] = 0;
}
graph->size++;
}
/* 刪除頂點 */
void removeVertex(GraphAdjMat *graph, int index) {
if (index < 0 || index >= graph->size) {
fprintf(stderr, "頂點索引越界\n");
return;
}
// 在頂點串列中移除索引 index 的頂點
for (int i = index; i < graph->size - 1; i++) {
graph->vertices[i] = graph->vertices[i + 1];
}
// 在鄰接矩陣中刪除索引 index 的行
for (int i = index; i < graph->size - 1; i++) {
for (int j = 0; j < graph->size; j++) {
graph->adjMat[i][j] = graph->adjMat[i + 1][j];
}
}
// 在鄰接矩陣中刪除索引 index 的列
for (int i = 0; i < graph->size; i++) {
for (int j = index; j < graph->size - 1; j++) {
graph->adjMat[i][j] = graph->adjMat[i][j + 1];
}
}
graph->size--;
}
/* 新增邊 */
// 參數 i, j 對應 vertices 元素索引
void addEdge(GraphAdjMat *graph, int i, int j) {
if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) {
fprintf(stderr, "邊索引越界或相等\n");
return;
}
graph->adjMat[i][j] = 1;
graph->adjMat[j][i] = 1;
}
/* 刪除邊 */
// 參數 i, j 對應 vertices 元素索引
void removeEdge(GraphAdjMat *graph, int i, int j) {
if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) {
fprintf(stderr, "邊索引越界或相等\n");
return;
}
graph->adjMat[i][j] = 0;
graph->adjMat[j][i] = 0;
}
/* 列印鄰接矩陣 */
void printGraphAdjMat(GraphAdjMat *graph) {
printf("頂點串列 = ");
printArray(graph->vertices, graph->size);
printf("鄰接矩陣 =\n");
for (int i = 0; i < graph->size; i++) {
printArray(graph->adjMat[i], graph->size);
}
}
/* Driver Code */
int main() {
// 初始化無向圖
GraphAdjMat *graph = newGraphAdjMat();
int vertices[] = {1, 3, 2, 5, 4};
for (int i = 0; i < 5; i++) {
addVertex(graph, vertices[i]);
}
int edges[][2] = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}};
for (int i = 0; i < 6; i++) {
addEdge(graph, edges[i][0], edges[i][1]);
}
printf("\n初始化後,圖為\n");
printGraphAdjMat(graph);
/* 新增邊 */
// 頂點 1, 2 的索引分別為 0, 2
addEdge(graph, 0, 2);
printf("\n新增邊 1-2 後,圖為\n");
printGraphAdjMat(graph);
/* 刪除邊 */
// 頂點 1, 3 的索引分別為 0, 1
removeEdge(graph, 0, 1);
printf("\n刪除邊 1-3 後,圖為\n");
printGraphAdjMat(graph);
/* 新增頂點 */
addVertex(graph, 6);
printf("\n新增頂點 6 後,圖為\n");
printGraphAdjMat(graph);
/* 刪除頂點 */
// 頂點 3 的索引為 1
removeVertex(graph, 1);
printf("\n刪除頂點 3 後,圖為\n");
printGraphAdjMat(graph);
// 釋放記憶體
delGraphAdjMat(graph);
return 0;
}

View File

@@ -0,0 +1,116 @@
/**
* File: graph_bfs.c
* Created Time: 2023-07-11
* Author: NI-SW (947743645@qq.com)
*/
#include "graph_adjacency_list.c"
// 假設節點最大數量為 100
#define MAX_SIZE 100
/* 節點佇列結構體 */
typedef struct {
Vertex *vertices[MAX_SIZE];
int front, rear, size;
} Queue;
/* 建構子 */
Queue *newQueue() {
Queue *q = (Queue *)malloc(sizeof(Queue));
q->front = q->rear = q->size = 0;
return q;
}
/* 判斷佇列是否為空 */
int isEmpty(Queue *q) {
return q->size == 0;
}
/* 入列操作 */
void enqueue(Queue *q, Vertex *vet) {
q->vertices[q->rear] = vet;
q->rear = (q->rear + 1) % MAX_SIZE;
q->size++;
}
/* 出列操作 */
Vertex *dequeue(Queue *q) {
Vertex *vet = q->vertices[q->front];
q->front = (q->front + 1) % MAX_SIZE;
q->size--;
return vet;
}
/* 檢查頂點是否已被訪問 */
int isVisited(Vertex **visited, int size, Vertex *vet) {
// 走訪查詢節點,使用 O(n) 時間
for (int i = 0; i < size; i++) {
if (visited[i] == vet)
return 1;
}
return 0;
}
/* 廣度優先走訪 */
// 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) {
// 佇列用於實現 BFS
Queue *queue = newQueue();
enqueue(queue, startVet);
visited[(*visitedSize)++] = startVet;
// 以頂點 vet 為起點,迴圈直至訪問完所有頂點
while (!isEmpty(queue)) {
Vertex *vet = dequeue(queue); // 佇列首頂點出隊
res[(*resSize)++] = vet; // 記錄訪問頂點
// 走訪該頂點的所有鄰接頂點
AdjListNode *node = findNode(graph, vet);
while (node != NULL) {
// 跳過已被訪問的頂點
if (!isVisited(visited, *visitedSize, node->vertex)) {
enqueue(queue, node->vertex); // 只入列未訪問的頂點
visited[(*visitedSize)++] = node->vertex; // 標記該頂點已被訪問
}
node = node->next;
}
}
// 釋放記憶體
free(queue);
}
/* Driver Code */
int main() {
// 初始化無向圖
int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int size = sizeof(vals) / sizeof(vals[0]);
Vertex **v = valsToVets(vals, size);
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]}, {v[2], v[5]}, {v[3], v[4]},
{v[3], v[6]}, {v[4], v[5]}, {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}};
int egdeSize = sizeof(edges) / sizeof(edges[0]);
GraphAdjList *graph = newGraphAdjList();
// 新增所有頂點和邊
for (int i = 0; i < size; i++) {
addVertex(graph, v[i]);
}
for (int i = 0; i < egdeSize; i++) {
addEdge(graph, edges[i][0], edges[i][1]);
}
printf("\n初始化後,圖為\n");
printGraph(graph);
// 廣度優先走訪
// 頂點走訪序列
Vertex *res[MAX_SIZE];
int resSize = 0;
// 用於記錄已被訪問過的頂點
Vertex *visited[MAX_SIZE];
int visitedSize = 0;
graphBFS(graph, v[0], res, &resSize, visited, &visitedSize);
printf("\n廣度優先走訪BFS頂點序列為\n");
printArray(vetsToVals(res, resSize), resSize);
// 釋放記憶體
delGraphAdjList(graph);
free(v);
return 0;
}

View File

@@ -0,0 +1,75 @@
/**
* File: graph_dfs.c
* Created Time: 2023-07-13
* Author: NI-SW (947743645@qq.com)
*/
#include "graph_adjacency_list.c"
// 假設節點最大數量為 100
#define MAX_SIZE 100
/* 檢查頂點是否已被訪問 */
int isVisited(Vertex **res, int size, Vertex *vet) {
// 走訪查詢節點,使用 O(n) 時間
for (int i = 0; i < size; i++) {
if (res[i] == vet) {
return 1;
}
}
return 0;
}
/* 深度優先走訪輔助函式 */
void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) {
// 記錄訪問頂點
res[(*resSize)++] = vet;
// 走訪該頂點的所有鄰接頂點
AdjListNode *node = findNode(graph, vet);
while (node != NULL) {
// 跳過已被訪問的頂點
if (!isVisited(res, *resSize, node->vertex)) {
// 遞迴訪問鄰接頂點
dfs(graph, res, resSize, node->vertex);
}
node = node->next;
}
}
/* 深度優先走訪 */
// 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) {
dfs(graph, res, resSize, startVet);
}
/* Driver Code */
int main() {
// 初始化無向圖
int vals[] = {0, 1, 2, 3, 4, 5, 6};
int size = sizeof(vals) / sizeof(vals[0]);
Vertex **v = valsToVets(vals, size);
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[5]}, {v[4], v[5]}, {v[5], v[6]}};
int egdeSize = sizeof(edges) / sizeof(edges[0]);
GraphAdjList *graph = newGraphAdjList();
// 新增所有頂點和邊
for (int i = 0; i < size; i++) {
addVertex(graph, v[i]);
}
for (int i = 0; i < egdeSize; i++) {
addEdge(graph, edges[i][0], edges[i][1]);
}
printf("\n初始化後,圖為\n");
printGraph(graph);
// 深度優先走訪
Vertex *res[MAX_SIZE];
int resSize = 0;
graphDFS(graph, v[0], res, &resSize);
printf("\n深度優先走訪DFS頂點序列為\n");
printArray(vetsToVals(res, resSize), resSize);
// 釋放記憶體
delGraphAdjList(graph);
free(v);
return 0;
}

View File

@@ -0,0 +1,8 @@
add_executable(coin_change_greedy coin_change_greedy.c)
add_executable(fractional_knapsack fractional_knapsack.c)
add_executable(max_capacity max_capacity.c)
add_executable(max_product_cutting max_product_cutting.c)
if (NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
target_link_libraries(max_product_cutting m)
endif()

View File

@@ -0,0 +1,60 @@
/**
* File: coin_change_greedy.c
* Created Time: 2023-09-07
* Author: lwbaptx (lwbaptx@gmail.com)
*/
#include "../utils/common.h"
/* 零錢兌換:貪婪 */
int coinChangeGreedy(int *coins, int size, int amt) {
// 假設 coins 串列有序
int i = size - 1;
int count = 0;
// 迴圈進行貪婪選擇,直到無剩餘金額
while (amt > 0) {
// 找到小於且最接近剩餘金額的硬幣
while (i > 0 && coins[i] > amt) {
i--;
}
// 選擇 coins[i]
amt -= coins[i];
count++;
}
// 若未找到可行方案,則返回 -1
return amt == 0 ? count : -1;
}
/* Driver Code */
int main() {
// 貪婪:能夠保證找到全域性最優解
int coins1[6] = {1, 5, 10, 20, 50, 100};
int amt = 186;
int res = coinChangeGreedy(coins1, 6, amt);
printf("\ncoins = ");
printArray(coins1, 6);
printf("amt = %d\n", amt);
printf("湊到 %d 所需的最少硬幣數量為 %d\n", amt, res);
// 貪婪:無法保證找到全域性最優解
int coins2[3] = {1, 20, 50};
amt = 60;
res = coinChangeGreedy(coins2, 3, amt);
printf("\ncoins = ");
printArray(coins2, 3);
printf("amt = %d\n", amt);
printf("湊到 %d 所需的最少硬幣數量為 %d\n", amt, res);
printf("實際上需要的最少數量為 3 ,即 20 + 20 + 20\n");
// 貪婪:無法保證找到全域性最優解
int coins3[3] = {1, 49, 50};
amt = 98;
res = coinChangeGreedy(coins3, 3, amt);
printf("\ncoins = ");
printArray(coins3, 3);
printf("amt = %d\n", amt);
printf("湊到 %d 所需的最少硬幣數量為 %d\n", amt, res);
printf("實際上需要的最少數量為 2 ,即 49 + 49\n");
return 0;
}

View File

@@ -0,0 +1,60 @@
/**
* File: fractional_knapsack.c
* Created Time: 2023-09-14
* Author: xianii (xianyi.xia@outlook.com)
*/
#include "../utils/common.h"
/* 物品 */
typedef struct {
int w; // 物品重量
int v; // 物品價值
} Item;
/* 按照價值密度排序 */
int sortByValueDensity(const void *a, const void *b) {
Item *t1 = (Item *)a;
Item *t2 = (Item *)b;
return (float)(t1->v) / t1->w < (float)(t2->v) / t2->w;
}
/* 分數背包:貪婪 */
float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) {
// 建立物品串列,包含兩個屬性:重量、價值
Item *items = malloc(sizeof(Item) * itemCount);
for (int i = 0; i < itemCount; i++) {
items[i] = (Item){.w = wgt[i], .v = val[i]};
}
// 按照單位價值 item.v / item.w 從高到低進行排序
qsort(items, (size_t)itemCount, sizeof(Item), sortByValueDensity);
// 迴圈貪婪選擇
float res = 0.0;
for (int i = 0; i < itemCount; i++) {
if (items[i].w <= cap) {
// 若剩餘容量充足,則將當前物品整個裝進背包
res += items[i].v;
cap -= items[i].w;
} else {
// 若剩餘容量不足,則將當前物品的一部分裝進背包
res += (float)cap / items[i].w * items[i].v;
cap = 0;
break;
}
}
free(items);
return res;
}
/* Driver Code */
int main(void) {
int wgt[] = {10, 20, 30, 40, 50};
int val[] = {50, 120, 150, 210, 240};
int capacity = 50;
// 貪婪演算法
float res = fractionalKnapsack(wgt, val, sizeof(wgt) / sizeof(int), capacity);
printf("不超過背包容量的最大物品價值為 %0.2f\n", res);
return 0;
}

View File

@@ -0,0 +1,49 @@
/**
* File: max_capacity.c
* Created Time: 2023-09-15
* Author: xianii (xianyi.xia@outlook.com)
*/
#include "../utils/common.h"
/* 求最小值 */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* 求最大值 */
int myMax(int a, int b) {
return a < b ? a : b;
}
/* 最大容量:貪婪 */
int maxCapacity(int ht[], int htLength) {
// 初始化 i, j使其分列陣列兩端
int i = 0;
int j = htLength - 1;
// 初始最大容量為 0
int res = 0;
// 迴圈貪婪選擇,直至兩板相遇
while (i < j) {
// 更新最大容量
int capacity = myMin(ht[i], ht[j]) * (j - i);
res = myMax(res, capacity);
// 向內移動短板
if (ht[i] < ht[j]) {
i++;
} else {
j--;
}
}
return res;
}
/* Driver Code */
int main(void) {
int ht[] = {3, 8, 5, 2, 7, 7, 3, 4};
// 貪婪演算法
int res = maxCapacity(ht, sizeof(ht) / sizeof(int));
printf("最大容量為 %d\n", res);
return 0;
}

View File

@@ -0,0 +1,38 @@
/**
* File: max_product_cutting.c
* Created Time: 2023-09-15
* Author: xianii (xianyi.xia@outlook.com)
*/
#include "../utils/common.h"
/* 最大切分乘積:貪婪 */
int maxProductCutting(int n) {
// 當 n <= 3 時,必須切分出一個 1
if (n <= 3) {
return 1 * (n - 1);
}
// 貪婪地切分出 3 a 為 3 的個數b 為餘數
int a = n / 3;
int b = n % 3;
if (b == 1) {
// 當餘數為 1 時,將一對 1 * 3 轉化為 2 * 2
return pow(3, a - 1) * 2 * 2;
}
if (b == 2) {
// 當餘數為 2 時,不做處理
return pow(3, a) * 2;
}
// 當餘數為 0 時,不做處理
return pow(3, a);
}
/* Driver Code */
int main(void) {
int n = 58;
// 貪婪演算法
int res = maxProductCutting(n);
printf("最大切分乘積為 %d\n", res);
return 0;
}

View File

@@ -0,0 +1,4 @@
add_executable(array_hash_map array_hash_map.c)
add_executable(hash_map_chaining hash_map_chaining.c)
add_executable(hash_map_open_addressing hash_map_open_addressing.c)
add_executable(simple_hash simple_hash.c)

View File

@@ -0,0 +1,212 @@
/**
* File: array_hash_map.c
* Created Time: 2023-03-18
* Author: Guanngxu (446678850@qq.com)
*/
#include "../utils/common.h"
/* 雜湊表預設大小 */
#define HASHTABLE_CAPACITY 100
/* 鍵值對 int->string */
typedef struct {
int key;
char *val;
} Pair;
/* 鍵值對的集合 */
typedef struct {
void *set;
int len;
} MapSet;
/* 基於陣列實現的雜湊表 */
typedef struct {
Pair *buckets[HASHTABLE_CAPACITY];
} ArrayHashMap;
/* 建構子 */
ArrayHashMap *newArrayHashMap() {
ArrayHashMap *hmap = malloc(sizeof(ArrayHashMap));
return hmap;
}
/* 析構函式 */
void delArrayHashMap(ArrayHashMap *hmap) {
for (int i = 0; i < HASHTABLE_CAPACITY; i++) {
if (hmap->buckets[i] != NULL) {
free(hmap->buckets[i]->val);
free(hmap->buckets[i]);
}
}
free(hmap);
}
/* 雜湊函式 */
int hashFunc(int key) {
int index = key % HASHTABLE_CAPACITY;
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 < HASHTABLE_CAPACITY; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
entries = malloc(sizeof(Pair) * total);
for (i = 0; i < HASHTABLE_CAPACITY; 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 < HASHTABLE_CAPACITY; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
keys = malloc(total * sizeof(int));
for (i = 0; i < HASHTABLE_CAPACITY; 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 < HASHTABLE_CAPACITY; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
vals = malloc(total * sizeof(char *));
for (i = 0; i < HASHTABLE_CAPACITY; 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新增完成後,雜湊表為\nKey -> Value\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 後,雜湊表為\nKey -> Value\n");
print(hmap);
/* 走訪雜湊表 */
int i;
printf("\n走訪鍵值對 Key->Value\n");
print(hmap);
MapSet set;
keySet(hmap, &set);
int *keys = (int *)set.set;
printf("\n單獨走訪鍵 Key\n");
for (i = 0; i < set.len; i++) {
printf("%d\n", keys[i]);
}
free(set.set);
valueSet(hmap, &set);
char **vals = (char **)set.set;
printf("\n單獨走訪鍵 Value\n");
for (i = 0; i < set.len; i++) {
printf("%s\n", vals[i]);
}
free(set.set);
delArrayHashMap(hmap);
return 0;
}

View File

@@ -0,0 +1,213 @@
/**
* File: hash_map_chaining.c
* Created Time: 2023-10-13
* Author: SenMing (1206575349@qq.com), krahets (krahets@163.com)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 假設 val 最大長度為 100
#define MAX_SIZE 100
/* 鍵值對 */
typedef struct {
int key;
char val[MAX_SIZE];
} Pair;
/* 鏈結串列節點 */
typedef struct Node {
Pair *pair;
struct Node *next;
} Node;
/* 鏈式位址雜湊表 */
typedef struct {
int size; // 鍵值對數量
int capacity; // 雜湊表容量
double loadThres; // 觸發擴容的負載因子閾值
int extendRatio; // 擴容倍數
Node **buckets; // 桶陣列
} HashMapChaining;
/* 建構子 */
HashMapChaining *newHashMapChaining() {
HashMapChaining *hashMap = (HashMapChaining *)malloc(sizeof(HashMapChaining));
hashMap->size = 0;
hashMap->capacity = 4;
hashMap->loadThres = 2.0 / 3.0;
hashMap->extendRatio = 2;
hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *));
for (int i = 0; i < hashMap->capacity; i++) {
hashMap->buckets[i] = NULL;
}
return hashMap;
}
/* 析構函式 */
void delHashMapChaining(HashMapChaining *hashMap) {
for (int i = 0; i < hashMap->capacity; i++) {
Node *cur = hashMap->buckets[i];
while (cur) {
Node *tmp = cur;
cur = cur->next;
free(tmp->pair);
free(tmp);
}
}
free(hashMap->buckets);
free(hashMap);
}
/* 雜湊函式 */
int hashFunc(HashMapChaining *hashMap, int key) {
return key % hashMap->capacity;
}
/* 負載因子 */
double loadFactor(HashMapChaining *hashMap) {
return (double)hashMap->size / (double)hashMap->capacity;
}
/* 查詢操作 */
char *get(HashMapChaining *hashMap, int key) {
int index = hashFunc(hashMap, key);
// 走訪桶,若找到 key ,則返回對應 val
Node *cur = hashMap->buckets[index];
while (cur) {
if (cur->pair->key == key) {
return cur->pair->val;
}
cur = cur->next;
}
return ""; // 若未找到 key ,則返回空字串
}
/* 新增操作 */
void put(HashMapChaining *hashMap, int key, const char *val);
/* 擴容雜湊表 */
void extend(HashMapChaining *hashMap) {
// 暫存原雜湊表
int oldCapacity = hashMap->capacity;
Node **oldBuckets = hashMap->buckets;
// 初始化擴容後的新雜湊表
hashMap->capacity *= hashMap->extendRatio;
hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *));
for (int i = 0; i < hashMap->capacity; i++) {
hashMap->buckets[i] = NULL;
}
hashMap->size = 0;
// 將鍵值對從原雜湊表搬運至新雜湊表
for (int i = 0; i < oldCapacity; i++) {
Node *cur = oldBuckets[i];
while (cur) {
put(hashMap, cur->pair->key, cur->pair->val);
Node *temp = cur;
cur = cur->next;
// 釋放記憶體
free(temp->pair);
free(temp);
}
}
free(oldBuckets);
}
/* 新增操作 */
void put(HashMapChaining *hashMap, int key, const char *val) {
// 當負載因子超過閾值時,執行擴容
if (loadFactor(hashMap) > hashMap->loadThres) {
extend(hashMap);
}
int index = hashFunc(hashMap, key);
// 走訪桶,若遇到指定 key ,則更新對應 val 並返回
Node *cur = hashMap->buckets[index];
while (cur) {
if (cur->pair->key == key) {
strcpy(cur->pair->val, val); // 若遇到指定 key ,則更新對應 val 並返回
return;
}
cur = cur->next;
}
// 若無該 key ,則將鍵值對新增至鏈結串列頭部
Pair *newPair = (Pair *)malloc(sizeof(Pair));
newPair->key = key;
strcpy(newPair->val, val);
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->pair = newPair;
newNode->next = hashMap->buckets[index];
hashMap->buckets[index] = newNode;
hashMap->size++;
}
/* 刪除操作 */
void removeItem(HashMapChaining *hashMap, int key) {
int index = hashFunc(hashMap, key);
Node *cur = hashMap->buckets[index];
Node *pre = NULL;
while (cur) {
if (cur->pair->key == key) {
// 從中刪除鍵值對
if (pre) {
pre->next = cur->next;
} else {
hashMap->buckets[index] = cur->next;
}
// 釋放記憶體
free(cur->pair);
free(cur);
hashMap->size--;
return;
}
pre = cur;
cur = cur->next;
}
}
/* 列印雜湊表 */
void print(HashMapChaining *hashMap) {
for (int i = 0; i < hashMap->capacity; i++) {
Node *cur = hashMap->buckets[i];
printf("[");
while (cur) {
printf("%d -> %s, ", cur->pair->key, cur->pair->val);
cur = cur->next;
}
printf("]\n");
}
}
/* Driver Code */
int main() {
/* 初始化雜湊表 */
HashMapChaining *hashMap = newHashMapChaining();
/* 新增操作 */
// 在雜湊表中新增鍵值對 (key, value)
put(hashMap, 12836, "小哈");
put(hashMap, 15937, "小囉");
put(hashMap, 16750, "小算");
put(hashMap, 13276, "小法");
put(hashMap, 10583, "小鴨");
printf("\n新增完成後,雜湊表為\nKey -> Value\n");
print(hashMap);
/* 查詢操作 */
// 向雜湊表中輸入鍵 key ,得到值 value
char *name = get(hashMap, 13276);
printf("\n輸入學號 13276 ,查詢到姓名 %s\n", name);
/* 刪除操作 */
// 在雜湊表中刪除鍵值對 (key, value)
removeItem(hashMap, 12836);
printf("\n刪除學號 12836 後,雜湊表為\nKey -> Value\n");
print(hashMap);
/* 釋放雜湊表空間 */
delHashMapChaining(hashMap);
return 0;
}

View File

@@ -0,0 +1,208 @@
/**
* 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 **)malloc(sizeof(Pair *) * hashMap->capacity);
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);
}
}
}
/* 雜湊函式 */
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 **)malloc(sizeof(Pair *) * hashMap->capacity);
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新增完成後,雜湊表為\nKey -> Value\n");
print(hashmap);
// 查詢操作
// 向雜湊表中輸入鍵 key ,得到值 val
char *name = get(hashmap, 13276);
printf("\n輸入學號 13276 ,查詢到姓名 %s\n", name);
// 刪除操作
// 在雜湊表中刪除鍵值對 (key, val)
removeItem(hashmap, 16750);
printf("\n刪除 16750 後,雜湊表為\nKey -> Value\n");
print(hashmap);
// 銷燬雜湊表
delHashMapOpenAddressing(hashmap);
return 0;
}

View File

@@ -0,0 +1,68 @@
/**
* File: simple_hash.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 加法雜湊 */
int addHash(char *key) {
long long hash = 0;
const int MODULUS = 1000000007;
for (int i = 0; i < strlen(key); i++) {
hash = (hash + (unsigned char)key[i]) % MODULUS;
}
return (int)hash;
}
/* 乘法雜湊 */
int mulHash(char *key) {
long long hash = 0;
const int MODULUS = 1000000007;
for (int i = 0; i < strlen(key); i++) {
hash = (31 * hash + (unsigned char)key[i]) % MODULUS;
}
return (int)hash;
}
/* 互斥或雜湊 */
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 dsad3241241dsa算123法";
int hash = addHash(key);
printf("加法雜湊值為 %d\n", hash);
hash = mulHash(key);
printf("乘法雜湊值為 %d\n", hash);
hash = xorHash(key);
printf("互斥或雜湊值為 %d\n", hash);
hash = rotHash(key);
printf("旋轉雜湊值為 %d\n", hash);
return 0;
}

View File

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

View File

@@ -0,0 +1,152 @@
/**
* File: my_heap.c
* Created Time: 2023-01-15
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 5000
/* 大頂堆積 */
typedef struct {
// size 代表的是實際元素的個數
int size;
// 使用預先分配記憶體的陣列,避免擴容
int data[MAX_SIZE];
} MaxHeap;
// 函式宣告
void siftDown(MaxHeap *maxHeap, int i);
void siftUp(MaxHeap *maxHeap, int i);
int parent(MaxHeap *maxHeap, int i);
/* 建構子,根據切片建堆積 */
MaxHeap *newMaxHeap(int nums[], int size) {
// 所有元素入堆積
MaxHeap *maxHeap = (MaxHeap *)malloc(sizeof(MaxHeap));
maxHeap->size = size;
memcpy(maxHeap->data, nums, size * sizeof(int));
for (int i = parent(maxHeap, size - 1); i >= 0; i--) {
// 堆積化除葉節點以外的其他所有節點
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);
// 當“越過根節點”或“節點無須修復”時,結束堆積化
if (p < 0 || maxHeap->data[i] <= maxHeap->data[p]) {
break;
}
// 交換兩節點
swap(maxHeap, i, p);
// 迴圈向上堆積化
i = p;
}
}

View File

@@ -0,0 +1,41 @@
/**
* File: my_heap_test.c
* Created Time: 2023-01-15
* Author: Reanon (793584285@qq.com)
*/
#include "my_heap.c"
/* Driver Code */
int main() {
/* 初始化堆積 */
// 初始化大頂堆積
int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2};
MaxHeap *maxHeap = newMaxHeap(nums, sizeof(nums) / sizeof(int));
printf("輸入陣列並建堆積後\n");
printHeap(maxHeap->data, maxHeap->size);
/* 獲取堆積頂元素 */
printf("\n堆積頂元素為 %d\n", peek(maxHeap));
/* 元素入堆積 */
push(maxHeap, 7);
printf("\n元素 7 入堆積後\n");
printHeap(maxHeap->data, maxHeap->size);
/* 堆積頂元素出堆積 */
int top = pop(maxHeap);
printf("\n堆積頂元素 %d 出堆積後\n", top);
printHeap(maxHeap->data, maxHeap->size);
/* 獲取堆積大小 */
printf("\n堆積元素數量為 %d\n", size(maxHeap));
/* 判斷堆積是否為空 */
printf("\n堆積是否為空 %d\n", isEmpty(maxHeap));
// 釋放記憶體
delMaxHeap(maxHeap);
return 0;
}

View File

@@ -0,0 +1,73 @@
/**
* File: top_k.c
* Created Time: 2023-10-26
* Author: krahets (krahets163.com)
*/
#include "my_heap.c"
/* 元素入堆積 */
void pushMinHeap(MaxHeap *maxHeap, int val) {
// 元素取反
push(maxHeap, -val);
}
/* 元素出堆積 */
int popMinHeap(MaxHeap *maxHeap) {
// 元素取反
return -pop(maxHeap);
}
/* 訪問堆積頂元素 */
int peekMinHeap(MaxHeap *maxHeap) {
// 元素取反
return -peek(maxHeap);
}
/* 取出堆積中元素 */
int *getMinHeap(MaxHeap *maxHeap) {
// 將堆積中所有元素取反並存入 res 陣列
int *res = (int *)malloc(maxHeap->size * sizeof(int));
for (int i = 0; i < maxHeap->size; i++) {
res[i] = -maxHeap->data[i];
}
return res;
}
// 基於堆積查詢陣列中最大的 k 個元素的函式
int *topKHeap(int *nums, int sizeNums, int k) {
// 初始化小頂堆積
// 請注意:我們將堆積中所有元素取反,從而用大頂堆積來模擬小頂堆積
int *empty = (int *)malloc(0);
MaxHeap *maxHeap = newMaxHeap(empty, 0);
// 將陣列的前 k 個元素入堆積
for (int i = 0; i < k; i++) {
pushMinHeap(maxHeap, nums[i]);
}
// 從第 k+1 個元素開始,保持堆積的長度為 k
for (int i = k; i < sizeNums; i++) {
// 若當前元素大於堆積頂元素,則將堆積頂元素出堆積、當前元素入堆積
if (nums[i] > peekMinHeap(maxHeap)) {
popMinHeap(maxHeap);
pushMinHeap(maxHeap, nums[i]);
}
}
int *res = getMinHeap(maxHeap);
// 釋放記憶體
delMaxHeap(maxHeap);
return res;
}
/* Driver Code */
int main() {
int nums[] = {1, 7, 6, 3, 2};
int k = 3;
int sizeNums = sizeof(nums) / sizeof(nums[0]);
int *res = topKHeap(nums, sizeNums, k);
printf("最大的 %d 個元素為: ", k);
printArray(res, k);
free(res);
return 0;
}

View File

@@ -0,0 +1,4 @@
add_executable(binary_search binary_search.c)
add_executable(two_sum two_sum.c)
add_executable(binary_search_edge binary_search_edge.c)
add_executable(binary_search_insertion binary_search_insertion.c)

View File

@@ -0,0 +1,59 @@
/**
* File: binary_search.c
* Created Time: 2023-03-18
* Author: Guanngxu (446678850@qq.com)
*/
#include "../utils/common.h"
/* 二分搜尋(雙閉區間) */
int binarySearch(int *nums, int len, int target) {
// 初始化雙閉區間 [0, n-1] ,即 i, j 分別指向陣列首元素、尾元素
int i = 0, j = len - 1;
// 迴圈,當搜尋區間為空時跳出(當 i > j 時為空)
while (i <= j) {
int m = i + (j - i) / 2; // 計算中點索引 m
if (nums[m] < target) // 此情況說明 target 在區間 [m+1, j] 中
i = m + 1;
else if (nums[m] > target) // 此情況說明 target 在區間 [i, m-1] 中
j = m - 1;
else // 找到目標元素,返回其索引
return m;
}
// 未找到目標元素,返回 -1
return -1;
}
/* 二分搜尋(左閉右開區間) */
int binarySearchLCRO(int *nums, int len, int target) {
// 初始化左閉右開區間 [0, n) ,即 i, j 分別指向陣列首元素、尾元素+1
int i = 0, j = len;
// 迴圈,當搜尋區間為空時跳出(當 i = j 時為空)
while (i < j) {
int m = i + (j - i) / 2; // 計算中點索引 m
if (nums[m] < target) // 此情況說明 target 在區間 [m+1, j) 中
i = m + 1;
else if (nums[m] > target) // 此情況說明 target 在區間 [i, m) 中
j = m;
else // 找到目標元素,返回其索引
return m;
}
// 未找到目標元素,返回 -1
return -1;
}
/* Driver Code */
int main() {
int target = 6;
int nums[10] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
/* 二分搜尋(雙閉區間) */
int index = binarySearch(nums, 10, target);
printf("目標元素 6 的索引 = %d\n", index);
/* 二分搜尋(左閉右開區間) */
index = binarySearchLCRO(nums, 10, target);
printf("目標元素 6 的索引 = %d\n", index);
return 0;
}

View File

@@ -0,0 +1,67 @@
/**
* File: binary_search_edge.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 二分搜尋插入點(存在重複元素) */
int binarySearchInsertion(int *nums, int numSize, int target) {
int i = 0, j = numSize - 1; // 初始化雙閉區間 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 計算中點索引 m
if (nums[m] < target) {
i = m + 1; // target 在區間 [m+1, j] 中
} else {
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
}
}
// 返回插入點 i
return i;
}
/* 二分搜尋最左一個 target */
int binarySearchLeftEdge(int *nums, int numSize, int target) {
// 等價於查詢 target 的插入點
int i = binarySearchInsertion(nums, numSize, target);
// 未找到 target ,返回 -1
if (i == numSize || nums[i] != target) {
return -1;
}
// 找到 target ,返回索引 i
return i;
}
/* 二分搜尋最右一個 target */
int binarySearchRightEdge(int *nums, int numSize, int target) {
// 轉化為查詢最左一個 target + 1
int i = binarySearchInsertion(nums, numSize, target + 1);
// j 指向最右一個 target i 指向首個大於 target 的元素
int j = i - 1;
// 未找到 target ,返回 -1
if (j == -1 || nums[j] != target) {
return -1;
}
// 找到 target ,返回索引 j
return j;
}
/* Driver Code */
int main() {
// 包含重複元素的陣列
int nums[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15};
printf("\n陣列 nums = ");
printArray(nums, sizeof(nums) / sizeof(nums[0]));
// 二分搜尋左邊界和右邊界
int targets[] = {6, 7};
for (int i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) {
int index = binarySearchLeftEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]);
printf("最左一個元素 %d 的索引為 %d\n", targets[i], index);
index = binarySearchRightEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]);
printf("最右一個元素 %d 的索引為 %d\n", targets[i], index);
}
return 0;
}

View File

@@ -0,0 +1,68 @@
/**
* File: binary_search_insertion.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 二分搜尋插入點(無重複元素) */
int binarySearchInsertionSimple(int *nums, int numSize, int target) {
int i = 0, j = numSize - 1; // 初始化雙閉區間 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 計算中點索引 m
if (nums[m] < target) {
i = m + 1; // target 在區間 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在區間 [i, m-1] 中
} else {
return m; // 找到 target ,返回插入點 m
}
}
// 未找到 target ,返回插入點 i
return i;
}
/* 二分搜尋插入點(存在重複元素) */
int binarySearchInsertion(int *nums, int numSize, int target) {
int i = 0, j = numSize - 1; // 初始化雙閉區間 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 計算中點索引 m
if (nums[m] < target) {
i = m + 1; // target 在區間 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在區間 [i, m-1] 中
} else {
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
}
}
// 返回插入點 i
return i;
}
/* Driver Code */
int main() {
// 無重複元素的陣列
int nums1[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
printf("\n陣列 nums = ");
printArray(nums1, sizeof(nums1) / sizeof(nums1[0]));
// 二分搜尋插入點
int targets1[] = {6, 9};
for (int i = 0; i < sizeof(targets1) / sizeof(targets1[0]); i++) {
int index = binarySearchInsertionSimple(nums1, sizeof(nums1) / sizeof(nums1[0]), targets1[i]);
printf("元素 %d 的插入點的索引為 %d\n", targets1[i], index);
}
// 包含重複元素的陣列
int nums2[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15};
printf("\n陣列 nums = ");
printArray(nums2, sizeof(nums2) / sizeof(nums2[0]));
// 二分搜尋插入點
int targets2[] = {2, 6, 20};
for (int i = 0; i < sizeof(targets2) / sizeof(int); i++) {
int index = binarySearchInsertion(nums2, sizeof(nums2) / sizeof(nums2[0]), targets2[i]);
printf("元素 %d 的插入點的索引為 %d\n", targets2[i], index);
}
return 0;
}

View File

@@ -0,0 +1,86 @@
/**
* File: two_sum.c
* Created Time: 2023-01-19
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* 方法一:暴力列舉 */
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;
}
}
/* 方法二:輔助雜湊表 */
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);
// 方法一
printf("方法一 res = ");
printArray(res, returnSize);
// 方法二
res = twoSumHashTable(nums, sizeof(nums) / sizeof(int), target, &returnSize);
printf("方法二 res = ");
printArray(res, returnSize);
return 0;
}

View File

@@ -0,0 +1,9 @@
add_executable(bubble_sort bubble_sort.c)
add_executable(insertion_sort insertion_sort.c)
add_executable(quick_sort quick_sort.c)
add_executable(counting_sort counting_sort.c)
add_executable(radix_sort radix_sort.c)
add_executable(merge_sort merge_sort.c)
add_executable(heap_sort heap_sort.c)
add_executable(bucket_sort bucket_sort.c)
add_executable(selection_sort selection_sort.c)

View File

@@ -0,0 +1,61 @@
/**
* File: bubble_sort.c
* Created Time: 2022-12-26
* Author: Listening (https://github.com/L-Super)
*/
#include "../utils/common.h"
/* 泡沫排序 */
void bubbleSort(int nums[], int size) {
// 外迴圈:未排序區間為 [0, i]
for (int i = size - 1; i > 0; i--) {
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
/* 泡沫排序(標誌最佳化)*/
void bubbleSortWithFlag(int nums[], int size) {
// 外迴圈:未排序區間為 [0, i]
for (int i = size - 1; i > 0; i--) {
bool flag = false;
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
flag = true;
}
}
if (!flag)
break;
}
}
/* Driver Code */
int main() {
int nums[6] = {4, 1, 3, 1, 5, 2};
printf("泡沫排序後: ");
bubbleSort(nums, 6);
for (int i = 0; i < 6; i++) {
printf("%d ", nums[i]);
}
int nums1[6] = {4, 1, 3, 1, 5, 2};
printf("\n最佳化版泡沫排序後: ");
bubbleSortWithFlag(nums1, 6);
for (int i = 0; i < 6; i++) {
printf("%d ", nums1[i]);
}
printf("\n");
return 0;
}

View File

@@ -0,0 +1,82 @@
/**
* File: bucket_sort.c
* Created Time: 2023-05-30
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
#define SIZE 10
/* 比較兩個浮點數的大小 */
int compare_float(const void *a, const void *b) {
float fa = *(const float *)a;
float fb = *(const float *)b;
return (fa > fb) - (fa < fb);
}
/* 交換兩個浮點數 */
void swap(float *a, float *b) {
float tmp = *a;
*a = *b;
*b = tmp;
}
/* 桶排序 */
void bucketSort(float nums[], int size) {
// 初始化 k = n/2 個桶,預期向每個桶分配 2 個元素
int k = size / 2;
float **buckets = calloc(k, sizeof(float *));
for (int i = 0; i < k; i++) {
// 每個桶最多可以分配 size 個元素
buckets[i] = calloc(size, sizeof(float));
}
// 1. 將陣列元素分配到各個桶中
for (int i = 0; i < size; i++) {
// 輸入資料範圍為 [0, 1),使用 num * k 對映到索引範圍 [0, k-1]
int bucket_idx = nums[i] * k;
int j = 0;
// 如果桶中有資料且資料小於當前值 nums[i], 要將其放到當前桶的後面,相當於 cpp 中的 push_back
while (buckets[bucket_idx][j] > 0 && buckets[bucket_idx][j] < nums[i]) {
j++;
}
float temp = nums[i];
while (j < size && buckets[bucket_idx][j] > 0) {
swap(&temp, &buckets[bucket_idx][j]);
j++;
}
buckets[bucket_idx][j] = temp;
}
// 2. 對各個桶執行排序
for (int i = 0; i < k; i++) {
qsort(buckets[i], size, sizeof(float), compare_float);
}
// 3. 走訪桶合併結果
for (int i = 0, j = 0; j < k; j++) {
for (int l = 0; l < size; l++) {
if (buckets[j][l] > 0) {
nums[i++] = buckets[j][l];
}
}
}
// 釋放上述分配的記憶體
for (int i = 0; i < k; i++) {
free(buckets[i]);
}
free(buckets);
}
/* Driver Code */
int main() {
// 設輸入資料為浮點數,範圍為 [0, 1)
float nums[SIZE] = {0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f};
bucketSort(nums, SIZE);
printf("桶排序完成後 nums = ");
printArrayFloat(nums, SIZE);
return 0;
}

View File

@@ -0,0 +1,86 @@
/**
* 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 的索引
}
// 使用結果陣列 res 覆蓋原陣列 nums
memcpy(nums, res, size * sizeof(int));
// 5. 釋放記憶體
free(counter);
}
/* Driver Code */
int main() {
int nums[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4};
int size = sizeof(nums) / sizeof(int);
countingSortNaive(nums, size);
printf("計數排序(無法排序物件)完成後 nums = ");
printArray(nums, size);
int nums1[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4};
int size1 = sizeof(nums1) / sizeof(int);
countingSort(nums1, size1);
printf("計數排序完成後 nums1 = ");
printArray(nums1, size1);
return 0;
}

View File

@@ -0,0 +1,60 @@
/**
* File: heap_sort.c
* Created Time: 2023-05-30
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 堆積的長度為 n ,從節點 i 開始,從頂至底堆積化 */
void siftDown(int nums[], int n, int i) {
while (1) {
// 判斷節點 i, l, r 中值最大的節點,記為 ma
int l = 2 * i + 1;
int r = 2 * i + 2;
int ma = i;
if (l < n && nums[l] > nums[ma])
ma = l;
if (r < n && nums[r] > nums[ma])
ma = r;
// 若節點 i 最大或索引 l, r 越界,則無須繼續堆積化,跳出
if (ma == i) {
break;
}
// 交換兩節點
int temp = nums[i];
nums[i] = nums[ma];
nums[ma] = temp;
// 迴圈向下堆積化
i = ma;
}
}
/* 堆積排序 */
void heapSort(int nums[], int n) {
// 建堆積操作:堆積化除葉節點以外的其他所有節點
for (int i = n / 2 - 1; i >= 0; --i) {
siftDown(nums, n, i);
}
// 從堆積中提取最大元素,迴圈 n-1 輪
for (int i = n - 1; i > 0; --i) {
// 交換根節點與最右葉節點(交換首元素與尾元素)
int tmp = nums[0];
nums[0] = nums[i];
nums[i] = tmp;
// 以根節點為起點,從頂至底進行堆積化
siftDown(nums, i, 0);
}
}
/* Driver Code */
int main() {
int nums[] = {4, 1, 3, 1, 5, 2};
int n = sizeof(nums) / sizeof(nums[0]);
heapSort(nums, n);
printf("堆積排序完成後 nums = ");
printArray(nums, n);
return 0;
}

View File

@@ -0,0 +1,36 @@
/**
* File: insertion_sort.c
* Created Time: 2022-12-29
* Author: Listening (https://github.com/L-Super)
*/
#include "../utils/common.h"
/* 插入排序 */
void insertionSort(int nums[], int size) {
// 外迴圈:已排序區間為 [0, i-1]
for (int i = 1; i < size; i++) {
int base = nums[i], j = i - 1;
// 內迴圈:將 base 插入到已排序區間 [0, i-1] 中的正確位置
while (j >= 0 && nums[j] > base) {
// 將 nums[j] 向右移動一位
nums[j + 1] = nums[j];
j--;
}
// 將 base 賦值到正確位置
nums[j + 1] = base;
}
}
/* Driver Code */
int main() {
int nums[] = {4, 1, 3, 1, 5, 2};
insertionSort(nums, 6);
printf("插入排序完成後 nums = ");
for (int i = 0; i < 6; i++) {
printf("%d ", nums[i]);
}
printf("\n");
return 0;
}

View File

@@ -0,0 +1,63 @@
/**
* File: merge_sort.c
* Created Time: 2022-03-21
* Author: Guanngxu (446678850@qq.com)
*/
#include "../utils/common.h"
/* 合併左子陣列和右子陣列 */
void merge(int *nums, int left, int mid, int right) {
// 左子陣列區間為 [left, mid], 右子陣列區間為 [mid+1, right]
// 建立一個臨時陣列 tmp ,用於存放合併後的結果
int tmpSize = right - left + 1;
int *tmp = (int *)malloc(tmpSize * sizeof(int));
// 初始化左子陣列和右子陣列的起始索引
int i = left, j = mid + 1, k = 0;
// 當左右子陣列都還有元素時,進行比較並將較小的元素複製到臨時陣列中
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) {
tmp[k++] = nums[i++];
} else {
tmp[k++] = nums[j++];
}
}
// 將左子陣列和右子陣列的剩餘元素複製到臨時陣列中
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
// 將臨時陣列 tmp 中的元素複製回原陣列 nums 的對應區間
for (k = 0; k < tmpSize; ++k) {
nums[left + k] = tmp[k];
}
// 釋放記憶體
free(tmp);
}
/* 合併排序 */
void mergeSort(int *nums, int left, int right) {
// 終止條件
if (left >= right)
return; // 當子陣列長度為 1 時終止遞迴
// 劃分階段
int mid = (left + right) / 2; // 計算中點
mergeSort(nums, left, mid); // 遞迴左子陣列
mergeSort(nums, mid + 1, right); // 遞迴右子陣列
// 合併階段
merge(nums, left, mid, right);
}
/* Driver Code */
int main() {
/* 合併排序 */
int nums[] = {7, 3, 2, 6, 0, 1, 5, 4};
int size = sizeof(nums) / sizeof(int);
mergeSort(nums, 0, size - 1);
printf("合併排序完成後 nums = ");
printArray(nums, size);
return 0;
}

View File

@@ -0,0 +1,134 @@
/**
* 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); // 遞迴排序左子陣列
left = pivot + 1; // 剩餘未排序區間為 [pivot + 1, right]
} else {
quickSortTailCall(nums, pivot + 1, right); // 遞迴排序右子陣列
right = pivot - 1; // 剩餘未排序區間為 [left, pivot - 1]
}
}
}
/* Driver Code */
int main() {
/* 快速排序 */
int nums[] = {2, 4, 1, 0, 3, 5};
int size = sizeof(nums) / sizeof(int);
quickSort(nums, 0, size - 1);
printf("快速排序完成後 nums = ");
printArray(nums, size);
/* 快速排序(中位基準數最佳化) */
int nums1[] = {2, 4, 1, 0, 3, 5};
quickSortMedian(nums1, 0, size - 1);
printf("快速排序(中位基準數最佳化)完成後 nums = ");
printArray(nums1, size);
/* 快速排序(尾遞迴最佳化) */
int nums2[] = {2, 4, 1, 0, 3, 5};
quickSortTailCall(nums2, 0, size - 1);
printf("快速排序(尾遞迴最佳化)完成後 nums = ");
printArray(nums1, size);
return 0;
}

View File

@@ -0,0 +1,71 @@
/**
* File: radix_sort.c
* Created Time: 2023-01-18
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* 獲取元素 num 的第 k 位,其中 exp = 10^(k-1) */
int digit(int num, int exp) {
// 傳入 exp 而非 k 可以避免在此重複執行昂貴的次方計算
return (num / exp) % 10;
}
/* 計數排序(根據 nums 第 k 位排序) */
void countingSortDigit(int nums[], int size, int exp) {
// 十進位制的位範圍為 0~9 ,因此需要長度為 10 的桶陣列
int *counter = (int *)malloc((sizeof(int) * 10));
// 統計 0~9 各數字的出現次數
for (int i = 0; i < size; i++) {
// 獲取 nums[i] 第 k 位,記為 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; // 獲取 d 在陣列中的索引 j
res[j] = nums[i]; // 將當前元素填入索引 j
counter[d]--; // 將 d 的數量減 1
}
// 使用結果覆蓋原陣列 nums
for (int i = 0; i < size; i++) {
nums[i] = res[i];
}
}
/* 基數排序 */
void radixSort(int nums[], int size) {
// 獲取陣列的最大元素,用於判斷最大位數
int max = INT32_MIN;
for (size_t i = 0; i < size - 1; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
// 按照從低位到高位的順序走訪
for (int exp = 1; max >= exp; exp *= 10)
// 對陣列元素的第 k 位執行計數排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
countingSortDigit(nums, size, exp);
}
/* Driver Code */
int main() {
// 基數排序
int nums[] = {10546151, 35663510, 42865989, 34862445, 81883077,
88906420, 72429244, 30524779, 82060337, 63832996};
int size = sizeof(nums) / sizeof(int);
radixSort(nums, size);
printf("基數排序完成後 nums = ");
printArray(nums, size);
}

View File

@@ -0,0 +1,37 @@
/**
* File: selection_sort.c
* Created Time: 2023-05-31
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 選擇排序 */
void selectionSort(int nums[], int n) {
// 外迴圈:未排序區間為 [i, n-1]
for (int i = 0; i < n - 1; i++) {
// 內迴圈:找到未排序區間內的最小元素
int k = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j; // 記錄最小元素的索引
}
// 將該最小元素與未排序區間的首個元素交換
int temp = nums[i];
nums[i] = nums[k];
nums[k] = temp;
}
}
/* Driver Code */
int main() {
int nums[] = {4, 1, 3, 1, 5, 2};
int n = sizeof(nums) / sizeof(nums[0]);
selectionSort(nums, n);
printf("選擇排序完成後 nums = ");
printArray(nums, n);
return 0;
}

View File

@@ -0,0 +1,6 @@
add_executable(array_stack array_stack.c)
add_executable(linkedlist_stack linkedlist_stack.c)
add_executable(array_queue array_queue.c)
add_executable(linkedlist_queue linkedlist_queue.c)
add_executable(array_deque array_deque.c)
add_executable(linkedlist_deque linkedlist_deque.c)

View File

@@ -0,0 +1,159 @@
/**
* File: array_deque.c
* Created Time: 2023-03-13
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 基於環形陣列實現的雙向佇列 */
typedef struct {
int *nums; // 用於儲存佇列元素的陣列
int front; // 佇列首指標,指向佇列首元素
int queSize; // 尾指標,指向佇列尾 + 1
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;
}
/* Driver Code */
int main() {
/* 初始化佇列 */
int capacity = 10;
ArrayDeque *deque = newArrayDeque(capacity);
pushLast(deque, 3);
pushLast(deque, 2);
pushLast(deque, 5);
printf("雙向佇列 deque = ");
printArray(deque->nums, deque->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 佇列尾入列後 deque = ");
printArray(deque->nums, deque->queSize);
pushFirst(deque, 1);
printf("元素 1 佇列首入列後 deque = ");
printArray(deque->nums, deque->queSize);
/* 元素出列 */
int popLastNum = popLast(deque);
printf("佇列尾出列元素 = %d ,佇列尾出列後 deque= ", popLastNum);
printArray(deque->nums, deque->queSize);
int popFirstNum = popFirst(deque);
printf("佇列首出列元素 = %d ,佇列首出列後 deque= ", popFirstNum);
printArray(deque->nums, deque->queSize);
/* 獲取佇列的長度 */
int dequeSize = size(deque);
printf("雙向佇列長度 size = %d\r\n", dequeSize);
/* 判斷佇列是否為空 */
bool isEmpty = empty(deque);
printf("佇列是否為空 = %s\r\n", isEmpty ? "true" : "false");
// 釋放記憶體
delArrayDeque(deque);
return 0;
}

View File

@@ -0,0 +1,121 @@
/**
* File: array_queue.c
* Created Time: 2023-01-28
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 基於環形陣列實現的佇列 */
typedef struct {
int *nums; // 用於儲存佇列元素的陣列
int front; // 佇列首指標,指向佇列首元素
int queSize; // 尾指標,指向佇列尾 + 1
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);
// 佇列首指標向後移動一位,若越過尾部,則返回到陣列頭部
queue->front = (queue->front + 1) % queue->queCapacity;
queue->queSize--;
return num;
}
/* Driver Code */
int main() {
/* 初始化佇列 */
int capacity = 10;
ArrayQueue *queue = newArrayQueue(capacity);
/* 元素入列 */
push(queue, 1);
push(queue, 3);
push(queue, 2);
push(queue, 5);
push(queue, 4);
printf("佇列 queue = ");
printArray(queue->nums, queue->queSize);
/* 訪問佇列首元素 */
int peekNum = peek(queue);
printf("佇列首元素 peek = %d\r\n", peekNum);
/* 元素出列 */
peekNum = pop(queue);
printf("出列元素 pop = %d ,出列後 queue = ", peekNum);
printArray(queue->nums, queue->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 輪入列 + 出列後 queue = ", i);
printArray(queue->nums, queue->queSize);
}
// 釋放記憶體
delArrayQueue(queue);
return 0;
}

View File

@@ -0,0 +1,103 @@
/**
* File: array_stack.c
* Created Time: 2023-01-12
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 5000
/* 基於陣列實現的堆疊 */
typedef struct {
int *data;
int size;
} ArrayStack;
/* 建構子 */
ArrayStack *newArrayStack() {
ArrayStack *stack = malloc(sizeof(ArrayStack));
// 初始化一個大容量,避免擴容
stack->data = malloc(sizeof(int) * MAX_SIZE);
stack->size = 0;
return stack;
}
/* 析構函式 */
void delArrayStack(ArrayStack *stack) {
free(stack->data);
free(stack);
}
/* 獲取堆疊的長度 */
int size(ArrayStack *stack) {
return stack->size;
}
/* 判斷堆疊是否為空 */
bool isEmpty(ArrayStack *stack) {
return stack->size == 0;
}
/* 入堆疊 */
void push(ArrayStack *stack, int num) {
if (stack->size == MAX_SIZE) {
printf("堆疊已滿\n");
return;
}
stack->data[stack->size] = num;
stack->size++;
}
/* 訪問堆疊頂元素 */
int peek(ArrayStack *stack) {
if (stack->size == 0) {
printf("堆疊為空\n");
return INT_MAX;
}
return stack->data[stack->size - 1];
}
/* 出堆疊 */
int pop(ArrayStack *stack) {
int val = peek(stack);
stack->size--;
return val;
}
/* Driver Code */
int main() {
/* 初始化堆疊 */
ArrayStack *stack = newArrayStack();
/* 元素入堆疊 */
push(stack, 1);
push(stack, 3);
push(stack, 2);
push(stack, 5);
push(stack, 4);
printf("堆疊 stack = ");
printArray(stack->data, stack->size);
/* 訪問堆疊頂元素 */
int val = peek(stack);
printf("堆疊頂元素 top = %d\n", val);
/* 元素出堆疊 */
val = pop(stack);
printf("出堆疊元素 pop = %d ,出堆疊後 stack = ", val);
printArray(stack->data, stack->size);
/* 獲取堆疊的長度 */
int size = stack->size;
printf("堆疊的長度 size = %d\n", size);
/* 判斷是否為空 */
bool empty = isEmpty(stack);
printf("堆疊是否為空 = %stack\n", empty ? "true" : "false");
// 釋放記憶體
delArrayStack(stack);
return 0;
}

View File

@@ -0,0 +1,212 @@
/**
* File: linkedlist_deque.c
* Created Time: 2023-03-13
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 雙向鏈結串列節點 */
typedef struct DoublyListNode {
int val; // 節點值
struct DoublyListNode *next; // 後繼節點
struct DoublyListNode *prev; // 前驅節點
} DoublyListNode;
/* 建構子 */
DoublyListNode *newDoublyListNode(int num) {
DoublyListNode *new = (DoublyListNode *)malloc(sizeof(DoublyListNode));
new->val = num;
new->next = NULL;
new->prev = NULL;
return new;
}
/* 析構函式 */
void delDoublyListNode(DoublyListNode *node) {
free(node);
}
/* 基於雙向鏈結串列實現的雙向佇列 */
typedef struct {
DoublyListNode *front, *rear; // 頭節點 front ,尾節點 rear
int queSize; // 雙向佇列的長度
} LinkedListDeque;
/* 建構子 */
LinkedListDeque *newLinkedListDeque() {
LinkedListDeque *deque = (LinkedListDeque *)malloc(sizeof(LinkedListDeque));
deque->front = NULL;
deque->rear = NULL;
deque->queSize = 0;
return deque;
}
/* 析構函式 */
void delLinkedListdeque(LinkedListDeque *deque) {
// 釋放所有節點
for (int i = 0; i < deque->queSize && deque->front != NULL; i++) {
DoublyListNode *tmp = deque->front;
deque->front = deque->front->next;
free(tmp);
}
// 釋放 deque 結構體
free(deque);
}
/* 獲取佇列的長度 */
int size(LinkedListDeque *deque) {
return deque->queSize;
}
/* 判斷佇列是否為空 */
bool empty(LinkedListDeque *deque) {
return (size(deque) == 0);
}
/* 入列 */
void push(LinkedListDeque *deque, int num, bool isFront) {
DoublyListNode *node = newDoublyListNode(num);
// 若鏈結串列為空,則令 front 和 rear 都指向node
if (empty(deque)) {
deque->front = deque->rear = node;
}
// 佇列首入列操作
else if (isFront) {
// 將 node 新增至鏈結串列頭部
deque->front->prev = node;
node->next = deque->front;
deque->front = node; // 更新頭節點
}
// 佇列尾入列操作
else {
// 將 node 新增至鏈結串列尾部
deque->rear->next = node;
node->prev = deque->rear;
deque->rear = node;
}
deque->queSize++; // 更新佇列長度
}
/* 佇列首入列 */
void pushFirst(LinkedListDeque *deque, int num) {
push(deque, num, true);
}
/* 佇列尾入列 */
void pushLast(LinkedListDeque *deque, int num) {
push(deque, num, false);
}
/* 訪問佇列首元素 */
int peekFirst(LinkedListDeque *deque) {
assert(size(deque) && deque->front);
return deque->front->val;
}
/* 訪問佇列尾元素 */
int peekLast(LinkedListDeque *deque) {
assert(size(deque) && deque->rear);
return deque->rear->val;
}
/* 出列 */
int pop(LinkedListDeque *deque, bool isFront) {
if (empty(deque))
return -1;
int val;
// 佇列首出列操作
if (isFront) {
val = peekFirst(deque); // 暫存頭節點值
DoublyListNode *fNext = deque->front->next;
if (fNext) {
fNext->prev = NULL;
deque->front->next = NULL;
delDoublyListNode(deque->front);
}
deque->front = fNext; // 更新頭節點
}
// 佇列尾出列操作
else {
val = peekLast(deque); // 暫存尾節點值
DoublyListNode *rPrev = deque->rear->prev;
if (rPrev) {
rPrev->next = NULL;
deque->rear->prev = NULL;
delDoublyListNode(deque->rear);
}
deque->rear = rPrev; // 更新尾節點
}
deque->queSize--; // 更新佇列長度
return val;
}
/* 佇列首出列 */
int popFirst(LinkedListDeque *deque) {
return pop(deque, true);
}
/* 佇列尾出列 */
int popLast(LinkedListDeque *deque) {
return pop(deque, false);
}
/* 列印佇列 */
void printLinkedListDeque(LinkedListDeque *deque) {
int *arr = malloc(sizeof(int) * deque->queSize);
// 複製鏈結串列中的資料到陣列
int i;
DoublyListNode *node;
for (i = 0, node = deque->front; i < deque->queSize; i++) {
arr[i] = node->val;
node = node->next;
}
printArray(arr, deque->queSize);
free(arr);
}
/* Driver Code */
int main() {
/* 初始化雙向佇列 */
LinkedListDeque *deque = newLinkedListDeque();
pushLast(deque, 3);
pushLast(deque, 2);
pushLast(deque, 5);
printf("雙向佇列 deque = ");
printLinkedListDeque(deque);
/* 訪問元素 */
int peekFirstNum = peekFirst(deque);
printf("佇列首元素 peekFirst = %d\r\n", peekFirstNum);
int peekLastNum = peekLast(deque);
printf("佇列首元素 peekLast = %d\r\n", peekLastNum);
/* 元素入列 */
pushLast(deque, 4);
printf("元素 4 佇列尾入列後 deque =");
printLinkedListDeque(deque);
pushFirst(deque, 1);
printf("元素 1 佇列首入列後 deque =");
printLinkedListDeque(deque);
/* 元素出列 */
int popLastNum = popLast(deque);
printf("佇列尾出列元素 popLast = %d ,佇列尾出列後 deque = ", popLastNum);
printLinkedListDeque(deque);
int popFirstNum = popFirst(deque);
printf("佇列首出列元素 popFirst = %d ,佇列首出列後 deque = ", popFirstNum);
printLinkedListDeque(deque);
/* 獲取佇列的長度 */
int dequeSize = size(deque);
printf("雙向佇列長度 size = %d\r\n", dequeSize);
/* 判斷佇列是否為空 */
bool isEmpty = empty(deque);
printf("雙向佇列是否為空 = %s\r\n", isEmpty ? "true" : "false");
// 釋放記憶體
delLinkedListdeque(deque);
return 0;
}

View File

@@ -0,0 +1,128 @@
/**
* File: linkedlist_queue.c
* Created Time: 2023-03-13
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 基於鏈結串列實現的佇列 */
typedef struct {
ListNode *front, *rear;
int queSize;
} LinkedListQueue;
/* 建構子 */
LinkedListQueue *newLinkedListQueue() {
LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue));
queue->front = NULL;
queue->rear = NULL;
queue->queSize = 0;
return queue;
}
/* 析構函式 */
void delLinkedListQueue(LinkedListQueue *queue) {
// 釋放所有節點
while (queue->front != NULL) {
ListNode *tmp = queue->front;
queue->front = queue->front->next;
free(tmp);
}
// 釋放 queue 結構體
free(queue);
}
/* 獲取佇列的長度 */
int size(LinkedListQueue *queue) {
return queue->queSize;
}
/* 判斷佇列是否為空 */
bool empty(LinkedListQueue *queue) {
return (size(queue) == 0);
}
/* 入列 */
void push(LinkedListQueue *queue, int num) {
// 尾節點處新增 node
ListNode *node = newListNode(num);
// 如果佇列為空,則令頭、尾節點都指向該節點
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 ,出列後 queue = ", peekNum);
printLinkedListQueue(queue);
/* 獲取佇列的長度 */
int queueSize = size(queue);
printf("佇列長度 size = %d\r\n", queueSize);
/* 判斷佇列是否為空 */
bool isEmpty = empty(queue);
printf("佇列是否為空 = %s\r\n", isEmpty ? "true" : "false");
// 釋放記憶體
delLinkedListQueue(queue);
return 0;
}

View File

@@ -0,0 +1,107 @@
/**
* File: linkedlist_stack.c
* Created Time: 2023-01-12
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 基於鏈結串列實現的堆疊 */
typedef struct {
ListNode *top; // 將頭節點作為堆疊頂
int size; // 堆疊的長度
} LinkedListStack;
/* 建構子 */
LinkedListStack *newLinkedListStack() {
LinkedListStack *s = malloc(sizeof(LinkedListStack));
s->top = NULL;
s->size = 0;
return s;
}
/* 析構函式 */
void delLinkedListStack(LinkedListStack *s) {
while (s->top) {
ListNode *n = s->top->next;
free(s->top);
s->top = n;
}
free(s);
}
/* 獲取堆疊的長度 */
int size(LinkedListStack *s) {
return s->size;
}
/* 判斷堆疊是否為空 */
bool isEmpty(LinkedListStack *s) {
return size(s) == 0;
}
/* 入堆疊 */
void push(LinkedListStack *s, int num) {
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
node->next = s->top; // 更新新加節點指標域
node->val = num; // 更新新加節點資料域
s->top = node; // 更新堆疊頂
s->size++; // 更新堆疊大小
}
/* 訪問堆疊頂元素 */
int peek(LinkedListStack *s) {
if (s->size == 0) {
printf("堆疊為空\n");
return INT_MAX;
}
return s->top->val;
}
/* 出堆疊 */
int pop(LinkedListStack *s) {
int val = peek(s);
ListNode *tmp = s->top;
s->top = s->top->next;
// 釋放記憶體
free(tmp);
s->size--;
return val;
}
/* Driver Code */
int main() {
/* 初始化堆疊 */
LinkedListStack *stack = newLinkedListStack();
/* 元素入堆疊 */
push(stack, 1);
push(stack, 3);
push(stack, 2);
push(stack, 5);
push(stack, 4);
printf("堆疊 stack = ");
printLinkedList(stack->top);
/* 訪問堆疊頂元素 */
int val = peek(stack);
printf("堆疊頂元素 top = %d\r\n", val);
/* 元素出堆疊 */
val = pop(stack);
printf("出堆疊元素 pop = %d, 出堆疊後 stack = ", val);
printLinkedList(stack->top);
/* 獲取堆疊的長度 */
printf("堆疊的長度 size = %d\n", size(stack));
/* 判斷是否為空 */
bool empty = isEmpty(stack);
printf("堆疊是否為空 = %s\n", empty ? "true" : "false");
// 釋放記憶體
delLinkedListStack(stack);
return 0;
}

View File

@@ -0,0 +1,6 @@
add_executable(avl_tree avl_tree.c)
add_executable(binary_tree binary_tree.c)
add_executable(binary_tree_bfs binary_tree_bfs.c)
add_executable(binary_tree_dfs binary_tree_dfs.c)
add_executable(binary_search_tree binary_search_tree.c)
add_executable(array_binary_tree array_binary_tree.c)

View File

@@ -0,0 +1,166 @@
/**
* File: array_binary_tree.c
* Created Time: 2023-07-29
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 陣列表示下的二元樹結構體 */
typedef struct {
int *tree;
int size;
} ArrayBinaryTree;
/* 建構子 */
ArrayBinaryTree *newArrayBinaryTree(int *arr, int arrSize) {
ArrayBinaryTree *abt = (ArrayBinaryTree *)malloc(sizeof(ArrayBinaryTree));
abt->tree = malloc(sizeof(int) * arrSize);
memcpy(abt->tree, arr, sizeof(int) * arrSize);
abt->size = arrSize;
return abt;
}
/* 析構函式 */
void delArrayBinaryTree(ArrayBinaryTree *abt) {
free(abt->tree);
free(abt);
}
/* 串列容量 */
int size(ArrayBinaryTree *abt) {
return abt->size;
}
/* 獲取索引為 i 節點的值 */
int val(ArrayBinaryTree *abt, int i) {
// 若索引越界,則返回 INT_MAX ,代表空位
if (i < 0 || i >= size(abt))
return INT_MAX;
return abt->tree[i];
}
/* 獲取索引為 i 節點的左子節點的索引 */
int left(int i) {
return 2 * i + 1;
}
/* 獲取索引為 i 節點的右子節點的索引 */
int right(int i) {
return 2 * i + 2;
}
/* 獲取索引為 i 節點的父節點的索引 */
int parent(int i) {
return (i - 1) / 2;
}
/* 層序走訪 */
int *levelOrder(ArrayBinaryTree *abt, int *returnSize) {
int *res = (int *)malloc(sizeof(int) * size(abt));
int index = 0;
// 直接走訪陣列
for (int i = 0; i < size(abt); i++) {
if (val(abt, i) != INT_MAX)
res[index++] = val(abt, i);
}
*returnSize = index;
return res;
}
/* 深度優先走訪 */
void dfs(ArrayBinaryTree *abt, int i, char *order, int *res, int *index) {
// 若為空位,則返回
if (val(abt, i) == INT_MAX)
return;
// 前序走訪
if (strcmp(order, "pre") == 0)
res[(*index)++] = val(abt, i);
dfs(abt, left(i), order, res, index);
// 中序走訪
if (strcmp(order, "in") == 0)
res[(*index)++] = val(abt, i);
dfs(abt, right(i), order, res, index);
// 後序走訪
if (strcmp(order, "post") == 0)
res[(*index)++] = val(abt, i);
}
/* 前序走訪 */
int *preOrder(ArrayBinaryTree *abt, int *returnSize) {
int *res = (int *)malloc(sizeof(int) * size(abt));
int index = 0;
dfs(abt, 0, "pre", res, &index);
*returnSize = index;
return res;
}
/* 中序走訪 */
int *inOrder(ArrayBinaryTree *abt, int *returnSize) {
int *res = (int *)malloc(sizeof(int) * size(abt));
int index = 0;
dfs(abt, 0, "in", res, &index);
*returnSize = index;
return res;
}
/* 後序走訪 */
int *postOrder(ArrayBinaryTree *abt, int *returnSize) {
int *res = (int *)malloc(sizeof(int) * size(abt));
int index = 0;
dfs(abt, 0, "post", res, &index);
*returnSize = index;
return res;
}
/* Driver Code */
int main() {
// 初始化二元樹
// 使用 INT_MAX 代表空位 NULL
int arr[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15};
int arrSize = sizeof(arr) / sizeof(arr[0]);
TreeNode *root = arrayToTree(arr, arrSize);
printf("\n初始化二元樹\n");
printf("二元樹的陣列表示:\n");
printArray(arr, arrSize);
printf("二元樹的鏈結串列表示:\n");
printTree(root);
ArrayBinaryTree *abt = newArrayBinaryTree(arr, arrSize);
// 訪問節點
int i = 1;
int l = left(i), r = right(i), p = parent(i);
printf("\n當前節點的索引為 %d值為 %d\n", i, val(abt, i));
printf("其左子節點的索引為 %d值為 %d\n", l, l < arrSize ? val(abt, l) : INT_MAX);
printf("其右子節點的索引為 %d值為 %d\n", r, r < arrSize ? val(abt, r) : INT_MAX);
printf("其父節點的索引為 %d值為 %d\n", p, p < arrSize ? val(abt, p) : INT_MAX);
// 走訪樹
int returnSize;
int *res;
res = levelOrder(abt, &returnSize);
printf("\n層序走訪為: ");
printArray(res, returnSize);
free(res);
res = preOrder(abt, &returnSize);
printf("前序走訪為: ");
printArray(res, returnSize);
free(res);
res = inOrder(abt, &returnSize);
printf("中序走訪為: ");
printArray(res, returnSize);
free(res);
res = postOrder(abt, &returnSize);
printf("後序走訪為: ");
printArray(res, returnSize);
free(res);
// 釋放記憶體
delArrayBinaryTree(abt);
return 0;
}

View File

@@ -0,0 +1,259 @@
/**
* File: avl_tree.c
* Created Time: 2023-01-15
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* AVL 樹結構體 */
typedef struct {
TreeNode *root;
} AVLTree;
/* 建構子 */
AVLTree *newAVLTree() {
AVLTree *tree = (AVLTree *)malloc(sizeof(AVLTree));
tree->root = NULL;
return tree;
}
/* 析構函式 */
void delAVLTree(AVLTree *tree) {
freeMemoryTree(tree->root);
free(tree);
}
/* 獲取節點高度 */
int height(TreeNode *node) {
// 空節點高度為 -1 ,葉節點高度為 0
if (node != NULL) {
return node->height;
}
return -1;
}
/* 更新節點高度 */
void updateHeight(TreeNode *node) {
int lh = height(node->left);
int rh = height(node->right);
// 節點高度等於最高子樹高度 + 1
if (lh > rh) {
node->height = lh + 1;
} else {
node->height = rh + 1;
}
}
/* 獲取平衡因子 */
int balanceFactor(TreeNode *node) {
// 空節點平衡因子為 0
if (node == NULL) {
return 0;
}
// 節點平衡因子 = 左子樹高度 - 右子樹高度
return height(node->left) - height(node->right);
}
/* 右旋操作 */
TreeNode *rightRotate(TreeNode *node) {
TreeNode *child, *grandChild;
child = node->left;
grandChild = child->right;
// 以 child 為原點,將 node 向右旋轉
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;
// 以 child 為原點,將 node 向左旋轉
child->left = node;
node->right = grandChild;
// 更新節點高度
updateHeight(node);
updateHeight(child);
// 返回旋轉後子樹的根節點
return child;
}
/* 執行旋轉操作,使該子樹重新恢復平衡 */
TreeNode *rotate(TreeNode *node) {
// 獲取節點 node 的平衡因子
int bf = balanceFactor(node);
// 左偏樹
if (bf > 1) {
if (balanceFactor(node->left) >= 0) {
// 右旋
return rightRotate(node);
} else {
// 先左旋後右旋
node->left = leftRotate(node->left);
return rightRotate(node);
}
}
// 右偏樹
if (bf < -1) {
if (balanceFactor(node->right) <= 0) {
// 左旋
return leftRotate(node);
} else {
// 先右旋後左旋
node->right = rightRotate(node->right);
return leftRotate(node);
}
}
// 平衡樹,無須旋轉,直接返回
return node;
}
/* 遞迴插入節點(輔助函式) */
TreeNode *insertHelper(TreeNode *node, int val) {
if (node == NULL) {
return newTreeNode(val);
}
/* 1. 查詢插入位置並插入節點 */
if (val < node->val) {
node->left = insertHelper(node->left, val);
} else if (val > node->val) {
node->right = insertHelper(node->right, val);
} else {
// 重複節點不插入,直接返回
return node;
}
// 更新節點高度
updateHeight(node);
/* 2. 執行旋轉操作,使該子樹重新恢復平衡 */
node = rotate(node);
// 返回子樹的根節點
return node;
}
/* 插入節點 */
void insert(AVLTree *tree, int val) {
tree->root = insertHelper(tree->root, val);
}
/* 遞迴刪除節點(輔助函式) */
TreeNode *removeHelper(TreeNode *node, int val) {
TreeNode *child, *grandChild;
if (node == NULL) {
return NULL;
}
/* 1. 查詢節點並刪除 */
if (val < node->val) {
node->left = removeHelper(node->left, val);
} else if (val > node->val) {
node->right = removeHelper(node->right, val);
} else {
if (node->left == NULL || node->right == NULL) {
child = node->left;
if (node->right != NULL) {
child = node->right;
}
// 子節點數量 = 0 ,直接刪除 node 並返回
if (child == NULL) {
return NULL;
} else {
// 子節點數量 = 1 ,直接刪除 node
node = child;
}
} else {
// 子節點數量 = 2 ,則將中序走訪的下個節點刪除,並用該節點替換當前節點
TreeNode *temp = node->right;
while (temp->left != NULL) {
temp = temp->left;
}
int tempVal = temp->val;
node->right = removeHelper(node->right, temp->val);
node->val = tempVal;
}
}
// 更新節點高度
updateHeight(node);
/* 2. 執行旋轉操作,使該子樹重新恢復平衡 */
node = rotate(node);
// 返回子樹的根節點
return node;
}
/* 刪除節點 */
// 由於引入了 stdio.h ,此處無法使用 remove 關鍵詞
void removeItem(AVLTree *tree, int val) {
TreeNode *root = removeHelper(tree->root, val);
}
/* 查詢節點 */
TreeNode *search(AVLTree *tree, int val) {
TreeNode *cur = tree->root;
// 迴圈查詢,越過葉節點後跳出
while (cur != NULL) {
if (cur->val < val) {
// 目標節點在 cur 的右子樹中
cur = cur->right;
} else if (cur->val > val) {
// 目標節點在 cur 的左子樹中
cur = cur->left;
} else {
// 找到目標節點,跳出迴圈
break;
}
}
// 找到目標節點,跳出迴圈
return cur;
}
void testInsert(AVLTree *tree, int val) {
insert(tree, val);
printf("\n插入節點 %d 後AVL 樹為 \n", val);
printTree(tree->root);
}
void testRemove(AVLTree *tree, int val) {
removeItem(tree, val);
printf("\n刪除節點 %d 後AVL 樹為 \n", val);
printTree(tree->root);
}
/* Driver Code */
int main() {
/* 初始化空 AVL 樹 */
AVLTree *tree = (AVLTree *)newAVLTree();
/* 插入節點 */
// 請關注插入節點後AVL 樹是如何保持平衡的
testInsert(tree, 1);
testInsert(tree, 2);
testInsert(tree, 3);
testInsert(tree, 4);
testInsert(tree, 5);
testInsert(tree, 8);
testInsert(tree, 7);
testInsert(tree, 9);
testInsert(tree, 10);
testInsert(tree, 6);
/* 插入重複節點 */
testInsert(tree, 7);
/* 刪除節點 */
// 請關注刪除節點後AVL 樹是如何保持平衡的
testRemove(tree, 8); // 刪除度為 0 的節點
testRemove(tree, 5); // 刪除度為 1 的節點
testRemove(tree, 4); // 刪除度為 2 的節點
/* 查詢節點 */
TreeNode *node = search(tree, 7);
printf("\n查詢到的節點物件節點值 = %d \n", node->val);
// 釋放記憶體
delAVLTree(tree);
return 0;
}

View File

@@ -0,0 +1,171 @@
/**
* File: binary_search_tree.c
* Created Time: 2023-01-11
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* 二元搜尋樹結構體 */
typedef struct {
TreeNode *root;
} BinarySearchTree;
/* 建構子 */
BinarySearchTree *newBinarySearchTree() {
// 初始化空樹
BinarySearchTree *bst = (BinarySearchTree *)malloc(sizeof(BinarySearchTree));
bst->root = NULL;
return bst;
}
/* 析構函式 */
void delBinarySearchTree(BinarySearchTree *bst) {
freeMemoryTree(bst->root);
free(bst);
}
/* 獲取二元樹根節點 */
TreeNode *getRoot(BinarySearchTree *bst) {
return bst->root;
}
/* 查詢節點 */
TreeNode *search(BinarySearchTree *bst, int num) {
TreeNode *cur = bst->root;
// 迴圈查詢,越過葉節點後跳出
while (cur != NULL) {
if (cur->val < num) {
// 目標節點在 cur 的右子樹中
cur = cur->right;
} else if (cur->val > num) {
// 目標節點在 cur 的左子樹中
cur = cur->left;
} else {
// 找到目標節點,跳出迴圈
break;
}
}
// 返回目標節點
return cur;
}
/* 插入節點 */
void insert(BinarySearchTree *bst, int num) {
// 若樹為空,則初始化根節點
if (bst->root == NULL) {
bst->root = newTreeNode(num);
return;
}
TreeNode *cur = bst->root, *pre = NULL;
// 迴圈查詢,越過葉節點後跳出
while (cur != NULL) {
// 找到重複節點,直接返回
if (cur->val == num) {
return;
}
pre = cur;
if (cur->val < num) {
// 插入位置在 cur 的右子樹中
cur = cur->right;
} else {
// 插入位置在 cur 的左子樹中
cur = cur->left;
}
}
// 插入節點
TreeNode *node = newTreeNode(num);
if (pre->val < num) {
pre->right = node;
} else {
pre->left = node;
}
}
/* 刪除節點 */
// 由於引入了 stdio.h ,此處無法使用 remove 關鍵詞
void removeItem(BinarySearchTree *bst, int num) {
// 若樹為空,直接提前返回
if (bst->root == NULL)
return;
TreeNode *cur = bst->root, *pre = NULL;
// 迴圈查詢,越過葉節點後跳出
while (cur != NULL) {
// 找到待刪除節點,跳出迴圈
if (cur->val == num)
break;
pre = cur;
if (cur->val < num) {
// 待刪除節點在 root 的右子樹中
cur = cur->right;
} else {
// 待刪除節點在 root 的左子樹中
cur = cur->left;
}
}
// 若無待刪除節點,則直接返回
if (cur == NULL)
return;
// 判斷待刪除節點是否存在子節點
if (cur->left == NULL || cur->right == NULL) {
/* 子節點數量 = 0 or 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);
// 用 tmp 覆蓋 cur
cur->val = tmpVal;
}
}
/* Driver Code */
int main() {
/* 初始化二元搜尋樹 */
int nums[] = {8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15};
BinarySearchTree *bst = newBinarySearchTree();
for (int i = 0; i < sizeof(nums) / sizeof(int); i++) {
insert(bst, nums[i]);
}
printf("初始化的二元樹為\n");
printTree(getRoot(bst));
/* 查詢節點 */
TreeNode *node = search(bst, 7);
printf("查詢到的節點物件的節點值 = %d\n", node->val);
/* 插入節點 */
insert(bst, 16);
printf("插入節點 16 後,二元樹為\n");
printTree(getRoot(bst));
/* 刪除節點 */
removeItem(bst, 1);
printf("刪除節點 1 後,二元樹為\n");
printTree(getRoot(bst));
removeItem(bst, 2);
printf("刪除節點 2 後,二元樹為\n");
printTree(getRoot(bst));
removeItem(bst, 4);
printf("刪除節點 4 後,二元樹為\n");
printTree(getRoot(bst));
// 釋放記憶體
delBinarySearchTree(bst);
return 0;
}

View File

@@ -0,0 +1,43 @@
/**
* File: binary_tree.c
* Created Time: 2023-01-11
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* Driver Code */
int main() {
/* 初始化二元樹 */
// 初始化節點
TreeNode *n1 = newTreeNode(1);
TreeNode *n2 = newTreeNode(2);
TreeNode *n3 = newTreeNode(3);
TreeNode *n4 = newTreeNode(4);
TreeNode *n5 = newTreeNode(5);
// 構建節點之間的引用(指標)
n1->left = n2;
n1->right = n3;
n2->left = n4;
n2->right = n5;
printf("初始化二元樹\n");
printTree(n1);
/* 插入與刪除節點 */
TreeNode *P = newTreeNode(0);
// 在 n1 -> n2 中間插入節點 P
n1->left = P;
P->left = n2;
printf("插入節點 P 後\n");
printTree(n1);
// 刪除節點 P
n1->left = n2;
// 釋放記憶體
free(P);
printf("刪除節點 P 後\n");
printTree(n1);
freeMemoryTree(n1);
return 0;
}

View File

@@ -0,0 +1,73 @@
/**
* File: binary_tree_bfs.c
* Created Time: 2023-01-11
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
/* 層序走訪 */
int *levelOrder(TreeNode *root, int *size) {
/* 輔助佇列 */
int front, rear;
int index, *arr;
TreeNode *node;
TreeNode **queue;
/* 輔助佇列 */
queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_SIZE);
// 佇列指標
front = 0, rear = 0;
// 加入根節點
queue[rear++] = root;
// 初始化一個串列,用於儲存走訪序列
/* 輔助陣列 */
arr = (int *)malloc(sizeof(int) * MAX_SIZE);
// 陣列指標
index = 0;
while (front < rear) {
// 隊列出隊
node = queue[front++];
// 儲存節點值
arr[index++] = node->val;
if (node->left != NULL) {
// 左子節點入列
queue[rear++] = node->left;
}
if (node->right != NULL) {
// 右子節點入列
queue[rear++] = node->right;
}
}
// 更新陣列長度的值
*size = index;
arr = realloc(arr, sizeof(int) * (*size));
// 釋放輔助陣列空間
free(queue);
return arr;
}
/* Driver Code */
int main() {
/* 初始化二元樹 */
// 這裡藉助了一個從陣列直接生成二元樹的函式
int nums[] = {1, 2, 3, 4, 5, 6, 7};
int size = sizeof(nums) / sizeof(int);
TreeNode *root = arrayToTree(nums, size);
printf("初始化二元樹\n");
printTree(root);
/* 層序走訪 */
// 需要傳入陣列的長度
int *arr = levelOrder(root, &size);
printf("層序走訪的節點列印序列 = ");
printArray(arr, size);
// 釋放記憶體
freeMemoryTree(root);
free(arr);
return 0;
}

View File

@@ -0,0 +1,75 @@
/**
* File: binary_tree_dfs.c
* Created Time: 2023-01-11
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
// 輔助陣列,用於儲存走訪序列
int arr[MAX_SIZE];
/* 前序走訪 */
void preOrder(TreeNode *root, int *size) {
if (root == NULL)
return;
// 訪問優先順序:根節點 -> 左子樹 -> 右子樹
arr[(*size)++] = root->val;
preOrder(root->left, size);
preOrder(root->right, size);
}
/* 中序走訪 */
void inOrder(TreeNode *root, int *size) {
if (root == NULL)
return;
// 訪問優先順序:左子樹 -> 根節點 -> 右子樹
inOrder(root->left, size);
arr[(*size)++] = root->val;
inOrder(root->right, size);
}
/* 後序走訪 */
void postOrder(TreeNode *root, int *size) {
if (root == NULL)
return;
// 訪問優先順序:左子樹 -> 右子樹 -> 根節點
postOrder(root->left, size);
postOrder(root->right, size);
arr[(*size)++] = root->val;
}
/* Driver Code */
int main() {
/* 初始化二元樹 */
// 這裡藉助了一個從陣列直接生成二元樹的函式
int nums[] = {1, 2, 3, 4, 5, 6, 7};
int size = sizeof(nums) / sizeof(int);
TreeNode *root = arrayToTree(nums, size);
printf("初始化二元樹\n");
printTree(root);
/* 前序走訪 */
// 初始化輔助陣列
size = 0;
preOrder(root, &size);
printf("前序走訪的節點列印序列 = ");
printArray(arr, size);
/* 中序走訪 */
size = 0;
inOrder(root, &size);
printf("中序走訪的節點列印序列 = ");
printArray(arr, size);
/* 後序走訪 */
size = 0;
postOrder(root, &size);
printf("後序走訪的節點列印序列 = ");
printArray(arr, size);
freeMemoryTree(root);
return 0;
}

View File

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

View File

@@ -0,0 +1,36 @@
/**
* File: common.h
* Created Time: 2022-12-20
* Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com)
*/
#ifndef C_INCLUDE_H
#define C_INCLUDE_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 // C_INCLUDE_H

View File

@@ -0,0 +1,35 @@
/**
* File: include_test.c
* Created Time: 2023-01-10
* Author: Reanon (793584285@qq.com)
*/
#include "common.h"
void testListNode() {
int nums[] = {2, 3, 5, 6, 7};
int size = sizeof(nums) / sizeof(int);
ListNode *head = arrToLinkedList(nums, size);
printLinkedList(head);
}
void testTreeNode() {
int nums[] = {1, 2, 3, INT_MAX, 5, 6, INT_MAX};
int size = sizeof(nums) / sizeof(int);
TreeNode *root = arrayToTree(nums, size);
// print tree
printTree(root);
// tree to arr
int *arr = treeToArray(root, &size);
printArray(arr, size);
}
int main(int argc, char *argv[]) {
printf("==testListNode==\n");
testListNode();
printf("==testTreeNode==\n");
testTreeNode();
return 0;
}

View File

@@ -0,0 +1,59 @@
/**
* File: list_node.h
* Created Time: 2023-01-09
* Author: Reanon (793584285@qq.com)
*/
#ifndef LIST_NODE_H
#define LIST_NODE_H
#ifdef __cplusplus
extern "C" {
#endif
/* 鏈結串列節點結構體 */
typedef struct ListNode {
int val; // 節點值
struct ListNode *next; // 指向下一節點的引用
} ListNode;
/* 建構子,初始化一個新節點 */
ListNode *newListNode(int val) {
ListNode *node;
node = (ListNode *)malloc(sizeof(ListNode));
node->val = val;
node->next = NULL;
return node;
}
/* 將陣列反序列化為鏈結串列 */
ListNode *arrToLinkedList(const int *arr, size_t size) {
if (size <= 0) {
return NULL;
}
ListNode *dummy = newListNode(0);
ListNode *node = dummy;
for (int i = 0; i < size; i++) {
node->next = newListNode(arr[i]);
node = node->next;
}
return dummy->next;
}
/* 釋放分配給鏈結串列的記憶體空間 */
void freeMemoryLinkedList(ListNode *cur) {
// 釋放記憶體
ListNode *pre;
while (cur != NULL) {
pre = cur;
cur = cur->next;
free(pre);
}
}
#ifdef __cplusplus
}
#endif
#endif // LIST_NODE_H

View File

@@ -0,0 +1,131 @@
/**
* File: print_util.h
* Created Time: 2022-12-21
* Author: MolDum (moldum@163.com), Reanon (793584285@qq.com)
*/
#ifndef PRINT_UTIL_H
#define PRINT_UTIL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list_node.h"
#include "tree_node.h"
#ifdef __cplusplus
extern "C" {
#endif
/* 列印陣列 */
void printArray(int arr[], int size) {
if (arr == NULL || size == 0) {
printf("[]");
return;
}
printf("[");
for (int i = 0; i < size - 1; i++) {
printf("%d, ", arr[i]);
}
printf("%d]\n", arr[size - 1]);
}
/* 列印陣列 */
void printArrayFloat(float arr[], int size) {
if (arr == NULL || size == 0) {
printf("[]");
return;
}
printf("[");
for (int i = 0; i < size - 1; i++) {
printf("%.2f, ", arr[i]);
}
printf("%.2f]\n", arr[size - 1]);
}
/* 列印鏈結串列 */
void printLinkedList(ListNode *node) {
if (node == NULL) {
return;
}
while (node->next != NULL) {
printf("%d -> ", node->val);
node = node->next;
}
printf("%d\n", node->val);
}
typedef struct Trunk {
struct Trunk *prev;
char *str;
} Trunk;
Trunk *newTrunk(Trunk *prev, char *str) {
Trunk *trunk = (Trunk *)malloc(sizeof(Trunk));
trunk->prev = prev;
trunk->str = (char *)malloc(sizeof(char) * 10);
strcpy(trunk->str, str);
return trunk;
}
void showTrunks(Trunk *trunk) {
if (trunk == NULL) {
return;
}
showTrunks(trunk->prev);
printf("%s", trunk->str);
}
/**
* 列印二元樹
* This tree printer is borrowed from 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

View File

@@ -0,0 +1,107 @@
/**
* File: tree_node.h
* Created Time: 2023-01-09
* Author: Reanon (793584285@qq.com)
*/
#ifndef TREE_NODE_H
#define TREE_NODE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <limits.h>
#define MAX_NODE_SIZE 5000
/* 二元樹節點結構體 */
typedef struct TreeNode {
int val; // 節點值
int height; // 節點高度
struct TreeNode *left; // 左子節點指標
struct TreeNode *right; // 右子節點指標
} TreeNode;
/* 建構子 */
TreeNode *newTreeNode(int val) {
TreeNode *node;
node = (TreeNode *)malloc(sizeof(TreeNode));
node->val = val;
node->height = 0;
node->left = NULL;
node->right = NULL;
return node;
}
// 序列化編碼規則請參考:
// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
// 二元樹的陣列表示:
// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
// 二元樹的鏈結串列表示:
// /——— 15
// /——— 7
// /——— 3
// | \——— 6
// | \——— 12
// ——— 1
// \——— 2
// | /——— 9
// \——— 4
// \——— 8
/* 將串列反序列化為二元樹:遞迴 */
TreeNode *arrayToTreeDFS(int *arr, int size, int i) {
if (i < 0 || i >= size || arr[i] == INT_MAX) {
return NULL;
}
TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode));
root->val = arr[i];
root->left = arrayToTreeDFS(arr, size, 2 * i + 1);
root->right = arrayToTreeDFS(arr, size, 2 * i + 2);
return root;
}
/* 將串列反序列化為二元樹 */
TreeNode *arrayToTree(int *arr, int size) {
return arrayToTreeDFS(arr, size, 0);
}
/* 將二元樹序列化為串列:遞迴 */
void treeToArrayDFS(TreeNode *root, int i, int *res, int *size) {
if (root == NULL) {
return;
}
while (i >= *size) {
res = realloc(res, (*size + 1) * sizeof(int));
res[*size] = INT_MAX;
(*size)++;
}
res[i] = root->val;
treeToArrayDFS(root->left, 2 * i + 1, res, size);
treeToArrayDFS(root->right, 2 * i + 2, res, size);
}
/* 將二元樹序列化為串列 */
int *treeToArray(TreeNode *root, int *size) {
*size = 0;
int *res = NULL;
treeToArrayDFS(root, 0, res, size);
return res;
}
/* 釋放二元樹記憶體 */
void freeMemoryTree(TreeNode *root) {
if (root == NULL)
return;
freeMemoryTree(root->left);
freeMemoryTree(root->right);
free(root);
}
#ifdef __cplusplus
}
#endif
#endif // TREE_NODE_H

File diff suppressed because it is too large Load Diff

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