Trie实践:一种比哈希表更快的数据结构

2023-05-16

本文乃Siliphen原创。转载请注明出处:http://blog.csdn.net/stevenkylelee


本文分为5部分。从思考和认知的角度,由浅到深带你认识Trie数据结构。

  1.桶状哈希表与直接定址表的概念。

  2.为什么直接定址表会比桶状哈希表快

  3.初识Trie数据结构

  4.Trie为什么会比桶状哈希表快

  5.实际做实验感受下Trie , std::map , std::unordered_map的差距

  6.最后的补充


1.桶状哈希表与直接定址表的概念。


先考虑一下这个问题:如何统计5万个0-99范围的数字出现的次数?

可以用哈希表来进行统计。如下:

	// 生成5万个0-99范围的随机数
	int * pNumbers = new int[ 50000 ] ;
	for ( int i = 0 ; i < 50000 ; ++i )
	{
		pNumbers[ i ] = rand( ) % 100 ;
	}

	// 统计每个数字出现个次数
	unordered_map< int , int > Counter ;
	for ( int i = 0 ; i < 50000 ; ++i )
	{
		++Counter[ pNumbers[ i ] ] ;
	}


普通的桶状哈希表可能是有冲突的,这取决于哈希函数的设计。

如果有冲突,那么就会退化成线性查找。


对于这个问题,有一种更好的做法,就是“直接定址表”

“直接定址表”的概念第一次我是在王爽著的《汇编语言》看到

使用“直接定址表”需要满足一些条件,比如:值刚好就是key


上面那题用直接定址表来统计的话,实现是这样:

	// 统计每个数字出现个次数
	int Counter[ 100 ] = { 0 } ; 
	for ( int i = 0 ; i < 50000 ; ++i )
	{
		++Counter[ pNumbers[ i ] ] ;
	}

以上代码只是把哈希表容器换成了一个数组。数组的0-99的下标范围就是表示0-99个数字,

下标对应的元素值就是该下标表示的数字的出现次数。


2.为什么直接定址表会比桶状哈希表快


直接定址表也是哈希的一种,只是比较特殊。

直接定址表不需要计算哈希散列值,既然没有哈希散列值自然就不存在哈希冲突处理了。

这就是直接定址表比桶状哈希表快的原因


3.初识Trie数据结构


再考虑这样一个问题:如何统计5万个单词出现的次数?

哈,这个有点难度了吧?只能用哈希表来做了吧?

实现是不是像这样:

	vector< string > words ;
	// 生成5万个随机单词,略。。。

	// 统计每个数字出现个次数
	unordered_map< string , int > Counter ;
	for ( int i = 0 ; i < 50000 ; ++i )
	{
		++Counter[ words[ i ] ] ;
	}

还有没有更快的统计方法呢?

首先我们来看下桶状哈希表慢在哪里,有2点

1.对每个字符串key都要执行一次哈希散列函数

2.如果哈希散列有冲突的话,就要做冲突处理

要提速,就要把这2点给干掉,不计算哈希散列,不做冲突处理。

咦!这不就是之前说的“直接定址表”么?

那用“直接定址表”怎样做字符串的统计?


如果,你自认为自己是一个天才的话,看到这里,就先别往下看。

先自己想想:怎样用直接定址表的思想来做字符串的统计、查找。


答案那就是Trie数据结构。Trie是啥?

简单地说,Trie就是直接定址表和树的结合的产物

Trie其实是一种树结构,既然是树,那就会有树节点,

Trie树节点的特殊在于:一个节点的子节点就是一个直接定址表


Trie树节点的定义类似如下:

// Trie树节点
struct TrieNode
{
	// 节点的值
	int Val ; 

	// 子节点
	Node* Children[ 256 ] ;

};

要直观地用图形表示Trie树,大概是这样:



4.Trie为什么会比桶状哈希表快


从代码定义和图示可以看出,每个节点,对其子节点的定位,都是一个直接定址表。

要查找"Siliphen"这个字符串对应的值,过程是怎样的呢?

从根节点开始,用S的Ascii值直接定位找到S对应的子节点,

从S对应的节点,直接定位找到i对应的子节点

从i对应的节点,直接定位找到l对应的子节点

以此类推,直到最后的

从e对应的节点,直接定位找到n对应的子节点

n对应的子节点的数据字段就是"Siliphen"的字符串对应的值


从这个过程可以看到对于字符串的键值映射查找,Trie根本没有进行哈希散列和冲突处理。

This is the reason that Trie is faster than Hashtable!

这就是Trie比哈希表快的原因!


5.实际做实验感受下Trie , std::map , std::unordered_map的差距


理论上来说,Trie要比哈希表快。

到底快多少呢?咱们就做一个实验看看吧。有一个直观的感受。

首先,我们要写一个Trie。


我自己实现了一个TrieMap,

模仿C++的std标准库的map , unordered_map写的一个模板类

代码如下:

#pragma once
#include <string>
#include <queue>
#include <stack>
#include <list>
using namespace std ;

template< typename Value_t >
class TireMap
{
public:
	TireMap( );
	~TireMap( ) ;

private:

	typedef pair< string , Value_t > Kv_t ;

	struct Node
	{
		Kv_t * pKv ;

		Node* Children[ 256 ] ;

		Node( ) :
			pKv( 0 )
		{
			memset( Children , 0 , sizeof( Children ) ) ;
		}

		~Node( )
		{
			if ( pKv != 0 )
			{
				//delete pKv ;
			}
		}

	};

public : 

	/*
	重载[ ]  运算符。和 map , unorder_map 容器接口一样。
	*/
	Value_t& operator[ ]( const string& strKey ) ;

	// 清除保存的数据
	void clear( ) ;

public : 

	const list< Kv_t >& GetKeyValueList( ) const { return m_Kvs ; }

protected:

	// 删除一棵树
	static void DeleteTree( Node *pNode ) ; 

protected:

	// 树根节点
	Node * m_pRoot ; 

	// 映射的键值列表
	list< Kv_t > m_Kvs ;

};

template< typename Value_t >
TireMap<Value_t>::TireMap( )
{
	m_pRoot = new Node( ) ;
}

template< typename Value_t >
TireMap<Value_t>::~TireMap( )
{
	clear( ) ;
	delete m_pRoot ;
}

template< typename Value_t >
void TireMap<Value_t>::clear( )
{
	for ( int i = 0 ; i < 256 ; ++i )
	{
		if ( m_pRoot->Children[ i ] != 0 )
		{
			DeleteTree( m_pRoot->Children[ i ] ) ;
			m_pRoot->Children[ i ] = 0 ;
		}
	}

	m_Kvs.clear( ) ; 
}

template< typename Value_t >
void TireMap<Value_t>::DeleteTree( Node * pRoot )
{
	// BFS 删除树
	stack< Node* > stk ; 
	stk.push( pRoot ) ; 
	for ( ; stk.size( ) > 0 ; )
	{
		Node * p = stk.top( ) ; stk.pop( ) ;
		// 扩展
		for ( int i = 0 ; i < 256 ; ++i )
		{
			Node* p2 = p->Children[ i ] ;
			if ( p2 == 0 )
			{
				continue; 
			}
			stk.push( p2 ) ;
		}

		delete p ; 
	}
}

template< typename Value_t >
Value_t& TireMap<Value_t>::operator[]( const string& strKey )
{
	Node * pNode = m_pRoot ;

	// 建立或者查找树路径
	for ( size_t i = 0 , size = strKey.size( ) ; i < size ; ++i )
	{
		const char& ch = strKey[ i ] ;
		Node*& Child = pNode->Children[ ch ] ;

		if ( Child == 0 )
		{
			pNode = Child = new Node( ) ;
		}
		else
		{
			pNode = Child ;
		}

	}
	// end for

	// 如果没有数据字段的话,就生成一个。
	if ( pNode->pKv == 0 )
	{
		m_Kvs.push_back( Kv_t( strKey , Value_t() ) ) ;
		pNode->pKv = &*( --m_Kvs.end( ) ) ;
	}

	return pNode->pKv->second ; 
}

有没有std的感觉?哈哈
核心代码就是[]运算符重载的实现。
为什么要我搞一个list< Kv_t > m_Kvs字段?
这个字段主要是用来方便查看结果。


OK。下面我们来写测试代码

看看 Trie , 与 std::map , std::unordered_map之间的差别

测试代码如下:

#include <string>
#include <vector>
#include <unordered_map>
#include <map>
#include <time.h>
#include "TireMap.h"
using namespace std ;

// 随机生成 Count 个随机字符组合的“单词”
template< typename StringList_t >
int CreateStirngs( StringList_t& strings , int Count )
{
	int nTimeStart , nElapsed ;
	nTimeStart = clock( ) ;
	strings.clear( ) ;
	for ( int i = 0 ; i < Count ; ++i )
	{
		int stringLen = 5 ;
		string str ;
		for ( int i = 0 ; i < stringLen ; ++i )
		{
			char ch = 'a' + rand( ) % ( 'z' - 'a' + 1 ) ;
			str.push_back( ch ) ;

			if ( ch == 'z' )
			{
				int a = 1 ; 
			}
		}
		strings.push_back( str ) ;
	}

	nElapsed = clock( ) - nTimeStart ;
	return nElapsed ; 
}

// 创建 Count 个整型数据。同样创建这些整型对应的字符串
template< typename StringList_t , typename IntList_t >
int CreateNumbers( StringList_t& strings , IntList_t& Ints , int Count )
{
	strings.clear( ) ; 
	Ints.clear( ) ; 

	for ( int i = 0 ; i < Count ; ++i )
	{
		int n =rand( ) % 0x00FFFFFF ; 
		char sz[ 256 ] = { 0 } ;
		_itoa_s( n , sz , 10 ) ; 

		strings.push_back( sz ) ;
		Ints.push_back( n ) ;
	}

	return 0 ;
}

// Tire 正确性检查
string Check( const unordered_map< string , int >& Right , const TireMap< int >& Tire )
{
	string strInfo = "Tire 统计正确" ;

	const auto& TireRet = Tire.GetKeyValueList( ) ;
	unordered_map< string , int > ttt ;
	for ( auto& kv : TireRet )
	{
		ttt[ kv.first ] = kv.second ;
	}

	if ( ttt.size( ) != Right.size( ) )
	{
		strInfo = "Tire统计有错" ;
	}
	else
	{
		for ( auto& kv : ttt )
		{
			auto it = Right.find( kv.first )  ;
			if ( it == Right.end( ) )
			{
				strInfo = "Tire统计有错" ;
				break ;
			}
			else if ( kv.second != it->second )
			{
				strInfo = "Tire统计有错" ;
				break ;
			}

		}
	}

	return strInfo ; 

}

// 统计模板函数。可以用map , unordered_map , TrieMap 做统计
template< typename StringList_t , typename Counter_t >
int Count( const StringList_t& strings , Counter_t& Counter )
{
	int nTimeStart , nElapsed ;

	nTimeStart = clock( ) ;
	map< string , int > Counter1 ;
	for ( const auto& str : strings )
	{
		++Counter[ str ] ;
	}
	nElapsed = clock( ) - nTimeStart ;

	return nElapsed  ;

}

int _tmain( int argc , _TCHAR* argv[ ] )
{
	map< string , int > ElapsedInfo ;
	int nTimeStart , nElapsed ;

	// 生成50000个随机单词
	list< string > strings ;
	nElapsed = CreateStirngs( strings , 50000 ) ;
	//ElapsedInfo[ "生成单词 耗时" ] = nElapsed  ;

	// 用 map 做统计
	map< string , int > Counter1 ;
	nElapsed = Count( strings , Counter1 ) ;
	ElapsedInfo[ "统计单词 用map 耗时" ] = nElapsed  ;

	// 用 unordered_map 做统计
	unordered_map< string , int > Counter2 ;
	nElapsed = Count( strings , Counter2 ) ;
	ElapsedInfo[ "统计单词 用unordered_map 耗时" ] =  nElapsed  ;

	// 用 Tire 做统计
	TireMap< int > Counter3 ;
	nElapsed = Count( strings , Counter3 ) ;
	ElapsedInfo[ "统计单词 用Tire 耗时" ] = nElapsed  ;

	// Tire 统计的结果。正确性检查
	string CheckRet = Check( Counter2 , Counter3 ) ; 

	// 用哈希表统计5万个整形数字出现的次数
	// 与 用Tire统计同样的5万个整形数字出现的次数的 对比
	// 当然,用Tire统计的话,先要把那5万个整形数据,转换成对应的字符串的表示。

	list< int > Ints ; 
	CreateNumbers( strings , Ints , 50000 ) ; 
	unordered_map< int , int > kivi ;
	nTimeStart = clock( ) ;
	for ( const auto& num : Ints )
	{
		++kivi[ num ] ;
	}
	nElapsed = clock( ) - nTimeStart ;
	ElapsedInfo[ "统计数字 unordered_map 耗时" ] = nElapsed  ;

 	//Counter3.clear( ) ; 这句话非常耗时。因为要遍历树逐个delete树节点。树有可能会非常大。所以我注释掉
	nElapsed = Count( strings , Counter3 ) ;
	ElapsedInfo[ "统计数字 用Tire 耗时" ] = nElapsed  ;

	return 0;
}

实际运行的结果是:




对于统计5万个单词出现的次数

std::map耗时:3122毫秒

std::unordered_map耗时:2421毫秒

而我们写的Trie耗时:1332毫秒


可以看到,红黑树实现的std::map比桶状哈希表实现的std::unordered_map慢了差不多一秒

std::unordered_map又比Trie慢了差不多一秒。


这里有一个有趣的实验。

哈希表的Key类型用int,会不会快?

最后,我生成了5万个随机int整型整数,同时也把这5万个int转换成对应的string。

用key为int的哈希表和key为string的Trie做测试,看哪个快。

答案是:用key为string的Trie超过了key为int的哈希表

unordered_map耗时:1269毫秒

Trie耗时:750毫秒


6.最后的补充


Trie又称为字典树,是哈希树的一个变种。

Trie有一个特点是:有字符串公共前缀的信息

比如字符串"Siliphen"和字符串"Siliphen Lee"的公共前缀是"Siliphen"

在匹配字符串"Siliphen Lee"时,一定会先发现是否存在"Siliphen",

因为走的前缀树路径都是一样的。

是否还记得KMP算法。一种带有回溯的字符串匹配算法。

如果Trie+KMP的话,就变成另一个玩意:AC自动机。

AC自动机用于编译原理。

也可以用来做格斗游戏的摇招判定。就像拳皇KOF的那种摇招系统。







本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Trie实践:一种比哈希表更快的数据结构 的相关文章

  • Ubuntu 16.04 Qt clang-format 插件安装使用教程

    Ubuntu 16 04 Qt clang format 插件安装使用教程 Qt安装下载安装修改qt环境变量 LLVM安装安装clang format配置qt打开工程文件配置clang format Qt安装 最新的qt5 12支持保存代码
  • HTTP请求首部——Authorization

    前几天的任务需要用到Authorization认证 xff0c 任务比较急 xff0c 就照着给的例子写好了 xff0c 现在任务结束了 xff0c 还是来了解一下这个Authorization Authorization 是一个HTTP安
  • 如何真正理解用户标签体系?

    对用户标签的理解不够透彻 xff1f 用户标签体系创建的方法论总是三头两绪 xff1f 具体业务场景中 xff0c 经常找不到数据分析的思路 xff1f 本文根据神策数据业务咨询师钟秉哲以 构建用户标签体系 xff0c 助力企业精细化运营
  • ubuntu设置tightvncserver自动启动

    vi etc init d vnc bin bash PATH 61 34 PATH usr bin 34 export USER 61 34 root 34 DISPLAY 61 34 1 34 DEPTH 61 34 24 34 GEO
  • 毕业设计小车搭建(1)测试思岚A1雷达数据

    采用的思岚A1型号的雷达 ubuntu系统上采集雷达数据并rviz显示 主要是根据官网给的教程步骤一步一步走下来的 思岚激光雷达 首先下载对应的官方功能包GitHub Slamtec rplidar ros 功能包创建结束后注意环境变量写入
  • 如何关闭docker容器里的进程

    如何关闭docker容器里的进程 1 使用docker exec 容器名 ps ef命令查看进程信息 示例 xff1a 创建名为 34 redis 34 的容器 xff0c 并在容器内部和宿主机中查看容器中的进程信息 xff1a 2 然后进
  • 浅谈嵌入式与互联网(详细)

    纲要 一 什么叫嵌入式 xff0c 以及与人工智能的关系 xff1f 二 嵌入式岗位 三 浅谈嵌入式开发优缺点 四 与互联网 CS相关的 xff0c 如平台服务器 xff0c 前端 APP 软件 对比 五 能力要求和薪资 参考知乎 以下均采
  • 那一年读过的技术经典书

    转载请注明 xff1a http blog csdn net xinzhangyanxiang article details 10199757 大学刚毕业 xff0c 总结起来读过的书并不算多 xff0c 而且主要集中在大四的时期读的 x
  • 关于Ubuntu的串口链接上但接收不了数据问题

    作为开始小白的我 xff0c 一开始链接串口以为按装了CuteCom就能使用 xff0c 不知道使用串口前是需要打开权限的 xff0c 所以我在CuteCom的时候链接上但收不了数据 xff0c 后来才知道打开权限 首先第一步 1 打开你的
  • 进程的调用

    每个进程都有一个非负整数的唯一ID xff0c 用pid t结构表示其ID xff0c 其中ID为0的是调度进程 xff0c 常被称为交换进程 是内核的一部分为系统进程 xff0c ID为1的是init进程 xff0c 他是一个普通用户进程
  • Oracle VM VirtualBox UUID already exists 问题解决

    当我们在VirtualBox下装完系统 xff0c 想拷贝一份备用的时候 xff0c 导入备份的虚拟磁盘的会提示VirtualBox UUID already exists问题 xff0c 其实这个问题在网络上早就有各种不同的解决方案了 x
  • Mac下将文件复制到移动硬盘

    现象分析 xff1a 如果你在使用Mac系统时 xff0c 发现Mac系统和移动硬盘之间无法拷贝数据 xff0c 很有可能你的移动硬盘是NTFS格式的 xff0c 因为目前苹果系统的硬盘格式暂时不兼容这样的格式拷贝 xff0c 只能从NTF
  • Maven打包时报Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.2:war解决方案

    问题现象 xff1a 用Maven打包时 xff0c 报Failed to execute goal org apache maven plugins maven war plugin 2 2 war错误 原因分析 xff1a 打包时在We
  • mac系统中怎么把显示在桌面上的磁盘图标取消掉?

    问题现象 xff1a 安装一些软件时 xff0c 桌面上总会出现外置磁盘图标如图1 下面就简单介绍下怎样取消这种外置图标 图1 解决方案 xff1a finder xff0d 偏好设置 xff0d 通用 xff0d 外置设置 取消前面对话框
  • tensorflow深度学习实战笔记(三):使用tensorflow lite把训练好的模型移植到手机端,编译成apk文件

    目录 一 准备工作 1 1模型训练 1 2模型固化和pb转tflite 1 3下载tensorflow源码 1 4安装android studio 二 在Android studio中进行开发 2 1修改app的build gradle文件
  • nvidia和cuda对应以及cuda和cuDNN对应版本

  • 修改spring Boot启动时的默认图案Banner

    一 修改Banner spring Boot启动的时候会有一个默认的启动图案 如下图 39 39 39 39 96 39 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
  • Java线程池自学手册Executor的使用

    准备做一个系列文章 xff0c 将零散的知识整理起来分享给大家 xff0c 希望给大家的工作和学习带来帮助 目录 1 Executor 2 ExecutorService 3 Executors 4 ThreadPoolExecutor 5
  • 常用开发资源整理(更新日:2017/4/26)

    说明 xff1a 为了方便 xff0c 今后将工作中用到一些常用的资源链接进行整理 xff0c 初衷是想发些各版本的冷资源 xff0c 免得在需要的时候花大量时间寻找 一 开发语言 1 Spring各版本压缩包下载 http repo sp
  • 博客地址迁移www.xiangquba.cn

    大家好 xff0c 非常感谢大家一直以来对我的关注 xff0c 博客有一年时间没有更新了 xff0c 其实我并没有停止分享 xff0c 只是这一年时间并没有在csdn上更新 xff0c 原因是因为起初csdn有一个自己给csdn发送短信绑定

随机推荐

  • FreeRTOS 任务函数里面的死循环

    任务函数是一个无限循环且不带返回值的函数 任务必须是死循环 xff0c 否则任务将经过 LR 返回 xff0c 如果 LR 指向了非法内存就会产生HardFault Handler xff0c 而 FreeRTOS 指向一个死循环 xff0
  • 使用libcurl异步发送http请求

    在工作中需要完成一个工具 xff0c 该工具主要的用途就是向指定的服务器和端口发送http请求 xff0c 为了提高性能 xff0c 采用多线程的方式进行 xff0c 同时采用libcurl的异步形式 代码如下 xff0c 在其中添加一些注
  • 【Kubernetes】K8S实践感受HPA的功能

    1 确保metrics server安装好 span class token punctuation span root 64 jdmaster span class token punctuation span span class to
  • 拓扑结构与IP地址划分

    拓扑结构 分为总线型 星型和环形 环形要比星型的可靠性更高 公司的网络拓扑结构是星型 中心是交换机 IP发展 第一阶段 分为A E类 第二阶段 子网划分 第三阶段 无类域间路由 现在多采用这一种 不仅满足大网分小 也满足子网合成超网 无类域
  • 多网卡udp绑定问题

    在多网卡机器上 udp包 协议会自动根据路由最优选择从哪个网卡发数据包出去 即使你在此之前把改socket绑定到了另一个网卡上 参考文章 https blog csdn net Scarlett OHara article details
  • QIODevice::readyRead()

    void QIODevice readyRead This signal is emitted once every time new data is available for reading from the device 每次当新数据
  • 论文投稿参考——如何撰写和发表SCI论文

    如何撰写和发表SCI论文 汪景秀 提要 对从事基础研究的科学工作者 能否在SCI收录的杂志发表论文 是能否进入学术前沿 在国际公认的同一个平台上参与学术竞争 做出原创性贡献的一个基本标志 那么怎样的论文才是合格的 xff1f 本文提出一些建
  • Win7电脑遇到蓝屏,并报错:IRQL NOT LESS OR EQUAL

    这一阵电脑老是蓝屏 xff0c 重启后window和360都检测到之前发生过蓝屏 xff0c 但是都不能够修复成功 一开始直接在百度上搜电脑蓝屏 xff0c 也没啥大的收获 直到有次注意到蓝屏时还有错误类型 xff1a IRQL NOT L
  • VS使用收获总结

    诊断工具在调试 gt 窗口 gt 显示诊断工具 报CL exe问题 可能是IDE的问题 比如写一个最简单的hello world代码来进行检验 可以在查找时使用正则表达式 xff0c 比如想要搜索 行尾为 61 1 xff1b 的文本 可以
  • 进程间的mutex

    设两个进程共用一个临界资源的互斥信号量mutex 61 1 xff0c 当mutex xff1d xff0d 1时表示 一个进程进入了临界区 xff0c 另一个进程等待 没有一个进程进入临界区 两个进程都进入临界区 两个进程都在等待 互斥信
  • 128种chatGPT可以为人类做的事情

    1 充当英语翻译 充当英语翻译员 拼写纠正员和改进员 xff0c 我会用任何语言与你交谈 xff0c 你会检测语言 xff0c 翻译它并用我的文本的更正和 改进版本用英语回答 2 充当词典 充当英英词典 xff0c 对于给出的英文单词 xf
  • thing_10

    Choose Your Tools with Care Modern applications are very rarely built from scratch They are assembled using existing too
  • Distinguish Business Exceptions from Technical 21

    Distinguish Business Exceptions from Technical There are basically two reasons that things go wrong at runtime technical
  • Do Lots of Deliberate Practice 22

    Do Lots of Deliberate Practice Deliberate practice is not simply performing a task If you ask yourself Why am I performi
  • 只抓 5 件事,让管理更有效

    管理的第一性原理 效率 效益 环境的巨大不确定性带来前所未有的挑战 练好基本功 xff1a 提升管理质量 5件事 xff1a 计划 组织 控制 领导 人员配备 计划的意义 集中资源 注意力管理 协同 让计划滚动起来 控制 xff1a 计划
  • 二维vector,clear()操作请慎重,当心遇到vector subscript out of range问题

    问题 今天想要用vector实现二维数组的功能 尝试了把二维vector 谁知道刚上手就遇到了雷 代码的形式大致如下 vector lt vector lt int gt gt vv 3 vv clear for int i 61 0 i
  • UART详解

    UART 通用异步收发传输器 xff08 Universal Asynchronous Receiver Transmitter xff0c 通常称作UART xff09 是一种串行异步收发协议 xff0c 应用十分广泛 UART工作原理是
  • OpenCV学习笔记(10)CvMat 与 STL vector 的格式转换与数据读写

    用STL vector来进行数组的数据读写非常方便 xff0c 可以动态调整数组大小 xff0c 不过在OpenCV里使用vector时 xff0c 要保存vector数组的数据 xff0c 就需要转换为 CvMat 格式 比如有一个双通道
  • 回首5年嵌入式开发之路

    转眼毕业5年 xff0c 也是从事嵌入式软件开发5年 回首过往 xff0c 发现都在埋头敲代码 xff0c 从没有过梳理过自己的知识脉络 xff0c 利用这里梳理一下自己的知识结构 xff0c 首先梳理的是自己从事的第一个工作也是最简单的单
  • Trie实践:一种比哈希表更快的数据结构

    本文乃Siliphen原创 转载请注明出处 xff1a http blog csdn net stevenkylelee 本文分为5部分 从我 思考和认知的角度 xff0c 由浅到深带你认识Trie数据结构 1 桶状哈希表与直接定址表的概念