树与查找专题讨论2

集美大学树与查找专题讨论2

项目名称 内容
课程名称 数据结构
班级 网安2511、2512
学号 202521336002
学号 202521336035
实验项目名称 手机通讯录问题

一、目的(本次实验所涉及并要求掌握的知识点)

  • 理解树的存储结构、核心原理,掌握插入、前缀查询等基本操作。
  • 学习利用树解决字符串前缀匹配问题的方法替代遍历的暴力算法。
  • 学习实现手机通讯录的前缀检索功能,完成初始化、姓名插入、前缀查询等核心功能。
  • 理解算法优化思想,掌握树在实际场景中的应用。

二、自然语言描述查找过程

  • 从根节点开始查找,在前缀字符串中从左到右读取字符,依次在树中进行匹配。
  • 取出需要匹配的字符,检查当前所在节点的子节点中,是否存在匹配的节点。如果找不到对应节点,返回空;如果找到,就继续移动到子节点上继续匹配下一个前缀字符。
  • 匹配完所有字符后,停留在所有以查找的前缀开头姓名的公共节点上。
  • 从这个公共节点开始遍历所有子节点,将节点存储的信息记录下来,直到把所有姓名都找完。
  • 将收集到的结果整理成列表,并返回。

三、实验内容与设计思想

树结构体

相关伪代码

treenode
    child:【字符→子节点】的映射表 //存储当前字符对应的子节点
    isEnd: 布尔值  // 标记:是否是某个姓名的最后一个字符
    fullName: 字符串  // 存储完整的联系人姓名
    节点初始化,isEnd = false,fullName = 空字符串

代码

struct treenode {
	unordered_map<char, treenode*>child;
	bool isend;
	string fullname;
	treenode():isend(false),fullname(""){}
};

模块1:姓名插入函数

函数相关伪代码

insert(name字符串)
    temp=root // 从根节点开始遍历
    // 逐个遍历姓名的每一个字符
    for(char ch:name) 
        // 如果当前节点没有 ch 对应的子节点,就新建一个
        if temp.child中不存在键ch
            创建ch对应的子节点
        移动到子节点,继续处理下一个字符
     遍历完所有字符,标记为姓名结尾,并存储完整姓名

函数代码

void insert(const string& name) {
    treenode* temp = root;
    for (char ch : name) {
        if (temp->child.find(ch) == temp->child.end()) {
            temp->child[ch] = new treenode();
        }
        temp = temp->child[ch];
    }
    temp->isend = true;
    temp->fullname = name;
}

模块2:前缀匹配函数

函数相关伪代码

match(index字符串)
    result = 空字符串列表  // 存储最终匹配结果
    temp=root 创建遍历指针指向根节点
    // 逐个遍历前缀的每一个字符
    for(char ch:index)
        // 如果找不到对应字符,说明无匹配,直接返回空列表
        if temp.child 中 不存在 键 ch
            return result 
        移动到子节点,继续匹配
    前缀匹配完成,递归搜索该节点下所有姓名
    // 返回所有匹配的姓名
    return result

函数代码

vector<string> match(const string& index) {
    vector<string> result;
    treenode* temp = root;
    for (char ch : index) {
        if (temp->child.find(ch) == temp->child.end()) {
            return result;
        }
        temp = temp->child[ch];
    }
    search(temp, result);
    return result;
}

模块3:搜索收集匹配前缀姓名函数

函数相关伪代码

search(node,result字符串列表)
    // 如果当前节点是姓名结尾,把完整姓名加入结果
    if(node->isend==true)
        将 node->fullName 添加到 result 中
    // 遍历当前节点的所有子节点,递归搜索
    for(auto& pair:node->child)
        search(子节点,result)   

函数代码

void search(treenode* node, vector<string>& result) {
    if (node->isend) {
        result.push_back(node->fullname);
    }
    for (auto& pair : node->child) {
        search(pair.second, result);
    }
}

四、时间复杂度

模块1:姓名插入函数

  • 遍历姓名字符m次循环,时间复杂度为O(m)

模块2:前缀匹配函数

  • 遍历前缀共n个字符,遍历所有匹配姓名访问k个结果对应节点,时间复杂度为O(k+n)

模块3:搜索收集匹配前缀姓名函数

  • 遍历子树,收集所有匹配的姓名,时间复杂度为O(t)

五、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)

本机运行截图
image
image


六、总结

  • 学会了使用c++实现手机通讯录的简易功能。
  • 理解了树的前缀查询效率之高,时间复杂度仅跟前缀长度和结果数量有关。
  • 掌握了树的插入、深度搜索遍历等操作,理解了树形结构在处理字符串方面的优势。

七、附件(参考文献和相关资料)

  1. 实验4-树
  2. 实验5-查找
posted @ 2026-05-04 12:23  xin_fffffly  阅读(55)  评论(0)    收藏  举报