算法 - 符号表-上

news2024/11/29 8:58:09

img

🏠个人主页:尘觉主页

文章目录

  • 算法 - 符号表
    • 前言
    • 初级实现
      • 1. 链表实现无序符号表
      • 2. 二分查找实现有序符号表
    • 二叉查找树
      • 1. get()
      • 2. put()
      • 3. 分析
      • 4. floor()
      • 5. rank()
      • 6. min()
      • 7. deleteMin()
      • 8. delete()
      • 9. keys()
      • 10. 分析

算法 - 符号表

前言

符号表(Symbol Table)是一种存储键值对的数据结构,可以支持快速查找操作。

符号表分为有序和无序两种,有序符号表主要指支持 min()、max() 等根据键的大小关系来实现的操作。

有序符号表的键需要实现 Comparable 接口。

public interface UnorderedST<Key, Value> {

    int size();

    Value get(Key key);

    void put(Key key, Value value);

    void delete(Key key);
}
public interface OrderedST<Key extends Comparable<Key>, Value> {

    int size();

    void put(Key key, Value value);

    Value get(Key key);

    Key min();

    Key max();

    int rank(Key key);

    List<Key> keys(Key l, Key h);
}

初级实现

1. 链表实现无序符号表

public class ListUnorderedST<Key, Value> implements UnorderedST<Key, Value> {

    private Node first;

    private class Node {
        Key key;
        Value value;
        Node next;

        Node(Key key, Value value, Node next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    @Override
    public int size() {
        int cnt = 0;
        Node cur = first;
        while (cur != null) {
            cnt++;
            cur = cur.next;
        }
        return cnt;
    }

    @Override
    public void put(Key key, Value value) {
        Node cur = first;
        // 如果在链表中找到节点的键等于 key 就更新这个节点的值为 value
        while (cur != null) {
            if (cur.key.equals(key)) {
                cur.value = value;
                return;
            }
            cur = cur.next;
        }
        // 否则使用头插法插入一个新节点
        first = new Node(key, value, first);
    }

    @Override
    public void delete(Key key) {
        if (first == null)
            return;
        if (first.key.equals(key))
            first = first.next;
        Node pre = first, cur = first.next;
        while (cur != null) {
            if (cur.key.equals(key)) {
                pre.next = cur.next;
                return;
            }
            pre = pre.next;
            cur = cur.next;
        }
    }

    @Override
    public Value get(Key key) {
        Node cur = first;
        while (cur != null) {
            if (cur.key.equals(key))
                return cur.value;
            cur = cur.next;
        }
        return null;
    }
}

2. 二分查找实现有序符号表

使用一对平行数组,一个存储键一个存储值。

二分查找的 rank() 方法至关重要,当键在表中时,它能够知道该键的位置;当键不在表中时,它也能知道在何处插入新键。

二分查找最多需要 logN+1 次比较,使用二分查找实现的符号表的查找操作所需要的时间最多是对数级别的。但是插入操作需要移动数组元素,是线性级别的。

public class BinarySearchOrderedST<Key extends Comparable<Key>, Value> implements OrderedST<Key, Value> {

    private Key[] keys;
    private Value[] values;
    private int N = 0;

    public BinarySearchOrderedST(int capacity) {
        keys = (Key[]) new Comparable[capacity];
        values = (Value[]) new Object[capacity];
    }

    @Override
    public int size() {
        return N;
    }

    @Override
    public int rank(Key key) {
        int l = 0, h = N - 1;
        while (l <= h) {
            int m = l + (h - l) / 2;
            int cmp = key.compareTo(keys[m]);
            if (cmp == 0)
                return m;
            else if (cmp < 0)
                h = m - 1;
            else
                l = m + 1;
        }
        return l;
    }

    @Override
    public List<Key> keys(Key l, Key h) {
        int index = rank(l);
        List<Key> list = new ArrayList<>();
        while (keys[index].compareTo(h) <= 0) {
            list.add(keys[index]);
            index++;
        }
        return list;
    }

    @Override
    public void put(Key key, Value value) {
        int index = rank(key);
        // 如果找到已经存在的节点键为 key,就更新这个节点的值为 value
        if (index < N && keys[index].compareTo(key) == 0) {
            values[index] = value;
            return;
        }
        // 否则在数组中插入新的节点,需要先将插入位置之后的元素都向后移动一个位置
        for (int j = N; j > index; j--) {
            keys[j] = keys[j - 1];
            values[j] = values[j - 1];
        }
        keys[index] = key;
        values[index] = value;
        N++;
    }

    @Override
    public Value get(Key key) {
        int index = rank(key);
        if (index < N && keys[index].compareTo(key) == 0)
            return values[index];
        return null;
    }

    @Override
    public Key min() {
        return keys[0];
    }

    @Override
    public Key max() {
        return keys[N - 1];
    }
}

二叉查找树

二叉树 是一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。

image-20240404154010822

二叉查找树 (BST)是一颗二叉树,并且每个节点的值都大于等于其左子树中的所有节点的值而小于等于右子树的所有节点的值。

BST 有一个重要性质,就是它的中序遍历结果递增排序。

image-20240404154034969

基本数据结构:

public class BST<Key extends Comparable<Key>, Value> implements OrderedST<Key, Value> {

    protected Node root;

    protected class Node {
        Key key;
        Value val;
        Node left;
        Node right;
        // 以该节点为根的子树节点总数
        int N;
        // 红黑树中使用
        boolean color;

        Node(Key key, Value val, int N) {
            this.key = key;
            this.val = val;
            this.N = N;
        }
    }

    @Override
    public int size() {
        return size(root);
    }

    private int size(Node x) {
        if (x == null)
            return 0;
        return x.N;
    }

    protected void recalculateSize(Node x) {
        x.N = size(x.left) + size(x.right) + 1;
    }
}

为了方便绘图,下文中二叉树的空链接不画出来。

1. get()

  • 如果树是空的,则查找未命中;
  • 如果被查找的键和根节点的键相等,查找命中;
  • 否则递归地在子树中查找:如果被查找的键较小就在左子树中查找,较大就在右子树中查找。
@Override
public Value get(Key key) {
    return get(root, key);
}

private Value get(Node x, Key key) {
    if (x == null)
        return null;
    int cmp = key.compareTo(x.key);
    if (cmp == 0)
        return x.val;
    else if (cmp < 0)
        return get(x.left, key);
    else
        return get(x.right, key);
}

2. put()

当插入的键不存在于树中,需要创建一个新节点,并且更新上层节点的链接指向该节点,使得该节点正确地链接到树中。

image-20240404154054094

 @Override
public void put(Key key, Value value) {
    root = put(root, key, value);
}

private Node put(Node x, Key key, Value value) {
    if (x == null)
        return new Node(key, value, 1);
    int cmp = key.compareTo(x.key);
    if (cmp == 0)
        x.val = value;
    else if (cmp < 0)
        x.left = put(x.left, key, value);
    else
        x.right = put(x.right, key, value);
    recalculateSize(x);
    return x;
}

3. 分析

二叉查找树的算法运行时间取决于树的形状,而树的形状又取决于键被插入的先后顺序。

最好的情况下树是完全平衡的,每条空链接和根节点的距离都为 logN。

image-20240404154208696

在最坏的情况下,树的高度为 N。

image-20240404154139062

4. floor()

floor(key):小于等于键的最大键

  • 如果键小于根节点的键,那么 floor(key) 一定在左子树中;
  • 如果键大于根节点的键,需要先判断右子树中是否存在 floor(key),如果存在就返回,否则根节点就是 floor(key)。
public Key floor(Key key) {
    Node x = floor(root, key);
    if (x == null)
        return null;
    return x.key;
}

private Node floor(Node x, Key key) {
    if (x == null)
        return null;
    int cmp = key.compareTo(x.key);
    if (cmp == 0)
        return x;
    if (cmp < 0)
        return floor(x.left, key);
    Node t = floor(x.right, key);
    return t != null ? t : x;
}

5. rank()

rank(key) 返回 key 的排名。

  • 如果键和根节点的键相等,返回左子树的节点数;
  • 如果小于,递归计算在左子树中的排名;
  • 如果大于,递归计算在右子树中的排名,加上左子树的节点数,再加上 1(根节点)。
@Override
public int rank(Key key) {
    return rank(key, root);
}

private int rank(Key key, Node x) {
    if (x == null)
        return 0;
    int cmp = key.compareTo(x.key);
    if (cmp == 0)
        return size(x.left);
    else if (cmp < 0)
        return rank(key, x.left);
    else
        return 1 + size(x.left) + rank(key, x.right);
}

6. min()

@Override
public Key min() {
    return min(root).key;
}

private Node min(Node x) {
    if (x == null)
        return null;
    if (x.left == null)
        return x;
    return min(x.left);
}

7. deleteMin()

令指向最小节点的链接指向最小节点的右子树。

image-20240404154255376

public void deleteMin() {
    root = deleteMin(root);
}

public Node deleteMin(Node x) {
    if (x.left == null)
        return x.right;
    x.left = deleteMin(x.left);
    recalculateSize(x);
    return x;
}

8. delete()

  • 如果待删除的节点只有一个子树, 那么只需要让指向待删除节点的链接指向唯一的子树即可;
  • 否则,让右子树的最小节点替换该节点。

image-20240404154326939

public void delete(Key key) {
    root = delete(root, key);
}
private Node delete(Node x, Key key) {
    if (x == null)
        return null;
    int cmp = key.compareTo(x.key);
    if (cmp < 0)
        x.left = delete(x.left, key);
    else if (cmp > 0)
        x.right = delete(x.right, key);
    else {
        if (x.right == null)
            return x.left;
        if (x.left == null)
            return x.right;
        Node t = x;
        x = min(t.right);
        x.right = deleteMin(t.right);
        x.left = t.left;
    }
    recalculateSize(x);
    return x;
}

9. keys()

利用二叉查找树中序遍历的结果为递增的特点。

@Override
public List<Key> keys(Key l, Key h) {
    return keys(root, l, h);
}

private List<Key> keys(Node x, Key l, Key h) {
    List<Key> list = new ArrayList<>();
    if (x == null)
        return list;
    int cmpL = l.compareTo(x.key);
    int cmpH = h.compareTo(x.key);
    if (cmpL < 0)
        list.addAll(keys(x.left, l, h));
    if (cmpL <= 0 && cmpH >= 0)
        list.add(x.key);
    if (cmpH > 0)
        list.addAll(keys(x.right, l, h));
    return list;
}

10. 分析

二叉查找树所有操作在最坏的情况下所需要的时间都和树的高度成正比。

算法 - 符号表-下

😁热门专栏推荐
想学习vue的可以看看这个

java基础合集

数据库合集

redis合集

nginx合集

linux合集

手写机制

微服务组件

spring_尘觉

springMVC

mybits

等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持

🤔欢迎大家加入我的社区 尘觉社区

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😁
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🍻
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🤞

img

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

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

相关文章

MQTT的学习

近期构建物联网平台&#xff0c;学习到MQTT&#xff0c;这里使用的是uniapp作为连接MQTT broker的&#xff0c;这里使用的是国产的EMQX。 MQTT的认识 MQTT 协议入门&#xff1a;基础知识和快速教程 | EMQ&#xff08;简单的认识&#xff09; 创建 MQTT 连接时如何设置参数&am…

前端开发语言有那些?

前端开发语言有那些&#xff1f; 1、html 超文本标记语言&#xff1a;构建前端网页的基本结构&#xff0c;就象人的骨架一样。 2、css 层叠样式表&#xff1a;控制网页的样式和布局&#xff0c;就象人需要穿各种服式展现不同风采。 3、javascript 简称 JS 动态脚本语言&#x…

微信小程序云函数调用方法和技术架构介绍

云函数 云函数是涂鸦根据微信小程序使用场景&#xff0c;结合涂鸦 IoT 开放能力&#xff0c;提供的小程序访问涂鸦 IoT 开放能力接口方案。为此在基础能力中&#xff0c;我们提供了基础请求云函数的 API。 对于特殊的业务场景&#xff0c;需要使用云函数访问业务数据的&#…

智慧园区预约管理系统:提升效率与保障安全的关键

在当今这个信息技术高度发达的时代&#xff0c;智慧园区如雨后春笋般迅速发展&#xff0c;而预约管理作为智慧园区的关键组成部分&#xff0c;其重要性日益凸显。 访客预约系统的精细化设计&#xff0c;为园区的安全和秩序提供了坚实可靠的保障。访客可以通过便捷的在线平台&am…

django系统模板

【一】引子 来看一段代码 def current_datetime(request):now datetime.datetime.now()html "<html><body>It is now %s.</body></html>" % nowreturn HttpResponse(html)直接把HTML页面嵌套在视图函数里返回给浏览器并不是一个好主意&a…

2024年天津中德应用技术大学退役大学生专升本专业考试准考证下载

2024年天津中德应用技术大学退役大学生高职升本科专业课考试准考证下载及考生须知 一、准考证下载打印 4月7日14点开始&#xff0c;天津中德应用技术大学专业课报名审核通过的考生&#xff0c;登录天津中德应用技术大学专业课报名系统&#xff08;http://125.65.42.21:8091/j…

【实战解析】YOLOv9全流程训练至优化终极指南

【实战解析】YOLOv9全流程训练至优化终极指南 0.引言1.环境准备2.数据预处理&#xff08;1&#xff09;数据准备&#xff08;2&#xff09;按比例划分数据集&#xff08;3&#xff09;xml转txt脚本&#xff08;4&#xff09;配置文件 3.模型训练&#xff08;1&#xff09;单GPU…

4.7Qt

自由发挥应用场景实现一个登录窗口界面。 mywidget.cpp #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口相关设置this->setWindowTitle("原神启动");this->setWindowIcon(QIcon("C:\\Users\\17212\\Pict…

【学习】移动端App性能测试流程有哪些

移动端App性能测试是保证App性能表现的重要环节之一。随着移动设备的普及和移动互联网的发展&#xff0c;移动端App的性能测试变得越来越重要&#xff0c;通过科学合理的性能测试可以发现并解决潜在的性能问题优化App运行效果提高用户体验。性能测试旨在评估App在各种场景下的性…

《从零开始学架构》读书笔记(一)

目录 软件架构设计产生的历史背景 软件架构设计的目的 系统复杂度来源 追求高性能 一、单机高性能 二、集群的高性能 追求高可用 一、计算高可用 二、存储高可用 追求可扩展性 一、预测变化 二、应对变化 追求安全、低成本、规模 一、安全 二、低成本 三、规模…

第7章 数据安全

思维导图 7.1 引言 数据安全包括安全策略和过程的规划、建立与执行&#xff0c;为数据和信息资产提供正确的身份验证、授权、访问和审计。虽然数据安全的详细情况(如哪些数据需要保护)因行业和国家有所不同&#xff0c;但是数据安全实践的目标是相同的&#xff0c;即根据隐私和…

数据结构初阶:栈和队列

栈 栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵守后进先出 LIFO &#xff08; Last In First Out &#xff09;的原则。…

【服务器uwsgi + flask + nginx的搭建】

目录 服务器uwsgi flask nginx的搭建1. 安装必要的软件2. 启动nginx服务3. 测试Nginx4. 配置uwsgi和flask5. 配置nginx 服务器uwsgi flask nginx的搭建 1. 安装必要的软件 安装Python、uWSGI、Flask 和 Nginx。 # Ubuntu 安装命令 sudo apt-get update sudo apt-get ins…

推动科技创新润德生物邀您到场参观2024第13届生物发酵展

参展企业介绍 山东润德生物科技有限公司成立于2014年10月17日&#xff0c;是一家围绕生物制品的研发、生产、营销、国际贸易、技术服务为核心业务的国家高新技术企业&#xff0c;近年来荣获国家制造业单项冠军示范企业、国家级绿色工厂、国家知识产权优势企业、国家工业产品绿…

新人硬件工程师往哪个方向更有前途?

如果是比较沉默寡言&#xff0c;不擅长交际的&#xff0c;那么可以走技术路线。我这里有一套自动化入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习自动化&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我…

30天拿下Rust之实战Web Server

概述 随着互联网技术的飞速发展&#xff0c;Web服务器作为承载网站与应用的核心组件&#xff0c;其性能、稳定性和安全性都显得至关重要。Rust语言凭借其独特的内存安全保证、高效的性能以及丰富的生态系统&#xff0c;成为了构建现代Web服务器的理想选择。 新建项目 首先&…

工业视觉检测

目录 我对工业视觉检测的了解 一、关键组成部分 二、应用场景 三、技术挑战 我对工业视觉检测的了解 工业视觉检测是利用机器视觉技术对产品质量进行自动化检查的过程&#xff0c;它在制造业中扮演着至关重要的角色&#xff0c;用于确保产品质量、提高生产效率、减少人工成…

数仓开发之Flume《一》:Flume的概述及安装

目录 1. &#x1f959;Flume概述 1.1 Flume简介 1.2 Flume的架构 1. &#x1f9c0;agent介绍 2. ​Agent 主要有 3 个部分组成&#xff0c;Source、Channel、Sink。 &#x1f957;2.1 Source &#x1f957;2.2 Sink &#x1f957;2.3 Channel 3. &#x1f9c0;Flume 自…

2.k8s架构

目录 k8s集群架构 控制平面 kube-apiserver kube-scheduler etcd kube-controller-manager node 组件 kubelet kube-proxy 容器运行时&#xff08;Container Runtime&#xff09; cloud-controller-manager 相关概念 k8s集群架构 一个Kubernetes集群至少包含一个控制…

蓝桥杯刷题day14——盖印章【算法赛】

一、问题描述 小 Z 喜欢盖印章。 有一天,小 Z 得到了一个 nm 的网格图,与此同时,他的手上有两种印章(分别称为 A,B),如下图所示。 他想将这两种印章盖在这个网格图上。 由于小 Z 是一个有原则的人,他将按照以下规则进行操作。 每个印章所形成的图案的边必须和网格图…