高级数据结构 Trie树(字典树)

news2025/1/10 11:21:45

高级数据结构 Trie树(字典树)

(Trie Tree)字典树_Rkun18的博客-CSDN博客

字典树节点表示

#define TRIE_MAX_CHAR_NUM 26
//这里你可以自由设置 根据不同需求设置 如果有大小或者其他符号你就需要增大这个数字

struct TrieNode{
    TrieNode *child[TRIE_MAX_CHAR_NUM];
    bool isEnd;
    TrieNode():isEnd(false){
        for (int i = 0; i < TRIE_MAX_CHAR_NUM; ++i) {
            child[i]=0;
        }
    }
};

构造字典树

这里使用维基百科里的一幅图举例,由于只是举例,使用较小的26个字母,把大小写统一规定成小写,图里’A’变成’a’,方便构造树

在这里插入图片描述

int main() {
    TrieNode root;//根
    TrieNode n1,n2,n3;//树的第一层
    root.child['t'-'a']=&n1;
    root.child['a'-'a']=&n2;
    root.child['i'-'a']=&n3;
    n2.isEnd= true;//单个字符串走到末尾
    n3.isEnd= true;
    TrieNode n4,n5,n6;//第二层
    n1.child['o'-'a']=&n4;
    n4.isEnd= true;
    n1.child['e'-'a']=&n5;
    n3.child['n'-'a']=&n6;
    n6.isEnd= true;
    TrieNode n7,n8,n9,n10;//最后一层
    n5.child['a'-'a']=&n7;
    n7.isEnd= true;
    n5.child['d'-'a']=&n8;
    n8.isEnd= true;
    n5.child['n'-'a']=&n9;
    n9.isEnd= true;
    n6.child['n'-'a']=&n10;
    n10.isEnd= true;
    preorder(&root,0);//先序遍历







    return 0;
}

先序遍历

void preorder(TrieNode *node,int layer){
    for (int i = 0; i < TRIE_MAX_CHAR_NUM; ++i) {
        if(node->child[i]){
            for (int j = 0; j <layer ; ++j) {
                printf("-");
            }
            printf("%c",i+'a');
            if(node->child[i]->isEnd){
                printf("[done]");
            }
            printf("\n");
            preorder(node->child[i],layer+1);
        }


    }

}

main方法运行:

a[done]  
i[done]  
-n[done] 
--n[done]
t        
-e       
--a[done]
--d[done]
--n[done]
-o[done] 

Trie获取所有单词

  • 深度搜索trie树,对于正在搜索的节点node

  • 遍历该节点的所有孩子指针child[i],如果指针不为空,将child[i]对应字符放入栈里

  • 如果孩子指针isEnd为真,从栈底到栈顶对栈遍历,生成字符串,将它保存至结果数组中去

  • 深度搜索child[i]

  • 弹出栈顶字符

void get_all_word(TrieNode *node, string &word, vector<string> &wordList) {
    for (int i = 0; i < TRIE_MAX_CHAR_NUM; ++i) {
        if (node->child[i]) {
            word.push_back((char) i + 'a');//字符入栈
            if (node->child[i]->isEnd) {
                wordList.push_back(word);
            }
            get_all_word(node->child[i], word, wordList);
            word.erase(word.length() - 1, 1);//弹出栈顶字符

        }

    }
}

int main() {
    TrieNode root;//根
    TrieNode n1, n2, n3;//树的第一层
    root.child['t' - 'a'] = &n1;
    root.child['a' - 'a'] = &n2;
    root.child['i' - 'a'] = &n3;
    n2.isEnd = true;//单个字符串走到末尾
    n3.isEnd = true;
    TrieNode n4, n5, n6;//第二层
    n1.child['o' - 'a'] = &n4;
    n4.isEnd = true;
    n1.child['e' - 'a'] = &n5;
    n3.child['n' - 'a'] = &n6;
    n6.isEnd = true;
    TrieNode n7, n8, n9, n10;//最后一层
    n5.child['a' - 'a'] = &n7;
    n7.isEnd = true;
    n5.child['d' - 'a'] = &n8;
    n8.isEnd = true;
    n5.child['n' - 'a'] = &n9;
    n9.isEnd = true;
    n6.child['n' - 'a'] = &n10;
    n10.isEnd = true;
    vector<string> wordList;
    string word = "";
    get_all_word(&root, word, wordList);
    for (int i = 0; i < wordList.size(); ++i) {
        cout << wordList[i] << endl;
    }


    return 0;
}
a
i  
in 
inn
tea
ted
ten
to 

整体功能

class Trie{
public:
    Trie(){
    }
    ~Trie(){
        for (int i = 0; i <_node_vec.size() ; ++i) {
             delete _node_vec[i];
        }
    } //插入
    void insert(const char *word){

    }//查找
    bool search(const char *word){

    }//是否有以某个字符串为前缀的
    bool startsWith(const char *prefix){

    }
    TrieNode *root(){
        return &_root;
    }


private:
    TrieNode *newNode(){
        TrieNode *node=new TrieNode();
        _node_vec.push_back(node);
        return node;

    }
    vector<TrieNode *>_node_vec;
    TrieNode _root;
};

插入

  • 使用指针ptr指向root

  • 逐个遍历待插入字符串中的各个字符

  • 计算下标pos=’正在遍历字符’-‘a’

  • 如果ptr指向第pos孩子为假:

  • 创建该节点第pos个孩子

  • ptr指向该节点的第pos个孩子

  • 标记ptr指向节点的isEnd为true

   void insert(const char *word){
        TrieNode *ptr=&_root;
        while (*word){
            int pos=*word-'a';
            if(!ptr->child[pos]){
                ptr->child[pos]=newNode();
                
            }
            ptr=ptr->child[pos];
            word++;
            
        }
        ptr->isEnd= true;

    }

搜索

  • 使用ptr指针指向root

  • 逐个遍历带搜索字符各个字符

  • 计算下标pos=’正在遍历字符’-‘a’

  • 如果ptr指向节点第pos个孩子为假:返回假

  • ptr指向第pos个孩子

  • 返回ptr指向节点的isEnd

 bool search(const char *word){
        TrieNode *ptr=&_root;
        while (*word){
            int pos=*word-'a';
            if(!ptr->child[pos]){
                return false;
            }
            ptr=ptr->child[pos];
            word++;
        }
        return ptr->isEnd;

    }

判断前缀是否存在

bool startsWith(const char *prefix){
        TrieNode *ptr=&_root;
        while (*prefix){
            int pos=*prefix-'a';
            if(!ptr->child[pos]){
                return false;
            }
            ptr=ptr->child[pos];
            prefix++;
        }
        return true;

    }

前缀树代码

class Trie{
public:
    Trie(){
    }
    ~Trie(){
        for (int i = 0; i <_node_vec.size() ; ++i) {
             delete _node_vec[i];
        }
    }
    void insert(const char *word){
        TrieNode *ptr=&_root;
        while (*word){
            int pos=*word-'a';
            if(!ptr->child[pos]){
                ptr->child[pos]=newNode();

            }
            ptr=ptr->child[pos];
            word++;

        }
        ptr->isEnd= true;

    }
    bool search(const char *word){
        TrieNode *ptr=&_root;
        while (*word){
            int pos=*word-'a';
            if(!ptr->child[pos]){
                return false;
            }
            ptr=ptr->child[pos];
            word++;
        }
        return ptr->isEnd;

    }
    bool startsWith(const char *prefix){
        TrieNode *ptr=&_root;
        while (*prefix){
            int pos=*prefix-'a';
            if(!ptr->child[pos]){
                return false;
            }
            ptr=ptr->child[pos];
            prefix++;
        }
        return true;

    }
    TrieNode *root(){
        return &_root;
    }


private:
    TrieNode *newNode(){
        TrieNode *node=new TrieNode();
        _node_vec.push_back(node);
        return node;

    }
    vector<TrieNode *>_node_vec;
    TrieNode _root;
};

测试

int main() {
    Trie trie;
    trie.insert("hello");
    trie.insert("echo");
    trie.insert("eleven");
    trie.insert("how");
    cout<<"preorder_trie:"<<endl;
    preorder(trie.root(),0);
    vector<string>ls;
    string word;
    cout<<"All words:"<<endl;
    get_all_word(trie.root(),word,ls);
    for (int i = 0; i < ls.size(); ++i){
        cout<<ls[i]<<endl;
    }
    cout<<"Search:"<<endl;
    printf("hello :%d\n",trie.search("hello"));
    printf("hel :%d\n",trie.search("hel"));
    cout<<"StartsWith:"<<endl;
    printf("hello :%d\n",trie.startsWith("hello"));
    printf("hel :%d\n",trie.startsWith("hel"));




    return 0;
}
e             
-c            
--h           
---o[done]    
-l            
--e           
---v          
----e         
-----n[done]  
h             
-e            
--l           
---l          
----o[done]   
-o            
--w[done]     
All words:    
echo          
eleven        
hello         
how           
Search:       
hello :1      
hel :0        
StartsWith:
hello :1
hel :1

208. 实现 Trie (前缀树)

力扣

直接使用Trie类,改个名,调用方法把string通过c_str(),变成字符数组

#define TRIE_MAX_CHAR_NUM 26
//这里你可以自由设置 根据不同需求设置 如果有大小或者其他符号你就需要增大这个数字

struct TrieNode{
    TrieNode *child[TRIE_MAX_CHAR_NUM];
    bool isEnd;
    TrieNode():isEnd(false){
        for (int i = 0; i < TRIE_MAX_CHAR_NUM; ++i) {
            child[i]=0;
        }
    }
};


using namespace std;
class Trie1{
public:
    Trie1(){
    }
    ~Trie1(){
        for (int i = 0; i <_node_vec.size() ; ++i) {
             delete _node_vec[i];
        }
    }
    void insert(const char *word){
        TrieNode *ptr=&_root;
        while (*word){
            int pos=*word-'a';
            if(!ptr->child[pos]){
                ptr->child[pos]=newNode();

            }
            ptr=ptr->child[pos];
            word++;

        }
        ptr->isEnd= true;

    }
    bool search(const char *word){
        TrieNode *ptr=&_root;
        while (*word){
            int pos=*word-'a';
            if(!ptr->child[pos]){
                return false;
            }
            ptr=ptr->child[pos];
            word++;
        }
        return ptr->isEnd;

    }
    bool startsWith(const char *prefix){
        TrieNode *ptr=&_root;
        while (*prefix){
            int pos=*prefix-'a';
            if(!ptr->child[pos]){
                return false;
            }
            ptr=ptr->child[pos];
            prefix++;
        }
        return true;

    }
    TrieNode *root(){
        return &_root;
    }


private:
    TrieNode *newNode(){
        TrieNode *node=new TrieNode();
        _node_vec.push_back(node);
        return node;

    }
    vector<TrieNode *>_node_vec;
    TrieNode _root;
};







class Trie {
public:
    Trie() {

    }
    
    void insert(string word) {
       trie.insert(word.c_str());
    }
    
    bool search(string word) {
       return trie.search(word.c_str());
    }
    
    bool startsWith(string prefix) {
        return trie.startsWith(prefix.c_str());
    }
private:
   Trie1 trie;
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */

211. 添加与搜索单词 - 数据结构设计

力扣

这里考虑搜索遇到’.'如何

  • 当遍历到单词结束时

  • 如果node指向的节点标记为单词的结尾(isEnd==true)返回真,否则返回假

  • 如果word遇到’.’

  • 遍历所有node的孩子指针,继续递归搜索,单词指针向前移动一个位置,如果递归搜索结果为真,返回真

  • 如果不是’.’

  • 计算孩子位置pos,pos指向当前孩子为真,继续深搜,指针向前移动一个位置,如果搜索结果为真,返回真

下面来改写这个search方法

#define TRIE_MAX_CHAR_NUM 26
//这里你可以自由设置 根据不同需求设置 如果有大小或者其他符号你就需要增大这个数字

struct TrieNode {
    TrieNode *child[TRIE_MAX_CHAR_NUM];
    bool isEnd;

    TrieNode() : isEnd(false) {
        for (int i = 0; i < TRIE_MAX_CHAR_NUM; ++i) {
            child[i] = 0;
        }
    }
};




class Trie {
public:
    Trie() {
    }

    ~Trie() {
        for (int i = 0; i < _node_vec.size(); ++i) {
            delete _node_vec[i];
        }
    }

    void insert(const char *word) {
        TrieNode *ptr = &_root;
        while (*word) {
            int pos = *word - 'a';
            if (!ptr->child[pos]) {
                ptr->child[pos] = newNode();

            }
            ptr = ptr->child[pos];
            word++;

        }
        ptr->isEnd = true;

    }

    bool search_tire(TrieNode *node, const char *word) {
        if (*word == '\0') {
            if (node->isEnd) {
                return true;
            }
            return false;
        }
        if (*word == '.') {
            for (int i = 0; i < TRIE_MAX_CHAR_NUM; ++i) {
                if (node->child[i] && search_tire(node->child[i], word + 1)) {
                    return true;
                }

            }
        } else {
            int pos = *word - 'a';
            if (node->child[pos] && search_tire(node->child[pos], word + 1)) {
                return true;
            }
        }
        return false;


    }

    bool startsWith(const char *prefix) {
        TrieNode *ptr = &_root;
        while (*prefix) {
            int pos = *prefix - 'a';
            if (!ptr->child[pos]) {
                return false;
            }
            ptr = ptr->child[pos];
            prefix++;
        }
        return true;

    }

    TrieNode *root() {
        return &_root;
    }


private:
    TrieNode *newNode() {
        TrieNode *node = new TrieNode();
        _node_vec.push_back(node);
        return node;

    }

    vector<TrieNode *> _node_vec;
    TrieNode _root;
};



class WordDictionary {
public:
    WordDictionary() {

    }
    
    void addWord(string word) {
        trie.insert(word.c_str());


    }
    
    bool search(string word) {
       return trie.search_tire(trie.root(),word.c_str());
    }
private:
      Trie trie;
};

/**
 * Your WordDictionary object will be instantiated and called as such:
 * WordDictionary* obj = new WordDictionary();
 * obj->addWord(word);
 * bool param_2 = obj->search(word);
 */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/478363.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在amd64与arm上用paddlelite部署paddelOCR(Ascend硬件)

由于部署的硬件是华为昇腾 NPU&#xff08;Ascend310&#xff09;&#xff0c;参考网址https://www.paddlepaddle.org.cn/lite/v2.10/demo_guides/huawei_ascend_npu.html#npu-paddle-lite 先拉取paddlelite用来编译库 git clone https://github.com/PaddlePaddle/Paddle-Lit…

2. Java 异常体系

2.1 Throwable java.lang.Throwable 类是 Java 程序执行过程中发生的异常事件对应的类的根父类。 Throwable 中的常用方法&#xff1a; public void printStackTrace()&#xff1a;打印异常的详细信息。 包含了异常的类型、异常的原因、异常出现的位置、在开发和调试阶段都得…

倾斜摄影超大场景的三维模型轻量化纹理压缩的关键技术

倾斜摄影超大场景的三维模型轻量化纹理压缩的关键技术 倾斜摄影超大场景的三维模型轻量化处理中纹理压缩是轻量化处理的重要手段之一&#xff0c;可以在保证模型真实感的前提下&#xff0c;减小数据体积、降低传输带宽和提高渲染性能。以下是几个关键的纹理压缩技术&#xff1a…

自然语言处理知识抽取(pkuseg、DDParser安装及使用)

一、分词简介 1.基本概念 分词是自然语言处理中的一个重要步骤&#xff0c;它可以帮助我们将文本分成一个个词语&#xff0c;以便更好地理解和分析文本。在计算机视觉、语音识别、机器翻译等领域&#xff0c;分词都扮演着重要的角色。 目前&#xff0c;常用的分词库包括 jie…

搭建Redis主从集群+哨兵+代理predixy

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Redis是什么&#xff1f;二、搭建Redis集群步骤1.环境和版本2.Redis 安装部署3.主从同步配置4.哨兵模式配置5.代理predixy配置 总结 前言 提示&#xff1a…

深度学习 -- Dataset与DataLoader

前言 在模型训练的步骤中&#xff0c;数据的部分非常重要&#xff0c;它的过程主要分为数据收集、数据划分、数据读取、数据预处理。 数据收集的有原始样本和标签(Img&#xff0c;label) 数据集的划分需要分为训练集、验证集、测试集。 训练集负责训练模型&#xff0c;验证集…

【C++】C++11常用特性总结

哥们哥们&#xff0c;把书读烂&#xff0c;困在爱里是笨蛋&#xff01; 文章目录 一、统一的列表初始化1.统一的{}初始化2.std::initializer_list类型的初始化 二、简化声明的关键字1.decltype2.auto && nullptr 三、STL中的一些变化1.新增容器&#xff1a;array &…

趣说数据结构(练习2) —— 顺序表/链表力扣刷题(中等难度)

练习 2 —— 顺序表/链表力扣刷题&#xff08;中等难度&#xff09; 1. 反转链表 II 力扣原题&#xff1a;https://leetcode.cn/problems/reverse-linked-list-ii/ 题目描述 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从…

数据可视化大屏的页面布局以及自适应

在做数据可视化大屏之前&#xff0c;我们需要考虑到页面的布局问题以及页面缩放自适应问题&#xff0c;下面分别就这两个方面讲解。 页面布局 类似这种页面区块的明显划分&#xff0c;常用的布局方式有两种&#xff1a; 1、flex布局 2、grid布局 grid布局 grid布局可以按区块…

山东专升本计算机第二章-Windows7操作系统

Windows7操作系统 2.3Windows7的文件和文件夹管理 考点7 Windows7的基础知识 文件&#xff0c;是指存放在外存储器上的一组相关信息的•集合 文件名是操作系统中区分不同文件的唯一标志 文件名是由主文件名和扩展名两部分组成不能使用 ▏ < > * ? \ / &#xff1a; …

单向带头链表的添加修改删除操作

public class HeroNodeDemo {public static void main(String[] args) {HeroNode hero1 new HeroNode(1, "松江");HeroNode hero2 new HeroNode(2, "武松");HeroNode hero3 new HeroNode(3, "及时雨");HeroNode hero4 new HeroNode(4, "…

prometheus实战之一:用ansible部署

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 关于《prometheus实战》 《prometheus实战》是欣宸原创的系列文章&#xff0c;旨在通过实战操作来熟悉和掌握prometheus常规技能 本篇概览 本文是《promet…

spring 容器结构/机制debug分析--Spring 学习的核心内容和几个重要概念--IOC 的开发模式--综合解图

目录 Spring Spring 学习的核心内容 解读上图: Spring 几个重要概念 ● 传统的开发模式 解读上图 ● IOC 的开发模式 解读上图 代码示例—入门 xml代码 注意事项和细节 1、说明 2、解释一下类加载路径 3、debug 看看 spring 容器结构/机制 综合解图 Spring Spr…

使用SPY++查看窗口信息去分析C++客户端UI软件问题

目录 1、使用SPY查看窗口的信息 2、使用SPY查看某些软件UI窗口用什么UI组件实现的 2.1、查看海康视频监控客户端安装包程序 2.2、查看华为协同办公软件WeLink 2.3、查看字节协同办公软件飞书 2.4、查看最新版本的Chrome浏览器 2.5、查看小鱼易连视频会议客户端软件 2.6…

STM32H7 DMA

CubeMX配置 发送调用&#xff1a; 发送速度确实挺快的。 接收&#xff1a; HAL_UART_Receive_DMA(&huart1,Rxbuffer,sizeof(Rxbuffer)); 这个函数开启DMA接收,将收到的数据存放到Rxbuffer数组中去,当接收到了sizeof(Rxbuffer)个数据就会回调DMA1_Stream0_IRQHandler函数…

Linux-基本指令

文章目录 Centos用户新增及删除新增删除 两个理论概念管理贯穿 ls指令文件的操作-l (列出详细信息)-a&#xff08;显示所有的文件&#xff0c;包括隐藏文件&#xff09;-F&#xff08;在每个文件名后附上一个字符以说明该文件的类型&#xff09;-d&#xff08;将目录象文件一样…

CTF权威指南 笔记 -第二章二进制文件-2.1-汇编原理

目录 编译原理 GCC编译 四个阶段 (1)预处理阶段 (2)编译阶段 (3)汇编阶段 (4)链接阶段 预处理阶段 编译阶段 汇遍阶段 链接阶段 C语言的生命是从 源文件开始 的 每条C语言都必须要给翻译成 一系列的低级语言 最后 按照可执行文件格式打包 并且作为二进制文件保存起来…

【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南

带你深入分析Spring所提供的缓存Cache功能的开发实战指南 CacheManager管理器的扩展支持缓存技术类型与CacheManger缓存依赖application配置缓存注解EnableCachingCacheableCachePutCacheEvictCacheConfig SpEL上下文数据注意 SpEL提供了多种运算符 不同Cache的实现机制Concurr…

快速入门微服务保护框架Sentinel

文章目录 一、Sentinel1.1 雪崩问题1.1.1 介绍1.1.2 解决方案 1.2 初识Sentinel1.3 sentinel下载和整合1.4 流量控制1.4.1 簇点链路1.4.2 Sentinel簇点链路设置1.4.3 流控规则1.4.4 热点参数限流1.4.5 隔离和降级1.4.6 授权规则 一、Sentinel 1.1 雪崩问题 1.1.1 介绍 雪崩问…