diff --git a/README.md b/README.md index c672611..38351fc 100755 --- a/README.md +++ b/README.md @@ -1,85 +1,247 @@ -# LeetCode Practice Journey 🚀 +# LeetCode 練習專案 -## 📊 進度統計 -- **總題數**: 0 -- **已完成**: 0 -- **開始日期**: 2025-09-01 -- **最後更新**: 2025-09-01 +這是一個用來紀錄與管理 LeetCode 練習的專案,整合 C# 與 Go 的題解、基本測試模板、月度學習日誌,以及一支小工具腳本,協助快速建立新題目與維持一致的結構與習慣。 -### 難度分布 -| 難度 | 完成數 | 總數 | 進度 | -|------|--------|------|------| -| Easy | 0 | 0 | 0% | -| Medium | 0 | 0 | 0% | -| Hard | 0 | 0 | 0% | +## 專案架構 -### 語言分布 -| 語言 | 完成題數 | -|------|----------| -| C++ | 0 | -| C# | 0 | -| Go | 0 | +``` +. +├── problems/ # 每題一個資料夾 (NNNN-kebab-case) +│ └── 3516-find-closest-person/ +│ ├── README.md # 題目說明與解題筆記 +│ ├── csharp/ # C# 解法 (dotnet) +│ ├── go/ # Go 解法 (go module) +│ └── test/ # C#/Go 測試模板與腳本 +├── utils/ +│ └── leetcode_helper.sh # 建題、更新 README、建立月誌的工具腳本 +├── logs/ # 月度練習紀錄 (YYYY-MM.md) +├── templates/ # 題目與月誌的 Markdown 範本 +├── legacy/ # 舊題或舊結構的紀錄 +├── LICENSE +└── README.md +``` -## 🎯 學習目標 -- [ ] 熟悉基本資料結構 (Array, LinkedList, Stack, Queue) -- [ ] 掌握雙指針技巧 -- [ ] 動態規劃入門 (目標:完成 10 題) -- [ ] 樹和圖的遍歷 -- [ ] 二分搜尋應用 +命名慣例:問題資料夾以「4 位數題號 + 題目英文 kebab-case」命名,例如 `3025-find-the-number-of-ways-to-place-people-i`。 -## 📚 題目分類 +## 需求與環境 -### Array & Hash Table -| 題號 | 題目 | 難度 | 狀態 | 語言 | 筆記 | -|------|------|------|------|------|------| -| 1 | [Two Sum](problems/0001-two-sum/) | Easy | ✅ | C#, Go | 經典入門題 | -| 15 | [3Sum](problems/0015-3sum/) | Medium | ✅ | C#, Go | 雙指針技巧 | +- C#: 安裝 `.NET SDK 8+`,常用指令 `dotnet build / run / test` +- Go: 安裝 `Go 1.21+`,常用指令 `go build / run / test` +- Bash: 可執行 `utils/leetcode_helper.sh` -### Two Pointers -| 題號 | 題目 | 難度 | 狀態 | 語言 | 筆記 | -|------|------|------|------|------|------| -| 11 | [Container With Most Water](problems/0011-container-with-most-water/) | Medium | ✅ | C# | 貪心思維 | +初次使用請先授權腳本: -### String -| 題號 | 題目 | 難度 | 狀態 | 語言 | 筆記 | -|------|------|------|------|------|------| -| 3 | [Longest Substring Without Repeating Characters](problems/0003-longest-substring/) | Medium | 🔄 | C# | 滑動窗口 | +``` +chmod +x utils/leetcode_helper.sh +``` -### Dynamic Programming -| 題號 | 題目 | 難度 | 狀態 | 語言 | 筆記 | -|------|------|------|------|------|------| -| 70 | [Climbing Stairs](problems/0070-climbing-stairs/) | Easy | 📝 | - | 待開始 | +## 常用操作 -## 📅 月度學習記錄 -- [2025年9月](logs/2025-09.md) - 基礎入門月 (完成 8 題) -- [2025年10月](logs/2025-09.md) - 進階提升月 (進行中...) +- 新增題目(自動建立資料夾、README、C#/Go 程式與測試模板) -## 🧠 重點學習筆記 +``` +./utils/leetcode_helper.sh problem <題號> <題目名稱-kebab> <難度> -### 常用模式總結 -1. **雙指針**: 適用於有序陣列的搜尋問題 -2. **滑動窗口**: 子陣列/子字串的最佳化問題 -3. **Hash Map**: 快速查找和計數問題 +# 範例 +./utils/leetcode_helper.sh problem 1 two-sum Easy +./utils/leetcode_helper.sh problem 3000 maximum-area-rectangle Medium +``` -### 容易出錯的地方 -- 邊界條件檢查 (空陣列、單一元素) -- 整數溢位問題 -- 陣列索引越界 +建立完成後的結構(示意): -## 📈 學習曲線反思 -- **第一週**: 熟悉基本語法和 LeetCode 平台 -- **第二週**: 開始理解演算法思維 -- **第三週**: 能獨立分析中等難度題目 -- **第四週**: 開始關注時間空間複雜度最佳化 +``` +problems/NNNN-name/ +├── README.md +├── csharp/ +│ ├── Program.cs +│ └── csharp.csproj +├── go/ +│ ├── main.go +│ └── go.mod +└── test/ + ├── SolutionTests.cs # xUnit 測試 + ├── TestProject.csproj + ├── main_test.go # Go testing + ├── input1.txt / output1.txt + ├── input2.txt / output2.txt + ├── edge_cases.md + └── run_tests.sh # 一鍵執行 C# 與 Go 測試 +``` -## 🔗 有用資源 -- [LeetCode Patterns](https://leetcode.com/discuss/general-discussion/458695/dynamic-programming-patterns) -- [演算法與資料結構筆記](https://github.com/your-notes-repo) -- [LeetCode Solutions 參考](https://github.com/your-solutions-repo) +- 建立月度日誌(依模板生成 `logs/YYYY-MM.md`) + +``` +./utils/leetcode_helper.sh log 2025-09 +``` + +- 更新主 README(目前顯示題目統計,後續可擴充自動彙整) + +``` +./utils/leetcode_helper.sh readme +``` + +## 執行與測試 + +- 執行 C# 解法: + +``` +cd problems/NNNN-name/csharp +dotnet run +``` + +- 執行 Go 解法: + +``` +cd problems/NNNN-name/go +go run main.go +``` + +- 執行該題的測試(同時跑 C# 與 Go): + +``` +cd problems/NNNN-name/test +./run_tests.sh +``` + +或分別執行: + +``` +cd problems/NNNN-name/test +dotnet test +go test -v +``` + +## utils 的用途 + +`utils/leetcode_helper.sh` 提供三個主要功能,將繁瑣步驟自動化、保持專案一致性: + +- problem:根據輸入的題號、名稱與難度,建立一個完整題目骨架 + - 產生題目 `README.md`(含題目資訊、思路、測試與筆記段落) + - 建立 `csharp/` 與 `go/` 目錄及基本程式碼 + - 建立 `test/` 目錄,內含 xUnit、Go 測試模板與 `run_tests.sh` + - 初始化 Go module(`go.mod`) +- readme:掃描 `problems/` 題目數量,輸出簡單統計到終端(可再擴充寫回本檔) +- log:依 `templates/logs-template.md.tmpl` 生成月誌雛形,方便每日記錄與月度回顧 + +小提醒:腳本內建的模板語言為繁中,可依需求修改 `templates/` 與腳本內容。 + +## 模板與筆記 + +- 題目模板:`templates/problem-template.md.tmpl` +- 月誌模板:`templates/logs-template.md.tmpl` + +建議每題完成後: +- 更新該題 `README.md` 的「解題思路」「測試案例」「學習筆記」段落 +- 在 `logs/YYYY-MM.md` 紀錄當日題目、時間、心得與統計 + +## Templates 強化(重點) + +- **Problem 模板新增**: + - 先備條件與限制(輸入範圍、是否需要 in-place/排序) + - 解法比較(A/B/C)、正確性要點、優缺點 + - 乾跑(Dry Run)步驟示例 + - API 設計(C#/Go 方法簽名)與常見陷阱清單 + - 對拍/隨機測試建議;更完整的邊界清單 +- **Logs 模板新增**: + - 本月設定(主題/目標/挑戰)與主題覆蓋追蹤 + - 連續天數(Streak)與錯誤類型統計 + - 困難案例復盤與下月計畫欄位 + +## Problem 模板範例(節錄,3516) + +``` +# [3516] Find Closest Person + +## 題目資訊 +- 難度: Easy +- 標籤: Math +- 題目連結: https://leetcode.com/problems/find-closest-person/ +- 練習日期: 2025-09-04 +- 目標複雜度: 時間 O(1), 空間 O(1) + +## 先備條件與限制 +- 輸入: x, y, z 為整數;值域小;需比較兩者距離 +- 回傳: 1/2/0 表示誰先到或同時到 + +## 解法比較 +1. 解法A(直接比距離) + - 思路: 計算 |x-z| 與 |y-z|,比較大小 + - 正確性: 距離等速 → 距離小者先到 + - 複雜度: O(1) / O(1) +2. 解法B(無) + +## 乾跑 +- x=2, y=7, z=4 → |2-4|=2, |7-4|=3 → 回傳 1 + +## 實作細節與 API 設計 +- C# + public int FindClosest(int x, int y, int z) { ... } +- Go + func findClosest(x, y, z int) int { ... } + +## 測試案例 +- 邊界: 等距(回傳0)、同點、極小/極大值 +``` + +完整寫法可參考:`problems/3516-find-closest-person/README.md`。 + +## 測試範例(可直接複製改題名) + +- xUnit(C#) + +```csharp +using Xunit; + +public class SolutionTests { + [Theory] + [InlineData(2, 7, 4, 1)] + [InlineData(2, 5, 6, 2)] + [InlineData(1, 5, 3, 0)] + public void FindClosest_Works(int x, int y, int z, int expected) { + var s = new Solution(); + Assert.Equal(expected, s.FindClosest(x, y, z)); + } +} +``` + +- Go testing + +```go +package main + +import "testing" + +func TestFindClosest(t *testing.T) { + tests := []struct{ x, y, z, want int }{ + {2, 7, 4, 1}, + {2, 5, 6, 2}, + {1, 5, 3, 0}, + } + for _, tt := range tests { + if got := findClosest(tt.x, tt.y, tt.z); got != tt.want { + t.Fatalf("x=%d y=%d z=%d want=%d got=%d", tt.x, tt.y, tt.z, tt.want, got) + } + } +} +``` + +## 自訂模板 + +- 直接編輯:`templates/problem-template.md.tmpl`、`templates/logs-template.md.tmpl` +- 工具腳本已改為讀取 `templates/` 目錄的檔案生成骨架: + - 問題頁:`templates/problem/README.md.tmpl` + - C#:`templates/problem/csharp/*` + - Go:`templates/problem/go/*` + - 測試:`templates/problem/test/*` + - 月誌:`templates/logs-template.md.tmpl` + 不需修改腳本本身即可客製化模板。 + +## 未來可擴充方向 + +- 主 README 自動彙整各題狀態(難度、語言、連結、完成度) +- 加入 CI(如 GitHub Actions)自動跑各題測試 +- 更完整的模板與範例(含 I/O、邊界測資) --- -*最後更新: 2025-09-01* ---- -account : iak64825@jioso.com -password : ww5&Hy73dgh \ No newline at end of file +歡迎依個人習慣調整腳本與模板,持續穩定地練習最重要! diff --git a/leetcode_helper.sh b/leetcode_helper.sh deleted file mode 100755 index 2681409..0000000 --- a/leetcode_helper.sh +++ /dev/null @@ -1,586 +0,0 @@ -# 1. 讓腳本可執行: -# chmod +x leetcode_helper.sh -# 2. 建立新題目: -# ./leetcode_helper.sh problem 1 two-sum Easy -# ./leetcode_helper.sh problem 3000 maximum-area-rectangle Medium -# 3. 建立月度日誌: -# ./leetcode_helper.sh log 2025-09 -# 4. 更新README: -# ./leetcode_helper.sh readme - -#!/bin/bash - -# 建立測試檔案 -function create_test_files() { - local problem_dir="$1" - local number="$2" - local name="$3" - - # 建立 C# 單元測試 (xUnit) - cat > "$problem_dir/test/SolutionTests.cs" << EOF -// LeetCode $number 單元測試 -// 使用 xUnit 框架 - -using Xunit; -using System; -using System.Collections.Generic; - -public class SolutionTests { - private readonly Solution _solution; - - public SolutionTests() { - _solution = new Solution(); - } - - [Fact] - public void TestCase1() { - // Arrange - // TODO: 設定輸入資料 - // var input = ; - // var expected = ; - - // Act - // var result = _solution.YourMethod(input); - - // Assert - // Assert.Equal(expected, result); - - Assert.True(true); // 暫時通過,等待實作 - } - - [Fact] - public void TestCase2() { - // TODO: 第二個測試案例 - Assert.True(true); - } - - [Fact] - public void TestEdgeCases() { - // TODO: 邊界情況測試 - // 空陣列、單一元素、最大值、最小值等 - Assert.True(true); - } -} -EOF - - # 建立 C# 測試專案檔 - cat > "$problem_dir/test/TestProject.csproj" << EOF - - - - net8.0 - enable - false - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - -EOF - - # 建立 Go 測試檔案 - cat > "$problem_dir/test/main_test.go" << EOF -// LeetCode $number Go 單元測試 -package main - -import ( - "testing" - "reflect" -) - -func TestCase1(t *testing.T) { - // TODO: 設定測試資料 - // input := - // expected := - - // result := solve(input) - - // if !reflect.DeepEqual(result, expected) { - // t.Errorf("TestCase1 failed. Expected: %v, Got: %v", expected, result) - // } - - // 暫時通過測試 - if false { - t.Errorf("請實作測試案例") - } -} - -func TestCase2(t *testing.T) { - // TODO: 第二個測試案例 - if false { - t.Errorf("請實作測試案例") - } -} - -func TestEdgeCases(t *testing.T) { - // TODO: 邊界情況測試 - if false { - t.Errorf("請實作邊界測試") - } -} - -// 輔助函數:比較 slice -func equalSlices(a, b []int) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} -EOF - - # 建立測試資料檔案模板 - cat > "$problem_dir/test/input1.txt" << EOF -# 測試案例 1 輸入 -# TODO: 從 LeetCode 複製範例輸入 -EOF - - cat > "$problem_dir/test/output1.txt" << EOF -# 測試案例 1 期望輸出 -# TODO: 從 LeetCode 複製範例輸出 -EOF - - cat > "$problem_dir/test/input2.txt" << EOF -# 測試案例 2 輸入 -# TODO: 加入額外測試案例 -EOF - - cat > "$problem_dir/test/output2.txt" << EOF -# 測試案例 2 期望輸出 -# TODO: 加入額外測試案例期望結果 -EOF - - cat > "$problem_dir/test/edge_cases.md" << EOF -# 邊界情況清單 - -## 需要測試的邊界情況 -- [ ] 空輸入 (空陣列、空字串等) -- [ ] 單一元素 -- [ ] 重複元素 -- [ ] 最大值/最小值 -- [ ] 負數情況 -- [ ] 超大資料量 -- [ ] 特殊字符 (如果是字串題目) - -## 額外測試案例 -### 案例 1: -**輸入**: -**輸出**: -**說明**: - -### 案例 2: -**輸入**: -**輸出**: -**說明**: -EOF - - # 建立測試執行腳本 - cat > "$problem_dir/test/run_tests.sh" << 'EOF' -#!/bin/bash - -echo "🧪 執行 LeetCode 題目測試" -echo "==========================" - -# 執行 C# 測試 -echo "📋 C# 測試結果:" -cd ../csharp -if dotnet build > /dev/null 2>&1; then - cd ../test - if dotnet test --logger "console;verbosity=minimal" 2>/dev/null; then - echo "✅ C# 測試通過" - else - echo "❌ C# 測試失敗" - fi -else - echo "❌ C# 編譯失敗" -fi - -echo "" - -# 執行 Go 測試 -echo "📋 Go 測試結果:" -cd ../go -if go build > /dev/null 2>&1; then - cd ../test - if go test -v > /dev/null 2>&1; then - echo "✅ Go 測試通過" - else - echo "❌ Go 測試失敗" - echo "詳細輸出:" - go test -v - fi -else - echo "❌ Go 編譯失敗" -fi - -echo "" -echo "📊 測試完成!" -EOF - - chmod +x "$problem_dir/test/run_tests.sh" -} - -# create_problem.sh - 建立新題目資料夾和檔案 - -function create_problem() { - local number="$1" - local name="$2" - local difficulty="$3" - - if [[ -z "$number" || -z "$name" || -z "$difficulty" ]]; then - echo "使用方法: create_problem <題號> <題目名稱> <難度>" - echo "範例: create_problem 1 two-sum Easy" - exit 1 - fi - - # 格式化題號 (補零到4位數) - local formatted_number=$(printf "%04d" "$number") - local folder_name="${formatted_number}-${name}" - local problem_dir="problems/${folder_name}" - - # 建立資料夾結構 - mkdir -p "$problem_dir" - mkdir -p "$problem_dir/test" - - # 建立 README.md - cat > "$problem_dir/README.md" << EOF -# [$number] $(echo "$name" | tr '-' ' ' | sed 's/\b\w/\u&/g') - -## 題目資訊 -- **難度**: $difficulty -- **標籤**: -- **題目連結**: [LeetCode](https://leetcode.com/problems/$name/) -- **練習日期**: $(date +%Y-%m-%d) - -## 題目描述 -> - -## 解題思路 - -### 初步分析 -- 這題主要考察什麼概念? -- 有什麼關鍵限制條件? -- 預期時間/空間複雜度? - -### 解法概述 -1. **暴力解法**: - - 思路: - - 時間複雜度:O(?) - - 空間複雜度:O(?) - -2. **優化解法**: - - 思路: - - 時間複雜度:O(?) - - 空間複雜度:O(?) - -## 實作細節 - -### C# 解法 -\`\`\`csharp -// 核心程式碼片段或關鍵邏輯說明 -\`\`\` - -### Go 解法 -\`\`\`go -// 核心程式碼片段或關鍵邏輯說明 -\`\`\` - -## 測試案例 - -### 範例輸入輸出 -\`\`\` -Input: -Output: -Explanation: -\`\`\` - -### 邊界情況 -- [ ] 空陣列/空字串 -- [ ] 單一元素 -- [ ] 最大/最小值 -- [ ] 重複元素 - -## 學習筆記 - -### 今天學到什麼? -- - -### 遇到的困難 -- - -### 改善方向 -- - -### 相關題目 -- [題目編號] 題目名稱 - 相似概念 -- [題目編號] 題目名稱 - 進階版本 - ---- -**總結**: 這題的核心概念是...,適合練習...技巧。 -EOF - - # 建立 C# 專案 - mkdir -p "$problem_dir/csharp" - cd "$problem_dir/csharp" - dotnet new console --force > /dev/null 2>&1 - - # 覆寫自動生成的 Program.cs - cat > "Program.cs" << EOF -// LeetCode $number: $(echo "$name" | tr '-' ' ' | sed 's/\b\w/\u&/g') -// 難度: $difficulty -// 日期: $(date +%Y-%m-%d) - -using System; -using System.Collections.Generic; -using System.Linq; - -public class Solution { - public void Solve() { - // TODO: 實作解法 - Console.WriteLine("Hello LeetCode $number!"); - } -} - -// 測試程式 -public class Program { - public static void Main() { - Solution sol = new Solution(); - sol.Solve(); - - // TODO: 加入測試案例 - // TestCase1(); - // TestCase2(); - } - - // 測試案例模板 - /* - static void TestCase1() { - // Input: - // Expected: - // Actual: - Console.WriteLine("Test 1: "); - } - */ -} -EOF - cd - > /dev/null - - # 建立 Go 解法檔案 - mkdir -p "$problem_dir/go" - cat > "$problem_dir/go/main.go" << EOF -// LeetCode $number: $(echo "$name" | tr '-' ' ' | sed 's/\b\w/\u&/g') -// 難度: $difficulty -// 日期: $(date +%Y-%m-%d) - -package main - -import "fmt" - -// TODO: 實作解法 -func solve() { - fmt.Printf("Hello LeetCode %d!\n", $number) -} - -// 測試程式 -func main() { - solve() - - // TODO: 加入測試案例 - // testCase1() - // testCase2() -} - -// 測試案例模板 -/* -func testCase1() { - // Input: - // Expected: - // Actual: - fmt.Println("Test 1:") -} -*/ -EOF - - # 初始化 Go module - cd "$problem_dir/go" - go mod init "leetcode-$number" > /dev/null 2>&1 - cd - > /dev/null - - # 建立測試檔案 - create_test_files "$problem_dir" "$number" "$name" - - echo "✅ 已建立題目資料夾: $problem_dir" - echo "📁 檔案結構:" - echo "$problem_dir/" - echo "├── README.md" - echo "├── csharp/" - echo "│ ├── Program.cs" - echo "│ └── *.csproj" - echo "├── go/" - echo "│ ├── main.go" - echo "│ └── go.mod" - echo "└── test/" - echo " ├── SolutionTests.cs" - echo " ├── TestProject.csproj" - echo " ├── main_test.go" - echo " ├── input1.txt" - echo " ├── output1.txt" - echo " ├── input2.txt" - echo " ├── output2.txt" - echo " ├── edge_cases.md" - echo " └── run_tests.sh" - echo "" - echo "🚀 執行方式:" - echo "C#: cd $problem_dir/csharp && dotnet run" - echo "Go: cd $problem_dir/go && go run main.go" - echo "測試: cd $problem_dir/test && ./run_tests.sh" -} - -# update_readme.sh - 更新主 README -function update_readme() { - echo "🔄 更新主 README.md..." - - # 計算統計資料 - local total_problems=$(find problems -maxdepth 1 -type d | wc -l) - total_problems=$((total_problems - 1)) # 排除 problems 資料夾本身 - - local completed_problems=$(find problems -name "README.md" -exec grep -l "✅" {} \; | wc -l) - - echo "📊 發現 $total_problems 個題目,其中 $completed_problems 個已完成" - - # 這裡可以加入更複雜的統計和README更新邏輯 - # 暫時只顯示統計結果 -} - -# create_monthly_log.sh - 建立月度日誌 -function create_monthly_log() { - local year_month="$1" - - if [[ -z "$year_month" ]]; then - year_month=$(date +%Y-%m) - fi - - local log_file="logs/${year_month}.md" - - if [[ -f "$log_file" ]]; then - echo "⚠️ 月度日誌已存在: $log_file" - return 1 - fi - - mkdir -p logs - - cat > "$log_file" << EOF -# ${year_month} 學習記錄 - -## 📅 每日練習記錄 - -### Week 1 -| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | -|------|------|------|------|------|------|------| -| | | | | | | | - -### Week 2 -| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | -|------|------|------|------|------|------|------| -| | | | | | | | - -### Week 3 -| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | -|------|------|------|------|------|------|------| -| | | | | | | | - -### Week 4 -| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | -|------|------|------|------|------|------|------| -| | | | | | | | - -## 📈 本月統計 - -### 完成情況 -- **總練習天數**: 天 -- **完成題數**: 題 -- **語言分布**: C# (題), Go (題) -- **難度分布**: Easy (題), Medium (題), Hard (題) - -### 時間投入 -- **總時間**: 小時 -- **平均每題**: 分鐘 -- **每日平均**: 分鐘 - -## 🎯 本月重點學習 - -### 新掌握的技巧 -1. - -### 常犯錯誤分析 -1. - -### 語言學習心得 -- **C#**: -- **Go**: - -## 🔄 遇到的困難與解決 - -### 困難1: -- **問題**: -- **解決**: -- **學習**: - -## 📝 改進計畫 - -### 下月目標 -1. - -### 學習方法調整 -1. - -## 💡 本月金句 -> - ---- -**總結**: -EOF - - echo "✅ 已建立月度日誌: $log_file" -} - -# 主程式 -case "$1" in - "problem") - create_problem "$2" "$3" "$4" - ;; - "readme") - update_readme - ;; - "log") - create_monthly_log "$2" - ;; - *) - echo "LeetCode Practice Helper" - echo "" - echo "使用方法:" - echo " $0 problem <題號> <題目名稱> <難度> # 建立新題目" - echo " $0 readme # 更新主README" - echo " $0 log [年月] # 建立月度日誌" - echo "" - echo "範例:" - echo " $0 problem 1 two-sum Easy" - echo " $0 problem 15 3sum Medium" - echo " $0 log 2025-09" - ;; -esac \ No newline at end of file diff --git a/templates/logs-template.md b/logs/2025-10.md similarity index 53% rename from templates/logs-template.md rename to logs/2025-10.md index c409ad1..dd18d84 100644 --- a/templates/logs-template.md +++ b/logs/2025-10.md @@ -83,4 +83,102 @@ > "演算法不是背出來的,是理解出來的" - 在第15題3Sum卡住兩天後的感悟 --- -**總結**: 八月是很好的開始,從完全新手到能獨立解決中等題目。九月要持續挑戰自己! \ No newline at end of file +**總結**: 八月是很好的開始,從完全新手到能獨立解決中等題目。九月要持續挑戰自己! +# YYYY年M月 學習記錄 + +## 本月設定(目標與主題) +- **主題**:例如 雙指針 / 滑動視窗 / DP / 圖論 +- **目標題數**:X 題(Easy Y / Medium Z / Hard W) +- **投入時間**:每日 ≥ N 分鐘;每週 ≥ M 小時 +- **挑戰**:至少完成 Hard ≥ H 題;每週寫 1 篇總結 + +## 📅 每日練習記錄 + +### Week 1 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 2 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 3 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 4 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +## 🧭 主題覆蓋追蹤(勾選) +- [ ] 陣列/字串(Two Pointers / Sliding Window) +- [ ] 資料結構(Stack/Queue/Heap/Set/Map) +- [ ] 排序與搜尋(Sorting / Binary Search) +- [ ] 數學與位運算(Math / Bit) +- [ ] 動態規劃(DP) +- [ ] 圖論(BFS/DFS/最短路/拓撲) +- [ ] 樹(BST/遍歷/序列化) + +## 📈 本月統計 + +### 完成情況 +- **練習天數**: 天 +- **完成題數**: 題(Easy / Medium / Hard) +- **語言分布**: C# (題), Go (題) +- **連續天數(Streak)**: 天;本月斷點:第 天 + +### 時間投入 +- **總時間**: 小時 +- **平均每題**: 分鐘 +- **每日平均**: 分鐘 + +### 正確率 / 嘗試次數 +- **一次通過**: 題 +- **二次通過**: 題 +- **多次調整**: 題(記錄主因:邊界/複雜度/實作) + +## 🎯 本月重點學習 + +### 新掌握的技巧(至少 3 條) +1. +2. +3. + +### 常見錯誤與對策 +1. 錯誤:;對策: +2. 錯誤:;對策: +3. 錯誤:;對策: + +### 語言心得(實作層面) +- **C#**:LINQ/Span/效能注意點/測試習慣 +- **Go**:slice/map/指標/錯誤處理/benchmark 習慣 + +## 🔄 困難案例復盤 + +### 案例1:題號/主題 +- 問題: +- 嘗試: +- 解決: +- 學習: + +### 案例2:題號/主題 +- 問題: +- 嘗試: +- 解決: +- 學習: + +## 📝 下月計畫 +- **目標題數**: +- **重點主題**: +- **練習節奏**: +- **要避免的坑**: + +## 💡 本月金句 +> + +--- +**總結**:本月最大收穫是 ______,接下來專注 ______。 diff --git a/logs/2025-11.md b/logs/2025-11.md new file mode 100644 index 0000000..b43f8f9 --- /dev/null +++ b/logs/2025-11.md @@ -0,0 +1,99 @@ +# YYYY年M月 學習記錄 + +## 本月設定(目標與主題) +- 主題:雙指針 / 滑動視窗 / DP / 圖論(自訂) +- 目標題數:X 題(Easy Y / Medium Z / Hard W) +- 投入時間:每日 ≥ N 分鐘;每週 ≥ M 小時 +- 挑戰:至少完成 Hard ≥ H 題;每週寫 1 篇總結 + +## 📅 每日練習記錄 + +### Week 1 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 2 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 3 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 4 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +## 🧭 主題覆蓋追蹤(勾選) +- [ ] 陣列/字串(Two Pointers / Sliding Window) +- [ ] 資料結構(Stack/Queue/Heap/Set/Map) +- [ ] 排序與搜尋(Sorting / Binary Search) +- [ ] 數學與位運算(Math / Bit) +- [ ] 動態規劃(DP) +- [ ] 圖論(BFS/DFS/最短路/拓撲) +- [ ] 樹(BST/遍歷/序列化) + +## 📈 本月統計 + +### 完成情況 +- 練習天數:天 +- 完成題數:題(Easy / Medium / Hard) +- 語言分布:C# (題), Go (題) +- 連續天數(Streak):天;本月斷點:第 天 + +### 時間投入 +- 總時間:小時 +- 平均每題:分鐘 +- 每日平均:分鐘 + +### 正確率 / 嘗試次數 +- 一次通過:題 +- 二次通過:題 +- 多次調整:題(主因:邊界/複雜度/實作) + +## 🎯 本月重點學習 + +### 新掌握的技巧(至少 3 條) +1. +2. +3. + +### 常見錯誤與對策 +1. 錯誤:;對策: +2. 錯誤:;對策: +3. 錯誤:;對策: + +### 語言心得(實作層面) +- C#:LINQ/Span/效能注意點/測試習慣 +- Go:slice/map/指標/錯誤處理/benchmark 習慣 + +## 🔄 困難案例復盤 + +### 案例1:題號/主題 +- 問題: +- 嘗試: +- 解決: +- 學習: + +### 案例2:題號/主題 +- 問題: +- 嘗試: +- 解決: +- 學習: + +## 📝 下月計畫 +- 目標題數: +- 重點主題: +- 練習節奏: +- 要避免的坑: + +## 💡 本月金句 +> + +--- +**總結**:本月最大收穫是 ______,接下來專注 ______。 + diff --git a/logs/2025-12.md b/logs/2025-12.md new file mode 100644 index 0000000..b43f8f9 --- /dev/null +++ b/logs/2025-12.md @@ -0,0 +1,99 @@ +# YYYY年M月 學習記錄 + +## 本月設定(目標與主題) +- 主題:雙指針 / 滑動視窗 / DP / 圖論(自訂) +- 目標題數:X 題(Easy Y / Medium Z / Hard W) +- 投入時間:每日 ≥ N 分鐘;每週 ≥ M 小時 +- 挑戰:至少完成 Hard ≥ H 題;每週寫 1 篇總結 + +## 📅 每日練習記錄 + +### Week 1 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 2 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 3 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 4 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +## 🧭 主題覆蓋追蹤(勾選) +- [ ] 陣列/字串(Two Pointers / Sliding Window) +- [ ] 資料結構(Stack/Queue/Heap/Set/Map) +- [ ] 排序與搜尋(Sorting / Binary Search) +- [ ] 數學與位運算(Math / Bit) +- [ ] 動態規劃(DP) +- [ ] 圖論(BFS/DFS/最短路/拓撲) +- [ ] 樹(BST/遍歷/序列化) + +## 📈 本月統計 + +### 完成情況 +- 練習天數:天 +- 完成題數:題(Easy / Medium / Hard) +- 語言分布:C# (題), Go (題) +- 連續天數(Streak):天;本月斷點:第 天 + +### 時間投入 +- 總時間:小時 +- 平均每題:分鐘 +- 每日平均:分鐘 + +### 正確率 / 嘗試次數 +- 一次通過:題 +- 二次通過:題 +- 多次調整:題(主因:邊界/複雜度/實作) + +## 🎯 本月重點學習 + +### 新掌握的技巧(至少 3 條) +1. +2. +3. + +### 常見錯誤與對策 +1. 錯誤:;對策: +2. 錯誤:;對策: +3. 錯誤:;對策: + +### 語言心得(實作層面) +- C#:LINQ/Span/效能注意點/測試習慣 +- Go:slice/map/指標/錯誤處理/benchmark 習慣 + +## 🔄 困難案例復盤 + +### 案例1:題號/主題 +- 問題: +- 嘗試: +- 解決: +- 學習: + +### 案例2:題號/主題 +- 問題: +- 嘗試: +- 解決: +- 學習: + +## 📝 下月計畫 +- 目標題數: +- 重點主題: +- 練習節奏: +- 要避免的坑: + +## 💡 本月金句 +> + +--- +**總結**:本月最大收穫是 ______,接下來專注 ______。 + diff --git a/problems/0042-answer-to-everything/README.md b/problems/0042-answer-to-everything/README.md new file mode 100644 index 0000000..f8180c0 --- /dev/null +++ b/problems/0042-answer-to-everything/README.md @@ -0,0 +1,91 @@ +# [42] Answer To Everything + +## 題目資訊 +- **難度**: Easy +- **標籤**: +- **題目連結**: https://leetcode.com/problems/answer-to-everything/ +- **練習日期**: 2025-09-12 +- **目標複雜度**: 時間 O(?)、空間 O(?) + +## 題目描述 +> 在這裡貼上題目的完整描述(或重點) + +## 先備條件與限制 +- 輸入限制:n ∈ [?, ?]、值域 ∈ [?, ?] +- 回傳/輸出格式:... +- 其他:是否允許排序/就地修改 + +## 解題思路 + +### 初步分析 +- 類型:雙指針 / 滑動視窗 / 排序 / DP / 貪心 / 圖論 ... +- 關鍵觀察: +- 複雜度目標理由: + +### 解法比較 +1. 解法A(基準/暴力): + - 思路: + - 正確性: + - 複雜度:O(?) / O(?) +2. 解法B(優化): + - 思路: + - 正確性: + - 複雜度:O(?) / O(?) + +### 乾跑(Dry Run) +- 範例:... + +## 實作細節與 API 設計 + +### C# 方法簽名(示意) +```csharp +public class Solution { + // TODO: 根據題意調整簽名 + public int Solve(int[] nums) { + return 0; + } +} +``` + +### Go 方法簽名(示意) +```go +func solve(nums []int) int { + return 0 +} +``` + +### 常見陷阱 +- 邊界:空/單一/極值/全相等 +- 去重:排序後跳重複、集合 +- 溢位:使用 64-bit + +## 測試案例 + +### 範例輸入輸出 +``` +Input: ... +Output: ... +Explanation: ... +``` + +### 邊界清單 +- [ ] 空陣列/空字串 +- [ ] 單一元素 / 全相同 +- [ ] 含負數/0/大數 +- [ ] 去重 +- [ ] 大資料壓力 + +## 複雜度分析 +- 最壞:時間 O(?)、空間 O(?) + +## 相關題目 / Follow-up +- + +## 學習筆記 +- 今天學到: +- 卡住與修正: +- 待優化: + +--- +**總結**:這題的核心在於 ______,適合練習 ______。 + diff --git a/problems/0042-answer-to-everything/csharp/Program.cs b/problems/0042-answer-to-everything/csharp/Program.cs new file mode 100644 index 0000000..c5e5c74 --- /dev/null +++ b/problems/0042-answer-to-everything/csharp/Program.cs @@ -0,0 +1,24 @@ +// LeetCode 42: Answer To Everything +// 難度: Easy +// 日期: 2025-09-12 + +using System; +using System.Collections.Generic; +using System.Linq; + +public class Solution { + // TODO: 根據題意調整簽名 + public int Solve(int[] nums) { + // 實作解法 + return 0; + } +} + +public class Program { + public static void Main() { + var s = new Solution(); + // TODO: 可加入簡單輸入/輸出測試 + Console.WriteLine("Hello LeetCode 42!"); + } +} + diff --git a/problems/0042-answer-to-everything/csharp/csharp.csproj b/problems/0042-answer-to-everything/csharp/csharp.csproj new file mode 100644 index 0000000..9dbab49 --- /dev/null +++ b/problems/0042-answer-to-everything/csharp/csharp.csproj @@ -0,0 +1,9 @@ + + + Exe + net8.0 + enable + enable + + + diff --git a/problems/0042-answer-to-everything/go/main.go b/problems/0042-answer-to-everything/go/main.go new file mode 100644 index 0000000..697cedc --- /dev/null +++ b/problems/0042-answer-to-everything/go/main.go @@ -0,0 +1,18 @@ +// LeetCode 42: Answer To Everything +// 難度: Easy +// 日期: 2025-09-12 + +package main + +import "fmt" + +// TODO: 根據題意調整簽名 +func solve(nums []int) int { + return 0 +} + +func main() { + fmt.Printf("Hello LeetCode 42!\n") + // TODO: 可加入簡單測試 +} + diff --git a/problems/0042-answer-to-everything/test/SolutionTests.cs b/problems/0042-answer-to-everything/test/SolutionTests.cs new file mode 100644 index 0000000..c0cfc74 --- /dev/null +++ b/problems/0042-answer-to-everything/test/SolutionTests.cs @@ -0,0 +1,24 @@ +// LeetCode 42 單元測試(xUnit) + +using Xunit; + +public class SolutionTests { + private readonly Solution _s = new Solution(); + + [Fact] + public void Example1() { + // TODO: Arrange + // var input = new int[] { }; + // var expected = 0; + // Act + // var got = _s.Solve(input); + // Assert.Equal(expected, got); + Assert.True(true); + } + + [Fact] + public void EdgeCases() { + Assert.True(true); + } +} + diff --git a/problems/0042-answer-to-everything/test/TestProject.csproj b/problems/0042-answer-to-everything/test/TestProject.csproj new file mode 100644 index 0000000..e2d8b41 --- /dev/null +++ b/problems/0042-answer-to-everything/test/TestProject.csproj @@ -0,0 +1,19 @@ + + + net8.0 + enable + false + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + diff --git a/problems/0042-answer-to-everything/test/edge_cases.md b/problems/0042-answer-to-everything/test/edge_cases.md new file mode 100644 index 0000000..4b1a927 --- /dev/null +++ b/problems/0042-answer-to-everything/test/edge_cases.md @@ -0,0 +1,20 @@ +# 邊界情況清單(42 Answer To Everything) + +## 需要測試的邊界 +- [ ] 空輸入 / 單一元素 +- [ ] 重複元素 / 全相同 +- [ ] 極值(最小/最大) +- [ ] 含負數 / 0 / 大數 +- [ ] 大資料量(接近上限) + +## 額外案例 +### 案例 1 +- 輸入: +- 預期: +- 說明: + +### 案例 2 +- 輸入: +- 預期: +- 說明: + diff --git a/problems/0042-answer-to-everything/test/input1.txt b/problems/0042-answer-to-everything/test/input1.txt new file mode 100644 index 0000000..d066371 --- /dev/null +++ b/problems/0042-answer-to-everything/test/input1.txt @@ -0,0 +1,2 @@ +# 測試案例 1 輸入(42 Answer To Everything) + diff --git a/problems/0042-answer-to-everything/test/input2.txt b/problems/0042-answer-to-everything/test/input2.txt new file mode 100644 index 0000000..bae9ccc --- /dev/null +++ b/problems/0042-answer-to-everything/test/input2.txt @@ -0,0 +1,2 @@ +# 測試案例 2 輸入(42 Answer To Everything) + diff --git a/problems/0042-answer-to-everything/test/main_test.go b/problems/0042-answer-to-everything/test/main_test.go new file mode 100644 index 0000000..8780fa7 --- /dev/null +++ b/problems/0042-answer-to-everything/test/main_test.go @@ -0,0 +1,12 @@ +// LeetCode 42 單元測試(Go testing) +package main + +import "testing" + +func TestExample(t *testing.T) { + // TODO: input := []int{} + // got := solve(input) + // want := 0 + // if got != want { t.Fatalf("want %v got %v", want, got) } +} + diff --git a/problems/0042-answer-to-everything/test/output1.txt b/problems/0042-answer-to-everything/test/output1.txt new file mode 100644 index 0000000..9f6ae41 --- /dev/null +++ b/problems/0042-answer-to-everything/test/output1.txt @@ -0,0 +1,2 @@ +# 測試案例 1 期望輸出(42 Answer To Everything) + diff --git a/problems/0042-answer-to-everything/test/output2.txt b/problems/0042-answer-to-everything/test/output2.txt new file mode 100644 index 0000000..65d613a --- /dev/null +++ b/problems/0042-answer-to-everything/test/output2.txt @@ -0,0 +1,2 @@ +# 測試案例 2 期望輸出(42 Answer To Everything) + diff --git a/problems/0042-answer-to-everything/test/run_tests.sh b/problems/0042-answer-to-everything/test/run_tests.sh new file mode 100755 index 0000000..4dac6e1 --- /dev/null +++ b/problems/0042-answer-to-everything/test/run_tests.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +echo "🧪 執行測試: 0042-answer-to-everything" +echo "==========================" + +pushd "$(dirname "$0")" >/dev/null + +echo "📋 C# 測試結果:" +if (cd ../csharp && dotnet build >/dev/null 2>&1); then + if dotnet test --logger "console;verbosity=minimal" >/dev/null 2>&1; then + echo "✅ C# 測試通過" + else + echo "❌ C# 測試失敗" + fi +else + echo "❌ C# 編譯失敗" +fi + +echo "" +echo "📋 Go 測試結果:" +if (cd ../go && go build >/dev/null 2>&1); then + if go test -v >/dev/null 2>&1; then + echo "✅ Go 測試通過" + else + echo "❌ Go 測試失敗(顯示詳細)" + go test -v + fi +else + echo "❌ Go 編譯失敗" +fi + +popd >/dev/null +echo "" +echo "📊 測試完成!" + diff --git a/templates/logs-template.md.tmpl b/templates/logs-template.md.tmpl new file mode 100644 index 0000000..b43f8f9 --- /dev/null +++ b/templates/logs-template.md.tmpl @@ -0,0 +1,99 @@ +# YYYY年M月 學習記錄 + +## 本月設定(目標與主題) +- 主題:雙指針 / 滑動視窗 / DP / 圖論(自訂) +- 目標題數:X 題(Easy Y / Medium Z / Hard W) +- 投入時間:每日 ≥ N 分鐘;每週 ≥ M 小時 +- 挑戰:至少完成 Hard ≥ H 題;每週寫 1 篇總結 + +## 📅 每日練習記錄 + +### Week 1 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 2 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 3 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 4 (YYYY-MM-DD ~ YYYY-MM-DD) +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +## 🧭 主題覆蓋追蹤(勾選) +- [ ] 陣列/字串(Two Pointers / Sliding Window) +- [ ] 資料結構(Stack/Queue/Heap/Set/Map) +- [ ] 排序與搜尋(Sorting / Binary Search) +- [ ] 數學與位運算(Math / Bit) +- [ ] 動態規劃(DP) +- [ ] 圖論(BFS/DFS/最短路/拓撲) +- [ ] 樹(BST/遍歷/序列化) + +## 📈 本月統計 + +### 完成情況 +- 練習天數:天 +- 完成題數:題(Easy / Medium / Hard) +- 語言分布:C# (題), Go (題) +- 連續天數(Streak):天;本月斷點:第 天 + +### 時間投入 +- 總時間:小時 +- 平均每題:分鐘 +- 每日平均:分鐘 + +### 正確率 / 嘗試次數 +- 一次通過:題 +- 二次通過:題 +- 多次調整:題(主因:邊界/複雜度/實作) + +## 🎯 本月重點學習 + +### 新掌握的技巧(至少 3 條) +1. +2. +3. + +### 常見錯誤與對策 +1. 錯誤:;對策: +2. 錯誤:;對策: +3. 錯誤:;對策: + +### 語言心得(實作層面) +- C#:LINQ/Span/效能注意點/測試習慣 +- Go:slice/map/指標/錯誤處理/benchmark 習慣 + +## 🔄 困難案例復盤 + +### 案例1:題號/主題 +- 問題: +- 嘗試: +- 解決: +- 學習: + +### 案例2:題號/主題 +- 問題: +- 嘗試: +- 解決: +- 學習: + +## 📝 下月計畫 +- 目標題數: +- 重點主題: +- 練習節奏: +- 要避免的坑: + +## 💡 本月金句 +> + +--- +**總結**:本月最大收穫是 ______,接下來專注 ______。 + diff --git a/templates/problem-template.md b/templates/problem-template.md deleted file mode 100644 index aac353d..0000000 --- a/templates/problem-template.md +++ /dev/null @@ -1,73 +0,0 @@ -# [題目編號] 題目名稱 - -## 題目資訊 -- **難度**: Easy/Medium/Hard -- **標籤**: Array, Hash Table, Two Pointers, etc. -- **題目連結**: [LeetCode](https://leetcode.com/problems/xxx/) -- **練習日期**: YYYY-MM-DD - -## 題目描述 -> 在這裡貼上題目的完整描述 - -## 解題思路 - -### 初步分析 -- 這題主要考察什麼概念? -- 有什麼關鍵限制條件? -- 預期時間/空間複雜度? - -### 解法概述 -1. **暴力解法**: - - 思路: - - 時間複雜度:O(?) - - 空間複雜度:O(?) - -2. **優化解法**: - - 思路: - - 時間複雜度:O(?) - - 空間複雜度:O(?) - -## 實作細節 - -### C# 解法 -```csharp -// 核心程式碼片段或關鍵邏輯說明 -``` - -### Go 解法 -```go -// 核心程式碼片段或關鍵邏輯說明 -``` - -## 測試案例 - -### 範例輸入輸出 -``` -Input: -Output: -Explanation: -``` - -### 邊界情況 -- [ ] 空陣列/空字串 -- [ ] 單一元素 -- [ ] 最大/最小值 -- [ ] 重複元素 - -## 學習筆記 - -### 今天學到什麼? -- - -### 遇到的困難 -- - -### 改善方向 -- - -### 相關題目 -- [題目編號] 題目名稱 - 相似概念 -- [題目編號] 題目名稱 - 進階版本 - ---- -**總結**: 這題的核心概念是...,適合練習...技巧。 \ No newline at end of file diff --git a/templates/problem-template.md.tmpl b/templates/problem-template.md.tmpl new file mode 100644 index 0000000..81acaa0 --- /dev/null +++ b/templates/problem-template.md.tmpl @@ -0,0 +1,105 @@ +# [題目編號] 題目名稱 + +## 題目資訊 +- **難度**: Easy/Medium/Hard +- **標籤**: Array, Hash Table, Two Pointers, Sliding Window, Greedy, DP, Graph, Tree, Math, etc. +- **題目連結**: [LeetCode](https://leetcode.com/problems/xxx/) +- **練習日期**: YYYY-MM-DD +- **目標複雜度**: 時間 O(?), 空間 O(?) + +## 題目描述 +> 在這裡貼上題目的完整描述(或摘要) + +## 先備條件與限制 +- 輸入限制:n ∈ [?, ?]、值域 ∈ [?, ?]、是否有重複、是否有負數/零 +- 回傳/輸出格式:... +- 其他:排序是否允許改變輸入、是否需穩定、是否需就地(in-place) + +## 解題思路 + +### 初步分析(快速定位問題類型) +- 問題類型:如 雙指針 / 滑動視窗 / 排序 + 掃描 / 貪心 / DP / 單調結構 / 前綴和 / 二分 / 位運算 ... +- 關鍵觀察:列出 2-4 點(不等式關係、單調性、區間性質、可分解性) +- 目標複雜度理由:為什麼 O(n) 或 O(n log n) 足夠/必要? + +### 解法比較 +1. **解法 A(基準/暴力/直接枚舉)** + - 思路:步驟 1-2-3 + - 正確性:為什麼不會漏/不會重? + - 複雜度:時間 O(?)、空間 O(?) + - 優缺點:易寫/慢;好理解/不通過大資料 + +2. **解法 B(優化:排序/雙指針/結構)** + - 思路:步驟 1-2-3(指出與解法 A 最大差異) + - 正確性:單調性/貪心選擇/狀態定義 的論證要點 + - 複雜度:時間 O(?)、空間 O(?) + - 優缺點:通過大資料/實作較複雜;可讀性需注意 + +3. (可選)**解法 C(進一步優化/其他路線)** + - 思路、正確性、複雜度、優缺點 + +### 乾跑(Dry Run) +使用一組小範例逐步展示索引/指標/狀態如何變化(建議附上 3-5 步驟)。 + +## 實作細節與 API 設計 + +### C# 方法簽名 +```csharp +public class Solution { + // TODO: 根據題意調整簽名 + public int Solve(int[] nums) { + // ... + } +} +``` + +### Go 方法簽名 +```go +// TODO: 根據題意調整簽名 +func solve(nums []int) int { + // ... +} +``` + +### 實作要點 / 常見陷阱 +- 邊界:空集合、單元素、全相等、極值(INT_MIN/INT_MAX) +- 去重:排序後跳重複、集合/哈希去重 +- 早停:提早終止條件(例如 window 擴不動就停) +- 精度/溢位:除法、乘法、64 位整數 + +## 測試案例 + +### 範例輸入輸出 +``` +Input: ... +Output: ... +Explanation: ... +``` + +### 邊界與特殊情況清單 +- [ ] 空陣列/空字串 +- [ ] 單一元素 / 全相同 / 全遞增 / 全遞減 +- [ ] 含負數 / 含 0 / 大數 +- [ ] 有重複值 / 需去重 +- [ ] 大資料壓力測試(n 接近上限) + +### 對拍/隨機測試(可選) +- 與暴力解法對拍 1000 次;隨機生成測資,確保輸出一致 + +## 複雜度分析 +- 最壞:時間 O(?)、空間 O(?) +- 平均:時間 O(?) +- 最佳:時間 O(?)(若有) + +## 相關題目與 Follow-up +- [題目編號] 題目名稱(相同套路) +- Follow-up:若輸入動態變化/資料流版本/線上查詢如何處理? + +## 學習筆記 +- 今天學到: +- 卡住點與修正: +- 下次優化想法: + +--- +**總結**:這題的核心在於 ______,適合練習 ______ 技巧。 + diff --git a/templates/problem/README.md.tmpl b/templates/problem/README.md.tmpl new file mode 100644 index 0000000..346d23e --- /dev/null +++ b/templates/problem/README.md.tmpl @@ -0,0 +1,91 @@ +# [{{NUMBER}}] {{NAME_TITLE}} + +## 題目資訊 +- **難度**: {{DIFFICULTY}} +- **標籤**: +- **題目連結**: https://leetcode.com/problems/{{NAME_SLUG}}/ +- **練習日期**: {{DATE}} +- **目標複雜度**: 時間 O(?)、空間 O(?) + +## 題目描述 +> 在這裡貼上題目的完整描述(或重點) + +## 先備條件與限制 +- 輸入限制:n ∈ [?, ?]、值域 ∈ [?, ?] +- 回傳/輸出格式:... +- 其他:是否允許排序/就地修改 + +## 解題思路 + +### 初步分析 +- 類型:雙指針 / 滑動視窗 / 排序 / DP / 貪心 / 圖論 ... +- 關鍵觀察: +- 複雜度目標理由: + +### 解法比較 +1. 解法A(基準/暴力): + - 思路: + - 正確性: + - 複雜度:O(?) / O(?) +2. 解法B(優化): + - 思路: + - 正確性: + - 複雜度:O(?) / O(?) + +### 乾跑(Dry Run) +- 範例:... + +## 實作細節與 API 設計 + +### C# 方法簽名(示意) +```csharp +public class Solution { + // TODO: 根據題意調整簽名 + public int Solve(int[] nums) { + return 0; + } +} +``` + +### Go 方法簽名(示意) +```go +func solve(nums []int) int { + return 0 +} +``` + +### 常見陷阱 +- 邊界:空/單一/極值/全相等 +- 去重:排序後跳重複、集合 +- 溢位:使用 64-bit + +## 測試案例 + +### 範例輸入輸出 +``` +Input: ... +Output: ... +Explanation: ... +``` + +### 邊界清單 +- [ ] 空陣列/空字串 +- [ ] 單一元素 / 全相同 +- [ ] 含負數/0/大數 +- [ ] 去重 +- [ ] 大資料壓力 + +## 複雜度分析 +- 最壞:時間 O(?)、空間 O(?) + +## 相關題目 / Follow-up +- + +## 學習筆記 +- 今天學到: +- 卡住與修正: +- 待優化: + +--- +**總結**:這題的核心在於 ______,適合練習 ______。 + diff --git a/templates/problem/csharp/Program.cs.tmpl b/templates/problem/csharp/Program.cs.tmpl new file mode 100644 index 0000000..c70034e --- /dev/null +++ b/templates/problem/csharp/Program.cs.tmpl @@ -0,0 +1,24 @@ +// LeetCode {{NUMBER}}: {{NAME_TITLE}} +// 難度: {{DIFFICULTY}} +// 日期: {{DATE}} + +using System; +using System.Collections.Generic; +using System.Linq; + +public class Solution { + // TODO: 根據題意調整簽名 + public int Solve(int[] nums) { + // 實作解法 + return 0; + } +} + +public class Program { + public static void Main() { + var s = new Solution(); + // TODO: 可加入簡單輸入/輸出測試 + Console.WriteLine("Hello LeetCode {{NUMBER}}!"); + } +} + diff --git a/templates/problem/csharp/csharp.csproj.tmpl b/templates/problem/csharp/csharp.csproj.tmpl new file mode 100644 index 0000000..9dbab49 --- /dev/null +++ b/templates/problem/csharp/csharp.csproj.tmpl @@ -0,0 +1,9 @@ + + + Exe + net8.0 + enable + enable + + + diff --git a/templates/problem/go/main.go.tmpl b/templates/problem/go/main.go.tmpl new file mode 100644 index 0000000..73af78c --- /dev/null +++ b/templates/problem/go/main.go.tmpl @@ -0,0 +1,18 @@ +// LeetCode {{NUMBER}}: {{NAME_TITLE}} +// 難度: {{DIFFICULTY}} +// 日期: {{DATE}} + +package main + +import "fmt" + +// TODO: 根據題意調整簽名 +func solve(nums []int) int { + return 0 +} + +func main() { + fmt.Printf("Hello LeetCode {{NUMBER}}!\n") + // TODO: 可加入簡單測試 +} + diff --git a/templates/problem/test/SolutionTests.cs.tmpl b/templates/problem/test/SolutionTests.cs.tmpl new file mode 100644 index 0000000..0b95883 --- /dev/null +++ b/templates/problem/test/SolutionTests.cs.tmpl @@ -0,0 +1,24 @@ +// LeetCode {{NUMBER}} 單元測試(xUnit) + +using Xunit; + +public class SolutionTests { + private readonly Solution _s = new Solution(); + + [Fact] + public void Example1() { + // TODO: Arrange + // var input = new int[] { }; + // var expected = 0; + // Act + // var got = _s.Solve(input); + // Assert.Equal(expected, got); + Assert.True(true); + } + + [Fact] + public void EdgeCases() { + Assert.True(true); + } +} + diff --git a/templates/problem/test/TestProject.csproj.tmpl b/templates/problem/test/TestProject.csproj.tmpl new file mode 100644 index 0000000..e2d8b41 --- /dev/null +++ b/templates/problem/test/TestProject.csproj.tmpl @@ -0,0 +1,19 @@ + + + net8.0 + enable + false + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + diff --git a/templates/problem/test/edge_cases.md.tmpl b/templates/problem/test/edge_cases.md.tmpl new file mode 100644 index 0000000..2717fac --- /dev/null +++ b/templates/problem/test/edge_cases.md.tmpl @@ -0,0 +1,20 @@ +# 邊界情況清單({{NUMBER}} {{NAME_TITLE}}) + +## 需要測試的邊界 +- [ ] 空輸入 / 單一元素 +- [ ] 重複元素 / 全相同 +- [ ] 極值(最小/最大) +- [ ] 含負數 / 0 / 大數 +- [ ] 大資料量(接近上限) + +## 額外案例 +### 案例 1 +- 輸入: +- 預期: +- 說明: + +### 案例 2 +- 輸入: +- 預期: +- 說明: + diff --git a/templates/problem/test/input1.txt.tmpl b/templates/problem/test/input1.txt.tmpl new file mode 100644 index 0000000..8376e10 --- /dev/null +++ b/templates/problem/test/input1.txt.tmpl @@ -0,0 +1,2 @@ +# 測試案例 1 輸入({{NUMBER}} {{NAME_TITLE}}) + diff --git a/templates/problem/test/input2.txt.tmpl b/templates/problem/test/input2.txt.tmpl new file mode 100644 index 0000000..fd143c2 --- /dev/null +++ b/templates/problem/test/input2.txt.tmpl @@ -0,0 +1,2 @@ +# 測試案例 2 輸入({{NUMBER}} {{NAME_TITLE}}) + diff --git a/templates/problem/test/main_test.go.tmpl b/templates/problem/test/main_test.go.tmpl new file mode 100644 index 0000000..a08d6ff --- /dev/null +++ b/templates/problem/test/main_test.go.tmpl @@ -0,0 +1,12 @@ +// LeetCode {{NUMBER}} 單元測試(Go testing) +package main + +import "testing" + +func TestExample(t *testing.T) { + // TODO: input := []int{} + // got := solve(input) + // want := 0 + // if got != want { t.Fatalf("want %v got %v", want, got) } +} + diff --git a/templates/problem/test/output1.txt.tmpl b/templates/problem/test/output1.txt.tmpl new file mode 100644 index 0000000..8fe77b2 --- /dev/null +++ b/templates/problem/test/output1.txt.tmpl @@ -0,0 +1,2 @@ +# 測試案例 1 期望輸出({{NUMBER}} {{NAME_TITLE}}) + diff --git a/templates/problem/test/output2.txt.tmpl b/templates/problem/test/output2.txt.tmpl new file mode 100644 index 0000000..9b4b34f --- /dev/null +++ b/templates/problem/test/output2.txt.tmpl @@ -0,0 +1,2 @@ +# 測試案例 2 期望輸出({{NUMBER}} {{NAME_TITLE}}) + diff --git a/templates/problem/test/run_tests.sh.tmpl b/templates/problem/test/run_tests.sh.tmpl new file mode 100644 index 0000000..cdd6c2f --- /dev/null +++ b/templates/problem/test/run_tests.sh.tmpl @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +echo "🧪 執行測試: {{NUMBER_PAD}}-{{NAME_SLUG}}" +echo "==========================" + +pushd "$(dirname "$0")" >/dev/null + +echo "📋 C# 測試結果:" +if (cd ../csharp && dotnet build >/dev/null 2>&1); then + if dotnet test --logger "console;verbosity=minimal" >/dev/null 2>&1; then + echo "✅ C# 測試通過" + else + echo "❌ C# 測試失敗" + fi +else + echo "❌ C# 編譯失敗" +fi + +echo "" +echo "📋 Go 測試結果:" +if (cd ../go && go build >/dev/null 2>&1); then + if go test -v >/dev/null 2>&1; then + echo "✅ Go 測試通過" + else + echo "❌ Go 測試失敗(顯示詳細)" + go test -v + fi +else + echo "❌ Go 編譯失敗" +fi + +popd >/dev/null +echo "" +echo "📊 測試完成!" + diff --git a/utils/leetcode_helper.sh b/utils/leetcode_helper.sh new file mode 100755 index 0000000..ac4c94d --- /dev/null +++ b/utils/leetcode_helper.sh @@ -0,0 +1,169 @@ +# 1. 讓腳本可執行: +# chmod +x utils/leetcode_helper.sh +# 2. 建立新題目: +# ./utils/leetcode_helper.sh problem 1 two-sum Easy +# ./utils/leetcode_helper.sh problem 3000 maximum-area-rectangle Medium +# 3. 建立月度日誌: +# ./utils/leetcode_helper.sh log 2025-09 +# 4. 更新README: +# ./utils/leetcode_helper.sh readme + +#!/bin/bash +set -euo pipefail + +# 路徑設定 +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +ROOT_DIR=$(cd "$SCRIPT_DIR/.." && pwd) +TEMPLATES_DIR="$ROOT_DIR/templates" + +# kebab-case -> Title Case(two-sum -> Two Sum) +to_title_case() { + local s="${1//-/ }" + echo "$s" | awk '{for(i=1;i<=NF;i++){ $i=toupper(substr($i,1,1)) substr($i,2) } print }' +} + +# 渲染模板到目的檔 +# 變數:{{NUMBER}}, {{NUMBER_PAD}}, {{NAME_SLUG}}, {{NAME_TITLE}}, {{DIFFICULTY}}, {{DATE}}, {{YEAR_MONTH}} +render_template() { + local src="$1"; shift + local dest="$1"; shift + + local number="${1:-}"; shift || true + local number_pad="${1:-}"; shift || true + local name_slug="${1:-}"; shift || true + local name_title="${1:-}"; shift || true + local difficulty="${1:-}"; shift || true + local today="${1:-}"; shift || true + local year_month="${1:-}"; shift || true + + mkdir -p "$(dirname "$dest")" + sed -e "s/{{NUMBER}}/${number}/g" \ + -e "s/{{NUMBER_PAD}}/${number_pad}/g" \ + -e "s/{{NAME_SLUG}}/${name_slug}/g" \ + -e "s/{{NAME_TITLE}}/${name_title}/g" \ + -e "s/{{DIFFICULTY}}/${difficulty}/g" \ + -e "s/{{DATE}}/${today}/g" \ + -e "s/{{YEAR_MONTH}}/${year_month}/g" \ + "$src" > "$dest" +} + +# 建立測試檔案(來自 templates/problem/test/*) +create_test_files() { + local problem_dir="$1"; local number="$2"; local name_slug="$3"; local difficulty="$4" + local number_pad=$(printf "%04d" "$number") + local name_title=$(to_title_case "$name_slug") + local today=$(date +%Y-%m-%d) + local year_month=$(date +%Y-%m) + + render_template "$TEMPLATES_DIR/problem/test/SolutionTests.cs.tmpl" \ + "$problem_dir/test/SolutionTests.cs" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + + render_template "$TEMPLATES_DIR/problem/test/TestProject.csproj.tmpl" \ + "$problem_dir/test/TestProject.csproj" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + + render_template "$TEMPLATES_DIR/problem/test/main_test.go.tmpl" \ + "$problem_dir/test/main_test.go" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + + render_template "$TEMPLATES_DIR/problem/test/edge_cases.md.tmpl" \ + "$problem_dir/test/edge_cases.md" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + + render_template "$TEMPLATES_DIR/problem/test/input1.txt.tmpl" "$problem_dir/test/input1.txt" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + render_template "$TEMPLATES_DIR/problem/test/output1.txt.tmpl" "$problem_dir/test/output1.txt" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + render_template "$TEMPLATES_DIR/problem/test/input2.txt.tmpl" "$problem_dir/test/input2.txt" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + render_template "$TEMPLATES_DIR/problem/test/output2.txt.tmpl" "$problem_dir/test/output2.txt" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + + render_template "$TEMPLATES_DIR/problem/test/run_tests.sh.tmpl" \ + "$problem_dir/test/run_tests.sh" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + chmod +x "$problem_dir/test/run_tests.sh" +} + +# 建立新題目 +create_problem() { + local number="$1"; local name_slug="$2"; local difficulty="$3" + if [[ -z "${number:-}" || -z "${name_slug:-}" || -z "${difficulty:-}" ]]; then + echo "使用方法: $0 problem <題號> <題目名稱-kebab> <難度>"; exit 1 + fi + + local number_pad=$(printf "%04d" "$number") + local folder_name="${number_pad}-${name_slug}" + local problem_dir="$ROOT_DIR/problems/${folder_name}" + local name_title=$(to_title_case "$name_slug") + local today=$(date +%Y-%m-%d) + local year_month=$(date +%Y-%m) + + mkdir -p "$problem_dir/csharp" "$problem_dir/go" "$problem_dir/test" + + # README + render_template "$TEMPLATES_DIR/problem/README.md.tmpl" \ + "$problem_dir/README.md" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + + # C# + render_template "$TEMPLATES_DIR/problem/csharp/Program.cs.tmpl" \ + "$problem_dir/csharp/Program.cs" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + render_template "$TEMPLATES_DIR/problem/csharp/csharp.csproj.tmpl" \ + "$problem_dir/csharp/csharp.csproj" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + + # Go + render_template "$TEMPLATES_DIR/problem/go/main.go.tmpl" \ + "$problem_dir/go/main.go" \ + "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" + (cd "$problem_dir/go" && go mod init "leetcode-$number" >/dev/null 2>&1 || true) + + # Tests + create_test_files "$problem_dir" "$number" "$name_slug" "$difficulty" + + echo "✅ 已建立題目: $problem_dir" + echo "C#: cd problems/${folder_name}/csharp && dotnet run" + echo "Go: cd problems/${folder_name}/go && go run main.go" + echo "測試: cd problems/${folder_name}/test && ./run_tests.sh" +} + +# 更新主 README(目前僅統計列印) +update_readme() { + echo "🔄 更新主 README 統計..." + local total_problems=$(find "$ROOT_DIR/problems" -maxdepth 1 -type d | wc -l) + total_problems=$((total_problems - 1)) + local completed_problems=$(find "$ROOT_DIR/problems" -name "README.md" -exec grep -l "✅" {} \; | wc -l) + echo "📊 題目總數: $total_problems,標記完成: $completed_problems" +} + +# 建立月度日誌(由模板 logs-template.md.tmpl 渲染) +create_monthly_log() { + local year_month="${1:-}" + if [[ -z "$year_month" ]]; then year_month=$(date +%Y-%m); fi + + local dest="$ROOT_DIR/logs/${year_month}.md" + if [[ -f "$dest" ]]; then echo "⚠️ 已存在: $dest"; return 1; fi + mkdir -p "$ROOT_DIR/logs" + render_template "$TEMPLATES_DIR/logs-template.md.tmpl" "$dest" "" "" "" "" "" "" "$year_month" + echo "✅ 已建立月度日誌: $dest" +} + +# 入口 +case "${1:-}" in + problem) + create_problem "${2:-}" "${3:-}" "${4:-}" ;; + readme) + update_readme ;; + log) + create_monthly_log "${2:-}" ;; + *) + echo "LeetCode Practice Helper" + echo "使用:" + echo " $0 problem <題號> <題目名稱-kebab> <難度>" + echo " $0 readme" + echo " $0 log [YYYY-MM]" + ;; +esac