首页 文章 精选 留言 我的

精选列表

搜索[国密算法],共10000篇文章
优秀的个人博客,低调大师

《Java 数据结构与算法》第7章:字典树

持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥博客:https://bugstack.cn ❝ 沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞ 一、前言 二、字典树数据结构 三、字典树结构实现 1. 树枝节点 2. 插入元素 3. 索引元素 四、字典树功能测试 五、常见面试题 一、前言 Trie 的历史 字典树 Trie 这个词来自于 retrieval,于 1912 年,Axel Thue 首次抽象地描述了一组字符串数据结构的存放方式为 Trie 的想法。这个想法于 1960 年由 Edward Fredkin 独立描述,并创造了 Trie 一词。你看看,多少程序员为了一个词、方法名、属性名,想破脑袋! 二、字典树数据结构 在计算机科学中,字典树(Trie)也被称为”单词查找树“或”数字树“,有时候也被称为基数树或前缀树(因为可以通过前缀的方式进行索引)。—— 它是一种搜索树,一种已排序的数据结构,通常用于存储动态集或键为字符串的关联数组。 与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。 这是一个把 battle 单词字符串,按照字母拆分到字典树进行存放的图。 键标注在节点中,值标注在节点之下。每一个完整的英文单词对应一个特定的整数。也就是26个字母对应的 ASCII 转换后的值。 三、字典树结构实现 字典树字母的存放有26个,也就是说在实现的过程中,每一个节点的分支都有26个槽位用来存放可能出现的字母组合。同理如果是数字树的话就是10个数字的组合,每个字典树上的节点对应的分支则有10个操作存放可能出现组合的数字。 接下来我们就基于 Java 语言实现一个字典树的存放和遍历索引的功能。 源码地址:https://github.com/fuzhengwei/java-algorithms 本章源码:https://github.com/fuzhengwei/java-algorithms/tree/main/data-structures/src/main/java/stack 1. 树枝节点 publicclassTrieNode{/**形成一个链*/publicTrieNode[]slot=newTrieNode[26];/**字母*/publiccharc;/**单词:数量>0表示一个单词*/publicbooleanisWord;/**前缀*/publicintprefix;/**单词:具体的一个单词字符串*/publicStringword;/**解释:单词的注释说明*/publicStringexplain;} 字典的树的节点需要包括此节点内嵌的关联节点,之后是节点的字母、到此字母是否为单词、单词的前缀、单词字符串和当前单词的非必要注释。 2. 插入元素 publicvoidinsert(Stringwords,Stringexplain){TrieNoderoot=wordsTree;char[]chars=words.toCharArray();for(charc:chars){intidx=c-'a';//-a从0开始,参考ASCII码表if(root.slot[idx]==null){root.slot[idx]=newTrieNode();}root=root.slot[idx];root.c=c;root.prefix++;}root.explain=explain;//单词的注释说明信息root.isWord=true;//循环拆解单词后标记} insert 方法接收单词和注释信息,并对一个单词按照 char 进行拆分,拆分后则计算出索引位置并以此存放。存放完成后标记单词和附属上单词的注释信息。 3. 索引元素 publicList<String>searchPrefix(Stringprefix){TrieNoderoot=wordsTree;char[]chars=prefix.toCharArray();StringBuildercache=newStringBuilder();//精准匹配:根据前置精准查找for(charc:chars){intidx=c-'a';//匹配为空if(idx>root.slot.length||idx<0||root.slot[idx]==null){returnCollections.emptyList();}cache.append(c);root=root.slot[idx];}//模糊匹配:根据前缀的最后一个单词,递归遍历所有的单词ArrayList<String>list=newArrayList<>();if(root.prefix!=0){for(inti=0;i<root.slot.length;i++){if(root.slot[i]!=null){charc=(char)(i+'a');collect(root.slot[i],String.valueOf(cache)+c,list,15);if(list.size()>=15){returnlist;}}}}returnlist;}protectedvoidcollect(TrieNodetrieNode,Stringpre,List<String>queue,intresultLimit){//找到单词if(trieNode.isWord){trieNode.word=pre;//保存检索到的单词到queuequeue.add(trieNode.word+"->"+trieNode.explain);if(queue.size()>=resultLimit){return;}}//递归调用,查找单词for(inti=0;i<trieNode.slot.length;i++){charc=(char)('a'+i);if(trieNode.slot[i]!=null){collect(trieNode.slot[i],pre+c,queue,resultLimit);}}} 从字典树从检索元素的过程分为2部分,第1部分是根据提供的索引前缀精准匹配到单词信息,第2部分是根据索引前缀的最后一个单词开始,循环递归遍历从当前位置所能关联到的字母直至判断为是单词标记为结束,通过这样的方式把所有匹配动的单词索引出来。 list.size() >= 15 是判定索引的最大长度,超过这个数量就停止索引了,毕竟这是一种O(n)时间复杂度的操作,如果加载数十万单词进行匹配,执行速度还是比较耗时的。 四、字典树功能测试 @Testpublicvoidtest_trie(){Trietrie=newTrie();//存入trie.insert("bat","大厂");trie.insert("batch","批量");trie.insert("bitch","彪子");trie.insert("battle","战斗");logger.info(trie.toString());//检索List<String>trieNodes=trie.searchPrefix("ba");logger.info("测试结果:{}",JSON.toJSONString(trieNodes));} 这里提供一些有相近字母的单词和名词,用于测试。你也可以尝试读取txt文件,检索存入数十万单词进行检索验证。 测试结果 06:21:38.226[main] INFO trie.__test__.TrieTest -测试结果:["bat->大厂","batch->批量","battle->战斗"]Processfinishedwithexitcode0 通过测试结果可以看到,把所有以 ba 开头的单词全部检索出来了。这也是字典树最核心功能的体现。 读者在学习的过程中,可以尝试在检索的方法体内打一些断点看一下具体的执行过程,方便学习整个执行步骤。 五、常见面试题 简述字典树的数据结构 叙述你怎么来实现一个字典树 字典树的实际业务场景举例【排序、全文搜索、网络搜索引擎、生物信息】 字典树的存入和检索的时间复杂度 还有哪些字典树的实现方式【后缀树、哈希树、帽子树】 - END - 下方扫码关注 公众号【bugstack虫洞栈】回复【星球】,加入小傅哥的【私有技术朋友圈】你将获得;分布式DDD抽奖、API网关、IM通信、手写Mybatis、手写Spring、面经手册等高质量大厂项目和技术小册/PDF等资料。更多介绍 公众号:bugstack虫洞栈 你好,我是小傅哥。一线互联网 java 工程师、T8架构师,开发过交易&营销、写过运营&活动、设计过中间件也倒腾过中继器、IO板卡。不只是写Java语言,也搞过C#、PHP,是一个技术活跃的折腾者。 本文分享自微信公众号 - bugstack虫洞栈(bugstack)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

看动画学算法之:二叉搜索树BST

简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的非线性的数据结构。 树是由很多个节点组成的,每个节点可以指向很多个节点。 如果一个树中的每个节点都只有0,1,2个子节点的话,这颗树就被称为二叉树,如果我们对二叉树进行一定的排序。 比如,对于二叉树中的每个节点,如果左子树节点的元素都小于根节点,而右子树的节点的元素都大于根节点,那么这样的树被叫做二叉搜索树(Binary Search Tree)简称BST。 今天我们来探讨一下BST的性质和对BST的基本操作。 BST的基本性质 刚刚我们已经讲过BST的基本特征了,现在我们再来总结一下: BST中任意节点的左子树一定要比该节点的值要小 BST中任意节点的右子树一定要比该节点的值要大 BST中任意节点的左右子树一定要是一个BST。 看一张图直观的感受一下BST: BST的构建 怎么用代码来构建一个BST呢? 首先,BST是由一个一个的节点Node组成的,Node节点除了保存节点的数据之外,还需要指向左右两个子节点,这样我们的BST完全可以由Node连接而成。 另外我们还需要一个root节点来表示BST的根节点。 相应的代码如下: public class BinarySearchTree { //根节点 Node root; class Node { int data; Node left; Node right; public Node(int data) { this.data = data; left = right = null; } } } BST的搜索 先看下BST的搜索,如果是上面的BST,我们想搜索32这个节点应该是什么样的步骤呢? 先上图: 搜索的基本步骤是: 从根节点41出发,比较根节点和搜索值的大小 如果搜索值小于节点值,那么递归搜索左侧树 如果搜索值大于节点值,那么递归搜索右侧树 如果节点匹配,则直接返回即可。 相应的java代码如下: //搜索方法,默认从根节点搜索 public Node search(int data){ return search(root,data); } //递归搜索节点 private Node search(Node node, int data) { // 如果节点匹配,则返回节点 if (node==null || node.data==data) return node; // 节点数据大于要搜索的数据,则继续搜索左边节点 if (node.data > data) return search(node.left, data); // 如果节点数据小于要搜素的数据,则继续搜索右边节点 return search(node.right, data); } BST的插入 搜索讲完了,我们再讲BST的插入。 先看一个动画: 上的例子中,我们向BST中插入两个节点30和55。 插入的逻辑是这样的: 从根节点出发,比较节点数据和要插入的数据 如果要插入的数据小于节点数据,则递归左子树插入 如果要插入的数据大于节点数据,则递归右子树插入 如果根节点为空,则插入当前数据作为根节点 相应的java代码如下: // 插入新节点,从根节点开始插入 public void insert(int data) { root = insert(root, data); } //递归插入新节点 private Node insert(Node node, int data) { //如果节点为空,则创建新的节点 if (node == null) { node = new Node(data); return node; } //节点不为空,则进行比较,从而递归进行左侧插入或者右侧插入 if (data < node.data) node.left = insert(node.left, data); else if (data > node.data) node.right = insert(node.right, data); //返回插入后的节点 return node; } BST的删除 BST的删除要比插入复杂一点,因为插入总是插入到叶子节点,而删除可能删除的是非叶子节点。 我们先看一个删除叶子节点的例子: 上面的例子中,我们删除了30和55这两个节点。 可以看到,删除叶子节点是相对简单的,找到之后删除即可。 我们再来看一个比较复杂的例子,比如我们要删除65这个节点: 可以看到需要找到65这个节点的右子树中最小的那个,替换掉65这个节点即可(当然也可以找到左子树中最大的那个)。 所以删除逻辑是这样的: 从根节点开始,比较要删除节点和根节点的大小 如果要删除节点比根节点小,则递归删除左子树 如果要删除节点比根节点大,则递归删除右子树 如果节点匹配,又有两种情况 如果是单边节点,直接返回节点的另外一边 如果是双边节点,则先找出右边最小的值,作为根节点,然后将删除最小值过后的右边的节点,作为根节点的右节点 看下代码的实现: // 删除新节点,从根节点开始删除 void delete(int data) { root = delete(root, data); } //递归删除节点 Node delete(Node node, int data) { //如果节点为空,直接返回 if (node == null) return node; //遍历左右两边的节点 if (data < node.data) node.left = delete(node.left, data); else if (data > root.data) node.right = delete(node.right, data); //如果节点匹配 else { //如果是单边节点,直接返回其下面的节点 if (node.left == null) return node.right; else if (node.right == null) return node.left; //如果是双边节点,则先找出右边最小的值,作为根节点,然后将删除最小值过后的右边的节点,作为根节点的右节点 node.data = minValue(node.right); // 从右边删除最小的节点 node.right = delete(node.right, node.data); } return node; } 这里我们使用递归来实现的删除双边节点,大家可以考虑一下有没有其他的方式来删除呢? 本文的代码地址: learn-algorithm 本文收录于www.flydean.com 最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现! 欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

优秀的个人博客,低调大师

某社交App cs签名算法解析(一) SSL双向认证

一、目标 奋飞: 老板,咱们得招几个妹子呀,咱们公司男女比例太失衡了。 李老板: 你去这个App上晃晃,据说上面妹子很多。 我去,包都抓不到,耍个毛线呀。 TIP: 新鲜热乎的 v3.83.0 二、步骤 SSL双向认证 问了下谷歌,有不少同道都遇到了返回值是 400 No required SSL certificate was sent 这种情况。 他们一致认为,是遇到了SSL双向认证。 不过谷歌传来的消息是,搞SSL双向认证很简单,把客户端证书,搞出来,然后再导入到 Charles,就大功告成了。 client.p12 在Apk包里面轻松找到 /assets/client.p12, 下一步就是找对应的证书密码,so easy嘛。 Jadx上,搜索字符串 "client.p12" 或者 "client.cer", 字符串倒是都找到了,不过问题是,找不到这些字符串被调用的地方。 看来被同道们搞了好几轮,新版本的App做了修改了。 Hook KeyStore来Dump证书 只要客户端有证书,那就一定会在使用中导入,我们在他导入的时候Dump出来,这样应该可以通杀了。 Java.perform(function() { console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/sdcard/Download/client_keystore_{nowtime}.p12 证书密码: fenfei"); Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { var result = this.getPrivateKey(); let filePath = "/sdcard/Download/client_keystore_" + "_" + getNowTime() + '.p12'; dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); return result; } Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { var result = this.getCertificateChain(); let filePath = "/sdcard/Download/client_keystore_" + "_" + getNowTime() + '.p12'; dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); return result; } }) 运行之前先允许这个App有读写存储卡的权限,因为最后我们是把证书写到sd卡里面了,否则会提示: Error: java.io.FileNotFoundException: /sdcard/Download/client_keystore__2021_05_24_xx_xx_xx_53.p12 (Permission denied) 跑一下,程序崩溃了。 这也难不倒我们,估计大概率是被检测到了, 那么换frida端口,然后把fridaServer换成hluda-server。 再来,还是崩溃。奇怪,还有啥呢? Xposed。 把Xposed Status关掉。 再来。 dump File dump OK !!! dump:/sdcard/Download/client_keystore__2021_05_24_16_48_09_24.p12 完美,证书出来了,咱们赶紧拷出来。 Charles添加证书 Proxy -> SSL Porxy Settings 然后输入证书监控的host , *.sxxapp.cn ,端口是 443 迫不及待了,跑一把试试。 完美收工。 三、总结 frida的spawn模式启动这个App的时候会崩掉,我认为是Xposed的原因,把Xposed关掉就好了。当然也许是我的手机环境有问题。 大家都在进步,所以要多掌握几种方法,东边不亮西边亮。 Dump证书的方法,参考 https://github.com/CreditTone/hooker 中的 keystore_dump.js 感谢大佬们提供的神奇工具。 当别人都很老实的时候,你就耍点儿小聪明;当别人都耍小聪明的时候,你就老实做人。当别人既会耍小聪明又会做老实人的时候,你就干点别的。 TIP: 本文的目的只有一个就是学习更多的逆向技巧和思路,如果有人利用本文技术去进行非法商业获取利益带来的法律责任都是操作者自己承担,和本文以及作者没关系,本文涉及到的代码项目可以去 奋飞的朋友们 知识星球自取,欢迎加入知识星球一起学习探讨技术。有问题可以加我wx: fenfei331 讨论下。 关注微信公众号: 奋飞安全,最新技术干货实时推送

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册