旅游推荐
基于 Top-K 堆算法的个性化景点推荐,评分可解释,支持类别与关键词检索
基于 Rust 自定义数据结构与算法,覆盖推荐、路线规划、
设施查询、旅游日记、社区搜索与美食推荐的全旅程平台
满足数据结构课程全部要求,覆盖完整旅行生命周期
基于 Top-K 堆算法的个性化景点推荐,评分可解释,支持类别与关键词检索
Dijkstra 最短路径 + 最近邻启发式多点旅游路线,2-opt 局部优化
有界 Dijkstra 扩展,基于图路径距离(非欧氏)的周边设施分类搜索
日记创建与管理,支持 Huffman 无损压缩存储,降低磁盘占用
2-gram 倒排索引全文搜索,综合 gram 命中、评分与热度的复合排名
菜系偏好 × 图路径距离 × 热度融合的 Top-K 美食排名推荐
五层分层设计,职责边界清晰,算法层完全自实现
全部以 Rust 自定义实现,不依赖标准库等价物
Dijkstra 优先队列与 Top-K 选择的核心支撑。维护最小元素在根部,push/pop 均 O(log n)。
struct MinHeap<T: Ord> { data: Vec<T> }
impl<T: Ord> MinHeap<T> {
fn push(&mut self, val: T) {
self.data.push(val);
self.sift_up(self.data.len() - 1);
}
fn pop(&mut self) -> Option<T> {
if self.data.is_empty() { return None; }
let last = self.data.pop().unwrap();
if self.data.is_empty() { return Some(last); }
let min = std::mem::replace(&mut self.data[0], last);
self.sift_down(0);
Some(min)
}
fn sift_up(&mut self, mut i: usize) {
while i > 0 {
let p = (i - 1) / 2;
if self.data[p] <= self.data[i] { break; }
self.data.swap(p, i);
i = p;
}
}
}
支持有向/无向加权边,Dijkstra 最短路径与有界扩展均基于此图实现。
struct Graph {
adj: Vec<Vec<(usize, f64)>>,
node_count: usize,
}
fn shortest_path(&self, start: usize, goal: usize)
-> Option<PathResult>
{
let mut dist = vec![f64::INFINITY; self.node_count];
let mut prev = vec![None; self.node_count];
let mut heap = MinHeap::new();
dist[start] = 0.0;
heap.push((OrderedFloat(0.0), start));
while let Some((d, u)) = heap.pop() {
if u == goal { return Some(reconstruct(prev, u)); }
if d.0 > dist[u] { continue; }
for &(v, w) in &self.adj[u] {
let nd = dist[u] + w;
if nd < dist[v] {
dist[v] = nd;
prev[v] = Some(u);
heap.push((OrderedFloat(nd), v));
}
}
}
None
}
邻接表结构示意 — 每个节点维护一个 (邻居, 权重) 列表
将文本按滑动窗口切分为字符对,建立 gram → 文档列表的倒排映射,支持模糊全文检索。
fn build_index(docs: &[DiaryEntry])
-> HashMap<String, Vec<usize>>
{
let mut idx: HashMap<String, Vec<usize>>
= HashMap::new();
for (id, doc) in docs.iter().enumerate() {
let text = format!("{} {}", doc.title, doc.content);
let chars: Vec<char> = text.chars().collect();
for w in chars.windows(2) {
let gram: String = w.iter().collect();
idx.entry(gram).or_default().push(id);
}
}
idx
}
// 评分:融合相关性 + 热度 + 评分
score = gram_hits * 1.5
+ if title_match { 2.0 } else { 0.0 }
+ diary.rating * 2.0
+ (diary.views as f64 + 1.0).ln();
搜索「旅行」的倒排检索与评分过程
对日记内容按字符频率进行无损压缩,高频字符编码短,低频字符编码长,最优前缀编码。
fn build_huffman_tree(
freq: &HashMap<char, usize>
) -> HuffmanNode {
// 用 MinHeap 以频率为键构建优先队列
let mut heap = MinHeap::new();
for (&ch, &f) in freq {
heap.push(HuffmanNode::leaf(ch, f));
}
// 贪心合并:每次取两个最小节点合并
while heap.len() > 1 {
let a = heap.pop().unwrap();
let b = heap.pop().unwrap();
heap.push(HuffmanNode::merge(a, b));
}
heap.pop().unwrap() // 根节点即完整编码树
}
字符串 "TRAVEL" 的 Huffman 编码树(叶节点显示编码)
交互式逐步动画,直观感受核心算法执行过程
单源最短路径,使用自定义 MinHeap 作优先队列。从节点 A 出发,寻找到 G 的最短路径。
| 节点 | A | B | C | D | E | F | G |
|---|---|---|---|---|---|---|---|
| dist | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ |
维护大小为 k 的最小堆,流式处理数据时保留最大的 k 个元素。O(n log k) 优于完整排序 O(n log n)。
Rust + Axum 实现,11 个端点,JSON 统一响应格式
| 端点路径 | 方法 | 功能描述 | 核心算法 |
|---|---|---|---|
/api/recommend/destinations |
POST | 个性化景点推荐(请求驱动) | Top-K 堆 |
/api/recommend/destinations/live |
GET | Top-K 推荐(数据库驱动) | Top-K 堆 |
/api/routes/shortest |
POST | 单源最短路径查询 | Dijkstra |
/api/facilities/nearby |
POST | 周边设施分类搜索 | 有界 Dijkstra |
/api/diaries |
POST | 创建新旅游日记 | — |
/api/diaries/search |
POST | 全文日记检索(请求驱动) | 2-gram 倒排索引 |
/api/diaries/search/live |
POST | 全文检索(数据库驱动) | 2-gram 倒排索引 |
/api/diaries/compress |
POST | Huffman 压缩演示 | Huffman 编码 |
/api/benchmarks |
GET | 算法性能对比报告 | 堆选择 vs 全排序 |
/healthz |
GET | 健康检查 | — |
北京邮电大学 2026 届 · 数据结构课程项目组
后端架构 · 核心算法实现
前端开发 · API 接口设计
数据处理 · 测试验证