Re-translate the Japanese version (#1871)

* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
This commit is contained in:
Yudong Jin
2026-03-30 07:30:15 +08:00
committed by GitHub
parent fe6443235b
commit d7b2277d2b
1444 changed files with 83312 additions and 8363 deletions

View File

@@ -1,134 +0,0 @@
# 中国語から日本語への貢献ガイドライン
「Hello アルゴリズム」を中国語から日本語に翻訳するにあたり、以下のアプローチを採用しています:
1. **AI翻訳**: 大規模言語モデルを使用して初期翻訳を実施します。
2. **人による最適化**: 機械生成された出力を手動で改良し、正確性と自然さを確保します。
3. **プルリクエストレビュー**: 最適化された翻訳は、GitHubのプルリクエストワークフローを通じてレビュアーによって二重チェックされます。
4. さらなる改善のため、ステップ `2.``3.` を繰り返します。
<img width="650" alt="translation_pipeline" src="https://github.com/user-attachments/assets/201930ef-723e-4179-b670-e5a084a8211e">
## 参加方法
以下の基準を満たす貢献者を求めています:
- **技術的背景**: コンピュータサイエンス、特にデータ構造とアルゴリズムに関する強固な基礎知識
- **言語スキル**: 日本語ネイティブまたは日本語に精通した方、中国語の読解力
- **利用可能な時間**: オープンソースプロジェクトへの貢献に専念し、長期的な翻訳作業に参加する意欲
つまり、私たちの貢献者は、さまざまな言語背景を持つコンピュータサイエンティスト、エンジニア、学生であり、それぞれの目的には異なる焦点があります:
- **中国語読解力を持つ日本語ネイティブ**: 中国語版と日本語版の間の翻訳の正確性と一貫性を確保する
- **日本語に精通した中国語話者**: 日本語コンテンツの自然さと流暢さを向上させ、自然で読みやすいものにする
> [!note]
> 参加にご興味がある方は、お気軽に krahetx@gmail.com またはWeChat `krahets-jyd` までご連絡ください。
>
> 進捗管理とタスク割り当てには、この[Notionページ](https://hello-algo.notion.site/chinese-to-english)を使用しています。詳細はこちらをご覧ください。
## 翻訳プロセス
> [!important]
> 作業を開始する前に、GitHubのプルリクエストワークフローに慣れ、以下の「翻訳基準」と「翻訳のための疑似コード」を必ずお読みください。
1. **タスク割り当て**: Notionワークスペースでタスクを自己割り当てします。
2. **翻訳**: ローカルPCで翻訳を最適化します。詳細は以下の「翻訳疑似コード」セクションを参照してください。
3. **ピアレビュー**: プルリクエストPRを提出する前に、変更を慎重にレビューしてください。PRは2名のレビュアーの承認後にメインブランチにマージされます。
## 翻訳基準
> [!tip]
> **「正確性」と「自然さ」は、主に中国語を理解できる日本語ネイティブスピーカーによって扱われます。**
>
> 場合によっては、「正確性(一貫性)」と「自然さ」はトレードオフの関係にあり、一方を最適化すると他方に大きな影響を与える可能性があります。そのような場合は、プルリクエストにコメントを残して議論してください。
**正確性**:
- [用語集](https://www.hello-algo.com/chapter_appendix/terminology/)セクションを参照して、翻訳全体で用語の一貫性を保ちます。
- 技術的な正確性を優先し、中国語版のトーンとスタイルを維持します。
- 修正が正確で包括的であることを確保するため、常に中国語版の内容とコンテキストを考慮してください。
**自然さ**:
- 翻訳は日本語の表現慣習に従い、自然で流暢に読めるようにすべきです。
- 記事を調和させるために、常にコンテンツのコンテキストを考慮してください。
- 中国語と日本語の文化的な違いに注意してください。例えば、中国語の「拼音」は日本語には存在しません。
- 最適化された文が元の意味を変える可能性がある場合は、議論のためにコメントを追加してください。
**フォーマット**:
- 図表は展開時に自動的に番号付けされるため、手動で番号を付けないでください。
- バグ修正を除き、各PRは管理可能なレビューサイズを確保するため、少なくとも1つの完全なドキュメントをカバーすべきです。
**レビュー**:
- レビュー中は、変更の評価を優先し、必要に応じて周囲のコンテキストを参照してください。
- お互いの視点から学ぶことで、より良い翻訳とより一貫性のある結果につながります。
## 翻訳疑似コード
以下の疑似コードは、典型的な翻訳プロセスのステップをモデル化しています。
```python
def optimize_translation(markdown_texts, lang_skill):
"""翻訳を最適化する"""
for sentence in markdown_texts:
"""正確性は主に中国語を理解できる日本語ネイティブスピーカーによって処理される"""
if lang_skill is "日本語ネイティブ + 中国語読解力":
if is_accurate_Chinese_to_Japanese(sentence):
continue
# 正確性を最適化
result = refine_accuracy(sentence)
"""
自然さは主に日本語ネイティブスピーカーによって処理され、
副次的に中国語話者によって処理される
"""
if is_authentic_Japanese(sentence):
continue
# 自然さを最適化
result = refine_authenticity(sentence)
# 一貫性を損なう可能性がある場合はPRにコメントを追加
if break_consistency(result):
add_comment(description)
pull_request = submit_pull_request(markdown_texts)
# PRは2名以上のレビュアーによる承認後にマージされる
while count_approvals(pull_request) < 2:
continue
merge(pull_request)
```
以下はレビュアー向けの疑似コードです:
```python
def review_pull_requests(pull_request, lang_skill):
"""PRをレビューする"""
# PR内のすべての変更をループ
while is_anything_left_to_review(pull_request):
change = get_next_change(pull_request)
"""正確性は主に中国語を理解できる日本語ネイティブスピーカーによって処理される"""
if lang_skill is "日本語ネイティブ + 中国語読解力":
# 中国語版と日本語版の間の正確性(一貫性)をチェック
if is_accurate_Chinese_to_Japanese(change):
continue
# 正確性(一貫性)を最適化
result = refine_accuracy(change)
# PRにコメントを追加
add_comment(result)
"""
自然さは主に日本語ネイティブスピーカーによって処理され、
副次的に中国語話者によって処理される
"""
if is_authentic_Japanese(change):
continue
# 自然な日本語でない場合は自然さを最適化
result = refine_authenticity(change)
# PRにコメントを追加
add_comment(result)
approve(pull_request)
```

View File

@@ -6,12 +6,14 @@
<p align="center">
<img style="height: 60px;" src="https://readme-typing-svg.demolab.com?font=Noto+Sans+JP&weight=400&duration=3500&pause=2000&color=21C8B8&center=true&vCenter=true&random=false&width=200&lines=Hello,+%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0+!" alt="hello-algo-typing-svg" />
</br>
アニメーション図解ワンクリック実行データ構造とアルゴリズム入門講座
アニメーション図解ワンクリック実行コードで学べる、データ構造とアルゴリズム入門
</p>
<p align="center">
<a href="https://www.hello-algo.com/ja/">
<img src="https://www.hello-algo.com/en/index.assets/btn_read_online_dark.svg" height="45"></a>
<a href="https://github.com/krahets/hello-algo/releases">
<img src="https://www.hello-algo.com/en/index.assets/btn_download_pdf_epub_dark.svg" height="45"></a>
</p>
<p align="center">
@@ -49,47 +51,45 @@
## この本について
このオープンソースプロジェクトは、データ構造とアルゴリズムの無料で初心者向けの入門講座を作成することを目的としています。
本プロジェクトは、無料かつオープンソースで、初心者にもやさしいデータ構造とアルゴリズムの入門書を作ることを目的としています。
- アニメーションによる図解、わかりやすい内容、なめらかな学習曲線によ、初者がデータ構造とアルゴリズムの知識マップ」を探索できます。
- ワンクリックでコードを実行でき、読者のプログラミングスキルを向上させ、アルゴリズムの動作原理とデータ構造の基礎となる実装を理解できます。
- 教えることで学ぶことを促進し、質問や洞察を自由に共有してください。議論を通じて一緒に成長しましょう。
- 全編をアニメーション図解で構成し、わかりやすい内容と無理のない学習曲線によって、初者がデータ構造とアルゴリズムの知識地図をたどれるようにしています。
- ソースコードはワンクリックで実行でき、演習を通してプログラミング力を高めながら、アルゴリズムの動作原理とデータ構造の内部実装を理解できます。
- 学び合いを大切にしており、コメント欄での質問や知見の共有を歓迎します。議論を通じて一緒に成長していきましょう。
この本が役立つと思われた場合は、スター :star: を付けてサポートしてください。ありがとうございます
本書が役に立ったら、ページ右上の Star :star: で応援していただけると嬉しいです。ありがとうございます
## 推薦の言葉
> データ構造アルゴリズムに関するわかりやすい本で、読者頭と手を使って学ぶように導きます。アルゴリズム初者に強くお勧めします!
> 「平易でわかりやすいデータ構造アルゴリズム入門書であり、読者頭と手の両方を使う学びへと導いてくれます。アルゴリズム初者に強く薦めます。」
>
> **—— 邓俊辉,清華大学コンピュータサイエンス技術学部**
> **—— 邓俊辉,清華大学計算機科学技術学部教授**
> データ構造とアルゴリズムを学んでいたときに『Hello Algo』があったなら、10 倍簡単だったでしょう!
> 「もし当時『Hello Algo』があれば、データ構造とアルゴリズムの学習は 10 倍は楽だったはずです!」
>
> **—— 李沐Amazon シニアプリンシパルサイエンティスト**
> **—— 李沐Amazon シニアプリンシパルサイエンティスト**
## 特別な感
## 謝
<p align="left">
<a href="https://go.warp.dev/hello-algo">
<img src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-02.png" alt="Warp-Github-LG-02" width="500"></a>
</p>
[Warp は複数の AI エージェントとにコーディングするために構築されています。](https://go.warp.dev/hello-algo)
[Warp は複数の AI エージェントとともにコーディングするために作られています。](https://go.warp.dev/hello-algo)
Warp ターミナルは、洗練された UI と使いやすい AI を兼ね備えており、非常に優れた体験を提供してくれます。
## 貢献
> [!Important]
>
> 日本語版の翻訳作業へのご参加を歓迎します!詳細は [CONTRIBUTING.md](CONTRIBUTING.md) をご覧ください。
本書は現在も継続的に更新されており、読者により良い学習コンテンツを届けるため、プロジェクトへの参加を歓迎しています。
このオープンソースブックは継続的に更新されており、読者により良い学習コンテンツを提供するため、このプロジェクトへの参加を歓迎します。
- [内容の修正](https://www.hello-algo.com/ja/chapter_appendix/contribution/):文法ミス、内容の欠落、表現の曖昧さ、無効なリンク、コードのバグなどがあれば、修正またはコメント欄でのご指摘をお願いします。
- [コードの移植](https://github.com/krahets/hello-algo/issues/15)Python、Java、C++、Go、JavaScript など、現在対応している 12 言語のコード整備への貢献をお待ちしています。
- [内容の修正](https://www.hello-algo.com/ja/chapter_appendix/contribution/):文法エラー、内容の欠落、曖昧さ、無効なリンク、コードのバグなど、コメントセクションで間違いを修正したり指摘したりしてください。
- [コードの移植](https://github.com/krahets/hello-algo/issues/15)さまざまなプログラミング言語でのご貢献をお待ちしています。現在、Python、Java、C++、Go、JavaScript を含む 12 言語をサポートしています。
ご意見・ご提案を歓迎します。ご不明点があれば Issue を作成するか、WeChat の `krahets-jyd` までご連絡ください。
貴重なご提案とフィードバックを歓迎します。ご質問がある場合は、Issues を提出するか、WeChat`krahets-jyd` でお問い合わせください
この本のすべての貢献者に感謝を捧げたいと思います。彼らの無私の献身により、この本がより良いものになりました。貢献者の皆様:
本書をより良いものにしてくれた、すべての執筆・貢献者の皆さんに感謝します。無私の協力によって、このオープンソース書籍は支えられています
<p align="left">
<a href="https://github.com/krahets/hello-algo/graphs/contributors">
@@ -99,4 +99,4 @@
## ライセンス
このリポジトリ内のテキスト、コード、画像、写真、動画は [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) の下でライセンスされています。
このリポジトリに含まれるテキスト、コード、画像、写真、動画は[CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) の下でライセンスされています。

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

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

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

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

View File

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

View File

@@ -0,0 +1,114 @@
/**
* File: array.c
* Created Time: 2022-12-20
* Author: MolDuM (moldum@163.com)
*/
#include "../utils/common.h"
/* 要素へランダムアクセス */
int randomAccess(int *nums, int size) {
// 区間 [0, size) からランダムに 1 つの数を選ぶ
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 以降の全要素を 1 つ後ろへ移動する
for (int i = size - 1; i > index; i--) {
nums[i] = nums[i - 1];
}
// index の要素に num を代入する
nums[index] = num;
}
/* index の要素を削除する */
// 注意: stdio.h が remove 識別子を使用している
void removeItem(int *nums, int size, int index) {
// インデックス index より後ろの全要素を 1 つ前へ移動する
for (int i = index; i < size - 1; i++) {
nums[i] = nums[i + 1];
}
}
/* 配列を走査 */
void traverse(int *nums, int size) {
int count = 0;
// インデックスで配列を走査
for (int i = 0; i < size; i++) {
count += nums[i];
}
}
/* 配列内で指定要素を探す */
int find(int *nums, int size, int target) {
for (int i = 0; i < size; i++) {
if (nums[i] == target)
return i;
}
return -1;
}
/* Driver Code */
int main() {
/* 配列を初期化 */
int size = 5;
int arr[5];
printf("配列 arr = ");
printArray(arr, size);
int nums[] = {1, 3, 2, 5, 4};
printf("配列 nums = ");
printArray(nums, size);
/* ランダムアクセス */
int randomNum = randomAccess(nums, size);
printf("nums からランダムな要素 %d を取得", randomNum);
/* 長さを拡張 */
int enlarge = 3;
int *res = extend(nums, size, enlarge);
size += enlarge;
printf("配列の長さを 8 に拡張し、nums = ");
printArray(res, size);
/* 要素を挿入する */
insert(res, size, 6, 3);
printf("インデックス 3 に数字 6 を挿入し、nums = ");
printArray(res, size);
/* 要素を削除 */
removeItem(res, size, 2);
printf("インデックス 2 の要素を削除し、nums = ");
printArray(res, size);
/* 配列を走査 */
traverse(res, size);
/* 要素を探索する */
int index = find(res, size, 3);
printf("res 内で要素 3 を検索し、インデックス = %d\n", index);
/* メモリを解放する */
free(res);
return 0;
}

View File

@@ -0,0 +1,89 @@
/**
* File: linked_list.c
* Created Time: 2023-01-12
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 連結リストでノード n0 の後ろにノード P を挿入する */
void insert(ListNode *n0, ListNode *P) {
ListNode *n1 = n0->next;
P->next = n1;
n0->next = P;
}
/* 連結リストでノード n0 の直後のノードを削除する */
// 注意: stdio.h が remove 識別子を使用している
void removeItem(ListNode *n0) {
if (!n0->next)
return;
// n0 -> P -> n1
ListNode *P = n0->next;
ListNode *n1 = P->next;
n0->next = n1;
// メモリを解放する
free(P);
}
/* 連結リスト内で index 番目のノードにアクセス */
ListNode *access(ListNode *head, int index) {
for (int i = 0; i < index; i++) {
if (head == NULL)
return NULL;
head = head->next;
}
return head;
}
/* 連結リストで値が target の最初のノードを探す */
int find(ListNode *head, int target) {
int index = 0;
while (head) {
if (head->val == target)
return index;
head = head->next;
index++;
}
return -1;
}
/* Driver Code */
int main() {
/* 連結リストを初期化 */
// 各ノードを初期化
ListNode *n0 = newListNode(1);
ListNode *n1 = newListNode(3);
ListNode *n2 = newListNode(2);
ListNode *n3 = newListNode(5);
ListNode *n4 = newListNode(4);
// ノード間の参照を構築する
n0->next = n1;
n1->next = n2;
n2->next = n3;
n3->next = n4;
printf("初期化後の連結リストは\r\n");
printLinkedList(n0);
/* ノードを挿入 */
insert(n0, newListNode(0));
printf("ノード挿入後の連結リストは\r\n");
printLinkedList(n0);
/* ノードを削除 */
removeItem(n0);
printf("ノード削除後の連結リストは\r\n");
printLinkedList(n0);
/* ノードにアクセス */
ListNode *node = access(n0, 3);
printf("連結リストのインデックス 3 にあるノードの値 = %d\r\n", node->val);
/* ノードを探索 */
int index = find(n0, 2);
printf("連結リスト内で値が 2 のノードのインデックス = %d\r\n", index);
// メモリを解放する
freeMemoryLinkedList(n0);
return 0;
}

View File

@@ -0,0 +1,163 @@
/**
* File: my_list.c
* Created Time: 2023-01-12
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
/* リストクラス */
typedef struct {
int *arr; // 配列(リスト要素を格納)
int capacity; // リスト容量
int size; // リストのサイズ
int extendRatio; // リストが拡張されるたびの倍率
} MyList;
void extendCapacity(MyList *nums);
/* コンストラクタ */
MyList *newMyList() {
MyList *nums = malloc(sizeof(MyList));
nums->capacity = 10;
nums->arr = malloc(sizeof(int) * nums->capacity);
nums->size = 0;
nums->extendRatio = 2;
return nums;
}
/* デストラクタ */
void delMyList(MyList *nums) {
free(nums->arr);
free(nums);
}
/* リストの長さを取得 */
int size(MyList *nums) {
return nums->size;
}
/* リスト容量を取得する */
int capacity(MyList *nums) {
return nums->capacity;
}
/* 要素にアクセス */
int get(MyList *nums, int index) {
assert(index >= 0 && index < nums->size);
return nums->arr[index];
}
/* 要素を更新 */
void set(MyList *nums, int index, int num) {
assert(index >= 0 && index < nums->size);
nums->arr[index] = num;
}
/* 末尾に要素を追加 */
void add(MyList *nums, int num) {
if (size(nums) == capacity(nums)) {
extendCapacity(nums); // 容量を拡張
}
nums->arr[size(nums)] = num;
nums->size++;
}
/* 中間に要素を挿入 */
void insert(MyList *nums, int index, int num) {
assert(index >= 0 && index < size(nums));
// 要素数が容量を超えると、拡張機構が発動する
if (size(nums) == capacity(nums)) {
extendCapacity(nums); // 容量を拡張
}
for (int i = size(nums); i > index; --i) {
nums->arr[i] = nums->arr[i - 1];
}
nums->arr[index] = num;
nums->size++;
}
/* 要素を削除 */
// 注意: stdio.h が remove 識別子を使用している
int removeItem(MyList *nums, int index) {
assert(index >= 0 && index < size(nums));
int num = nums->arr[index];
for (int i = index; i < size(nums) - 1; i++) {
nums->arr[i] = nums->arr[i + 1];
}
nums->size--;
return num;
}
/* リストの拡張 */
void extendCapacity(MyList *nums) {
// 先に領域を確保する
int newCapacity = capacity(nums) * nums->extendRatio;
int *extend = (int *)malloc(sizeof(int) * newCapacity);
int *temp = nums->arr;
// 古いデータを新しいデータにコピー
for (int i = 0; i < size(nums); i++)
extend[i] = nums->arr[i];
// 古いデータを解放する
free(temp);
// 新しいデータに更新
nums->arr = extend;
nums->capacity = newCapacity;
}
/* 出力用にリストを Array に変換 */
int *toArray(MyList *nums) {
return nums->arr;
}
/* Driver Code */
int main() {
/* リストを初期化 */
MyList *nums = newMyList();
/* 末尾に要素を追加 */
add(nums, 1);
add(nums, 3);
add(nums, 2);
add(nums, 5);
add(nums, 4);
printf("リスト nums = ");
printArray(toArray(nums), size(nums));
printf("容量 = %d ,長さ = %d\n", capacity(nums), size(nums));
/* 中間に要素を挿入 */
insert(nums, 3, 6);
printf("インデックス 3 に数字 6 を挿入し、nums = ");
printArray(toArray(nums), size(nums));
/* 要素を削除 */
removeItem(nums, 3);
printf("インデックス 3 の要素を削除し、nums = ");
printArray(toArray(nums), size(nums));
/* 要素にアクセス */
int num = get(nums, 1);
printf("インデックス 1 の要素にアクセスし、num = %d\n", num);
/* 要素を更新 */
set(nums, 1, 0);
printf("インデックス 1 の要素を 0 に更新し、nums = ");
printArray(toArray(nums), size(nums));
/* 拡張機構をテストする */
for (int i = 0; i < 10; i++) {
// i = 5 のとき、リスト長が容量を超えるため、この時点で拡張機構が発動する
add(nums, i);
}
printf("拡張後のリスト nums = ");
printArray(toArray(nums), size(nums));
printf("容量 = %d ,長さ = %d\n", capacity(nums), size(nums));
/* 確保したメモリを解放する */
delMyList(nums);
return 0;
}

View File

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

View File

@@ -0,0 +1,95 @@
/**
* File : n_queens.c
* Created Time: 2023-09-25
* Author : lucas (superrat6@gmail.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
/* バックトラッキングN クイーン */
void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE],
bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) {
// すべての行への配置が完了したら、解を記録する
if (row == n) {
res[*resSize] = (char **)malloc(sizeof(char *) * n);
for (int i = 0; i < n; ++i) {
res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1));
strcpy(res[*resSize][i], state[i]);
}
(*resSize)++;
return;
}
// すべての列を走査
for (int col = 0; col < n; col++) {
// このマスに対応する主対角線と副対角線を計算
int diag1 = row - col + n - 1;
int diag2 = row + col;
// 枝刈り:そのマスの列、主対角線、副対角線にクイーンがあってはならない
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
// 試行:そのマスにクイーンを置く
state[row][col] = 'Q';
cols[col] = diags1[diag1] = diags2[diag2] = true;
// 次の行に配置する
backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2);
// 戻す:そのマスを空きマスに戻す
state[row][col] = '#';
cols[col] = diags1[diag1] = diags2[diag2] = false;
}
}
}
/* N クイーンを解く */
char ***nQueens(int n, int *returnSize) {
char state[MAX_SIZE][MAX_SIZE];
// n*n の盤面を初期化する。'Q' はクイーン、'#' は空きマスを表す
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
state[i][j] = '#';
}
state[i][n] = '\0';
}
bool cols[MAX_SIZE] = {false}; // 列にクイーンがあるか記録
bool diags1[2 * MAX_SIZE - 1] = {false}; // 主対角線にクイーンがあるかを記録
bool diags2[2 * MAX_SIZE - 1] = {false}; // 副対角線にクイーンがあるかを記録
char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE);
*returnSize = 0;
backtrack(0, n, state, res, returnSize, cols, diags1, diags2);
return res;
}
/* Driver Code */
int main() {
int n = 4;
int returnSize;
char ***res = nQueens(n, &returnSize);
printf("盤面の縦横は%d\n", n);
printf("クイーンの配置方法は全部で %d 通り\n", returnSize);
for (int i = 0; i < returnSize; ++i) {
for (int j = 0; j < n; ++j) {
printf("[");
for (int k = 0; res[i][j][k] != '\0'; ++k) {
printf("%c", res[i][j][k]);
if (res[i][j][k + 1] != '\0') {
printf(", ");
}
}
printf("]\n");
}
printf("---------------------\n");
}
// メモリを解放する
for (int i = 0; i < returnSize; ++i) {
for (int j = 0; j < n; ++j) {
free(res[i][j]);
}
free(res[i]);
}
free(res);
return 0;
}

View File

@@ -0,0 +1,79 @@
/**
* File: permutations_i.c
* Created Time: 2023-06-04
* Author: Gonglja (glj0@outlook.com), krahets (krahets@163.com)
*/
#include "../utils/common.h"
// 順列は最大 1000 個と仮定
#define MAX_SIZE 1000
/* バックトラッキング:順列 I */
void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
// 状態の長さが要素数に等しければ、解を記録
if (stateSize == choicesSize) {
res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
for (int i = 0; i < choicesSize; i++) {
res[*resSize][i] = state[i];
}
(*resSize)++;
return;
}
// すべての選択肢を走査
for (int i = 0; i < choicesSize; i++) {
int choice = choices[i];
// 枝刈り:要素の重複選択を許可しない
if (!selected[i]) {
// 試行: 選択を行い、状態を更新
selected[i] = true;
state[stateSize] = choice;
// 次の選択へ進む
backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
// バックトラック:選択を取り消し、前の状態に戻す
selected[i] = false;
}
}
}
/* 全順列 I */
int **permutationsI(int *nums, int numsSize, int *returnSize) {
int *state = (int *)malloc(numsSize * sizeof(int));
bool *selected = (bool *)malloc(numsSize * sizeof(bool));
for (int i = 0; i < numsSize; i++) {
selected[i] = false;
}
int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
*returnSize = 0;
backtrack(state, 0, nums, numsSize, selected, res, returnSize);
free(state);
free(selected);
return res;
}
/* Driver Code */
int main() {
int nums[] = {1, 2, 3};
int numsSize = sizeof(nums) / sizeof(nums[0]);
int returnSize;
int **res = permutationsI(nums, numsSize, &returnSize);
printf("入力配列 nums = ");
printArray(nums, numsSize);
printf("\nすべての順列 res = \n");
for (int i = 0; i < returnSize; i++) {
printArray(res[i], numsSize);
}
// メモリを解放する
for (int i = 0; i < returnSize; i++) {
free(res[i]);
}
free(res);
return 0;
}

View File

@@ -0,0 +1,81 @@
/**
* File: permutations_ii.c
* Created Time: 2023-10-17
* Author: krahets (krahets@163.com)
*/
#include "../utils/common.h"
// 順列は最大 1000 個、要素の最大値は 1000 と仮定する
#define MAX_SIZE 1000
/* バックトラッキング:順列 II */
void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
// 状態の長さが要素数に等しければ、解を記録
if (stateSize == choicesSize) {
res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
for (int i = 0; i < choicesSize; i++) {
res[*resSize][i] = state[i];
}
(*resSize)++;
return;
}
// すべての選択肢を走査
bool duplicated[MAX_SIZE] = {false};
for (int i = 0; i < choicesSize; i++) {
int choice = choices[i];
// 枝刈り:要素の重複選択を許可せず、同値要素の重複選択も許可しない
if (!selected[i] && !duplicated[choice]) {
// 試行: 選択を行い、状態を更新
duplicated[choice] = true; // 選択済みの要素値を記録
selected[i] = true;
state[stateSize] = choice;
// 次の選択へ進む
backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
// バックトラック:選択を取り消し、前の状態に戻す
selected[i] = false;
}
}
}
/* 全順列 II */
int **permutationsII(int *nums, int numsSize, int *returnSize) {
int *state = (int *)malloc(numsSize * sizeof(int));
bool *selected = (bool *)malloc(numsSize * sizeof(bool));
for (int i = 0; i < numsSize; i++) {
selected[i] = false;
}
int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
*returnSize = 0;
backtrack(state, 0, nums, numsSize, selected, res, returnSize);
free(state);
free(selected);
return res;
}
/* Driver Code */
int main() {
int nums[] = {1, 1, 2};
int numsSize = sizeof(nums) / sizeof(nums[0]);
int returnSize;
int **res = permutationsII(nums, numsSize, &returnSize);
printf("入力配列 nums = ");
printArray(nums, numsSize);
printf("\nすべての順列 res = \n");
for (int i = 0; i < returnSize; i++) {
printArray(res[i], numsSize);
}
// メモリを解放する
for (int i = 0; i < returnSize; i++) {
free(res[i]);
}
free(res);
return 0;
}

View File

@@ -0,0 +1,49 @@
/**
* File: preorder_traversal_i_compact.c
* Created Time: 2023-05-10
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
// 結果の長さは 100 を超えないと仮定する
#define MAX_SIZE 100
TreeNode *res[MAX_SIZE];
int resSize = 0;
/* 前順走査:例題 1 */
void preOrder(TreeNode *root) {
if (root == NULL) {
return;
}
if (root->val == 7) {
// 解を記録
res[resSize++] = root;
}
preOrder(root->left);
preOrder(root->right);
}
/* Driver Code */
int main() {
int arr[] = {1, 7, 3, 4, 5, 6, 7};
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
printf("\n二分木を初期化\n");
printTree(root);
// 先行順走査
preOrder(root);
printf("\n値が 7 のすべてのノードを出力\n");
int *vals = malloc(resSize * sizeof(int));
for (int i = 0; i < resSize; i++) {
vals[i] = res[i]->val;
}
printArray(vals, resSize);
// メモリを解放する
freeMemoryTree(root);
free(vals);
return 0;
}

View File

@@ -0,0 +1,61 @@
/**
* File: preorder_traversal_ii_compact.c
* Created Time: 2023-05-28
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
// パスと結果の長さは 100 以下と仮定
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
TreeNode *path[MAX_SIZE];
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
int pathSize = 0, resSize = 0;
/* 前順走査:例題 2 */
void preOrder(TreeNode *root) {
if (root == NULL) {
return;
}
// 試す
path[pathSize++] = root;
if (root->val == 7) {
// 解を記録
for (int i = 0; i < pathSize; ++i) {
res[resSize][i] = path[i];
}
resSize++;
}
preOrder(root->left);
preOrder(root->right);
// バックトラック
pathSize--;
}
/* Driver Code */
int main() {
int arr[] = {1, 7, 3, 4, 5, 6, 7};
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
printf("\n二分木を初期化\n");
printTree(root);
// 先行順走査
preOrder(root);
printf("\nルートノードからノード 7 までのすべての経路を出力\n");
for (int i = 0; i < resSize; ++i) {
int *vals = malloc(MAX_SIZE * sizeof(int));
int size = 0;
for (int j = 0; res[i][j] != NULL; ++j) {
vals[size++] = res[i][j]->val;
}
printArray(vals, size);
free(vals);
}
// メモリを解放する
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,62 @@
/**
* File: preorder_traversal_iii_compact.c
* Created Time: 2023-06-04
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
// パスと結果の長さは 100 以下と仮定
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
TreeNode *path[MAX_SIZE];
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
int pathSize = 0, resSize = 0;
/* 前順走査:例題 3 */
void preOrder(TreeNode *root) {
// 枝刈り
if (root == NULL || root->val == 3) {
return;
}
// 試す
path[pathSize++] = root;
if (root->val == 7) {
// 解を記録
for (int i = 0; i < pathSize; i++) {
res[resSize][i] = path[i];
}
resSize++;
}
preOrder(root->left);
preOrder(root->right);
// バックトラック
pathSize--;
}
/* Driver Code */
int main() {
int arr[] = {1, 7, 3, 4, 5, 6, 7};
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
printf("\n二分木を初期化\n");
printTree(root);
// 先行順走査
preOrder(root);
printf("\nルートノードからノード 7 までのすべての経路を出力し、経路に値が 3 のノードを含めない\n");
for (int i = 0; i < resSize; ++i) {
int *vals = malloc(MAX_SIZE * sizeof(int));
int size = 0;
for (int j = 0; res[i][j] != NULL; ++j) {
vals[size++] = res[i][j]->val;
}
printArray(vals, size);
free(vals);
}
// メモリを解放する
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,93 @@
/**
* File: preorder_traversal_iii_template.c
* Created Time: 2023-06-04
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
// パスと結果の長さは 100 以下と仮定
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
TreeNode *path[MAX_SIZE];
TreeNode *res[MAX_RES_SIZE][MAX_SIZE];
int pathSize = 0, resSize = 0;
/* 現在の状態が解かどうかを判定 */
bool isSolution(void) {
return pathSize > 0 && path[pathSize - 1]->val == 7;
}
/* 解を記録 */
void recordSolution(void) {
for (int i = 0; i < pathSize; i++) {
res[resSize][i] = path[i];
}
resSize++;
}
/* 現在の状態で、この選択が有効かどうかを判定 */
bool isValid(TreeNode *choice) {
return choice != NULL && choice->val != 3;
}
/* 状態を更新 */
void makeChoice(TreeNode *choice) {
path[pathSize++] = choice;
}
/* 状態を元に戻す */
void undoChoice(void) {
pathSize--;
}
/* バックトラッキング:例題 3 */
void backtrack(TreeNode *choices[2]) {
// 解かどうかを確認
if (isSolution()) {
// 解を記録
recordSolution();
}
// すべての選択肢を走査
for (int i = 0; i < 2; i++) {
TreeNode *choice = choices[i];
// 枝刈り:選択が妥当かを確認する
if (isValid(choice)) {
// 試行: 選択を行い、状態を更新
makeChoice(choice);
// 次の選択へ進む
TreeNode *nextChoices[2] = {choice->left, choice->right};
backtrack(nextChoices);
// バックトラック:選択を取り消し、前の状態に戻す
undoChoice();
}
}
}
/* Driver Code */
int main() {
int arr[] = {1, 7, 3, 4, 5, 6, 7};
TreeNode *root = arrayToTree(arr, sizeof(arr) / sizeof(arr[0]));
printf("\n二分木を初期化\n");
printTree(root);
// バックトラッキング法
TreeNode *choices[2] = {root, NULL};
backtrack(choices);
printf("\nルートノードからノード 7 までのすべての経路を出力し、経路に値が 3 のノードを含めない\n");
for (int i = 0; i < resSize; ++i) {
int *vals = malloc(MAX_SIZE * sizeof(int));
int size = 0;
for (int j = 0; res[i][j] != NULL; ++j) {
vals[size++] = res[i][j]->val;
}
printArray(vals, size);
free(vals);
}
// メモリを解放する
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,78 @@
/**
* File: subset_sum_i.c
* Created Time: 2023-07-29
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
// 状態(部分集合)
int state[MAX_SIZE];
int stateSize = 0;
// 結果リスト(部分集合のリスト)
int res[MAX_RES_SIZE][MAX_SIZE];
int resColSizes[MAX_RES_SIZE];
int resSize = 0;
/* バックトラッキング:部分和 I */
void backtrack(int target, int *choices, int choicesSize, int start) {
// 部分集合の和が target に等しければ、解を記録
if (target == 0) {
for (int i = 0; i < stateSize; ++i) {
res[resSize][i] = state[i];
}
resColSizes[resSize++] = stateSize;
return;
}
// すべての選択肢を走査
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
for (int i = start; i < choicesSize; i++) {
// 枝刈り1部分集合の和が target を超えたら、直ちにループを終了する
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
if (target - choices[i] < 0) {
break;
}
// 試す選択を行い、target と start を更新
state[stateSize] = choices[i];
stateSize++;
// 次の選択へ進む
backtrack(target - choices[i], choices, choicesSize, i);
// バックトラック:選択を取り消し、前の状態に戻す
stateSize--;
}
}
/* 比較関数 */
int cmp(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}
/* 部分和 I を解く */
void subsetSumI(int *nums, int numsSize, int target) {
qsort(nums, numsSize, sizeof(int), cmp); // nums をソート
int start = 0; // 開始点を走査
backtrack(target, nums, numsSize, start);
}
/* Driver Code */
int main() {
int nums[] = {3, 4, 5};
int numsSize = sizeof(nums) / sizeof(nums[0]);
int target = 9;
subsetSumI(nums, numsSize, target);
printf("入力配列 nums = ");
printArray(nums, numsSize);
printf("target = %d\n", target);
printf("合計が %d に等しいすべての部分集合 res = \n", target);
for (int i = 0; i < resSize; ++i) {
printArray(res[i], resColSizes[i]);
}
return 0;
}

View File

@@ -0,0 +1,69 @@
/**
* File: subset_sum_i_naive.c
* Created Time: 2023-07-28
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
// 状態(部分集合)
int state[MAX_SIZE];
int stateSize = 0;
// 結果リスト(部分集合のリスト)
int res[MAX_RES_SIZE][MAX_SIZE];
int resColSizes[MAX_RES_SIZE];
int resSize = 0;
/* バックトラッキング:部分和 I */
void backtrack(int target, int total, int *choices, int choicesSize) {
// 部分集合の和が target に等しければ、解を記録
if (total == target) {
for (int i = 0; i < stateSize; i++) {
res[resSize][i] = state[i];
}
resColSizes[resSize++] = stateSize;
return;
}
// すべての選択肢を走査
for (int i = 0; i < choicesSize; i++) {
// 枝刈り:部分和が target を超える場合はその選択をスキップする
if (total + choices[i] > target) {
continue;
}
// 試行:選択を行い、要素と total を更新する
state[stateSize++] = choices[i];
// 次の選択へ進む
backtrack(target, total + choices[i], choices, choicesSize);
// バックトラック:選択を取り消し、前の状態に戻す
stateSize--;
}
}
/* 部分和 I を解く(重複部分集合を含む) */
void subsetSumINaive(int *nums, int numsSize, int target) {
resSize = 0; // 解の個数を 0 に初期化する
backtrack(target, 0, nums, numsSize);
}
/* Driver Code */
int main() {
int nums[] = {3, 4, 5};
int numsSize = sizeof(nums) / sizeof(nums[0]);
int target = 9;
subsetSumINaive(nums, numsSize, target);
printf("入力配列 nums = ");
printArray(nums, numsSize);
printf("target = %d\n", target);
printf("合計が %d に等しいすべての部分集合 res = \n", target);
for (int i = 0; i < resSize; i++) {
printArray(res[i], resColSizes[i]);
}
return 0;
}

View File

@@ -0,0 +1,83 @@
/**
* File: subset_sum_ii.c
* Created Time: 2023-07-29
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
#define MAX_RES_SIZE 100
// 状態(部分集合)
int state[MAX_SIZE];
int stateSize = 0;
// 結果リスト(部分集合のリスト)
int res[MAX_RES_SIZE][MAX_SIZE];
int resColSizes[MAX_RES_SIZE];
int resSize = 0;
/* バックトラッキング:部分和 II */
void backtrack(int target, int *choices, int choicesSize, int start) {
// 部分集合の和が target に等しければ、解を記録
if (target == 0) {
for (int i = 0; i < stateSize; i++) {
res[resSize][i] = state[i];
}
resColSizes[resSize++] = stateSize;
return;
}
// すべての選択肢を走査
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
// 枝刈り 3: start から走査し、同じ要素の重複選択を避ける
for (int i = start; i < choicesSize; i++) {
// 枝刈り 1: 部分集合の和が target を超えたら、そのままスキップする
if (target - choices[i] < 0) {
continue;
}
// 枝刈り4この要素が左隣の要素と等しければ、その探索分岐は重複しているためスキップする
if (i > start && choices[i] == choices[i - 1]) {
continue;
}
// 試す選択を行い、target と start を更新
state[stateSize] = choices[i];
stateSize++;
// 次の選択へ進む
backtrack(target - choices[i], choices, choicesSize, i + 1);
// バックトラック:選択を取り消し、前の状態に戻す
stateSize--;
}
}
/* 比較関数 */
int cmp(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}
/* 部分和 II を解く */
void subsetSumII(int *nums, int numsSize, int target) {
// nums をソート
qsort(nums, numsSize, sizeof(int), cmp);
// バックトラッキングを開始
backtrack(target, nums, numsSize, 0);
}
/* Driver Code */
int main() {
int nums[] = {4, 4, 5};
int numsSize = sizeof(nums) / sizeof(nums[0]);
int target = 9;
subsetSumII(nums, numsSize, target);
printf("入力配列 nums = ");
printArray(nums, numsSize);
printf("target = %d\n", target);
printf("合計が %d に等しいすべての部分集合 res = \n", target);
for (int i = 0; i < resSize; ++i) {
printArray(res[i], resColSizes[i]);
}
return 0;
}

View File

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

View File

@@ -0,0 +1,81 @@
/**
* File: iteration.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com), MwumLi (mwumli@hotmail.com)
*/
#include "../utils/common.h"
/* for ループ */
int forLoop(int n) {
int res = 0;
// 1, 2, ..., n-1, n を順に加算する
for (int i = 1; i <= n; i++) {
res += i;
}
return res;
}
/* while ループ */
int whileLoop(int n) {
int res = 0;
int i = 1; // 条件変数を初期化する
// 1, 2, ..., n-1, n を順に加算する
while (i <= n) {
res += i;
i++; // 条件変数を更新する
}
return res;
}
/* while ループ2回更新 */
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 ループ2回更新の合計結果 res = %d\n", res);
char *resStr = nestedForLoop(n);
printf("\n二重 for ループの走査結果 %s\r\n", resStr);
free(resStr);
return 0;
}

View File

@@ -0,0 +1,77 @@
/**
* File: recursion.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 再帰 */
int recur(int n) {
// 終了条件
if (n == 1)
return 1;
// 再帰:再帰呼び出し
int res = recur(n - 1);
// 帰りがけ:結果を返す
return n + res;
}
/* 反復で再帰を模擬する */
int forLoopRecur(int n) {
int stack[1000]; // 大きな配列を使ってスタックを実装する
int top = -1; // スタックトップのインデックス
int res = 0;
// 再帰:再帰呼び出し
for (int i = n; i > 0; i--) {
// 「スタックへのプッシュ」で「再帰」を模擬する
stack[1 + top++] = i;
}
// 帰りがけ:結果を返す
while (top >= 0) {
// 「スタックから取り出す操作」で「帰り」をシミュレート
res += stack[top--];
}
// res = 1+2+3+...+n
return res;
}
/* 末尾再帰 */
int tailRecur(int n, int res) {
// 終了条件
if (n == 0)
return res;
// 末尾再帰呼び出し
return tailRecur(n - 1, res + n);
}
/* フィボナッチ数列:再帰 */
int fib(int n) {
// 終了条件 f(1) = 0, f(2) = 1
if (n == 1 || n == 2)
return n - 1;
// f(n) = f(n-1) + f(n-2) を再帰的に呼び出す
int res = fib(n - 1) + fib(n - 2);
// 結果 f(n) を返す
return res;
}
/* Driver Code */
int main() {
int n = 5;
int res;
res = recur(n);
printf("\n再帰関数の合計結果 res = %d\n", res);
res = forLoopRecur(n);
printf("\n反復で再帰をシミュレートした合計結果 res = %d\n", res);
res = tailRecur(n, 0);
printf("\n末尾再帰関数の合計結果 res = %d\n", res);
res = fib(n);
printf("\nフィボナッチ数列の第 %d 項は %d\n", n, res);
return 0;
}

View File

@@ -0,0 +1,141 @@
/**
* File: space_complexity.c
* Created Time: 2023-04-15
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 関数 */
int func() {
// 何らかの処理を行う
return 0;
}
/* 定数階 */
void constant(int n) {
// 定数、変数、オブジェクトは O(1) の空間を占める
const int a = 0;
int b = 0;
int nums[1000];
ListNode *node = newListNode(0);
free(node);
// ループ内の変数は O(1) の空間を占める
for (int i = 0; i < n; i++) {
int c = 0;
}
// ループ内の関数は O(1) の空間を占める
for (int i = 0; i < n; i++) {
func();
}
}
/* ハッシュテーブル */
typedef struct {
int key;
int val;
UT_hash_handle hh; // uthash.h を用いて実装
} HashTable;
/* 線形階 */
void linear(int n) {
// 長さ n の配列は O(n) の空間を使用
int *nums = malloc(sizeof(int) * n);
free(nums);
// 長さ n のリストは O(n) の空間を使用
ListNode **nodes = malloc(sizeof(ListNode *) * n);
for (int i = 0; i < n; i++) {
nodes[i] = newListNode(i);
}
// メモリを解放する
for (int i = 0; i < n; i++) {
free(nodes[i]);
}
free(nodes);
// 長さ n のハッシュテーブルは O(n) の空間を使用
HashTable *h = NULL;
for (int i = 0; i < n; i++) {
HashTable *tmp = malloc(sizeof(HashTable));
tmp->key = i;
tmp->val = i;
HASH_ADD_INT(h, key, tmp);
}
// メモリを解放する
HashTable *curr, *tmp;
HASH_ITER(hh, h, curr, tmp) {
HASH_DEL(h, curr);
free(curr);
}
}
/* 線形時間(再帰実装) */
void linearRecur(int n) {
printf("再帰 n = %d\r\n", n);
if (n == 1)
return;
linearRecur(n - 1);
}
/* 二乗階 */
void quadratic(int n) {
// 二次元リストは O(n^2) の空間を使用
int **numMatrix = malloc(sizeof(int *) * n);
for (int i = 0; i < n; i++) {
int *tmp = malloc(sizeof(int) * n);
for (int j = 0; j < n; j++) {
tmp[j] = 0;
}
numMatrix[i] = tmp;
}
// メモリを解放する
for (int i = 0; i < n; i++) {
free(numMatrix[i]);
}
free(numMatrix);
}
/* 二次時間(再帰実装) */
int quadraticRecur(int n) {
if (n <= 0)
return 0;
int *nums = malloc(sizeof(int) * n);
printf("再帰 n = %d における nums の長さ = %d\r\n", n, n);
int res = quadraticRecur(n - 1);
free(nums);
return res;
}
/* 指数時間(完全二分木の構築) */
TreeNode *buildTree(int n) {
if (n == 0)
return NULL;
TreeNode *root = newTreeNode(0);
root->left = buildTree(n - 1);
root->right = buildTree(n - 1);
return root;
}
/* Driver Code */
int main() {
int n = 5;
// 定数階
constant(n);
// 線形階
linear(n);
linearRecur(n);
// 二乗階
quadratic(n);
quadraticRecur(n);
// 指数オーダー
TreeNode *root = buildTree(n);
printTree(root);
// メモリを解放する
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,179 @@
/**
* File: time_complexity.c
* Created Time: 2023-01-03
* Author: codingonion (coderonion@gmail.com)
*/
#include "../utils/common.h"
/* 定数階 */
int constant(int n) {
int count = 0;
int size = 100000;
int i = 0;
for (int i = 0; i < size; i++) {
count++;
}
return count;
}
/* 線形階 */
int linear(int n) {
int count = 0;
for (int i = 0; i < n; i++) {
count++;
}
return count;
}
/* 線形時間(配列を走査) */
int arrayTraversal(int *nums, int n) {
int count = 0;
// ループ回数は配列長に比例する
for (int i = 0; i < n; i++) {
count++;
}
return count;
}
/* 二乗階 */
int quadratic(int n) {
int count = 0;
// ループ回数はデータサイズ n の二乗に比例する
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
count++;
}
}
return count;
}
/* 二次時間(バブルソート) */
int bubbleSort(int *nums, int n) {
int count = 0; // カウンタ
// 外側のループ:未ソート区間は [0, i]
for (int i = n - 1; i > 0; i--) {
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// nums[j] と nums[j + 1] を交換
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
count += 3; // 要素交換には 3 回の単位操作が含まれる
}
}
}
return count;
}
/* 指数時間(ループ実装) */
int exponential(int n) {
int count = 0;
int bas = 1;
// 細胞は各ラウンドで 2 つに分裂し、数列 1, 2, 4, 8, ..., 2^(n-1) を形成する
for (int i = 0; i < n; i++) {
for (int j = 0; j < bas; j++) {
count++;
}
bas *= 2;
}
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
return count;
}
/* 指数時間(再帰実装) */
int expRecur(int n) {
if (n == 1)
return 1;
return expRecur(n - 1) + expRecur(n - 1) + 1;
}
/* 対数時間(ループ実装) */
int logarithmic(int n) {
int count = 0;
while (n > 1) {
n = n / 2;
count++;
}
return count;
}
/* 対数時間(再帰実装) */
int logRecur(int n) {
if (n <= 1)
return 0;
return logRecur(n / 2) + 1;
}
/* 線形対数時間 */
int linearLogRecur(int n) {
if (n <= 1)
return 1;
int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);
for (int i = 0; i < n; i++) {
count++;
}
return count;
}
/* 階乗時間(再帰実装) */
int factorialRecur(int n) {
if (n == 0)
return 1;
int count = 0;
for (int i = 0; i < n; i++) {
count += factorialRecur(n - 1);
}
return count;
}
/* Driver Code */
int main(int argc, char *argv[]) {
// n を変えて実行し、各計算量で操作回数がどう変化するかを確認できる
int n = 8;
printf("入力データサイズ n = %d\n", n);
int count = constant(n);
printf("定数オーダーの操作回数 = %d\n", count);
count = linear(n);
printf("線形オーダーの操作回数 = %d\n", count);
// ヒープ領域にメモリを確保する(要素数 n、要素型 int の一次元可変長配列を作成)
int *nums = (int *)malloc(n * sizeof(int));
count = arrayTraversal(nums, n);
printf("線形オーダー(配列の走査)の操作回数 = %d\n", count);
count = quadratic(n);
printf("平方オーダーの操作回数 = %d\n", count);
for (int i = 0; i < n; i++) {
nums[i] = n - i; // [n,n-1,...,2,1]
}
count = bubbleSort(nums, n);
printf("平方オーダー(バブルソート)の操作回数 = %d\n", count);
count = exponential(n);
printf("指数オーダー(ループ実装)の操作回数 = %d\n", count);
count = expRecur(n);
printf("指数オーダー(再帰実装)の操作回数 = %d\n", count);
count = logarithmic(n);
printf("対数オーダー(ループ実装)の操作回数 = %d\n", count);
count = logRecur(n);
printf("対数オーダー(再帰実装)の操作回数 = %d\n", count);
count = linearLogRecur(n);
printf("線形対数オーダー(再帰実装)の操作回数 = %d\n", count);
count = factorialRecur(n);
printf("階乗オーダー(再帰実装)の操作回数 = %d\n", count);
// ヒープ領域のメモリを解放
if (nums != NULL) {
free(nums);
nums = NULL;
}
getchar();
return 0;
}

View File

@@ -0,0 +1,57 @@
/**
* File: worst_best_time_complexity.c
* Created Time: 2023-01-03
* Author: codingonion (coderonion@gmail.com)
*/
#include "../utils/common.h"
/* 要素が { 1, 2, ..., n } で、順序がシャッフルされた配列を生成 */
int *randomNumbers(int n) {
// ヒープ領域にメモリを確保する(要素数 n、要素型 int の一次元可変長配列を作成)
int *nums = (int *)malloc(n * sizeof(int));
// 配列 nums = { 1, 2, 3, ..., n } を生成
for (int i = 0; i < n; i++) {
nums[i] = i + 1;
}
// 配列要素をランダムにシャッフル
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
return nums;
}
/* 配列 nums 内で数値 1 のインデックスを探す */
int findOne(int *nums, int n) {
for (int i = 0; i < n; i++) {
// 要素 1 が配列の先頭にあるとき、最良時間計算量 O(1) となる
// 要素 1 が配列の末尾にあるとき、最悪時間計算量 O(n) となる
if (nums[i] == 1)
return i;
}
return -1;
}
/* Driver Code */
int main(int argc, char *argv[]) {
// 乱数シードを初期化する
srand((unsigned int)time(NULL));
for (int i = 0; i < 10; i++) {
int n = 100;
int *nums = randomNumbers(n);
int index = findOne(nums, n);
printf("\n配列 [ 1, 2, ..., n ] をシャッフルした後 = ");
printArray(nums, n);
printf("数値 1 のインデックスは %d\n", index);
// ヒープ領域のメモリを解放
if (nums != NULL) {
free(nums);
nums = NULL;
}
}
return 0;
}

View File

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

View File

@@ -0,0 +1,47 @@
/**
* File: binary_search_recur.c
* Created Time: 2023-10-01
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 二分探索:問題 f(i, j) */
int dfs(int nums[], int target, int i, int j) {
// 区間が空なら対象要素は存在しないので -1 を返す
if (i > j) {
return -1;
}
// 中点インデックス m を計算
int m = (i + j) / 2;
if (nums[m] < target) {
// 部分問題 f(m+1, j) を再帰的に解く
return dfs(nums, target, m + 1, j);
} else if (nums[m] > target) {
// 部分問題 f(i, m-1) を再帰的に解く
return dfs(nums, target, i, m - 1);
} else {
// 目標要素が見つかったらそのインデックスを返す
return m;
}
}
/* 二分探索 */
int binarySearch(int nums[], int target, int numsSize) {
int n = numsSize;
// 問題 f(0, n-1) を解く
return dfs(nums, target, 0, n - 1);
}
/* Driver Code */
int main() {
int target = 6;
int nums[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
int numsSize = sizeof(nums) / sizeof(nums[0]);
// 二分探索(両閉区間)
int index = binarySearch(nums, target, numsSize);
printf("対象要素 6 のインデックス = %d\n", index);
return 0;
}

View File

@@ -0,0 +1,61 @@
/**
* File : build_tree.c
* Created Time: 2023-10-16
* Author : lucas (superrat6@gmail.com)
*/
#include "../utils/common.h"
// すべての要素が 1000 未満であると仮定する
#define MAX_SIZE 1000
/* 二分木を構築:分割統治 */
TreeNode *dfs(int *preorder, int *inorderMap, int i, int l, int r, int size) {
// 部分木区間が空なら終了する
if (r - l < 0)
return NULL;
// ルートノードを初期化する
TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode));
root->val = preorder[i];
root->left = NULL;
root->right = NULL;
// m を求めて左右部分木を分割する
int m = inorderMap[preorder[i]];
// 部分問題:左部分木を構築する
root->left = dfs(preorder, inorderMap, i + 1, l, m - 1, size);
// 部分問題:右部分木を構築する
root->right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r, size);
// 根ノードを返す
return root;
}
/* 二分木を構築 */
TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) {
// inorder の要素からインデックスへの対応を格納するハッシュテーブルを初期化する
int *inorderMap = (int *)malloc(sizeof(int) * MAX_SIZE);
for (int i = 0; i < inorderSize; i++) {
inorderMap[inorder[i]] = i;
}
TreeNode *root = dfs(preorder, inorderMap, 0, 0, inorderSize - 1, inorderSize);
free(inorderMap);
return root;
}
/* Driver Code */
int main() {
int preorder[] = {3, 9, 2, 1, 7};
int inorder[] = {9, 3, 1, 2, 7};
int preorderSize = sizeof(preorder) / sizeof(preorder[0]);
int inorderSize = sizeof(inorder) / sizeof(inorder[0]);
printf("前順走査 = ");
printArray(preorder, preorderSize);
printf("中順走査 = ");
printArray(inorder, inorderSize);
TreeNode *root = buildTree(preorder, preorderSize, inorder, inorderSize);
printf("構築した二分木は:\n");
printTree(root);
freeMemoryTree(root);
return 0;
}

View File

@@ -0,0 +1,74 @@
/**
* File: hanota.c
* Created Time: 2023-10-01
* Author: Zuoxun (845242523@qq.com), lucas(superrat6@gmail.com)
*/
#include "../utils/common.h"
// 順列は最大 1000 個と仮定
#define MAX_SIZE 1000
/* 円盤を 1 枚移動 */
void move(int *src, int *srcSize, int *tar, int *tarSize) {
// src の上から円盤を1枚取り出す
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 に円盤が 1 枚だけ残っている場合は、そのまま 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 に残る 1 枚の円盤を tar に移す
move(src, srcSize, tar, tarSize);
// 部分問題 f(i-1)buf の上部 i-1 枚の円盤を src を補助にして tar へ移す
dfs(i - 1, buf, bufSize, src, srcSize, tar, tarSize);
}
/* ハノイの塔を解く */
void solveHanota(int *A, int *ASize, int *B, int *BSize, int *C, int *CSize) {
// A の上から n 枚の円盤を B を介して C へ移す
dfs(*ASize, A, ASize, B, BSize, C, CSize);
}
/* Driver Code */
int main() {
// リスト末尾が柱の頂上
int a[] = {5, 4, 3, 2, 1};
int b[MAX_SIZE] = {0};
int c[MAX_SIZE] = {0};
int ASize = sizeof(a) / sizeof(a[0]);
int BSize = 0;
int CSize = 0;
printf("\n初期状態:");
printf("\nA = ");
printArray(a, ASize);
printf("B = ");
printArray(b, BSize);
printf("C = ");
printArray(c, CSize);
solveHanota(a, &ASize, b, &BSize, c, &CSize);
printf("\n円盤の移動完了後:");
printf("A = ");
printArray(a, ASize);
printf("B = ");
printArray(b, BSize);
printf("C = ");
printArray(c, CSize);
return 0;
}

View File

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

View File

@@ -0,0 +1,47 @@
/**
* File: climbing_stairs_backtrack.c
* Created Time: 2023-09-22
* Author: huawuque404 (huawuque404@163.com)
*/
#include "../utils/common.h"
/* バックトラッキング */
void backtrack(int *choices, int state, int n, int *res, int len) {
// 第 n 段に到達したら、方法数を 1 増やす
if (state == n)
res[0]++;
// すべての選択肢を走査
for (int i = 0; i < len; i++) {
int choice = choices[i];
// 枝刈り: 第 n 段を超えないようにする
if (state + choice > n)
continue;
// 試行: 選択を行い、状態を更新
backtrack(choices, state + choice, n, res, len);
// バックトラック
}
}
/* 階段登り:バックトラッキング */
int climbingStairsBacktrack(int n) {
int choices[2] = {1, 2}; // 1 段または 2 段上ることを選べる
int state = 0; // 第 0 段から上り始める
int *res = (int *)malloc(sizeof(int));
*res = 0; // res[0] を使って方法数を記録する
int len = sizeof(choices) / sizeof(int);
backtrack(choices, state, n, res, len);
int result = *res;
free(res);
return result;
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsBacktrack(n);
printf("%d 段の階段を登る方法は全部で %d 通りです\n", n, res);
return 0;
}

View File

@@ -0,0 +1,46 @@
/**
* File: climbing_stairs_constraint_dp.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 制約付き階段登り:動的計画法 */
int climbingStairsConstraintDP(int n) {
if (n == 1 || n == 2) {
return 1;
}
// 部分問題の解を保存するために dp テーブルを初期化
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(3, sizeof(int));
}
// 初期状態:最小部分問題の解をあらかじめ設定
dp[1][1] = 1;
dp[1][2] = 0;
dp[2][1] = 0;
dp[2][2] = 1;
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
for (int i = 3; i <= n; i++) {
dp[i][1] = dp[i - 1][2];
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
}
int res = dp[n][1] + dp[n][2];
// メモリを解放する
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
free(dp);
return res;
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsConstraintDP(n);
printf("%d 段の階段を登る方法は全部で %d 通りです\n", n, res);
return 0;
}

View File

@@ -0,0 +1,32 @@
/**
* File: climbing_stairs_dfs.c
* Created Time: 2023-09-19
* Author: huawuque404 (huawuque404@163.com)
*/
#include "../utils/common.h"
/* 検索 */
int dfs(int i) {
// dp[1] と dp[2] は既知なので返す
if (i == 1 || i == 2)
return i;
// dp[i] = dp[i-1] + dp[i-2]
int count = dfs(i - 1) + dfs(i - 2);
return count;
}
/* 階段登り:探索 */
int climbingStairsDFS(int n) {
return dfs(n);
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsDFS(n);
printf("%d 段の階段を登る方法は全部で %d 通りです\n", n, res);
return 0;
}

View File

@@ -0,0 +1,44 @@
/**
* File: climbing_stairs_dfs_mem.c
* Created Time: 2023-09-19
* Author: huawuque404 (huawuque404@163.com)
*/
#include "../utils/common.h"
/* メモ化探索 */
int dfs(int i, int *mem) {
// dp[1] と dp[2] は既知なので返す
if (i == 1 || i == 2)
return i;
// dp[i] の記録があれば、それをそのまま返す
if (mem[i] != -1)
return mem[i];
// dp[i] = dp[i-1] + dp[i-2]
int count = dfs(i - 1, mem) + dfs(i - 2, mem);
// dp[i] を記録する
mem[i] = count;
return count;
}
/* 階段登り:メモ化探索 */
int climbingStairsDFSMem(int n) {
// mem[i] は第 i 段まで上る方法の総数を記録し、-1 は未記録を表す
int *mem = (int *)malloc((n + 1) * sizeof(int));
for (int i = 0; i <= n; i++) {
mem[i] = -1;
}
int result = dfs(n, mem);
free(mem);
return result;
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsDFSMem(n);
printf("%d 段の階段を登る方法は全部で %d 通りです\n", n, res);
return 0;
}

View File

@@ -0,0 +1,51 @@
/**
* File: climbing_stairs_dp.c
* Created Time: 2023-09-19
* Author: huawuque404 (huawuque404@163.com)
*/
#include "../utils/common.h"
/* 階段登り:動的計画法 */
int climbingStairsDP(int n) {
if (n == 1 || n == 2)
return n;
// 部分問題の解を保存するために dp テーブルを初期化
int *dp = (int *)malloc((n + 1) * sizeof(int));
// 初期状態:最小部分問題の解をあらかじめ設定
dp[1] = 1;
dp[2] = 2;
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
int result = dp[n];
free(dp);
return result;
}
/* 階段登り:空間最適化した動的計画法 */
int climbingStairsDPComp(int n) {
if (n == 1 || n == 2)
return n;
int a = 1, b = 2;
for (int i = 3; i <= n; i++) {
int tmp = b;
b = a + b;
a = tmp;
}
return b;
}
/* Driver Code */
int main() {
int n = 9;
int res = climbingStairsDP(n);
printf("%d 段の階段を登る方法は全部で %d 通りです\n", n, res);
res = climbingStairsDPComp(n);
printf("%d 段の階段を登る方法は全部で %d 通りです\n", n, res);
return 0;
}

View File

@@ -0,0 +1,92 @@
/**
* File: coin_change.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 最小値を求める */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* コイン両替:動的計画法 */
int coinChangeDP(int coins[], int amt, int coinsSize) {
int n = coinsSize;
int MAX = amt + 1;
// dp テーブルを初期化
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(amt + 1, sizeof(int));
}
// 状態遷移:先頭行と先頭列
for (int a = 1; a <= amt; a++) {
dp[0][a] = MAX;
}
// 状態遷移: 残りの行と列
for (int i = 1; i <= n; i++) {
for (int a = 1; a <= amt; a++) {
if (coins[i - 1] > a) {
// 目標金額を超えるなら硬貨 i は選ばない
dp[i][a] = dp[i - 1][a];
} else {
// 硬貨 i を選ばない場合と選ぶ場合の小さい方
dp[i][a] = myMin(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1);
}
}
}
int res = dp[n][amt] != MAX ? dp[n][amt] : -1;
// メモリを解放する
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
free(dp);
return res;
}
/* コイン交換:空間最適化後の動的計画法 */
int coinChangeDPComp(int coins[], int amt, int coinsSize) {
int n = coinsSize;
int MAX = amt + 1;
// dp テーブルを初期化
int *dp = malloc((amt + 1) * sizeof(int));
for (int j = 1; j <= amt; j++) {
dp[j] = MAX;
}
dp[0] = 0;
// 状態遷移
for (int i = 1; i <= n; i++) {
for (int a = 1; a <= amt; a++) {
if (coins[i - 1] > a) {
// 目標金額を超えるなら硬貨 i は選ばない
dp[a] = dp[a];
} else {
// 硬貨 i を選ばない場合と選ぶ場合の小さい方
dp[a] = myMin(dp[a], dp[a - coins[i - 1]] + 1);
}
}
}
int res = dp[amt] != MAX ? dp[amt] : -1;
// メモリを解放する
free(dp);
return res;
}
/* Driver code */
int main() {
int coins[] = {1, 2, 5};
int coinsSize = sizeof(coins) / sizeof(coins[0]);
int amt = 4;
// 動的計画法
int res = coinChangeDP(coins, amt, coinsSize);
printf("目標金額に必要な最小硬貨枚数は %d\n", res);
// 空間最適化後の動的計画法
res = coinChangeDPComp(coins, amt, coinsSize);
printf("目標金額に必要な最小硬貨枚数は %d\n", res);
return 0;
}

View File

@@ -0,0 +1,81 @@
/**
* File: coin_change_ii.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* コイン両替 II動的計画法 */
int coinChangeIIDP(int coins[], int amt, int coinsSize) {
int n = coinsSize;
// dp テーブルを初期化
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(amt + 1, sizeof(int));
}
// 先頭列を初期化する
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
// 状態遷移
for (int i = 1; i <= n; i++) {
for (int a = 1; a <= amt; a++) {
if (coins[i - 1] > a) {
// 目標金額を超えるなら硬貨 i は選ばない
dp[i][a] = dp[i - 1][a];
} else {
// コイン i を選ばない場合と選ぶ場合の和
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]];
}
}
}
int res = dp[n][amt];
// メモリを解放する
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
free(dp);
return res;
}
/* コイン両替 II空間最適化した動的計画法 */
int coinChangeIIDPComp(int coins[], int amt, int coinsSize) {
int n = coinsSize;
// dp テーブルを初期化
int *dp = calloc(amt + 1, sizeof(int));
dp[0] = 1;
// 状態遷移
for (int i = 1; i <= n; i++) {
for (int a = 1; a <= amt; a++) {
if (coins[i - 1] > a) {
// 目標金額を超えるなら硬貨 i は選ばない
dp[a] = dp[a];
} else {
// コイン i を選ばない場合と選ぶ場合の和
dp[a] = dp[a] + dp[a - coins[i - 1]];
}
}
}
int res = dp[amt];
// メモリを解放する
free(dp);
return res;
}
/* Driver code */
int main() {
int coins[] = {1, 2, 5};
int coinsSize = sizeof(coins) / sizeof(coins[0]);
int amt = 5;
// 動的計画法
int res = coinChangeIIDP(coins, amt, coinsSize);
printf("目標金額になる硬貨の組合せ数は %d\n", res);
// 空間最適化後の動的計画法
res = coinChangeIIDPComp(coins, amt, coinsSize);
printf("目標金額になる硬貨の組合せ数は %d\n", res);
return 0;
}

View File

@@ -0,0 +1,159 @@
/**
* File: edit_distance.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 最小値を求める */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* 編集距離:総当たり探索 */
int editDistanceDFS(char *s, char *t, int i, int j) {
// s と t がともに空なら 0 を返す
if (i == 0 && j == 0)
return 0;
// s が空なら t の長さを返す
if (i == 0)
return j;
// t が空なら s の長さを返す
if (j == 0)
return i;
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
if (s[i - 1] == t[j - 1])
return editDistanceDFS(s, t, i - 1, j - 1);
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 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];
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
if (s[i - 1] == t[j - 1])
return editDistanceDFSMem(s, t, memCols, mem, i - 1, j - 1);
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 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]) {
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
dp[i][j] = dp[i - 1][j - 1];
} else {
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 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]) {
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
dp[j] = leftup;
} else {
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
dp[j] = myMin(myMin(dp[j - 1], dp[j]), leftup) + 1;
}
leftup = temp; // 次の反復の dp[i-1, j-1] に更新する
}
}
int res = dp[m];
// メモリを解放する
free(dp);
return res;
}
/* Driver Code */
int main() {
char *s = "bag";
char *t = "pack";
int n = strlen(s), m = strlen(t);
// 全探索
int res = editDistanceDFS(s, t, n, m);
printf("%s を %s に変更するには最小で %d 回の編集が必要です\n", s, t, res);
// メモ化探索
int **mem = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
mem[i] = malloc((m + 1) * sizeof(int));
memset(mem[i], -1, (m + 1) * sizeof(int));
}
res = editDistanceDFSMem(s, t, m + 1, mem, n, m);
printf("%s を %s に変更するには最小で %d 回の編集が必要です\n", s, t, res);
// メモリを解放する
for (int i = 0; i <= n; i++) {
free(mem[i]);
}
free(mem);
// 動的計画法
res = editDistanceDP(s, t, n, m);
printf("%s を %s に変更するには最小で %d 回の編集が必要です\n", s, t, res);
// 空間最適化後の動的計画法
res = editDistanceDPComp(s, t, n, m);
printf("%s を %s に変更するには最小で %d 回の編集が必要です\n", s, t, res);
return 0;
}

View File

@@ -0,0 +1,137 @@
/**
* File: knapsack.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 最大値を求める */
int myMax(int a, int b) {
return a > b ? a : b;
}
/* 0-1 ナップサック:総当たり探索 */
int knapsackDFS(int wgt[], int val[], int i, int c) {
// すべての品物を選び終えたか、ナップサックに残り容量がなければ、価値 0 を返す
if (i == 0 || c == 0) {
return 0;
}
// ナップサック容量を超える場合は、入れない選択しかできない
if (wgt[i - 1] > c) {
return knapsackDFS(wgt, val, i - 1, c);
}
// 品物 i を入れない場合と入れる場合の最大価値を計算する
int no = knapsackDFS(wgt, val, i - 1, c);
int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1];
// 2つの案のうち価値が大きいほうを返す
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];
// 2 つの案のうち価値が大きい方を記録して返す
mem[i][c] = myMax(no, yes);
return mem[i][c];
}
/* 0-1 ナップサック:動的計画法 */
int knapsackDP(int wgt[], int val[], int cap, int wgtSize) {
int n = wgtSize;
// dp テーブルを初期化
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(cap + 1, sizeof(int));
}
// 状態遷移
for (int i = 1; i <= n; i++) {
for (int c = 1; c <= cap; c++) {
if (wgt[i - 1] > c) {
// ナップサック容量を超えるなら品物 i は選ばない
dp[i][c] = dp[i - 1][c];
} else {
// 品物 i を選ばない場合と選ぶ場合の大きい方
dp[i][c] = myMax(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]);
}
}
}
int res = dp[n][cap];
// メモリを解放する
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
return res;
}
/* 0-1 ナップサック:空間最適化後の動的計画法 */
int knapsackDPComp(int wgt[], int val[], int cap, int wgtSize) {
int n = wgtSize;
// dp テーブルを初期化
int *dp = calloc(cap + 1, sizeof(int));
// 状態遷移
for (int i = 1; i <= n; i++) {
// 逆順に走査する
for (int c = cap; c >= 1; c--) {
if (wgt[i - 1] <= c) {
// 品物 i を選ばない場合と選ぶ場合の大きい方
dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
}
}
}
int res = dp[cap];
// メモリを解放する
free(dp);
return res;
}
/* Driver Code */
int main() {
int wgt[] = {10, 20, 30, 40, 50};
int val[] = {50, 120, 150, 210, 240};
int cap = 50;
int n = sizeof(wgt) / sizeof(wgt[0]);
int wgtSize = n;
// 全探索
int res = knapsackDFS(wgt, val, n, cap);
printf("ナップサック容量を超えない最大価値は %d\n", res);
// メモ化探索
int **mem = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
mem[i] = malloc((cap + 1) * sizeof(int));
memset(mem[i], -1, (cap + 1) * sizeof(int));
}
res = knapsackDFSMem(wgt, val, cap + 1, mem, n, cap);
printf("ナップサック容量を超えない最大価値は %d\n", res);
// メモリを解放する
for (int i = 0; i <= n; i++) {
free(mem[i]);
}
free(mem);
// 動的計画法
res = knapsackDP(wgt, val, cap, wgtSize);
printf("ナップサック容量を超えない最大価値は %d\n", res);
// 空間最適化後の動的計画法
res = knapsackDPComp(wgt, val, cap, wgtSize);
printf("ナップサック容量を超えない最大価値は %d\n", res);
return 0;
}

View File

@@ -0,0 +1,62 @@
/**
* File: min_cost_climbing_stairs_dp.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 最小値を求める */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* 階段登りの最小コスト:動的計画法 */
int minCostClimbingStairsDP(int cost[], int costSize) {
int n = costSize - 1;
if (n == 1 || n == 2)
return cost[n];
// 部分問題の解を保存するために dp テーブルを初期化
int *dp = calloc(n + 1, sizeof(int));
// 初期状態:最小部分問題の解をあらかじめ設定
dp[1] = cost[1];
dp[2] = cost[2];
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
for (int i = 3; i <= n; i++) {
dp[i] = myMin(dp[i - 1], dp[i - 2]) + cost[i];
}
int res = dp[n];
// メモリを解放する
free(dp);
return res;
}
/* 階段昇りの最小コスト:空間最適化後の動的計画法 */
int minCostClimbingStairsDPComp(int cost[], int costSize) {
int n = costSize - 1;
if (n == 1 || n == 2)
return cost[n];
int a = cost[1], b = cost[2];
for (int i = 3; i <= n; i++) {
int tmp = b;
b = myMin(a, tmp) + cost[i];
a = tmp;
}
return b;
}
/* Driver Code */
int main() {
int cost[] = {0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1};
int costSize = sizeof(cost) / sizeof(cost[0]);
printf("入力された階段コストのリストは:");
printArray(cost, costSize);
int res = minCostClimbingStairsDP(cost, costSize);
printf("階段を登り切る最小コストは %d\n", res);
res = minCostClimbingStairsDPComp(cost, costSize);
printf("階段を登り切る最小コストは %d\n", res);
return 0;
}

View File

@@ -0,0 +1,134 @@
/**
* File: min_path_sum.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
// 行列の最大行数・列数を 100 と仮定する
#define MAX_SIZE 100
/* 最小値を求める */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* 最小経路和:全探索 */
int minPathSumDFS(int grid[MAX_SIZE][MAX_SIZE], int i, int j) {
// 左上のセルなら探索を終了する
if (i == 0 && j == 0) {
return grid[0][0];
}
// 行または列のインデックスが範囲外なら、コスト +∞ を返す
if (i < 0 || j < 0) {
return INT_MAX;
}
// 左上から (i-1, j) および (i, j-1) までの最小経路コストを計算する
int up = minPathSumDFS(grid, i - 1, j);
int left = minPathSumDFS(grid, i, j - 1);
// 左上隅から (i, j) までの最小経路コストを返す
return myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX;
}
/* 最小経路和:メモ化探索 */
int minPathSumDFSMem(int grid[MAX_SIZE][MAX_SIZE], int mem[MAX_SIZE][MAX_SIZE], int i, int j) {
// 左上のセルなら探索を終了する
if (i == 0 && j == 0) {
return grid[0][0];
}
// 行または列のインデックスが範囲外なら、コスト +∞ を返す
if (i < 0 || j < 0) {
return INT_MAX;
}
// 既に記録があればそのまま返す
if (mem[i][j] != -1) {
return mem[i][j];
}
// 左と上のセルからの最小経路コスト
int up = minPathSumDFSMem(grid, mem, i - 1, j);
int left = minPathSumDFSMem(grid, mem, i, j - 1);
// 左上から (i, j) までの最小経路コストを記録して返す
mem[i][j] = myMin(left, up) != INT_MAX ? myMin(left, up) + grid[i][j] : INT_MAX;
return mem[i][j];
}
/* 最小経路和:動的計画法 */
int minPathSumDP(int grid[MAX_SIZE][MAX_SIZE], int n, int m) {
// dp テーブルを初期化
int **dp = malloc(n * sizeof(int *));
for (int i = 0; i < n; i++) {
dp[i] = calloc(m, sizeof(int));
}
dp[0][0] = grid[0][0];
// 状態遷移:先頭行
for (int j = 1; j < m; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
// 状態遷移:先頭列
for (int i = 1; i < n; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
// 状態遷移: 残りの行と列
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
dp[i][j] = myMin(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
}
}
int res = dp[n - 1][m - 1];
// メモリを解放する
for (int i = 0; i < n; i++) {
free(dp[i]);
}
return res;
}
/* 最小経路和:空間最適化後の動的計画法 */
int minPathSumDPComp(int grid[MAX_SIZE][MAX_SIZE], int n, int m) {
// dp テーブルを初期化
int *dp = calloc(m, sizeof(int));
// 状態遷移:先頭行
dp[0] = grid[0][0];
for (int j = 1; j < m; j++) {
dp[j] = dp[j - 1] + grid[0][j];
}
// 状態遷移:残りの行
for (int i = 1; i < n; i++) {
// 状態遷移:先頭列
dp[0] = dp[0] + grid[i][0];
// 状態遷移:残りの列
for (int j = 1; j < m; j++) {
dp[j] = myMin(dp[j - 1], dp[j]) + grid[i][j];
}
}
int res = dp[m - 1];
// メモリを解放する
free(dp);
return res;
}
/* Driver Code */
int main() {
int grid[MAX_SIZE][MAX_SIZE] = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}};
int n = 4, m = 4; // 行列の容量は `MAX_SIZE * MAX_SIZE`、有効な行数と列数は `n * m`
// 全探索
int res = minPathSumDFS(grid, n - 1, m - 1);
printf("左上から右下までの最小経路和は %d\n", res);
// メモ化探索
int mem[MAX_SIZE][MAX_SIZE];
memset(mem, -1, sizeof(mem));
res = minPathSumDFSMem(grid, mem, n - 1, m - 1);
printf("左上から右下までの最小経路和は %d\n", res);
// 動的計画法
res = minPathSumDP(grid, n, m);
printf("左上から右下までの最小経路和は %d\n", res);
// 空間最適化後の動的計画法
res = minPathSumDPComp(grid, n, m);
printf("左上から右下までの最小経路和は %d\n", res);
return 0;
}

View File

@@ -0,0 +1,81 @@
/**
* File: unbounded_knapsack.c
* Created Time: 2023-10-02
* Author: Zuoxun (845242523@qq.com)
*/
#include "../utils/common.h"
/* 最大値を求める */
int myMax(int a, int b) {
return a > b ? a : b;
}
/* 完全ナップサック問題:動的計画法 */
int unboundedKnapsackDP(int wgt[], int val[], int cap, int wgtSize) {
int n = wgtSize;
// dp テーブルを初期化
int **dp = malloc((n + 1) * sizeof(int *));
for (int i = 0; i <= n; i++) {
dp[i] = calloc(cap + 1, sizeof(int));
}
// 状態遷移
for (int i = 1; i <= n; i++) {
for (int c = 1; c <= cap; c++) {
if (wgt[i - 1] > c) {
// ナップサック容量を超えるなら品物 i は選ばない
dp[i][c] = dp[i - 1][c];
} else {
// 品物 i を選ばない場合と選ぶ場合の大きい方
dp[i][c] = myMax(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1]);
}
}
}
int res = dp[n][cap];
// メモリを解放する
for (int i = 0; i <= n; i++) {
free(dp[i]);
}
return res;
}
/* 完全ナップサック問題:空間最適化後の動的計画法 */
int unboundedKnapsackDPComp(int wgt[], int val[], int cap, int wgtSize) {
int n = wgtSize;
// dp テーブルを初期化
int *dp = calloc(cap + 1, sizeof(int));
// 状態遷移
for (int i = 1; i <= n; i++) {
for (int c = 1; c <= cap; c++) {
if (wgt[i - 1] > c) {
// ナップサック容量を超えるなら品物 i は選ばない
dp[c] = dp[c];
} else {
// 品物 i を選ばない場合と選ぶ場合の大きい方
dp[c] = myMax(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
}
}
}
int res = dp[cap];
// メモリを解放する
free(dp);
return res;
}
/* Driver code */
int main() {
int wgt[] = {1, 2, 3};
int val[] = {5, 11, 15};
int wgtSize = sizeof(wgt) / sizeof(wgt[0]);
int cap = 4;
// 動的計画法
int res = unboundedKnapsackDP(wgt, val, cap, wgtSize);
printf("ナップサック容量を超えない最大価値は %d\n", res);
// 空間最適化後の動的計画法
res = unboundedKnapsackDPComp(wgt, val, cap, wgtSize);
printf("ナップサック容量を超えない最大価値は %d\n", res);
return 0;
}

View File

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

View File

@@ -0,0 +1,171 @@
/**
* File: graph_adjacency_list.c
* Created Time: 2023-07-07
* Author: NI-SW (947743645@qq.com)
*/
#include "../utils/common.h"
// ノード数の上限を 100 と仮定
#define MAX_SIZE 100
/* ノード構造体 */
typedef struct AdjListNode {
Vertex *vertex; // 頂点
struct AdjListNode *next; // 後続ノード
} AdjListNode;
/* 隣接リストに基づく無向グラフクラス */
typedef struct {
AdjListNode *heads[MAX_SIZE]; // ノード配列
int size; // ノード数
} GraphAdjList;
/* コンストラクタ */
GraphAdjList *newGraphAdjList() {
GraphAdjList *graph = (GraphAdjList *)malloc(sizeof(GraphAdjList));
if (!graph) {
return NULL;
}
graph->size = 0;
for (int i = 0; i < MAX_SIZE; i++) {
graph->heads[i] = NULL;
}
return graph;
}
/* デストラクタ */
void delGraphAdjList(GraphAdjList *graph) {
for (int i = 0; i < graph->size; i++) {
AdjListNode *cur = graph->heads[i];
while (cur != NULL) {
AdjListNode *next = cur->next;
if (cur != graph->heads[i]) {
free(cur);
}
cur = next;
}
free(graph->heads[i]->vertex);
free(graph->heads[i]);
}
free(graph);
}
/* 頂点に対応するノードを検索 */
AdjListNode *findNode(GraphAdjList *graph, Vertex *vet) {
for (int i = 0; i < graph->size; i++) {
if (graph->heads[i]->vertex == vet) {
return graph->heads[i];
}
}
return NULL;
}
/* 辺を追加する補助関数 */
void addEdgeHelper(AdjListNode *head, Vertex *vet) {
AdjListNode *node = (AdjListNode *)malloc(sizeof(AdjListNode));
node->vertex = vet;
// 先頭挿入法
node->next = head->next;
head->next = node;
}
/* 辺を追加 */
void addEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) {
AdjListNode *head1 = findNode(graph, vet1);
AdjListNode *head2 = findNode(graph, vet2);
assert(head1 != NULL && head2 != NULL && head1 != head2);
// 辺 vet1 - vet2 を追加
addEdgeHelper(head1, vet2);
addEdgeHelper(head2, vet1);
}
/* 辺削除の補助関数 */
void removeEdgeHelper(AdjListNode *head, Vertex *vet) {
AdjListNode *pre = head;
AdjListNode *cur = head->next;
// 連結リスト内で vet に対応するノードを探索
while (cur != NULL && cur->vertex != vet) {
pre = cur;
cur = cur->next;
}
if (cur == NULL)
return;
// vet に対応するノードを連結リストから削除
pre->next = cur->next;
// メモリを解放する
free(cur);
}
/* 辺を削除 */
void removeEdge(GraphAdjList *graph, Vertex *vet1, Vertex *vet2) {
AdjListNode *head1 = findNode(graph, vet1);
AdjListNode *head2 = findNode(graph, vet2);
assert(head1 != NULL && head2 != NULL);
// 辺 vet1 - vet2 を削除
removeEdgeHelper(head1, head2->vertex);
removeEdgeHelper(head2, head1->vertex);
}
/* 頂点を追加 */
void addVertex(GraphAdjList *graph, Vertex *vet) {
assert(graph != NULL && graph->size < MAX_SIZE);
AdjListNode *head = (AdjListNode *)malloc(sizeof(AdjListNode));
head->vertex = vet;
head->next = NULL;
// 隣接リストに新しいリストを追加
graph->heads[graph->size++] = head;
}
/* 頂点を削除 */
void removeVertex(GraphAdjList *graph, Vertex *vet) {
AdjListNode *node = findNode(graph, vet);
assert(node != NULL);
// 隣接リストから頂点 vet に対応するリストを削除
AdjListNode *cur = node, *pre = NULL;
while (cur) {
pre = cur;
cur = cur->next;
free(pre);
}
// 他の頂点のリストを走査し、vet を含むすべての辺を削除
for (int i = 0; i < graph->size; i++) {
cur = graph->heads[i];
pre = NULL;
while (cur) {
pre = cur;
cur = cur->next;
if (cur && cur->vertex == vet) {
pre->next = cur->next;
free(cur);
break;
}
}
}
// この頂点より後ろの頂点を前に詰めて欠損を埋める
int i;
for (i = 0; i < graph->size; i++) {
if (graph->heads[i] == node)
break;
}
for (int j = i; j < graph->size - 1; j++) {
graph->heads[j] = graph->heads[j + 1];
}
graph->size--;
free(vet);
}
/* 隣接リストを出力 */
void printGraph(const GraphAdjList *graph) {
printf("隣接リスト =\n");
for (int i = 0; i < graph->size; ++i) {
AdjListNode *node = graph->heads[i];
printf("%d: [", node->vertex->val);
node = node->next;
while (node) {
printf("%d, ", node->vertex->val);
node = node->next;
}
printf("]\n");
}
}

View File

@@ -0,0 +1,55 @@
/**
* File: graph_adjacency_list_test.c
* Created Time: 2023-07-11
* Author: NI-SW (947743645@qq.com)
*/
#include "graph_adjacency_list.c"
/* Driver Code */
int main() {
int vals[] = {1, 3, 2, 5, 4};
int size = sizeof(vals) / sizeof(vals[0]);
Vertex **v = valsToVets(vals, size);
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[3]}, {v[2], v[4]}, {v[3], v[4]}};
int egdeSize = sizeof(edges) / sizeof(edges[0]);
GraphAdjList *graph = newGraphAdjList();
// すべての頂点と辺を追加
for (int i = 0; i < size; i++) {
addVertex(graph, v[i]);
}
for (int i = 0; i < egdeSize; i++) {
addEdge(graph, edges[i][0], edges[i][1]);
}
printf("\n初期化後のグラフは\n");
printGraph(graph);
/* 辺を追加 */
// 頂点 1, 2 は v[0], v[2]
addEdge(graph, v[0], v[2]);
printf("\n辺 1-2 を追加した後のグラフは\n");
printGraph(graph);
/* 辺を削除 */
// 頂点 1, 3 は v[0], v[1]
removeEdge(graph, v[0], v[1]);
printf("\n辺 1-3 を削除した後のグラフは\n");
printGraph(graph);
/* 頂点を追加 */
Vertex *v5 = newVertex(6);
addVertex(graph, v5);
printf("\n頂点 6 を追加した後のグラフは\n");
printGraph(graph);
/* 頂点を削除 */
// 頂点 3 は v[1]
removeVertex(graph, v[1]);
printf("\n頂点 3 を削除すると、グラフは次のようになります:\n");
printGraph(graph);
// メモリを解放する
delGraphAdjList(graph);
free(v);
return 0;
}

View File

@@ -0,0 +1,150 @@
/**
* File: graph_adjacency_matrix.c
* Created Time: 2023-07-06
* Author: NI-SW (947743645@qq.com)
*/
#include "../utils/common.h"
// 頂点数の最大値を 100 と仮定する
#define MAX_SIZE 100
/* 隣接行列に基づく無向グラフ構造体 */
typedef struct {
int vertices[MAX_SIZE];
int adjMat[MAX_SIZE][MAX_SIZE];
int size;
} GraphAdjMat;
/* コンストラクタ */
GraphAdjMat *newGraphAdjMat() {
GraphAdjMat *graph = (GraphAdjMat *)malloc(sizeof(GraphAdjMat));
graph->size = 0;
for (int i = 0; i < MAX_SIZE; i++) {
for (int j = 0; j < MAX_SIZE; j++) {
graph->adjMat[i][j] = 0;
}
}
return graph;
}
/* デストラクタ */
void delGraphAdjMat(GraphAdjMat *graph) {
free(graph);
}
/* 頂点を追加 */
void addVertex(GraphAdjMat *graph, int val) {
if (graph->size == MAX_SIZE) {
fprintf(stderr, "グラフの頂点数が最大値に達しました\n");
return;
}
// n 番目の頂点を追加し、n 行目と n 列目を 0 にする
int n = graph->size;
graph->vertices[n] = val;
for (int i = 0; i <= n; i++) {
graph->adjMat[n][i] = graph->adjMat[i][n] = 0;
}
graph->size++;
}
/* 頂点を削除 */
void removeVertex(GraphAdjMat *graph, int index) {
if (index < 0 || index >= graph->size) {
fprintf(stderr, "頂点インデックスが範囲外です\n");
return;
}
// 頂点リストから index の頂点を削除する
for (int i = index; i < graph->size - 1; i++) {
graph->vertices[i] = graph->vertices[i + 1];
}
// 隣接行列で index 行を削除する
for (int i = index; i < graph->size - 1; i++) {
for (int j = 0; j < graph->size; j++) {
graph->adjMat[i][j] = graph->adjMat[i + 1][j];
}
}
// 隣接行列で index 列を削除する
for (int i = 0; i < graph->size; i++) {
for (int j = index; j < graph->size - 1; j++) {
graph->adjMat[i][j] = graph->adjMat[i][j + 1];
}
}
graph->size--;
}
/* 辺を追加 */
// 引数 i, j は vertices の要素インデックスに対応する
void addEdge(GraphAdjMat *graph, int i, int j) {
if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) {
fprintf(stderr, "辺インデックスが範囲外であるか、同一です\n");
return;
}
graph->adjMat[i][j] = 1;
graph->adjMat[j][i] = 1;
}
/* 辺を削除 */
// 引数 i, j は vertices の要素インデックスに対応する
void removeEdge(GraphAdjMat *graph, int i, int j) {
if (i < 0 || j < 0 || i >= graph->size || j >= graph->size || i == j) {
fprintf(stderr, "辺インデックスが範囲外であるか、同一です\n");
return;
}
graph->adjMat[i][j] = 0;
graph->adjMat[j][i] = 0;
}
/* 隣接行列を出力 */
void printGraphAdjMat(GraphAdjMat *graph) {
printf("頂点リスト = ");
printArray(graph->vertices, graph->size);
printf("隣接行列 =\n");
for (int i = 0; i < graph->size; i++) {
printArray(graph->adjMat[i], graph->size);
}
}
/* Driver Code */
int main() {
// 無向グラフを初期化
GraphAdjMat *graph = newGraphAdjMat();
int vertices[] = {1, 3, 2, 5, 4};
for (int i = 0; i < 5; i++) {
addVertex(graph, vertices[i]);
}
int edges[][2] = {{0, 1}, {0, 3}, {1, 2}, {2, 3}, {2, 4}, {3, 4}};
for (int i = 0; i < 6; i++) {
addEdge(graph, edges[i][0], edges[i][1]);
}
printf("\n初期化後のグラフは\n");
printGraphAdjMat(graph);
/* 辺を追加 */
// 頂点 1, 2 のインデックスはそれぞれ 0, 2
addEdge(graph, 0, 2);
printf("\n辺 1-2 を追加した後のグラフは\n");
printGraphAdjMat(graph);
/* 辺を削除 */
// 頂点 1, 3 のインデックスはそれぞれ 0, 1
removeEdge(graph, 0, 1);
printf("\n辺 1-3 を削除した後のグラフは\n");
printGraphAdjMat(graph);
/* 頂点を追加 */
addVertex(graph, 6);
printf("\n頂点 6 を追加した後のグラフは\n");
printGraphAdjMat(graph);
/* 頂点を削除 */
// 頂点 3 のインデックスは 1
removeVertex(graph, 1);
printf("\n頂点 3 を削除すると、グラフは次のようになります\n");
printGraphAdjMat(graph);
// メモリを解放する
delGraphAdjMat(graph);
return 0;
}

View File

@@ -0,0 +1,116 @@
/**
* File: graph_bfs.c
* Created Time: 2023-07-11
* Author: NI-SW (947743645@qq.com)
*/
#include "graph_adjacency_list.c"
// ノード数の上限を 100 と仮定
#define MAX_SIZE 100
/* ノードキュー構造体 */
typedef struct {
Vertex *vertices[MAX_SIZE];
int front, rear, size;
} Queue;
/* コンストラクタ */
Queue *newQueue() {
Queue *q = (Queue *)malloc(sizeof(Queue));
q->front = q->rear = q->size = 0;
return q;
}
/* キューが空かどうかを判定 */
int isEmpty(Queue *q) {
return q->size == 0;
}
/* エンキュー操作 */
void enqueue(Queue *q, Vertex *vet) {
q->vertices[q->rear] = vet;
q->rear = (q->rear + 1) % MAX_SIZE;
q->size++;
}
/* デキュー操作 */
Vertex *dequeue(Queue *q) {
Vertex *vet = q->vertices[q->front];
q->front = (q->front + 1) % MAX_SIZE;
q->size--;
return vet;
}
/* 頂点が訪問済みかを確認 */
int isVisited(Vertex **visited, int size, Vertex *vet) {
// 走査してードを探すため、O(n) 時間を要する
for (int i = 0; i < size; i++) {
if (visited[i] == vet)
return 1;
}
return 0;
}
/* 幅優先探索 */
// グラフを隣接リストで表し、指定した頂点の隣接頂点をすべて取得できるようにする
void graphBFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize, Vertex **visited, int *visitedSize) {
// BFS の実装にキューを用いる
Queue *queue = newQueue();
enqueue(queue, startVet);
visited[(*visitedSize)++] = startVet;
// 頂点 vet を起点に、すべての頂点を訪問し終えるまで繰り返す
while (!isEmpty(queue)) {
Vertex *vet = dequeue(queue); // 先頭の頂点をデキュー
res[(*resSize)++] = vet; // 訪問した頂点を記録
// この頂点のすべての隣接頂点を走査
AdjListNode *node = findNode(graph, vet);
while (node != NULL) {
// 訪問済みの頂点をスキップ
if (!isVisited(visited, *visitedSize, node->vertex)) {
enqueue(queue, node->vertex); // 未訪問の頂点のみをキューに追加
visited[(*visitedSize)++] = node->vertex; // この頂点を訪問済みにする
}
node = node->next;
}
}
// メモリを解放する
free(queue);
}
/* Driver Code */
int main() {
// 無向グラフを初期化
int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int size = sizeof(vals) / sizeof(vals[0]);
Vertex **v = valsToVets(vals, size);
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[1], v[4]}, {v[2], v[5]}, {v[3], v[4]},
{v[3], v[6]}, {v[4], v[5]}, {v[4], v[7]}, {v[5], v[8]}, {v[6], v[7]}, {v[7], v[8]}};
int egdeSize = sizeof(edges) / sizeof(edges[0]);
GraphAdjList *graph = newGraphAdjList();
// すべての頂点と辺を追加
for (int i = 0; i < size; i++) {
addVertex(graph, v[i]);
}
for (int i = 0; i < egdeSize; i++) {
addEdge(graph, edges[i][0], edges[i][1]);
}
printf("\n初期化後のグラフは\n");
printGraph(graph);
// 幅優先探索
// 頂点の走査順序
Vertex *res[MAX_SIZE];
int resSize = 0;
// 訪問済みの頂点を記録する
Vertex *visited[MAX_SIZE];
int visitedSize = 0;
graphBFS(graph, v[0], res, &resSize, visited, &visitedSize);
printf("\n幅優先探索BFSの頂点列は次のとおりです\n");
printArray(vetsToVals(res, resSize), resSize);
// メモリを解放する
delGraphAdjList(graph);
free(v);
return 0;
}

View File

@@ -0,0 +1,75 @@
/**
* File: graph_dfs.c
* Created Time: 2023-07-13
* Author: NI-SW (947743645@qq.com)
*/
#include "graph_adjacency_list.c"
// ノード数の上限を 100 と仮定
#define MAX_SIZE 100
/* 頂点が訪問済みかを確認 */
int isVisited(Vertex **res, int size, Vertex *vet) {
// 走査してードを探すため、O(n) 時間を要する
for (int i = 0; i < size; i++) {
if (res[i] == vet) {
return 1;
}
}
return 0;
}
/* 深さ優先走査の補助関数 */
void dfs(GraphAdjList *graph, Vertex **res, int *resSize, Vertex *vet) {
// 訪問した頂点を記録
res[(*resSize)++] = vet;
// この頂点のすべての隣接頂点を走査
AdjListNode *node = findNode(graph, vet);
while (node != NULL) {
// 訪問済みの頂点をスキップ
if (!isVisited(res, *resSize, node->vertex)) {
// 隣接頂点を再帰的に訪問
dfs(graph, res, resSize, node->vertex);
}
node = node->next;
}
}
/* 深さ優先探索 */
// グラフを隣接リストで表し、指定した頂点の隣接頂点をすべて取得できるようにする
void graphDFS(GraphAdjList *graph, Vertex *startVet, Vertex **res, int *resSize) {
dfs(graph, res, resSize, startVet);
}
/* Driver Code */
int main() {
// 無向グラフを初期化
int vals[] = {0, 1, 2, 3, 4, 5, 6};
int size = sizeof(vals) / sizeof(vals[0]);
Vertex **v = valsToVets(vals, size);
Vertex *edges[][2] = {{v[0], v[1]}, {v[0], v[3]}, {v[1], v[2]}, {v[2], v[5]}, {v[4], v[5]}, {v[5], v[6]}};
int egdeSize = sizeof(edges) / sizeof(edges[0]);
GraphAdjList *graph = newGraphAdjList();
// すべての頂点と辺を追加
for (int i = 0; i < size; i++) {
addVertex(graph, v[i]);
}
for (int i = 0; i < egdeSize; i++) {
addEdge(graph, edges[i][0], edges[i][1]);
}
printf("\n初期化後のグラフは\n");
printGraph(graph);
// 深さ優先探索
Vertex *res[MAX_SIZE];
int resSize = 0;
graphDFS(graph, v[0], res, &resSize);
printf("\n深さ優先探索DFSの頂点列は次のとおりです\n");
printArray(vetsToVals(res, resSize), resSize);
// メモリを解放する
delGraphAdjList(graph);
free(v);
return 0;
}

View File

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

View File

@@ -0,0 +1,60 @@
/**
* File: coin_change_greedy.c
* Created Time: 2023-09-07
* Author: lwbaptx (lwbaptx@gmail.com)
*/
#include "../utils/common.h"
/* コイン交換:貪欲法 */
int coinChangeGreedy(int *coins, int size, int amt) {
// coins リストはソート済みと仮定する
int i = size - 1;
int count = 0;
// 残額がなくなるまで貪欲選択を繰り返す
while (amt > 0) {
// 残額以下で最も近い硬貨を見つける
while (i > 0 && coins[i] > amt) {
i--;
}
// coins[i] を選択する
amt -= coins[i];
count++;
}
// 実行可能な解が見つからなければ -1 を返す
return amt == 0 ? count : -1;
}
/* Driver Code */
int main() {
// 貪欲法:大域最適解を保証できる
int coins1[6] = {1, 5, 10, 20, 50, 100};
int amt = 186;
int res = coinChangeGreedy(coins1, 6, amt);
printf("\ncoins = ");
printArray(coins1, 6);
printf("amt = %d\n", amt);
printf("%d を作るのに必要な最小硬貨枚数は %d です\n", amt, res);
// 貪欲法:大域最適解を保証できない
int coins2[3] = {1, 20, 50};
amt = 60;
res = coinChangeGreedy(coins2, 3, amt);
printf("\ncoins = ");
printArray(coins2, 3);
printf("amt = %d\n", amt);
printf("%d を作るのに必要な最小硬貨枚数は %d です\n", amt, res);
printf("実際に必要な最小枚数は 3、つまり 20 + 20 + 20 です\n");
// 貪欲法:大域最適解を保証できない
int coins3[3] = {1, 49, 50};
amt = 98;
res = coinChangeGreedy(coins3, 3, amt);
printf("\ncoins = ");
printArray(coins3, 3);
printf("amt = %d\n", amt);
printf("%d を作るのに必要な最小硬貨枚数は %d です\n", amt, res);
printf("実際に必要な最小枚数は 2、つまり 49 + 49 です\n");
return 0;
}

View File

@@ -0,0 +1,60 @@
/**
* File: fractional_knapsack.c
* Created Time: 2023-09-14
* Author: xianii (xianyi.xia@outlook.com)
*/
#include "../utils/common.h"
/* 品物 */
typedef struct {
int w; // 品物の重さ
int v; // 品物の価値
} Item;
/* 価値密度でソート */
int sortByValueDensity(const void *a, const void *b) {
Item *t1 = (Item *)a;
Item *t2 = (Item *)b;
return (float)(t1->v) / t1->w < (float)(t2->v) / t2->w;
}
/* 分数ナップサック:貪欲法 */
float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) {
// 重さと価値の 2 属性を持つ品物リストを作成
Item *items = malloc(sizeof(Item) * itemCount);
for (int i = 0; i < itemCount; i++) {
items[i] = (Item){.w = wgt[i], .v = val[i]};
}
// 単位価値 item.v / item.w の高い順にソートする
qsort(items, (size_t)itemCount, sizeof(Item), sortByValueDensity);
// 貪欲選択を繰り返す
float res = 0.0;
for (int i = 0; i < itemCount; i++) {
if (items[i].w <= cap) {
// 残り容量が十分なら、現在の品物を丸ごとナップサックに入れる
res += items[i].v;
cap -= items[i].w;
} else {
// 残り容量が足りない場合は、現在の品物の一部だけをナップサックに入れる
res += (float)cap / items[i].w * items[i].v;
cap = 0;
break;
}
}
free(items);
return res;
}
/* Driver Code */
int main(void) {
int wgt[] = {10, 20, 30, 40, 50};
int val[] = {50, 120, 150, 210, 240};
int capacity = 50;
// 貪欲法
float res = fractionalKnapsack(wgt, val, sizeof(wgt) / sizeof(int), capacity);
printf("ナップサック容量を超えない最大の品物価値は %0.2f です\n", res);
return 0;
}

View File

@@ -0,0 +1,49 @@
/**
* File: max_capacity.c
* Created Time: 2023-09-15
* Author: xianii (xianyi.xia@outlook.com)
*/
#include "../utils/common.h"
/* 最小値を求める */
int myMin(int a, int b) {
return a < b ? a : b;
}
/* 最大値を求める */
int myMax(int a, int b) {
return a > b ? a : b;
}
/* 最大容量:貪欲法 */
int maxCapacity(int ht[], int htLength) {
// i, j を初期化し、それぞれ配列の両端に置く
int i = 0;
int j = htLength - 1;
// 初期の最大容量は 0
int res = 0;
// 2 枚の板が出会うまで貪欲選択を繰り返す
while (i < j) {
// 最大容量を更新する
int capacity = myMin(ht[i], ht[j]) * (j - i);
res = myMax(res, capacity);
// 短い方を内側へ動かす
if (ht[i] < ht[j]) {
i++;
} else {
j--;
}
}
return res;
}
/* Driver Code */
int main(void) {
int ht[] = {3, 8, 5, 2, 7, 7, 3, 4};
// 貪欲法
int res = maxCapacity(ht, sizeof(ht) / sizeof(int));
printf("最大容量は %d です\n", res);
return 0;
}

View File

@@ -0,0 +1,38 @@
/**
* File: max_product_cutting.c
* Created Time: 2023-09-15
* Author: xianii (xianyi.xia@outlook.com)
*/
#include "../utils/common.h"
/* 最大切断積:貪欲法 */
int maxProductCutting(int n) {
// n <= 3 のときは、必ず 1 を切り出す
if (n <= 3) {
return 1 * (n - 1);
}
// 貪欲に 3 を切り出し、a を 3 の個数、b を余りとする
int a = n / 3;
int b = n % 3;
if (b == 1) {
// 余りが 1 のときは、1 * 3 を 2 * 2 に変える
return pow(3, a - 1) * 2 * 2;
}
if (b == 2) {
// 余りが 2 のときは、そのままにする
return pow(3, a) * 2;
}
// 余りが 0 のときは、そのままにする
return pow(3, a);
}
/* Driver Code */
int main(void) {
int n = 58;
// 貪欲法
int res = maxProductCutting(n);
printf("最大分割積は %d です\n", res);
return 0;
}

View File

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

View File

@@ -0,0 +1,215 @@
/**
* File: array_hash_map.c
* Created Time: 2023-03-18
* Author: Guanngxu (446678850@qq.com)
*/
#include "../utils/common.h"
/* ハッシュテーブルのデフォルトサイズ */
#define MAX_SIZE 100
/* キーと値の組 int->string */
typedef struct {
int key;
char *val;
} Pair;
/* キーと値の組の集合 */
typedef struct {
void *set;
int len;
} MapSet;
/* 配列ベースのハッシュテーブル */
typedef struct {
Pair *buckets[MAX_SIZE];
} ArrayHashMap;
/* コンストラクタ */
ArrayHashMap *newArrayHashMap() {
ArrayHashMap *hmap = malloc(sizeof(ArrayHashMap));
for (int i=0; i < MAX_SIZE; i++) {
hmap->buckets[i] = NULL;
}
return hmap;
}
/* デストラクタ */
void delArrayHashMap(ArrayHashMap *hmap) {
for (int i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
free(hmap->buckets[i]->val);
free(hmap->buckets[i]);
}
}
free(hmap);
}
/* ハッシュ関数 */
int hashFunc(int key) {
int index = key % MAX_SIZE;
return index;
}
/* 検索操作 */
const char *get(const ArrayHashMap *hmap, const int key) {
int index = hashFunc(key);
const Pair *Pair = hmap->buckets[index];
if (Pair == NULL)
return NULL;
return Pair->val;
}
/* 追加操作 */
void put(ArrayHashMap *hmap, const int key, const char *val) {
Pair *Pair = malloc(sizeof(Pair));
Pair->key = key;
Pair->val = malloc(strlen(val) + 1);
strcpy(Pair->val, val);
int index = hashFunc(key);
hmap->buckets[index] = Pair;
}
/* 削除操作 */
void removeItem(ArrayHashMap *hmap, const int key) {
int index = hashFunc(key);
free(hmap->buckets[index]->val);
free(hmap->buckets[index]);
hmap->buckets[index] = NULL;
}
/* すべてのキーと値のペアを取得 */
void pairSet(ArrayHashMap *hmap, MapSet *set) {
Pair *entries;
int i = 0, index = 0;
int total = 0;
/* 有効なキーと値のペア数を集計 */
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
entries = malloc(sizeof(Pair) * total);
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
entries[index].key = hmap->buckets[i]->key;
entries[index].val = malloc(strlen(hmap->buckets[i]->val) + 1);
strcpy(entries[index].val, hmap->buckets[i]->val);
index++;
}
}
set->set = entries;
set->len = total;
}
/* すべてのキーを取得 */
void keySet(ArrayHashMap *hmap, MapSet *set) {
int *keys;
int i = 0, index = 0;
int total = 0;
/* 有効なキーと値のペア数を集計 */
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
keys = malloc(total * sizeof(int));
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
keys[index] = hmap->buckets[i]->key;
index++;
}
}
set->set = keys;
set->len = total;
}
/* すべての値を取得 */
void valueSet(ArrayHashMap *hmap, MapSet *set) {
char **vals;
int i = 0, index = 0;
int total = 0;
/* 有効なキーと値のペア数を集計 */
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
vals = malloc(total * sizeof(char *));
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
vals[index] = hmap->buckets[i]->val;
index++;
}
}
set->set = vals;
set->len = total;
}
/* ハッシュテーブルを出力 */
void print(ArrayHashMap *hmap) {
int i;
MapSet set;
pairSet(hmap, &set);
Pair *entries = (Pair *)set.set;
for (i = 0; i < set.len; i++) {
printf("%d -> %s\n", entries[i].key, entries[i].val);
}
free(set.set);
}
/* Driver Code */
int main() {
/* ハッシュテーブルを初期化 */
ArrayHashMap *hmap = newArrayHashMap();
/* 追加操作 */
// ハッシュテーブルにキーと値のペア (key, value) を追加
put(hmap, 12836, "シャオハー");
put(hmap, 15937, "シャオルオ");
put(hmap, 16750, "シャオスワン");
put(hmap, 13276, "シャオファー");
put(hmap, 10583, "シャオヤー");
printf("\n追加完了後、ハッシュテーブルは\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("\n10583 を削除した後、ハッシュテーブルは\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("\nValue のみを個別に走査\n");
for (i = 0; i < set.len; i++) {
printf("%s\n", vals[i]);
}
free(set.set);
delArrayHashMap(hmap);
return 0;
}

View File

@@ -0,0 +1,213 @@
/**
* File: hash_map_chaining.c
* Created Time: 2023-10-13
* Author: SenMing (1206575349@qq.com), krahets (krahets@163.com)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// val の最大長を 100 と仮定する
#define MAX_SIZE 100
/* キーと値の組 */
typedef struct {
int key;
char val[MAX_SIZE];
} Pair;
/* 連結リストノード */
typedef struct Node {
Pair *pair;
struct Node *next;
} Node;
/* チェイン法ハッシュテーブル */
typedef struct {
int size; // キーと値のペア数
int capacity; // ハッシュテーブル容量
double loadThres; // リサイズを発動する負荷率のしきい値
int extendRatio; // 拡張倍率
Node **buckets; // バケット配列
} HashMapChaining;
/* コンストラクタ */
HashMapChaining *newHashMapChaining() {
HashMapChaining *hashMap = (HashMapChaining *)malloc(sizeof(HashMapChaining));
hashMap->size = 0;
hashMap->capacity = 4;
hashMap->loadThres = 2.0 / 3.0;
hashMap->extendRatio = 2;
hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *));
for (int i = 0; i < hashMap->capacity; i++) {
hashMap->buckets[i] = NULL;
}
return hashMap;
}
/* デストラクタ */
void delHashMapChaining(HashMapChaining *hashMap) {
for (int i = 0; i < hashMap->capacity; i++) {
Node *cur = hashMap->buckets[i];
while (cur) {
Node *tmp = cur;
cur = cur->next;
free(tmp->pair);
free(tmp);
}
}
free(hashMap->buckets);
free(hashMap);
}
/* ハッシュ関数 */
int hashFunc(HashMapChaining *hashMap, int key) {
return key % hashMap->capacity;
}
/* 負荷率 */
double loadFactor(HashMapChaining *hashMap) {
return (double)hashMap->size / (double)hashMap->capacity;
}
/* 検索操作 */
char *get(HashMapChaining *hashMap, int key) {
int index = hashFunc(hashMap, key);
// バケットを走査し、key が見つかれば対応する val を返す
Node *cur = hashMap->buckets[index];
while (cur) {
if (cur->pair->key == key) {
return cur->pair->val;
}
cur = cur->next;
}
return ""; // key が見つからない場合は空文字列を返す
}
/* 追加操作 */
void put(HashMapChaining *hashMap, int key, const char *val);
/* ハッシュテーブルを拡張 */
void extend(HashMapChaining *hashMap) {
// 元のハッシュテーブルを一時保存
int oldCapacity = hashMap->capacity;
Node **oldBuckets = hashMap->buckets;
// リサイズ後の新しいハッシュテーブルを初期化
hashMap->capacity *= hashMap->extendRatio;
hashMap->buckets = (Node **)malloc(hashMap->capacity * sizeof(Node *));
for (int i = 0; i < hashMap->capacity; i++) {
hashMap->buckets[i] = NULL;
}
hashMap->size = 0;
// キーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移す
for (int i = 0; i < oldCapacity; i++) {
Node *cur = oldBuckets[i];
while (cur) {
put(hashMap, cur->pair->key, cur->pair->val);
Node *temp = cur;
cur = cur->next;
// メモリを解放する
free(temp->pair);
free(temp);
}
}
free(oldBuckets);
}
/* 追加操作 */
void put(HashMapChaining *hashMap, int key, const char *val) {
// 負荷率がしきい値を超えたら、リサイズを実行
if (loadFactor(hashMap) > hashMap->loadThres) {
extend(hashMap);
}
int index = hashFunc(hashMap, key);
// バケットを走査し、指定した key が見つかれば対応する val を更新して返す
Node *cur = hashMap->buckets[index];
while (cur) {
if (cur->pair->key == key) {
strcpy(cur->pair->val, val); // 指定した `key` に遭遇したら、対応する `val` を更新して返す
return;
}
cur = cur->next;
}
// 該当する `key` がなければ、キーと値のペアを連結リストの先頭に追加する
Pair *newPair = (Pair *)malloc(sizeof(Pair));
newPair->key = key;
strcpy(newPair->val, val);
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->pair = newPair;
newNode->next = hashMap->buckets[index];
hashMap->buckets[index] = newNode;
hashMap->size++;
}
/* 削除操作 */
void removeItem(HashMapChaining *hashMap, int key) {
int index = hashFunc(hashMap, key);
Node *cur = hashMap->buckets[index];
Node *pre = NULL;
while (cur) {
if (cur->pair->key == key) {
// そこからキーと値の組を削除する
if (pre) {
pre->next = cur->next;
} else {
hashMap->buckets[index] = cur->next;
}
// メモリを解放する
free(cur->pair);
free(cur);
hashMap->size--;
return;
}
pre = cur;
cur = cur->next;
}
}
/* ハッシュテーブルを出力 */
void print(HashMapChaining *hashMap) {
for (int i = 0; i < hashMap->capacity; i++) {
Node *cur = hashMap->buckets[i];
printf("[");
while (cur) {
printf("%d -> %s, ", cur->pair->key, cur->pair->val);
cur = cur->next;
}
printf("]\n");
}
}
/* Driver Code */
int main() {
/* ハッシュテーブルを初期化 */
HashMapChaining *hashMap = newHashMapChaining();
/* 追加操作 */
// ハッシュテーブルにキーと値のペア (key, value) を追加
put(hashMap, 12836, "シャオハー");
put(hashMap, 15937, "シャオルオ");
put(hashMap, 16750, "シャオスワン");
put(hashMap, 13276, "シャオファー");
put(hashMap, 10583, "シャオヤー");
printf("\n追加完了後、ハッシュテーブルは\nKey -> Value\n");
print(hashMap);
/* 検索操作 */
// キー key をハッシュテーブルに渡し、値 value を取得
char *name = get(hashMap, 13276);
printf("\n学籍番号 13276 を入力すると、名前 %s が見つかりました\n", name);
/* 削除操作 */
// ハッシュテーブルからキーと値のペア (key, value) を削除
removeItem(hashMap, 12836);
printf("\n学籍番号 12836 を削除した後、ハッシュテーブルは\nKey -> Value\n");
print(hashMap);
/* ハッシュテーブルの領域を解放する */
delHashMapChaining(hashMap);
return 0;
}

View File

@@ -0,0 +1,211 @@
/**
* File: hash_map_open_addressing.c
* Created Time: 2023-10-6
* Author: lclc6 (w1929522410@163.com)
*/
#include "../utils/common.h"
/* オープンアドレス法ハッシュテーブル */
typedef struct {
int key;
char *val;
} Pair;
/* オープンアドレス法ハッシュテーブル */
typedef struct {
int size; // キーと値のペア数
int capacity; // ハッシュテーブル容量
double loadThres; // リサイズを発動する負荷率のしきい値
int extendRatio; // 拡張倍率
Pair **buckets; // バケット配列
Pair *TOMBSTONE; // 削除済みマーク
} HashMapOpenAddressing;
// 関数宣言
void extend(HashMapOpenAddressing *hashMap);
/* コンストラクタ */
HashMapOpenAddressing *newHashMapOpenAddressing() {
HashMapOpenAddressing *hashMap = (HashMapOpenAddressing *)malloc(sizeof(HashMapOpenAddressing));
hashMap->size = 0;
hashMap->capacity = 4;
hashMap->loadThres = 2.0 / 3.0;
hashMap->extendRatio = 2;
hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *));
hashMap->TOMBSTONE = (Pair *)malloc(sizeof(Pair));
hashMap->TOMBSTONE->key = -1;
hashMap->TOMBSTONE->val = "-1";
return hashMap;
}
/* デストラクタ */
void delHashMapOpenAddressing(HashMapOpenAddressing *hashMap) {
for (int i = 0; i < hashMap->capacity; i++) {
Pair *pair = hashMap->buckets[i];
if (pair != NULL && pair != hashMap->TOMBSTONE) {
free(pair->val);
free(pair);
}
}
free(hashMap->buckets);
free(hashMap->TOMBSTONE);
free(hashMap);
}
/* ハッシュ関数 */
int hashFunc(HashMapOpenAddressing *hashMap, int key) {
return key % hashMap->capacity;
}
/* 負荷率 */
double loadFactor(HashMapOpenAddressing *hashMap) {
return (double)hashMap->size / (double)hashMap->capacity;
}
/* key に対応するバケットインデックスを探す */
int findBucket(HashMapOpenAddressing *hashMap, int key) {
int index = hashFunc(hashMap, key);
int firstTombstone = -1;
// 線形プロービングを行い、空バケットに達したら終了
while (hashMap->buckets[index] != NULL) {
// key が見つかったら、対応するバケットのインデックスを返す
if (hashMap->buckets[index]->key == key) {
// 以前に削除マークが見つかっていれば、そのインデックスへキーと値のペアを移動
if (firstTombstone != -1) {
hashMap->buckets[firstTombstone] = hashMap->buckets[index];
hashMap->buckets[index] = hashMap->TOMBSTONE;
return firstTombstone; // 移動後のバケットインデックスを返す
}
return index; // バケットのインデックスを返す
}
// 最初に見つかった削除マークを記録
if (firstTombstone == -1 && hashMap->buckets[index] == hashMap->TOMBSTONE) {
firstTombstone = index;
}
// バケットのインデックスを計算し、末尾を越えたら先頭に戻る
index = (index + 1) % hashMap->capacity;
}
// key が存在しない場合は追加位置のインデックスを返す
return firstTombstone == -1 ? index : firstTombstone;
}
/* 検索操作 */
char *get(HashMapOpenAddressing *hashMap, int key) {
// key に対応するバケットインデックスを探す
int index = findBucket(hashMap, key);
// キーと値の組が見つかったら、対応する val を返す
if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) {
return hashMap->buckets[index]->val;
}
// キーと値の組が存在しない場合は空文字列を返す
return "";
}
/* 追加操作 */
void put(HashMapOpenAddressing *hashMap, int key, char *val) {
// 負荷率がしきい値を超えたら、リサイズを実行
if (loadFactor(hashMap) > hashMap->loadThres) {
extend(hashMap);
}
// key に対応するバケットインデックスを探す
int index = findBucket(hashMap, key);
// キーと値の組が見つかったら、val を上書きして返す
if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) {
free(hashMap->buckets[index]->val);
hashMap->buckets[index]->val = (char *)malloc(sizeof(strlen(val) + 1));
strcpy(hashMap->buckets[index]->val, val);
hashMap->buckets[index]->val[strlen(val)] = '\0';
return;
}
// キーと値の組が存在しない場合は、その組を追加する
Pair *pair = (Pair *)malloc(sizeof(Pair));
pair->key = key;
pair->val = (char *)malloc(sizeof(strlen(val) + 1));
strcpy(pair->val, val);
pair->val[strlen(val)] = '\0';
hashMap->buckets[index] = pair;
hashMap->size++;
}
/* 削除操作 */
void removeItem(HashMapOpenAddressing *hashMap, int key) {
// key に対応するバケットインデックスを探す
int index = findBucket(hashMap, key);
// キーと値の組が見つかったら、削除マーカーで上書きする
if (hashMap->buckets[index] != NULL && hashMap->buckets[index] != hashMap->TOMBSTONE) {
Pair *pair = hashMap->buckets[index];
free(pair->val);
free(pair);
hashMap->buckets[index] = hashMap->TOMBSTONE;
hashMap->size--;
}
}
/* ハッシュテーブルを拡張 */
void extend(HashMapOpenAddressing *hashMap) {
// 元のハッシュテーブルを一時保存
Pair **bucketsTmp = hashMap->buckets;
int oldCapacity = hashMap->capacity;
// リサイズ後の新しいハッシュテーブルを初期化
hashMap->capacity *= hashMap->extendRatio;
hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *));
hashMap->size = 0;
// キーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移す
for (int i = 0; i < oldCapacity; i++) {
Pair *pair = bucketsTmp[i];
if (pair != NULL && pair != hashMap->TOMBSTONE) {
put(hashMap, pair->key, pair->val);
free(pair->val);
free(pair);
}
}
free(bucketsTmp);
}
/* ハッシュテーブルを出力 */
void print(HashMapOpenAddressing *hashMap) {
for (int i = 0; i < hashMap->capacity; i++) {
Pair *pair = hashMap->buckets[i];
if (pair == NULL) {
printf("NULL\n");
} else if (pair == hashMap->TOMBSTONE) {
printf("TOMBSTONE\n");
} else {
printf("%d -> %s\n", pair->key, pair->val);
}
}
}
/* Driver Code */
int main() {
// ハッシュテーブルを初期化
HashMapOpenAddressing *hashmap = newHashMapOpenAddressing();
// 追加操作
// ハッシュテーブルにキーと値の組 (key, val) を追加する
put(hashmap, 12836, "シャオハー");
put(hashmap, 15937, "シャオルオ");
put(hashmap, 16750, "シャオスワン");
put(hashmap, 13276, "シャオファー");
put(hashmap, 10583, "シャオヤー");
printf("\n追加完了後、ハッシュテーブルは\nKey -> Value\n");
print(hashmap);
// 検索操作
// ハッシュテーブルにキー key を入力し、値 val を得る
char *name = get(hashmap, 13276);
printf("\n学籍番号 13276 を入力すると、名前 %s が見つかりました\n", name);
// 削除操作
// ハッシュテーブルからキーと値の組 (key, val) を削除する
removeItem(hashmap, 16750);
printf("\n16750 を削除した後、ハッシュテーブルは\nKey -> Value\n");
print(hashmap);
// ハッシュテーブルを破棄する
delHashMapOpenAddressing(hashmap);
return 0;
}

View File

@@ -0,0 +1,68 @@
/**
* File: simple_hash.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 加算ハッシュ */
int addHash(char *key) {
long long hash = 0;
const int MODULUS = 1000000007;
for (int i = 0; i < strlen(key); i++) {
hash = (hash + (unsigned char)key[i]) % MODULUS;
}
return (int)hash;
}
/* 乗算ハッシュ */
int mulHash(char *key) {
long long hash = 0;
const int MODULUS = 1000000007;
for (int i = 0; i < strlen(key); i++) {
hash = (31 * hash + (unsigned char)key[i]) % MODULUS;
}
return (int)hash;
}
/* XOR ハッシュ */
int xorHash(char *key) {
int hash = 0;
const int MODULUS = 1000000007;
for (int i = 0; i < strlen(key); i++) {
hash ^= (unsigned char)key[i];
}
return hash & MODULUS;
}
/* 回転ハッシュ */
int rotHash(char *key) {
long long hash = 0;
const int MODULUS = 1000000007;
for (int i = 0; i < strlen(key); i++) {
hash = ((hash << 4) ^ (hash >> 28) ^ (unsigned char)key[i]) % MODULUS;
}
return (int)hash;
}
/* Driver Code */
int main() {
char *key = "Hello アルゴリズム";
int hash = addHash(key);
printf("加算ハッシュ値は %d\n", hash);
hash = mulHash(key);
printf("乗算ハッシュ値は %d\n", hash);
hash = xorHash(key);
printf("XORハッシュ値は %d\n", hash);
hash = rotHash(key);
printf("回転ハッシュ値は %d\n", hash);
return 0;
}

View File

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

View File

@@ -0,0 +1,152 @@
/**
* File: my_heap.c
* Created Time: 2023-01-15
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 5000
/* 最大ヒープ */
typedef struct {
// size は実際の要素数を表す
int size;
// あらかじめメモリを確保した配列を使い、拡張を避ける
int data[MAX_SIZE];
} MaxHeap;
// 関数宣言
void siftDown(MaxHeap *maxHeap, int i);
void siftUp(MaxHeap *maxHeap, int i);
int parent(MaxHeap *maxHeap, int i);
/* コンストラクタ。スライスからヒープを構築する */
MaxHeap *newMaxHeap(int nums[], int size) {
// すべての要素をヒープに入れる
MaxHeap *maxHeap = (MaxHeap *)malloc(sizeof(MaxHeap));
maxHeap->size = size;
memcpy(maxHeap->data, nums, size * sizeof(int));
for (int i = parent(maxHeap, size - 1); i >= 0; i--) {
// 葉ノード以外のすべてのノードをヒープ化
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;
}
// 2 つのノードを交換
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;
}
// 2 つのノードを交換
swap(maxHeap, i, p);
// ループで下から上へヒープ化
i = p;
}
}

View File

@@ -0,0 +1,41 @@
/**
* File: my_heap_test.c
* Created Time: 2023-01-15
* Author: Reanon (793584285@qq.com)
*/
#include "my_heap.c"
/* Driver Code */
int main() {
/* ヒープを初期化 */
// 最大ヒープを初期化
int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2};
MaxHeap *maxHeap = newMaxHeap(nums, sizeof(nums) / sizeof(int));
printf("配列を入力してヒープ化した後\n");
printHeap(maxHeap->data, maxHeap->size);
/* ヒープ頂点の要素を取得 */
printf("\nヒープの先頭要素は %d\n", peek(maxHeap));
/* 要素をヒープに追加 */
push(maxHeap, 7);
printf("\n要素 7 をヒープに追加した後\n");
printHeap(maxHeap->data, maxHeap->size);
/* ヒープ頂点の要素を取り出す */
int top = pop(maxHeap);
printf("\nヒープの先頭要素 %d を取り出した後\n", top);
printHeap(maxHeap->data, maxHeap->size);
/* ヒープのサイズを取得 */
printf("\nヒープの要素数は %d\n", size(maxHeap));
/* ヒープが空かどうかを判定 */
printf("\nヒープが空かどうか %d\n", isEmpty(maxHeap));
// メモリを解放する
delMaxHeap(maxHeap);
return 0;
}

View File

@@ -0,0 +1,73 @@
/**
* File: top_k.c
* Created Time: 2023-10-26
* Author: krahets (krahets163.com)
*/
#include "my_heap.c"
/* 要素をヒープに追加 */
void pushMinHeap(MaxHeap *maxHeap, int val) {
// 要素を反転する
push(maxHeap, -val);
}
/* 要素をヒープから取り出す */
int popMinHeap(MaxHeap *maxHeap) {
// 要素を反転する
return -pop(maxHeap);
}
/* ヒープ先頭要素にアクセス */
int peekMinHeap(MaxHeap *maxHeap) {
// 要素を反転する
return -peek(maxHeap);
}
/* ヒープから要素を取り出す */
int *getMinHeap(MaxHeap *maxHeap) {
// ヒープ内のすべての要素を反転して res 配列に格納
int *res = (int *)malloc(maxHeap->size * sizeof(int));
for (int i = 0; i < maxHeap->size; i++) {
res[i] = -maxHeap->data[i];
}
return res;
}
// ヒープに基づいて配列中の最大の k 個の要素を求める関数
int *topKHeap(int *nums, int sizeNums, int k) {
// 最小ヒープを初期化する
// 注意: ヒープ内の全要素を反転し、最大ヒープで最小ヒープをシミュレートする
int *empty = (int *)malloc(0);
MaxHeap *maxHeap = newMaxHeap(empty, 0);
// 配列の先頭 k 個の要素をヒープに追加
for (int i = 0; i < k; i++) {
pushMinHeap(maxHeap, nums[i]);
}
// k+1 番目の要素から開始し、ヒープ長を k に保つ
for (int i = k; i < sizeNums; i++) {
// 現在の要素がヒープ先頭より大きければ、ヒープ先頭を取り出して現在の要素を追加する
if (nums[i] > peekMinHeap(maxHeap)) {
popMinHeap(maxHeap);
pushMinHeap(maxHeap, nums[i]);
}
}
int *res = getMinHeap(maxHeap);
// メモリを解放する
delMaxHeap(maxHeap);
return res;
}
/* Driver Code */
int main() {
int nums[] = {1, 7, 6, 3, 2};
int k = 3;
int sizeNums = sizeof(nums) / sizeof(nums[0]);
int *res = topKHeap(nums, sizeNums, k);
printf("最大の %d 個の要素は: ", k);
printArray(res, k);
free(res);
return 0;
}

View File

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

View File

@@ -0,0 +1,59 @@
/**
* File: binary_search.c
* Created Time: 2023-03-18
* Author: Guanngxu (446678850@qq.com)
*/
#include "../utils/common.h"
/* 二分探索(両閉区間) */
int binarySearch(int *nums, int len, int target) {
// 両閉区間 [0, n-1] を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素を指す
int i = 0, j = len - 1;
// ループし、探索区間が空になったら終了するi > j で空)
while (i <= j) {
int m = i + (j - i) / 2; // 中点インデックス m を計算
if (nums[m] < target) // この場合、target は区間 [m+1, j] にある
i = m + 1;
else if (nums[m] > target) // この場合、target は区間 [i, m-1] にある
j = m - 1;
else // 目標要素が見つかったらそのインデックスを返す
return m;
}
// 目標要素が見つからなければ -1 を返す
return -1;
}
/* 二分探索(左閉右開区間) */
int binarySearchLCRO(int *nums, int len, int target) {
// 左閉右開区間 [0, n) を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素+1を指す
int i = 0, j = len;
// ループし、探索区間が空になったら終了するi = j で空)
while (i < j) {
int m = i + (j - i) / 2; // 中点インデックス m を計算
if (nums[m] < target) // この場合、target は区間 [m+1, j) にある
i = m + 1;
else if (nums[m] > target) // この場合、target は区間 [i, m) にある
j = m;
else // 目標要素が見つかったらそのインデックスを返す
return m;
}
// 目標要素が見つからなければ -1 を返す
return -1;
}
/* Driver Code */
int main() {
int target = 6;
int nums[10] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
/* 二分探索(両閉区間) */
int index = binarySearch(nums, 10, target);
printf("対象要素 6 のインデックス = %d\n", index);
/* 二分探索(左閉右開区間) */
index = binarySearchLCRO(nums, 10, target);
printf("対象要素 6 のインデックス = %d\n", index);
return 0;
}

View File

@@ -0,0 +1,67 @@
/**
* File: binary_search_edge.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 二分探索で挿入位置を探す(重複要素あり) */
int binarySearchInsertion(int *nums, int numSize, int target) {
int i = 0, j = numSize - 1; // 両閉区間 [0, n-1] を初期化
while (i <= j) {
int m = i + (j - i) / 2; // 中点インデックス m を計算
if (nums[m] < target) {
i = m + 1; // target は区間 [m+1, j] にある
} else {
j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある
}
}
// 挿入位置 i を返す
return i;
}
/* 最も左の target を二分探索 */
int binarySearchLeftEdge(int *nums, int numSize, int target) {
// target の挿入位置を探すのと等価
int i = binarySearchInsertion(nums, numSize, target);
// target が見つからなければ、-1 を返す
if (i == numSize || nums[i] != target) {
return -1;
}
// target が見つかったら、インデックス i を返す
return i;
}
/* 最も右の target を二分探索 */
int binarySearchRightEdge(int *nums, int numSize, int target) {
// 最左の target + 1 を探す問題に変換する
int i = binarySearchInsertion(nums, numSize, target + 1);
// j は最も右の target を指し、i は target より大きい最初の要素を指す
int j = i - 1;
// target が見つからなければ、-1 を返す
if (j == -1 || nums[j] != target) {
return -1;
}
// target が見つかったら、インデックス j を返す
return j;
}
/* Driver Code */
int main() {
// 重複要素を含む配列
int nums[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15};
printf("\n配列 nums = ");
printArray(nums, sizeof(nums) / sizeof(nums[0]));
// 二分探索で左端と右端を探す
int targets[] = {6, 7};
for (int i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) {
int index = binarySearchLeftEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]);
printf("最も左の要素 %d のインデックスは %d\n", targets[i], index);
index = binarySearchRightEdge(nums, sizeof(nums) / sizeof(nums[0]), targets[i]);
printf("最も右の要素 %d のインデックスは %d\n", targets[i], index);
}
return 0;
}

View File

@@ -0,0 +1,68 @@
/**
* File: binary_search_insertion.c
* Created Time: 2023-09-09
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 二分探索で挿入位置を探す(重複要素なし) */
int binarySearchInsertionSimple(int *nums, int numSize, int target) {
int i = 0, j = numSize - 1; // 両閉区間 [0, n-1] を初期化
while (i <= j) {
int m = i + (j - i) / 2; // 中点インデックス m を計算
if (nums[m] < target) {
i = m + 1; // target は区間 [m+1, j] にある
} else if (nums[m] > target) {
j = m - 1; // target は区間 [i, m-1] にある
} else {
return m; // target が見つかったら、挿入位置 m を返す
}
}
// target が見つからなければ、挿入位置 i を返す
return i;
}
/* 二分探索で挿入位置を探す(重複要素あり) */
int binarySearchInsertion(int *nums, int numSize, int target) {
int i = 0, j = numSize - 1; // 両閉区間 [0, n-1] を初期化
while (i <= j) {
int m = i + (j - i) / 2; // 中点インデックス m を計算
if (nums[m] < target) {
i = m + 1; // target は区間 [m+1, j] にある
} else if (nums[m] > target) {
j = m - 1; // target は区間 [i, m-1] にある
} else {
j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある
}
}
// 挿入位置 i を返す
return i;
}
/* Driver Code */
int main() {
// 重複要素のない配列
int nums1[] = {1, 3, 6, 8, 12, 15, 23, 26, 31, 35};
printf("\n配列 nums = ");
printArray(nums1, sizeof(nums1) / sizeof(nums1[0]));
// 二分探索で挿入位置を探す
int targets1[] = {6, 9};
for (int i = 0; i < sizeof(targets1) / sizeof(targets1[0]); i++) {
int index = binarySearchInsertionSimple(nums1, sizeof(nums1) / sizeof(nums1[0]), targets1[i]);
printf("要素 %d の挿入位置のインデックスは %d\n", targets1[i], index);
}
// 重複要素を含む配列
int nums2[] = {1, 3, 6, 6, 6, 6, 6, 10, 12, 15};
printf("\n配列 nums = ");
printArray(nums2, sizeof(nums2) / sizeof(nums2[0]));
// 二分探索で挿入位置を探す
int targets2[] = {2, 6, 20};
for (int i = 0; i < sizeof(targets2) / sizeof(int); i++) {
int index = binarySearchInsertion(nums2, sizeof(nums2) / sizeof(nums2[0]), targets2[i]);
printf("要素 %d の挿入位置のインデックスは %d\n", targets2[i], index);
}
return 0;
}

View File

@@ -0,0 +1,86 @@
/**
* File: two_sum.c
* Created Time: 2023-01-19
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* 方法 1総当たり列挙 */
int *twoSumBruteForce(int *nums, int numsSize, int target, int *returnSize) {
for (int i = 0; i < numsSize; ++i) {
for (int j = i + 1; j < numsSize; ++j) {
if (nums[i] + nums[j] == target) {
int *res = malloc(sizeof(int) * 2);
res[0] = i, res[1] = j;
*returnSize = 2;
return res;
}
}
}
*returnSize = 0;
return NULL;
}
/* ハッシュテーブル */
typedef struct {
int key;
int val;
UT_hash_handle hh; // uthash.h を用いて実装
} HashTable;
/* ハッシュテーブルを検索する */
HashTable *find(HashTable *h, int key) {
HashTable *tmp;
HASH_FIND_INT(h, &key, tmp);
return tmp;
}
/* ハッシュテーブルに要素を挿入する */
void insert(HashTable **h, int key, int val) {
HashTable *t = find(*h, key);
if (t == NULL) {
HashTable *tmp = malloc(sizeof(HashTable));
tmp->key = key, tmp->val = val;
HASH_ADD_INT(*h, key, tmp);
} else {
t->val = val;
}
}
/* 方法 2補助ハッシュテーブル */
int *twoSumHashTable(int *nums, int numsSize, int target, int *returnSize) {
HashTable *hashtable = NULL;
for (int i = 0; i < numsSize; i++) {
HashTable *t = find(hashtable, target - nums[i]);
if (t != NULL) {
int *res = malloc(sizeof(int) * 2);
res[0] = t->val, res[1] = i;
*returnSize = 2;
return res;
}
insert(&hashtable, nums[i], i);
}
*returnSize = 0;
return NULL;
}
/* Driver Code */
int main() {
// ======= Test Case =======
int nums[] = {2, 7, 11, 15};
int target = 13;
// ====== Driver Code ======
int returnSize;
int *res = twoSumBruteForce(nums, sizeof(nums) / sizeof(int), target, &returnSize);
// 方法 1
printf("方法1 res = ");
printArray(res, returnSize);
// 方法 2
res = twoSumHashTable(nums, sizeof(nums) / sizeof(int), target, &returnSize);
printf("方法2 res = ");
printArray(res, returnSize);
return 0;
}

View File

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

View File

@@ -0,0 +1,61 @@
/**
* File: bubble_sort.c
* Created Time: 2022-12-26
* Author: Listening (https://github.com/L-Super)
*/
#include "../utils/common.h"
/* バブルソート */
void bubbleSort(int nums[], int size) {
// 外側のループ:未ソート区間は [0, i]
for (int i = size - 1; i > 0; i--) {
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
/* バブルソート(フラグ最適化) */
void bubbleSortWithFlag(int nums[], int size) {
// 外側のループ:未ソート区間は [0, i]
for (int i = size - 1; i > 0; i--) {
bool flag = false;
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
flag = true;
}
}
if (!flag)
break;
}
}
/* Driver Code */
int main() {
int nums[6] = {4, 1, 3, 1, 5, 2};
printf("バブルソート後: ");
bubbleSort(nums, 6);
for (int i = 0; i < 6; i++) {
printf("%d ", nums[i]);
}
int nums1[6] = {4, 1, 3, 1, 5, 2};
printf("\n最適化版バブルソート後: ");
bubbleSortWithFlag(nums1, 6);
for (int i = 0; i < 6; i++) {
printf("%d ", nums1[i]);
}
printf("\n");
return 0;
}

View File

@@ -0,0 +1,57 @@
/**
* File: bucket_sort.c
* Created Time: 2023-05-30
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
#define SIZE 10
/* `qsort` 用の比較関数 */
int compare(const void *a, const void *b) {
float fa = *(const float *)a;
float fb = *(const float *)b;
return (fa > fb) - (fa < fb);
}
/* バケットソート */
void bucketSort(float nums[], int n) {
int k = n / 2; // k = n/2 個のバケットを初期化する
int *sizes = malloc(k * sizeof(int)); // 各バケットのサイズを記録する
float **buckets = malloc(k * sizeof(float *)); // 動的配列の配列(バケット)
// 各バケットに十分な容量を事前確保する
for (int i = 0; i < k; ++i) {
buckets[i] = (float *)malloc(n * sizeof(float));
sizes[i] = 0;
}
// 1. 配列要素を各バケットに振り分ける
for (int i = 0; i < n; ++i) {
int idx = (int)(nums[i] * k);
buckets[idx][sizes[idx]++] = nums[i];
}
// 2. 各バケットをソートする
for (int i = 0; i < k; ++i) {
qsort(buckets[i], sizes[i], sizeof(float), compare);
}
// 3. ソート済みのバケットを結合する
int idx = 0;
for (int i = 0; i < k; ++i) {
for (int j = 0; j < sizes[i]; ++j) {
nums[idx++] = buckets[i][j];
}
// メモリを解放する
free(buckets[i]);
}
}
/* Driver Code */
int main() {
// 入力データは範囲 [0, 1) の浮動小数点数とする
float nums[SIZE] = {0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f};
bucketSort(nums, SIZE);
printf("バケットソート完了後 nums = ");
printArrayFloat(nums, SIZE);
return 0;
}

View File

@@ -0,0 +1,87 @@
/**
* File: counting_sort.c
* Created Time: 2023-03-20
* Author: Reanon (793584285@qq.com), Guanngxu (446678850@qq.com)
*/
#include "../utils/common.h"
/* 計数ソート */
// 簡易実装のため、オブジェクトのソートには使えない
void countingSortNaive(int nums[], int size) {
// 1. 配列の最大要素 m を求める
int m = 0;
for (int i = 0; i < size; i++) {
if (nums[i] > m) {
m = nums[i];
}
}
// 2. 各数値の出現回数を数える
// counter[num] は num の出現回数を表す
int *counter = calloc(m + 1, sizeof(int));
for (int i = 0; i < size; i++) {
counter[nums[i]]++;
}
// 3. counter を走査し、各要素を元の配列 nums に書き戻す
int i = 0;
for (int num = 0; num < m + 1; num++) {
for (int j = 0; j < counter[num]; j++, i++) {
nums[i] = num;
}
}
// 4. メモリを解放する
free(counter);
}
/* 計数ソート */
// 完全な実装で、オブジェクトをソートでき、かつ安定ソートである
void countingSort(int nums[], int size) {
// 1. 配列の最大要素 m を求める
int m = 0;
for (int i = 0; i < size; i++) {
if (nums[i] > m) {
m = nums[i];
}
}
// 2. 各数値の出現回数を数える
// counter[num] は num の出現回数を表す
int *counter = calloc(m, sizeof(int));
for (int i = 0; i < size; i++) {
counter[nums[i]]++;
}
// 3. counter の累積和を求めて、「出現回数」を「末尾インデックス」に変換する
// つまり counter[num]-1 は、num が res に最後に現れるインデックス
for (int i = 0; i < m; i++) {
counter[i + 1] += counter[i];
}
// 4. nums を逆順に走査し、各要素を結果配列 res に格納する
// 結果を記録するための配列 res を初期化
int *res = malloc(sizeof(int) * size);
for (int i = size - 1; i >= 0; i--) {
int num = nums[i];
res[counter[num] - 1] = num; // num を対応するインデックスに配置
counter[num]--; // 累積和を 1 減らして、次に num を配置するインデックスを得る
}
// 結果配列 res で元の配列 nums を上書きする
memcpy(nums, res, size * sizeof(int));
// 5. メモリを解放する
free(res);
free(counter);
}
/* Driver Code */
int main() {
int nums[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4};
int size = sizeof(nums) / sizeof(int);
countingSortNaive(nums, size);
printf("計数ソート(オブジェクトはソート不可)完了後 nums = ");
printArray(nums, size);
int nums1[] = {1, 0, 1, 2, 0, 4, 0, 2, 2, 4};
int size1 = sizeof(nums1) / sizeof(int);
countingSort(nums1, size1);
printf("計数ソート完了後 nums1 = ");
printArray(nums1, size1);
return 0;
}

View File

@@ -0,0 +1,60 @@
/**
* File: heap_sort.c
* Created Time: 2023-05-30
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* ヒープの長さは n。ード i から下方向にヒープ化 */
void siftDown(int nums[], int n, int i) {
while (1) {
// ノード i, l, r のうち値が最大のノードを ma とする
int l = 2 * i + 1;
int r = 2 * i + 2;
int ma = i;
if (l < n && nums[l] > nums[ma])
ma = l;
if (r < n && nums[r] > nums[ma])
ma = r;
// ノード i が最大、またはインデックス l, r が範囲外なら、ヒープ化は不要なので抜ける
if (ma == i) {
break;
}
// 2 つのノードを交換
int temp = nums[i];
nums[i] = nums[ma];
nums[ma] = temp;
// ループで上から下へヒープ化
i = ma;
}
}
/* ヒープソート */
void heapSort(int nums[], int n) {
// ヒープ構築:葉ノード以外のすべてのノードをヒープ化する
for (int i = n / 2 - 1; i >= 0; --i) {
siftDown(nums, n, i);
}
// ヒープから最大要素を取り出し、n-1 回繰り返す
for (int i = n - 1; i > 0; --i) {
// 根ノードと最も右の葉ノードを交換(先頭要素と末尾要素を交換)
int tmp = nums[0];
nums[0] = nums[i];
nums[i] = tmp;
// 根ノードを起点に、上から下へヒープ化
siftDown(nums, i, 0);
}
}
/* Driver Code */
int main() {
int nums[] = {4, 1, 3, 1, 5, 2};
int n = sizeof(nums) / sizeof(nums[0]);
heapSort(nums, n);
printf("ヒープソート完了後 nums = ");
printArray(nums, n);
return 0;
}

View File

@@ -0,0 +1,36 @@
/**
* File: insertion_sort.c
* Created Time: 2022-12-29
* Author: Listening (https://github.com/L-Super)
*/
#include "../utils/common.h"
/* 挿入ソート */
void insertionSort(int nums[], int size) {
// 外側ループ:整列済み区間は [0, i-1]
for (int i = 1; i < size; i++) {
int base = nums[i], j = i - 1;
// 内側ループ: base をソート済み区間 [0, i-1] の正しい位置に挿入する
while (j >= 0 && nums[j] > base) {
// nums[j] を 1 つ右へ移動する
nums[j + 1] = nums[j];
j--;
}
// base を正しい位置に配置する
nums[j + 1] = base;
}
}
/* Driver Code */
int main() {
int nums[] = {4, 1, 3, 1, 5, 2};
insertionSort(nums, 6);
printf("挿入ソート完了後 nums = ");
for (int i = 0; i < 6; i++) {
printf("%d ", nums[i]);
}
printf("\n");
return 0;
}

View File

@@ -0,0 +1,63 @@
/**
* File: merge_sort.c
* Created Time: 2022-03-21
* Author: Guanngxu (446678850@qq.com)
*/
#include "../utils/common.h"
/* 左部分配列と右部分配列をマージ */
void merge(int *nums, int left, int mid, int right) {
// 左部分配列の区間は [left, mid]、右部分配列の区間は [mid+1, right]
// マージ結果を格納する一時配列 tmp を作成
int tmpSize = right - left + 1;
int *tmp = (int *)malloc(tmpSize * sizeof(int));
// 左右の部分配列の開始インデックスを初期化する
int i = left, j = mid + 1, k = 0;
// 左右の部分配列にまだ要素がある間は比較し、小さいほうを一時配列にコピーする
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) {
tmp[k++] = nums[i++];
} else {
tmp[k++] = nums[j++];
}
}
// 左右の部分配列の残り要素を一時配列にコピーする
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
// 一時配列 tmp の要素を元の配列 nums の対応区間にコピーする
for (k = 0; k < tmpSize; ++k) {
nums[left + k] = tmp[k];
}
// メモリを解放する
free(tmp);
}
/* マージソート */
void mergeSort(int *nums, int left, int right) {
// 終了条件
if (left >= right)
return; // 部分配列の長さが 1 になったら再帰を終了
// 分割フェーズ
int mid = left + (right - left) / 2; // 中点を計算
mergeSort(nums, left, mid); // 左部分配列を再帰処理
mergeSort(nums, mid + 1, right); // 右部分配列を再帰処理
// マージフェーズ
merge(nums, left, mid, right);
}
/* Driver Code */
int main() {
/* マージソート */
int nums[] = {7, 3, 2, 6, 0, 1, 5, 4};
int size = sizeof(nums) / sizeof(int);
mergeSort(nums, 0, size - 1);
printf("マージソート完了後 nums = ");
printArray(nums, size);
return 0;
}

View File

@@ -0,0 +1,137 @@
/**
* File: quick_sort.c
* Created Time: 2023-01-18
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* 要素の交換 */
void swap(int nums[], int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
/* 番兵分割 */
int partition(int nums[], int left, int right) {
// nums[left] を基準値とする
int i = left, j = right;
while (i < j) {
while (i < j && nums[j] >= nums[left]) {
j--; // 右から左へ基準値未満の最初の要素を探す
}
while (i < j && nums[i] <= nums[left]) {
i++; // 左から右へ基準値より大きい最初の要素を探す
}
// この 2 つの要素を交換
swap(nums, i, j);
}
// 基準値を 2 つの部分配列の境界へ交換する
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);
}
// 以下は中央値最適化版のクイックソート
/* 3つの候補要素の中央値を選ぶ */
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;
}
/* 番兵による分割処理3 点中央値) */
int partitionMedian(int nums[], int left, int right) {
// 3つの候補要素の中央値を選ぶ
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); // この 2 つの要素を交換
}
swap(nums, i, left); // 基準値を 2 つの部分配列の境界へ交換する
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);
// 2 つの部分配列のうち短いほうにクイックソートを適用する
if (pivot - left < right - pivot) {
// 左部分配列を再帰的にソート
quickSortTailCall(nums, left, pivot - 1);
// 未ソート区間の残りは [pivot + 1, right]
left = pivot + 1;
} else {
// 右部分配列を再帰的にソート
quickSortTailCall(nums, pivot + 1, right);
// 未ソート区間の残りは [left, pivot - 1]
right = pivot - 1;
}
}
}
/* Driver Code */
int main() {
/* クイックソート */
int nums[] = {2, 4, 1, 0, 3, 5};
int size = sizeof(nums) / sizeof(int);
quickSort(nums, 0, size - 1);
printf("クイックソート完了後 nums = ");
printArray(nums, size);
/* クイックソート(中央値の基準値で最適化) */
int nums1[] = {2, 4, 1, 0, 3, 5};
quickSortMedian(nums1, 0, size - 1);
printf("クイックソート(中央値ピボット最適化)完了後 nums = ");
printArray(nums1, size);
/* クイックソート(再帰深度最適化) */
int nums2[] = {2, 4, 1, 0, 3, 5};
quickSortTailCall(nums2, 0, size - 1);
printf("クイックソート(再帰深度最適化)完了後 nums = ");
printArray(nums1, size);
return 0;
}

View File

@@ -0,0 +1,75 @@
/**
* File: radix_sort.c
* Created Time: 2023-01-18
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* 要素 num の下から k 桁目を取得exp = 10^(k-1) */
int digit(int num, int exp) {
// ここで高コストな累乗計算を繰り返さないよう、k ではなく exp を渡す
return (num / exp) % 10;
}
/* 計数ソートnums の k 桁目でソート) */
void countingSortDigit(int nums[], int size, int exp) {
// 10 進数の各桁は 0~9 の範囲なので、長さ 10 のバケット配列が必要
int *counter = (int *)malloc((sizeof(int) * 10));
memset(counter, 0, sizeof(int) * 10); // 後続のメモリ解放に備えて 0 で初期化する
// 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];
}
// メモリを解放する
free(res);
free(counter);
}
/* 基数ソート */
void radixSort(int nums[], int size) {
// 最大桁数の判定用に配列の最大要素を取得
int max = INT32_MIN;
for (int i = 0; i < size; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
// 下位桁から上位桁の順に走査する
for (int exp = 1; max >= exp; exp *= 10)
// 配列要素の k 桁目に対して計数ソートを行う
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// つまり exp = 10^(k-1)
countingSortDigit(nums, size, exp);
}
/* Driver Code */
int main() {
// 基数ソート
int nums[] = {10546151, 35663510, 42865989, 34862445, 81883077,
88906420, 72429244, 30524779, 82060337, 63832996};
int size = sizeof(nums) / sizeof(int);
radixSort(nums, size);
printf("基数ソート完了後 nums = ");
printArray(nums, size);
}

View File

@@ -0,0 +1,37 @@
/**
* File: selection_sort.c
* Created Time: 2023-05-31
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 選択ソート */
void selectionSort(int nums[], int n) {
// 外側ループ:未整列区間は [i, n-1]
for (int i = 0; i < n - 1; i++) {
// 内側のループ:未ソート区間の最小要素を見つける
int k = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j; // 最小要素のインデックスを記録
}
// その最小要素を未整列区間の先頭要素と交換する
int temp = nums[i];
nums[i] = nums[k];
nums[k] = temp;
}
}
/* Driver Code */
int main() {
int nums[] = {4, 1, 3, 1, 5, 2};
int n = sizeof(nums) / sizeof(nums[0]);
selectionSort(nums, n);
printf("選択ソート完了後 nums = ");
printArray(nums, n);
return 0;
}

View File

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

View File

@@ -0,0 +1,172 @@
/**
* File: array_deque.c
* Created Time: 2023-03-13
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 循環配列ベースの両端キュー */
typedef struct {
int *nums; // キュー要素を格納する配列
int front; // 先頭ポインタ。先頭要素を指す
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;
}
// 先頭ポインタを左に 1 つ移動する
// 剰余演算により 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);
// 先頭ポインタを 1 つ後ろへ進める
deque->front = dequeIndex(deque, deque->front + 1);
deque->queSize--;
return num;
}
/* キュー末尾からデキュー */
int popLast(ArrayDeque *deque) {
int num = peekLast(deque);
deque->queSize--;
return num;
}
/* 出力用の配列を返す */
int *toArray(ArrayDeque *deque, int *queSize) {
*queSize = deque->queSize;
int *res = (int *)calloc(deque->queSize, sizeof(int));
int j = deque->front;
for (int i = 0; i < deque->queSize; i++) {
res[i] = deque->nums[j % deque->queCapacity];
j++;
}
return res;
}
/* Driver Code */
int main() {
/* キューを初期化 */
int capacity = 10;
int queSize;
ArrayDeque *deque = newArrayDeque(capacity);
pushLast(deque, 3);
pushLast(deque, 2);
pushLast(deque, 5);
printf("両端キュー deque = ");
printArray(toArray(deque, &queSize), queSize);
/* 要素にアクセス */
int peekFirstNum = peekFirst(deque);
printf("先頭要素 peekFirst = %d\r\n", peekFirstNum);
int peekLastNum = peekLast(deque);
printf("末尾要素 peekLast = %d\r\n", peekLastNum);
/* 要素をエンキュー */
pushLast(deque, 4);
printf("要素 4 を末尾に追加後 deque = ");
printArray(toArray(deque, &queSize), queSize);
pushFirst(deque, 1);
printf("要素 1 を先頭に追加後 deque = ");
printArray(toArray(deque, &queSize), queSize);
/* 要素をデキュー */
int popLastNum = popLast(deque);
printf("末尾から取り出した要素 = %d ,末尾から取り出した後 deque= ", popLastNum);
printArray(toArray(deque, &queSize), queSize);
int popFirstNum = popFirst(deque);
printf("先頭から取り出した要素 = %d ,先頭から取り出した後 deque= ", popFirstNum);
printArray(toArray(deque, &queSize), queSize);
/* キューの長さを取得 */
int dequeSize = size(deque);
printf("両端キューの長さ size = %d\r\n", dequeSize);
/* キューが空かどうかを判定 */
bool isEmpty = empty(deque);
printf("キューが空かどうか = %s\r\n", isEmpty ? "true" : "false");
// メモリを解放する
delArrayDeque(deque);
return 0;
}

View File

@@ -0,0 +1,134 @@
/**
* File: array_queue.c
* Created Time: 2023-01-28
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 循環配列ベースのキュー */
typedef struct {
int *nums; // キュー要素を格納する配列
int front; // 先頭ポインタ。先頭要素を指す
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);
// 先頭ポインタを1つ後ろへ進め、末尾を越えたら配列先頭に戻す
queue->front = (queue->front + 1) % queue->queCapacity;
queue->queSize--;
return num;
}
/* 出力用の配列を返す */
int *toArray(ArrayQueue *queue, int *queSize) {
*queSize = queue->queSize;
int *res = (int *)calloc(queue->queSize, sizeof(int));
int j = queue->front;
for (int i = 0; i < queue->queSize; i++) {
res[i] = queue->nums[j % queue->queCapacity];
j++;
}
return res;
}
/* Driver Code */
int main() {
/* キューを初期化 */
int capacity = 10;
int queSize;
ArrayQueue *queue = newArrayQueue(capacity);
/* 要素をエンキュー */
push(queue, 1);
push(queue, 3);
push(queue, 2);
push(queue, 5);
push(queue, 4);
printf("キュー queue = ");
printArray(toArray(queue, &queSize), queSize);
/* キュー先頭の要素にアクセス */
int peekNum = peek(queue);
printf("先頭要素 peek = %d\r\n", peekNum);
/* 要素をデキュー */
peekNum = pop(queue);
printf("デキューした要素 pop = %d ,デキュー後 queue = ", peekNum);
printArray(toArray(queue, &queSize), queSize);
/* キューの長さを取得 */
int queueSize = size(queue);
printf("キューの長さ size = %d\r\n", queueSize);
/* キューが空かどうかを判定 */
bool isEmpty = empty(queue);
printf("キューが空かどうか = %s\r\n", isEmpty ? "true" : "false");
/* 循環配列をテストする */
for (int i = 0; i < 10; i++) {
push(queue, i);
pop(queue);
printf("第 %d 回のエンキュー + デキュー後 queue = ", i);
printArray(toArray(queue, &queSize), queSize);
}
// メモリを解放する
delArrayQueue(queue);
return 0;
}

View File

@@ -0,0 +1,103 @@
/**
* File: array_stack.c
* Created Time: 2023-01-12
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 5000
/* 配列ベースのスタック */
typedef struct {
int *data;
int size;
} ArrayStack;
/* コンストラクタ */
ArrayStack *newArrayStack() {
ArrayStack *stack = malloc(sizeof(ArrayStack));
// 大きめの容量で初期化し、拡張を避ける
stack->data = malloc(sizeof(int) * MAX_SIZE);
stack->size = 0;
return stack;
}
/* デストラクタ */
void delArrayStack(ArrayStack *stack) {
free(stack->data);
free(stack);
}
/* スタックの長さを取得 */
int size(ArrayStack *stack) {
return stack->size;
}
/* スタックが空かどうかを判定 */
bool isEmpty(ArrayStack *stack) {
return stack->size == 0;
}
/* プッシュ */
void push(ArrayStack *stack, int num) {
if (stack->size == MAX_SIZE) {
printf("スタックは満杯です\n");
return;
}
stack->data[stack->size] = num;
stack->size++;
}
/* スタックトップの要素にアクセス */
int peek(ArrayStack *stack) {
if (stack->size == 0) {
printf("スタックは空です\n");
return INT_MAX;
}
return stack->data[stack->size - 1];
}
/* ポップ */
int pop(ArrayStack *stack) {
int val = peek(stack);
stack->size--;
return val;
}
/* Driver Code */
int main() {
/* スタックを初期化 */
ArrayStack *stack = newArrayStack();
/* 要素をプッシュ */
push(stack, 1);
push(stack, 3);
push(stack, 2);
push(stack, 5);
push(stack, 4);
printf("スタック stack = ");
printArray(stack->data, stack->size);
/* スタックトップの要素にアクセス */
int val = peek(stack);
printf("先頭要素 top = %d\n", val);
/* 要素をポップ */
val = pop(stack);
printf("ポップした要素 pop = %d ,ポップ後 stack = ", val);
printArray(stack->data, stack->size);
/* スタックの長さを取得 */
int size = stack->size;
printf("スタックの長さ size = %d\n", size);
/* 空かどうかを判定 */
bool empty = isEmpty(stack);
printf("スタックが空かどうか = %s\n", empty ? "true" : "false");
// メモリを解放する
delArrayStack(stack);
return 0;
}

View File

@@ -0,0 +1,212 @@
/**
* File: linkedlist_deque.c
* Created Time: 2023-03-13
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 双方向連結リストノード */
typedef struct DoublyListNode {
int val; // ノード値
struct DoublyListNode *next; // 後続ノード
struct DoublyListNode *prev; // 前駆ノード
} DoublyListNode;
/* コンストラクタ */
DoublyListNode *newDoublyListNode(int num) {
DoublyListNode *new = (DoublyListNode *)malloc(sizeof(DoublyListNode));
new->val = num;
new->next = NULL;
new->prev = NULL;
return new;
}
/* デストラクタ */
void delDoublyListNode(DoublyListNode *node) {
free(node);
}
/* 双方向連結リストベースの両端キュー */
typedef struct {
DoublyListNode *front, *rear; // 先頭ノード front、末尾ード rear
int queSize; // 両端キューの長さ
} LinkedListDeque;
/* コンストラクタ */
LinkedListDeque *newLinkedListDeque() {
LinkedListDeque *deque = (LinkedListDeque *)malloc(sizeof(LinkedListDeque));
deque->front = NULL;
deque->rear = NULL;
deque->queSize = 0;
return deque;
}
/* デストラクタ */
void delLinkedListdeque(LinkedListDeque *deque) {
// すべてのノードを解放
for (int i = 0; i < deque->queSize && deque->front != NULL; i++) {
DoublyListNode *tmp = deque->front;
deque->front = deque->front->next;
free(tmp);
}
// deque 構造体を解放する
free(deque);
}
/* キューの長さを取得 */
int size(LinkedListDeque *deque) {
return deque->queSize;
}
/* キューが空かどうかを判定 */
bool empty(LinkedListDeque *deque) {
return (size(deque) == 0);
}
/* エンキュー */
void push(LinkedListDeque *deque, int num, bool isFront) {
DoublyListNode *node = newDoublyListNode(num);
// 連結リストが空なら、`front` と `rear` の両方を `node` に向ける
if (empty(deque)) {
deque->front = deque->rear = node;
}
// 先頭へのエンキュー操作
else if (isFront) {
// node を連結リストの先頭に追加
deque->front->prev = node;
node->next = deque->front;
deque->front = node; // 先頭ノードを更新する
}
// 末尾へのエンキュー操作
else {
// node を連結リストの末尾に追加
deque->rear->next = node;
node->prev = deque->rear;
deque->rear = node;
}
deque->queSize++; // キューの長さを更新
}
/* キュー先頭にエンキュー */
void pushFirst(LinkedListDeque *deque, int num) {
push(deque, num, true);
}
/* キュー末尾にエンキュー */
void pushLast(LinkedListDeque *deque, int num) {
push(deque, num, false);
}
/* キュー先頭の要素にアクセス */
int peekFirst(LinkedListDeque *deque) {
assert(size(deque) && deque->front);
return deque->front->val;
}
/* キュー末尾の要素にアクセス */
int peekLast(LinkedListDeque *deque) {
assert(size(deque) && deque->rear);
return deque->rear->val;
}
/* デキュー */
int pop(LinkedListDeque *deque, bool isFront) {
if (empty(deque))
return -1;
int val;
// キュー先頭からの取り出し
if (isFront) {
val = peekFirst(deque); // 先頭ノードの値を一時保存
DoublyListNode *fNext = deque->front->next;
if (fNext) {
fNext->prev = NULL;
deque->front->next = NULL;
}
delDoublyListNode(deque->front);
deque->front = fNext; // 先頭ノードを更新する
}
// キュー末尾からの取り出し
else {
val = peekLast(deque); // 末尾ノードの値を一時保存
DoublyListNode *rPrev = deque->rear->prev;
if (rPrev) {
rPrev->next = NULL;
deque->rear->prev = NULL;
}
delDoublyListNode(deque->rear);
deque->rear = rPrev; // 末尾ノードを更新する
}
deque->queSize--; // キューの長さを更新
return val;
}
/* キュー先頭からデキュー */
int popFirst(LinkedListDeque *deque) {
return pop(deque, true);
}
/* キュー末尾からデキュー */
int popLast(LinkedListDeque *deque) {
return pop(deque, false);
}
/* キューを出力する */
void printLinkedListDeque(LinkedListDeque *deque) {
int *arr = malloc(sizeof(int) * deque->queSize);
// 連結リスト内のデータを配列にコピー
int i;
DoublyListNode *node;
for (i = 0, node = deque->front; i < deque->queSize; i++) {
arr[i] = node->val;
node = node->next;
}
printArray(arr, deque->queSize);
free(arr);
}
/* Driver Code */
int main() {
/* 両端キューを初期化 */
LinkedListDeque *deque = newLinkedListDeque();
pushLast(deque, 3);
pushLast(deque, 2);
pushLast(deque, 5);
printf("両端キュー deque = ");
printLinkedListDeque(deque);
/* 要素にアクセス */
int peekFirstNum = peekFirst(deque);
printf("先頭要素 peekFirst = %d\r\n", peekFirstNum);
int peekLastNum = peekLast(deque);
printf("先頭要素 peekLast = %d\r\n", peekLastNum);
/* 要素をエンキュー */
pushLast(deque, 4);
printf("要素 4 を末尾に追加した後 deque =");
printLinkedListDeque(deque);
pushFirst(deque, 1);
printf("要素 1 を先頭に追加した後 deque =");
printLinkedListDeque(deque);
/* 要素をデキュー */
int popLastNum = popLast(deque);
printf("末尾から取り出した要素 popLast = %d ,末尾から取り出した後 deque = ", popLastNum);
printLinkedListDeque(deque);
int popFirstNum = popFirst(deque);
printf("先頭から取り出した要素 popFirst = %d ,先頭から取り出した後 deque = ", popFirstNum);
printLinkedListDeque(deque);
/* キューの長さを取得 */
int dequeSize = size(deque);
printf("両端キューの長さ size = %d\r\n", dequeSize);
/* キューが空かどうかを判定 */
bool isEmpty = empty(deque);
printf("両端キューが空かどうか = %s\r\n", isEmpty ? "true" : "false");
// メモリを解放する
delLinkedListdeque(deque);
return 0;
}

View File

@@ -0,0 +1,128 @@
/**
* File: linkedlist_queue.c
* Created Time: 2023-03-13
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 連結リストベースのキュー */
typedef struct {
ListNode *front, *rear;
int queSize;
} LinkedListQueue;
/* コンストラクタ */
LinkedListQueue *newLinkedListQueue() {
LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue));
queue->front = NULL;
queue->rear = NULL;
queue->queSize = 0;
return queue;
}
/* デストラクタ */
void delLinkedListQueue(LinkedListQueue *queue) {
// すべてのノードを解放
while (queue->front != NULL) {
ListNode *tmp = queue->front;
queue->front = queue->front->next;
free(tmp);
}
// queue 構造体を解放する
free(queue);
}
/* キューの長さを取得 */
int size(LinkedListQueue *queue) {
return queue->queSize;
}
/* キューが空かどうかを判定 */
bool empty(LinkedListQueue *queue) {
return (size(queue) == 0);
}
/* エンキュー */
void push(LinkedListQueue *queue, int num) {
// 末尾ノードに node を追加
ListNode *node = newListNode(num);
// キューが空なら、先頭・末尾ノードをともにそのノードに設定
if (queue->front == NULL) {
queue->front = node;
queue->rear = node;
}
// キューが空でなければ、そのノードを末尾ノードの後ろに追加
else {
queue->rear->next = node;
queue->rear = node;
}
queue->queSize++;
}
/* キュー先頭の要素にアクセス */
int peek(LinkedListQueue *queue) {
assert(size(queue) && queue->front);
return queue->front->val;
}
/* デキュー */
int pop(LinkedListQueue *queue) {
int num = peek(queue);
ListNode *tmp = queue->front;
queue->front = queue->front->next;
free(tmp);
queue->queSize--;
return num;
}
/* キューを出力する */
void printLinkedListQueue(LinkedListQueue *queue) {
int *arr = malloc(sizeof(int) * queue->queSize);
// 連結リスト内のデータを配列にコピー
int i;
ListNode *node;
for (i = 0, node = queue->front; i < queue->queSize; i++) {
arr[i] = node->val;
node = node->next;
}
printArray(arr, queue->queSize);
free(arr);
}
/* Driver Code */
int main() {
/* キューを初期化 */
LinkedListQueue *queue = newLinkedListQueue();
/* 要素をエンキュー */
push(queue, 1);
push(queue, 3);
push(queue, 2);
push(queue, 5);
push(queue, 4);
printf("キュー queue = ");
printLinkedListQueue(queue);
/* キュー先頭の要素にアクセス */
int peekNum = peek(queue);
printf("先頭要素 peek = %d\r\n", peekNum);
/* 要素をデキュー */
peekNum = pop(queue);
printf("デキューした要素 pop = %d ,デキュー後 queue = ", peekNum);
printLinkedListQueue(queue);
/* キューの長さを取得 */
int queueSize = size(queue);
printf("キューの長さ size = %d\r\n", queueSize);
/* キューが空かどうかを判定 */
bool isEmpty = empty(queue);
printf("キューが空かどうか = %s\r\n", isEmpty ? "true" : "false");
// メモリを解放する
delLinkedListQueue(queue);
return 0;
}

View File

@@ -0,0 +1,107 @@
/**
* File: linkedlist_stack.c
* Created Time: 2023-01-12
* Author: Zero (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 連結リストベースのスタック */
typedef struct {
ListNode *top; // 先頭ノードをスタックトップとする
int size; // スタックの長さ
} LinkedListStack;
/* コンストラクタ */
LinkedListStack *newLinkedListStack() {
LinkedListStack *s = malloc(sizeof(LinkedListStack));
s->top = NULL;
s->size = 0;
return s;
}
/* デストラクタ */
void delLinkedListStack(LinkedListStack *s) {
while (s->top) {
ListNode *n = s->top->next;
free(s->top);
s->top = n;
}
free(s);
}
/* スタックの長さを取得 */
int size(LinkedListStack *s) {
return s->size;
}
/* スタックが空かどうかを判定 */
bool isEmpty(LinkedListStack *s) {
return size(s) == 0;
}
/* プッシュ */
void push(LinkedListStack *s, int num) {
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
node->next = s->top; // 新しく追加したノードのポインタフィールドを更新
node->val = num; // 新しく追加したノードのデータフィールドを更新
s->top = node; // スタックトップを更新
s->size++; // スタックサイズを更新
}
/* スタックトップの要素にアクセス */
int peek(LinkedListStack *s) {
if (s->size == 0) {
printf("スタックは空です\n");
return INT_MAX;
}
return s->top->val;
}
/* ポップ */
int pop(LinkedListStack *s) {
int val = peek(s);
ListNode *tmp = s->top;
s->top = s->top->next;
// メモリを解放する
free(tmp);
s->size--;
return val;
}
/* Driver Code */
int main() {
/* スタックを初期化 */
LinkedListStack *stack = newLinkedListStack();
/* 要素をプッシュ */
push(stack, 1);
push(stack, 3);
push(stack, 2);
push(stack, 5);
push(stack, 4);
printf("スタック stack = ");
printLinkedList(stack->top);
/* スタックトップの要素にアクセス */
int val = peek(stack);
printf("スタックトップ要素 top = %d\r\n", val);
/* 要素をポップ */
val = pop(stack);
printf("ポップした要素 pop = %d, ポップ後 stack = ", val);
printLinkedList(stack->top);
/* スタックの長さを取得 */
printf("スタックの長さ size = %d\n", size(stack));
/* 空かどうかを判定 */
bool empty = isEmpty(stack);
printf("スタックが空かどうか = %s\n", empty ? "true" : "false");
// メモリを解放する
delLinkedListStack(stack);
return 0;
}

View File

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

View File

@@ -0,0 +1,166 @@
/**
* File: array_binary_tree.c
* Created Time: 2023-07-29
* Author: Gonglja (glj0@outlook.com)
*/
#include "../utils/common.h"
/* 配列表現による二分木の構造体 */
typedef struct {
int *tree;
int size;
} ArrayBinaryTree;
/* コンストラクタ */
ArrayBinaryTree *newArrayBinaryTree(int *arr, int arrSize) {
ArrayBinaryTree *abt = (ArrayBinaryTree *)malloc(sizeof(ArrayBinaryTree));
abt->tree = malloc(sizeof(int) * arrSize);
memcpy(abt->tree, arr, sizeof(int) * arrSize);
abt->size = arrSize;
return abt;
}
/* デストラクタ */
void delArrayBinaryTree(ArrayBinaryTree *abt) {
free(abt->tree);
free(abt);
}
/* リスト容量 */
int size(ArrayBinaryTree *abt) {
return abt->size;
}
/* インデックス i のノードの値を取得 */
int val(ArrayBinaryTree *abt, int i) {
// インデックスが範囲外なら、空きを表す INT_MAX を返す
if (i < 0 || i >= size(abt))
return INT_MAX;
return abt->tree[i];
}
/* インデックス i のノードの左子ノードのインデックスを取得 */
int left(int i) {
return 2 * i + 1;
}
/* インデックス i のノードの右子ノードのインデックスを取得 */
int right(int i) {
return 2 * i + 2;
}
/* インデックス i のノードの親ノードのインデックスを取得 */
int parent(int i) {
return (i - 1) / 2;
}
/* レベル順走査 */
int *levelOrder(ArrayBinaryTree *abt, int *returnSize) {
int *res = (int *)malloc(sizeof(int) * size(abt));
int index = 0;
// 配列を直接走査する
for (int i = 0; i < size(abt); i++) {
if (val(abt, i) != INT_MAX)
res[index++] = val(abt, i);
}
*returnSize = index;
return res;
}
/* 深さ優先探索 */
void dfs(ArrayBinaryTree *abt, int i, char *order, int *res, int *index) {
// 空きスロットなら返す
if (val(abt, i) == INT_MAX)
return;
// 先行順走査
if (strcmp(order, "pre") == 0)
res[(*index)++] = val(abt, i);
dfs(abt, left(i), order, res, index);
// 中順走査
if (strcmp(order, "in") == 0)
res[(*index)++] = val(abt, i);
dfs(abt, right(i), order, res, index);
// 後順走査
if (strcmp(order, "post") == 0)
res[(*index)++] = val(abt, i);
}
/* 先行順走査 */
int *preOrder(ArrayBinaryTree *abt, int *returnSize) {
int *res = (int *)malloc(sizeof(int) * size(abt));
int index = 0;
dfs(abt, 0, "pre", res, &index);
*returnSize = index;
return res;
}
/* 中順走査 */
int *inOrder(ArrayBinaryTree *abt, int *returnSize) {
int *res = (int *)malloc(sizeof(int) * size(abt));
int index = 0;
dfs(abt, 0, "in", res, &index);
*returnSize = index;
return res;
}
/* 後順走査 */
int *postOrder(ArrayBinaryTree *abt, int *returnSize) {
int *res = (int *)malloc(sizeof(int) * size(abt));
int index = 0;
dfs(abt, 0, "post", res, &index);
*returnSize = index;
return res;
}
/* Driver Code */
int main() {
// 二分木を初期化する
// 空き位置 NULL は INT_MAX で表す
int arr[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15};
int arrSize = sizeof(arr) / sizeof(arr[0]);
TreeNode *root = arrayToTree(arr, arrSize);
printf("\n二分木を初期化\n");
printf("二分木の配列表現:\n");
printArray(arr, arrSize);
printf("二分木の連結リスト表現:\n");
printTree(root);
ArrayBinaryTree *abt = newArrayBinaryTree(arr, arrSize);
// ノードにアクセス
int i = 1;
int l = left(i), r = right(i), p = parent(i);
printf("\n現在のノードのインデックスは %d、値は %d\n", i, val(abt, i));
printf("左の子ノードのインデックスは %d、値は %d\n", l, l < arrSize ? val(abt, l) : INT_MAX);
printf("右の子ノードのインデックスは %d、値は %d\n", r, r < arrSize ? val(abt, r) : INT_MAX);
printf("親ノードのインデックスは %d、値は %d\n", p, p < arrSize ? val(abt, p) : INT_MAX);
// 木を走査
int returnSize;
int *res;
res = levelOrder(abt, &returnSize);
printf("\nレベル順走査: ");
printArray(res, returnSize);
free(res);
res = preOrder(abt, &returnSize);
printf("前順走査: ");
printArray(res, returnSize);
free(res);
res = inOrder(abt, &returnSize);
printf("中順走査: ");
printArray(res, returnSize);
free(res);
res = postOrder(abt, &returnSize);
printf("後順走査: ");
printArray(res, returnSize);
free(res);
// メモリを解放する
delArrayBinaryTree(abt);
return 0;
}

View File

@@ -0,0 +1,259 @@
/**
* File: avl_tree.c
* Created Time: 2023-01-15
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* AVL 木構造体 */
typedef struct {
TreeNode *root;
} AVLTree;
/* コンストラクタ */
AVLTree *newAVLTree() {
AVLTree *tree = (AVLTree *)malloc(sizeof(AVLTree));
tree->root = NULL;
return tree;
}
/* デストラクタ */
void delAVLTree(AVLTree *tree) {
freeMemoryTree(tree->root);
free(tree);
}
/* ノードの高さを取得 */
int height(TreeNode *node) {
// 空ノードの高さは -1、葉ードの高さは 0
if (node != NULL) {
return node->height;
}
return -1;
}
/* ノードの高さを更新する */
void updateHeight(TreeNode *node) {
int lh = height(node->left);
int rh = height(node->right);
// ノードの高さは最も高い部分木の高さ + 1 に等しい
if (lh > rh) {
node->height = lh + 1;
} else {
node->height = rh + 1;
}
}
/* 平衡係数を取得 */
int balanceFactor(TreeNode *node) {
// 空ノードの平衡係数は 0
if (node == NULL) {
return 0;
}
// ノードの平衡係数 = 左部分木の高さ - 右部分木の高さ
return height(node->left) - height(node->right);
}
/* 右回転 */
TreeNode *rightRotate(TreeNode *node) {
TreeNode *child, *grandChild;
child = node->left;
grandChild = child->right;
// child を支点として node を右回転させる
child->right = node;
node->left = grandChild;
// ノードの高さを更新する
updateHeight(node);
updateHeight(child);
// 回転後の部分木の根ノードを返す
return child;
}
/* 左回転 */
TreeNode *leftRotate(TreeNode *node) {
TreeNode *child, *grandChild;
child = node->right;
grandChild = child->left;
// child を支点として node を左回転させる
child->left = node;
node->right = grandChild;
// ノードの高さを更新する
updateHeight(node);
updateHeight(child);
// 回転後の部分木の根ノードを返す
return child;
}
/* 回転操作を行い、この部分木の平衡を回復する */
TreeNode *rotate(TreeNode *node) {
// ノード node の平衡係数を取得
int bf = balanceFactor(node);
// 左に偏った木
if (bf > 1) {
if (balanceFactor(node->left) >= 0) {
// 右回転
return rightRotate(node);
} else {
// 左回転してから右回転
node->left = leftRotate(node->left);
return rightRotate(node);
}
}
// 右に偏った木
if (bf < -1) {
if (balanceFactor(node->right) <= 0) {
// 左回転
return leftRotate(node);
} else {
// 右回転してから左回転
node->right = rightRotate(node->right);
return leftRotate(node);
}
}
// 平衡木なので回転不要、そのまま返す
return node;
}
/* ノードを再帰的に挿入する(補助関数) */
TreeNode *insertHelper(TreeNode *node, int val) {
if (node == NULL) {
return newTreeNode(val);
}
/* 1. 挿入位置を探索してノードを挿入 */
if (val < node->val) {
node->left = insertHelper(node->left, val);
} else if (val > node->val) {
node->right = insertHelper(node->right, val);
} else {
// 重複ノードは挿入せず、そのまま返す
return node;
}
// ノードの高さを更新する
updateHeight(node);
/* 2. 回転操作を行い、部分木の平衡を回復する */
node = rotate(node);
// 部分木の根ノードを返す
return node;
}
/* ノードを挿入 */
void insert(AVLTree *tree, int val) {
tree->root = insertHelper(tree->root, val);
}
/* ノードを再帰的に削除する(補助関数) */
TreeNode *removeHelper(TreeNode *node, int val) {
TreeNode *child, *grandChild;
if (node == NULL) {
return NULL;
}
/* 1. ノードを探索して削除 */
if (val < node->val) {
node->left = removeHelper(node->left, val);
} else if (val > node->val) {
node->right = removeHelper(node->right, val);
} else {
if (node->left == NULL || node->right == NULL) {
child = node->left;
if (node->right != NULL) {
child = node->right;
}
// 子ノード数 = 0 の場合、node をそのまま削除して返す
if (child == NULL) {
return NULL;
} else {
// 子ノード数 = 1 の場合、node をそのまま削除する
node = child;
}
} else {
// 子ノード数 = 2 の場合、中順走査の次のノードを削除し、そのノードで現在のノードを置き換える
TreeNode *temp = node->right;
while (temp->left != NULL) {
temp = temp->left;
}
int tempVal = temp->val;
node->right = removeHelper(node->right, temp->val);
node->val = tempVal;
}
}
// ノードの高さを更新する
updateHeight(node);
/* 2. 回転操作を行い、部分木の平衡を回復する */
node = rotate(node);
// 部分木の根ノードを返す
return node;
}
/* ノードを削除 */
// stdio.h を導入しているため、ここでは remove 識別子を使えない
void removeItem(AVLTree *tree, int val) {
TreeNode *root = removeHelper(tree->root, val);
}
/* ノードを探索 */
TreeNode *search(AVLTree *tree, int val) {
TreeNode *cur = tree->root;
// ループで探索し、葉ノードを越えたら抜ける
while (cur != NULL) {
if (cur->val < val) {
// 目標ノードは cur の右部分木にある
cur = cur->right;
} else if (cur->val > val) {
// 目標ノードは cur の左部分木にある
cur = cur->left;
} else {
// 目標ノードが見つかったらループを抜ける
break;
}
}
// 目標ノードが見つかったらループを抜ける
return cur;
}
void testInsert(AVLTree *tree, int val) {
insert(tree, val);
printf("\nノード %d を挿入した後、AVL 木は \n", val);
printTree(tree->root);
}
void testRemove(AVLTree *tree, int val) {
removeItem(tree, val);
printf("\nノード %d を削除した後、AVL 木は \n", val);
printTree(tree->root);
}
/* Driver Code */
int main() {
/* 空の AVL 木を初期化する */
AVLTree *tree = (AVLTree *)newAVLTree();
/* ノードを挿入 */
// ノード挿入後に AVL 木がどのように平衡を保つかに注目してほしい
testInsert(tree, 1);
testInsert(tree, 2);
testInsert(tree, 3);
testInsert(tree, 4);
testInsert(tree, 5);
testInsert(tree, 8);
testInsert(tree, 7);
testInsert(tree, 9);
testInsert(tree, 10);
testInsert(tree, 6);
/* 重複ノードを挿入する */
testInsert(tree, 7);
/* ノードを削除 */
// ノード削除後に AVL 木がどのように平衡を保つかに注目してほしい
testRemove(tree, 8); // 次数 0 のノードを削除する
testRemove(tree, 5); // 次数 1 のノードを削除する
testRemove(tree, 4); // 次数 2 のノードを削除する
/* ノードを検索 */
TreeNode *node = search(tree, 7);
printf("\n見つかったノードオブジェクトのノード値 = %d \n", node->val);
// メモリを解放する
delAVLTree(tree);
return 0;
}

View File

@@ -0,0 +1,171 @@
/**
* File: binary_search_tree.c
* Created Time: 2023-01-11
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* 二分探索木構造体 */
typedef struct {
TreeNode *root;
} BinarySearchTree;
/* コンストラクタ */
BinarySearchTree *newBinarySearchTree() {
// 空の木を初期化する
BinarySearchTree *bst = (BinarySearchTree *)malloc(sizeof(BinarySearchTree));
bst->root = NULL;
return bst;
}
/* デストラクタ */
void delBinarySearchTree(BinarySearchTree *bst) {
freeMemoryTree(bst->root);
free(bst);
}
/* 二分木の根ノードを取得 */
TreeNode *getRoot(BinarySearchTree *bst) {
return bst->root;
}
/* ノードを探索 */
TreeNode *search(BinarySearchTree *bst, int num) {
TreeNode *cur = bst->root;
// ループで探索し、葉ノードを越えたら抜ける
while (cur != NULL) {
if (cur->val < num) {
// 目標ノードは cur の右部分木にある
cur = cur->right;
} else if (cur->val > num) {
// 目標ノードは cur の左部分木にある
cur = cur->left;
} else {
// 目標ノードが見つかったらループを抜ける
break;
}
}
// 目標ノードを返す
return cur;
}
/* ノードを挿入 */
void insert(BinarySearchTree *bst, int num) {
// 木が空なら、根ノードを初期化する
if (bst->root == NULL) {
bst->root = newTreeNode(num);
return;
}
TreeNode *cur = bst->root, *pre = NULL;
// ループで探索し、葉ノードを越えたら抜ける
while (cur != NULL) {
// 重複ノードが見つかったら、直ちに返す
if (cur->val == num) {
return;
}
pre = cur;
if (cur->val < num) {
// 挿入位置は cur の右部分木にある
cur = cur->right;
} else {
// 挿入位置は cur の左部分木にある
cur = cur->left;
}
}
// ノードを挿入
TreeNode *node = newTreeNode(num);
if (pre->val < num) {
pre->right = node;
} else {
pre->left = node;
}
}
/* ノードを削除 */
// stdio.h を導入しているため、ここでは remove 識別子を使えない
void removeItem(BinarySearchTree *bst, int num) {
// 木が空なら、そのまま早期リターンする
if (bst->root == NULL)
return;
TreeNode *cur = bst->root, *pre = NULL;
// ループで探索し、葉ノードを越えたら抜ける
while (cur != NULL) {
// 削除対象のノードが見つかったら、ループを抜ける
if (cur->val == num)
break;
pre = cur;
if (cur->val < num) {
// 削除対象ノードは root の右部分木にある
cur = cur->right;
} else {
// 削除対象ノードは root の左部分木にある
cur = cur->left;
}
}
// 削除対象ノードがなければそのまま返す
if (cur == NULL)
return;
// 削除対象ノードに子ノードがあるかを判定する
if (cur->left == NULL || cur->right == NULL) {
/* 子ノード数 = 0 or 1 */
// 子ノード数 = 0 / 1 のとき、child = nullptr / その子ノード
TreeNode *child = cur->left != NULL ? cur->left : cur->right;
// ノード cur を削除する
if (pre->left == cur) {
pre->left = child;
} else {
pre->right = child;
}
// メモリを解放する
free(cur);
} else {
/* 子ノード数 = 2 */
// 中順走査における cur の次ノードを取得
TreeNode *tmp = cur->right;
while (tmp->left != NULL) {
tmp = tmp->left;
}
int tmpVal = tmp->val;
// ノード tmp を再帰的に削除
removeItem(bst, tmp->val);
// tmp で cur を上書きする
cur->val = tmpVal;
}
}
/* Driver Code */
int main() {
/* 二分探索木を初期化 */
int nums[] = {8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15};
BinarySearchTree *bst = newBinarySearchTree();
for (int i = 0; i < sizeof(nums) / sizeof(int); i++) {
insert(bst, nums[i]);
}
printf("初期化した二分木は\n");
printTree(getRoot(bst));
/* ノードを探索 */
TreeNode *node = search(bst, 7);
printf("見つかったノードオブジェクトのノード値 = %d\n", node->val);
/* ノードを挿入 */
insert(bst, 16);
printf("ノード 16 を挿入した後、二分木は\n");
printTree(getRoot(bst));
/* ノードを削除 */
removeItem(bst, 1);
printf("ノード 1 を削除した後、二分木は\n");
printTree(getRoot(bst));
removeItem(bst, 2);
printf("ノード 2 を削除した後、二分木は\n");
printTree(getRoot(bst));
removeItem(bst, 4);
printf("ノード 4 を削除した後、二分木は\n");
printTree(getRoot(bst));
// メモリを解放する
delBinarySearchTree(bst);
return 0;
}

View File

@@ -0,0 +1,43 @@
/**
* File: binary_tree.c
* Created Time: 2023-01-11
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
/* Driver Code */
int main() {
/* 二分木を初期化 */
// ノードを初期化
TreeNode *n1 = newTreeNode(1);
TreeNode *n2 = newTreeNode(2);
TreeNode *n3 = newTreeNode(3);
TreeNode *n4 = newTreeNode(4);
TreeNode *n5 = newTreeNode(5);
// ノード間の参照(ポインタ)を構築する
n1->left = n2;
n1->right = n3;
n2->left = n4;
n2->right = n5;
printf("二分木を初期化\n");
printTree(n1);
/* ノードの挿入と削除 */
TreeNode *P = newTreeNode(0);
// n1 -> n2 の間にノード P を挿入
n1->left = P;
P->left = n2;
printf("ノード P を挿入した後\n");
printTree(n1);
// ノード P を削除
n1->left = n2;
// メモリを解放する
free(P);
printf("ノード P を削除した後\n");
printTree(n1);
freeMemoryTree(n1);
return 0;
}

View File

@@ -0,0 +1,73 @@
/**
* File: binary_tree_bfs.c
* Created Time: 2023-01-11
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
/* レベル順走査 */
int *levelOrder(TreeNode *root, int *size) {
/* 補助キュー */
int front, rear;
int index, *arr;
TreeNode *node;
TreeNode **queue;
/* 補助キュー */
queue = (TreeNode **)malloc(sizeof(TreeNode *) * MAX_SIZE);
// キューへのポインタ
front = 0, rear = 0;
// 根ノードを追加する
queue[rear++] = root;
// 走査順序を保存するためのリストを初期化する
/* 補助配列 */
arr = (int *)malloc(sizeof(int) * MAX_SIZE);
// 配列ポインタ
index = 0;
while (front < rear) {
// デキュー
node = queue[front++];
// ノードの値を保存する
arr[index++] = node->val;
if (node->left != NULL) {
// 左子ノードをキューに追加
queue[rear++] = node->left;
}
if (node->right != NULL) {
// 右子ノードをキューに追加
queue[rear++] = node->right;
}
}
// 配列長の値を更新
*size = index;
arr = realloc(arr, sizeof(int) * (*size));
// 補助配列の領域を解放する
free(queue);
return arr;
}
/* Driver Code */
int main() {
/* 二分木を初期化 */
// ここでは、配列から直接二分木を生成する関数を利用する
int nums[] = {1, 2, 3, 4, 5, 6, 7};
int size = sizeof(nums) / sizeof(int);
TreeNode *root = arrayToTree(nums, size);
printf("二分木を初期化\n");
printTree(root);
/* レベル順走査 */
// 配列の長さを渡す必要がある
int *arr = levelOrder(root, &size);
printf("レベル順走査のノード出力シーケンス = ");
printArray(arr, size);
// メモリを解放する
freeMemoryTree(root);
free(arr);
return 0;
}

View File

@@ -0,0 +1,75 @@
/**
* File: binary_tree_dfs.c
* Created Time: 2023-01-11
* Author: Reanon (793584285@qq.com)
*/
#include "../utils/common.h"
#define MAX_SIZE 100
// 走査順序を格納するための補助配列
int arr[MAX_SIZE];
/* 先行順走査 */
void preOrder(TreeNode *root, int *size) {
if (root == NULL)
return;
// 訪問順序:根ノード -> 左部分木 -> 右部分木
arr[(*size)++] = root->val;
preOrder(root->left, size);
preOrder(root->right, size);
}
/* 中順走査 */
void inOrder(TreeNode *root, int *size) {
if (root == NULL)
return;
// 訪問優先順: 左部分木 -> 根ノード -> 右部分木
inOrder(root->left, size);
arr[(*size)++] = root->val;
inOrder(root->right, size);
}
/* 後順走査 */
void postOrder(TreeNode *root, int *size) {
if (root == NULL)
return;
// 訪問優先順: 左部分木 -> 右部分木 -> 根ノード
postOrder(root->left, size);
postOrder(root->right, size);
arr[(*size)++] = root->val;
}
/* Driver Code */
int main() {
/* 二分木を初期化 */
// ここでは、配列から直接二分木を生成する関数を利用する
int nums[] = {1, 2, 3, 4, 5, 6, 7};
int size = sizeof(nums) / sizeof(int);
TreeNode *root = arrayToTree(nums, size);
printf("二分木を初期化\n");
printTree(root);
/* 先行順走査 */
// 補助配列を初期化する
size = 0;
preOrder(root, &size);
printf("前順走査のノード出力シーケンス = ");
printArray(arr, size);
/* 中順走査 */
size = 0;
inOrder(root, &size);
printf("中順走査のノード出力シーケンス = ");
printArray(arr, size);
/* 後順走査 */
size = 0;
postOrder(root, &size);
printf("後順走査のノード出力シーケンス = ");
printArray(arr, size);
freeMemoryTree(root);
return 0;
}

View File

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

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

@@ -0,0 +1,36 @@
/**
* File: common.h
* Created Time: 2022-12-20
* Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com)
*/
#ifndef COMMON_H
#define COMMON_H
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include "list_node.h"
#include "print_util.h"
#include "tree_node.h"
#include "vertex.h"
// hash table lib
#include "uthash.h"
#include "vector.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif // COMMON_H

View File

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

View File

@@ -0,0 +1,59 @@
/**
* File: list_node.h
* Created Time: 2023-01-09
* Author: Reanon (793584285@qq.com)
*/
#ifndef LIST_NODE_H
#define LIST_NODE_H
#ifdef __cplusplus
extern "C" {
#endif
/* 連結リストノード構造体 */
typedef struct ListNode {
int val; // ノード値
struct ListNode *next; // 次のノードへの参照
} ListNode;
/* コンストラクタ。新しいノードを初期化する */
ListNode *newListNode(int val) {
ListNode *node;
node = (ListNode *)malloc(sizeof(ListNode));
node->val = val;
node->next = NULL;
return node;
}
/* 配列をデシリアライズして連結リストに変換する */
ListNode *arrToLinkedList(const int *arr, size_t size) {
if (size <= 0) {
return NULL;
}
ListNode *dummy = newListNode(0);
ListNode *node = dummy;
for (int i = 0; i < size; i++) {
node->next = newListNode(arr[i]);
node = node->next;
}
return dummy->next;
}
/* 連結リストに割り当てたメモリを解放する */
void freeMemoryLinkedList(ListNode *cur) {
// メモリを解放する
ListNode *pre;
while (cur != NULL) {
pre = cur;
cur = cur->next;
free(pre);
}
}
#ifdef __cplusplus
}
#endif
#endif // LIST_NODE_H

View File

@@ -0,0 +1,131 @@
/**
* File: print_util.h
* Created Time: 2022-12-21
* Author: MolDum (moldum@163.com), Reanon (793584285@qq.com)
*/
#ifndef PRINT_UTIL_H
#define PRINT_UTIL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list_node.h"
#include "tree_node.h"
#ifdef __cplusplus
extern "C" {
#endif
/* 配列を出力する */
void printArray(int arr[], int size) {
if (arr == NULL || size == 0) {
printf("[]");
return;
}
printf("[");
for (int i = 0; i < size - 1; i++) {
printf("%d, ", arr[i]);
}
printf("%d]\n", arr[size - 1]);
}
/* 配列を出力する */
void printArrayFloat(float arr[], int size) {
if (arr == NULL || size == 0) {
printf("[]");
return;
}
printf("[");
for (int i = 0; i < size - 1; i++) {
printf("%.2f, ", arr[i]);
}
printf("%.2f]\n", arr[size - 1]);
}
/* 連結リストを出力 */
void printLinkedList(ListNode *node) {
if (node == NULL) {
return;
}
while (node->next != NULL) {
printf("%d -> ", node->val);
node = node->next;
}
printf("%d\n", node->val);
}
typedef struct Trunk {
struct Trunk *prev;
char *str;
} Trunk;
Trunk *newTrunk(Trunk *prev, char *str) {
Trunk *trunk = (Trunk *)malloc(sizeof(Trunk));
trunk->prev = prev;
trunk->str = (char *)malloc(sizeof(char) * 10);
strcpy(trunk->str, str);
return trunk;
}
void showTrunks(Trunk *trunk) {
if (trunk == NULL) {
return;
}
showTrunks(trunk->prev);
printf("%s", trunk->str);
}
/**
* 二分木を出力
* This tree printer is borrowed from TECHIE DELIGHT
* https://www.techiedelight.com/c-program-print-binary-tree/
*/
void printTreeHelper(TreeNode *node, Trunk *prev, bool isRight) {
if (node == NULL) {
return;
}
char *prev_str = " ";
Trunk *trunk = newTrunk(prev, prev_str);
printTreeHelper(node->right, trunk, true);
if (prev == NULL) {
trunk->str = "———";
} else if (isRight) {
trunk->str = "/———";
prev_str = " |";
} else {
trunk->str = "\\———";
prev->str = prev_str;
}
showTrunks(trunk);
printf("%d\n", node->val);
if (prev != NULL) {
prev->str = prev_str;
}
trunk->str = " |";
printTreeHelper(node->left, trunk, false);
}
/* 二分木を出力 */
void printTree(TreeNode *root) {
printTreeHelper(root, NULL, false);
}
/* ヒープを出力 */
void printHeap(int arr[], int size) {
TreeNode *root;
printf("ヒープの配列表現:");
printArray(arr, size);
printf("ヒープの木構造表現:\n");
root = arrayToTree(arr, size);
printTree(root);
}
#ifdef __cplusplus
}
#endif
#endif // PRINT_UTIL_H

View File

@@ -0,0 +1,107 @@
/**
* File: tree_node.h
* Created Time: 2023-01-09
* Author: Reanon (793584285@qq.com)
*/
#ifndef TREE_NODE_H
#define TREE_NODE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <limits.h>
#define MAX_NODE_SIZE 5000
/* 二分木ノード構造体 */
typedef struct TreeNode {
int val; // ノード値
int height; // ノードの高さ
struct TreeNode *left; // 左の子ノードへのポインタ
struct TreeNode *right; // 右の子ノードへのポインタ
} TreeNode;
/* コンストラクタ */
TreeNode *newTreeNode(int val) {
TreeNode *node;
node = (TreeNode *)malloc(sizeof(TreeNode));
node->val = val;
node->height = 0;
node->left = NULL;
node->right = NULL;
return node;
}
// シリアライズの符号化規則は以下を参照:
// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
// 二分木の配列表現:
// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
// 二分木の連結リスト表現:
// /——— 15
// /——— 7
// /——— 3
// | \——— 6
// | \——— 12
// ——— 1
// \——— 2
// | /——— 9
// \——— 4
// \——— 8
/* リストを二分木にデシリアライズする: 再帰 */
TreeNode *arrayToTreeDFS(int *arr, int size, int i) {
if (i < 0 || i >= size || arr[i] == INT_MAX) {
return NULL;
}
TreeNode *root = (TreeNode *)malloc(sizeof(TreeNode));
root->val = arr[i];
root->left = arrayToTreeDFS(arr, size, 2 * i + 1);
root->right = arrayToTreeDFS(arr, size, 2 * i + 2);
return root;
}
/* リストを二分木にデシリアライズする */
TreeNode *arrayToTree(int *arr, int size) {
return arrayToTreeDFS(arr, size, 0);
}
/* 二分木をリストにシリアライズする: 再帰 */
void treeToArrayDFS(TreeNode *root, int i, int *res, int *size) {
if (root == NULL) {
return;
}
while (i >= *size) {
res = realloc(res, (*size + 1) * sizeof(int));
res[*size] = INT_MAX;
(*size)++;
}
res[i] = root->val;
treeToArrayDFS(root->left, 2 * i + 1, res, size);
treeToArrayDFS(root->right, 2 * i + 2, res, size);
}
/* 二分木をリストにシリアライズする */
int *treeToArray(TreeNode *root, int *size) {
*size = 0;
int *res = NULL;
treeToArrayDFS(root, 0, res, size);
return res;
}
/* 二分木のメモリを解放する */
void freeMemoryTree(TreeNode *root) {
if (root == NULL)
return;
freeMemoryTree(root->left);
freeMemoryTree(root->right);
free(root);
}
#ifdef __cplusplus
}
#endif
#endif // TREE_NODE_H

1140
ja/codes/c/utils/uthash.h Normal file

File diff suppressed because it is too large Load Diff

259
ja/codes/c/utils/vector.h Normal file
View File

@@ -0,0 +1,259 @@
/**
* File: vector.h
* Created Time: 2023-07-13
* Author: Zuoxun (845242523@qq.com)、Gonglja (glj0@outlook.com)
*/
#ifndef VECTOR_H
#define VECTOR_H
#ifdef __cplusplus
extern "C" {
#endif
/* ベクタ型を定義 */
typedef struct vector {
int size; // 現在のベクタのサイズ
int capacity; // 現在のベクタの容量
int depth; // 現在のベクタの深さ
void **data; // データを指すポインタ配列
} vector;
/* ベクタを構築 */
vector *newVector() {
vector *v = malloc(sizeof(vector));
v->size = 0;
v->capacity = 4;
v->depth = 1;
v->data = malloc(v->capacity * sizeof(void *));
return v;
}
/* ベクタを構築し、サイズと要素のデフォルト値を指定する */
vector *_newVector(int size, void *elem, int elemSize) {
vector *v = malloc(sizeof(vector));
v->size = size;
v->capacity = size;
v->depth = 1;
v->data = malloc(v->capacity * sizeof(void *));
for (int i = 0; i < size; i++) {
void *tmp = malloc(sizeof(char) * elemSize);
memcpy(tmp, elem, elemSize);
v->data[i] = tmp;
}
return v;
}
/* ベクタを破棄 */
void delVector(vector *v) {
if (v) {
if (v->depth == 0) {
return;
} else if (v->depth == 1) {
for (int i = 0; i < v->size; i++) {
free(v->data[i]);
}
free(v);
} else {
for (int i = 0; i < v->size; i++) {
delVector(v->data[i]);
}
v->depth--;
}
}
}
/* 要素をベクタの末尾に追加する(コピー方式) */
void vectorPushback(vector *v, void *elem, int elemSize) {
if (v->size == v->capacity) {
v->capacity *= 2;
v->data = realloc(v->data, v->capacity * sizeof(void *));
}
void *tmp = malloc(sizeof(char) * elemSize);
memcpy(tmp, elem, elemSize);
v->data[v->size++] = tmp;
}
/* ベクタの末尾から要素を取り出す */
void vectorPopback(vector *v) {
if (v->size != 0) {
free(v->data[v->size - 1]);
v->size--;
}
}
/* ベクタをクリア */
void vectorClear(vector *v) {
delVector(v);
v->size = 0;
v->capacity = 4;
v->depth = 1;
v->data = malloc(v->capacity * sizeof(void *));
}
/* ベクタのサイズを取得する */
int vectorSize(vector *v) {
return v->size;
}
/* ベクタの末尾要素を取得する */
void *vectorBack(vector *v) {
int n = v->size;
return n > 0 ? v->data[n - 1] : NULL;
}
/* ベクタの先頭要素を取得する */
void *vectorFront(vector *v) {
return v->size > 0 ? v->data[0] : NULL;
}
/* ベクタの添字 `pos` の要素を取得する */
void *vectorAt(vector *v, int pos) {
if (pos < 0 || pos >= v->size) {
printf("vectorAt: out of range\n");
return NULL;
}
return v->data[pos];
}
/* ベクタの添字 `pos` の要素を設定する */
void vectorSet(vector *v, int pos, void *elem, int elemSize) {
if (pos < 0 || pos >= v->size) {
printf("vectorSet: out of range\n");
return;
}
free(v->data[pos]);
void *tmp = malloc(sizeof(char) * elemSize);
memcpy(tmp, elem, elemSize);
v->data[pos] = tmp;
}
/* ベクトルを拡張する */
void vectorExpand(vector *v) {
v->capacity *= 2;
v->data = realloc(v->data, v->capacity * sizeof(void *));
}
/* ベクトルを縮小する */
void vectorShrink(vector *v) {
v->capacity /= 2;
v->data = realloc(v->data, v->capacity * sizeof(void *));
}
/* ベクタの添字 pos に要素を挿入 */
void vectorInsert(vector *v, int pos, void *elem, int elemSize) {
if (v->size == v->capacity) {
vectorExpand(v);
}
for (int j = v->size; j > pos; j--) {
v->data[j] = v->data[j - 1];
}
void *tmp = malloc(sizeof(char) * elemSize);
memcpy(tmp, elem, elemSize);
v->data[pos] = tmp;
v->size++;
}
/* ベクトルの添字 pos の要素を削除する */
void vectorErase(vector *v, int pos) {
if (v->size != 0) {
free(v->data[pos]);
for (int j = pos; j < v->size - 1; j++) {
v->data[j] = v->data[j + 1];
}
v->size--;
}
}
/* ベクトルの要素を交換する */
void vectorSwap(vector *v, int i, int j) {
void *tmp = v->data[i];
v->data[i] = v->data[j];
v->data[j] = tmp;
}
/* ベクトルが空かどうか */
bool vectorEmpty(vector *v) {
return v->size == 0;
}
/* ベクトルが満杯かどうか */
bool vectorFull(vector *v) {
return v->size == v->capacity;
}
/* ベクトルが等しいかどうか */
bool vectorEqual(vector *v1, vector *v2) {
if (v1->size != v2->size) {
printf("size not equal\n");
return false;
}
for (int i = 0; i < v1->size; i++) {
void *a = v1->data[i];
void *b = v2->data[i];
if (memcmp(a, b, sizeof(a)) != 0) {
printf("data %d not equal\n", i);
return false;
}
}
return true;
}
/* ベクタ内部をソート */
void vectorSort(vector *v, int (*cmp)(const void *, const void *)) {
qsort(v->data, v->size, sizeof(void *), cmp);
}
/* 出力関数。出力対象の変数を表示する関数を渡す必要がある */
/* 現在は深さ 1 の vector のみ出力をサポート */
void printVector(vector *v, void (*printFunc)(vector *v, void *p)) {
if (v) {
if (v->depth == 0) {
return;
} else if (v->depth == 1) {
if(v->size == 0) {
printf("\n");
return;
}
for (int i = 0; i < v->size; i++) {
if (i == 0) {
printf("[");
} else if (i == v->size - 1) {
printFunc(v, v->data[i]);
printf("]\r\n");
break;
}
printFunc(v, v->data[i]);
printf(",");
}
} else {
for (int i = 0; i < v->size; i++) {
printVector(v->data[i], printFunc);
}
v->depth--;
}
}
}
/* 現在は深さ 2 の vector のみ出力をサポート */
void printVectorMatrix(vector *vv, void (*printFunc)(vector *v, void *p)) {
printf("[\n");
for (int i = 0; i < vv->size; i++) {
vector *v = (vector *)vv->data[i];
printf(" [");
for (int j = 0; j < v->size; j++) {
printFunc(v, v->data[j]);
if (j != v->size - 1)
printf(",");
}
printf("],");
printf("\n");
}
printf("]\n");
}
#ifdef __cplusplus
}
#endif
#endif // VECTOR_H

49
ja/codes/c/utils/vertex.h Normal file
View File

@@ -0,0 +1,49 @@
/**
* File: vertex.h
* Created Time: 2023-10-28
* Author: krahets (krahets@163.com)
*/
#ifndef VERTEX_H
#define VERTEX_H
#ifdef __cplusplus
extern "C" {
#endif
/* 頂点構造体 */
typedef struct {
int val;
} Vertex;
/* コンストラクタ。新しいノードを初期化する */
Vertex *newVertex(int val) {
Vertex *vet;
vet = (Vertex *)malloc(sizeof(Vertex));
vet->val = val;
return vet;
}
/* 値の配列を頂点配列に変換 */
Vertex **valsToVets(int *vals, int size) {
Vertex **vertices = (Vertex **)malloc(size * sizeof(Vertex *));
for (int i = 0; i < size; ++i) {
vertices[i] = newVertex(vals[i]);
}
return vertices;
}
/* 頂点配列を値配列に変換 */
int *vetsToVals(Vertex **vertices, int size) {
int *vals = (int *)malloc(size * sizeof(int));
for (int i = 0; i < size; ++i) {
vals[i] = vertices[i]->val;
}
return vals;
}
#ifdef __cplusplus
}
#endif
#endif // VERTEX_H

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