BK Tree

2023-11-11 10:48
文章标签 tree bk

本文主要是介绍BK Tree,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

BK Tree或Burkhard Keller Tree是一种数据结构,用于根据编辑距离(Levenshtein距离)概念执行拼写检查。 BK树也用于近似字符串匹配。基于该数据结构,可以实现许多软件中的各种自动校正特征。

假设我们有一个单词字典,然后我们有一些其他的单词要在字典中检查拼写错误。我们需要收集字典中与给定单词非常接近的所有单词。例如,如果我们检查一个单词“ruk”,我们将有{“truck”,“buck”,“duck”,......}。因此,拼写错误可以通过删除单词中的字符或在单词中添加新字符或通过将单词中的字符替换为适当的字符来更正。因此,我们将使用编辑距离作为衡量错误拼写单词与字典中单词的正确性和匹配的度量。

现在,让我们看看我们的BK树的结构。与所有其他树一样,BK树由节点和边组成。 BK树中的节点将代表我们字典中的单个单词,并且节点的数量与字典中单词的数量完全相同。边将包含一些整数权重,它将告诉我们从一个节点到另一个节点的编辑距离。假设我们有一个从节点u到节点v的边缘有一些边缘权重w,那么w是将字符串u转换为v所需的编辑距离。

考虑字典集合:{“help”,“hell”,“hello”}。因此,对于这一字典集合,我们的BK树将如下所示。

 

image.png

BK树中的每个节点都只有一个具有相同编辑距离的子节点。如果我们在插入时遇到编辑距离的某些冲突,我们将向子集传播插入过程,直到找到字符串节点的适当父节点。

BK树中的每个插入都将从根节点开始。根节点可以是字典中的任何单词。

例如,在上面的字典中添加另一个单词“shell”。现在Dict [] = {“help”,“hell”,“hello”,“shell”}。现在显而易见的是,“shell”具有与“hello”相同的编辑距离来自根节点“help”,即2.因此,我们遇到了碰撞。因此,我们通过在预先存在的冲突节点上递归地执行此插入过程来处理此冲突。

因此,现在我们不是在根节点“help”处插入“shell”,而是将它插入到碰撞节点“hello”。现在新的节点“shell”被添加到树中,它的节点“hello”作为其父节点,edge-weigth为2(编辑距离)。下图描述了插入后的BK树。

 

image.png

所以,到现在为止,我们已经了解了如何构建我们的BK树。现在,问题是如何找到拼写错误的单词最接近的正确单词?首先,我们需要设置容差值。此容差值只是从拼写错误的单词到字典中正确单词的最大编辑距离。因此,要在容差限度内找到符合条件的正确单词,Naive方法将迭代字典中的所有单词并收集容差限制内的单词。但是这种方法具有O(n * m * n)时间复杂度(n是dict []中的单词数,m是正确单词的平均大小,n是拼写错误单词的长度),对于较大的字典大小超时。

因此,可以用BK树解决这一问题。我们知道BK树中的每个节点都是根据其父节点的编辑距离度量构建的。因此,我们将直接从根节点到达容差限制内的特定节点。让我们说,我们的容差限制是TOL,当前节点与拼写错误的单词的编辑距离是dist。因此,现在不是迭代它的所有子节点,而是迭代它在范围内具有编辑距离的子节点 [dist-TOL,dist + TOL]。这将在很大程度上降低我们的复杂性。我们将在时间复杂度分析中讨论这个问题。

考虑下面构造的BK树。

 

image.png

假设我们有一个拼写错误的单词“oop”,容差限制为2.现在,我们将看到我们将如何收集给定拼写错误单词的预期正确值。

  • 迭代1:我们将开始检查根节点的编辑距离。 D(“oop” - >“help”)= 3.现在我们将迭代其编辑距离范围[D-TOL,D + TOL],即[1,5]
  • 迭代2:让我们从最高可能的编辑距离中开始迭代,即节点“循环”与编辑距离4.现在我们将再次从拼写错误的单词中找到它的编辑距离。 D(“oop”,“loop”)= 1。
    这里D = 1,即D <= TOL,所以我们将“循环”添加到预期的正确单词列表并处理其子节点的编辑距离在[D-TOL,D + TOL]中,即[1,3]
  • 迭代3:现在,我们处于节点“troop”。我们将再一次检查拼写错误单词的编辑距离。 D(“oop”,“troop”)= 2.再次D <= TOL,因此我们再次将“troop”添加到预期的正确单词列表中。

对于从根节点开始直到最底部叶节点的[D-TOL,D + TOL]范围内的所有单词,我们将继续相同。这类似于树上的DFS遍历,有选择地访问边缘权重在某个给定范围内的子节点。

因此,最后我们将只留下拼写错误的单词“oop”的2个预期单词,即{“loop”,“troop”}

// C++ program to demonstrate working of BK-Tree 
#include "bits/stdc++.h" 
using namespace std; // maximum number of words in dict[] 
#define MAXN 100 // defines the tolerence value 
#define TOL 2 // defines maximum length of a word 
#define LEN 10 struct Node 
{ // stores the word of the current Node string word; // links to other Node in the tree int next[2*LEN]; // constructors Node(string x):word(x) { // initializing next[i] = 0 for(int i=0; i<2*LEN; i++) next[i] = 0; } Node() {} 
}; // stores the root Node 
Node RT; // stores every Node of the tree 
Node tree[MAXN]; // index for current Node of tree 
int ptr; int min(int a, int b, int c) 
{ return min(a, min(b, c)); 
} // Edit Distance 
// Dynamic-Approach O(m*n) 
int editDistance(string& a,string& b) 
{ int m = a.length(), n = b.length(); int dp[m+1][n+1]; // filling base cases for (int i=0; i<=m; i++) dp[i][0] = i; for (int j=0; j<=n; j++) dp[0][j] = j; // populating matrix using dp-approach for (int i=1; i<=m; i++) { for (int j=1; j<=n; j++) { if (a[i-1] != b[j-1]) { dp[i][j] = min( 1 + dp[i-1][j], // deletion 1 + dp[i][j-1], // insertion 1 + dp[i-1][j-1] // replacement ); } elsedp[i][j] = dp[i-1][j-1]; } } return dp[m][n]; 
} // adds curr Node to the tree 
void add(Node& root,Node& curr) 
{ if (root.word == "" ) { // if it is the first Node // then make it the root Node root = curr; return; } // get its editDist from the Root Node int dist = editDistance(curr.word,root.word); if (tree[root.next[dist]].word == "") { /* if no Node exists at this dist from root * make it child of root Node*/// incrementing the pointer for curr Node ptr++; // adding curr Node to the tree tree[ptr] = curr; // curr as child of root Node root.next[dist] = ptr; } else{ // recursively find the parent for curr Node add(tree[root.next[dist]],curr); } 
} vector <string> getSimilarWords(Node& root,string& s) 
{ vector < string > ret; if (root.word == "") return ret; // calculating editdistance of s from root int dist = editDistance(root.word,s); // if dist is less than tolerance value // add it to similar words if (dist <= TOL) ret.push_back(root.word); // iterate over the string havinng tolerane // in range (dist-TOL , dist+TOL) int start = dist - TOL; if (start < 0) start = 1; while (start < dist + TOL) { vector <string> tmp = getSimilarWords(tree[root.next[start]],s); for (auto i : tmp) ret.push_back(i); start++; } return ret; 
} // driver program to run above functions 
int main(int argc, char const *argv[]) 
{ // dictionary words string dictionary[] = {"hell","help","shel","smell", "fell","felt","oops","pop","oouch","halt"}; ptr = 0; int sz = sizeof(dictionary)/sizeof(string); // adding dict[] words on to tree for(int i=0; i<sz; i++) { Node tmp = Node(dictionary[i]); add(RT,tmp); } string w1 = "ops"; string w2 = "helt"; vector < string > match = getSimilarWords(RT,w1); cout << "similar words in dictionary for : " << w1 << ":\n"; for (auto x : match) cout << x << endl; match = getSimilarWords(RT,w2); cout << "Correct words in dictionary for " << w2 << ":\n"; for (auto x : match) cout << x << endl; return 0; 
} 



作者:SeanC52111
链接:https://www.jianshu.com/p/cedbd94f4f45
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这篇关于BK Tree的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/389509

相关文章

树(Tree)——《啊哈!算法》

树 什么是树?树是一种特殊的图,不包含回路的连通无向树。 正因为树有着“不包含回路”这个特点,所以树就被赋予了很多特性。 一棵树中的任意两个结点有且仅有唯一的一条路径连通。一棵树如果有n个结点,那么它一定恰好有n-1条边。在一棵树中加一条边将会构成一个回路。 一棵树有且只有一个根结点。 没有父结点的结点称为根结点(祖先)。没有子结点的结点称为叶结点。如果一个结点既不是根结点也不是叶

226 Invert Binary Tree

//226 Invert Binary Tree//算法思路:主要使用递归算法public class Solution {public TreeNode invertTree(TreeNode root) {//1 出口 空节点if (root==null)return null;//2 递归 调用自己TreeNode left = root.left;TreeNode right = ro

Sorry!Hbase的LSM Tree就是可以为所欲为!

我们先抛出一个问题: LSM树是HBase里使用的非常有创意的一种数据结构。在有代表性的关系型数据库如MySQL、SQL Server、Oracle中,数据存储与索引的基本结构就是我们耳熟能详的B树和B+树。而在一些主流的NoSQL数据库如HBase、Cassandra、LevelDB、RocksDB中,则是使用日志结构合并树(Log-structured Merge Tree,LSM Tr

【spring】does not have member field ‘com.sun.tools.javac.tree.JCTree qualid

spring-in-action-6-samples 的JDK版本 最小是11,我使用 了22: jdk21 jdk22 都与lombok 不兼容,必须使用兼容版本, 否则报错: thingsboard 的大神解释了: java: java.lang.NoSuchFieldError: Class com

[LeetCode] 863. All Nodes Distance K in Binary Tree

题:https://leetcode.com/problems/all-nodes-distance-k-in-binary-tree/ 题目大意 求给树中,距给定 结点 指定长度的 所有结点的val 思路 tree -> graph 、 bfs 先遍历树,并用map记录每个结点的父结点 ,将树变为图,然后 bfs。 /*** Definition for a binary tree

js实现树级递归,通过js生成tree树形菜单(递归算法)

1、效果图 需求:首先这是一个数据集—js的类型,我们需要把生成一个tree形式的对象 : var data = [{ id: 1, name: "办公管理", pid: 0 },{ id: 2, name: "请假申请", pid: 1 },{ id: 3, name: "出差申请", pid: 1 },{ id: 4, name: "请假记录", pid: 2 },{ id:

【unity实战】利用Root Motion+Blend Tree+Input System+Cinemachine制作一个简单的角色控制器

文章目录 前言动画设置Blend Tree配置角色添加刚体和碰撞体代码控制人物移动那么我们接下来调整一下相机的视角效果参考完结 前言 Input System知识参考: 【推荐100个unity插件之18】Unity 新版输入系统Input System的使用,看这篇就够了 Cinemachine虚拟相机知识参考: 【推荐100个unity插件之10】Unity最全的最详细的C

树链剖分 + 后缀数组 - E. Misha and LCP on Tree

E. Misha and LCP on Tree  Problem's Link   Mean:  给出一棵树,每个结点上有一个字母。每个询问给出两个路径,问这两个路径的串的最长公共前缀。 analyse: 做法:树链剖分+后缀数组. 记录每条链的串,正反都需要标记,组成一个长串. 然后记录每条链对应的串在大串中的位置,对大串求后缀数组,最后询问就是在一些链

数据结构 - Codeforces Round #353 (Div. 2) D. Tree Construction

Tree Construction  Problem's Link  ---------------------------------------------------------------------------- Mean:  给定n个数,按照构造Binary Search Tree的方式来构造BST树,按顺序输出每一个非root结点的父节点的值。 analyse

【HDU】4871 Shortest-path tree 最短路+点分治

传送门:【HDU】4871 Shortest-path tree 题目分析: 学了点分治后来看这道题,简直就是水题。。。 但是我竟然花了将近一个晚上才写出来。。。就因为一个地方写漏了T U T。。 首先根据题意求一棵树,最短路一下,然后最小字典序就按照编号顺序遍历邻接表给节点标记。 剩下的就是树分治的事了。 在以重心X为根的子树中,按照X的子节点v的子树中最长路径包含节点数升序遍