专题讨论2:树与查找
专题讨论2:(树与查找)决策树
| 项目名称 | 内容 |
|---|---|
| 课程名称 | 数据结构 |
| 班级 | 网安2511 |
| 学号 | 202521336001 、202521336006 |
| 实验项目名称 | 10:决策树 |
| 上机实践日期 | 2026.05 |
一、关于选题起源
某天,我盯着自己的歌单陷入了沉思:
为什么我收藏了那么多“高级感”十足的后摇,
最后循环最多的,却永远是那几首神曲?
为了直面这种惨淡的人生真相,
我们决定请出数据结构里的“福尔摩斯”——决策树。
这位侦探先生破案的方式非常耿直,
既不看歌词,也不分析旋律,
只负责问你一堆看似废话的问题:
“你是不是那种听到吉他solo就兴奋的人?”
“你是不是非英文歌不听的‘伪文青’?”
每一个“是”或“否”,都会把你引向一条新的岔路,
直到最后,它拍拍你的肩膀,淡淡地说一句:
“别装了,你就是个标准的 Pop 流行乐受害者。”
本次实验的目标很简单:
搭建一棵属于我自己的音乐推荐决策树,
让算法代替镜子,照出我们那真实听歌习惯————
今天,就让我们用最简单的入门案例,带大家搞懂:决策树到底是什么?怎么建立决策树?
首先我们先厘清,决策树究竟是个什么东西?其实它本质上就是一种模拟人类决策过程的树形结构,就像我们平时做选择一样,一步步根据条件判断、分支,最终得到结果。它由根节点、分支节点和叶子节点组成,节点里存放判断特征,分支存放不同的判断结果,叶子节点就是最终的结论。
这篇博客将举一个简单的例子,通过这个简单的例子我们可以掌握决策树的内涵与实现方法,从而为未来大家去构建更复杂的决策树打下基础,也能真正理解树结构的实战价值。
二、实验设计
- 结构示意
“那么,这棵树是如何一步步‘审问’你的听歌偏好的?别急,看完下面这个简陋却精悍的结构示意,你就全明白了。”
是否喜欢摇滚?
├─ 是 → Rock
└─ 否 → 是否常听欧美?
├─ 是 → Pop
└─ 否 → Classical
-
示例图
![eb26d162897c776bb475414c8f15d8aa]()
-
构建决策树
“光看上面的示意图不过瘾?来,让我们一起剖析这棵树的‘生长过程’,看看在代码世界里,它是怎么被一砖一瓦搭建起来的~”
函数 buildMusicTree()
// 创建根节点
根节点 = 新建节点()
根节点.特征 = "LikeRock" // 是否喜欢摇滚
// 分支:喜欢摇滚 → 推荐 Rock
摇滚节点 = 新建节点()
摇滚节点.标签 = "Rock"
根节点.子节点["Yes"] = 摇滚节点
// 分支:不喜欢摇滚 → 进入下一个判断
不喜欢摇滚节点 = 新建节点()
不喜欢摇滚节点.特征 = "ListenWestern" // 是否听欧美
// 听欧美 → 推荐 Pop
流行节点 = 新建节点()
流行节点.标签 = "Pop"
不喜欢摇滚节点.子节点["Yes"] = 流行节点
// 不听欧美 → 推荐 Classical
古典节点 = 新建节点()
古典节点.标签 = "Classical"
不喜欢摇滚节点.子节点["No"] = 古典节点
根节点.子节点["No"] = 不喜欢摇滚节点
返回 根节点
- 预测函数伪代码
函数 predict(节点, 样本):
如果 节点.标签 不为空:
返回 节点.标签
特征值 ← 样本[节点.特征]
子节点 ← 节点.子节点[特征值]
返回 predict(子节点, 样本)
三、代码实现
- 数据结构定义(给树打个骨架!)
要建房子先打地基,要建树,就得先定义“节点”这个基本单元。
我们不用晦涩的术语,咱们就把它想象成一个个“带问号的盒子”:盒子里存着问题(比如“喜欢摇滚吗”),还有指向不同答案的指针。
下面这个 struct Node,就是咱们这棵音乐决策树的“基因蓝图”————
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct Node {
string feature; // 划分特征
map<string, Node*> children;
string label; // 叶子节点类别
};
- 构建决策树(开始种树啦!)
骨架有了,接下来就是往里面填肉了。
这一步,我们要亲手把这棵树“种”出来。
我们写一个 buildMusicTree()函数,它就像个“树农”,负责把一个个节点挂到该挂的位置。
你看,根节点是“喜欢摇滚吗”,不喜欢就去问“听欧美吗”,听着听着,最后就给你贴上“Pop”或“Classical”的标签。
整个过程,就像在玩“文字版大富翁”,走到哪格,就触发哪个事件~
Node* buildMusicTree() {
Node* root = new Node();
root->feature = "LikeRock";
Node* rock = new Node();
rock->label = "Rock";
root->children["Yes"] = rock;
Node* noRock = new Node();
noRock->feature = "ListenWestern";
Node* pop = new Node();
pop->label = "Pop";
noRock->children["Yes"] = pop;
Node* classical = new Node();
classical->label = "Classical";
noRock->children["No"] = classical;
root->children["No"] = noRock;
return root;
}
- 预测函数(递归思想)
树种好了,现在轮到它发挥价值了—————怎么用它来“猜”用户的喜好?
这就是最精彩的部分:预测函数。
它的逻辑其实特别简单,就是一个“递归”的过程:
拿到用户的特征(比如“喜欢摇滚=Yes”),顺着树往下走一步;
到了下一个节点,再看下一个问题……
直到走到一个“叶子节点”(也就是没有孩子了),嘶~答案就出来了。
string predict(Node* root, map<string, string>& sample) {
if (!root->label.empty())
return root->label;
string value = sample[root->feature];
return predict(root->children[value], sample);
}
- 测试入口(让树开口说话~)
逻辑都写完了,总得跑起来看看效果吧?
最后,咱们写个 main函数,模拟一个真实的用户来“问卦”。
用户输入自己的听歌习惯(“喜欢摇滚吗?”“听欧美吗?”),程序就会顺着树一路“盘问”,最后输出一个“音乐人格诊断书”。
这一步,就是让这棵静态的树,“活”过来,开始为你服务的时刻。
int main() {
Node* tree = buildMusicTree();
map<string, string> user;
cout << "是否喜欢摇滚?(Yes/No): "; cin >> user["LikeRock"];
cout << "是否常听欧美音乐?(Yes/No): "; cin >> user["ListenWestern"];
cout << "预测喜欢的音乐类型:"
<< predict(tree, user) << endl;
return 0;
}
四、复杂度分析
- 构建阶段:
- 本实验采用手动构建方式,需要依次创建每个节点并建立分支关系,操作次数与节点数量成正比。
👉 时间复杂度:O(n)(n 为树中节点总数,本实验 n=5)
- 预测阶段:
- 预测过程相当于在树上进行深度优先遍历
- 最坏情况下遍历树的高度 h
👉 时间复杂度:O(h)
- 空间复杂度:
- 空间用于存储所有节点与分支,与节点数量成正比。
👉 空间复杂度:O(n)
五、运行展示
本机运行截图



六、实验小结
收获:
- 理解决策树是一种递归结构
- 掌握决策树构建与预测的基本流程
- 能够将现实问题(音乐喜好)抽象为特征与类别
为什么选用决策树做音乐喜好判断,而非简单的 if-else?
有人可能会忍不住问:这么简单的判断逻辑,直接用几行 if-else 嵌套不就能实现吗?何必大费周章去搭建一棵树结构?
自然,针对本次只有两层判断的极简案例,if-else 代码写起来确实更简短,可这也仅仅局限于超简单、无扩展的固定场景。一旦我们想要新增一个判断特征,比如加入 “是否喜欢慢节奏”“是否偏爱小众音乐”,if-else 就会陷入层层嵌套的困境,代码变得臃肿混乱、可读性极差,后期修改和维护更是难上加难,完全是硬编码的固定逻辑,没有任何灵活性。
对比优势:
| 维度 | if-else | 决策树 |
|---|---|---|
| 扩展性 | 新增条件需修改原有逻辑 | 只需新增节点,不动核心代码 |
| 可读性 | 多层嵌套难以一眼看清流程 | 树状结构直观对应人类决策过程 |
| 维护性 | 改一处可能牵一发而动全身 | 局部调整,互不干扰 |
这就是为什么我们宁愿"大费周章"建树————它把逻辑结构和代码实现解耦了。
我们选择用决策树,正是因为它脱离了单纯的代码逻辑,上升到了数据结构的层面。它把每一个判断、每一个分支都封装成独立的节点,新增判断条件、修改推荐结果,只需要增减或调整节点,完全不用改动原有核心代码;同时它的结构和我们的决策思路完全对应,哪怕是零基础的人,也能一眼看懂整个判断流程,更能帮我们真正理解 “树结构” 在实际问题中的应用,而不是停留在表层的条件判断。
这篇文章的初衷,从来不是为了用复杂代码实现简单功能,而是借这个极简的音乐偏好预测案例,让大家轻松入门:看懂决策树的基本结构,掌握它的搭建和使用逻辑,明白它相比硬编码的 if-else,在扩展性、可读性、可维护性上的核心优势。毕竟学会决策树的思想,才能应对后续更复杂、更多维度的决策问题,这才是学习数据结构的真正意义。
七、附件(参考文献和相关资料)
AI 声明:本文核心思路为原创,AI 做辅助文字润色与结构梳理。

浙公网安备 33010602011771号