mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-13 17:09:46 +08:00
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:
@@ -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>
|
||||
|
||||
@@ -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
82
zh-hant/README.md
Normal 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¢er=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
35
zh-hant/codes/Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
# Use Ubuntu image from Aliyun
|
||||
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
|
||||
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
|
||||
sed -i 's/ports.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && apt-get install -y wget
|
||||
|
||||
# Install languages environment
|
||||
ARG LANGS
|
||||
RUN for LANG in $LANGS; do \
|
||||
case $LANG in \
|
||||
python) \
|
||||
apt-get install -y python3.10 && \
|
||||
update-alternatives --install /usr/bin/python python /usr/bin/python3.10 1 ;; \
|
||||
cpp) \
|
||||
apt-get install -y g++ gdb ;; \
|
||||
java) \
|
||||
apt-get install -y openjdk-17-jdk ;; \
|
||||
csharp) \
|
||||
wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \
|
||||
dpkg -i packages-microsoft-prod.deb && \
|
||||
apt-get update && \
|
||||
apt-get install -y dotnet-sdk-8.0 ;; \
|
||||
# More languages...
|
||||
*) \
|
||||
echo "Warning: No installation workflow for $LANG" ;; \
|
||||
esac \
|
||||
done
|
||||
|
||||
WORKDIR /codes
|
||||
COPY ./ ./
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
9
zh-hant/codes/c/.gitignore
vendored
Normal file
9
zh-hant/codes/c/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# Ignore all
|
||||
*
|
||||
# Unignore all with extensions
|
||||
!*.*
|
||||
# Unignore all dirs
|
||||
!*/
|
||||
*.dSYM/
|
||||
|
||||
build/
|
||||
20
zh-hant/codes/c/CMakeLists.txt
Normal file
20
zh-hant/codes/c/CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(hello_algo C)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
include_directories(./include)
|
||||
|
||||
add_subdirectory(chapter_computational_complexity)
|
||||
add_subdirectory(chapter_array_and_linkedlist)
|
||||
add_subdirectory(chapter_stack_and_queue)
|
||||
add_subdirectory(chapter_hashing)
|
||||
add_subdirectory(chapter_tree)
|
||||
add_subdirectory(chapter_heap)
|
||||
add_subdirectory(chapter_graph)
|
||||
add_subdirectory(chapter_searching)
|
||||
add_subdirectory(chapter_sorting)
|
||||
add_subdirectory(chapter_divide_and_conquer)
|
||||
add_subdirectory(chapter_backtracking)
|
||||
add_subdirectory(chapter_dynamic_programming)
|
||||
add_subdirectory(chapter_greedy)
|
||||
@@ -0,0 +1,3 @@
|
||||
add_executable(array array.c)
|
||||
add_executable(linked_list linked_list.c)
|
||||
add_executable(my_list my_list.c)
|
||||
114
zh-hant/codes/c/chapter_array_and_linkedlist/array.c
Normal file
114
zh-hant/codes/c/chapter_array_and_linkedlist/array.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* File: array.c
|
||||
* Created Time: 2022-12-20
|
||||
* Author: MolDuM (moldum@163.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 隨機訪問元素 */
|
||||
int randomAccess(int *nums, int size) {
|
||||
// 在區間 [0, size) 中隨機抽取一個數字
|
||||
int randomIndex = rand() % size;
|
||||
// 獲取並返回隨機元素
|
||||
int randomNum = nums[randomIndex];
|
||||
return randomNum;
|
||||
}
|
||||
|
||||
/* 擴展陣列長度 */
|
||||
int *extend(int *nums, int size, int enlarge) {
|
||||
// 初始化一個擴展長度後的陣列
|
||||
int *res = (int *)malloc(sizeof(int) * (size + enlarge));
|
||||
// 將原陣列中的所有元素複製到新陣列
|
||||
for (int i = 0; i < size; i++) {
|
||||
res[i] = nums[i];
|
||||
}
|
||||
// 初始化擴展後的空間
|
||||
for (int i = size; i < size + enlarge; i++) {
|
||||
res[i] = 0;
|
||||
}
|
||||
// 返回擴展後的新陣列
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 在陣列的索引 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;
|
||||
}
|
||||
89
zh-hant/codes/c/chapter_array_and_linkedlist/linked_list.c
Normal file
89
zh-hant/codes/c/chapter_array_and_linkedlist/linked_list.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* File: linked_list.c
|
||||
* Created Time: 2023-01-12
|
||||
* Author: Zero (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 在鏈結串列的節點 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;
|
||||
}
|
||||
163
zh-hant/codes/c/chapter_array_and_linkedlist/my_list.c
Normal file
163
zh-hant/codes/c/chapter_array_and_linkedlist/my_list.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* File: my_list.c
|
||||
* Created Time: 2023-01-12
|
||||
* Author: Zero (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 串列類別 */
|
||||
typedef struct {
|
||||
int *arr; // 陣列(儲存串列元素)
|
||||
int capacity; // 串列容量
|
||||
int size; // 串列大小
|
||||
int extendRatio; // 串列每次擴容的倍數
|
||||
} MyList;
|
||||
|
||||
void extendCapacity(MyList *nums);
|
||||
|
||||
/* 建構子 */
|
||||
MyList *newMyList() {
|
||||
MyList *nums = malloc(sizeof(MyList));
|
||||
nums->capacity = 10;
|
||||
nums->arr = malloc(sizeof(int) * nums->capacity);
|
||||
nums->size = 0;
|
||||
nums->extendRatio = 2;
|
||||
return nums;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delMyList(MyList *nums) {
|
||||
free(nums->arr);
|
||||
free(nums);
|
||||
}
|
||||
|
||||
/* 獲取串列長度 */
|
||||
int size(MyList *nums) {
|
||||
return nums->size;
|
||||
}
|
||||
|
||||
/* 獲取串列容量 */
|
||||
int capacity(MyList *nums) {
|
||||
return nums->capacity;
|
||||
}
|
||||
|
||||
/* 訪問元素 */
|
||||
int get(MyList *nums, int index) {
|
||||
assert(index >= 0 && index < nums->size);
|
||||
return nums->arr[index];
|
||||
}
|
||||
|
||||
/* 更新元素 */
|
||||
void set(MyList *nums, int index, int num) {
|
||||
assert(index >= 0 && index < nums->size);
|
||||
nums->arr[index] = num;
|
||||
}
|
||||
|
||||
/* 在尾部新增元素 */
|
||||
void add(MyList *nums, int num) {
|
||||
if (size(nums) == capacity(nums)) {
|
||||
extendCapacity(nums); // 擴容
|
||||
}
|
||||
nums->arr[size(nums)] = num;
|
||||
nums->size++;
|
||||
}
|
||||
|
||||
/* 在中間插入元素 */
|
||||
void insert(MyList *nums, int index, int num) {
|
||||
assert(index >= 0 && index < size(nums));
|
||||
// 元素數量超出容量時,觸發擴容機制
|
||||
if (size(nums) == capacity(nums)) {
|
||||
extendCapacity(nums); // 擴容
|
||||
}
|
||||
for (int i = size(nums); i > index; --i) {
|
||||
nums->arr[i] = nums->arr[i - 1];
|
||||
}
|
||||
nums->arr[index] = num;
|
||||
nums->size++;
|
||||
}
|
||||
|
||||
/* 刪除元素 */
|
||||
// 注意:stdio.h 佔用了 remove 關鍵詞
|
||||
int removeItem(MyList *nums, int index) {
|
||||
assert(index >= 0 && index < size(nums));
|
||||
int num = nums->arr[index];
|
||||
for (int i = index; i < size(nums) - 1; i++) {
|
||||
nums->arr[i] = nums->arr[i + 1];
|
||||
}
|
||||
nums->size--;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* 串列擴容 */
|
||||
void extendCapacity(MyList *nums) {
|
||||
// 先分配空間
|
||||
int newCapacity = capacity(nums) * nums->extendRatio;
|
||||
int *extend = (int *)malloc(sizeof(int) * newCapacity);
|
||||
int *temp = nums->arr;
|
||||
|
||||
// 複製舊資料到新資料
|
||||
for (int i = 0; i < size(nums); i++)
|
||||
extend[i] = nums->arr[i];
|
||||
|
||||
// 釋放舊資料
|
||||
free(temp);
|
||||
|
||||
// 更新新資料
|
||||
nums->arr = extend;
|
||||
nums->capacity = newCapacity;
|
||||
}
|
||||
|
||||
/* 將串列轉換為 Array 用於列印 */
|
||||
int *toArray(MyList *nums) {
|
||||
return nums->arr;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化串列 */
|
||||
MyList *nums = newMyList();
|
||||
/* 在尾部新增元素 */
|
||||
add(nums, 1);
|
||||
add(nums, 3);
|
||||
add(nums, 2);
|
||||
add(nums, 5);
|
||||
add(nums, 4);
|
||||
printf("串列 nums = ");
|
||||
printArray(toArray(nums), size(nums));
|
||||
printf("容量 = %d ,長度 = %d\n", capacity(nums), size(nums));
|
||||
|
||||
/* 在中間插入元素 */
|
||||
insert(nums, 3, 6);
|
||||
printf("在索引 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;
|
||||
}
|
||||
10
zh-hant/codes/c/chapter_backtracking/CMakeLists.txt
Normal file
10
zh-hant/codes/c/chapter_backtracking/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
add_executable(permutations_i permutations_i.c)
|
||||
add_executable(permutations_ii permutations_ii.c)
|
||||
add_executable(preorder_traversal_i_compact preorder_traversal_i_compact.c)
|
||||
add_executable(preorder_traversal_ii_compact preorder_traversal_ii_compact.c)
|
||||
add_executable(preorder_traversal_iii_compact preorder_traversal_iii_compact.c)
|
||||
add_executable(preorder_traversal_iii_template preorder_traversal_iii_template.c)
|
||||
add_executable(subset_sum_i_naive subset_sum_i_naive.c)
|
||||
add_executable(subset_sum_i subset_sum_i.c)
|
||||
add_executable(subset_sum_ii subset_sum_ii.c)
|
||||
add_executable(n_queens n_queens.c)
|
||||
95
zh-hant/codes/c/chapter_backtracking/n_queens.c
Normal file
95
zh-hant/codes/c/chapter_backtracking/n_queens.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* File : n_queens.c
|
||||
* Created Time: 2023-09-25
|
||||
* Author : lucas (superrat6@gmail.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
#define MAX_SIZE 100
|
||||
|
||||
/* 回溯演算法:n 皇后 */
|
||||
void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE],
|
||||
bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) {
|
||||
// 當放置完所有行時,記錄解
|
||||
if (row == n) {
|
||||
res[*resSize] = (char **)malloc(sizeof(char *) * n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1));
|
||||
strcpy(res[*resSize][i], state[i]);
|
||||
}
|
||||
(*resSize)++;
|
||||
return;
|
||||
}
|
||||
// 走訪所有列
|
||||
for (int col = 0; col < n; col++) {
|
||||
// 計算該格子對應的主對角線和次對角線
|
||||
int diag1 = row - col + n - 1;
|
||||
int diag2 = row + col;
|
||||
// 剪枝:不允許該格子所在列、主對角線、次對角線上存在皇后
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// 嘗試:將皇后放置在該格子
|
||||
state[row][col] = 'Q';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true;
|
||||
// 放置下一行
|
||||
backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2);
|
||||
// 回退:將該格子恢復為空位
|
||||
state[row][col] = '#';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 求解 n 皇后 */
|
||||
char ***nQueens(int n, int *returnSize) {
|
||||
char state[MAX_SIZE][MAX_SIZE];
|
||||
// 初始化 n*n 大小的棋盤,其中 'Q' 代表皇后,'#' 代表空位
|
||||
for (int i = 0; i < n; ++i) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
state[i][j] = '#';
|
||||
}
|
||||
state[i][n] = '\0';
|
||||
}
|
||||
bool cols[MAX_SIZE] = {false}; // 記錄列是否有皇后
|
||||
bool diags1[2 * MAX_SIZE - 1] = {false}; // 記錄主對角線上是否有皇后
|
||||
bool diags2[2 * MAX_SIZE - 1] = {false}; // 記錄次對角線上是否有皇后
|
||||
|
||||
char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE);
|
||||
*returnSize = 0;
|
||||
backtrack(0, n, state, res, returnSize, cols, diags1, diags2);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int n = 4;
|
||||
int returnSize;
|
||||
char ***res = nQueens(n, &returnSize);
|
||||
|
||||
printf("輸入棋盤長寬為%d\n", n);
|
||||
printf("皇后放置方案共有 %d 種\n", returnSize);
|
||||
for (int i = 0; i < returnSize; ++i) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
printf("[");
|
||||
for (int k = 0; res[i][j][k] != '\0'; ++k) {
|
||||
printf("%c", res[i][j][k]);
|
||||
if (res[i][j][k + 1] != '\0') {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
printf("---------------------\n");
|
||||
}
|
||||
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i < returnSize; ++i) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
free(res[i][j]);
|
||||
}
|
||||
free(res[i]);
|
||||
}
|
||||
free(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
79
zh-hant/codes/c/chapter_backtracking/permutations_i.c
Normal file
79
zh-hant/codes/c/chapter_backtracking/permutations_i.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* File: permutations_i.c
|
||||
* Created Time: 2023-06-04
|
||||
* Author: Gonglja (glj0@outlook.com), krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設最多有 1000 個排列
|
||||
#define MAX_SIZE 1000
|
||||
|
||||
/* 回溯演算法:全排列 I */
|
||||
void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
|
||||
// 當狀態長度等於元素數量時,記錄解
|
||||
if (stateSize == choicesSize) {
|
||||
res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
|
||||
for (int i = 0; i < choicesSize; i++) {
|
||||
res[*resSize][i] = state[i];
|
||||
}
|
||||
(*resSize)++;
|
||||
return;
|
||||
}
|
||||
// 走訪所有選擇
|
||||
for (int i = 0; i < choicesSize; i++) {
|
||||
int choice = choices[i];
|
||||
// 剪枝:不允許重複選擇元素
|
||||
if (!selected[i]) {
|
||||
// 嘗試:做出選擇,更新狀態
|
||||
selected[i] = true;
|
||||
state[stateSize] = choice;
|
||||
// 進行下一輪選擇
|
||||
backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
|
||||
// 回退:撤銷選擇,恢復到之前的狀態
|
||||
selected[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全排列 I */
|
||||
int **permutationsI(int *nums, int numsSize, int *returnSize) {
|
||||
int *state = (int *)malloc(numsSize * sizeof(int));
|
||||
bool *selected = (bool *)malloc(numsSize * sizeof(bool));
|
||||
for (int i = 0; i < numsSize; i++) {
|
||||
selected[i] = false;
|
||||
}
|
||||
int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
|
||||
*returnSize = 0;
|
||||
|
||||
backtrack(state, 0, nums, numsSize, selected, res, returnSize);
|
||||
|
||||
free(state);
|
||||
free(selected);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int nums[] = {1, 2, 3};
|
||||
int numsSize = sizeof(nums) / sizeof(nums[0]);
|
||||
int returnSize;
|
||||
|
||||
int **res = permutationsI(nums, numsSize, &returnSize);
|
||||
|
||||
printf("輸入陣列 nums = ");
|
||||
printArray(nums, numsSize);
|
||||
printf("\n所有排列 res = \n");
|
||||
for (int i = 0; i < returnSize; i++) {
|
||||
printArray(res[i], numsSize);
|
||||
}
|
||||
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i < returnSize; i++) {
|
||||
free(res[i]);
|
||||
}
|
||||
free(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
81
zh-hant/codes/c/chapter_backtracking/permutations_ii.c
Normal file
81
zh-hant/codes/c/chapter_backtracking/permutations_ii.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* File: permutations_ii.c
|
||||
* Created Time: 2023-10-17
|
||||
* Author: krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設最多有 1000 個排列,元素最大為 1000
|
||||
#define MAX_SIZE 1000
|
||||
|
||||
/* 回溯演算法:全排列 II */
|
||||
void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
|
||||
// 當狀態長度等於元素數量時,記錄解
|
||||
if (stateSize == choicesSize) {
|
||||
res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
|
||||
for (int i = 0; i < choicesSize; i++) {
|
||||
res[*resSize][i] = state[i];
|
||||
}
|
||||
(*resSize)++;
|
||||
return;
|
||||
}
|
||||
// 走訪所有選擇
|
||||
bool duplicated[MAX_SIZE] = {false};
|
||||
for (int i = 0; i < choicesSize; i++) {
|
||||
int choice = choices[i];
|
||||
// 剪枝:不允許重複選擇元素 且 不允許重複選擇相等元素
|
||||
if (!selected[i] && !duplicated[choice]) {
|
||||
// 嘗試:做出選擇,更新狀態
|
||||
duplicated[choice] = true; // 記錄選擇過的元素值
|
||||
selected[i] = true;
|
||||
state[stateSize] = choice;
|
||||
// 進行下一輪選擇
|
||||
backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
|
||||
// 回退:撤銷選擇,恢復到之前的狀態
|
||||
selected[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全排列 II */
|
||||
int **permutationsII(int *nums, int numsSize, int *returnSize) {
|
||||
int *state = (int *)malloc(numsSize * sizeof(int));
|
||||
bool *selected = (bool *)malloc(numsSize * sizeof(bool));
|
||||
for (int i = 0; i < numsSize; i++) {
|
||||
selected[i] = false;
|
||||
}
|
||||
int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
|
||||
*returnSize = 0;
|
||||
|
||||
backtrack(state, 0, nums, numsSize, selected, res, returnSize);
|
||||
|
||||
free(state);
|
||||
free(selected);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int nums[] = {1, 1, 2};
|
||||
int numsSize = sizeof(nums) / sizeof(nums[0]);
|
||||
int returnSize;
|
||||
|
||||
int **res = permutationsII(nums, numsSize, &returnSize);
|
||||
|
||||
printf("輸入陣列 nums = ");
|
||||
printArray(nums, numsSize);
|
||||
printf("\n所有排列 res = \n");
|
||||
for (int i = 0; i < returnSize; i++) {
|
||||
printArray(res[i], numsSize);
|
||||
}
|
||||
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i < returnSize; i++) {
|
||||
free(res[i]);
|
||||
}
|
||||
free(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: preorder_traversal_i_compact.c
|
||||
* Created Time: 2023-05-10
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設結果長度不超過 100
|
||||
#define MAX_SIZE 100
|
||||
|
||||
TreeNode *res[MAX_SIZE];
|
||||
int resSize = 0;
|
||||
|
||||
/* 前序走訪:例題一 */
|
||||
void preOrder(TreeNode *root) {
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
if (root->val == 7) {
|
||||
// 記錄解
|
||||
res[resSize++] = root;
|
||||
}
|
||||
preOrder(root->left);
|
||||
preOrder(root->right);
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int arr[] = {1, 7, 3, 4, 5, 6, 7};
|
||||
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
|
||||
printf("\n初始化二元樹\n");
|
||||
printTree(root);
|
||||
|
||||
// 前序走訪
|
||||
preOrder(root);
|
||||
|
||||
printf("\n輸出所有值為 7 的節點\n");
|
||||
int *vals = malloc(resSize * sizeof(int));
|
||||
for (int i = 0; i < resSize; i++) {
|
||||
vals[i] = res[i]->val;
|
||||
}
|
||||
printArray(vals, resSize);
|
||||
|
||||
// 釋放記憶體
|
||||
freeMemoryTree(root);
|
||||
free(vals);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* File: preorder_traversal_ii_compact.c
|
||||
* Created Time: 2023-05-28
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設路徑和結果長度不超過 100
|
||||
#define MAX_SIZE 100
|
||||
#define MAX_RES_SIZE 100
|
||||
|
||||
TreeNode *path[MAX_SIZE];
|
||||
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
|
||||
int pathSize = 0, resSize = 0;
|
||||
|
||||
/* 前序走訪:例題二 */
|
||||
void preOrder(TreeNode *root) {
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
// 嘗試
|
||||
path[pathSize++] = root;
|
||||
if (root->val == 7) {
|
||||
// 記錄解
|
||||
for (int i = 0; i < pathSize; ++i) {
|
||||
res[resSize][i] = path[i];
|
||||
}
|
||||
resSize++;
|
||||
}
|
||||
preOrder(root->left);
|
||||
preOrder(root->right);
|
||||
// 回退
|
||||
pathSize--;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int arr[] = {1, 7, 3, 4, 5, 6, 7};
|
||||
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
|
||||
printf("\n初始化二元樹\n");
|
||||
printTree(root);
|
||||
|
||||
// 前序走訪
|
||||
preOrder(root);
|
||||
|
||||
printf("\n輸出所有根節點到節點 7 的路徑\n");
|
||||
for (int i = 0; i < resSize; ++i) {
|
||||
int *vals = malloc(MAX_SIZE * sizeof(int));
|
||||
int size = 0;
|
||||
for (int j = 0; res[i][j] != NULL; ++j) {
|
||||
vals[size++] = res[i][j]->val;
|
||||
}
|
||||
printArray(vals, size);
|
||||
free(vals);
|
||||
}
|
||||
|
||||
// 釋放記憶體
|
||||
freeMemoryTree(root);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* File: preorder_traversal_iii_compact.c
|
||||
* Created Time: 2023-06-04
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設路徑和結果長度不超過 100
|
||||
#define MAX_SIZE 100
|
||||
#define MAX_RES_SIZE 100
|
||||
|
||||
TreeNode *path[MAX_SIZE];
|
||||
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
|
||||
int pathSize = 0, resSize = 0;
|
||||
|
||||
/* 前序走訪:例題三 */
|
||||
void preOrder(TreeNode *root) {
|
||||
// 剪枝
|
||||
if (root == NULL || root->val == 3) {
|
||||
return;
|
||||
}
|
||||
// 嘗試
|
||||
path[pathSize++] = root;
|
||||
if (root->val == 7) {
|
||||
// 記錄解
|
||||
for (int i = 0; i < pathSize; i++) {
|
||||
res[resSize][i] = path[i];
|
||||
}
|
||||
resSize++;
|
||||
}
|
||||
preOrder(root->left);
|
||||
preOrder(root->right);
|
||||
// 回退
|
||||
pathSize--;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int arr[] = {1, 7, 3, 4, 5, 6, 7};
|
||||
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
|
||||
printf("\n初始化二元樹\n");
|
||||
printTree(root);
|
||||
|
||||
// 前序走訪
|
||||
preOrder(root);
|
||||
|
||||
printf("\n輸出所有根節點到節點 7 的路徑,要求路徑中不包含值為 3 的節點\n");
|
||||
for (int i = 0; i < resSize; ++i) {
|
||||
int *vals = malloc(MAX_SIZE * sizeof(int));
|
||||
int size = 0;
|
||||
for (int j = 0; res[i][j] != NULL; ++j) {
|
||||
vals[size++] = res[i][j]->val;
|
||||
}
|
||||
printArray(vals, size);
|
||||
free(vals);
|
||||
}
|
||||
|
||||
// 釋放記憶體
|
||||
freeMemoryTree(root);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* File: preorder_traversal_iii_template.c
|
||||
* Created Time: 2023-06-04
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設路徑和結果長度不超過 100
|
||||
#define MAX_SIZE 100
|
||||
#define MAX_RES_SIZE 100
|
||||
|
||||
TreeNode *path[MAX_SIZE];
|
||||
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
|
||||
int pathSize = 0, resSize = 0;
|
||||
|
||||
/* 判斷當前狀態是否為解 */
|
||||
bool isSolution(void) {
|
||||
return pathSize > 0 && path[pathSize - 1]->val == 7;
|
||||
}
|
||||
|
||||
/* 記錄解 */
|
||||
void recordSolution(void) {
|
||||
for (int i = 0; i < pathSize; i++) {
|
||||
res[resSize][i] = path[i];
|
||||
}
|
||||
resSize++;
|
||||
}
|
||||
|
||||
/* 判斷在當前狀態下,該選擇是否合法 */
|
||||
bool isValid(TreeNode *choice) {
|
||||
return choice != NULL && choice->val != 3;
|
||||
}
|
||||
|
||||
/* 更新狀態 */
|
||||
void makeChoice(TreeNode *choice) {
|
||||
path[pathSize++] = choice;
|
||||
}
|
||||
|
||||
/* 恢復狀態 */
|
||||
void undoChoice(void) {
|
||||
pathSize--;
|
||||
}
|
||||
|
||||
/* 回溯演算法:例題三 */
|
||||
void backtrack(TreeNode *choices[2]) {
|
||||
// 檢查是否為解
|
||||
if (isSolution()) {
|
||||
// 記錄解
|
||||
recordSolution();
|
||||
}
|
||||
// 走訪所有選擇
|
||||
for (int i = 0; i < 2; i++) {
|
||||
TreeNode *choice = choices[i];
|
||||
// 剪枝:檢查選擇是否合法
|
||||
if (isValid(choice)) {
|
||||
// 嘗試:做出選擇,更新狀態
|
||||
makeChoice(choice);
|
||||
// 進行下一輪選擇
|
||||
TreeNode *nextChoices[2] = {choice->left, choice->right};
|
||||
backtrack(nextChoices);
|
||||
// 回退:撤銷選擇,恢復到之前的狀態
|
||||
undoChoice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int arr[] = {1, 7, 3, 4, 5, 6, 7};
|
||||
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
|
||||
printf("\n初始化二元樹\n");
|
||||
printTree(root);
|
||||
|
||||
// 回溯演算法
|
||||
TreeNode *choices[2] = {root, NULL};
|
||||
backtrack(choices);
|
||||
|
||||
printf("\n輸出所有根節點到節點 7 的路徑,要求路徑中不包含值為 3 的節點\n");
|
||||
for (int i = 0; i < resSize; ++i) {
|
||||
int *vals = malloc(MAX_SIZE * sizeof(int));
|
||||
int size = 0;
|
||||
for (int j = 0; res[i][j] != NULL; ++j) {
|
||||
vals[size++] = res[i][j]->val;
|
||||
}
|
||||
printArray(vals, size);
|
||||
free(vals);
|
||||
}
|
||||
|
||||
// 釋放記憶體
|
||||
freeMemoryTree(root);
|
||||
return 0;
|
||||
}
|
||||
78
zh-hant/codes/c/chapter_backtracking/subset_sum_i.c
Normal file
78
zh-hant/codes/c/chapter_backtracking/subset_sum_i.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* File: subset_sum_i.c
|
||||
* Created Time: 2023-07-29
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
#define MAX_SIZE 100
|
||||
#define MAX_RES_SIZE 100
|
||||
|
||||
// 狀態(子集)
|
||||
int state[MAX_SIZE];
|
||||
int stateSize = 0;
|
||||
|
||||
// 結果串列(子集串列)
|
||||
int res[MAX_RES_SIZE][MAX_SIZE];
|
||||
int resColSizes[MAX_RES_SIZE];
|
||||
int resSize = 0;
|
||||
|
||||
/* 回溯演算法:子集和 I */
|
||||
void backtrack(int target, int *choices, int choicesSize, int start) {
|
||||
// 子集和等於 target 時,記錄解
|
||||
if (target == 0) {
|
||||
for (int i = 0; i < stateSize; ++i) {
|
||||
res[resSize][i] = state[i];
|
||||
}
|
||||
resColSizes[resSize++] = stateSize;
|
||||
return;
|
||||
}
|
||||
// 走訪所有選擇
|
||||
// 剪枝二:從 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;
|
||||
}
|
||||
69
zh-hant/codes/c/chapter_backtracking/subset_sum_i_naive.c
Normal file
69
zh-hant/codes/c/chapter_backtracking/subset_sum_i_naive.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* File: subset_sum_i_naive.c
|
||||
* Created Time: 2023-07-28
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
#define MAX_SIZE 100
|
||||
#define MAX_RES_SIZE 100
|
||||
|
||||
// 狀態(子集)
|
||||
int state[MAX_SIZE];
|
||||
int stateSize = 0;
|
||||
|
||||
// 結果串列(子集串列)
|
||||
int res[MAX_RES_SIZE][MAX_SIZE];
|
||||
int resColSizes[MAX_RES_SIZE];
|
||||
int resSize = 0;
|
||||
|
||||
/* 回溯演算法:子集和 I */
|
||||
void backtrack(int target, int total, int *choices, int choicesSize) {
|
||||
// 子集和等於 target 時,記錄解
|
||||
if (total == target) {
|
||||
for (int i = 0; i < stateSize; i++) {
|
||||
res[resSize][i] = state[i];
|
||||
}
|
||||
resColSizes[resSize++] = stateSize;
|
||||
return;
|
||||
}
|
||||
// 走訪所有選擇
|
||||
for (int i = 0; i < choicesSize; i++) {
|
||||
// 剪枝:若子集和超過 target ,則跳過該選擇
|
||||
if (total + choices[i] > target) {
|
||||
continue;
|
||||
}
|
||||
// 嘗試:做出選擇,更新元素和 total
|
||||
state[stateSize++] = choices[i];
|
||||
// 進行下一輪選擇
|
||||
backtrack(target, total + choices[i], choices, choicesSize);
|
||||
// 回退:撤銷選擇,恢復到之前的狀態
|
||||
stateSize--;
|
||||
}
|
||||
}
|
||||
|
||||
/* 求解子集和 I(包含重複子集) */
|
||||
void subsetSumINaive(int *nums, int numsSize, int target) {
|
||||
resSize = 0; // 初始化解的數量為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;
|
||||
}
|
||||
83
zh-hant/codes/c/chapter_backtracking/subset_sum_ii.c
Normal file
83
zh-hant/codes/c/chapter_backtracking/subset_sum_ii.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* File: subset_sum_ii.c
|
||||
* Created Time: 2023-07-29
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
#define MAX_SIZE 100
|
||||
#define MAX_RES_SIZE 100
|
||||
|
||||
// 狀態(子集)
|
||||
int state[MAX_SIZE];
|
||||
int stateSize = 0;
|
||||
|
||||
// 結果串列(子集串列)
|
||||
int res[MAX_RES_SIZE][MAX_SIZE];
|
||||
int resColSizes[MAX_RES_SIZE];
|
||||
int resSize = 0;
|
||||
|
||||
/* 回溯演算法:子集和 II */
|
||||
void backtrack(int target, int *choices, int choicesSize, int start) {
|
||||
// 子集和等於 target 時,記錄解
|
||||
if (target == 0) {
|
||||
for (int i = 0; i < stateSize; i++) {
|
||||
res[resSize][i] = state[i];
|
||||
}
|
||||
resColSizes[resSize++] = stateSize;
|
||||
return;
|
||||
}
|
||||
// 走訪所有選擇
|
||||
// 剪枝二:從 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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
add_executable(iteration iteration.c)
|
||||
add_executable(recursion recursion.c)
|
||||
add_executable(time_complexity time_complexity.c)
|
||||
add_executable(worst_best_time_complexity worst_best_time_complexity.c)
|
||||
add_executable(space_complexity space_complexity.c)
|
||||
81
zh-hant/codes/c/chapter_computational_complexity/iteration.c
Normal file
81
zh-hant/codes/c/chapter_computational_complexity/iteration.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* File: iteration.c
|
||||
* Created Time: 2023-09-09
|
||||
* Author: Gonglja (glj0@outlook.com), MwumLi (mwumli@hotmail.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* for 迴圈 */
|
||||
int forLoop(int n) {
|
||||
int res = 0;
|
||||
// 迴圈求和 1, 2, ..., n-1, n
|
||||
for (int i = 1; i <= n; i++) {
|
||||
res += i;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* while 迴圈 */
|
||||
int whileLoop(int n) {
|
||||
int res = 0;
|
||||
int i = 1; // 初始化條件變數
|
||||
// 迴圈求和 1, 2, ..., n-1, n
|
||||
while (i <= n) {
|
||||
res += i;
|
||||
i++; // 更新條件變數
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* while 迴圈(兩次更新) */
|
||||
int whileLoopII(int n) {
|
||||
int res = 0;
|
||||
int i = 1; // 初始化條件變數
|
||||
// 迴圈求和 1, 4, 10, ...
|
||||
while (i <= n) {
|
||||
res += i;
|
||||
// 更新條件變數
|
||||
i++;
|
||||
i *= 2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 雙層 for 迴圈 */
|
||||
char *nestedForLoop(int n) {
|
||||
// n * n 為對應點數量,"(i, j), " 對應字串長最大為 6+10*2,加上最後一個空字元 \0 的額外空間
|
||||
int size = n * n * 26 + 1;
|
||||
char *res = malloc(size * sizeof(char));
|
||||
// 迴圈 i = 1, 2, ..., n-1, n
|
||||
for (int i = 1; i <= n; i++) {
|
||||
// 迴圈 j = 1, 2, ..., n-1, n
|
||||
for (int j = 1; j <= n; j++) {
|
||||
char tmp[26];
|
||||
snprintf(tmp, sizeof(tmp), "(%d, %d), ", i, j);
|
||||
strncat(res, tmp, size - strlen(res) - 1);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int n = 5;
|
||||
int res;
|
||||
|
||||
res = forLoop(n);
|
||||
printf("\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;
|
||||
}
|
||||
77
zh-hant/codes/c/chapter_computational_complexity/recursion.c
Normal file
77
zh-hant/codes/c/chapter_computational_complexity/recursion.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* File: recursion.c
|
||||
* Created Time: 2023-09-09
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 遞迴 */
|
||||
int recur(int n) {
|
||||
// 終止條件
|
||||
if (n == 1)
|
||||
return 1;
|
||||
// 遞:遞迴呼叫
|
||||
int res = recur(n - 1);
|
||||
// 迴:返回結果
|
||||
return n + res;
|
||||
}
|
||||
|
||||
/* 使用迭代模擬遞迴 */
|
||||
int forLoopRecur(int n) {
|
||||
int stack[1000]; // 藉助一個大陣列來模擬堆疊
|
||||
int top = -1; // 堆疊頂索引
|
||||
int res = 0;
|
||||
// 遞:遞迴呼叫
|
||||
for (int i = n; i > 0; i--) {
|
||||
// 透過“入堆疊操作”模擬“遞”
|
||||
stack[1 + top++] = i;
|
||||
}
|
||||
// 迴:返回結果
|
||||
while (top >= 0) {
|
||||
// 透過“出堆疊操作”模擬“迴”
|
||||
res += stack[top--];
|
||||
}
|
||||
// res = 1+2+3+...+n
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 尾遞迴 */
|
||||
int tailRecur(int n, int res) {
|
||||
// 終止條件
|
||||
if (n == 0)
|
||||
return res;
|
||||
// 尾遞迴呼叫
|
||||
return tailRecur(n - 1, res + n);
|
||||
}
|
||||
|
||||
/* 費波那契數列:遞迴 */
|
||||
int fib(int n) {
|
||||
// 終止條件 f(1) = 0, f(2) = 1
|
||||
if (n == 1 || n == 2)
|
||||
return n - 1;
|
||||
// 遞迴呼叫 f(n) = f(n-1) + f(n-2)
|
||||
int res = fib(n - 1) + fib(n - 2);
|
||||
// 返回結果 f(n)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int n = 5;
|
||||
int res;
|
||||
|
||||
res = recur(n);
|
||||
printf("\n遞迴函式的求和結果 res = %d\n", res);
|
||||
|
||||
res = forLoopRecur(n);
|
||||
printf("\n使用迭代模擬遞迴求和結果 res = %d\n", res);
|
||||
|
||||
res = tailRecur(n, 0);
|
||||
printf("\n尾遞迴函式的求和結果 res = %d\n", res);
|
||||
|
||||
res = fib(n);
|
||||
printf("\n費波那契數列的第 %d 項為 %d\n", n, res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* File: time_complexity.c
|
||||
* Created Time: 2023-01-03
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 常數階 */
|
||||
int constant(int n) {
|
||||
int count = 0;
|
||||
int size = 100000;
|
||||
int i = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 線性階 */
|
||||
int linear(int n) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 線性階(走訪陣列) */
|
||||
int arrayTraversal(int *nums, int n) {
|
||||
int count = 0;
|
||||
// 迴圈次數與陣列長度成正比
|
||||
for (int i = 0; i < n; i++) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 平方階 */
|
||||
int quadratic(int n) {
|
||||
int count = 0;
|
||||
// 迴圈次數與資料大小 n 成平方關係
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 平方階(泡沫排序) */
|
||||
int bubbleSort(int *nums, int n) {
|
||||
int count = 0; // 計數器
|
||||
// 外迴圈:未排序區間為 [0, i]
|
||||
for (int i = n - 1; i > 0; i--) {
|
||||
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交換 nums[j] 與 nums[j + 1]
|
||||
int tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
count += 3; // 元素交換包含 3 個單元操作
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 指數階(迴圈實現) */
|
||||
int exponential(int n) {
|
||||
int count = 0;
|
||||
int bas = 1;
|
||||
// 細胞每輪一分為二,形成數列 1, 2, 4, 8, ..., 2^(n-1)
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < bas; j++) {
|
||||
count++;
|
||||
}
|
||||
bas *= 2;
|
||||
}
|
||||
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 指數階(遞迴實現) */
|
||||
int expRecur(int n) {
|
||||
if (n == 1)
|
||||
return 1;
|
||||
return expRecur(n - 1) + expRecur(n - 1) + 1;
|
||||
}
|
||||
|
||||
/* 對數階(迴圈實現) */
|
||||
int logarithmic(int n) {
|
||||
int count = 0;
|
||||
while (n > 1) {
|
||||
n = n / 2;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 對數階(遞迴實現) */
|
||||
int logRecur(int n) {
|
||||
if (n <= 1)
|
||||
return 0;
|
||||
return logRecur(n / 2) + 1;
|
||||
}
|
||||
|
||||
/* 線性對數階 */
|
||||
int linearLogRecur(int n) {
|
||||
if (n <= 1)
|
||||
return 1;
|
||||
int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);
|
||||
for (int i = 0; i < n; i++) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 階乘階(遞迴實現) */
|
||||
int factorialRecur(int n) {
|
||||
if (n == 0)
|
||||
return 1;
|
||||
int count = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
count += factorialRecur(n - 1);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main(int argc, char *argv[]) {
|
||||
// 可以修改 n 執行,體會一下各種複雜度的操作數量變化趨勢
|
||||
int n = 8;
|
||||
printf("輸入資料大小 n = %d\n", n);
|
||||
|
||||
int count = constant(n);
|
||||
printf("常數階的操作數量 = %d\n", count);
|
||||
|
||||
count = linear(n);
|
||||
printf("線性階的操作數量 = %d\n", count);
|
||||
// 分配堆積區記憶體(建立一維可變長陣列:陣列中元素數量為 n ,元素型別為 int )
|
||||
int *nums = (int *)malloc(n * sizeof(int));
|
||||
count = arrayTraversal(nums, n);
|
||||
printf("線性階(走訪陣列)的操作數量 = %d\n", count);
|
||||
|
||||
count = quadratic(n);
|
||||
printf("平方階的操作數量 = %d\n", count);
|
||||
for (int i = 0; i < n; i++) {
|
||||
nums[i] = n - i; // [n,n-1,...,2,1]
|
||||
}
|
||||
count = bubbleSort(nums, n);
|
||||
printf("平方階(泡沫排序)的操作數量 = %d\n", count);
|
||||
|
||||
count = exponential(n);
|
||||
printf("指數階(迴圈實現)的操作數量 = %d\n", count);
|
||||
count = expRecur(n);
|
||||
printf("指數階(遞迴實現)的操作數量 = %d\n", count);
|
||||
|
||||
count = logarithmic(n);
|
||||
printf("對數階(迴圈實現)的操作數量 = %d\n", count);
|
||||
count = logRecur(n);
|
||||
printf("對數階(遞迴實現)的操作數量 = %d\n", count);
|
||||
|
||||
count = linearLogRecur(n);
|
||||
printf("線性對數階(遞迴實現)的操作數量 = %d\n", count);
|
||||
|
||||
count = factorialRecur(n);
|
||||
printf("階乘階(遞迴實現)的操作數量 = %d\n", count);
|
||||
|
||||
// 釋放堆積區記憶體
|
||||
if (nums != NULL) {
|
||||
free(nums);
|
||||
nums = NULL;
|
||||
}
|
||||
getchar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* File: worst_best_time_complexity.c
|
||||
* Created Time: 2023-01-03
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 生成一個陣列,元素為 { 1, 2, ..., n },順序被打亂 */
|
||||
int *randomNumbers(int n) {
|
||||
// 分配堆積區記憶體(建立一維可變長陣列:陣列中元素數量為 n ,元素型別為 int )
|
||||
int *nums = (int *)malloc(n * sizeof(int));
|
||||
// 生成陣列 nums = { 1, 2, 3, ..., n }
|
||||
for (int i = 0; i < n; i++) {
|
||||
nums[i] = i + 1;
|
||||
}
|
||||
// 隨機打亂陣列元素
|
||||
for (int i = n - 1; i > 0; i--) {
|
||||
int j = rand() % (i + 1);
|
||||
int temp = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = temp;
|
||||
}
|
||||
return nums;
|
||||
}
|
||||
|
||||
/* 查詢陣列 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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* File: binary_search_recur.c
|
||||
* Created Time: 2023-10-01
|
||||
* Author: Zuoxun (845242523@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 二分搜尋:問題 f(i, j) */
|
||||
int dfs(int nums[], int target, int i, int j) {
|
||||
// 若區間為空,代表無目標元素,則返回 -1
|
||||
if (i > j) {
|
||||
return -1;
|
||||
}
|
||||
// 計算中點索引 m
|
||||
int m = (i + j) / 2;
|
||||
if (nums[m] < target) {
|
||||
// 遞迴子問題 f(m+1, j)
|
||||
return dfs(nums, target, m + 1, j);
|
||||
} else if (nums[m] > target) {
|
||||
// 遞迴子問題 f(i, m-1)
|
||||
return dfs(nums, target, i, m - 1);
|
||||
} else {
|
||||
// 找到目標元素,返回其索引
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
/* 二分搜尋 */
|
||||
int binarySearch(int nums[], int target, int numsSize) {
|
||||
int n = numsSize;
|
||||
// 求解問題 f(0, n-1)
|
||||
return dfs(nums, target, 0, n - 1);
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int target = 6;
|
||||
int nums[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
|
||||
int numsSize = sizeof(nums) / sizeof(nums[0]);
|
||||
|
||||
// 二分搜尋(雙閉區間)
|
||||
int index = binarySearch(nums, target, numsSize);
|
||||
printf("目標元素 6 的索引 = %d\n", index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
61
zh-hant/codes/c/chapter_divide_and_conquer/build_tree.c
Normal file
61
zh-hant/codes/c/chapter_divide_and_conquer/build_tree.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* File : build_tree.c
|
||||
* Created Time: 2023-10-16
|
||||
* Author : lucas (superrat6@gmail.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設所有元素都小於 1000
|
||||
#define MAX_SIZE 1000
|
||||
|
||||
/* 構建二元樹:分治 */
|
||||
TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) {
|
||||
// 子樹區間為空時終止
|
||||
if (r - l < 0)
|
||||
return NULL;
|
||||
// 初始化根節點
|
||||
TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode));
|
||||
root->val = preorder[i];
|
||||
root->left = NULL;
|
||||
root->right = NULL;
|
||||
// 查詢 m ,從而劃分左右子樹
|
||||
int m = inorderMap[preorder[i]];
|
||||
// 子問題:構建左子樹
|
||||
root->left = dfs(preorder, inorderMap, i + 1, l, m - 1, size);
|
||||
// 子問題:構建右子樹
|
||||
root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r, size);
|
||||
// 返回根節點
|
||||
return root;
|
||||
}
|
||||
|
||||
/* 構建二元樹 */
|
||||
TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) {
|
||||
// 初始化雜湊表,儲存 inorder 元素到索引的對映
|
||||
int *inorderMap = (int *)malloc(sizeof(int) * MAX_SIZE);
|
||||
for (int i = 0; i < inorderSize; i++) {
|
||||
inorderMap[inorder[i]] = i;
|
||||
}
|
||||
TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorderSize - 1, inorderSize);
|
||||
free(inorderMap);
|
||||
return root;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int preorder[] = {3, 9, 2, 1, 7};
|
||||
int inorder[] = {9, 3, 1, 2, 7};
|
||||
int preorderSize = sizeof(preorder) / sizeof(preorder[0]);
|
||||
int inorderSize = sizeof(inorder) / sizeof(inorder[0]);
|
||||
printf("前序走訪 = ");
|
||||
printArray(preorder, preorderSize);
|
||||
printf("中序走訪 = ");
|
||||
printArray(inorder, inorderSize);
|
||||
|
||||
TreeNode *root = buildTree(preorder, preorderSize, inorder, inorderSize);
|
||||
printf("構建的二元樹為:\n");
|
||||
printTree(root);
|
||||
|
||||
freeMemoryTree(root);
|
||||
return 0;
|
||||
}
|
||||
74
zh-hant/codes/c/chapter_divide_and_conquer/hanota.c
Normal file
74
zh-hant/codes/c/chapter_divide_and_conquer/hanota.c
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* File: hanota.c
|
||||
* Created Time: 2023-10-01
|
||||
* Author: Zuoxun (845242523@qq.com), lucas(superrat6@gmail.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設最多有 1000 個排列
|
||||
#define MAX_SIZE 1000
|
||||
|
||||
/* 移動一個圓盤 */
|
||||
void move(int *src, int *srcSize, int *tar, int *tarSize) {
|
||||
// 從 src 頂部拿出一個圓盤
|
||||
int pan = src[*srcSize - 1];
|
||||
src[*srcSize - 1] = 0;
|
||||
(*srcSize)--;
|
||||
// 將圓盤放入 tar 頂部
|
||||
tar[*tarSize] = pan;
|
||||
(*tarSize)++;
|
||||
}
|
||||
|
||||
/* 求解河內塔問題 f(i) */
|
||||
void dfs(int i, int *src, int *srcSize, int *buf, int *bufSize, int *tar, int *tarSize) {
|
||||
// 若 src 只剩下一個圓盤,則直接將其移到 tar
|
||||
if (i == 1) {
|
||||
move(src, srcSize, tar, tarSize);
|
||||
return;
|
||||
}
|
||||
// 子問題 f(i-1) :將 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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
add_executable(climbing_stairs_constraint_dp climbing_stairs_constraint_dp.c)
|
||||
add_executable(min_cost_climbing_stairs_dp min_cost_climbing_stairs_dp.c)
|
||||
add_executable(min_path_sum min_path_sum.c)
|
||||
add_executable(knapsack knapsack.c)
|
||||
add_executable(unbounded_knapsack unbounded_knapsack.c)
|
||||
add_executable(coin_change coin_change.c)
|
||||
add_executable(coin_change_ii coin_change_ii.c)
|
||||
add_executable(edit_distance edit_distance.c)
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* File: climbing_stairs_backtrack.c
|
||||
* Created Time: 2023-09-22
|
||||
* Author: huawuque404 (huawuque404@163.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 回溯 */
|
||||
void backtrack(int *choices, int state, int n, int *res, int len) {
|
||||
// 當爬到第 n 階時,方案數量加 1
|
||||
if (state == n)
|
||||
res[0]++;
|
||||
// 走訪所有選擇
|
||||
for (int i = 0; i < len; i++) {
|
||||
int choice = choices[i];
|
||||
// 剪枝:不允許越過第 n 階
|
||||
if (state + choice > n)
|
||||
continue;
|
||||
// 嘗試:做出選擇,更新狀態
|
||||
backtrack(choices, state + choice, n, res, len);
|
||||
// 回退
|
||||
}
|
||||
}
|
||||
|
||||
/* 爬樓梯:回溯 */
|
||||
int climbingStairsBacktrack(int n) {
|
||||
int choices[2] = {1, 2}; // 可選擇向上爬 1 階或 2 階
|
||||
int state = 0; // 從第 0 階開始爬
|
||||
int *res = (int *)malloc(sizeof(int));
|
||||
*res = 0; // 使用 res[0] 記錄方案數量
|
||||
int len = sizeof(choices) / sizeof(int);
|
||||
backtrack(choices, state, n, res, len);
|
||||
int result = *res;
|
||||
free(res);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int n = 9;
|
||||
|
||||
int res = climbingStairsBacktrack(n);
|
||||
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* File: climbing_stairs_constraint_dp.c
|
||||
* Created Time: 2023-10-02
|
||||
* Author: Zuoxun (845242523@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 帶約束爬樓梯:動態規劃 */
|
||||
int climbingStairsConstraintDP(int n) {
|
||||
if (n == 1 || n == 2) {
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用於儲存子問題的解
|
||||
int **dp = malloc((n + 1) * sizeof(int *));
|
||||
for (int i = 0; i <= n; i++) {
|
||||
dp[i] = calloc(3, sizeof(int));
|
||||
}
|
||||
// 初始狀態:預設最小子問題的解
|
||||
dp[1][1] = 1;
|
||||
dp[1][2] = 0;
|
||||
dp[2][1] = 0;
|
||||
dp[2][2] = 1;
|
||||
// 狀態轉移:從較小子問題逐步求解較大子問題
|
||||
for (int i = 3; i <= n; i++) {
|
||||
dp[i][1] = dp[i - 1][2];
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
|
||||
}
|
||||
int res = dp[n][1] + dp[n][2];
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i <= n; i++) {
|
||||
free(dp[i]);
|
||||
}
|
||||
free(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int n = 9;
|
||||
|
||||
int res = climbingStairsConstraintDP(n);
|
||||
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* File: climbing_stairs_dfs.c
|
||||
* Created Time: 2023-09-19
|
||||
* Author: huawuque404 (huawuque404@163.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 搜尋 */
|
||||
int dfs(int i) {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
if (i == 1 || i == 2)
|
||||
return i;
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
int count = dfs(i - 1) + dfs(i - 2);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 爬樓梯:搜尋 */
|
||||
int climbingStairsDFS(int n) {
|
||||
return dfs(n);
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int n = 9;
|
||||
|
||||
int res = climbingStairsDFS(n);
|
||||
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* File: climbing_stairs_dfs_mem.c
|
||||
* Created Time: 2023-09-19
|
||||
* Author: huawuque404 (huawuque404@163.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 記憶化搜尋 */
|
||||
int dfs(int i, int *mem) {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
if (i == 1 || i == 2)
|
||||
return i;
|
||||
// 若存在記錄 dp[i] ,則直接返回之
|
||||
if (mem[i] != -1)
|
||||
return mem[i];
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
int count = dfs(i - 1, mem) + dfs(i - 2, mem);
|
||||
// 記錄 dp[i]
|
||||
mem[i] = count;
|
||||
return count;
|
||||
}
|
||||
|
||||
/* 爬樓梯:記憶化搜尋 */
|
||||
int climbingStairsDFSMem(int n) {
|
||||
// mem[i] 記錄爬到第 i 階的方案總數,-1 代表無記錄
|
||||
int *mem = (int *)malloc((n + 1) * sizeof(int));
|
||||
for (int i = 0; i <= n; i++) {
|
||||
mem[i] = -1;
|
||||
}
|
||||
int result = dfs(n, mem);
|
||||
free(mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int n = 9;
|
||||
|
||||
int res = climbingStairsDFSMem(n);
|
||||
printf("爬 %d 階樓梯共有 %d 種方案\n", n, res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
88
zh-hant/codes/c/chapter_dynamic_programming/coin_change.c
Normal file
88
zh-hant/codes/c/chapter_dynamic_programming/coin_change.c
Normal 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;
|
||||
}
|
||||
81
zh-hant/codes/c/chapter_dynamic_programming/coin_change_ii.c
Normal file
81
zh-hant/codes/c/chapter_dynamic_programming/coin_change_ii.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* File: coin_change_ii.c
|
||||
* Created Time: 2023-10-02
|
||||
* Author: Zuoxun (845242523@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 零錢兌換 II:動態規劃 */
|
||||
int coinChangeIIDP(int coins[], int amt, int coinsSize) {
|
||||
int n = coinsSize;
|
||||
// 初始化 dp 表
|
||||
int **dp = malloc((n + 1) * sizeof(int *));
|
||||
for (int i = 0; i <= n; i++) {
|
||||
dp[i] = calloc(amt + 1, sizeof(int));
|
||||
}
|
||||
// 初始化首列
|
||||
for (int i = 0; i <= n; i++) {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
// 狀態轉移
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超過目標金額,則不選硬幣 i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// 不選和選硬幣 i 這兩種方案之和
|
||||
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
int res = dp[n][amt];
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i <= n; i++) {
|
||||
free(dp[i]);
|
||||
}
|
||||
free(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 零錢兌換 II:空間最佳化後的動態規劃 */
|
||||
int coinChangeIIDPComp(int coins[], int amt, int coinsSize) {
|
||||
int n = coinsSize;
|
||||
// 初始化 dp 表
|
||||
int *dp = calloc(amt + 1, sizeof(int));
|
||||
dp[0] = 1;
|
||||
// 狀態轉移
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超過目標金額,則不選硬幣 i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// 不選和選硬幣 i 這兩種方案之和
|
||||
dp[a] = dp[a] + dp[a - coins[i - 1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
int res = dp[amt];
|
||||
// 釋放記憶體
|
||||
free(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver code */
|
||||
int main() {
|
||||
int coins[] = {1, 2, 5};
|
||||
int coinsSize = sizeof(coins) / sizeof(coins[0]);
|
||||
int amt = 5;
|
||||
|
||||
// 動態規劃
|
||||
int res = coinChangeIIDP(coins, amt, coinsSize);
|
||||
printf("湊出目標金額的硬幣組合數量為 %d\n", res);
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = coinChangeIIDPComp(coins, amt, coinsSize);
|
||||
printf("湊出目標金額的硬幣組合數量為 %d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
159
zh-hant/codes/c/chapter_dynamic_programming/edit_distance.c
Normal file
159
zh-hant/codes/c/chapter_dynamic_programming/edit_distance.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* File: edit_distance.c
|
||||
* Created Time: 2023-10-02
|
||||
* Author: Zuoxun (845242523@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 求最小值 */
|
||||
int myMin(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/* 編輯距離:暴力搜尋 */
|
||||
int editDistanceDFS(char *s, char *t, int i, int j) {
|
||||
// 若 s 和 t 都為空,則返回 0
|
||||
if (i == 0 && j == 0)
|
||||
return 0;
|
||||
// 若 s 為空,則返回 t 長度
|
||||
if (i == 0)
|
||||
return j;
|
||||
// 若 t 為空,則返回 s 長度
|
||||
if (j == 0)
|
||||
return i;
|
||||
// 若兩字元相等,則直接跳過此兩字元
|
||||
if (s[i - 1] == t[j - 1])
|
||||
return editDistanceDFS(s, t, i - 1, j - 1);
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
int insert = editDistanceDFS(s, t, i, j - 1);
|
||||
int del = editDistanceDFS(s, t, i - 1, j);
|
||||
int replace = editDistanceDFS(s, t, i - 1, j - 1);
|
||||
// 返回最少編輯步數
|
||||
return myMin(myMin(insert, del), replace) + 1;
|
||||
}
|
||||
|
||||
/* 編輯距離:記憶化搜尋 */
|
||||
int editDistanceDFSMem(char *s, char *t, int memCols, int **mem, int i, int j) {
|
||||
// 若 s 和 t 都為空,則返回 0
|
||||
if (i == 0 && j == 0)
|
||||
return 0;
|
||||
// 若 s 為空,則返回 t 長度
|
||||
if (i == 0)
|
||||
return j;
|
||||
// 若 t 為空,則返回 s 長度
|
||||
if (j == 0)
|
||||
return i;
|
||||
// 若已有記錄,則直接返回之
|
||||
if (mem[i][j] != -1)
|
||||
return mem[i][j];
|
||||
// 若兩字元相等,則直接跳過此兩字元
|
||||
if (s[i - 1] == t[j - 1])
|
||||
return editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1);
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
int insert = editDistanceDFSMem(s, t, memCols, mem, i, j - 1);
|
||||
int del = editDistanceDFSMem(s, t, memCols, mem, i - 1, j);
|
||||
int replace = editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1);
|
||||
// 記錄並返回最少編輯步數
|
||||
mem[i][j] = myMin(myMin(insert, del), replace) + 1;
|
||||
return mem[i][j];
|
||||
}
|
||||
|
||||
/* 編輯距離:動態規劃 */
|
||||
int editDistanceDP(char *s, char *t, int n, int m) {
|
||||
int **dp = malloc((n + 1) * sizeof(int *));
|
||||
for (int i = 0; i <= n; i++) {
|
||||
dp[i] = calloc(m + 1, sizeof(int));
|
||||
}
|
||||
// 狀態轉移:首行首列
|
||||
for (int i = 1; i <= n; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (int j = 1; j <= m; j++) {
|
||||
dp[0][j] = j;
|
||||
}
|
||||
// 狀態轉移:其餘行和列
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= m; j++) {
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// 若兩字元相等,則直接跳過此兩字元
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
dp[i][j] = myMin(myMin(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
int res = dp[n][m];
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i <= n; i++) {
|
||||
free(dp[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 編輯距離:空間最佳化後的動態規劃 */
|
||||
int editDistanceDPComp(char *s, char *t, int n, int m) {
|
||||
int *dp = calloc(m + 1, sizeof(int));
|
||||
// 狀態轉移:首行
|
||||
for (int j = 1; j <= m; j++) {
|
||||
dp[j] = j;
|
||||
}
|
||||
// 狀態轉移:其餘行
|
||||
for (int i = 1; i <= n; i++) {
|
||||
// 狀態轉移:首列
|
||||
int leftup = dp[0]; // 暫存 dp[i-1, j-1]
|
||||
dp[0] = i;
|
||||
// 狀態轉移:其餘列
|
||||
for (int j = 1; j <= m; j++) {
|
||||
int temp = dp[j];
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// 若兩字元相等,則直接跳過此兩字元
|
||||
dp[j] = leftup;
|
||||
} else {
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
dp[j] = myMin(myMin(dp[j - 1], dp[j]), leftup) + 1;
|
||||
}
|
||||
leftup = temp; // 更新為下一輪的 dp[i-1, j-1]
|
||||
}
|
||||
}
|
||||
int res = dp[m];
|
||||
// 釋放記憶體
|
||||
free(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
char *s = "bag";
|
||||
char *t = "pack";
|
||||
int n = strlen(s), m = strlen(t);
|
||||
|
||||
// 暴力搜尋
|
||||
int res = editDistanceDFS(s, t, n, m);
|
||||
printf("將 %s 更改為 %s 最少需要編輯 %d 步\n", s, t, res);
|
||||
|
||||
// 記憶化搜尋
|
||||
int **mem = malloc((n + 1) * sizeof(int *));
|
||||
for (int i = 0; i <= n; i++) {
|
||||
mem[i] = malloc((m + 1) * sizeof(int));
|
||||
memset(mem[i], -1, (m + 1) * sizeof(int));
|
||||
}
|
||||
res = editDistanceDFSMem(s, t, m + 1, mem, n, m);
|
||||
printf("將 %s 更改為 %s 最少需要編輯 %d 步\n", s, t, res);
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i <= n; i++) {
|
||||
free(mem[i]);
|
||||
}
|
||||
free(mem);
|
||||
|
||||
// 動態規劃
|
||||
res = editDistanceDP(s, t, n, m);
|
||||
printf("將 %s 更改為 %s 最少需要編輯 %d 步\n", s, t, res);
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = editDistanceDPComp(s, t, n, m);
|
||||
printf("將 %s 更改為 %s 最少需要編輯 %d 步\n", s, t, res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
137
zh-hant/codes/c/chapter_dynamic_programming/knapsack.c
Normal file
137
zh-hant/codes/c/chapter_dynamic_programming/knapsack.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* File: knapsack.c
|
||||
* Created Time: 2023-10-02
|
||||
* Author: Zuoxun (845242523@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 求最大值 */
|
||||
int myMax(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
/* 0-1 背包:暴力搜尋 */
|
||||
int knapsackDFS(int wgt[], int val[], int i, int c) {
|
||||
// 若已選完所有物品或背包無剩餘容量,則返回價值 0
|
||||
if (i == 0 || c == 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若超過背包容量,則只能選擇不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFS(wgt, val, i - 1, c);
|
||||
}
|
||||
// 計算不放入和放入物品 i 的最大價值
|
||||
int no = knapsackDFS(wgt, val, i - 1, c);
|
||||
int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1];
|
||||
// 返回兩種方案中價值更大的那一個
|
||||
return myMax(no, yes);
|
||||
}
|
||||
|
||||
/* 0-1 背包:記憶化搜尋 */
|
||||
int knapsackDFSMem(int wgt[], int val[], int memCols, int **mem, int i, int c) {
|
||||
// 若已選完所有物品或背包無剩餘容量,則返回價值 0
|
||||
if (i == 0 || c == 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若已有記錄,則直接返回
|
||||
if (mem[i][c] != -1) {
|
||||
return mem[i][c];
|
||||
}
|
||||
// 若超過背包容量,則只能選擇不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFSMem(wgt, val, memCols, mem, i - 1, c);
|
||||
}
|
||||
// 計算不放入和放入物品 i 的最大價值
|
||||
int no = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c);
|
||||
int yes = knapsackDFSMem(wgt, val, memCols, mem, i - 1, c - wgt[i - 1]) + val[i - 1];
|
||||
// 記錄並返回兩種方案中價值更大的那一個
|
||||
mem[i][c] = myMax(no, yes);
|
||||
return mem[i][c];
|
||||
}
|
||||
|
||||
/* 0-1 背包:動態規劃 */
|
||||
int knapsackDP(int wgt[], int val[], int cap, int wgtSize) {
|
||||
int n = wgtSize;
|
||||
// 初始化 dp 表
|
||||
int **dp = malloc((n + 1) * sizeof(int *));
|
||||
for (int i = 0; i <= n; i++) {
|
||||
dp[i] = calloc(cap + 1, sizeof(int));
|
||||
}
|
||||
// 狀態轉移
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超過背包容量,則不選物品 i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[i][c] = myMax(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
int res = dp[n][cap];
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i <= n; i++) {
|
||||
free(dp[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 0-1 背包:空間最佳化後的動態規劃 */
|
||||
int knapsackDPComp(int wgt[], int val[], int cap, int wgtSize) {
|
||||
int n = wgtSize;
|
||||
// 初始化 dp 表
|
||||
int *dp = calloc(cap + 1, sizeof(int));
|
||||
// 狀態轉移
|
||||
for (int i = 1; i <= n; i++) {
|
||||
// 倒序走訪
|
||||
for (int c = cap; c >= 1; c--) {
|
||||
if (wgt[i - 1] <= c) {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
int res = dp[cap];
|
||||
// 釋放記憶體
|
||||
free(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int wgt[] = {10, 20, 30, 40, 50};
|
||||
int val[] = {50, 120, 150, 210, 240};
|
||||
int cap = 50;
|
||||
int n = sizeof(wgt) / sizeof(wgt[0]);
|
||||
int wgtSize = n;
|
||||
|
||||
// 暴力搜尋
|
||||
int res = knapsackDFS(wgt, val, n, cap);
|
||||
printf("不超過背包容量的最大物品價值為 %d\n", res);
|
||||
|
||||
// 記憶化搜尋
|
||||
int **mem = malloc((n + 1) * sizeof(int *));
|
||||
for (int i = 0; i <= n; i++) {
|
||||
mem[i] = malloc((cap + 1) * sizeof(int));
|
||||
memset(mem[i], -1, (cap + 1) * sizeof(int));
|
||||
}
|
||||
res = knapsackDFSMem(wgt, val, cap + 1, mem, n, cap);
|
||||
printf("不超過背包容量的最大物品價值為 %d\n", res);
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i <= n; i++) {
|
||||
free(mem[i]);
|
||||
}
|
||||
free(mem);
|
||||
|
||||
// 動態規劃
|
||||
res = knapsackDP(wgt, val, cap, wgtSize);
|
||||
printf("不超過背包容量的最大物品價值為 %d\n", res);
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = knapsackDPComp(wgt, val, cap, wgtSize);
|
||||
printf("不超過背包容量的最大物品價值為 %d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* File: min_cost_climbing_stairs_dp.c
|
||||
* Created Time: 2023-10-02
|
||||
* Author: Zuoxun (845242523@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 求最小值 */
|
||||
int myMin(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/* 爬樓梯最小代價:動態規劃 */
|
||||
int minCostClimbingStairsDP(int cost[], int costSize) {
|
||||
int n = costSize - 1;
|
||||
if (n == 1 || n == 2)
|
||||
return cost[n];
|
||||
// 初始化 dp 表,用於儲存子問題的解
|
||||
int *dp = calloc(n + 1, sizeof(int));
|
||||
// 初始狀態:預設最小子問題的解
|
||||
dp[1] = cost[1];
|
||||
dp[2] = cost[2];
|
||||
// 狀態轉移:從較小子問題逐步求解較大子問題
|
||||
for (int i = 3; i <= n; i++) {
|
||||
dp[i] = myMin(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
}
|
||||
int res = dp[n];
|
||||
// 釋放記憶體
|
||||
free(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 爬樓梯最小代價:空間最佳化後的動態規劃 */
|
||||
int minCostClimbingStairsDPComp(int cost[], int costSize) {
|
||||
int n = costSize - 1;
|
||||
if (n == 1 || n == 2)
|
||||
return cost[n];
|
||||
int a = cost[1], b = cost[2];
|
||||
for (int i = 3; i <= n; i++) {
|
||||
int tmp = b;
|
||||
b = myMin(a, tmp) + cost[i];
|
||||
a = tmp;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int cost[] = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1};
|
||||
int costSize = sizeof(cost) / sizeof(cost[0]);
|
||||
printf("輸入樓梯的代價串列為:");
|
||||
printArray(cost, costSize);
|
||||
|
||||
int res = minCostClimbingStairsDP(cost, costSize);
|
||||
printf("爬完樓梯的最低代價為 %d\n", res);
|
||||
|
||||
res = minCostClimbingStairsDPComp(cost, costSize);
|
||||
printf("爬完樓梯的最低代價為 %d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
134
zh-hant/codes/c/chapter_dynamic_programming/min_path_sum.c
Normal file
134
zh-hant/codes/c/chapter_dynamic_programming/min_path_sum.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* File: min_path_sum.c
|
||||
* Created Time: 2023-10-02
|
||||
* Author: Zuoxun (845242523@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設矩陣最大行列數為 100
|
||||
#define MAX_SIZE 100
|
||||
|
||||
/* 求最小值 */
|
||||
int myMin(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/* 最小路徑和:暴力搜尋 */
|
||||
int minPathSumDFS(int grid[MAX_SIZE][MAX_SIZE], int i, int j) {
|
||||
// 若為左上角單元格,則終止搜尋
|
||||
if (i == 0 && j == 0) {
|
||||
return grid[0][0];
|
||||
}
|
||||
// 若行列索引越界,則返回 +∞ 代價
|
||||
if (i < 0 || j < 0) {
|
||||
return INT_MAX;
|
||||
}
|
||||
// 計算從左上角到 (i-1, j) 和 (i, j-1) 的最小路徑代價
|
||||
int up = minPathSumDFS(grid, i - 1, j);
|
||||
int left = minPathSumDFS(grid, i, j - 1);
|
||||
// 返回從左上角到 (i, j) 的最小路徑代價
|
||||
return myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX;
|
||||
}
|
||||
|
||||
/* 最小路徑和:記憶化搜尋 */
|
||||
int minPathSumDFSMem(int grid[MAX_SIZE][MAX_SIZE], int mem[MAX_SIZE][MAX_SIZE], int i, int j) {
|
||||
// 若為左上角單元格,則終止搜尋
|
||||
if (i == 0 && j == 0) {
|
||||
return grid[0][0];
|
||||
}
|
||||
// 若行列索引越界,則返回 +∞ 代價
|
||||
if (i < 0 || j < 0) {
|
||||
return INT_MAX;
|
||||
}
|
||||
// 若已有記錄,則直接返回
|
||||
if (mem[i][j] != -1) {
|
||||
return mem[i][j];
|
||||
}
|
||||
// 左邊和上邊單元格的最小路徑代價
|
||||
int up = minPathSumDFSMem(grid, mem, i - 1, j);
|
||||
int left = minPathSumDFSMem(grid, mem, i, j - 1);
|
||||
// 記錄並返回左上角到 (i, j) 的最小路徑代價
|
||||
mem[i][j] = myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX;
|
||||
return mem[i][j];
|
||||
}
|
||||
|
||||
/* 最小路徑和:動態規劃 */
|
||||
int minPathSumDP(int grid[MAX_SIZE][MAX_SIZE], int n, int m) {
|
||||
// 初始化 dp 表
|
||||
int **dp = malloc(n * sizeof(int *));
|
||||
for (int i = 0; i < n; i++) {
|
||||
dp[i] = calloc(m, sizeof(int));
|
||||
}
|
||||
dp[0][0] = grid[0][0];
|
||||
// 狀態轉移:首行
|
||||
for (int j = 1; j < m; j++) {
|
||||
dp[0][j] = dp[0][j - 1] + grid[0][j];
|
||||
}
|
||||
// 狀態轉移:首列
|
||||
for (int i = 1; i < n; i++) {
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0];
|
||||
}
|
||||
// 狀態轉移:其餘行和列
|
||||
for (int i = 1; i < n; i++) {
|
||||
for (int j = 1; j < m; j++) {
|
||||
dp[i][j] = myMin(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
|
||||
}
|
||||
}
|
||||
int res = dp[n - 1][m - 1];
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i < n; i++) {
|
||||
free(dp[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 最小路徑和:空間最佳化後的動態規劃 */
|
||||
int minPathSumDPComp(int grid[MAX_SIZE][MAX_SIZE], int n, int m) {
|
||||
// 初始化 dp 表
|
||||
int *dp = calloc(m, sizeof(int));
|
||||
// 狀態轉移:首行
|
||||
dp[0] = grid[0][0];
|
||||
for (int j = 1; j < m; j++) {
|
||||
dp[j] = dp[j - 1] + grid[0][j];
|
||||
}
|
||||
// 狀態轉移:其餘行
|
||||
for (int i = 1; i < n; i++) {
|
||||
// 狀態轉移:首列
|
||||
dp[0] = dp[0] + grid[i][0];
|
||||
// 狀態轉移:其餘列
|
||||
for (int j = 1; j < m; j++) {
|
||||
dp[j] = myMin(dp[j - 1], dp[j]) + grid[i][j];
|
||||
}
|
||||
}
|
||||
int res = dp[m - 1];
|
||||
// 釋放記憶體
|
||||
free(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int grid[MAX_SIZE][MAX_SIZE] = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}};
|
||||
int n = 4, m = 4; // 矩陣容量為 MAX_SIZE * MAX_SIZE ,有效行列數為 n * m
|
||||
|
||||
// 暴力搜尋
|
||||
int res = minPathSumDFS(grid, n - 1, m - 1);
|
||||
printf("從左上角到右下角的最小路徑和為 %d\n", res);
|
||||
|
||||
// 記憶化搜尋
|
||||
int mem[MAX_SIZE][MAX_SIZE];
|
||||
memset(mem, -1, sizeof(mem));
|
||||
res = minPathSumDFSMem(grid, mem, n - 1, m - 1);
|
||||
printf("從左上角到右下角的最小路徑和為 %d\n", res);
|
||||
|
||||
// 動態規劃
|
||||
res = minPathSumDP(grid, n, m);
|
||||
printf("從左上角到右下角的最小路徑和為 %d\n", res);
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = minPathSumDPComp(grid, n, m);
|
||||
printf("從左上角到右下角的最小路徑和為 %d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* File: unbounded_knapsack.c
|
||||
* Created Time: 2023-10-02
|
||||
* Author: Zuoxun (845242523@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 求最大值 */
|
||||
int myMax(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
/* 完全背包:動態規劃 */
|
||||
int unboundedKnapsackDP(int wgt[], int val[], int cap, int wgtSize) {
|
||||
int n = wgtSize;
|
||||
// 初始化 dp 表
|
||||
int **dp = malloc((n + 1) * sizeof(int *));
|
||||
for (int i = 0; i <= n; i++) {
|
||||
dp[i] = calloc(cap + 1, sizeof(int));
|
||||
}
|
||||
// 狀態轉移
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超過背包容量,則不選物品 i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[i][c] = myMax(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
int res = dp[n][cap];
|
||||
// 釋放記憶體
|
||||
for (int i = 0; i <= n; i++) {
|
||||
free(dp[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 完全背包:空間最佳化後的動態規劃 */
|
||||
int unboundedKnapsackDPComp(int wgt[], int val[], int cap, int wgtSize) {
|
||||
int n = wgtSize;
|
||||
// 初始化 dp 表
|
||||
int *dp = calloc(cap + 1, sizeof(int));
|
||||
// 狀態轉移
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超過背包容量,則不選物品 i
|
||||
dp[c] = dp[c];
|
||||
} else {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
int res = dp[cap];
|
||||
// 釋放記憶體
|
||||
free(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver code */
|
||||
int main() {
|
||||
int wgt[] = {1, 2, 3};
|
||||
int val[] = {5, 11, 15};
|
||||
int wgtSize = sizeof(wgt) / sizeof(wgt[0]);
|
||||
int cap = 4;
|
||||
|
||||
// 動態規劃
|
||||
int res = unboundedKnapsackDP(wgt, val, cap, wgtSize);
|
||||
printf("不超過背包容量的最大物品價值為 %d\n", res);
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = unboundedKnapsackDPComp(wgt, val, cap, wgtSize);
|
||||
printf("不超過背包容量的最大物品價值為 %d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
4
zh-hant/codes/c/chapter_graph/CMakeLists.txt
Normal file
4
zh-hant/codes/c/chapter_graph/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
add_executable(graph_adjacency_matrix graph_adjacency_matrix.c)
|
||||
add_executable(graph_adjacency_list_test graph_adjacency_list_test.c)
|
||||
add_executable(graph_bfs graph_bfs.c)
|
||||
add_executable(graph_dfs graph_dfs.c)
|
||||
171
zh-hant/codes/c/chapter_graph/graph_adjacency_list.c
Normal file
171
zh-hant/codes/c/chapter_graph/graph_adjacency_list.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* File: graph_adjacency_list.c
|
||||
* Created Time: 2023-07-07
|
||||
* Author: NI-SW (947743645@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設節點最大數量為 100
|
||||
#define MAX_SIZE 100
|
||||
|
||||
/* 節點結構體 */
|
||||
typedef struct AdjListNode {
|
||||
Vertex *vertex; // 頂點
|
||||
struct AdjListNode *next; // 後繼節點
|
||||
} AdjListNode;
|
||||
|
||||
/* 基於鄰接表實現的無向圖類別 */
|
||||
typedef struct {
|
||||
AdjListNode *heads[MAX_SIZE]; // 節點陣列
|
||||
int size; // 節點數量
|
||||
} GraphAdjList;
|
||||
|
||||
/* 建構子 */
|
||||
GraphAdjList *newGraphAdjList() {
|
||||
GraphAdjList *graph = (GraphAdjList *)malloc(sizeof(GraphAdjList));
|
||||
if (!graph) {
|
||||
return NULL;
|
||||
}
|
||||
graph->size = 0;
|
||||
for (int i = 0; i < MAX_SIZE; i++) {
|
||||
graph->heads[i] = NULL;
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delGraphAdjList(GraphAdjList *graph) {
|
||||
for (int i = 0; i < graph->size; i++) {
|
||||
AdjListNode *cur = graph->heads[i];
|
||||
while (cur != NULL) {
|
||||
AdjListNode *next = cur->next;
|
||||
if (cur != graph->heads[i]) {
|
||||
free(cur);
|
||||
}
|
||||
cur = next;
|
||||
}
|
||||
free(graph->heads[i]->vertex);
|
||||
free(graph->heads[i]);
|
||||
}
|
||||
free(graph);
|
||||
}
|
||||
|
||||
/* 查詢頂點對應的節點 */
|
||||
AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) {
|
||||
for (int i = 0; i < graph->size; i++) {
|
||||
if (graph->heads[i]->vertex == vet) {
|
||||
return graph->heads[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 新增邊輔助函式 */
|
||||
void addEdgeHelper(AdjListNode *head, Vertex *vet) {
|
||||
AdjListNode *node = (AdjListNode *)malloc(sizeof(AdjListNode));
|
||||
node->vertex = vet;
|
||||
// 頭插法
|
||||
node->next = head->next;
|
||||
head->next = node;
|
||||
}
|
||||
|
||||
/* 新增邊 */
|
||||
void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) {
|
||||
AdjListNode *head1 = findNode(graph, vet1);
|
||||
AdjListNode *head2 = findNode(graph, vet2);
|
||||
assert(head1 != NULL && head2 != NULL && head1 != head2);
|
||||
// 新增邊 vet1 - vet2
|
||||
addEdgeHelper(head1, vet2);
|
||||
addEdgeHelper(head2, vet1);
|
||||
}
|
||||
|
||||
/* 刪除邊輔助函式 */
|
||||
void removeEdgeHelper(AdjListNode *head, Vertex *vet) {
|
||||
AdjListNode *pre = head;
|
||||
AdjListNode *cur = head->next;
|
||||
// 在鏈結串列中搜索 vet 對應節點
|
||||
while (cur != NULL && cur->vertex != vet) {
|
||||
pre = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
if (cur == NULL)
|
||||
return;
|
||||
// 將 vet 對應節點從鏈結串列中刪除
|
||||
pre->next = cur->next;
|
||||
// 釋放記憶體
|
||||
free(cur);
|
||||
}
|
||||
|
||||
/* 刪除邊 */
|
||||
void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) {
|
||||
AdjListNode *head1 = findNode(graph, vet1);
|
||||
AdjListNode *head2 = findNode(graph, vet2);
|
||||
assert(head1 != NULL && head2 != NULL);
|
||||
// 刪除邊 vet1 - vet2
|
||||
removeEdgeHelper(head1, head2->vertex);
|
||||
removeEdgeHelper(head2, head1->vertex);
|
||||
}
|
||||
|
||||
/* 新增頂點 */
|
||||
void addVertex(GraphAdjList *graph, Vertex *vet) {
|
||||
assert(graph != NULL && graph->size < MAX_SIZE);
|
||||
AdjListNode *head = (AdjListNode *)malloc(sizeof(AdjListNode));
|
||||
head->vertex = vet;
|
||||
head->next = NULL;
|
||||
// 在鄰接表中新增一個新鏈結串列
|
||||
graph->heads[graph->size++] = head;
|
||||
}
|
||||
|
||||
/* 刪除頂點 */
|
||||
void removeVertex(GraphAdjList *graph, Vertex *vet) {
|
||||
AdjListNode *node = findNode(graph, vet);
|
||||
assert(node != NULL);
|
||||
// 在鄰接表中刪除頂點 vet 對應的鏈結串列
|
||||
AdjListNode *cur = node, *pre = NULL;
|
||||
while (cur) {
|
||||
pre = cur;
|
||||
cur = cur->next;
|
||||
free(pre);
|
||||
}
|
||||
// 走訪其他頂點的鏈結串列,刪除所有包含 vet 的邊
|
||||
for (int i = 0; i < graph->size; i++) {
|
||||
cur = graph->heads[i];
|
||||
pre = NULL;
|
||||
while (cur) {
|
||||
pre = cur;
|
||||
cur = cur->next;
|
||||
if (cur && cur->vertex == vet) {
|
||||
pre->next = cur->next;
|
||||
free(cur);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 將該頂點之後的頂點向前移動,以填補空缺
|
||||
int i;
|
||||
for (i = 0; i < graph->size; i++) {
|
||||
if (graph->heads[i] == node)
|
||||
break;
|
||||
}
|
||||
for (int j = i; j < graph->size - 1; j++) {
|
||||
graph->heads[j] = graph->heads[j + 1];
|
||||
}
|
||||
graph->size--;
|
||||
free(vet);
|
||||
}
|
||||
|
||||
/* 列印鄰接表 */
|
||||
void printGraph(const GraphAdjList *graph) {
|
||||
printf("鄰接表 =\n");
|
||||
for (int i = 0; i < graph->size; ++i) {
|
||||
AdjListNode *node = graph->heads[i];
|
||||
printf("%d: [", node->vertex->val);
|
||||
node = node->next;
|
||||
while (node) {
|
||||
printf("%d, ", node->vertex->val);
|
||||
node = node->next;
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
}
|
||||
55
zh-hant/codes/c/chapter_graph/graph_adjacency_list_test.c
Normal file
55
zh-hant/codes/c/chapter_graph/graph_adjacency_list_test.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: graph_adjacency_list_test.c
|
||||
* Created Time: 2023-07-11
|
||||
* Author: NI-SW (947743645@qq.com)
|
||||
*/
|
||||
|
||||
#include "graph_adjacency_list.c"
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int vals[] = {1, 3, 2, 5, 4};
|
||||
int size = sizeof(vals) / sizeof(vals[0]);
|
||||
Vertex **v = valsToVets(vals, size);
|
||||
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}};
|
||||
int egdeSize = sizeof(edges) / sizeof(edges[0]);
|
||||
GraphAdjList *graph = newGraphAdjList();
|
||||
// 新增所有頂點和邊
|
||||
for (int i = 0; i < size; i++) {
|
||||
addVertex(graph, v[i]);
|
||||
}
|
||||
for (int i = 0; i < egdeSize; i++) {
|
||||
addEdge(graph, edges[i][0], edges[i][1]);
|
||||
}
|
||||
printf("\n初始化後,圖為\n");
|
||||
printGraph(graph);
|
||||
|
||||
/* 新增邊 */
|
||||
// 頂點 1, 2 即 v[0], v[2]
|
||||
addEdge(graph, v[0], v[2]);
|
||||
printf("\n新增邊 1-2 後,圖為\n");
|
||||
printGraph(graph);
|
||||
|
||||
/* 刪除邊 */
|
||||
// 頂點 1, 3 即 v[0], v[1]
|
||||
removeEdge(graph, v[0], v[1]);
|
||||
printf("\n刪除邊 1-3 後,圖為\n");
|
||||
printGraph(graph);
|
||||
|
||||
/* 新增頂點 */
|
||||
Vertex *v5 = newVertex(6);
|
||||
addVertex(graph, v5);
|
||||
printf("\n新增頂點 6 後,圖為\n");
|
||||
printGraph(graph);
|
||||
|
||||
/* 刪除頂點 */
|
||||
// 頂點 3 即 v[1]
|
||||
removeVertex(graph, v[1]);
|
||||
printf("\n刪除頂點 3 後,圖為:\n");
|
||||
printGraph(graph);
|
||||
|
||||
// 釋放記憶體
|
||||
delGraphAdjList(graph);
|
||||
free(v);
|
||||
return 0;
|
||||
}
|
||||
150
zh-hant/codes/c/chapter_graph/graph_adjacency_matrix.c
Normal file
150
zh-hant/codes/c/chapter_graph/graph_adjacency_matrix.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* File: graph_adjacency_matrix.c
|
||||
* Created Time: 2023-07-06
|
||||
* Author: NI-SW (947743645@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
// 假設頂點數量最大為 100
|
||||
#define MAX_SIZE 100
|
||||
|
||||
/* 基於鄰接矩陣實現的無向圖結構體 */
|
||||
typedef struct {
|
||||
int vertices[MAX_SIZE];
|
||||
int adjMat[MAX_SIZE][MAX_SIZE];
|
||||
int size;
|
||||
} GraphAdjMat;
|
||||
|
||||
/* 建構子 */
|
||||
GraphAdjMat *newGraphAdjMat() {
|
||||
GraphAdjMat *graph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat));
|
||||
graph->size = 0;
|
||||
for (int i = 0; i < MAX_SIZE; i++) {
|
||||
for (int j = 0; j < MAX_SIZE; j++) {
|
||||
graph->adjMat[i][j] = 0;
|
||||
}
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delGraphAdjMat(GraphAdjMat *graph) {
|
||||
free(graph);
|
||||
}
|
||||
|
||||
/* 新增頂點 */
|
||||
void addVertex(GraphAdjMat *graph, int val) {
|
||||
if (graph->size == MAX_SIZE) {
|
||||
fprintf(stderr, "圖的頂點數量已達最大值\n");
|
||||
return;
|
||||
}
|
||||
// 新增第 n 個頂點,並將第 n 行和列置零
|
||||
int n = graph->size;
|
||||
graph->vertices[n] = val;
|
||||
for (int i = 0; i <= n; i++) {
|
||||
graph->adjMat[n][i] = graph->adjMat[i][n] = 0;
|
||||
}
|
||||
graph->size++;
|
||||
}
|
||||
|
||||
/* 刪除頂點 */
|
||||
void removeVertex(GraphAdjMat *graph, int index) {
|
||||
if (index < 0 || index >= graph->size) {
|
||||
fprintf(stderr, "頂點索引越界\n");
|
||||
return;
|
||||
}
|
||||
// 在頂點串列中移除索引 index 的頂點
|
||||
for (int i = index; i < graph->size - 1; i++) {
|
||||
graph->vertices[i] = graph->vertices[i + 1];
|
||||
}
|
||||
// 在鄰接矩陣中刪除索引 index 的行
|
||||
for (int i = index; i < graph->size - 1; i++) {
|
||||
for (int j = 0; j < graph->size; j++) {
|
||||
graph->adjMat[i][j] = graph->adjMat[i + 1][j];
|
||||
}
|
||||
}
|
||||
// 在鄰接矩陣中刪除索引 index 的列
|
||||
for (int i = 0; i < graph->size; i++) {
|
||||
for (int j = index; j < graph->size - 1; j++) {
|
||||
graph->adjMat[i][j] = graph->adjMat[i][j + 1];
|
||||
}
|
||||
}
|
||||
graph->size--;
|
||||
}
|
||||
|
||||
/* 新增邊 */
|
||||
// 參數 i, j 對應 vertices 元素索引
|
||||
void addEdge(GraphAdjMat *graph, int i, int j) {
|
||||
if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) {
|
||||
fprintf(stderr, "邊索引越界或相等\n");
|
||||
return;
|
||||
}
|
||||
graph->adjMat[i][j] = 1;
|
||||
graph->adjMat[j][i] = 1;
|
||||
}
|
||||
|
||||
/* 刪除邊 */
|
||||
// 參數 i, j 對應 vertices 元素索引
|
||||
void removeEdge(GraphAdjMat *graph, int i, int j) {
|
||||
if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) {
|
||||
fprintf(stderr, "邊索引越界或相等\n");
|
||||
return;
|
||||
}
|
||||
graph->adjMat[i][j] = 0;
|
||||
graph->adjMat[j][i] = 0;
|
||||
}
|
||||
|
||||
/* 列印鄰接矩陣 */
|
||||
void printGraphAdjMat(GraphAdjMat *graph) {
|
||||
printf("頂點串列 = ");
|
||||
printArray(graph->vertices, graph->size);
|
||||
printf("鄰接矩陣 =\n");
|
||||
for (int i = 0; i < graph->size; i++) {
|
||||
printArray(graph->adjMat[i], graph->size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
// 初始化無向圖
|
||||
GraphAdjMat *graph = newGraphAdjMat();
|
||||
int vertices[] = {1, 3, 2, 5, 4};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
addVertex(graph, vertices[i]);
|
||||
}
|
||||
int edges[][2] = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}};
|
||||
for (int i = 0; i < 6; i++) {
|
||||
addEdge(graph, edges[i][0], edges[i][1]);
|
||||
}
|
||||
printf("\n初始化後,圖為\n");
|
||||
printGraphAdjMat(graph);
|
||||
|
||||
/* 新增邊 */
|
||||
// 頂點 1, 2 的索引分別為 0, 2
|
||||
addEdge(graph, 0, 2);
|
||||
printf("\n新增邊 1-2 後,圖為\n");
|
||||
printGraphAdjMat(graph);
|
||||
|
||||
/* 刪除邊 */
|
||||
// 頂點 1, 3 的索引分別為 0, 1
|
||||
removeEdge(graph, 0, 1);
|
||||
printf("\n刪除邊 1-3 後,圖為\n");
|
||||
printGraphAdjMat(graph);
|
||||
|
||||
/* 新增頂點 */
|
||||
addVertex(graph, 6);
|
||||
printf("\n新增頂點 6 後,圖為\n");
|
||||
printGraphAdjMat(graph);
|
||||
|
||||
/* 刪除頂點 */
|
||||
// 頂點 3 的索引為 1
|
||||
removeVertex(graph, 1);
|
||||
printf("\n刪除頂點 3 後,圖為\n");
|
||||
printGraphAdjMat(graph);
|
||||
|
||||
// 釋放記憶體
|
||||
delGraphAdjMat(graph);
|
||||
|
||||
return 0;
|
||||
}
|
||||
116
zh-hant/codes/c/chapter_graph/graph_bfs.c
Normal file
116
zh-hant/codes/c/chapter_graph/graph_bfs.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* File: graph_bfs.c
|
||||
* Created Time: 2023-07-11
|
||||
* Author: NI-SW (947743645@qq.com)
|
||||
*/
|
||||
|
||||
#include "graph_adjacency_list.c"
|
||||
|
||||
// 假設節點最大數量為 100
|
||||
#define MAX_SIZE 100
|
||||
|
||||
/* 節點佇列結構體 */
|
||||
typedef struct {
|
||||
Vertex *vertices[MAX_SIZE];
|
||||
int front, rear, size;
|
||||
} Queue;
|
||||
|
||||
/* 建構子 */
|
||||
Queue *newQueue() {
|
||||
Queue *q = (Queue *)malloc(sizeof(Queue));
|
||||
q->front = q->rear = q->size = 0;
|
||||
return q;
|
||||
}
|
||||
|
||||
/* 判斷佇列是否為空 */
|
||||
int isEmpty(Queue *q) {
|
||||
return q->size == 0;
|
||||
}
|
||||
|
||||
/* 入列操作 */
|
||||
void enqueue(Queue *q, Vertex *vet) {
|
||||
q->vertices[q->rear] = vet;
|
||||
q->rear = (q->rear + 1) % MAX_SIZE;
|
||||
q->size++;
|
||||
}
|
||||
|
||||
/* 出列操作 */
|
||||
Vertex *dequeue(Queue *q) {
|
||||
Vertex *vet = q->vertices[q->front];
|
||||
q->front = (q->front + 1) % MAX_SIZE;
|
||||
q->size--;
|
||||
return vet;
|
||||
}
|
||||
|
||||
/* 檢查頂點是否已被訪問 */
|
||||
int isVisited(Vertex **visited, int size, Vertex *vet) {
|
||||
// 走訪查詢節點,使用 O(n) 時間
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (visited[i] == vet)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 廣度優先走訪 */
|
||||
// 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
|
||||
void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) {
|
||||
// 佇列用於實現 BFS
|
||||
Queue *queue = newQueue();
|
||||
enqueue(queue, startVet);
|
||||
visited[(*visitedSize)++] = startVet;
|
||||
// 以頂點 vet 為起點,迴圈直至訪問完所有頂點
|
||||
while (!isEmpty(queue)) {
|
||||
Vertex *vet = dequeue(queue); // 佇列首頂點出隊
|
||||
res[(*resSize)++] = vet; // 記錄訪問頂點
|
||||
// 走訪該頂點的所有鄰接頂點
|
||||
AdjListNode *node = findNode(graph, vet);
|
||||
while (node != NULL) {
|
||||
// 跳過已被訪問的頂點
|
||||
if (!isVisited(visited, *visitedSize, node->vertex)) {
|
||||
enqueue(queue, node->vertex); // 只入列未訪問的頂點
|
||||
visited[(*visitedSize)++] = node->vertex; // 標記該頂點已被訪問
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
// 釋放記憶體
|
||||
free(queue);
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
// 初始化無向圖
|
||||
int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
int size = sizeof(vals) / sizeof(vals[0]);
|
||||
Vertex **v = valsToVets(vals, size);
|
||||
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]}, {v[2], v[5]}, {v[3], v[4]},
|
||||
{v[3], v[6]}, {v[4], v[5]}, {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}};
|
||||
int egdeSize = sizeof(edges) / sizeof(edges[0]);
|
||||
GraphAdjList *graph = newGraphAdjList();
|
||||
// 新增所有頂點和邊
|
||||
for (int i = 0; i < size; i++) {
|
||||
addVertex(graph, v[i]);
|
||||
}
|
||||
for (int i = 0; i < egdeSize; i++) {
|
||||
addEdge(graph, edges[i][0], edges[i][1]);
|
||||
}
|
||||
printf("\n初始化後,圖為\n");
|
||||
printGraph(graph);
|
||||
|
||||
// 廣度優先走訪
|
||||
// 頂點走訪序列
|
||||
Vertex *res[MAX_SIZE];
|
||||
int resSize = 0;
|
||||
// 用於記錄已被訪問過的頂點
|
||||
Vertex *visited[MAX_SIZE];
|
||||
int visitedSize = 0;
|
||||
graphBFS(graph, v[0], res, &resSize, visited, &visitedSize);
|
||||
printf("\n廣度優先走訪(BFS)頂點序列為\n");
|
||||
printArray(vetsToVals(res, resSize), resSize);
|
||||
|
||||
// 釋放記憶體
|
||||
delGraphAdjList(graph);
|
||||
free(v);
|
||||
return 0;
|
||||
}
|
||||
75
zh-hant/codes/c/chapter_graph/graph_dfs.c
Normal file
75
zh-hant/codes/c/chapter_graph/graph_dfs.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* File: graph_dfs.c
|
||||
* Created Time: 2023-07-13
|
||||
* Author: NI-SW (947743645@qq.com)
|
||||
*/
|
||||
|
||||
#include "graph_adjacency_list.c"
|
||||
|
||||
// 假設節點最大數量為 100
|
||||
#define MAX_SIZE 100
|
||||
|
||||
/* 檢查頂點是否已被訪問 */
|
||||
int isVisited(Vertex **res, int size, Vertex *vet) {
|
||||
// 走訪查詢節點,使用 O(n) 時間
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (res[i] == vet) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 深度優先走訪輔助函式 */
|
||||
void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) {
|
||||
// 記錄訪問頂點
|
||||
res[(*resSize)++] = vet;
|
||||
// 走訪該頂點的所有鄰接頂點
|
||||
AdjListNode *node = findNode(graph, vet);
|
||||
while (node != NULL) {
|
||||
// 跳過已被訪問的頂點
|
||||
if (!isVisited(res, *resSize, node->vertex)) {
|
||||
// 遞迴訪問鄰接頂點
|
||||
dfs(graph, res, resSize, node->vertex);
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* 深度優先走訪 */
|
||||
// 使用鄰接表來表示圖,以便獲取指定頂點的所有鄰接頂點
|
||||
void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) {
|
||||
dfs(graph, res, resSize, startVet);
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
// 初始化無向圖
|
||||
int vals[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
int size = sizeof(vals) / sizeof(vals[0]);
|
||||
Vertex **v = valsToVets(vals, size);
|
||||
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[5]}, {v[4], v[5]}, {v[5], v[6]}};
|
||||
int egdeSize = sizeof(edges) / sizeof(edges[0]);
|
||||
GraphAdjList *graph = newGraphAdjList();
|
||||
// 新增所有頂點和邊
|
||||
for (int i = 0; i < size; i++) {
|
||||
addVertex(graph, v[i]);
|
||||
}
|
||||
for (int i = 0; i < egdeSize; i++) {
|
||||
addEdge(graph, edges[i][0], edges[i][1]);
|
||||
}
|
||||
printf("\n初始化後,圖為\n");
|
||||
printGraph(graph);
|
||||
|
||||
// 深度優先走訪
|
||||
Vertex *res[MAX_SIZE];
|
||||
int resSize = 0;
|
||||
graphDFS(graph, v[0], res, &resSize);
|
||||
printf("\n深度優先走訪(DFS)頂點序列為\n");
|
||||
printArray(vetsToVals(res, resSize), resSize);
|
||||
|
||||
// 釋放記憶體
|
||||
delGraphAdjList(graph);
|
||||
free(v);
|
||||
return 0;
|
||||
}
|
||||
8
zh-hant/codes/c/chapter_greedy/CMakeLists.txt
Normal file
8
zh-hant/codes/c/chapter_greedy/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
add_executable(coin_change_greedy coin_change_greedy.c)
|
||||
add_executable(fractional_knapsack fractional_knapsack.c)
|
||||
add_executable(max_capacity max_capacity.c)
|
||||
add_executable(max_product_cutting max_product_cutting.c)
|
||||
|
||||
if (NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
target_link_libraries(max_product_cutting m)
|
||||
endif()
|
||||
60
zh-hant/codes/c/chapter_greedy/coin_change_greedy.c
Normal file
60
zh-hant/codes/c/chapter_greedy/coin_change_greedy.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* File: coin_change_greedy.c
|
||||
* Created Time: 2023-09-07
|
||||
* Author: lwbaptx (lwbaptx@gmail.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 零錢兌換:貪婪 */
|
||||
int coinChangeGreedy(int *coins, int size, int amt) {
|
||||
// 假設 coins 串列有序
|
||||
int i = size - 1;
|
||||
int count = 0;
|
||||
// 迴圈進行貪婪選擇,直到無剩餘金額
|
||||
while (amt > 0) {
|
||||
// 找到小於且最接近剩餘金額的硬幣
|
||||
while (i > 0 && coins[i] > amt) {
|
||||
i--;
|
||||
}
|
||||
// 選擇 coins[i]
|
||||
amt -= coins[i];
|
||||
count++;
|
||||
}
|
||||
// 若未找到可行方案,則返回 -1
|
||||
return amt == 0 ? count : -1;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
// 貪婪:能夠保證找到全域性最優解
|
||||
int coins1[6] = {1, 5, 10, 20, 50, 100};
|
||||
int amt = 186;
|
||||
int res = coinChangeGreedy(coins1, 6, amt);
|
||||
printf("\ncoins = ");
|
||||
printArray(coins1, 6);
|
||||
printf("amt = %d\n", amt);
|
||||
printf("湊到 %d 所需的最少硬幣數量為 %d\n", amt, res);
|
||||
|
||||
// 貪婪:無法保證找到全域性最優解
|
||||
int coins2[3] = {1, 20, 50};
|
||||
amt = 60;
|
||||
res = coinChangeGreedy(coins2, 3, amt);
|
||||
printf("\ncoins = ");
|
||||
printArray(coins2, 3);
|
||||
printf("amt = %d\n", amt);
|
||||
printf("湊到 %d 所需的最少硬幣數量為 %d\n", amt, res);
|
||||
printf("實際上需要的最少數量為 3 ,即 20 + 20 + 20\n");
|
||||
|
||||
// 貪婪:無法保證找到全域性最優解
|
||||
int coins3[3] = {1, 49, 50};
|
||||
amt = 98;
|
||||
res = coinChangeGreedy(coins3, 3, amt);
|
||||
printf("\ncoins = ");
|
||||
printArray(coins3, 3);
|
||||
printf("amt = %d\n", amt);
|
||||
printf("湊到 %d 所需的最少硬幣數量為 %d\n", amt, res);
|
||||
printf("實際上需要的最少數量為 2 ,即 49 + 49\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
60
zh-hant/codes/c/chapter_greedy/fractional_knapsack.c
Normal file
60
zh-hant/codes/c/chapter_greedy/fractional_knapsack.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* File: fractional_knapsack.c
|
||||
* Created Time: 2023-09-14
|
||||
* Author: xianii (xianyi.xia@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 物品 */
|
||||
typedef struct {
|
||||
int w; // 物品重量
|
||||
int v; // 物品價值
|
||||
} Item;
|
||||
|
||||
/* 按照價值密度排序 */
|
||||
int sortByValueDensity(const void *a, const void *b) {
|
||||
Item *t1 = (Item *)a;
|
||||
Item *t2 = (Item *)b;
|
||||
return (float)(t1->v) / t1->w < (float)(t2->v) / t2->w;
|
||||
}
|
||||
|
||||
/* 分數背包:貪婪 */
|
||||
float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) {
|
||||
// 建立物品串列,包含兩個屬性:重量、價值
|
||||
Item *items = malloc(sizeof(Item) * itemCount);
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
items[i] = (Item){.w = wgt[i], .v = val[i]};
|
||||
}
|
||||
// 按照單位價值 item.v / item.w 從高到低進行排序
|
||||
qsort(items, (size_t)itemCount, sizeof(Item), sortByValueDensity);
|
||||
// 迴圈貪婪選擇
|
||||
float res = 0.0;
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (items[i].w <= cap) {
|
||||
// 若剩餘容量充足,則將當前物品整個裝進背包
|
||||
res += items[i].v;
|
||||
cap -= items[i].w;
|
||||
} else {
|
||||
// 若剩餘容量不足,則將當前物品的一部分裝進背包
|
||||
res += (float)cap / items[i].w * items[i].v;
|
||||
cap = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(items);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main(void) {
|
||||
int wgt[] = {10, 20, 30, 40, 50};
|
||||
int val[] = {50, 120, 150, 210, 240};
|
||||
int capacity = 50;
|
||||
|
||||
// 貪婪演算法
|
||||
float res = fractionalKnapsack(wgt, val, sizeof(wgt) / sizeof(int), capacity);
|
||||
printf("不超過背包容量的最大物品價值為 %0.2f\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
49
zh-hant/codes/c/chapter_greedy/max_capacity.c
Normal file
49
zh-hant/codes/c/chapter_greedy/max_capacity.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: max_capacity.c
|
||||
* Created Time: 2023-09-15
|
||||
* Author: xianii (xianyi.xia@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 求最小值 */
|
||||
int myMin(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
/* 求最大值 */
|
||||
int myMax(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/* 最大容量:貪婪 */
|
||||
int maxCapacity(int ht[], int htLength) {
|
||||
// 初始化 i, j,使其分列陣列兩端
|
||||
int i = 0;
|
||||
int j = htLength - 1;
|
||||
// 初始最大容量為 0
|
||||
int res = 0;
|
||||
// 迴圈貪婪選擇,直至兩板相遇
|
||||
while (i < j) {
|
||||
// 更新最大容量
|
||||
int capacity = myMin(ht[i], ht[j]) * (j - i);
|
||||
res = myMax(res, capacity);
|
||||
// 向內移動短板
|
||||
if (ht[i] < ht[j]) {
|
||||
i++;
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main(void) {
|
||||
int ht[] = {3, 8, 5, 2, 7, 7, 3, 4};
|
||||
|
||||
// 貪婪演算法
|
||||
int res = maxCapacity(ht, sizeof(ht) / sizeof(int));
|
||||
printf("最大容量為 %d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
38
zh-hant/codes/c/chapter_greedy/max_product_cutting.c
Normal file
38
zh-hant/codes/c/chapter_greedy/max_product_cutting.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* File: max_product_cutting.c
|
||||
* Created Time: 2023-09-15
|
||||
* Author: xianii (xianyi.xia@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 最大切分乘積:貪婪 */
|
||||
int maxProductCutting(int n) {
|
||||
// 當 n <= 3 時,必須切分出一個 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// 貪婪地切分出 3 ,a 為 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;
|
||||
}
|
||||
4
zh-hant/codes/c/chapter_hashing/CMakeLists.txt
Normal file
4
zh-hant/codes/c/chapter_hashing/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
add_executable(array_hash_map array_hash_map.c)
|
||||
add_executable(hash_map_chaining hash_map_chaining.c)
|
||||
add_executable(hash_map_open_addressing hash_map_open_addressing.c)
|
||||
add_executable(simple_hash simple_hash.c)
|
||||
212
zh-hant/codes/c/chapter_hashing/array_hash_map.c
Normal file
212
zh-hant/codes/c/chapter_hashing/array_hash_map.c
Normal 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;
|
||||
}
|
||||
213
zh-hant/codes/c/chapter_hashing/hash_map_chaining.c
Normal file
213
zh-hant/codes/c/chapter_hashing/hash_map_chaining.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* File: hash_map_chaining.c
|
||||
* Created Time: 2023-10-13
|
||||
* Author: SenMing (1206575349@qq.com), krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 假設 val 最大長度為 100
|
||||
#define MAX_SIZE 100
|
||||
|
||||
/* 鍵值對 */
|
||||
typedef struct {
|
||||
int key;
|
||||
char val[MAX_SIZE];
|
||||
} Pair;
|
||||
|
||||
/* 鏈結串列節點 */
|
||||
typedef struct Node {
|
||||
Pair *pair;
|
||||
struct Node *next;
|
||||
} Node;
|
||||
|
||||
/* 鏈式位址雜湊表 */
|
||||
typedef struct {
|
||||
int size; // 鍵值對數量
|
||||
int capacity; // 雜湊表容量
|
||||
double loadThres; // 觸發擴容的負載因子閾值
|
||||
int extendRatio; // 擴容倍數
|
||||
Node **buckets; // 桶陣列
|
||||
} HashMapChaining;
|
||||
|
||||
/* 建構子 */
|
||||
HashMapChaining *newHashMapChaining() {
|
||||
HashMapChaining *hashMap = (HashMapChaining *)malloc(sizeof(HashMapChaining));
|
||||
hashMap->size = 0;
|
||||
hashMap->capacity = 4;
|
||||
hashMap->loadThres = 2.0 / 3.0;
|
||||
hashMap->extendRatio = 2;
|
||||
hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *));
|
||||
for (int i = 0; i < hashMap->capacity; i++) {
|
||||
hashMap->buckets[i] = NULL;
|
||||
}
|
||||
return hashMap;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delHashMapChaining(HashMapChaining *hashMap) {
|
||||
for (int i = 0; i < hashMap->capacity; i++) {
|
||||
Node *cur = hashMap->buckets[i];
|
||||
while (cur) {
|
||||
Node *tmp = cur;
|
||||
cur = cur->next;
|
||||
free(tmp->pair);
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
free(hashMap->buckets);
|
||||
free(hashMap);
|
||||
}
|
||||
|
||||
/* 雜湊函式 */
|
||||
int hashFunc(HashMapChaining *hashMap, int key) {
|
||||
return key % hashMap->capacity;
|
||||
}
|
||||
|
||||
/* 負載因子 */
|
||||
double loadFactor(HashMapChaining *hashMap) {
|
||||
return (double)hashMap->size / (double)hashMap->capacity;
|
||||
}
|
||||
|
||||
/* 查詢操作 */
|
||||
char *get(HashMapChaining *hashMap, int key) {
|
||||
int index = hashFunc(hashMap, key);
|
||||
// 走訪桶,若找到 key ,則返回對應 val
|
||||
Node *cur = hashMap->buckets[index];
|
||||
while (cur) {
|
||||
if (cur->pair->key == key) {
|
||||
return cur->pair->val;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
return ""; // 若未找到 key ,則返回空字串
|
||||
}
|
||||
|
||||
/* 新增操作 */
|
||||
void put(HashMapChaining *hashMap, int key, const char *val);
|
||||
|
||||
/* 擴容雜湊表 */
|
||||
void extend(HashMapChaining *hashMap) {
|
||||
// 暫存原雜湊表
|
||||
int oldCapacity = hashMap->capacity;
|
||||
Node **oldBuckets = hashMap->buckets;
|
||||
// 初始化擴容後的新雜湊表
|
||||
hashMap->capacity *= hashMap->extendRatio;
|
||||
hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *));
|
||||
for (int i = 0; i < hashMap->capacity; i++) {
|
||||
hashMap->buckets[i] = NULL;
|
||||
}
|
||||
hashMap->size = 0;
|
||||
// 將鍵值對從原雜湊表搬運至新雜湊表
|
||||
for (int i = 0; i < oldCapacity; i++) {
|
||||
Node *cur = oldBuckets[i];
|
||||
while (cur) {
|
||||
put(hashMap, cur->pair->key, cur->pair->val);
|
||||
Node *temp = cur;
|
||||
cur = cur->next;
|
||||
// 釋放記憶體
|
||||
free(temp->pair);
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
free(oldBuckets);
|
||||
}
|
||||
|
||||
/* 新增操作 */
|
||||
void put(HashMapChaining *hashMap, int key, const char *val) {
|
||||
// 當負載因子超過閾值時,執行擴容
|
||||
if (loadFactor(hashMap) > hashMap->loadThres) {
|
||||
extend(hashMap);
|
||||
}
|
||||
int index = hashFunc(hashMap, key);
|
||||
// 走訪桶,若遇到指定 key ,則更新對應 val 並返回
|
||||
Node *cur = hashMap->buckets[index];
|
||||
while (cur) {
|
||||
if (cur->pair->key == key) {
|
||||
strcpy(cur->pair->val, val); // 若遇到指定 key ,則更新對應 val 並返回
|
||||
return;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
// 若無該 key ,則將鍵值對新增至鏈結串列頭部
|
||||
Pair *newPair = (Pair *)malloc(sizeof(Pair));
|
||||
newPair->key = key;
|
||||
strcpy(newPair->val, val);
|
||||
Node *newNode = (Node *)malloc(sizeof(Node));
|
||||
newNode->pair = newPair;
|
||||
newNode->next = hashMap->buckets[index];
|
||||
hashMap->buckets[index] = newNode;
|
||||
hashMap->size++;
|
||||
}
|
||||
|
||||
/* 刪除操作 */
|
||||
void removeItem(HashMapChaining *hashMap, int key) {
|
||||
int index = hashFunc(hashMap, key);
|
||||
Node *cur = hashMap->buckets[index];
|
||||
Node *pre = NULL;
|
||||
while (cur) {
|
||||
if (cur->pair->key == key) {
|
||||
// 從中刪除鍵值對
|
||||
if (pre) {
|
||||
pre->next = cur->next;
|
||||
} else {
|
||||
hashMap->buckets[index] = cur->next;
|
||||
}
|
||||
// 釋放記憶體
|
||||
free(cur->pair);
|
||||
free(cur);
|
||||
hashMap->size--;
|
||||
return;
|
||||
}
|
||||
pre = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* 列印雜湊表 */
|
||||
void print(HashMapChaining *hashMap) {
|
||||
for (int i = 0; i < hashMap->capacity; i++) {
|
||||
Node *cur = hashMap->buckets[i];
|
||||
printf("[");
|
||||
while (cur) {
|
||||
printf("%d -> %s, ", cur->pair->key, cur->pair->val);
|
||||
cur = cur->next;
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化雜湊表 */
|
||||
HashMapChaining *hashMap = newHashMapChaining();
|
||||
|
||||
/* 新增操作 */
|
||||
// 在雜湊表中新增鍵值對 (key, value)
|
||||
put(hashMap, 12836, "小哈");
|
||||
put(hashMap, 15937, "小囉");
|
||||
put(hashMap, 16750, "小算");
|
||||
put(hashMap, 13276, "小法");
|
||||
put(hashMap, 10583, "小鴨");
|
||||
printf("\n新增完成後,雜湊表為\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;
|
||||
}
|
||||
208
zh-hant/codes/c/chapter_hashing/hash_map_open_addressing.c
Normal file
208
zh-hant/codes/c/chapter_hashing/hash_map_open_addressing.c
Normal 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;
|
||||
}
|
||||
68
zh-hant/codes/c/chapter_hashing/simple_hash.c
Normal file
68
zh-hant/codes/c/chapter_hashing/simple_hash.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* File: simple_hash.c
|
||||
* Created Time: 2023-09-09
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 加法雜湊 */
|
||||
int addHash(char *key) {
|
||||
long long hash = 0;
|
||||
const int MODULUS = 1000000007;
|
||||
for (int i = 0; i < strlen(key); i++) {
|
||||
hash = (hash + (unsigned char)key[i]) % MODULUS;
|
||||
}
|
||||
return (int)hash;
|
||||
}
|
||||
|
||||
/* 乘法雜湊 */
|
||||
int mulHash(char *key) {
|
||||
long long hash = 0;
|
||||
const int MODULUS = 1000000007;
|
||||
for (int i = 0; i < strlen(key); i++) {
|
||||
hash = (31 * hash + (unsigned char)key[i]) % MODULUS;
|
||||
}
|
||||
return (int)hash;
|
||||
}
|
||||
|
||||
/* 互斥或雜湊 */
|
||||
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;
|
||||
}
|
||||
2
zh-hant/codes/c/chapter_heap/CMakeLists.txt
Normal file
2
zh-hant/codes/c/chapter_heap/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
add_executable(my_heap_test my_heap_test.c)
|
||||
add_executable(top_k top_k.c)
|
||||
152
zh-hant/codes/c/chapter_heap/my_heap.c
Normal file
152
zh-hant/codes/c/chapter_heap/my_heap.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* File: my_heap.c
|
||||
* Created Time: 2023-01-15
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
#define MAX_SIZE 5000
|
||||
|
||||
/* 大頂堆積 */
|
||||
typedef struct {
|
||||
// size 代表的是實際元素的個數
|
||||
int size;
|
||||
// 使用預先分配記憶體的陣列,避免擴容
|
||||
int data[MAX_SIZE];
|
||||
} MaxHeap;
|
||||
|
||||
// 函式宣告
|
||||
void siftDown(MaxHeap *maxHeap, int i);
|
||||
void siftUp(MaxHeap *maxHeap, int i);
|
||||
int parent(MaxHeap *maxHeap, int i);
|
||||
|
||||
/* 建構子,根據切片建堆積 */
|
||||
MaxHeap *newMaxHeap(int nums[], int size) {
|
||||
// 所有元素入堆積
|
||||
MaxHeap *maxHeap = (MaxHeap *)malloc(sizeof(MaxHeap));
|
||||
maxHeap->size = size;
|
||||
memcpy(maxHeap->data, nums, size * sizeof(int));
|
||||
for (int i = parent(maxHeap, size - 1); i >= 0; i--) {
|
||||
// 堆積化除葉節點以外的其他所有節點
|
||||
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;
|
||||
}
|
||||
}
|
||||
41
zh-hant/codes/c/chapter_heap/my_heap_test.c
Normal file
41
zh-hant/codes/c/chapter_heap/my_heap_test.c
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* File: my_heap_test.c
|
||||
* Created Time: 2023-01-15
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "my_heap.c"
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化堆積 */
|
||||
// 初始化大頂堆積
|
||||
int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2};
|
||||
MaxHeap *maxHeap = newMaxHeap(nums, sizeof(nums) / sizeof(int));
|
||||
printf("輸入陣列並建堆積後\n");
|
||||
printHeap(maxHeap->data, maxHeap->size);
|
||||
|
||||
/* 獲取堆積頂元素 */
|
||||
printf("\n堆積頂元素為 %d\n", peek(maxHeap));
|
||||
|
||||
/* 元素入堆積 */
|
||||
push(maxHeap, 7);
|
||||
printf("\n元素 7 入堆積後\n");
|
||||
printHeap(maxHeap->data, maxHeap->size);
|
||||
|
||||
/* 堆積頂元素出堆積 */
|
||||
int top = pop(maxHeap);
|
||||
printf("\n堆積頂元素 %d 出堆積後\n", top);
|
||||
printHeap(maxHeap->data, maxHeap->size);
|
||||
|
||||
/* 獲取堆積大小 */
|
||||
printf("\n堆積元素數量為 %d\n", size(maxHeap));
|
||||
|
||||
/* 判斷堆積是否為空 */
|
||||
printf("\n堆積是否為空 %d\n", isEmpty(maxHeap));
|
||||
|
||||
// 釋放記憶體
|
||||
delMaxHeap(maxHeap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
73
zh-hant/codes/c/chapter_heap/top_k.c
Normal file
73
zh-hant/codes/c/chapter_heap/top_k.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* File: top_k.c
|
||||
* Created Time: 2023-10-26
|
||||
* Author: krahets (krahets163.com)
|
||||
*/
|
||||
|
||||
#include "my_heap.c"
|
||||
|
||||
/* 元素入堆積 */
|
||||
void pushMinHeap(MaxHeap *maxHeap, int val) {
|
||||
// 元素取反
|
||||
push(maxHeap, -val);
|
||||
}
|
||||
|
||||
/* 元素出堆積 */
|
||||
int popMinHeap(MaxHeap *maxHeap) {
|
||||
// 元素取反
|
||||
return -pop(maxHeap);
|
||||
}
|
||||
|
||||
/* 訪問堆積頂元素 */
|
||||
int peekMinHeap(MaxHeap *maxHeap) {
|
||||
// 元素取反
|
||||
return -peek(maxHeap);
|
||||
}
|
||||
|
||||
/* 取出堆積中元素 */
|
||||
int *getMinHeap(MaxHeap *maxHeap) {
|
||||
// 將堆積中所有元素取反並存入 res 陣列
|
||||
int *res = (int *)malloc(maxHeap->size * sizeof(int));
|
||||
for (int i = 0; i < maxHeap->size; i++) {
|
||||
res[i] = -maxHeap->data[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// 基於堆積查詢陣列中最大的 k 個元素的函式
|
||||
int *topKHeap(int *nums, int sizeNums, int k) {
|
||||
// 初始化小頂堆積
|
||||
// 請注意:我們將堆積中所有元素取反,從而用大頂堆積來模擬小頂堆積
|
||||
int *empty = (int *)malloc(0);
|
||||
MaxHeap *maxHeap = newMaxHeap(empty, 0);
|
||||
// 將陣列的前 k 個元素入堆積
|
||||
for (int i = 0; i < k; i++) {
|
||||
pushMinHeap(maxHeap, nums[i]);
|
||||
}
|
||||
// 從第 k+1 個元素開始,保持堆積的長度為 k
|
||||
for (int i = k; i < sizeNums; i++) {
|
||||
// 若當前元素大於堆積頂元素,則將堆積頂元素出堆積、當前元素入堆積
|
||||
if (nums[i] > peekMinHeap(maxHeap)) {
|
||||
popMinHeap(maxHeap);
|
||||
pushMinHeap(maxHeap, nums[i]);
|
||||
}
|
||||
}
|
||||
int *res = getMinHeap(maxHeap);
|
||||
// 釋放記憶體
|
||||
delMaxHeap(maxHeap);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int nums[] = {1, 7, 6, 3, 2};
|
||||
int k = 3;
|
||||
int sizeNums = sizeof(nums) / sizeof(nums[0]);
|
||||
|
||||
int *res = topKHeap(nums, sizeNums, k);
|
||||
printf("最大的 %d 個元素為: ", k);
|
||||
printArray(res, k);
|
||||
|
||||
free(res);
|
||||
return 0;
|
||||
}
|
||||
4
zh-hant/codes/c/chapter_searching/CMakeLists.txt
Normal file
4
zh-hant/codes/c/chapter_searching/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
add_executable(binary_search binary_search.c)
|
||||
add_executable(two_sum two_sum.c)
|
||||
add_executable(binary_search_edge binary_search_edge.c)
|
||||
add_executable(binary_search_insertion binary_search_insertion.c)
|
||||
59
zh-hant/codes/c/chapter_searching/binary_search.c
Normal file
59
zh-hant/codes/c/chapter_searching/binary_search.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* File: binary_search.c
|
||||
* Created Time: 2023-03-18
|
||||
* Author: Guanngxu (446678850@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 二分搜尋(雙閉區間) */
|
||||
int binarySearch(int *nums, int len, int target) {
|
||||
// 初始化雙閉區間 [0, n-1] ,即 i, j 分別指向陣列首元素、尾元素
|
||||
int i = 0, j = len - 1;
|
||||
// 迴圈,當搜尋區間為空時跳出(當 i > j 時為空)
|
||||
while (i <= j) {
|
||||
int m = i + (j - i) / 2; // 計算中點索引 m
|
||||
if (nums[m] < target) // 此情況說明 target 在區間 [m+1, j] 中
|
||||
i = m + 1;
|
||||
else if (nums[m] > target) // 此情況說明 target 在區間 [i, m-1] 中
|
||||
j = m - 1;
|
||||
else // 找到目標元素,返回其索引
|
||||
return m;
|
||||
}
|
||||
// 未找到目標元素,返回 -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 二分搜尋(左閉右開區間) */
|
||||
int binarySearchLCRO(int *nums, int len, int target) {
|
||||
// 初始化左閉右開區間 [0, n) ,即 i, j 分別指向陣列首元素、尾元素+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;
|
||||
}
|
||||
67
zh-hant/codes/c/chapter_searching/binary_search_edge.c
Normal file
67
zh-hant/codes/c/chapter_searching/binary_search_edge.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* File: binary_search_edge.c
|
||||
* Created Time: 2023-09-09
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 二分搜尋插入點(存在重複元素) */
|
||||
int binarySearchInsertion(int *nums, int numSize, int target) {
|
||||
int i = 0, j = numSize - 1; // 初始化雙閉區間 [0, n-1]
|
||||
while (i <= j) {
|
||||
int m = i + (j - i) / 2; // 計算中點索引 m
|
||||
if (nums[m] < target) {
|
||||
i = m + 1; // target 在區間 [m+1, j] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
return i;
|
||||
}
|
||||
|
||||
/* 二分搜尋最左一個 target */
|
||||
int binarySearchLeftEdge(int *nums, int numSize, int target) {
|
||||
// 等價於查詢 target 的插入點
|
||||
int i = binarySearchInsertion(nums, numSize, target);
|
||||
// 未找到 target ,返回 -1
|
||||
if (i == numSize || nums[i] != target) {
|
||||
return -1;
|
||||
}
|
||||
// 找到 target ,返回索引 i
|
||||
return i;
|
||||
}
|
||||
|
||||
/* 二分搜尋最右一個 target */
|
||||
int binarySearchRightEdge(int *nums, int numSize, int target) {
|
||||
// 轉化為查詢最左一個 target + 1
|
||||
int i = binarySearchInsertion(nums, numSize, target + 1);
|
||||
// j 指向最右一個 target ,i 指向首個大於 target 的元素
|
||||
int j = i - 1;
|
||||
// 未找到 target ,返回 -1
|
||||
if (j == -1 || nums[j] != target) {
|
||||
return -1;
|
||||
}
|
||||
// 找到 target ,返回索引 j
|
||||
return j;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
// 包含重複元素的陣列
|
||||
int nums[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15};
|
||||
printf("\n陣列 nums = ");
|
||||
printArray(nums, sizeof(nums) / sizeof(nums[0]));
|
||||
|
||||
// 二分搜尋左邊界和右邊界
|
||||
int targets[] = {6, 7};
|
||||
for (int i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) {
|
||||
int index = binarySearchLeftEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]);
|
||||
printf("最左一個元素 %d 的索引為 %d\n", targets[i], index);
|
||||
index = binarySearchRightEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]);
|
||||
printf("最右一個元素 %d 的索引為 %d\n", targets[i], index);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
68
zh-hant/codes/c/chapter_searching/binary_search_insertion.c
Normal file
68
zh-hant/codes/c/chapter_searching/binary_search_insertion.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* File: binary_search_insertion.c
|
||||
* Created Time: 2023-09-09
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 二分搜尋插入點(無重複元素) */
|
||||
int binarySearchInsertionSimple(int *nums, int numSize, int target) {
|
||||
int i = 0, j = numSize - 1; // 初始化雙閉區間 [0, n-1]
|
||||
while (i <= j) {
|
||||
int m = i + (j - i) / 2; // 計算中點索引 m
|
||||
if (nums[m] < target) {
|
||||
i = m + 1; // target 在區間 [m+1, j] 中
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
return m; // 找到 target ,返回插入點 m
|
||||
}
|
||||
}
|
||||
// 未找到 target ,返回插入點 i
|
||||
return i;
|
||||
}
|
||||
|
||||
/* 二分搜尋插入點(存在重複元素) */
|
||||
int binarySearchInsertion(int *nums, int numSize, int target) {
|
||||
int i = 0, j = numSize - 1; // 初始化雙閉區間 [0, n-1]
|
||||
while (i <= j) {
|
||||
int m = i + (j - i) / 2; // 計算中點索引 m
|
||||
if (nums[m] < target) {
|
||||
i = m + 1; // target 在區間 [m+1, j] 中
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
// 無重複元素的陣列
|
||||
int nums1[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
|
||||
printf("\n陣列 nums = ");
|
||||
printArray(nums1, sizeof(nums1) / sizeof(nums1[0]));
|
||||
// 二分搜尋插入點
|
||||
int targets1[] = {6, 9};
|
||||
for (int i = 0; i < sizeof(targets1) / sizeof(targets1[0]); i++) {
|
||||
int index = binarySearchInsertionSimple(nums1, sizeof(nums1) / sizeof(nums1[0]), targets1[i]);
|
||||
printf("元素 %d 的插入點的索引為 %d\n", targets1[i], index);
|
||||
}
|
||||
|
||||
// 包含重複元素的陣列
|
||||
int nums2[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15};
|
||||
printf("\n陣列 nums = ");
|
||||
printArray(nums2, sizeof(nums2) / sizeof(nums2[0]));
|
||||
// 二分搜尋插入點
|
||||
int targets2[] = {2, 6, 20};
|
||||
for (int i = 0; i < sizeof(targets2) / sizeof(int); i++) {
|
||||
int index = binarySearchInsertion(nums2, sizeof(nums2) / sizeof(nums2[0]), targets2[i]);
|
||||
printf("元素 %d 的插入點的索引為 %d\n", targets2[i], index);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
86
zh-hant/codes/c/chapter_searching/two_sum.c
Normal file
86
zh-hant/codes/c/chapter_searching/two_sum.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* File: two_sum.c
|
||||
* Created Time: 2023-01-19
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 方法一:暴力列舉 */
|
||||
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;
|
||||
}
|
||||
9
zh-hant/codes/c/chapter_sorting/CMakeLists.txt
Normal file
9
zh-hant/codes/c/chapter_sorting/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
add_executable(bubble_sort bubble_sort.c)
|
||||
add_executable(insertion_sort insertion_sort.c)
|
||||
add_executable(quick_sort quick_sort.c)
|
||||
add_executable(counting_sort counting_sort.c)
|
||||
add_executable(radix_sort radix_sort.c)
|
||||
add_executable(merge_sort merge_sort.c)
|
||||
add_executable(heap_sort heap_sort.c)
|
||||
add_executable(bucket_sort bucket_sort.c)
|
||||
add_executable(selection_sort selection_sort.c)
|
||||
61
zh-hant/codes/c/chapter_sorting/bubble_sort.c
Normal file
61
zh-hant/codes/c/chapter_sorting/bubble_sort.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* File: bubble_sort.c
|
||||
* Created Time: 2022-12-26
|
||||
* Author: Listening (https://github.com/L-Super)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 泡沫排序 */
|
||||
void bubbleSort(int nums[], int size) {
|
||||
// 外迴圈:未排序區間為 [0, i]
|
||||
for (int i = size - 1; i > 0; i--) {
|
||||
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
int temp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 泡沫排序(標誌最佳化)*/
|
||||
void bubbleSortWithFlag(int nums[], int size) {
|
||||
// 外迴圈:未排序區間為 [0, i]
|
||||
for (int i = size - 1; i > 0; i--) {
|
||||
bool flag = false;
|
||||
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
int temp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = temp;
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (!flag)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int nums[6] = {4, 1, 3, 1, 5, 2};
|
||||
printf("泡沫排序後: ");
|
||||
bubbleSort(nums, 6);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
printf("%d ", nums[i]);
|
||||
}
|
||||
|
||||
int nums1[6] = {4, 1, 3, 1, 5, 2};
|
||||
printf("\n最佳化版泡沫排序後: ");
|
||||
bubbleSortWithFlag(nums1, 6);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
printf("%d ", nums1[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
82
zh-hant/codes/c/chapter_sorting/bucket_sort.c
Normal file
82
zh-hant/codes/c/chapter_sorting/bucket_sort.c
Normal 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;
|
||||
}
|
||||
86
zh-hant/codes/c/chapter_sorting/counting_sort.c
Normal file
86
zh-hant/codes/c/chapter_sorting/counting_sort.c
Normal 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;
|
||||
}
|
||||
60
zh-hant/codes/c/chapter_sorting/heap_sort.c
Normal file
60
zh-hant/codes/c/chapter_sorting/heap_sort.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* File: heap_sort.c
|
||||
* Created Time: 2023-05-30
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 堆積的長度為 n ,從節點 i 開始,從頂至底堆積化 */
|
||||
void siftDown(int nums[], int n, int i) {
|
||||
while (1) {
|
||||
// 判斷節點 i, l, r 中值最大的節點,記為 ma
|
||||
int l = 2 * i + 1;
|
||||
int r = 2 * i + 2;
|
||||
int ma = i;
|
||||
if (l < n && nums[l] > nums[ma])
|
||||
ma = l;
|
||||
if (r < n && nums[r] > nums[ma])
|
||||
ma = r;
|
||||
// 若節點 i 最大或索引 l, r 越界,則無須繼續堆積化,跳出
|
||||
if (ma == i) {
|
||||
break;
|
||||
}
|
||||
// 交換兩節點
|
||||
int temp = nums[i];
|
||||
nums[i] = nums[ma];
|
||||
nums[ma] = temp;
|
||||
// 迴圈向下堆積化
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
|
||||
/* 堆積排序 */
|
||||
void heapSort(int nums[], int n) {
|
||||
// 建堆積操作:堆積化除葉節點以外的其他所有節點
|
||||
for (int i = n / 2 - 1; i >= 0; --i) {
|
||||
siftDown(nums, n, i);
|
||||
}
|
||||
// 從堆積中提取最大元素,迴圈 n-1 輪
|
||||
for (int i = n - 1; i > 0; --i) {
|
||||
// 交換根節點與最右葉節點(交換首元素與尾元素)
|
||||
int tmp = nums[0];
|
||||
nums[0] = nums[i];
|
||||
nums[i] = tmp;
|
||||
// 以根節點為起點,從頂至底進行堆積化
|
||||
siftDown(nums, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int nums[] = {4, 1, 3, 1, 5, 2};
|
||||
int n = sizeof(nums) / sizeof(nums[0]);
|
||||
|
||||
heapSort(nums, n);
|
||||
printf("堆積排序完成後 nums = ");
|
||||
printArray(nums, n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
zh-hant/codes/c/chapter_sorting/insertion_sort.c
Normal file
36
zh-hant/codes/c/chapter_sorting/insertion_sort.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* File: insertion_sort.c
|
||||
* Created Time: 2022-12-29
|
||||
* Author: Listening (https://github.com/L-Super)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 插入排序 */
|
||||
void insertionSort(int nums[], int size) {
|
||||
// 外迴圈:已排序區間為 [0, i-1]
|
||||
for (int i = 1; i < size; i++) {
|
||||
int base = nums[i], j = i - 1;
|
||||
// 內迴圈:將 base 插入到已排序區間 [0, i-1] 中的正確位置
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
// 將 nums[j] 向右移動一位
|
||||
nums[j + 1] = nums[j];
|
||||
j--;
|
||||
}
|
||||
// 將 base 賦值到正確位置
|
||||
nums[j + 1] = base;
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int nums[] = {4, 1, 3, 1, 5, 2};
|
||||
insertionSort(nums, 6);
|
||||
printf("插入排序完成後 nums = ");
|
||||
for (int i = 0; i < 6; i++) {
|
||||
printf("%d ", nums[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
63
zh-hant/codes/c/chapter_sorting/merge_sort.c
Normal file
63
zh-hant/codes/c/chapter_sorting/merge_sort.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* File: merge_sort.c
|
||||
* Created Time: 2022-03-21
|
||||
* Author: Guanngxu (446678850@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 合併左子陣列和右子陣列 */
|
||||
void merge(int *nums, int left, int mid, int right) {
|
||||
// 左子陣列區間為 [left, mid], 右子陣列區間為 [mid+1, right]
|
||||
// 建立一個臨時陣列 tmp ,用於存放合併後的結果
|
||||
int tmpSize = right - left + 1;
|
||||
int *tmp = (int *)malloc(tmpSize * sizeof(int));
|
||||
// 初始化左子陣列和右子陣列的起始索引
|
||||
int i = left, j = mid + 1, k = 0;
|
||||
// 當左右子陣列都還有元素時,進行比較並將較小的元素複製到臨時陣列中
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j]) {
|
||||
tmp[k++] = nums[i++];
|
||||
} else {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
}
|
||||
// 將左子陣列和右子陣列的剩餘元素複製到臨時陣列中
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++];
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 將臨時陣列 tmp 中的元素複製回原陣列 nums 的對應區間
|
||||
for (k = 0; k < tmpSize; ++k) {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
// 釋放記憶體
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
/* 合併排序 */
|
||||
void mergeSort(int *nums, int left, int right) {
|
||||
// 終止條件
|
||||
if (left >= right)
|
||||
return; // 當子陣列長度為 1 時終止遞迴
|
||||
// 劃分階段
|
||||
int mid = (left + right) / 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;
|
||||
}
|
||||
134
zh-hant/codes/c/chapter_sorting/quick_sort.c
Normal file
134
zh-hant/codes/c/chapter_sorting/quick_sort.c
Normal 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;
|
||||
}
|
||||
71
zh-hant/codes/c/chapter_sorting/radix_sort.c
Normal file
71
zh-hant/codes/c/chapter_sorting/radix_sort.c
Normal 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);
|
||||
}
|
||||
37
zh-hant/codes/c/chapter_sorting/selection_sort.c
Normal file
37
zh-hant/codes/c/chapter_sorting/selection_sort.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* File: selection_sort.c
|
||||
* Created Time: 2023-05-31
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 選擇排序 */
|
||||
void selectionSort(int nums[], int n) {
|
||||
// 外迴圈:未排序區間為 [i, n-1]
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
// 內迴圈:找到未排序區間內的最小元素
|
||||
int k = i;
|
||||
for (int j = i + 1; j < n; j++) {
|
||||
if (nums[j] < nums[k])
|
||||
k = j; // 記錄最小元素的索引
|
||||
}
|
||||
// 將該最小元素與未排序區間的首個元素交換
|
||||
int temp = nums[i];
|
||||
nums[i] = nums[k];
|
||||
nums[k] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
int nums[] = {4, 1, 3, 1, 5, 2};
|
||||
int n = sizeof(nums) / sizeof(nums[0]);
|
||||
|
||||
selectionSort(nums, n);
|
||||
|
||||
printf("選擇排序完成後 nums = ");
|
||||
printArray(nums, n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
6
zh-hant/codes/c/chapter_stack_and_queue/CMakeLists.txt
Normal file
6
zh-hant/codes/c/chapter_stack_and_queue/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
add_executable(array_stack array_stack.c)
|
||||
add_executable(linkedlist_stack linkedlist_stack.c)
|
||||
add_executable(array_queue array_queue.c)
|
||||
add_executable(linkedlist_queue linkedlist_queue.c)
|
||||
add_executable(array_deque array_deque.c)
|
||||
add_executable(linkedlist_deque linkedlist_deque.c)
|
||||
159
zh-hant/codes/c/chapter_stack_and_queue/array_deque.c
Normal file
159
zh-hant/codes/c/chapter_stack_and_queue/array_deque.c
Normal 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;
|
||||
}
|
||||
121
zh-hant/codes/c/chapter_stack_and_queue/array_queue.c
Normal file
121
zh-hant/codes/c/chapter_stack_and_queue/array_queue.c
Normal 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;
|
||||
}
|
||||
103
zh-hant/codes/c/chapter_stack_and_queue/array_stack.c
Normal file
103
zh-hant/codes/c/chapter_stack_and_queue/array_stack.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* File: array_stack.c
|
||||
* Created Time: 2023-01-12
|
||||
* Author: Zero (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
#define MAX_SIZE 5000
|
||||
|
||||
/* 基於陣列實現的堆疊 */
|
||||
typedef struct {
|
||||
int *data;
|
||||
int size;
|
||||
} ArrayStack;
|
||||
|
||||
/* 建構子 */
|
||||
ArrayStack *newArrayStack() {
|
||||
ArrayStack *stack = malloc(sizeof(ArrayStack));
|
||||
// 初始化一個大容量,避免擴容
|
||||
stack->data = malloc(sizeof(int) * MAX_SIZE);
|
||||
stack->size = 0;
|
||||
return stack;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delArrayStack(ArrayStack *stack) {
|
||||
free(stack->data);
|
||||
free(stack);
|
||||
}
|
||||
|
||||
/* 獲取堆疊的長度 */
|
||||
int size(ArrayStack *stack) {
|
||||
return stack->size;
|
||||
}
|
||||
|
||||
/* 判斷堆疊是否為空 */
|
||||
bool isEmpty(ArrayStack *stack) {
|
||||
return stack->size == 0;
|
||||
}
|
||||
|
||||
/* 入堆疊 */
|
||||
void push(ArrayStack *stack, int num) {
|
||||
if (stack->size == MAX_SIZE) {
|
||||
printf("堆疊已滿\n");
|
||||
return;
|
||||
}
|
||||
stack->data[stack->size] = num;
|
||||
stack->size++;
|
||||
}
|
||||
|
||||
/* 訪問堆疊頂元素 */
|
||||
int peek(ArrayStack *stack) {
|
||||
if (stack->size == 0) {
|
||||
printf("堆疊為空\n");
|
||||
return INT_MAX;
|
||||
}
|
||||
return stack->data[stack->size - 1];
|
||||
}
|
||||
|
||||
/* 出堆疊 */
|
||||
int pop(ArrayStack *stack) {
|
||||
int val = peek(stack);
|
||||
stack->size--;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化堆疊 */
|
||||
ArrayStack *stack = newArrayStack();
|
||||
|
||||
/* 元素入堆疊 */
|
||||
push(stack, 1);
|
||||
push(stack, 3);
|
||||
push(stack, 2);
|
||||
push(stack, 5);
|
||||
push(stack, 4);
|
||||
printf("堆疊 stack = ");
|
||||
printArray(stack->data, stack->size);
|
||||
|
||||
/* 訪問堆疊頂元素 */
|
||||
int val = peek(stack);
|
||||
printf("堆疊頂元素 top = %d\n", val);
|
||||
|
||||
/* 元素出堆疊 */
|
||||
val = pop(stack);
|
||||
printf("出堆疊元素 pop = %d ,出堆疊後 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;
|
||||
}
|
||||
212
zh-hant/codes/c/chapter_stack_and_queue/linkedlist_deque.c
Normal file
212
zh-hant/codes/c/chapter_stack_and_queue/linkedlist_deque.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* File: linkedlist_deque.c
|
||||
* Created Time: 2023-03-13
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 雙向鏈結串列節點 */
|
||||
typedef struct DoublyListNode {
|
||||
int val; // 節點值
|
||||
struct DoublyListNode *next; // 後繼節點
|
||||
struct DoublyListNode *prev; // 前驅節點
|
||||
} DoublyListNode;
|
||||
|
||||
/* 建構子 */
|
||||
DoublyListNode *newDoublyListNode(int num) {
|
||||
DoublyListNode *new = (DoublyListNode *)malloc(sizeof(DoublyListNode));
|
||||
new->val = num;
|
||||
new->next = NULL;
|
||||
new->prev = NULL;
|
||||
return new;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delDoublyListNode(DoublyListNode *node) {
|
||||
free(node);
|
||||
}
|
||||
|
||||
/* 基於雙向鏈結串列實現的雙向佇列 */
|
||||
typedef struct {
|
||||
DoublyListNode *front, *rear; // 頭節點 front ,尾節點 rear
|
||||
int queSize; // 雙向佇列的長度
|
||||
} LinkedListDeque;
|
||||
|
||||
/* 建構子 */
|
||||
LinkedListDeque *newLinkedListDeque() {
|
||||
LinkedListDeque *deque = (LinkedListDeque *)malloc(sizeof(LinkedListDeque));
|
||||
deque->front = NULL;
|
||||
deque->rear = NULL;
|
||||
deque->queSize = 0;
|
||||
return deque;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delLinkedListdeque(LinkedListDeque *deque) {
|
||||
// 釋放所有節點
|
||||
for (int i = 0; i < deque->queSize && deque->front != NULL; i++) {
|
||||
DoublyListNode *tmp = deque->front;
|
||||
deque->front = deque->front->next;
|
||||
free(tmp);
|
||||
}
|
||||
// 釋放 deque 結構體
|
||||
free(deque);
|
||||
}
|
||||
|
||||
/* 獲取佇列的長度 */
|
||||
int size(LinkedListDeque *deque) {
|
||||
return deque->queSize;
|
||||
}
|
||||
|
||||
/* 判斷佇列是否為空 */
|
||||
bool empty(LinkedListDeque *deque) {
|
||||
return (size(deque) == 0);
|
||||
}
|
||||
|
||||
/* 入列 */
|
||||
void push(LinkedListDeque *deque, int num, bool isFront) {
|
||||
DoublyListNode *node = newDoublyListNode(num);
|
||||
// 若鏈結串列為空,則令 front 和 rear 都指向node
|
||||
if (empty(deque)) {
|
||||
deque->front = deque->rear = node;
|
||||
}
|
||||
// 佇列首入列操作
|
||||
else if (isFront) {
|
||||
// 將 node 新增至鏈結串列頭部
|
||||
deque->front->prev = node;
|
||||
node->next = deque->front;
|
||||
deque->front = node; // 更新頭節點
|
||||
}
|
||||
// 佇列尾入列操作
|
||||
else {
|
||||
// 將 node 新增至鏈結串列尾部
|
||||
deque->rear->next = node;
|
||||
node->prev = deque->rear;
|
||||
deque->rear = node;
|
||||
}
|
||||
deque->queSize++; // 更新佇列長度
|
||||
}
|
||||
|
||||
/* 佇列首入列 */
|
||||
void pushFirst(LinkedListDeque *deque, int num) {
|
||||
push(deque, num, true);
|
||||
}
|
||||
|
||||
/* 佇列尾入列 */
|
||||
void pushLast(LinkedListDeque *deque, int num) {
|
||||
push(deque, num, false);
|
||||
}
|
||||
|
||||
/* 訪問佇列首元素 */
|
||||
int peekFirst(LinkedListDeque *deque) {
|
||||
assert(size(deque) && deque->front);
|
||||
return deque->front->val;
|
||||
}
|
||||
|
||||
/* 訪問佇列尾元素 */
|
||||
int peekLast(LinkedListDeque *deque) {
|
||||
assert(size(deque) && deque->rear);
|
||||
return deque->rear->val;
|
||||
}
|
||||
|
||||
/* 出列 */
|
||||
int pop(LinkedListDeque *deque, bool isFront) {
|
||||
if (empty(deque))
|
||||
return -1;
|
||||
int val;
|
||||
// 佇列首出列操作
|
||||
if (isFront) {
|
||||
val = peekFirst(deque); // 暫存頭節點值
|
||||
DoublyListNode *fNext = deque->front->next;
|
||||
if (fNext) {
|
||||
fNext->prev = NULL;
|
||||
deque->front->next = NULL;
|
||||
delDoublyListNode(deque->front);
|
||||
}
|
||||
deque->front = fNext; // 更新頭節點
|
||||
}
|
||||
// 佇列尾出列操作
|
||||
else {
|
||||
val = peekLast(deque); // 暫存尾節點值
|
||||
DoublyListNode *rPrev = deque->rear->prev;
|
||||
if (rPrev) {
|
||||
rPrev->next = NULL;
|
||||
deque->rear->prev = NULL;
|
||||
delDoublyListNode(deque->rear);
|
||||
}
|
||||
deque->rear = rPrev; // 更新尾節點
|
||||
}
|
||||
deque->queSize--; // 更新佇列長度
|
||||
return val;
|
||||
}
|
||||
|
||||
/* 佇列首出列 */
|
||||
int popFirst(LinkedListDeque *deque) {
|
||||
return pop(deque, true);
|
||||
}
|
||||
|
||||
/* 佇列尾出列 */
|
||||
int popLast(LinkedListDeque *deque) {
|
||||
return pop(deque, false);
|
||||
}
|
||||
|
||||
/* 列印佇列 */
|
||||
void printLinkedListDeque(LinkedListDeque *deque) {
|
||||
int *arr = malloc(sizeof(int) * deque->queSize);
|
||||
// 複製鏈結串列中的資料到陣列
|
||||
int i;
|
||||
DoublyListNode *node;
|
||||
for (i = 0, node = deque->front; i < deque->queSize; i++) {
|
||||
arr[i] = node->val;
|
||||
node = node->next;
|
||||
}
|
||||
printArray(arr, deque->queSize);
|
||||
free(arr);
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化雙向佇列 */
|
||||
LinkedListDeque *deque = newLinkedListDeque();
|
||||
pushLast(deque, 3);
|
||||
pushLast(deque, 2);
|
||||
pushLast(deque, 5);
|
||||
printf("雙向佇列 deque = ");
|
||||
printLinkedListDeque(deque);
|
||||
|
||||
/* 訪問元素 */
|
||||
int peekFirstNum = peekFirst(deque);
|
||||
printf("佇列首元素 peekFirst = %d\r\n", peekFirstNum);
|
||||
int peekLastNum = peekLast(deque);
|
||||
printf("佇列首元素 peekLast = %d\r\n", peekLastNum);
|
||||
|
||||
/* 元素入列 */
|
||||
pushLast(deque, 4);
|
||||
printf("元素 4 佇列尾入列後 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;
|
||||
}
|
||||
128
zh-hant/codes/c/chapter_stack_and_queue/linkedlist_queue.c
Normal file
128
zh-hant/codes/c/chapter_stack_and_queue/linkedlist_queue.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* File: linkedlist_queue.c
|
||||
* Created Time: 2023-03-13
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 基於鏈結串列實現的佇列 */
|
||||
typedef struct {
|
||||
ListNode *front, *rear;
|
||||
int queSize;
|
||||
} LinkedListQueue;
|
||||
|
||||
/* 建構子 */
|
||||
LinkedListQueue *newLinkedListQueue() {
|
||||
LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue));
|
||||
queue->front = NULL;
|
||||
queue->rear = NULL;
|
||||
queue->queSize = 0;
|
||||
return queue;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delLinkedListQueue(LinkedListQueue *queue) {
|
||||
// 釋放所有節點
|
||||
while (queue->front != NULL) {
|
||||
ListNode *tmp = queue->front;
|
||||
queue->front = queue->front->next;
|
||||
free(tmp);
|
||||
}
|
||||
// 釋放 queue 結構體
|
||||
free(queue);
|
||||
}
|
||||
|
||||
/* 獲取佇列的長度 */
|
||||
int size(LinkedListQueue *queue) {
|
||||
return queue->queSize;
|
||||
}
|
||||
|
||||
/* 判斷佇列是否為空 */
|
||||
bool empty(LinkedListQueue *queue) {
|
||||
return (size(queue) == 0);
|
||||
}
|
||||
|
||||
/* 入列 */
|
||||
void push(LinkedListQueue *queue, int num) {
|
||||
// 尾節點處新增 node
|
||||
ListNode *node = newListNode(num);
|
||||
// 如果佇列為空,則令頭、尾節點都指向該節點
|
||||
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;
|
||||
}
|
||||
107
zh-hant/codes/c/chapter_stack_and_queue/linkedlist_stack.c
Normal file
107
zh-hant/codes/c/chapter_stack_and_queue/linkedlist_stack.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* File: linkedlist_stack.c
|
||||
* Created Time: 2023-01-12
|
||||
* Author: Zero (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 基於鏈結串列實現的堆疊 */
|
||||
typedef struct {
|
||||
ListNode *top; // 將頭節點作為堆疊頂
|
||||
int size; // 堆疊的長度
|
||||
} LinkedListStack;
|
||||
|
||||
/* 建構子 */
|
||||
LinkedListStack *newLinkedListStack() {
|
||||
LinkedListStack *s = malloc(sizeof(LinkedListStack));
|
||||
s->top = NULL;
|
||||
s->size = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delLinkedListStack(LinkedListStack *s) {
|
||||
while (s->top) {
|
||||
ListNode *n = s->top->next;
|
||||
free(s->top);
|
||||
s->top = n;
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* 獲取堆疊的長度 */
|
||||
int size(LinkedListStack *s) {
|
||||
return s->size;
|
||||
}
|
||||
|
||||
/* 判斷堆疊是否為空 */
|
||||
bool isEmpty(LinkedListStack *s) {
|
||||
return size(s) == 0;
|
||||
}
|
||||
|
||||
/* 入堆疊 */
|
||||
void push(LinkedListStack *s, int num) {
|
||||
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
|
||||
node->next = s->top; // 更新新加節點指標域
|
||||
node->val = num; // 更新新加節點資料域
|
||||
s->top = node; // 更新堆疊頂
|
||||
s->size++; // 更新堆疊大小
|
||||
}
|
||||
|
||||
/* 訪問堆疊頂元素 */
|
||||
int peek(LinkedListStack *s) {
|
||||
if (s->size == 0) {
|
||||
printf("堆疊為空\n");
|
||||
return INT_MAX;
|
||||
}
|
||||
return s->top->val;
|
||||
}
|
||||
|
||||
/* 出堆疊 */
|
||||
int pop(LinkedListStack *s) {
|
||||
int val = peek(s);
|
||||
ListNode *tmp = s->top;
|
||||
s->top = s->top->next;
|
||||
// 釋放記憶體
|
||||
free(tmp);
|
||||
s->size--;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化堆疊 */
|
||||
LinkedListStack *stack = newLinkedListStack();
|
||||
|
||||
/* 元素入堆疊 */
|
||||
push(stack, 1);
|
||||
push(stack, 3);
|
||||
push(stack, 2);
|
||||
push(stack, 5);
|
||||
push(stack, 4);
|
||||
|
||||
printf("堆疊 stack = ");
|
||||
printLinkedList(stack->top);
|
||||
|
||||
/* 訪問堆疊頂元素 */
|
||||
int val = peek(stack);
|
||||
printf("堆疊頂元素 top = %d\r\n", val);
|
||||
|
||||
/* 元素出堆疊 */
|
||||
val = pop(stack);
|
||||
printf("出堆疊元素 pop = %d, 出堆疊後 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;
|
||||
}
|
||||
6
zh-hant/codes/c/chapter_tree/CMakeLists.txt
Normal file
6
zh-hant/codes/c/chapter_tree/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
add_executable(avl_tree avl_tree.c)
|
||||
add_executable(binary_tree binary_tree.c)
|
||||
add_executable(binary_tree_bfs binary_tree_bfs.c)
|
||||
add_executable(binary_tree_dfs binary_tree_dfs.c)
|
||||
add_executable(binary_search_tree binary_search_tree.c)
|
||||
add_executable(array_binary_tree array_binary_tree.c)
|
||||
166
zh-hant/codes/c/chapter_tree/array_binary_tree.c
Normal file
166
zh-hant/codes/c/chapter_tree/array_binary_tree.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* File: array_binary_tree.c
|
||||
* Created Time: 2023-07-29
|
||||
* Author: Gonglja (glj0@outlook.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 陣列表示下的二元樹結構體 */
|
||||
typedef struct {
|
||||
int *tree;
|
||||
int size;
|
||||
} ArrayBinaryTree;
|
||||
|
||||
/* 建構子 */
|
||||
ArrayBinaryTree *newArrayBinaryTree(int *arr, int arrSize) {
|
||||
ArrayBinaryTree *abt = (ArrayBinaryTree *)malloc(sizeof(ArrayBinaryTree));
|
||||
abt->tree = malloc(sizeof(int) * arrSize);
|
||||
memcpy(abt->tree, arr, sizeof(int) * arrSize);
|
||||
abt->size = arrSize;
|
||||
return abt;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delArrayBinaryTree(ArrayBinaryTree *abt) {
|
||||
free(abt->tree);
|
||||
free(abt);
|
||||
}
|
||||
|
||||
/* 串列容量 */
|
||||
int size(ArrayBinaryTree *abt) {
|
||||
return abt->size;
|
||||
}
|
||||
|
||||
/* 獲取索引為 i 節點的值 */
|
||||
int val(ArrayBinaryTree *abt, int i) {
|
||||
// 若索引越界,則返回 INT_MAX ,代表空位
|
||||
if (i < 0 || i >= size(abt))
|
||||
return INT_MAX;
|
||||
return abt->tree[i];
|
||||
}
|
||||
|
||||
/* 獲取索引為 i 節點的左子節點的索引 */
|
||||
int left(int i) {
|
||||
return 2 * i + 1;
|
||||
}
|
||||
|
||||
/* 獲取索引為 i 節點的右子節點的索引 */
|
||||
int right(int i) {
|
||||
return 2 * i + 2;
|
||||
}
|
||||
|
||||
/* 獲取索引為 i 節點的父節點的索引 */
|
||||
int parent(int i) {
|
||||
return (i - 1) / 2;
|
||||
}
|
||||
|
||||
/* 層序走訪 */
|
||||
int *levelOrder(ArrayBinaryTree *abt, int *returnSize) {
|
||||
int *res = (int *)malloc(sizeof(int) * size(abt));
|
||||
int index = 0;
|
||||
// 直接走訪陣列
|
||||
for (int i = 0; i < size(abt); i++) {
|
||||
if (val(abt, i) != INT_MAX)
|
||||
res[index++] = val(abt, i);
|
||||
}
|
||||
*returnSize = index;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 深度優先走訪 */
|
||||
void dfs(ArrayBinaryTree *abt, int i, char *order, int *res, int *index) {
|
||||
// 若為空位,則返回
|
||||
if (val(abt, i) == INT_MAX)
|
||||
return;
|
||||
// 前序走訪
|
||||
if (strcmp(order, "pre") == 0)
|
||||
res[(*index)++] = val(abt, i);
|
||||
dfs(abt, left(i), order, res, index);
|
||||
// 中序走訪
|
||||
if (strcmp(order, "in") == 0)
|
||||
res[(*index)++] = val(abt, i);
|
||||
dfs(abt, right(i), order, res, index);
|
||||
// 後序走訪
|
||||
if (strcmp(order, "post") == 0)
|
||||
res[(*index)++] = val(abt, i);
|
||||
}
|
||||
|
||||
/* 前序走訪 */
|
||||
int *preOrder(ArrayBinaryTree *abt, int *returnSize) {
|
||||
int *res = (int *)malloc(sizeof(int) * size(abt));
|
||||
int index = 0;
|
||||
dfs(abt, 0, "pre", res, &index);
|
||||
*returnSize = index;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 中序走訪 */
|
||||
int *inOrder(ArrayBinaryTree *abt, int *returnSize) {
|
||||
int *res = (int *)malloc(sizeof(int) * size(abt));
|
||||
int index = 0;
|
||||
dfs(abt, 0, "in", res, &index);
|
||||
*returnSize = index;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 後序走訪 */
|
||||
int *postOrder(ArrayBinaryTree *abt, int *returnSize) {
|
||||
int *res = (int *)malloc(sizeof(int) * size(abt));
|
||||
int index = 0;
|
||||
dfs(abt, 0, "post", res, &index);
|
||||
*returnSize = index;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
// 初始化二元樹
|
||||
// 使用 INT_MAX 代表空位 NULL
|
||||
int arr[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15};
|
||||
int arrSize = sizeof(arr) / sizeof(arr[0]);
|
||||
TreeNode *root = arrayToTree(arr, arrSize);
|
||||
printf("\n初始化二元樹\n");
|
||||
printf("二元樹的陣列表示:\n");
|
||||
printArray(arr, arrSize);
|
||||
printf("二元樹的鏈結串列表示:\n");
|
||||
printTree(root);
|
||||
|
||||
ArrayBinaryTree *abt = newArrayBinaryTree(arr, arrSize);
|
||||
|
||||
// 訪問節點
|
||||
int i = 1;
|
||||
int l = left(i), r = right(i), p = parent(i);
|
||||
printf("\n當前節點的索引為 %d,值為 %d\n", i, val(abt, i));
|
||||
printf("其左子節點的索引為 %d,值為 %d\n", l, l < arrSize ? val(abt, l) : INT_MAX);
|
||||
printf("其右子節點的索引為 %d,值為 %d\n", r, r < arrSize ? val(abt, r) : INT_MAX);
|
||||
printf("其父節點的索引為 %d,值為 %d\n", p, p < arrSize ? val(abt, p) : INT_MAX);
|
||||
|
||||
// 走訪樹
|
||||
int returnSize;
|
||||
int *res;
|
||||
|
||||
res = levelOrder(abt, &returnSize);
|
||||
printf("\n層序走訪為: ");
|
||||
printArray(res, returnSize);
|
||||
free(res);
|
||||
|
||||
res = preOrder(abt, &returnSize);
|
||||
printf("前序走訪為: ");
|
||||
printArray(res, returnSize);
|
||||
free(res);
|
||||
|
||||
res = inOrder(abt, &returnSize);
|
||||
printf("中序走訪為: ");
|
||||
printArray(res, returnSize);
|
||||
free(res);
|
||||
|
||||
res = postOrder(abt, &returnSize);
|
||||
printf("後序走訪為: ");
|
||||
printArray(res, returnSize);
|
||||
free(res);
|
||||
|
||||
// 釋放記憶體
|
||||
delArrayBinaryTree(abt);
|
||||
return 0;
|
||||
}
|
||||
259
zh-hant/codes/c/chapter_tree/avl_tree.c
Normal file
259
zh-hant/codes/c/chapter_tree/avl_tree.c
Normal file
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* File: avl_tree.c
|
||||
* Created Time: 2023-01-15
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* AVL 樹結構體 */
|
||||
typedef struct {
|
||||
TreeNode *root;
|
||||
} AVLTree;
|
||||
|
||||
/* 建構子 */
|
||||
AVLTree *newAVLTree() {
|
||||
AVLTree *tree = (AVLTree *)malloc(sizeof(AVLTree));
|
||||
tree->root = NULL;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delAVLTree(AVLTree *tree) {
|
||||
freeMemoryTree(tree->root);
|
||||
free(tree);
|
||||
}
|
||||
|
||||
/* 獲取節點高度 */
|
||||
int height(TreeNode *node) {
|
||||
// 空節點高度為 -1 ,葉節點高度為 0
|
||||
if (node != NULL) {
|
||||
return node->height;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 更新節點高度 */
|
||||
void updateHeight(TreeNode *node) {
|
||||
int lh = height(node->left);
|
||||
int rh = height(node->right);
|
||||
// 節點高度等於最高子樹高度 + 1
|
||||
if (lh > rh) {
|
||||
node->height = lh + 1;
|
||||
} else {
|
||||
node->height = rh + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 獲取平衡因子 */
|
||||
int balanceFactor(TreeNode *node) {
|
||||
// 空節點平衡因子為 0
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
// 節點平衡因子 = 左子樹高度 - 右子樹高度
|
||||
return height(node->left) - height(node->right);
|
||||
}
|
||||
|
||||
/* 右旋操作 */
|
||||
TreeNode *rightRotate(TreeNode *node) {
|
||||
TreeNode *child, *grandChild;
|
||||
child = node->left;
|
||||
grandChild = child->right;
|
||||
// 以 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;
|
||||
}
|
||||
171
zh-hant/codes/c/chapter_tree/binary_search_tree.c
Normal file
171
zh-hant/codes/c/chapter_tree/binary_search_tree.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* File: binary_search_tree.c
|
||||
* Created Time: 2023-01-11
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* 二元搜尋樹結構體 */
|
||||
typedef struct {
|
||||
TreeNode *root;
|
||||
} BinarySearchTree;
|
||||
|
||||
/* 建構子 */
|
||||
BinarySearchTree *newBinarySearchTree() {
|
||||
// 初始化空樹
|
||||
BinarySearchTree *bst = (BinarySearchTree *)malloc(sizeof(BinarySearchTree));
|
||||
bst->root = NULL;
|
||||
return bst;
|
||||
}
|
||||
|
||||
/* 析構函式 */
|
||||
void delBinarySearchTree(BinarySearchTree *bst) {
|
||||
freeMemoryTree(bst->root);
|
||||
free(bst);
|
||||
}
|
||||
|
||||
/* 獲取二元樹根節點 */
|
||||
TreeNode *getRoot(BinarySearchTree *bst) {
|
||||
return bst->root;
|
||||
}
|
||||
|
||||
/* 查詢節點 */
|
||||
TreeNode *search(BinarySearchTree *bst, int num) {
|
||||
TreeNode *cur = bst->root;
|
||||
// 迴圈查詢,越過葉節點後跳出
|
||||
while (cur != NULL) {
|
||||
if (cur->val < num) {
|
||||
// 目標節點在 cur 的右子樹中
|
||||
cur = cur->right;
|
||||
} else if (cur->val > num) {
|
||||
// 目標節點在 cur 的左子樹中
|
||||
cur = cur->left;
|
||||
} else {
|
||||
// 找到目標節點,跳出迴圈
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 返回目標節點
|
||||
return cur;
|
||||
}
|
||||
|
||||
/* 插入節點 */
|
||||
void insert(BinarySearchTree *bst, int num) {
|
||||
// 若樹為空,則初始化根節點
|
||||
if (bst->root == NULL) {
|
||||
bst->root = newTreeNode(num);
|
||||
return;
|
||||
}
|
||||
TreeNode *cur = bst->root, *pre = NULL;
|
||||
// 迴圈查詢,越過葉節點後跳出
|
||||
while (cur != NULL) {
|
||||
// 找到重複節點,直接返回
|
||||
if (cur->val == num) {
|
||||
return;
|
||||
}
|
||||
pre = cur;
|
||||
if (cur->val < num) {
|
||||
// 插入位置在 cur 的右子樹中
|
||||
cur = cur->right;
|
||||
} else {
|
||||
// 插入位置在 cur 的左子樹中
|
||||
cur = cur->left;
|
||||
}
|
||||
}
|
||||
// 插入節點
|
||||
TreeNode *node = newTreeNode(num);
|
||||
if (pre->val < num) {
|
||||
pre->right = node;
|
||||
} else {
|
||||
pre->left = node;
|
||||
}
|
||||
}
|
||||
|
||||
/* 刪除節點 */
|
||||
// 由於引入了 stdio.h ,此處無法使用 remove 關鍵詞
|
||||
void removeItem(BinarySearchTree *bst, int num) {
|
||||
// 若樹為空,直接提前返回
|
||||
if (bst->root == NULL)
|
||||
return;
|
||||
TreeNode *cur = bst->root, *pre = NULL;
|
||||
// 迴圈查詢,越過葉節點後跳出
|
||||
while (cur != NULL) {
|
||||
// 找到待刪除節點,跳出迴圈
|
||||
if (cur->val == num)
|
||||
break;
|
||||
pre = cur;
|
||||
if (cur->val < num) {
|
||||
// 待刪除節點在 root 的右子樹中
|
||||
cur = cur->right;
|
||||
} else {
|
||||
// 待刪除節點在 root 的左子樹中
|
||||
cur = cur->left;
|
||||
}
|
||||
}
|
||||
// 若無待刪除節點,則直接返回
|
||||
if (cur == NULL)
|
||||
return;
|
||||
// 判斷待刪除節點是否存在子節點
|
||||
if (cur->left == NULL || cur->right == NULL) {
|
||||
/* 子節點數量 = 0 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;
|
||||
}
|
||||
43
zh-hant/codes/c/chapter_tree/binary_tree.c
Normal file
43
zh-hant/codes/c/chapter_tree/binary_tree.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* File: binary_tree.c
|
||||
* Created Time: 2023-01-11
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化二元樹 */
|
||||
// 初始化節點
|
||||
TreeNode *n1 = newTreeNode(1);
|
||||
TreeNode *n2 = newTreeNode(2);
|
||||
TreeNode *n3 = newTreeNode(3);
|
||||
TreeNode *n4 = newTreeNode(4);
|
||||
TreeNode *n5 = newTreeNode(5);
|
||||
// 構建節點之間的引用(指標)
|
||||
n1->left = n2;
|
||||
n1->right = n3;
|
||||
n2->left = n4;
|
||||
n2->right = n5;
|
||||
printf("初始化二元樹\n");
|
||||
printTree(n1);
|
||||
|
||||
/* 插入與刪除節點 */
|
||||
TreeNode *P = newTreeNode(0);
|
||||
// 在 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;
|
||||
}
|
||||
73
zh-hant/codes/c/chapter_tree/binary_tree_bfs.c
Normal file
73
zh-hant/codes/c/chapter_tree/binary_tree_bfs.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* File: binary_tree_bfs.c
|
||||
* Created Time: 2023-01-11
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
#define MAX_SIZE 100
|
||||
|
||||
/* 層序走訪 */
|
||||
int *levelOrder(TreeNode *root, int *size) {
|
||||
/* 輔助佇列 */
|
||||
int front, rear;
|
||||
int index, *arr;
|
||||
TreeNode *node;
|
||||
TreeNode **queue;
|
||||
|
||||
/* 輔助佇列 */
|
||||
queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_SIZE);
|
||||
// 佇列指標
|
||||
front = 0, rear = 0;
|
||||
// 加入根節點
|
||||
queue[rear++] = root;
|
||||
// 初始化一個串列,用於儲存走訪序列
|
||||
/* 輔助陣列 */
|
||||
arr = (int *)malloc(sizeof(int) * MAX_SIZE);
|
||||
// 陣列指標
|
||||
index = 0;
|
||||
while (front < rear) {
|
||||
// 隊列出隊
|
||||
node = queue[front++];
|
||||
// 儲存節點值
|
||||
arr[index++] = node->val;
|
||||
if (node->left != NULL) {
|
||||
// 左子節點入列
|
||||
queue[rear++] = node->left;
|
||||
}
|
||||
if (node->right != NULL) {
|
||||
// 右子節點入列
|
||||
queue[rear++] = node->right;
|
||||
}
|
||||
}
|
||||
// 更新陣列長度的值
|
||||
*size = index;
|
||||
arr = realloc(arr, sizeof(int) * (*size));
|
||||
|
||||
// 釋放輔助陣列空間
|
||||
free(queue);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化二元樹 */
|
||||
// 這裡藉助了一個從陣列直接生成二元樹的函式
|
||||
int nums[] = {1, 2, 3, 4, 5, 6, 7};
|
||||
int size = sizeof(nums) / sizeof(int);
|
||||
TreeNode *root = arrayToTree(nums, size);
|
||||
printf("初始化二元樹\n");
|
||||
printTree(root);
|
||||
|
||||
/* 層序走訪 */
|
||||
// 需要傳入陣列的長度
|
||||
int *arr = levelOrder(root, &size);
|
||||
printf("層序走訪的節點列印序列 = ");
|
||||
printArray(arr, size);
|
||||
|
||||
// 釋放記憶體
|
||||
freeMemoryTree(root);
|
||||
free(arr);
|
||||
return 0;
|
||||
}
|
||||
75
zh-hant/codes/c/chapter_tree/binary_tree_dfs.c
Normal file
75
zh-hant/codes/c/chapter_tree/binary_tree_dfs.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* File: binary_tree_dfs.c
|
||||
* Created Time: 2023-01-11
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.h"
|
||||
|
||||
#define MAX_SIZE 100
|
||||
|
||||
// 輔助陣列,用於儲存走訪序列
|
||||
int arr[MAX_SIZE];
|
||||
|
||||
/* 前序走訪 */
|
||||
void preOrder(TreeNode *root, int *size) {
|
||||
if (root == NULL)
|
||||
return;
|
||||
// 訪問優先順序:根節點 -> 左子樹 -> 右子樹
|
||||
arr[(*size)++] = root->val;
|
||||
preOrder(root->left, size);
|
||||
preOrder(root->right, size);
|
||||
}
|
||||
|
||||
/* 中序走訪 */
|
||||
void inOrder(TreeNode *root, int *size) {
|
||||
if (root == NULL)
|
||||
return;
|
||||
// 訪問優先順序:左子樹 -> 根節點 -> 右子樹
|
||||
inOrder(root->left, size);
|
||||
arr[(*size)++] = root->val;
|
||||
inOrder(root->right, size);
|
||||
}
|
||||
|
||||
/* 後序走訪 */
|
||||
void postOrder(TreeNode *root, int *size) {
|
||||
if (root == NULL)
|
||||
return;
|
||||
// 訪問優先順序:左子樹 -> 右子樹 -> 根節點
|
||||
postOrder(root->left, size);
|
||||
postOrder(root->right, size);
|
||||
arr[(*size)++] = root->val;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化二元樹 */
|
||||
// 這裡藉助了一個從陣列直接生成二元樹的函式
|
||||
int nums[] = {1, 2, 3, 4, 5, 6, 7};
|
||||
int size = sizeof(nums) / sizeof(int);
|
||||
TreeNode *root = arrayToTree(nums, size);
|
||||
printf("初始化二元樹\n");
|
||||
printTree(root);
|
||||
|
||||
/* 前序走訪 */
|
||||
// 初始化輔助陣列
|
||||
size = 0;
|
||||
preOrder(root, &size);
|
||||
printf("前序走訪的節點列印序列 = ");
|
||||
printArray(arr, size);
|
||||
|
||||
/* 中序走訪 */
|
||||
size = 0;
|
||||
inOrder(root, &size);
|
||||
printf("中序走訪的節點列印序列 = ");
|
||||
printArray(arr, size);
|
||||
|
||||
/* 後序走訪 */
|
||||
size = 0;
|
||||
postOrder(root, &size);
|
||||
printf("後序走訪的節點列印序列 = ");
|
||||
printArray(arr, size);
|
||||
|
||||
freeMemoryTree(root);
|
||||
return 0;
|
||||
}
|
||||
5
zh-hant/codes/c/utils/CMakeLists.txt
Normal file
5
zh-hant/codes/c/utils/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
add_executable(utils
|
||||
common_test.c
|
||||
common.h print_util.h
|
||||
list_node.h tree_node.h
|
||||
uthash.h)
|
||||
36
zh-hant/codes/c/utils/common.h
Normal file
36
zh-hant/codes/c/utils/common.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* File: common.h
|
||||
* Created Time: 2022-12-20
|
||||
* Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#ifndef 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
|
||||
35
zh-hant/codes/c/utils/common_test.c
Normal file
35
zh-hant/codes/c/utils/common_test.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* File: include_test.c
|
||||
* Created Time: 2023-01-10
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void testListNode() {
|
||||
int nums[] = {2, 3, 5, 6, 7};
|
||||
int size = sizeof(nums) / sizeof(int);
|
||||
ListNode *head = arrToLinkedList(nums, size);
|
||||
printLinkedList(head);
|
||||
}
|
||||
|
||||
void testTreeNode() {
|
||||
int nums[] = {1, 2, 3, INT_MAX, 5, 6, INT_MAX};
|
||||
int size = sizeof(nums) / sizeof(int);
|
||||
TreeNode *root = arrayToTree(nums, size);
|
||||
|
||||
// print tree
|
||||
printTree(root);
|
||||
|
||||
// tree to arr
|
||||
int *arr = treeToArray(root, &size);
|
||||
printArray(arr, size);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("==testListNode==\n");
|
||||
testListNode();
|
||||
printf("==testTreeNode==\n");
|
||||
testTreeNode();
|
||||
return 0;
|
||||
}
|
||||
59
zh-hant/codes/c/utils/list_node.h
Normal file
59
zh-hant/codes/c/utils/list_node.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* File: list_node.h
|
||||
* Created Time: 2023-01-09
|
||||
* Author: Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#ifndef LIST_NODE_H
|
||||
#define LIST_NODE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 鏈結串列節點結構體 */
|
||||
typedef struct ListNode {
|
||||
int val; // 節點值
|
||||
struct ListNode *next; // 指向下一節點的引用
|
||||
} ListNode;
|
||||
|
||||
/* 建構子,初始化一個新節點 */
|
||||
ListNode *newListNode(int val) {
|
||||
ListNode *node;
|
||||
node = (ListNode *)malloc(sizeof(ListNode));
|
||||
node->val = val;
|
||||
node->next = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* 將陣列反序列化為鏈結串列 */
|
||||
ListNode *arrToLinkedList(const int *arr, size_t size) {
|
||||
if (size <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListNode *dummy = newListNode(0);
|
||||
ListNode *node = dummy;
|
||||
for (int i = 0; i < size; i++) {
|
||||
node->next = newListNode(arr[i]);
|
||||
node = node->next;
|
||||
}
|
||||
return dummy->next;
|
||||
}
|
||||
|
||||
/* 釋放分配給鏈結串列的記憶體空間 */
|
||||
void freeMemoryLinkedList(ListNode *cur) {
|
||||
// 釋放記憶體
|
||||
ListNode *pre;
|
||||
while (cur != NULL) {
|
||||
pre = cur;
|
||||
cur = cur->next;
|
||||
free(pre);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIST_NODE_H
|
||||
131
zh-hant/codes/c/utils/print_util.h
Normal file
131
zh-hant/codes/c/utils/print_util.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* File: print_util.h
|
||||
* Created Time: 2022-12-21
|
||||
* Author: MolDum (moldum@163.com), Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#ifndef PRINT_UTIL_H
|
||||
#define PRINT_UTIL_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "list_node.h"
|
||||
#include "tree_node.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 列印陣列 */
|
||||
void printArray(int arr[], int size) {
|
||||
if (arr == NULL || size == 0) {
|
||||
printf("[]");
|
||||
return;
|
||||
}
|
||||
printf("[");
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
printf("%d, ", arr[i]);
|
||||
}
|
||||
printf("%d]\n", arr[size - 1]);
|
||||
}
|
||||
|
||||
/* 列印陣列 */
|
||||
void printArrayFloat(float arr[], int size) {
|
||||
if (arr == NULL || size == 0) {
|
||||
printf("[]");
|
||||
return;
|
||||
}
|
||||
printf("[");
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
printf("%.2f, ", arr[i]);
|
||||
}
|
||||
printf("%.2f]\n", arr[size - 1]);
|
||||
}
|
||||
|
||||
/* 列印鏈結串列 */
|
||||
void printLinkedList(ListNode *node) {
|
||||
if (node == NULL) {
|
||||
return;
|
||||
}
|
||||
while (node->next != NULL) {
|
||||
printf("%d -> ", node->val);
|
||||
node = node->next;
|
||||
}
|
||||
printf("%d\n", node->val);
|
||||
}
|
||||
|
||||
typedef struct Trunk {
|
||||
struct Trunk *prev;
|
||||
char *str;
|
||||
} Trunk;
|
||||
|
||||
Trunk *newTrunk(Trunk *prev, char *str) {
|
||||
Trunk *trunk = (Trunk *)malloc(sizeof(Trunk));
|
||||
trunk->prev = prev;
|
||||
trunk->str = (char *)malloc(sizeof(char) * 10);
|
||||
strcpy(trunk->str, str);
|
||||
return trunk;
|
||||
}
|
||||
|
||||
void showTrunks(Trunk *trunk) {
|
||||
if (trunk == NULL) {
|
||||
return;
|
||||
}
|
||||
showTrunks(trunk->prev);
|
||||
printf("%s", trunk->str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列印二元樹
|
||||
* 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
|
||||
107
zh-hant/codes/c/utils/tree_node.h
Normal file
107
zh-hant/codes/c/utils/tree_node.h
Normal 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
|
||||
1140
zh-hant/codes/c/utils/uthash.h
Normal file
1140
zh-hant/codes/c/utils/uthash.h
Normal file
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
Reference in New Issue
Block a user