查找-多路查找详解篇2

简介: 查找-多路查找详解篇2

B+树(B+tree)

B+树(B+ tree)是B-树的一种变种,特别适用于范围查询和顺序访问。

结构特点:

B+树与B-树类似,由节点组成,每个节点可以存储多个关键字,这些关键字按升
序排列。
B+树的特点是只有叶子节点存储了真实数据,而内部节点仅用于索引。叶子节点
通过指针连接形成一个链表,方便范围查询和顺序访问。
内部节点特点:
内部节点存储关键字和指向子节点的指针。
内部节点的关键字按升序排列,用于指示范围查询的起点。
内部节点的指针指向比关键字更大的子节点。
叶子节点特点:
叶子节点存储真实数据和指向下一个叶子节点的指针。
叶子节点的关键字按升序排列,支持范围查询和顺序访问。
所有叶子节点通过指针连接成一个链表,便于范围查询和顺序访问。

插入操作:

当要插入一个关键字时,从根节点开始,找到合适的叶子节点。
如果叶子节点已满,则需要进行节点分裂操作。将中间位置的关键字提升到父节
点,并将两个剩余的部分分别创建为新的叶子节点。
如果叶子节点还没有满,则直接将关键字插入到合适的位置。

删除操作:

当要删除一个关键字时,从根节点开始,找到包含该关键字的叶子节点。
直接删除叶子节点中的关键字,并更新链表指针。
删除操作后,如果叶子节点的关键字个数过少,则需要从兄弟节点借用关键字或
进行节点合并。

查询操作:

B+树的查询操作与B-树类似,从根节点开始,根据关键字的大小比较,向左或向
  右子节点递归查询,直到找到匹配的关键字或遇到叶子节点。
对于范围查询和顺序访问,可以从叶子节点开始,沿着链表进行遍历。

强调

B+树的特点在于只有叶子节点存储真实数据,这样使得范围查询和顺序访问更加
高效,因为数据在叶子节点上连续存储,读取连续的数据块比随机读取更快。而
内部节点仅存储索引信息,可以容纳更多的索引,提高了查询效率。B+树的实现
适用于需要高效地处理大量数据的数据库和文件系统,能够提供较高的查询性能
和存储效率。

代码实现

import java.util.ArrayList;
import java.util.List;
class BPlusTreeNode {
    public boolean isLeaf;
    public List<Integer> keys;
    public List<Object> values;
    public List<BPlusTreeNode> children;
    public BPlusTreeNode next;
    public BPlusTreeNode() {
        isLeaf = false;
        keys = new ArrayList<>();
        values = new ArrayList<>();
        children = new ArrayList<>();
        next = null;
    }
}
class BPlusTree {
    private BPlusTreeNode root;
    private int m;
    public BPlusTree(int m) {
        root = new BPlusTreeNode();
        root.isLeaf = true;
        this.m = m;
    }
    // 插入操作
    public void insert(int key, Object value) {
        if (root.keys.size() == m) {
            BPlusTreeNode newRoot = new BPlusTreeNode();
            newRoot.children.add(root);
            splitChild(newRoot, 0, root);
            root = newRoot;
        }
        insertNonFull(root, key, value);
    }
    // 非满子节点插入操作
    private void insertNonFull(BPlusTreeNode node, int key, Object value) {
        int index = node.keys.size() - 1;
        if (node.isLeaf) {
            while (index >= 0 && node.keys.get(index) > key) {
                index--;
            }
            node.keys.add(index + 1, key);
            node.values.add(index + 1, value);
            node.next = node.next;
        } else {
            while (index >= 0 && node.keys.get(index) > key) {
                index--;
            }
            index++;
            if (node.children.get(index).keys.size() == m) {
                splitChild(node, index, node.children.get(index));
                if (node.keys.get(index) < key) {
                    index++;
                }
            }
            insertNonFull(node.children.get(index), key, value);
        }
    }
    // 分裂满子节点
    private void splitChild(BPlusTreeNode parent, int index, BPlusTreeNode node) {
        BPlusTreeNode newNode = new BPlusTreeNode();
        newNode.isLeaf = node.isLeaf;
        parent.keys.add(index, node.keys.get(m / 2));
        parent.children.add(index + 1, newNode);
        newNode.keys.addAll(node.keys.subList((m / 2) + 1, m));
        newNode.values.addAll(node.values.subList((m / 2) + 1, m));
        if (!node.isLeaf) {
            newNode.children.addAll(node.children.subList((m / 2) + 1, m + 1));
            node.children.subList((m / 2) + 1, m + 1).clear();
        } else {
            newNode.next = node.next;
            node.next = newNode;
        }
        node.keys.subList(m / 2, m).clear();
        node.values.subList(m / 2, m).clear();
    }
    // 搜索操作
    public List<Object> search(int key) {
        return search(root, key);
    }
    private List<Object> search(BPlusTreeNode node, int key) {
        int index = 0;
        while (index < node.keys.size() && key > node.keys.get(index)) {
            index++;
        }
        if (index < node.keys.size() && key == node.keys.get(index)) {
            return node.values.get(index);
        } else if (node.isLeaf) {
            return null;
        } else {
            return search(node.children.get(index), key);
        }
    }
}

B树(B-tree)

B树(B-tree)是一种平衡的多路查找树,主要用于在磁盘等外部存储设备中高
效地存储和检索大量数据。以下是关于B树的详细介绍:

结构特点:

B树由节点组成,每个节点可以存储多个关键字,这些关键字按升序排列。
B树的特点是节点的关键字按升序排列,具有高度平衡的特性。
每个节点通常有多个子节点,最多可以拥有m个子节点,其中m称为B树的阶数。

插入操作:

当要插入一个关键字时,从根节点开始,判断关键字应插入的位置。
如果节点已满(即已有m-1个关键字),则需要进行节点分裂操作。将中间位置
的关键字提升为父节点,并将节点分裂为两个节点,将剩余的关键字均匀分配到
这两个节点中。
如果要插入的节点还没有满,则直接将关键字插入到合适的位置。

删除操作:

当要删除一个关键字时,从根节点开始,找到包含该关键字的节点。
如果该节点是叶子节点,直接删除关键字。
如果该节点是内部节点,有几种情况需要处理:
如果该节点有足够多的关键字,则可以直接删除关键字。
如果该节点的关键字数量过少,需要考虑兄弟节点的关键字数量以及兄弟节点合
并的情况。

查询操作:

B树的查询操作与二叉查找树类似,从根节点开始,根据关键字的大小比较,向
左或向右子节点递归查询,直到找到匹配的关键字或遇到叶子节点。
B树适用于大规模数据存储和查询的场景,特别适用于外部存储设备上的数据存
储。其平衡性保证了较为均衡的查询性能,因为从根节点到叶子节点的路径长度
相等或差别不大。B树的阶数m可以根据具体应用和硬件限制来选择,较大的阶数
有助于减少磁盘访问的次数,提高效率。

强调

B树的变种B+树在B树的基础上做了一些优化,将所有的数据都存储在叶子节点
中,使得范围查询和顺序访问更加高效。因此,B+树在现代数据库系统和文件
系统中更为常见和广泛应用。、

代码实现

import java.util.ArrayList;
import java.util.List;
class BTreeNode {
    int degree; // B树的阶数
    List<Integer> keys; // 节点中存储的关键字
    List<BTreeNode> children; // 节点的子节点
    boolean isLeaf; // 是否是叶子节点
    public BTreeNode(int degree, boolean isLeaf) {
        this.degree = degree;
        this.isLeaf = isLeaf;
        keys = new ArrayList<>();
        children = new ArrayList<>();
    }
}
class BTree {
    BTreeNode root; // B树的根节点
    int degree; // B树的阶数
    public BTree(int degree) {
        this.degree = degree;
        root = new BTreeNode(degree, true);
    }
    // 插入关键字
    public void insert(int key) {
        if (root.keys.size() == (2 * degree - 1)) {
            BTreeNode newRoot = new BTreeNode(degree, false);
            newRoot.children.add(root);
            splitChild(newRoot, 0, root);
            root = newRoot;
        }
        insertNonFull(root, key);
    }
    // 在非满节点插入关键字
    private void insertNonFull(BTreeNode node, int key) {
        int index = node.keys.size() - 1;
        if (node.isLeaf) {
            while (index >= 0 && key < node.keys.get(index)) {
                index--;
            }
            node.keys.add(index + 1, key);
        } else {
            while (index >= 0 && key < node.keys.get(index)) {
                index--;
            }
            index++;
            if (node.children.get(index).keys.size() == (2 * degree - 1)) {
                splitChild(node, index, node.children.get(index));
                if (key > node.keys.get(index)) {
                    index++;
                }
            }
            insertNonFull(node.children.get(index), key);
        }
    }
    // 分裂子节点
    private void splitChild(BTreeNode parent, int index, BTreeNode node) {
        BTreeNode newNode = new BTreeNode(degree, node.isLeaf);
        parent.keys.add(index, node.keys.get(degree - 1));
        parent.children.add(index + 1, newNode);
        for (int i = 0; i < degree - 1; i++) {
            newNode.keys.add(node.keys.get(i + degree));
            if (!node.isLeaf) {
                newNode.children.add(node.children.get(i + degree));
            }
        }
        if (!node.isLeaf) {
            newNode.children.add(node.children.get(2 * degree - 1));
        }
        for (int i = degree - 1; i >= 0; i--) {
            node.keys.remove(i + degree - 1);
            if (!node.isLeaf) {
                node.children.remove(i + degree);
            }
        }
    }
    // 搜索关键字
    public boolean search(int key) {
        return search(root, key);
    }
    private boolean search(BTreeNode node, int key) {
        int index = 0;
        while (index < node.keys.size() && key > node.keys.get(index)) {
            index++;
        }
        if (index < node.keys.size() && key == node.keys.get(index)) {
            return true;
        } else if (node.isLeaf) {
            return false;
        } else {
            return search(node.children.get(index), key);
        }
    }
}

Trie树(字典树或前缀树)

Trie树,也被称为字典树或前缀树,是一种用于高效存储和搜索字符串的树型数
据结构。Trie树的主要特点是通过字符串的前缀来进行搜索和匹配。

结构特点:

Trie树由根节点和一系列子节点组成。
根节点不包含任何关键字,每个子节点都表示一个字符,并按字符的顺序连接形
成路径。
从根节点到每个叶子节点的路径都对应一个字符串。
每个节点可以存储额外的信息,如词频或附加数据等。

插入操作:

当要插入一个字符串时,从根节点开始,逐个字符按顺序插入。
如果某个字符对应的子节点不存在,则创建一个新的子节点。
插入字符串的最后一个字符后,将当前节点标记为一个单词的结束。

搜索操作:

当要搜索一个字符串时,从根节点开始,逐个字符按顺序匹配。
如果某个字符对应的子节点存在,则继续匹配下一个字符。
如果匹配遇到缺失的字符或到达某个节点后没有子节点,则表示字符串不在Trie
树中。
如果匹配成功并且在Trie树中找到最后一个字符,则表示字符串存在于Trie树中。

删除操作:

当要删除一个字符串时,从根节点开始,逐个字符按顺序遍历。
如果遍历过程中发现某个字符对应的子节点不存在,则表示字符串不存在于Trie
树中。
如果遍历成功,并到达字符串的最后一个字符,将当前节点的结束标记取消。
如果遍历成功,但还存在其他相关字符串(例如,删除"abc"但还有"abcd"),
可以保留当前节点以表示其他相关字符串。

优点:

搜索的时间复杂度与字符串长度无关,仅与Trie树的高度相关,通常比哈希表更
高效。
可以高效地搜索具有相同前缀的字符串集合。
对于字符串的前缀匹配和自动补全,Trie树可以提供高效的结果。

缺点:

空间消耗较大,尤其在处理大量长字符串时。为了缓解这个问题,可以使用压缩
的Trie树,如压缩前缀树(Patricia树)或Trie树的变种来减少存储空间。

代码实现

class TrieNode {
    private TrieNode[] children;
    private boolean isEndOfWord;
    public TrieNode() {
        children = new TrieNode[26]; // 26个英文字母
        isEndOfWord = false;
    }
    public TrieNode getChild(char ch) {
        return children[ch - 'a'];
    }
    public void setChild(char ch, TrieNode node) {
        children[ch - 'a'] = node;
    }
    public boolean isEndOfWord() {
        return isEndOfWord;
    }
    public void setEndOfWord(boolean isEndOfWord) {
        this.isEndOfWord = isEndOfWord;
    }
}
class Trie {
    private TrieNode root;
    public Trie() {
        root = new TrieNode();
    }
    public void insert(String word) {
        TrieNode node = root;
        for (char ch : word.toCharArray()) {
            if (node.getChild(ch) == null) {
                node.setChild(ch, new TrieNode());
            }
            node = node.getChild(ch);
        }
        node.setEndOfWord(true);
    }
    public boolean search(String word) {
        TrieNode node = findNode(word);
        return node != null && node.isEndOfWord();
    }
    public boolean startsWith(String prefix) {
        TrieNode node = findNode(prefix);
        return node != null;
    }
    private TrieNode findNode(String str) {
        TrieNode node = root;
        for (char ch : str.toCharArray()) {
            node = node.getChild(ch);
            if (node == null) {
                return null;
            }
        }
        return node;
    }
}

使用示例

public class Main {
    public static void main(String[] args) {
        Trie trie = new Trie();
        trie.insert("apple");
        trie.insert("banana");
        trie.insert("grape");
        System.out.println(trie.search("apple")); // 输出: true
        System.out.println(trie.search("orange")); // 输出: false
        System.out.println(trie.startsWith("app")); // 输出: true
        System.out.println(trie.startsWith("ban")); // 输出: true
        System.out.println(trie.startsWith("grap")); // 输出: true
    }
}
相关文章
|
文字识别 Java API
SpringBoot+Tess4j实现牛逼的OCR识别工具
SpringBoot+Tess4j实现牛逼的OCR识别工具
1943 0
SpringBoot+Tess4j实现牛逼的OCR识别工具
|
Ubuntu 网络协议 网络安全
ubuntu防火墙的安装,开启,关闭和添加规则等操作
ubuntu防火墙的安装,开启,关闭和添加规则等操作
6488 0
ubuntu防火墙的安装,开启,关闭和添加规则等操作
|
7月前
|
机器学习/深度学习 数据处理
大语言模型中的归一化技术:LayerNorm与RMSNorm的深入研究
本文分析了大规模Transformer架构(如LLama)中归一化技术的关键作用,重点探讨了LayerNorm被RMSNorm替代的原因。归一化通过调整数据量纲保持分布形态不变,提升计算稳定性和收敛速度。LayerNorm通过均值和方差归一化确保数值稳定,适用于序列模型;而RMSNorm仅使用均方根归一化,省略均值计算,降低计算成本并缓解梯度消失问题。RMSNorm在深层网络中表现出更高的训练稳定性和效率,为复杂模型性能提升做出重要贡献。
1399 14
大语言模型中的归一化技术:LayerNorm与RMSNorm的深入研究
|
8月前
|
XML 机器学习/深度学习 人工智能
CLaMP 3:音乐搜索AI革命!多模态AI能听懂乐谱/MIDI/音频,用27国语言搜索全球音乐
CLaMP 3是由清华大学团队开发的多模态、多语言音乐信息检索框架,支持27种语言,能够进行跨模态音乐检索、零样本分类和音乐推荐等任务。
361 1
CLaMP 3:音乐搜索AI革命!多模态AI能听懂乐谱/MIDI/音频,用27国语言搜索全球音乐
|
机器学习/深度学习 人工智能 自然语言处理
【ACL2024】基于长尾检索知识增强的大语言模型
近日,阿里云人工智能平台PAI与阿里集团安全部内容安全算法团队、华东师范大学何晓丰教授团队合作,在自然语言处理顶级会议ACL2024上发表论文《On the Role of Long-tail Knowledge in Retrieval Augmented Large Language Models》,论文主题为长尾知识检索增强的大语言模型。通过将问题识别为普通可回答和长尾两种性质,让大模型针对性的对长尾问题进行检索文档增强。对于普通可回答的用户提问可以直接通过大模型回答,而不需要进行文档检索增强,从而能增强大模型处理不同类型用户提问的效率。
|
监控 Kubernetes 测试技术
掌握Docker网络模式:构建高效容器通信
【10月更文挑战第3天】本文深入探讨了Docker的网络模式,包括它们的工作原理、使用场景以及如何配置和优化容器间的通信。希望能够帮助开发者在项目中有效地应用Docker网络模式,构建高效的容器化应用。
|
网络安全 数据安全/隐私保护 C++
VS Code 的SSH连接不成功问题分析与解决
VS Code 的SSH连接不成功问题分析与解决
|
IDE Java 开发工具
解决IntelliJ IDEA报错Error:Cannot determine path to ‘tools.jar‘ library for 17 (D:/JAVA)
解决IntelliJ IDEA报错Error:Cannot determine path to ‘tools.jar‘ library for 17 (D:/JAVA)
2236 0
|
人工智能 安全 测试技术
微软开源4.2B参数多模态SLM模型Phi-3-vision,魔搭社区推理、微调实战教程来啦!
在 Microsoft Build 2024 上,微软持续开源了 Phi-3 系列的新模型们。包括 Phi-3-vision,这是一种将语言和视觉功能结合在一起的多模态模型。
|
机器学习/深度学习 运维 搜索推荐
机器学习中准确率、精确率、召回率、误报率、漏报率、F1-Score、AP&mAP、AUC、MAE、MAPE、MSE、RMSE、R-Squared等指标的定义和说明
在机器学习和深度学习用于异常检测(Anomaly detection)、电子商务(E-commerce)、信息检索(Information retrieval, IR)等领域任务(Task)中,有很多的指标来判断机器学习和深度学习效果的好坏。这些指标有相互权衡的,有相互背向的,所以往往需要根据实际的任务和场景来选择衡量指标。本篇博文对这些指标进行一个梳理。
机器学习中准确率、精确率、召回率、误报率、漏报率、F1-Score、AP&mAP、AUC、MAE、MAPE、MSE、RMSE、R-Squared等指标的定义和说明