diff --git a/public/py/ch1/compare.py b/public/py/ch1/compare.py new file mode 100644 index 0000000..a82af7c --- /dev/null +++ b/public/py/ch1/compare.py @@ -0,0 +1,32 @@ +""" +性能对比:循环累加 vs 高斯求和公式 +比较两种算法计算 1+2+...+n 的耗时差异 +""" +import time + +def sum_by_loop(n): + """循环累加 O(n)""" + total = 0 + for i in range(1, n + 1): + total += i + return total + +def sum_by_gauss(n): + """高斯求和公式 O(1)""" + return n * (n + 1) // 2 + +# 测试不同规模 +for n in [1000, 10000, 100000, 1000000]: + print(f"\n--- n = {n} ---") + + start = time.time() + r1 = sum_by_loop(n) + t1 = time.time() - start + + start = time.time() + r2 = sum_by_gauss(n) + t2 = time.time() - start + + print(f"循环累加: {t1:.6f} 秒, 结果 = {r1}") + print(f"高斯公式: {t2:.6f} 秒, 结果 = {r2}") + print(f"加速比: {t1/t2:.2f}x") diff --git a/public/py/ch1/first.py b/public/py/ch1/first.py new file mode 100644 index 0000000..81949e4 --- /dev/null +++ b/public/py/ch1/first.py @@ -0,0 +1,22 @@ +""" +递归打印图案 — 演示递归基本结构 +""" +def print_rect(n, current=1): + """递归打印矩形""" + if current > n: + return + print("*" * n) + print_rect(n, current + 1) + +def print_triangle(n, current=1): + """递归打印三角形""" + if current > n: + return + print("*" * current) + print_triangle(n, current + 1) + +if __name__ == "__main__": + print("矩形图案 (5x5):") + print_rect(5) + print("\n三角形图案:") + print_triangle(5) diff --git a/public/py/ch2/allpai/permutations.py b/public/py/ch2/allpai/permutations.py new file mode 100644 index 0000000..18a9921 --- /dev/null +++ b/public/py/ch2/allpai/permutations.py @@ -0,0 +1,34 @@ +""" +全排列生成 — 递归交换法 +生成 n 个元素的所有排列 (n!) +""" + +def permute(arr, start, end): + """递归生成全排列""" + if start == end: + print("".join(map(str, arr))) + else: + for i in range(start, end + 1): + arr[start], arr[i] = arr[i], arr[start] + permute(arr, start + 1, end) + arr[start], arr[i] = arr[i], arr[start] # 回溯 + +def permute_unique(arr, start, end): + """生成去重全排列""" + if start == end: + print("".join(map(str, arr))) + else: + seen = set() + for i in range(start, end + 1): + if arr[i] not in seen: + seen.add(arr[i]) + arr[start], arr[i] = arr[i], arr[start] + permute_unique(arr, start + 1, end) + arr[start], arr[i] = arr[i], arr[start] + +if __name__ == "__main__": + print("全排列 [1,2,3]:") + permute([1, 2, 3], 0, 2) + + print("\n去重全排列 [1,1,2]:") + permute_unique([1, 1, 2], 0, 2) diff --git a/public/py/ch2/digui/print_number.py b/public/py/ch2/digui/print_number.py new file mode 100644 index 0000000..4a549a5 --- /dev/null +++ b/public/py/ch2/digui/print_number.py @@ -0,0 +1,25 @@ +""" +递归示例 — 递归打印整数各位数字 +""" + +def print_digits(n): + """递归按位打印整数(高位到低位)""" + if n < 10: + print(n, end=" ") + else: + print_digits(n // 10) + print(n % 10, end=" ") + +def print_triangle(n, current=1): + """递归打印星号三角形""" + if current > n: + return + print("*" * current) + print_triangle(n, current + 1) + +if __name__ == "__main__": + n = 12345 + print(f"递归打印 {n} 的各位数字:") + print_digits(n) + print("\n\n递归三角形 (n=5):") + print_triangle(5) diff --git a/public/py/ch2/halfsearch/binary_search.py b/public/py/ch2/halfsearch/binary_search.py new file mode 100644 index 0000000..7390595 --- /dev/null +++ b/public/py/ch2/halfsearch/binary_search.py @@ -0,0 +1,43 @@ +""" +二分查找 — 在有序数组中查找目标值 +时间复杂度 O(log n) +""" + +def binary_search_recursive(arr, target, left, right): + """递归版二分查找""" + if left > right: + return -1 + + mid = left + (right - left) // 2 + + if arr[mid] == target: + return mid + elif arr[mid] > target: + return binary_search_recursive(arr, target, left, mid - 1) + else: + return binary_search_recursive(arr, target, mid + 1, right) + +def binary_search_iterative(arr, target): + """迭代版二分查找""" + left, right = 0, len(arr) - 1 + + while left <= right: + mid = left + (right - left) // 2 + if arr[mid] == target: + return mid + elif arr[mid] > target: + right = mid - 1 + else: + left = mid + 1 + + return -1 + +if __name__ == "__main__": + arr = [1, 3, 5, 7, 9, 11, 13, 15] + target = 7 + + idx1 = binary_search_recursive(arr, target, 0, len(arr) - 1) + idx2 = binary_search_iterative(arr, target) + + print(f"数组: {arr}") + print(f"查找 {target}: 递归版结果索引={idx1}, 迭代版结果索引={idx2}") diff --git a/public/py/ch2/mergesort/mergesort.py b/public/py/ch2/mergesort/mergesort.py new file mode 100644 index 0000000..9466e1d --- /dev/null +++ b/public/py/ch2/mergesort/mergesort.py @@ -0,0 +1,50 @@ +""" +归并排序 — 分治法的经典应用 +时间复杂度 O(n log n),空间复杂度 O(n) +""" + +def merge(arr, left, mid, right): + """合并两个有序子数组""" + # 创建临时数组 + L = arr[left:mid + 1] + R = arr[mid + 1:right + 1] + + i = j = 0 + k = left + + # 合并两个有序数组 + while i < len(L) and j < len(R): + if L[i] <= R[j]: + arr[k] = L[i] + i += 1 + else: + arr[k] = R[j] + j += 1 + k += 1 + + # 复制剩余元素 + while i < len(L): + arr[k] = L[i] + i += 1 + k += 1 + while j < len(R): + arr[k] = R[j] + j += 1 + k += 1 + +def merge_sort(arr, left, right): + """归并排序主函数""" + if left < right: + mid = (left + right) // 2 + merge_sort(arr, left, mid) + merge_sort(arr, mid + 1, right) + merge(arr, left, mid, right) + +def sort(arr): + merge_sort(arr, 0, len(arr) - 1) + +if __name__ == "__main__": + data = [38, 27, 43, 3, 9, 82, 10] + print("排序前:", data) + sort(data) + print("排序后:", data) diff --git a/public/py/ch2/quicksort/quicksort.py b/public/py/ch2/quicksort/quicksort.py new file mode 100644 index 0000000..35177e2 --- /dev/null +++ b/public/py/ch2/quicksort/quicksort.py @@ -0,0 +1,33 @@ +""" +快速排序 — 分治策略 +平均时间复杂度 O(n log n),最坏 O(n²) +""" + +def partition(arr, low, high): + """划分:选择基准,将小于基准的放左边,大于的放右边""" + pivot = arr[high] # 选最后一个元素作为基准 + i = low - 1 + + for j in range(low, high): + if arr[j] <= pivot: + i += 1 + arr[i], arr[j] = arr[j], arr[i] + + arr[i + 1], arr[high] = arr[high], arr[i + 1] + return i + 1 + +def quick_sort(arr, low, high): + """快速排序递归实现""" + if low < high: + pi = partition(arr, low, high) + quick_sort(arr, low, pi - 1) + quick_sort(arr, pi + 1, high) + +def sort(arr): + quick_sort(arr, 0, len(arr) - 1) + +if __name__ == "__main__": + data = [10, 7, 8, 9, 1, 5] + print("排序前:", data) + sort(data) + print("排序后:", data) diff --git a/public/py/ch3/bag01/knapsack01.py b/public/py/ch3/bag01/knapsack01.py new file mode 100644 index 0000000..763f9c5 --- /dev/null +++ b/public/py/ch3/bag01/knapsack01.py @@ -0,0 +1,47 @@ +""" +0/1 背包问题 — 动态规划经典问题 +给定容量 W 和价值/重量数组,求最大价值 +""" + +def knapsack_01(weights, values, W): + """0/1背包 DP 解法""" + n = len(weights) + # dp[i][w] 表示前 i 个物品在容量 w 下的最大价值 + dp = [[0] * (W + 1) for _ in range(n + 1)] + + for i in range(1, n + 1): + for w in range(1, W + 1): + if weights[i - 1] <= w: + # 选或不选当前物品 + dp[i][w] = max( + dp[i - 1][w], + dp[i - 1][w - weights[i - 1]] + values[i - 1] + ) + else: + dp[i][w] = dp[i - 1][w] + + return dp[n][W] + +def knapsack_01_optimized(weights, values, W): + """空间优化版:一维数组""" + dp = [0] * (W + 1) + + for i in range(len(weights)): + for w in range(W, weights[i] - 1, -1): + dp[w] = max(dp[w], dp[w - weights[i]] + values[i]) + + return dp[W] + +if __name__ == "__main__": + weights = [2, 3, 4, 5] + values = [3, 4, 5, 6] + W = 8 + + result1 = knapsack_01(weights, values, W) + result2 = knapsack_01_optimized(weights, values, W) + + print(f"物品重量: {weights}") + print(f"物品价值: {values}") + print(f"背包容量: {W}") + print(f"最大价值 (二维DP): {result1}") + print(f"最大价值 (一维优化): {result2}") diff --git a/public/py/ch3/lcs/lcs.py b/public/py/ch3/lcs/lcs.py new file mode 100644 index 0000000..734e81e --- /dev/null +++ b/public/py/ch3/lcs/lcs.py @@ -0,0 +1,47 @@ +""" +最长公共子序列 (LCS) — 动态规划 +比较两个序列的相似度 +""" + +def lcs_length(X, Y): + """计算 LCS 长度并返回 DP 表""" + m, n = len(X), len(Y) + dp = [[0] * (n + 1) for _ in range(m + 1)] + + for i in range(1, m + 1): + for j in range(1, n + 1): + if X[i - 1] == Y[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + 1 + else: + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + + return dp[m][n], dp + +def lcs_backtrack(X, Y, dp): + """回溯找出 LCS 序列""" + i, j = len(X), len(Y) + result = [] + + while i > 0 and j > 0: + if X[i - 1] == Y[j - 1]: + result.append(X[i - 1]) + i -= 1 + j -= 1 + elif dp[i - 1][j] > dp[i][j - 1]: + i -= 1 + else: + j -= 1 + + return ''.join(reversed(result)) + +if __name__ == "__main__": + X = "ABCBDAB" + Y = "BDCABB" + + length, dp_table = lcs_length(X, Y) + lcs_str = lcs_backtrack(X, Y, dp_table) + + print(f"序列 X: {X}") + print(f"序列 Y: {Y}") + print(f"LCS 长度: {length}") + print(f"LCS 序列: {lcs_str}") diff --git a/public/py/ch4/huffman/huffman.py b/public/py/ch4/huffman/huffman.py new file mode 100644 index 0000000..f6ca85e --- /dev/null +++ b/public/py/ch4/huffman/huffman.py @@ -0,0 +1,64 @@ +""" +Huffman 编码 — 贪心算法 +构建最优前缀码实现数据压缩 +""" +import heapq +from collections import Counter + +class HuffmanNode: + def __init__(self, char, freq): + self.char = char + self.freq = freq + self.left = None + self.right = None + + def __lt__(self, other): + return self.freq < other.freq + +def build_huffman_tree(text): + """构建 Huffman 树""" + freq = Counter(text) + heap = [HuffmanNode(char, f) for char, f in freq.items()] + heapq.heapify(heap) + + while len(heap) > 1: + left = heapq.heappop(heap) + right = heapq.heappop(heap) + merged = HuffmanNode(None, left.freq + right.freq) + merged.left = left + merged.right = right + heapq.heappush(heap, merged) + + return heap[0] if heap else None + +def generate_codes(node, prefix="", code_map=None): + """从 Huffman 树生成编码""" + if code_map is None: + code_map = {} + + if node is None: + return code_map + + if node.char is not None: + code_map[node.char] = prefix or "0" + else: + generate_codes(node.left, prefix + "0", code_map) + generate_codes(node.right, prefix + "1", code_map) + + return code_map + +if __name__ == "__main__": + text = "ABRACADABRA" + print(f"原文: {text}") + + root = build_huffman_tree(text) + codes = generate_codes(root) + + print("\nHuffman 编码:") + for char, code in sorted(codes.items()): + print(f" '{char}': {code}") + + encoded = "".join(codes[c] for c in text) + print(f"\n编码结果: {encoded}") + print(f"原始大小: {len(text) * 8} bits") + print(f"压缩大小: {len(encoded)} bits") diff --git a/public/py/ch4/huodong/activity_selection.py b/public/py/ch4/huodong/activity_selection.py new file mode 100644 index 0000000..9b0acc4 --- /dev/null +++ b/public/py/ch4/huodong/activity_selection.py @@ -0,0 +1,30 @@ +""" +活动选择问题 — 贪心算法 +选择最多的不重叠活动 +""" + +def activity_selection(start, finish): + """贪心选择最早结束的活动""" + n = len(start) + # 按结束时间排序 + activities = sorted(zip(start, finish), key=lambda x: x[1]) + + selected = [activities[0]] + last_finish = activities[0][1] + + for i in range(1, n): + if activities[i][0] >= last_finish: + selected.append(activities[i]) + last_finish = activities[i][1] + + return selected + +if __name__ == "__main__": + start = [1, 3, 0, 5, 8, 5] + finish = [2, 4, 6, 7, 9, 9] + + result = activity_selection(start, finish) + print("选择的活动 (开始, 结束):") + for s, f in result: + print(f" [{s}, {f})") + print(f"共 {len(result)} 个活动") diff --git a/public/py/ch4/money/coin_change.py b/public/py/ch4/money/coin_change.py new file mode 100644 index 0000000..5195c5d --- /dev/null +++ b/public/py/ch4/money/coin_change.py @@ -0,0 +1,42 @@ +""" +找零问题 — 贪心算法 +使用最少硬币凑出指定金额 +""" + +def min_coins_greedy(coins, amount): + """贪心找零(硬币面额需满足贪心选择性质)""" + coins_sorted = sorted(coins, reverse=True) + result = [] + remaining = amount + + for coin in coins_sorted: + while remaining >= coin: + result.append(coin) + remaining -= coin + + return result + +def min_coins_dp(coins, amount): + """动态规划找零(通用解法)""" + MAX = float('inf') + dp = [MAX] * (amount + 1) + dp[0] = 0 + + for i in range(1, amount + 1): + for coin in coins: + if i >= coin: + dp[i] = min(dp[i], dp[i - coin] + 1) + + return dp[amount] if dp[amount] != MAX else -1 + +if __name__ == "__main__": + coins = [1, 5, 10, 25] + amount = 63 + + greedy_result = min_coins_greedy(coins, amount) + dp_result = min_coins_dp(coins, amount) + + print(f"硬币面额: {coins}") + print(f"需要凑出: {amount}") + print(f"贪心结果: {greedy_result} (共 {len(greedy_result)} 枚)") + print(f"DP最少数量: {dp_result} 枚") diff --git a/server/server.cjs b/server/server.cjs index 0200067..7ad8a8e 100644 --- a/server/server.cjs +++ b/server/server.cjs @@ -188,6 +188,60 @@ app.post('/api/run-c', (req, res) => { } }) +// 运行 Python 代码 +app.post('/api/run-py', (req, res) => { + const { code } = req.body + if (!code) { + return res.status(400).json({ error: '请提供 Python 代码' }) + } + + // 查找 python3 或 python + let pythonCmd = 'python3' + try { + execSync(`${pythonCmd} --version`, { stdio: 'pipe', timeout: 3000 }) + } catch { + pythonCmd = 'python' + try { + execSync(`${pythonCmd} --version`, { stdio: 'pipe', timeout: 3000 }) + } catch { + return res.status(400).json({ + error: '未找到 Python 解释器', + hint: '请安装 Python 3 并确保在 PATH 环境变量中', + installGuide: [ + '--- 安装 Python ---', + '1. 访问 https://www.python.org/downloads/ 下载并安装 Python 3', + '2. 安装时勾选 "Add Python to PATH"', + '3. 安装后重启终端', + ].join('\n') + }) + } + } + + const tmpDir = path.join(__dirname, '..', 'tmp') + if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir, { recursive: true }) + } + + const srcFile = path.join(tmpDir, `code_${Date.now()}.py`) + + try { + fs.writeFileSync(srcFile, code, 'utf-8') + const output = execSync(`"${pythonCmd}" "${srcFile}"`, { + timeout: 15000, + cwd: tmpDir, + encoding: 'utf-8' + }) + res.json({ output }) + } catch (runErr) { + res.json({ + output: runErr.stdout || runErr.message, + isError: true + }) + } finally { + try { if (fs.existsSync(srcFile)) fs.unlinkSync(srcFile) } catch {} + } +}) + // 健康检查 app.get('/api/status', (req, res) => { const compiler = findCompiler() diff --git a/src/components/CodeViewer.vue b/src/components/CodeViewer.vue index e36211b..071e9ca 100644 --- a/src/components/CodeViewer.vue +++ b/src/components/CodeViewer.vue @@ -27,7 +27,7 @@ class="tool-btn run-btn" @click="runCode" :disabled="running" - :title="running ? '正在编译运行...' : '编译并运行 C 代码'" + :title="running ? '正在运行...' : `运行${language === 'python' ? ' Python' : ' C'}代码`" > {{ running ? '⏳' : '▶' }} 运行 @@ -66,13 +66,14 @@