正向最大匹配中文分词算法

article/2025/9/23 0:42:32

中文分词一直都是中文自然语言处理领域的基础研究。目前,网络上流行的很多中文分词软件都可以在付出较少的代价的同时,具备较高的正确率。而且不少中文分词软件支持Lucene扩展。但不管实现如何,目前而言的分词系统绝大多数都是基于中文词典的匹配算法。

 

在这里我想介绍一下中文分词的一个最基础算法:最大匹配算法 (Maximum Matching,以下简称MM算法) 。MM算法有两种:一种正向最大匹配,一种逆向最大匹配。

 

● 算法思想

 

正向最大匹配算法:从左到右将待分词文本中的几个连续字符与词表匹配,如果匹配上,则切分出一个词。但这里有一个问题:要做到最大匹配,并不是第一次匹配到就可以切分的 。我们来举个例子:

           待分词文本:   content[]={"中","华","民","族","从","此","站","起","来","了","。"}

           词表:   dict[]={"中华", "中华民族" , "从此","站起来"}

(1) 从content[1]开始,当扫描到content[2]的时候,发现"中华"已经在词表dict[]中了。但还不能切分出来,因为我们不知道后面的词语能不能组成更长的词(最大匹配)。

(2) 继续扫描content[3],发现"中华民"并不是dict[]中的词。但是我们还不能确定是否前面找到的"中华"已经是最大的词了。因为"中华民"是dict[2]的前缀。

(3) 扫描content[4],发现"中华民族"是dict[]中的词。继续扫描下去:

(4) 当扫描content[5]的时候,发现"中华民族从"并不是词表中的词,也不是词的前缀。因此可以切分出前面最大的词——"中华民族"。

 

由此可见,最大匹配出的词必须保证下一个扫描不是词表中的词或词的前缀才可以结束。

 

 

● 算法实现

 

词表的内存表示: 很显然,匹配过程中是需要找词前缀的,因此我们不能将词表简单的存储为Hash结构。在这里我们考虑一种高效的字符串前缀处理结构——Trie树《Trie Tree 串集合查找 》。这种结构使得查找每一个词的时间复杂度为O(word.length),而且可以很方便的判断是否匹配成功或匹配到了字符串的前缀。

 

下图是我们建立的Trie结构词典的部分,(词语例子:"中华","中华名族","中间","感召","感召力","感受")。

           

(1) 每个结点都是词语中的一个汉字。

(2) 结点中的指针指向了该汉字在某一个词中的下一个汉字。这些指针存放在以汉字为key的hash结构中。

(3) 结点中的"#"表示当前结点中的汉字是从根结点到该汉字结点所组成的词的最后一个字。

 

TrieNode源代码如下:

Java代码  收藏代码
  1. import java.util.HashMap;  
  2. /** 
  3.  * 构建内存词典的Trie树结点 
  4.  *  
  5.  * @author single(宋乐) 
  6.  * @version 1.01, 10/11/2009 
  7.  */  
  8. public class TrieNode {  
  9.   
  10.     /**结点关键字,其值为中文词中的一个字*/  
  11.     public char key=(char)0;  
  12.     /**如果该字在词语的末尾,则bound=true*/  
  13.     public boolean bound=false;  
  14.     /**指向下一个结点的指针结构,用来存放当前字在词中的下一个字的位置*/  
  15.     public HashMap<Character,TrieNode> childs=new HashMap<Character,TrieNode>();  
  16.       
  17.     public TrieNode(){  
  18.     }  
  19.       
  20.     public TrieNode(char k){  
  21.         this.key=k;  
  22.     }  
  23. }  

 

这套分词代码的优点是:

(1) 分词效率高。纯内存分词速度大约240.6ms/M,算上IO读取时间平均1.6s/M。测试环境:Pentium(R)  4 CPU  3.06GHZ、1G内存。

(2) 传统的最大匹配算法需要实现确定一个切分的最大长度maxLen。如果maxLen过大,则大大影响分词效率。而且超过maxLen的词语将无法分出来。但本算法不需要设置maxLen。只要词表中有的词,不管多长,都能够切分。

(3) 对非汉字的未登录词具备一定的切分能力。比如英文单词[happy, steven],产品型号[Nokia-7320],网址[http://www.sina.com]等。

 

缺点也很明显:

(1) 暂时无词性标注功能,对中文汉字的未登录词无法识别,比如某个人名。

(2) 内存占用稍大,目前词表为86725个词。如果继续扩展词表,很有可能内存Trie树将非常庞大。

 

代码的进一步优化方案:

(1) 想在内存占用空间上降低代价。实际上Trie树主要的空间消耗在每个结点的指针HashMap上。我使用的是JDK中的HashMap,其加载因子为 loadFactor= 0.75,初始化空间大小为DEFAULT_INITIAL_CAPACITY= 16。每次存储数据量超过 loadFactor*DEFAULT_INITIAL_CAPACITY的时候,整个Map空间将翻倍。 因此会照成一定的空间浪费。

 

      但目前还没有想到很好的办法,即能够随机定位到下一个结点的指针,又降低Hash结构的空间代价?

转自:http://hxraid.iteye.com/blog/667134

 

【串和序列处理 3】Trie Tree 串集合查找

文章分类:综合技术

Trie 树, 又称字典树,单词查找树。它来源于retrieval(检索)中取中间四个字符构成(读音同try)。用于存储大量的字符串以便支持快速模式匹配。主要应用在信息检索领域。

 

Trie 有三种结构: 标准trie (standard trie)、压缩trie、后缀trie(suffix trie) 。 最后一种将在《字符串处理4:后缀树》中详细讲,这里只将前两种。

 

 

1. 标准Trie (standard trie)

 

标准 Trie树的结构 : 所有含有公共前缀的字符串将挂在树中同一个结点下。实际上trie简明的存储了存在于串集合中的所有公共前缀。 假如有这样一个字符串集合X{bear,bell,bid,bull,buy,sell,stock,stop}。它的标准Trie树如下图:

 

 

      上图(蓝色圆形结点为内部结点,红色方形结点为外部结点),我们可以很清楚的看到字符串集合X构造的Trie树结构。其中从根结点到红色方框叶子节点所经历的所有字符组成的串就是字符串集合X中的一个串。

 

      注意这里有一个问题: 如果X集合中有一个串是另一个串的前缀呢? 比如,X集合中加入串bi。那么上图的Trie树在绿色箭头所指的内部结点i 就应该也标记成红色方形结点。这样话,一棵树的枝干上将出现两个连续的叶子结点(这是不合常理的)。

 

      也就是说字符串集合X中不存在一个串是另外一个串的前缀 。如何满足这个要求呢?我们可以在X中的每个串后面加入一个特殊字符$(这个字符将不会出现在字母表中)。这样,集合X{bear$、bell$、.... bi$、bid$}一定会满足这个要求。

 

      总结:一个存储长度为n,来自大小为d的字母表中s个串的集合X的标准trie具有性质如下:

      (1) 树中每个内部结点至多有d个子结点。

      (2) 树有s个外部结点。

      (3) 树的高度等于X中最长串的长度。

      (4) 树中的结点数为O(n)。

 

 

标准 Trie树的查找

       对于英文单词的查找,我们完全可以在内部结点中建立26个元素组成的指针数组。如果要查找a,只需要在内部节点的指针数组中找第0个指针即可(b=第1个指针,随机定位)。时间复杂度为O(1)。

 

      查找过程:假如我们要在上面那棵Trie中查找字符串bull (b-u-l-l)。

      (1) 在root结点中查找第('b'-'a'=1)号孩子指针,发现该指针不为空,则定位到第1号孩子结点处——b结点。

      (2) 在b结点中查找第('u'-'a'=20)号孩子指针,发现该指针不为空,则定位到第20号孩子结点处——u结点。

      (3) ... 一直查找到叶子结点出现特殊字符'$'位置,表示找到了bull字符串

 

      如果在查找过程中终止于内部结点,则表示没有找到待查找字符串。

 

      效率:对于有n个英文字母的串来说,在内部结点中定位指针所需要花费O(d)时间,d为字母表的大小,英文为26。由于在上面的算法中内部结点指针定位使用了数组随机存储方式,因此时间复杂度降为了O(1)。但是如果是中文字,下面在实际应用中会提到。因此我们在这里还是用O(d)。 查找成功的时候恰好走了一条从根结点到叶子结点的路径。因此时间复杂度为O(d*n)。

      但是,当查找集合X中所有字符串两两都不共享前缀时,trie中出现最坏情况。除根之外,所有内部结点都自由一个子结点。此时的查找时间复杂度蜕化为O(d*(n^2))

 

 

标准 Trie树的Java代码实现:

 

Java代码  收藏代码
  1. package net.hr.algorithm.stroper;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. enum NodeKind{LN,BN};  
  6. /** 
  7.  * Trie结点 
  8.  */  
  9. class TrieNode{  
  10.   
  11.     char key;  
  12.     TrieNode[] points=null;  
  13.     NodeKind kind=null;  
  14. }  
  15. /** 
  16.  * Trie叶子结点 
  17.  */  
  18. class LeafNode extends TrieNode{  
  19.       
  20.     LeafNode(char k){  
  21.         super.key=k;  
  22.         super.kind=NodeKind.LN;  
  23.     }  
  24. }  
  25. /** 
  26.  * Trie内部结点 
  27.  */  
  28. class BranchNode extends TrieNode{  
  29.       
  30.     BranchNode(char k){  
  31.         super.key=k;  
  32.         super.kind=NodeKind.BN;  
  33.         super.points=new TrieNode[27];  
  34.     }  
  35. }  
  36. /** 
  37.  * Trie树 
  38.  * @author heartraid 
  39.  */  
  40. public class StandardTrie {  
  41.   
  42.     private TrieNode root=new BranchNode(' ');  
  43.       
  44.     /** 
  45.      * 想Tire中插入字符串 
  46.      */  
  47.     public void insert(String word){  
  48.           
  49.         //System.out.println("插入字符串:"+word);  
  50.         //从根结点出发  
  51.         TrieNode curNode=root;  
  52.         //为了满足字符串集合X中不存在一个串是另外一个串的前缀   
  53.         word=word+"$";  
  54.         //获取每个字符  
  55.         char[] chars=word.toCharArray();  
  56.         //插入  
  57.         for(int i=0;i<chars.length;i++){  
  58.             //System.out.println("   插入"+chars[i]);   
  59.             if(chars[i]=='$'){  
  60.                 curNode.points[26]=new LeafNode('$');  
  61.             //  System.out.println("   插入完毕,使当前结点"+curNode.key+"的第26孩子指针指向字符:$");  
  62.             }  
  63.             else{  
  64.                 int pSize=chars[i]-'a';  
  65.                 if(curNode.points[pSize]==null){  
  66.                     curNode.points[pSize]=new BranchNode(chars[i]);  
  67.                 //  System.out.println("   使当前结点"+curNode.key+"的第"+pSize+"孩子指针指向字符: "+chars[i]);  
  68.                     curNode=curNode.points[pSize];  
  69.                 }  
  70.                 else{  
  71.                 //  System.out.println("   不插入,找到当前结点"+curNode.key+"的第"+pSize+"孩子指针已经指向字符: "+chars[i]);  
  72.                     curNode=curNode.points[pSize];  
  73.                 }  
  74.             }  
  75.         }  
  76.     }  
  77.     /** 
  78.      * Trie的字符串全字匹配 
  79.      */  
  80.     public boolean fullMatch(String word){  
  81.         //System.out.print("查找字符串:"+word+"/n查找路径:");  
  82.         //从根结点出发  
  83.         TrieNode curNode=root;  
  84.         //获取每个字符  
  85.         char[] chars=word.toCharArray();  
  86.           
  87.         for(int i=0;i<chars.length;i++){  
  88.             if(curNode.key=='$'){  
  89.                 System.out.println('&');  
  90.             //  System.out.println(" 【成功】");  
  91.                 return true;  
  92.             }else{  
  93.                 System.out.print(chars[i]+" -> ");     
  94.                 int pSize=chars[i]-'a';  
  95.                 if(curNode.points[pSize]==null){  
  96.                 //  System.out.println(" 【失败】");  
  97.                     return false;  
  98.                 }else{  
  99.                     curNode=curNode.points[pSize];  
  100.                 }  
  101.             }  
  102.         }  
  103.     //  System.out.println("  【失败】");  
  104.         return false;  
  105.     }  
  106.       
  107.   
  108.     /** 
  109.      * 先根遍历Tire树 
  110.      */  
  111.     private void preRootTraverse(TrieNode curNode){  
  112.           
  113.         if(curNode!=null){  
  114.             System.out.print(curNode.key+" ");  
  115.             if(curNode.kind==NodeKind.BN)  
  116.                 for(TrieNode childNode:curNode.points)  
  117.                     preRootTraverse(childNode);  
  118.         }  
  119.           
  120.     }  
  121.     /** 
  122.      * 得到Trie根结点 
  123.      */  
  124.     public TrieNode getRoot(){  
  125.         return this.root;  
  126.     }  
  127.     /** 
  128.      * 测试 
  129.      */  
  130.     public static void main(String[] args) {  
  131.           
  132.         StandardTrie trie=new StandardTrie();  
  133.         trie.insert("bear");  
  134.         trie.insert("bell");  
  135.         trie.insert("bid");  
  136.         trie.insert("bull");  
  137.         trie.insert("buy");  
  138.         trie.insert("sell");  
  139.         trie.insert("stock");  
  140.         trie.insert("stop");  
  141.           
  142.         trie.preRootTraverse(trie.getRoot());  
  143.           
  144.         trie.fullMatch("stoops");  
  145.     }  
  146. }  

 

中文词语的 标准 Trie树

      由于中文的字远比英文的26个字母多的多。因此对于trie树的内部结点,不可能用一个26的数组来存储指针。如果每个结点都开辟几万个中国字的指针空间。估计内存要爆了,就连磁盘也消耗很大。

 

      一般我们采取这样种措施:

     (1) 以词语中相同的第一个字为根组成一棵树。这样的话,一个中文词汇的集合就可以构成一片Trie森林。这篇森林都存储在磁盘上。森林的root中的字和root所在磁盘的位置都记录在一张以Unicode码值排序的有序字表中。字表可以存放在内存里。

    (2) 内部结点的指针用可变长数组存储。

 

     特点:由于中文词语很少操作4个字的,因此Trie树的高度不长。查找的时间主要耗费在内部结点指针的查找。因此将这项指向字的指针按照字的Unicode码值排序,然后加载进内存以后通过二分查找能够提高效率。

 

 

标准Trie树的应用和优缺点

     (1) 全字匹配:确定待查字串是否与集合的一个单词完全匹配。如上代码fullMatch()。

     (2) 前缀匹配:查找集合中与以s为前缀的所有串。

 

     注意:Trie树的结构并不适合用来查找子串。这一点和前面提到的PAT Tree以及后面专门要提到的Suffix Tree的作用有很大不同。

 

      优点: 查找效率比与集合中的每一个字符串做匹配的效率要高很多。在o(m)时间内搜索一个长度为m的字符串s是否在字典里。

      缺点:标准Trie的空间利用率不高,可能存在大量结点中只有一个子结点,这样的结点绝对是一种浪费。正是这个原因,才迅速推动了下面所讲的压缩trie的开发。

 

 

 

2. 压缩Trie (compressed trie)

 

      压缩Trie类似于标准Trie,但它能保证trie中的每个内部结点至少有两个子节点(根结点除外)。通过把单子结点链压缩进叶子节点来执行这个规则。

 

压缩Trie的定义

 

      冗余结点(redundant node):如果T的一个非根内部结点v只有一个子结点,那么我们称v是冗余的。

      冗余链(redundant link):如上标准Trie图中,内部结点e只有一个内部子结点l,而l也只有一个叶子结点。那么e-l-l就构成了一条冗余链。

      压缩(compressed):对于冗余链 v1- v2- v3- ... -vn,我们可以用单边v1-vn来替代。

 

      对上面标准Trie的图压缩之后,形成了Compressed Trie的字符表示图如下:

压缩Trie的性质和优势:

 

     与标准Trie比较,压缩Trie的结点数与串的个数成正比了,而不是与串的总长度成正比。一棵存储来自大小为d的字母表中的s个串的结合T的压缩trie具有如下性质:

 

     (1) T中的每个内部结点至少有两个子结点,至多有d个子结点。

     (2) T有s个外部结点。

     (3) T中的结点数为O(s)


     存储空间从标准Trie的O(n)降低到压缩后的O(s),其中n为集合T中总字符串长度,s为T中的字符串个数。

 

压缩Trie的压缩表示

 

     上面的图是压缩Trie的字符串表示。相比标准Trie而言,确实少了不少结点。但是细心的读者会发现,叶子结点中的字符数量增加了,比如结点ell,那么这种压缩空间的效率当然会打折扣了。那么有什么好办法呢,这里我们介绍一种压缩表示方法。即把所有结点中的字符串用三元组的形式表示如下图:

 

    

      其中三元组(i,j,k)表示S[i]的从第j个位置到第k个位置间的子串。比如(5,1,3,)表示S[5][1...3]="ell"。

 

      这种压缩表示的一个巨大的优点就是:无论结点需要存储多长的字串,全部都可以用一个三元组表示,而且三元组所占的空间是固定有限的。但是为了做到这一点,必须有一张辅助索引结构(如上图右侧s0—s7所示)。   

 转自:http://hxraid.iteye.com/blog/618962

 


http://chatgpt.dhexx.cn/article/d73SdbdN.shtml

相关文章

NLP|中文分词技术及应用

摘要:中文分词是中文信息处理的重要基础,本文详细阐述了目前主要的几种中文分词算法的技术原理 、中文分词目前的瓶颈和评价准则,以及中文分词的具体应用。 中文分词指将一个汉字序列切分成一个个单独的词。现有的中文分词算法有五大类:基于词典的方法,基于统计的方法,基…

入门科普:一文看懂NLP和中文分词算法(附代码举例)

导读&#xff1a;在人类社会中&#xff0c;语言扮演着重要的角色&#xff0c;语言是人类区别于其他动物的根本标志&#xff0c;没有语言&#xff0c;人类的思维无从谈起&#xff0c;沟通交流更是无源之水。 所谓“自然”乃是寓意自然进化形成&#xff0c;是为了区分一些人造语言…

中文分词算法—— 基于词典的方法

1、基于词典的方法&#xff08;字符串匹配&#xff0c;机械分词方法&#xff09; 定义:按照一定策略将待分析的汉字串与一个“大机器词典”中的词条进行匹配&#xff0c;若在词典中找到某个字符串&#xff0c;则匹配成功。 按照扫描方向的不同&#xff1a;正向匹配和逆向匹配…

【NLP】中文分词:原理及分词算法

一、中文分词 词是最小的能够独立活动的有意义的语言成分&#xff0c;英文单词之间是以空格作为自然分界符的&#xff0c;而汉语是以字为基本的书写单位&#xff0c;词语之间没有明显的区分标记&#xff0c;因此&#xff0c;中文词语分析是中文信息处理的基础与关键。 Lucene中…

常见分词算法综述

常见分词算法综述 文章目录 常见分词算法综述一、基于词典的分词1. 最大匹配分词算法2. 最短路径分词算法&#xff1a;2.1基于dijkstra算法求最短路径&#xff1a;2.2N-dijkstra算法求最短路径&#xff1a;2.3. 基于n-gram model的分词算法&#xff1a; 二、基于字的分词算法生…

中文分词原理及分词工具介绍

转自&#xff1a;https://blog.csdn.net/flysky1991/article/details/73948971 本文首先介绍下中文分词的基本原理&#xff0c;然后介绍下国内比较流行的中文分词工具&#xff0c;如jieba、SnowNLP、THULAC、NLPIR&#xff0c;上述分词工具都已经在github上开源&#xff0c;后…

中文分词常见方法

中文分词是中文文本处理的一个基础步骤&#xff0c;也是中文人机自然语言交互的基础模块。不同于英文的是&#xff0c;中文句子中没有词的界限&#xff0c;因此在进行中文自然语言处理时&#xff0c;通常需要先进行分词&#xff0c;分词效果将直接影响词性、句法树等模块的效果…

自然语言处理之中文分词技术与算法

1 正向最大匹配法 1.1 正向最大匹配&#xff08;Maximum Match Method, MM法&#xff09;的基本思想&#xff1a; 假定分词词典中的最长词有i个汉字字符&#xff0c;则用被处理文档的当前字串中的前i个字作为匹配字段&#xff0c;查找字典。若字典中存在这样的一个i字词&#…

列举:中文分词算法你知道几种?

列举&#xff1a;中文分词算法你知道几种&#xff1f; 摘要&#xff1a;看似普通的一句话&#xff0c;甚至几个词&#xff0c;在机器眼里都要经过好几道“程序”。这个过程主要靠中文分词算法&#xff0c;这个算法分为三大类&#xff1a;机械分词算法、基于n元语法的分词算法、…

(转)Linux下管道的原理

7.1.1 Linux管道的实现机制 在Linux中&#xff0c;管道是一种使用非常频繁的通信机制。从本质上说&#xff0c;管道也是一种文件&#xff0c;但它又和一般的文件有所不同&#xff0c;管道可以克服使用文件进行通信的两个问题&#xff0c;具体表现为&#xff1a; 限制管…

Linux之进程间通信——管道

文章目录 前言一、进程间通信1.概念2.目的3.进程间通信分类 二、管道1.管道介绍2.管道分类1.匿名管道pipi创建管道文件&#xff0c;打开读写端fork子进程关闭父进程的写入端&#xff0c;关闭子进程的读取端读写特征管道特征 2.命名管道mkfifo创建管道文件删除管道文件通信 三、…

Linux系统中的管道通信

目录 管道如何通信 管道的访问控制机制&#xff1a; 匿名管道 匿名管道数据传输的原理 如何使用&#xff08;代码案例&#xff09; 用C/C代码编译实现父子进程间通信案例 &#xff1a; 思路 实现 命名管道 为什么要有命名管道 回归进程间通信的本质 匿名管道的短板…

linux 管道 (单管道与双管道)

管道的局限性&#xff1a; ①数据不能进程自己写&#xff0c;自己读。 ②管道中数据不可反复读取。-旦读走, 管道中不再存在。 ③采用半双工通信方式&#xff0c;数据只能在单方向上流动。 ④只能在有公共祖先的进程间使用管道 单通道将小写字母改为大写例程&#xff1a; #in…

Linux 管道文件

管道分为无名管道和有名管道两种管道&#xff0c;管道文件是建立在内存之上可以同时被两个进程访问的文件。 先来说说有名管道&#xff1a; mkfifo函数创建有名管道&#xff0c;属于系统调用。 在linux操作系统中为实现下述功能&#xff0c; 先创建一个有名管道文件fifo。 …

【Linux】Linux 管道命令Cut、sort、wc、uniq、tee、tr【一】

目录 &#x1f40b;Cut— 根据条件 从命令结果中 提取 对应内容 &#x1f40b;sort—可针对文本文件的内容&#xff0c;以行为单位来排序。 &#x1f40b;wc命令— 显示/统计 指定文件 字节数, 单词数, 行数 信息. &#x1f40b; uniq— 用于检查及删除文本文件中重复出现的…

Linux管道命令(pipe)全

目录 选取命令&#xff1a;cut、grep 传送门 排序命令&#xff1a;sort、wc、uniq 传送门 双向重定向&#xff1a;tee 字符转换命令&#xff1a;tr、col、join、paste、expand 传送门 划分命令&#xff1a;split 传送门 参数代换&#xff1a;xargs 传送门 关于减号…

Linux中管道命令的用法

原文地址&#xff1a;http://blog.csdn.net/wirelessqa/article/details/8968381 一. 管道命令 管道命令操作符是&#xff1a;”|”,它只能处理经由前面一个指令传出的正确输出信息&#xff0c;对错误信息信息没有直接处理能力。然后&#xff0c;传递给下一个命令&#xff0c;…

Linux管道符

管道 1、管道符 管道符&#xff1a;| 作用&#xff1a;管道是一种通信机制&#xff0c;通常用于进程间的通信。它表现出来的形式将前面每一个进程的输出&#xff08;stdout&#xff09;直接作为下一个进程的输入&#xff08;stdin&#xff09;。 2、过滤功能 # ls / | gr…

linux管道相关命令

目标 cutsortwcuniqteetrsplitawksedgrep 准备数据 zhangsan 68 99 26 lisi 98 66 96 wangwu 38 33 86 zhaoliu 78 44 36 maq 88 22 66 zhouba 98 44 46以上是成绩表信息 使用 逗号 分割, 第一列 是 姓名, 第二列是 语文成绩, 第三列是 数学成绩, 第四列是 英语成绩 需求1: …

Linux管道到底能有多快?

【CSDN 编者按】本文作者通过一个示例程序&#xff0c;演示了通过Linux管道读写数据的性能优化过程&#xff0c;使吞吐量从最初的 3.5GiB/s&#xff0c;提高到最终的 65GiB/s。即便只是一个小例子&#xff0c;可它涉及的知识点却不少&#xff0c;包括零拷贝操作、环形缓冲区、分…