add python code
This commit is contained in:
@@ -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")
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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}")
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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}")
|
||||
@@ -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}")
|
||||
@@ -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")
|
||||
@@ -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)} 个活动")
|
||||
@@ -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} 枚")
|
||||
@@ -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()
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
class="tool-btn run-btn"
|
||||
@click="runCode"
|
||||
:disabled="running"
|
||||
:title="running ? '正在编译运行...' : '编译并运行 C 代码'"
|
||||
:title="running ? '正在运行...' : `运行${language === 'python' ? ' Python' : ' C'}代码`"
|
||||
>
|
||||
{{ running ? '⏳' : '▶' }} 运行
|
||||
</button>
|
||||
@@ -66,13 +66,14 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { loadCodeFile, highlightC } from '../utils/codeUtils.js'
|
||||
import { loadCodeFile, highlightC, highlightPython } from '../utils/codeUtils.js'
|
||||
|
||||
const props = defineProps({
|
||||
filePath: String,
|
||||
fileName: String,
|
||||
fileLabel: String,
|
||||
description: String
|
||||
description: String,
|
||||
language: { type: String, default: 'c' } // 'c' | 'python'
|
||||
})
|
||||
|
||||
const codeContent = ref('')
|
||||
@@ -90,6 +91,7 @@ const FONT_STEP = 2
|
||||
|
||||
const highlightedCode = computed(() => {
|
||||
if (!codeContent.value) return ''
|
||||
if (props.language === 'python') return highlightPython(codeContent.value)
|
||||
return highlightC(codeContent.value)
|
||||
})
|
||||
|
||||
@@ -166,8 +168,10 @@ async function runCode() {
|
||||
runOutput.value = null
|
||||
outputCollapsed.value = false
|
||||
|
||||
const apiEndpoint = props.language === 'python' ? '/api/run-py' : '/api/run-c'
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/run-c', {
|
||||
const res = await fetch(apiEndpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ code: codeContent.value })
|
||||
|
||||
+12
-12
@@ -17,8 +17,8 @@ export const chapters = [
|
||||
'实验对比分析方法'
|
||||
],
|
||||
files: [
|
||||
{ path: 'c/ch1/first.c', name: 'first.c', label: '递归打印图案', description: '递归打印矩形/三角形图案,演示递归基本结构' },
|
||||
{ path: 'c/ch1/compare.c', name: 'compare.c', label: '性能对比(累加 vs 高斯公式)', description: '比较循环累加与高斯求和公式的耗时差异,直观感受算法效率' },
|
||||
{ path: 'c/ch1/first.c', name: 'first.c', label: '递归打印图案', description: '递归打印矩形/三角形图案,演示递归基本结构', pyPath: 'py/ch1/first.py' },
|
||||
{ path: 'c/ch1/compare.c', name: 'compare.c', label: '性能对比(累加 vs 高斯公式)', description: '比较循环累加与高斯求和公式的耗时差异,直观感受算法效率', pyPath: 'py/ch1/compare.py' },
|
||||
{ path: 'c/ch1/compareall.c', name: 'compareall.c', label: '多项性能对比', description: '更多累加与高斯公式的计时对比实验' }
|
||||
]
|
||||
},
|
||||
@@ -43,7 +43,7 @@ export const chapters = [
|
||||
name: 'mergesort',
|
||||
label: '归并排序',
|
||||
files: [
|
||||
{ path: 'c/ch2/mergesort/guibing.c', name: 'guibing.c', label: '归并排序完整实现', description: '分治归并排序,包含 merge 与 mergeSort 函数' },
|
||||
{ path: 'c/ch2/mergesort/guibing.c', name: 'guibing.c', label: '归并排序完整实现', description: '分治归并排序,包含 merge 与 mergeSort 函数', pyPath: 'py/ch2/mergesort/mergesort.py' },
|
||||
{ path: 'c/ch2/mergesort/merge.c', name: 'merge.c', label: '归并操作', description: '合并两个有序子数组的核心 merge 操作' }
|
||||
],
|
||||
demo: { path: '/sort-demo#merge', label: '🎬 动态演示', description: '观看归并排序的完整动画过程' }
|
||||
@@ -52,7 +52,7 @@ export const chapters = [
|
||||
name: 'quicksort',
|
||||
label: '快速排序',
|
||||
files: [
|
||||
{ path: 'c/ch2/quicksort/danppt.c', name: 'danppt.c', label: '快速排序(单侧指针)', description: '单侧指针遍历划分的快速排序实现' },
|
||||
{ path: 'c/ch2/quicksort/danppt.c', name: 'danppt.c', label: '快速排序(单侧指针)', description: '单侧指针遍历划分的快速排序实现', pyPath: 'py/ch2/quicksort/quicksort.py' },
|
||||
{ path: 'c/ch2/quicksort/danleft.c', name: 'danleft.c', label: '快速排序(左指针法)', description: '基于左右指针移动的快速排序变体' },
|
||||
{ path: 'c/ch2/quicksort/dantwo.c', name: 'dantwo.c', label: '快速排序(双指针法)', description: '另一种双指针快速排序实现' },
|
||||
{ path: 'c/ch2/quicksort/shuangbian.c', name: 'shuangbian.c', label: '快速排序(双边扫描)', description: '经典的双边扫描分区快速排序' }
|
||||
@@ -63,7 +63,7 @@ export const chapters = [
|
||||
name: 'halfsearch',
|
||||
label: '二分查找',
|
||||
files: [
|
||||
{ path: 'c/ch2/student/halfsearch.c', name: 'halfsearch.c', label: '二分查找(递归版)', description: '递归实现的二分查找算法' },
|
||||
{ path: 'c/ch2/student/halfsearch.c', name: 'halfsearch.c', label: '二分查找(递归版)', description: '递归实现的二分查找算法', pyPath: 'py/ch2/halfsearch/binary_search.py' },
|
||||
{ path: 'c/ch2/student/halfsearchnew.c', name: 'halfsearchnew.c', label: '二分查找(迭代版)', description: '迭代实现的二分查找算法' },
|
||||
{ path: 'c/ch2/halfsearch/fenzhi.c', name: 'fenzhi.c', label: '分治分割示例', description: '二分分治分割示例' },
|
||||
{ path: 'c/ch2/halfsearch/fenzhirec.c', name: 'fenzhirec.c', label: '递归查找', description: '递归结构的查找实现' }
|
||||
@@ -90,7 +90,7 @@ export const chapters = [
|
||||
name: 'digui',
|
||||
label: '递归示例',
|
||||
files: [
|
||||
{ path: 'c/ch2/digui/printnumber.c', name: 'printnumber.c', label: '递归打印整数', description: '递归按位打印整数(高位到低位)' },
|
||||
{ path: 'c/ch2/digui/printnumber.c', name: 'printnumber.c', label: '递归打印整数', description: '递归按位打印整数(高位到低位)', pyPath: 'py/ch2/digui/print_number.py' },
|
||||
{ path: 'c/ch2/digui/tuxing.c', name: 'tuxing.c', label: '递归图形', description: '递归绘制图形示例' },
|
||||
{ path: 'c/ch2/shangji/findarrmax.c', name: 'findarrmax.c', label: '递归求最大值', description: '递归查找数组最大值' },
|
||||
{ path: 'c/ch2/shangji/sanjiao.c', name: 'sanjiao.c', label: '递归三角形', description: '递归打印星号三角形' }
|
||||
@@ -107,7 +107,7 @@ export const chapters = [
|
||||
name: 'allpai',
|
||||
label: '全排列',
|
||||
files: [
|
||||
{ path: 'c/ch2/ch2.allpai/allpai.c', name: 'allpai.c', label: '全排列生成', description: '递归交换法全排列生成' },
|
||||
{ path: 'c/ch2/ch2.allpai/allpai.c', name: 'allpai.c', label: '全排列生成', description: '递归交换法全排列生成', pyPath: 'py/ch2/allpai/permutations.py' },
|
||||
{ path: 'c/ch2/ch2.allpai/allpaichong.c', name: 'allpaichong.c', label: '全排列(去重)', description: '带去重逻辑的全排列生成' },
|
||||
{ path: 'c/ch2/ch2.allpai/arrpaichong.c', name: 'arrpaichong.c', label: '数组排列去重', description: '数组排列生成与去重' },
|
||||
{ path: 'c/ch2/student/quanpai.c', name: 'quanpai.c', label: '全排列(学生版)', description: '全排列/排列打印程序' },
|
||||
@@ -145,7 +145,7 @@ export const chapters = [
|
||||
name: 'bag01',
|
||||
label: '0/1背包问题',
|
||||
files: [
|
||||
{ path: 'c/ch3/bag01/bag01.c', name: 'bag01.c', label: '0/1背包 DP 实现', description: '经典二维DP矩阵解法' },
|
||||
{ path: 'c/ch3/bag01/bag01.c', name: 'bag01.c', label: '0/1背包 DP 实现', description: '经典二维DP矩阵解法', pyPath: 'py/ch3/bag01/knapsack01.py' },
|
||||
{ path: 'c/ch3/bag01/bagbag.c', name: 'bagbag.c', label: '背包状态追踪', description: '多种背包实现/状态追踪' },
|
||||
{ path: 'c/ch3/bag01/bagevery.c', name: 'bagevery.c', label: '背包枚举', description: '背包问题枚举/遍历示例' },
|
||||
{ path: 'c/ch3/bag01/yanghui.c', name: 'yanghui.c', label: '杨辉三角(二维数组)', description: '使用二维数组打印杨辉三角' },
|
||||
@@ -156,7 +156,7 @@ export const chapters = [
|
||||
name: 'lcs',
|
||||
label: '最长公共子序列(LCS)',
|
||||
files: [
|
||||
{ path: 'c/ch3/lcs/lcs1.c', name: 'lcs1.c', label: 'LCS 实现(一)', description: '构建LCS DP表' },
|
||||
{ path: 'c/ch3/lcs/lcs1.c', name: 'lcs1.c', label: 'LCS 实现(一)', description: '构建LCS DP表', pyPath: 'py/ch3/lcs/lcs.py' },
|
||||
{ path: 'c/ch3/lcs/lcs2.c', name: 'lcs2.c', label: 'LCS 实现(二)', description: 'LCS动态规划实现,返回长度' },
|
||||
{ path: 'c/ch3/lcs/printtable.c', name: 'printtable.c', label: 'LCS 路径回溯', description: '打印LCS路径表并回溯输出LCS' }
|
||||
]
|
||||
@@ -210,14 +210,14 @@ export const chapters = [
|
||||
name: 'huodong',
|
||||
label: '活动选择',
|
||||
files: [
|
||||
{ path: 'c/ch4/huodong/fenpei.c', name: 'fenpei.c', label: '活动分配', description: '活动选择问题的贪心实现' }
|
||||
{ path: 'c/ch4/huodong/fenpei.c', name: 'fenpei.c', label: '活动分配', description: '活动选择问题的贪心实现', pyPath: 'py/ch4/huodong/activity_selection.py' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'huffman',
|
||||
label: 'Huffman编码',
|
||||
files: [
|
||||
{ path: 'c/ch4/huffman/halfall.c', name: 'halfall.c', label: 'Huffman编码主流程', description: '构建Huffman树并生成编码' },
|
||||
{ path: 'c/ch4/huffman/halfall.c', name: 'halfall.c', label: 'Huffman编码主流程', description: '构建Huffman树并生成编码', pyPath: 'py/ch4/huffman/huffman.py' },
|
||||
{ path: 'c/ch4/huffman/halfcode.c', name: 'halfcode.c', label: 'Huffman编码生成', description: '从Huffman树回溯得到编码' },
|
||||
{ path: 'c/ch4/huffman/halftree.c', name: 'halftree.c', label: 'Huffman树构建', description: '节点创建、合并、排序构建Huffman树' },
|
||||
{ path: 'c/ch4/huffman/treelist.c', name: 'treelist.c', label: 'Huffman节点管理', description: '树节点创建和列表操作' }
|
||||
@@ -243,7 +243,7 @@ export const chapters = [
|
||||
name: 'money',
|
||||
label: '找零问题',
|
||||
files: [
|
||||
{ path: 'c/ch4/money/getmoney.c', name: 'getmoney.c', label: '贪心找零', description: '按面额计算所需最少张数' },
|
||||
{ path: 'c/ch4/money/getmoney.c', name: 'getmoney.c', label: '贪心找零', description: '按面额计算所需最少张数', pyPath: 'py/ch4/money/coin_change.py' },
|
||||
{ path: 'c/ch4/money/moneytwowei.c', name: 'moneytwowei.c', label: '找零(二维)', description: '面额分解的另一版本' }
|
||||
]
|
||||
},
|
||||
|
||||
@@ -140,6 +140,152 @@ export function highlightC(code) {
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Python 语法高亮
|
||||
*/
|
||||
export function highlightPython(code) {
|
||||
const keywords = new Set([
|
||||
'False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await',
|
||||
'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except',
|
||||
'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
|
||||
'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return',
|
||||
'try', 'while', 'with', 'yield', 'print', 'range', 'len', 'int',
|
||||
'float', 'str', 'list', 'dict', 'set', 'tuple', 'bool', 'input',
|
||||
'enumerate', 'zip', 'map', 'filter', 'sorted', 'reversed', 'open',
|
||||
'self', 'cls', 'super', 'property', 'staticmethod', 'classmethod',
|
||||
'TypeVar', 'Optional', 'List', 'Dict', 'Tuple', 'Any', 'Union'
|
||||
])
|
||||
|
||||
const builtins = new Set([
|
||||
'print', 'range', 'len', 'int', 'float', 'str', 'list', 'dict',
|
||||
'set', 'tuple', 'bool', 'input', 'enumerate', 'zip', 'map',
|
||||
'filter', 'sorted', 'reversed', 'open', 'type', 'isinstance',
|
||||
'hasattr', 'getattr', 'setattr', 'abs', 'max', 'min', 'sum',
|
||||
'all', 'any', 'ord', 'chr', 'repr', 'format', 'id', 'hash'
|
||||
])
|
||||
|
||||
let result = ''
|
||||
let i = 0
|
||||
const len = code.length
|
||||
|
||||
while (i < len) {
|
||||
// 单行注释 #
|
||||
if (code[i] === '#') {
|
||||
let end = i + 1
|
||||
while (end < len && code[end] !== '\n') end++
|
||||
result += '<span class="hl-comment">' + escapeHtml(code.slice(i, end)) + '</span>'
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// 多行注释/文档字符串 """...""" 或 '''...'''
|
||||
if ((code[i] === '"' && i + 2 < len && code[i + 1] === '"' && code[i + 2] === '"') ||
|
||||
(code[i] === "'" && i + 2 < len && code[i + 1] === "'" && code[i + 2] === "'")) {
|
||||
const quote = code.slice(i, i + 3)
|
||||
let end = i + 3
|
||||
while (end + 2 < len && code.slice(end, end + 3) !== quote) {
|
||||
if (code[end] === '\\') end++
|
||||
end++
|
||||
}
|
||||
if (end + 2 < len) end += 3
|
||||
result += '<span class="hl-comment">' + escapeHtml(code.slice(i, end)) + '</span>'
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// 双引号字符串 "..."
|
||||
if (code[i] === '"') {
|
||||
let end = i + 1
|
||||
while (end < len && code[end] !== '"') {
|
||||
if (code[end] === '\\') end++
|
||||
end++
|
||||
}
|
||||
if (end < len) end++
|
||||
result += '<span class="hl-string">' + escapeHtml(code.slice(i, end)) + '</span>'
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// 单引号字符串 '...'
|
||||
if (code[i] === "'") {
|
||||
let end = i + 1
|
||||
while (end < len && code[end] !== "'") {
|
||||
if (code[end] === '\\') end++
|
||||
end++
|
||||
}
|
||||
if (end < len) end++
|
||||
result += '<span class="hl-string">' + escapeHtml(code.slice(i, end)) + '</span>'
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// f-string f"..."
|
||||
if (code[i] === 'f' && i + 1 < len && code[i + 1] === '"') {
|
||||
let end = i + 2
|
||||
const stack = []
|
||||
while (end < len && code[end] !== '"') {
|
||||
if (code[end] === '{') stack.push(end)
|
||||
else if (code[end] === '}' && stack.length) stack.pop()
|
||||
if (code[end] === '\\') end++
|
||||
end++
|
||||
}
|
||||
if (end < len) end++
|
||||
result += '<span class="hl-string">' + escapeHtml(code.slice(i, end)) + '</span>'
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// 数字
|
||||
if (isDigit(code[i]) || (code[i] === '.' && i + 1 < len && isDigit(code[i + 1]))) {
|
||||
let start = i
|
||||
while (i < len && (isDigit(code[i]) || code[i] === '.' || code[i] === 'e' || code[i] === 'E' ||
|
||||
code[i] === 'x' || code[i] === 'X' || code[i] === 'o' || code[i] === 'O' ||
|
||||
code[i] === 'b' || code[i] === 'B' || code[i] === '+' || code[i] === '-' ||
|
||||
(code[i] >= 'a' && code[i] <= 'f') || (code[i] >= 'A' && code[i] <= 'F'))) {
|
||||
// 简单数字检测,遇到非数字字符停止
|
||||
if (isWordChar(code[i]) && !isDigit(code[i]) &&
|
||||
!(code[i] >= 'a' && code[i] <= 'f') && !(code[i] >= 'A' && code[i] <= 'F') &&
|
||||
code[i] !== 'x' && code[i] !== 'X' && code[i] !== 'o' && code[i] !== 'O' &&
|
||||
code[i] !== 'b' && code[i] !== 'B' && code[i] !== 'e' && code[i] !== 'E' &&
|
||||
code[i] !== '.' && code[i] !== '+' && code[i] !== '-') break
|
||||
i++
|
||||
}
|
||||
result += '<span class="hl-number">' + escapeHtml(code.slice(start, i)) + '</span>'
|
||||
continue
|
||||
}
|
||||
|
||||
// @decorator
|
||||
if (code[i] === '@' && i + 1 < len && isWordStart(code[i + 1])) {
|
||||
let end = i + 1
|
||||
while (end < len && isWordChar(code[end])) end++
|
||||
result += '<span class="hl-preprocessor">' + escapeHtml(code.slice(i, end)) + '</span>'
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
|
||||
// 标识符/关键字
|
||||
if (isWordStart(code[i])) {
|
||||
let start = i
|
||||
while (i < len && isWordChar(code[i])) i++
|
||||
const word = code.slice(start, i)
|
||||
if (keywords.has(word)) {
|
||||
result += '<span class="hl-keyword">' + escapeHtml(word) + '</span>'
|
||||
} else if (builtins.has(word)) {
|
||||
result += '<span class="hl-preprocessor">' + escapeHtml(word) + '</span>'
|
||||
} else {
|
||||
result += escapeHtml(word)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 其他字符
|
||||
result += escapeHtml(code[i])
|
||||
i++
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
|
||||
@@ -77,14 +77,28 @@
|
||||
<div class="drawer-panel">
|
||||
<div class="drawer-header">
|
||||
<span class="drawer-title">💻 代码查看</span>
|
||||
<div class="drawer-lang-tabs">
|
||||
<button
|
||||
class="lang-tab"
|
||||
:class="{ active: language === 'c' }"
|
||||
@click="switchLanguage('c')"
|
||||
>C</button>
|
||||
<button
|
||||
class="lang-tab"
|
||||
:class="{ active: language === 'python' }"
|
||||
@click="switchLanguage('python')"
|
||||
>Python</button>
|
||||
</div>
|
||||
<button class="drawer-close-btn" @click="closeDrawer">✕</button>
|
||||
</div>
|
||||
<div class="drawer-body">
|
||||
<CodeViewer
|
||||
:filePath="currentFile.path"
|
||||
:fileName="currentFile.name"
|
||||
:key="currentFilePath"
|
||||
:filePath="currentFilePath"
|
||||
:fileName="currentFileName"
|
||||
:fileLabel="currentFile.label"
|
||||
:description="currentFile.description"
|
||||
:language="language"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -113,6 +127,30 @@ const props = defineProps({
|
||||
const route = useRoute()
|
||||
const chapterId = computed(() => props.id || route.params.id)
|
||||
const currentFile = ref(null)
|
||||
const language = ref('c')
|
||||
|
||||
// 根据语言生成文件路径
|
||||
const currentFilePath = computed(() => {
|
||||
if (!currentFile.value) return null
|
||||
if (language.value === 'python') {
|
||||
return currentFile.value.pyPath || currentFile.value.path.replace(/^c\//, 'py/').replace(/\.c$/, '.py')
|
||||
}
|
||||
return currentFile.value.path
|
||||
})
|
||||
|
||||
const currentFileName = computed(() => {
|
||||
if (!currentFile.value) return ''
|
||||
if (language.value === 'python') {
|
||||
return currentFile.value.pyPath
|
||||
? currentFile.value.pyPath.split('/').pop()
|
||||
: currentFile.value.name.replace(/\.c$/, '.py')
|
||||
}
|
||||
return currentFile.value.name
|
||||
})
|
||||
|
||||
function switchLanguage(lang) {
|
||||
language.value = lang
|
||||
}
|
||||
|
||||
const chapter = computed(() => getChapterById(chapterId.value))
|
||||
|
||||
@@ -127,6 +165,7 @@ function selectFile(file) {
|
||||
|
||||
function closeDrawer() {
|
||||
currentFile.value = null
|
||||
language.value = 'c'
|
||||
}
|
||||
|
||||
function getFolderDescription(folderName) {
|
||||
@@ -383,7 +422,7 @@ function getFolderDescription(folderName) {
|
||||
.drawer-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
flex-shrink: 0;
|
||||
@@ -393,6 +432,38 @@ function getFolderDescription(folderName) {
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.drawer-lang-tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
background: var(--hover-bg);
|
||||
padding: 3px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.lang-tab {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
transition: all 0.15s;
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.lang-tab:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.lang-tab.active {
|
||||
background: var(--bg);
|
||||
color: var(--primary-color);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.drawer-close-btn {
|
||||
|
||||
Reference in New Issue
Block a user