【算法自由之路】前缀树 桶排序之计数排序和基数排序

news2025/1/19 20:40:19

【算法自由之路】前缀树 & 桶排序之计数排序和基数排序

前缀树(字典树)

首先是前缀树,前缀树是由字符构成的树结构,它记录有多少前缀字符通过,以及有多少个同样的字符串,其找这类信息的时间复杂度是极快的,只与字符串本身长度有关,是 O(logK) 级别,K 为字符串长度,可以应用于一些列前缀问题中。

有些抽象,举一个经典的例子,给定一组仅小写字母组成的字符串,如何构建前缀树?

["abc","adui","abd","bcd","af"] 

这个数组构建出来的前缀树是这样的, pass 记录的是有多少个字符串通过了它,end 记录的是有多少字符串以该节点字符结尾

在这里插入图片描述

基本的方法有:

向前缀树中添加一个字符串 insert

查找某个字符串在前缀树中数量 getNum

查找有多少字符串以某个前缀开头 getPrfixNum

向前缀树中删除一个字符串 delete

package algorithmic.base;

import com.sun.tools.javac.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @program: algorithmic-total-solution
 * @description: 字典树
 * @author: wangzibin
 * @create: 2022-11-26
 **/
public class TrieTree {

    private Node root;

    public TrieTree() {
        root = new Node();
    }

    private static class Node {
        int pass;
        int end;
        Map<Integer, Node> next;

        public Node() {
            this.pass = 0;
            this.end = 0;
            this.next = new HashMap<>();
        }
    }

    public void insert(String str) {
        if (str == null || str.length() < 1) {
            return;
        }
        int length = str.length();
        Node current = root;
        root.pass++;
        for (int i = 0; i < length; i++) {
            char c = str.charAt(i);
            int index = c - 'a';
            Node node = current.next.get(index);
            if (node == null) {
                node = new Node();
                current.next.put(index, node);
            }
            node.pass++;
            current = node;
        }
        current.end++;
    }

    public void delete(String str) {
        if (getNum(str) == 0) {
            return;
        }
        int length = str.length();
        Node current = root;
        root.pass--;
        for (int i = 0; i < length; i++) {
            char c = str.charAt(i);
            int index = c - 'a';
            Node node = current.next.get(index);
            node.pass--;
            if (node.pass == 0){
                // 当某个通路 pass 已经是 0 则直接 remove 完成 delete
                current.next.remove(index);
                return;
            }
            current = node;
        }
        current.end--;
    }

    public int getNum(String str) {
        if (str == null || str.length() < 1) {
            return 0;
        }
        int length = str.length();
        Node current = root;
        for (int i = 0; i < length; i++) {
            char c = str.charAt(i);
            int index = c - 'a';
            Node node = current.next.get(index);
            if (node == null) {
                return 0;
            }
            current = node;
        }
        return current.end;
    }

    public int getPrefixNum(String str) {
        if (str == null || str.length() < 1) {
            return 0;
        }
        int length = str.length();
        Node current = root;
        for (int i = 0; i < length; i++) {
            char c = str.charAt(i);
            int index = c - 'a';
            Node node = current.next.get(index);
            if (node == null) {
                return 0;
            }
            current = node;
        }
        return current.pass;
    }

    public static void main(String[] args) {
        TrieTree trieTree = new TrieTree();
        trieTree.insert("abccd");
        trieTree.insert("abccd");
        trieTree.insert("abccd");
        trieTree.insert("abccd");
        trieTree.insert("abccd");
        trieTree.insert("abccde1123%^$$8*吧");
        trieTree.insert("abccdf");
        trieTree.insert("abccdq");
        System.out.println(trieTree.getNum("abccde1123%^$$8*吧"));
        System.out.println(trieTree.getPrefixNum("abccde1123%^$$"));
        System.out.println(trieTree.getNum("abccd"));
        System.out.println(trieTree.root.pass);
        trieTree.delete("abccde1123%^$$8*吧");
        trieTree.delete("abccde");
        trieTree.delete("abccd");
        trieTree.delete("abccd");
        trieTree.delete("abccd");
        trieTree.delete("abccd");
        trieTree.delete("abccd");
        trieTree.delete("abccd");
        System.out.println(trieTree.getPrefixNum("abccde1123%^$$8*吧"));
        System.out.println(trieTree.getPrefixNum("abccde1123%^$$"));
        System.out.println(trieTree.getNum("abccd"));
        System.out.println(trieTree.root.pass);

    }

}

如上 Node 节点记录了 pass 和 end 数量,在解决某些前缀问题时或可增加一些特性属性来进行解答。

桶排序

这里桶排序是一种统称,桶即指容器,这个容器可以是 Java 中的数组、链表、哈希表,只要是可以容纳一部分数据的结构都可以称为容器,或者桶。

计数排序、基数排序都是桶排序的一种,它们是不基于比较的排序,它们的排序时间复杂度可以达到 O(N) ,但其对排序数据本身要求比较苛刻,可使用场景有限。

计数排序

顾名思义:统计数据。

比如有这样一个场景,人口普查统计年龄,给所有人的年龄排序,基本上我们可以认为,人的年龄在 0 - 200 之间。

这个时候我们可以准备一个 200 个长度的数组,然后遍历年龄,年龄对应数组下标,遇到一个则 +1,最后遍历数组反向生成排序好的数组。

数组每个下标就是一个桶记录这个桶中有多少个数。

可以见得,这个对数据本身要求很高,比如我的数据如果变了取值范围,达到上亿的值,那准备上亿长度的数组显然不现实,同时就算可以,遍历这个数组的成本也不可忽略。

基数排序

基数:所谓基数,就是进位计数制的每位数上可能有的数码的个数

基数排序一般要求数据是十进制正整数,它是基于分配的排序,根据位数对数据进行分桶最终实现排序。

思路是,十进制,准备十个桶分别代表 0 1 2 3 …… 9 ,所有数据高位根据最大长度数补齐 0

比如

[193,21,33,34,5]
// 补齐后,这里不一定是真的补齐成字符串哈,实现看自己
[193,021,033,034,005]

有几位就进行几次分配入桶,先从低位入桶,这个桶是先进先出队列,这一位数字是几就进几号桶,然后从 0 号桶依次出桶,最终排序完成,这个其实是利用了高位数高于低位的优先级进行的分配排序

在这里插入图片描述

对于基数排序,有一个很有意思的实现,即我们不需要真的准备 10 个这样的桶,只需要一个长度为 10 的数组即可。

这个数组每个下标位表示小于等于该位的数有几个,从后往前遍历原数组,取当前位,找到当前数小于等于当前位的数总共有 x 个,根据之前先进先出原则,此时是从后遍历,可以推算出当前数弹出时位置即 arr[x-1] 这个位置

package algorithmic.base;

import util.RandomUtil;

import java.util.Arrays;
import java.util.Random;

/**
 * @program: algorithmic-total-solution
 * @description:
 * @author: wangzibin
 * @create: 2022-11-28
 **/
public class RadixSort {

    public static void sort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int len = arr.length;
        int digit = 0;
        for (int i = 0; i < len; i++) {
            digit = Math.max(digit, getDigitNum(arr[i]));
        }
        int[] help = new int[len];
        for (int i = 0; i < digit; i++) {
            int[] count = new int[10];
            // 计算 i 位 等于 j 的有多少
            for (int j = 0; j < len; j++) {
                count[getDigit(arr[j], i)]++;
            }
            // 计算出 i 位 小于等于 j 的有多少
            for (int j = 1; j < 10; j++) {
                count[j] = count[j] + count[j - 1];
            }

            for (int j = len - 1; j >= 0; j--) {
                // 模拟先进先出,
                int currentD = getDigit(arr[j], i);
                int num = count[currentD];
                help[num - 1] = arr[j];
                count[currentD]--;
            }
            for (int j = 0; j < len; j++) {
                arr[j] = help[j];
            }
        }

    }

    private static int getDigitNum(int num) {
        int i = 0;
        while (num > 0) {
            num /= 10;
            i++;
        }
        return i;
    }

    private static int getDigit(int num, int digit) {
        for (int i = 0; i < digit; i++) {
            num /= 10;
        }
        return num % 10;
    }

    public static void main(String[] args) {
        int[] arr;
        int[] arr2;
        SelectionSort selectionSort = new SelectionSort();
        Random random = new Random();
        for (int i = 0; i < 1000; i++) {
            int len = random.nextInt(20) + 5;
            arr = new int[len];
            arr2 = new int[len];
            for (int j = 0; j < len; j++) {
                int item = random.nextInt(100);
                arr[j] = item;
                arr2[j] = item;
            }
            sort(arr);
            selectionSort.sort(arr2);
            for (int k = 0; k < len; k++) {
                if (arr[k] != arr2[k]) {
                    System.out.println(Arrays.toString(arr));
                    System.out.println(Arrays.toString(arr2));
                    System.out.println("error " + i);
                    return;
                }
            }
        }
        System.out.println("success");
    }
}

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

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

相关文章

minigui编译移植

minigui编译移植 一:文件系统依赖支持二:交叉编译libminigui-1.6.10三:交叉编译mg-samples-1.6.10四:资源minigui-res-1.6.10四:开发板拷贝资源五:/etc/MiniGUI.cfg配置文件修改六:系统环境变量设置一:文件系统依赖支持 zlib libpng libjpeg 二:交叉编译libminigui-1.6.10 conf…

第五届安洵杯网络挑战赛WP

Crypto Cry1 crypto签到题&#xff0c;就是先对SHA256的哈希值进行爆破&#xff0c;然后猜数字 用hashcat一条命令秒穿 hashcat --custom-charset1 ?d?l?u -a 3 -m 1400 3075696ea46516c3a0a43930fab5a0f1c68ea4b315dd87a9cd123dac7f20f3a6 ?1?1?1?1GJWVMYlh5ApWLbF…

MySQL源码分析之SQL函数执行

1.MySQL中执行一条SQL的总体流程 一条包含函数的SQL语句&#xff0c;在MySQL中会经过: 客户端发送&#xff0c;服务器连接&#xff0c;语法解析&#xff0c;语句执行的过程。 调试源码&#xff0c;分析函数的具体执行过程&#xff0c;在客户端&#xff0c;执行select to_char…

【数据结构与算法】初识时间空间复杂度

文章目录1.数据结构与算法概念2.时间复杂度3.大O计数法表示时间复杂度4.线性结构与非线性结构1.数据结构与算法概念 &#xff08;1&#xff09;什么是数据结构 数据结构指的是相互之间有一种或者多种特定的关系数据元素集合。数据结构可以分成逻辑结构和物理结构。逻辑结构&a…

全网首发克莱斯勒东南大捷龙jeep道奇DIY数码碟盒增加USB和蓝牙播放音乐功能使用原车接口无损改装

文章目录前言碟盒功能1、设计指标3、外观设计4、PCB设计5、程序设计6、调试7、大捷龙车机尾插接口定义公头东南大捷龙车机白色插头模块与白色插头连接方法8、安装方法9、 使用方法9.1 CD车机按钮功能定义11、 联系我前言 ​ 之前写过四篇关于车机增加音频输入的方法。 1、07宝…

[数据结构] 并查集

并查集相关概念并查集的模拟实现1&#xff09;实现基本框架2&#xff09;实现基础操作findRoot查找元素属于哪个集合Union合并两个集合IsOneSet判断两个元素是否属于同一集合SetSize集合个数相关概念 初始时&#xff0c;每个数据的下标都为-1&#xff0c;表示10棵树&#xff1…

【EDA365电子论坛】RISC-V 能否超越 x86、Arm,成为新一代计算机系统架构?

前言 指令集架构(Instruction Set Architecture&#xff0c;缩写为ISA&#xff09;&#xff0c;是一组指令的集合&#xff0c;指令是指处理器进行操作的最小单元&#xff08;譬如加减乘除操作或者读&#xff0f;写存储器数据&#xff09;。指令集架构&#xff0c;有时简称为“架…

[附源码]SSM计算机毕业设计小超市进销存管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【js】日期控件的实现

需求&#xff1a;通过日期控件实现只显示年月 效果如下图&#xff1a; 日期控件使用的是My97DatePicker&#xff1a; 可以从官网下载&#xff1a;http://www.my97.net/&#xff0c;或者&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1KRXSjfqpyguZ67vBrOWM8g 提取码…

Python创建增量目录的代码实例

目录1、需求很简单2、代码3、使用方法1、需求很简单 比如我在做机器学习实验的时候&#xff0c;实验结果的保存路径是’runs/exp’。 这样就会出现一个问题&#xff1a;当我第二次运行程序的时候&#xff0c;如果我忘记更改代码中的路径名或者清除上次实验结果&#xff0c;这…

BurpSuite官方实验室之逻辑漏洞

BurpSuite官方实验室之逻辑漏洞 这是BurpSuit官方的实验室靶场&#xff0c;以下将记录个人逻辑漏洞共11个Lab的通关过程 Web Security Academy: Free Online Training from PortSwigger lab1&#xff1a; Excessive trust in client-side controls 过度信任客户端控件 目…

PyTorch学习笔记-Convolution Layers与Pooling Layers

1. Convolution Layers 由于图像是二维的&#xff0c;因此基本上最常用到的就是二维的卷积类&#xff1a;torch.nn.Conv2d&#xff0c;官方文档&#xff1a;torch.nn.Conv2d。 Conv2d 的主要参数有以下几个&#xff1a; in_channels&#xff1a;输入图像的通道数&#xff0c…

IDEA关于数据库报错SQL dialect is not configured或Unable to resolve table ‘表名‘

目录一、SQL dialect is not configured1.1 报错场景展示1.2 方式一&#xff0c;万能altenter1.3 方式二&#xff0c;在setting中设置二、Unable to resolve table 表名2.1 报错场景展示2.2 方式一&#xff0c;万能altenter2.3 方式二&#xff0c;在setting中设置一、SQL diale…

vscode开发STM32(三)---调试篇

vscode开发STM32&#xff08;三&#xff09;—调试篇 文章目录vscode开发STM32&#xff08;三&#xff09;---调试篇前提条件配置调试配置JLink使用JLinkGDB进行调试配置stlink使用openOCD进行调试完整的launch文件内容前提条件 安装Cortex-Debug插件 安装OpenOCD 安装JLink驱…

LeetCode HOT 100 —— 48.旋转图像

题目 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 思路 方法一&#xff1a;使用辅助数组 可以得出规律&#xff0c;将图像旋…

集合框架----源码解读HashMap篇(一)

1.HashMap官方介绍 基于哈希表的Map接口实现。该实现提供了所有可选的映射操作&#xff0c;并允许空值和空键。(HashMap类大致相当于Hashtable&#xff0c;除了它是非同步的&#xff0c;并且允许为空值。)这个类不保证映射的顺序;特别是&#xff0c;它不能保证顺序随时间的推移…

Nodejs -- Express托管静态资源

文章目录托管静态资源1 expess.static()2 托管多个静态资源目录3 挂载路径前缀托管静态资源 1 expess.static() express提供了一个非常好用的函数&#xff0c;叫做express.static()&#xff0c;通过它&#xff0c;我们可以非常方便地创建一个静态资源服务器&#xff0c;例如&…

PG::FunboxEasyEnum

nmap -Pn -p- -T4 --min-rate1000 192.168.81.132 nmap -Pn -p 22,80 -sCV 192.168.81.132 80端口是Apache2 Ubuntu的默认页面 尝试路径爆破 /mini.php可以进行文件上传 直接上传reverse-php-shell 上传linpeas脚本进行枚举&#xff0c;得到oracle用户的密码hash oracle…

2022-11-28-大数据可视化“可视化国产/进口电影票房榜单”分析,特征维度大于50

可视化国产/进口电影票房榜单前言数据分析数据可视化过程分析总结前言 党的十八大以来&#xff0c;国产电影产业与事业快速发展&#xff0c;创作水平不断提高&#xff0c;题材类型丰富多元&#xff0c;受众口碑不断提升&#xff0c;在市场竞争中表现愈发突出&#xff0c;已成为…

《论文阅读》BA-NET: DENSE BUNDLE ADJUSTMENT NETWORKS

留个笔记自用 BA-NET: DENSE BUNDLE ADJUSTMENT NETWORKS 做什么 首先是最基础的&#xff0c;Structure-from-Motion&#xff08;SFM&#xff09;&#xff0c;SFM可以简单翻译成运动估计&#xff0c;是一种基于dui8序列图片进行三维重建的算法。简单来说就是是从运动中不同角…