-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
112 lines (112 loc) · 63.1 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[你好2020]]></title>
<url>%2F2020%2F01%2F31%2F%E4%BD%A0%E5%A5%BD2020%2F</url>
<content type="text"><![CDATA[拾起你好,2020Hello 2020]]></content>
<categories>
<category>日志</category>
</categories>
<tags>
<tag>计划</tag>
</tags>
</entry>
<entry>
<title><![CDATA[学习JavaScript引擎执行机制]]></title>
<url>%2F2019%2F03%2F23%2F%E5%AD%A6%E4%B9%A0JavaScript%E5%BC%95%E6%93%8E%E6%89%A7%E8%A1%8C%E6%9C%BA%E5%88%B6%2F</url>
<content type="text"><![CDATA[此博文用于记录学习浏览器的JavaScript引擎执行机制的一些知识点,可用于解决以下的问题: JavaScript是单线程语言,是如何实现异步的? JavaScript引擎的event loop机制是怎样的? 谈谈 async/await 和 promise 的执行顺序 JavaScript的单线程特性与异步为什么JavaScript是单线程的?首先要考虑到JavaScript设计之初就是为了用于浏览器上。假设:JavaScript是多线程的,当浏览器渲染页面的时候分别有:线程1和线程2,两者同时操作同一个dom节点。线程1命令删除该节点,而线程2命令编辑此节点。两个线程的指令同时指定给同一个dom节点,且指令是矛盾的,浏览器就不知道怎么执行了。(有点像后端多线程针对同一个数据库资源分别进行读写操作) 为什么JavaScript需要异步?反向思考,假如JavaScript不支持异步,那么在JavaScript执行的过程中,如果出现某段代码执行时间过长,导致下面的代码被阻塞了,出现长时间的卡死,导致功能不完整,甚至功能不可用,最终给用户很差的用户体验。 有了异步执行,JavaScript可以灵活地处理代码的执行,开发人员在开发的过程中会知道哪些需要同步处理,哪些可以异步处理。 单线程的JavaScript如何实现异步?JavaScript是通过事件循环机制(event loop)来实现异步的,事件循环机制也就是JavaScript的执行机制(浏览器端和nodejs端的事件循环机制会有一点差别,这里不做展开)。 JavaScript的事件循环机制同步任务和异步任务?为了容易的理解,先将JavaScript的任务分为两类:同步任务和异步任务。 JavaScript在执行代码的时候会判断当前任务类型是什么,如果是同步任务则进入主线程中按顺执行,而异步任务则进入任务队列当中,等待JavaScript引擎的调用。当某个异步任务被JavaScript调用的时候,会离开任务队列而进入主线程执行(如果此异步任务有回调函数的话,否则就不需要进入主线程)。 主线程顾名思义就是JavaScript唯一的执行线程,里面的任务是按顺序执行。而任务队列则是异步任务等待执行的地方。 当主线程的任务全部完成了,JavaScript引擎就会检查任务队列中异步任务,是否可以进入主线程了。 宏任务和微任务到这里就基本认识了JavaScript的循环机制,但实际上并不能简单地将任务划分为同步任务和异步任务两种,而是应该划分为 宏任务 和 微任务: 特点: 宏任务的优先级要高于微任务 每一个宏任务执行完毕都必须将当前的微任务队列清空 第一个 script 标签的代码是第一个宏任务 macro-task(宏任务):包括整体代码script,setTimeout,setInterval micro-task(微任务):Promise,process.nextTick 参考链接: 异步操作概述 - 阮一峰 令人费解的 async/await 执行顺序 - Jialiang_T 8张图帮你一步步看清 async/await 和 promise 的执行顺序]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JavaScript</tag>
<tag>基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《微服务—新技术架构术语的定义》读后笔记]]></title>
<url>%2F2019%2F02%2F23%2F%E3%80%8A%E5%BE%AE%E6%9C%8D%E5%8A%A1%E2%80%94%E6%96%B0%E6%8A%80%E6%9C%AF%E6%9E%B6%E6%9E%84%E6%9C%AF%E8%AF%AD%E7%9A%84%E5%AE%9A%E4%B9%89%E3%80%8B%E8%AF%BB%E5%90%8E%E6%84%9F%2F</url>
<content type="text"><![CDATA[《微服务—新技术架构术语的定义》读后笔记 原博文地址 译文地址 从文章发布时间来看,刚好处在微服务这一概念很火(流行在社交和学术研究)但却又很模糊(在当时还没有对于微服务能被大众接收的定义)的时间。Martin Fowler的这篇博文算是给大众指出一个探究“微服务架构”的方向。同时可以结合Martin Fowler的这次演讲视频学习(需要柯-学-上-网),内容就是对微服务架构定义的介绍,是对原博文的提炼。 个人认为,学习微服务架构的目标就是能给一个复杂的单体架构应用系统做有效的服务拆分(需要仔细考虑服务的职责边界),开发完成后实现自动化的独立部署和维护。前提是能理解微服务架构出现的背景,即从 传统的单体架构 向 面向服务架构 的发展中,到底有什么问题需要被解决。「微服务架构是“面向服务架构(SOA)”的一个子集」。 微服务的定义微服务,是以开发一组小型服务的方式来开发一个独立的应用系统,里面的每个小型服务都独立运行在各自的进程中,相互通过api来进行通信。 每个小型服务的构建都是围绕其在业务流程中的功能,能通过全自动部署的机制来进行独立部署(独立升级、独立替换,符合组件化的特征)。 这些微服务可以使用不同的语言来编写(可以根据语言特性进行技术选型,提升技术创新),并且可以使用不同的数据存储技术(适应业务需求,从响应速度、延展等维度进行考虑)。 对这些微服务我们仅做最低限度的集中管理(总线)。 特性一:组件化和多服务组件是一个可被独立进行更换和升级的软件单元。 传统的单体应用系统是内嵌了多个组件,但它们依旧是运行在同一个进程里面的,只是在需要被调用时跳转到指定的内存地址进行计算。一旦需要针对某个组件进行修改,就要重新部署整个应用系统。 而微服务通过将系统根据功能来划分为多个服务并独立部署,相互之间通过明显的接口(HTTP接口)来进行通信调用服务。与传统的单体应用系统相比,修改成本会更低,只需修改服务的内部实现并重新部署此服务。 不足之处在于:远程调用服务的成本 会比在 同一进程内调用函数更高,且不能严格控制api接口、不能方便地修改服务之间的职责。 简单的应用系统是不应该采用微服务架构的,成本太高了(服务的职责划分颇为费时、部署服务器的成本)。 特性二:围绕“业务功能”组织团队既然整个系统已经被划分为多个微服务,那开发团队还会像传统那样被组建成用户界面团队、服务器端团队和数据库团队吗?答案是否定的。每个微服务都应成为独立部署、独立升级和工作的模块,其中均需要从上述三个基本维度来进行构建。全部微服务模块均由同样的用户界面团队、服务器端团队和数据库团队完成的话,很可能会导致每个微服务之间存在代码上的紧耦合。 即划分开发组织团队的标准并非是其技术类型,而是以所负责的“业务功能”。每个团队都是全栈的,拥有软件开发所需的全方位的技能:用户体验、数据库和项目管理。 比方说某个在线视频网站会用到上述多套解决方案,把这些都看作是内部多个微服务的实现。每个团队根据具体的业务能力来构建和维护多套产品,各自能被拆分为独立的服务,同时还能达到用户体验要求。 作者为了更好地让大家理解,引入了康威定律。。 任何设计(广义上的)系统的组织,都会产生这样一个设计,即该设计的结构与该组织的沟通结构相一致。 ——梅尔文•康威(Melvyn Conway), 1967年 特性三:“做产品”而非“做项目”即将每个微服务模块看作独立的产品来进行维护,只要服务还在线上,就需要原开发团队对其进行负责。(国内中小型公司估计很难是做到这一点,非技术出身的管理者更加愿意从企业经营效益的角度来进行组织架构和人员的调整) 特性四:“智能端点”与“傻瓜管道”前者“智能端点”的做法指的是微服务之间相互调用时,都有清晰明确的目标地方,我们只需要给每个微服务约定好调用接口,利用 RESTAPI 来完成资源调用即可。以便减少微服务间的耦合(接口一旦定下来了可就不能轻易变更)。 后者“傻瓜管道”则是由消息总线来把微服务的调用请求,按照目标来发送给合适的微服务处理,异步返回处理结果(消息队列)。 给微服务选择通讯方式还得根据自身的条件进行吧,至少微服务间的调用有一定的延迟。。 特性五:“去中心化”地治理技术需要根据业务特点,选择合适的工具(技术栈)来完成构建服务。这样可以更好地提高团队的技术创新能力,这也是开发们提升技术拓展能力的好机会把。 特性六:“去中心化”地管理数据数据的持久化管理应该集中在一起还是由各自微服务来完成呢?这需要参考业务上对于数据一致性的要求以及为修复“数据不一致”所付出的成本。微服务架构推崇各自服务完成自己的数据持久化任务,这样可以减轻统一数据库所带来的无休止的事务混杂问题,且数据库类型的选择也应该遵循特性五。 特性七:“基础设置”自动化持续交付这个目标是肯定要达到的。跑通大量自动化测试,然后在新的环境下(甚至热更新)进行自动化部署,这就需要“基础设置”自动化。 特性八:“容错”设计容错是要求,而不是特征。微服务架构决定了每类服务都不是运行在同一个进程中,服务之间的通讯联系有可能出现超时或无响应。每一类微服务由多台实例部署服务,当其中出现了某些实例挂掉以后,势必会导致其他实例的依赖增加、响应延迟增加等问题,最终就是导致请求线程的阻塞。(负载均衡?服务发现?) 容错设计不但但体现在服务响应部分,还包括用户体验上的设计:是直接抛出错误(体验极差)还是提供缓存或重试选择? 特性九:进化设计微服务架构应该是从单体架构系统进化而成的一个阶段,而非系统架构模式中的的银弹。具备足够简单模型的应用系统一开始就应该采用单体架构,因为微服务架构引入了分布式计算、复杂的沟通和异步处理,这些加在一起已经足够复杂了。 还要考虑什么?除此之外,还要考虑数据一致性要求(微服务架构很难达到高读数据一致性),内部模块重构(这就涉及到了微服务的边界界定,一旦定下来以后进行模块重构,会被边界所限定,重构的工作将很有可能涉及到其他微服务模块)。 微服务:部分部署能力(特性一)、可用性(部分服务挂掉不代表这个系统不能用)、持续模块化、可部署在多个不同的平台(因为整个系统未必是由同一类语言所编写的)。 例如考虑到微服务便捷部署的特点,你家的服务器配置是不是也能跟得上速度呢?上云服务部署实例还是有专人负责配置呢(事实上在云中部署更快)?例如怎样设计系统的监控?怎样才能尽早地发现问题? 以上就是我的一点拙见。]]></content>
<categories>
<category>后端</category>
</categories>
<tags>
<tag>微服务</tag>
<tag>架构</tag>
<tag>读后感</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java连接数据库]]></title>
<url>%2F2019%2F02%2F18%2FJava%E8%BF%9E%E6%8E%A5%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%BA%93%2F</url>
<content type="text"><![CDATA[使用Java连接本地数据库的一些笔记。 准备 引入 jdbc 的包。 123456<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc --><dependency> <groupId>org.xerial</groupId> <artifactId>sqlite-jdbc</artifactId> <version>3.25.2</version></dependency> 依赖注入(工厂模式) 兼容支持 sql 的数据库。引入java.sql.DriverManager 可以用来驱动数据库,代码只需要依赖于 jdbc 的接口(即支持标准SQL语言的数据库),不需要考虑具体的数据库实现。 Statement 是 SQL 包里面的一个类,没有办法直接声明成 Component,因此需要用工厂模式去生成可以注入的依赖对象。然后在 getObject方法里面进行连接数据库。 具体实现 数据库中的表在 java 代码里的映射和封装会体现在类的属性上 例如: Product.class 里面有的属性是 id、name、price 和 description,那么将会对应在数据库中 Product 表里面的字段。未必需要一一对应。 创建 DAO 对象来实现数据操作的逻辑 数据库地址应该放到配置文件中 数据库初始化只需要执行一次 SQL 语句的执行应该封装在同一个地方,不要散落到其他文件中]]></content>
<categories>
<category>数据库</category>
</categories>
<tags>
<tag>Java</tag>
<tag>数据库</tag>
<tag>SQL</tag>
</tags>
</entry>
<entry>
<title><![CDATA[学习SQLite]]></title>
<url>%2F2019%2F02%2F13%2F%E5%AD%A6%E4%B9%A0SQLite%2F</url>
<content type="text"><![CDATA[简介SQLite是一个轻量级的关系型数据库,运算速度快,占用资源少,很适合在移动设备上使用, 不仅支持标准SQL语法,还遵循ACID(数据库事务)原则,无需账号,使用起来非常方便! SQLite的存储类型有五类,分别是 NULL、INTEGER、REAL、TEXT和BLOB。SQLite支持列的亲和类型概念,即任何列仍然可以存储任何类型的数据。 数据存储(存储位置、存储形式)SQLite通过文件来保存数据库在本地,一个文件就是一个数据库,数据库中又包含多个表格,表格里又有多条记录,每个记录由多个字段构成,每个字段有对应的值,每个值我们可以指定类型,也可以不指定类型(主键除外)。 数据结构和组织SQLite的一个DB对应一个物理文件,文件中的数据被分成若干个大小相等的 Page 进行存储,这些 Page 之间的关系由树形结构来组织管理。SQLite 以 B+树(B+tree)的数据结构形式存储在磁盘上,最大支持 2TB 的数据库。 B/B+树 广泛应用于文件系统及数据库中,如: Window:HPFS文件系统 Mac:HFS,HFS+文件系统 Linux:ResiserFS,XFS,Ext3FS,JFS文件系统 数据库:Oracle,MySQL,SQLServer,SQLite等了解B树与B+树 ==注:以上三张图来自朱清华的论文《基于Android手机SQLite的取证系统设计实现》 #9E9E9E== 关于大文件究竟是直接存储在db,还是将文件持久化到本地,然后将数据索引添加到数据库呢? 官方答案: pageSize 为8192 或者16384 ,对于大文件的存储能达到最好的性能 大于100 kb 的文件建议用外部存储,小于100kb 的直接写db就好。 如何选择适合自己的 pageSize 呢? 假设有一个字段,存储数据平均大小为 M,我们只要确保尽量不要出现溢出就好。也就是 pageSize 尽量比 M 大一点,避免开辟溢出页。 当存在很大的数据,那么直接用外部存储吧,数据库用来存外部数据索引就好。对于第二个问题,很简单,把经常查询需要用到的字段安排在前面,避免存到溢出页。 pageSize 默认值是 1024,理论上的设置范围是 512~65536(必须是2的幂),之所以称之为理论值,是因为它还受到 SQLITE_MAX_DEFAULT_PAGE_SIZE 的限制。需要注意的是,page_size的设置需要在数据库创建之前才能生效! 数据索引 (如何找到数据)索引介绍数据库的索引跟字典目录的概念是一致的。索引本质上是一个指向数据库中的数据的指针。索引是有序的,因而可以对其执行二分查找。 索引有助于加快 SELECT 查询和 WHERE 子句,但会减慢使用 UPDATE 和 INSERT 语句时的数据输入。索引可以创建或者删除,但不会影响数据。 上面提及到,SQLite底层是使用B+树来存储数据的,B+树的结构特点就是:非叶子节点仅具有索引作用,所有的数据均存放在叶子节点中。每一个叶子节点都带有指向下一个节点的指针,形成了一个有序链表。 比如需要查询元素3: 索引的优点: 大大加快了数据的检索速度,这也是创建索引的主要原因 加快表和表之间的链接 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。 索引的缺点: 创建索引需要耗费一定的时间 索引需要占用物理空间,特别是聚集索引,需要较大的空间(SQLite不支持聚集索引) 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度,这个是比较大的问题。 索引语句 单列索引:只基于表的一个列上创建的索引 12CREATE INDEX index_nameON table_name (column_name); 唯一索引:不允许任何重复的值插入到表中,为了性能,同时也为了数据的完整性 12CREATE UNIQUE INDEX index_nameon table_name (column_name); 组合索引:基于一个表的两个或多个列上创建的索引 12CREATE INDEX index_nameon table_name (column1, column2); 隐式索引:在创建对象时,由数据库服务器自动创建的索引。索引自动创建为主键约束和唯一约束。 数据的更新和查询SQLite 只提供了表级锁,在并发、多线程操作上存在局限性。SQLite支持使用 SQL语言来进行更新和查询。 SQL语句 更新 123UPDATE table_nameSET column1 = value1, column2 = value2....,columnN = valueNWHERE [condition]; 查询 123SELECT column1,column2,columnNFROM table_nameWHERE [condition] 数据的备份和冗余 (防止丢失和保持可用)SQLite可以直接备份数据库文件来进行全量备份(手动操作,恢复的时候进行数据库文件替换即可)。12sqlite> .backup 'backup.db'sqlite> .restore 'backup.db' 数据一致性, 事务事务是对一个数据库执行工作单元。你可以把许多的 SQLite 查询联合成一组,把这些放在一起作为事务的一部分进行执行。 事务保障了关系数据库的一致性,事务的准则是:要么全部成功,要么全部失败。 当发出begin命令时,事务将持续到调用commit或rollback,或者sql命令引起约束违反进而导致rollback。一般情况下,锁持续时间隐藏在事务持续时间内,他们总是一起结束。下图可以看到事务的声明周期和锁状态 事务的控制主要有三条命令: BEGIN TRANSACTIION 开始处理事务 COMMIT 保存更改,或者可以使用 END TRANSACTION 命令 ROLLBACK 回滚所做的更改 事务控制命令只与 DML 命令 INSERT、UPDATE 和 DELETE 一起使用。他们不能在创建表或删除表时使用,因为这些操作在数据库中是自动提交的。 事务详解 数据管理数据分析和PostgreSQL非常相似,SQLite中的ANALYZE命令也同样用于分析数据表和索引中的数据,并将统计结果存放于SQLite的内部系统表中,以便于查询优化器可以根据分析后的统计数据选择最优的查询执行路径,从而提高整个查询的效率。见如下示例: 如果在ANALYZE命令之后没有指定任何参数,则分析当前连接中所有Attached数据库中的表和索引。 1sqlite> ANALYZE; 如果指定数据库作为ANALYZE的参数,那么该数据库下的所有表和索引都将被分析并生成统计数据。 1sqlite> ANALYZE main; 如果指定了数据库中的某个表或索引为ANALYZE的参数,那么该表和其所有关联的索引都将被分析。 12sqlite> ANALYZE main.testtable;sqlite> ANALYZE main.testtable_idx2; 数据管理工具DataGripNavicat 参考链接开放源码嵌入式数据库 SQLite 简介 菜鸟教程 SQLite使用总结 SQLite源码分析 SQLite学习手册]]></content>
<categories>
<category>数据库</category>
</categories>
<tags>
<tag>数据库</tag>
<tag>SQL</tag>
<tag>SQLite</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java简单爬虫]]></title>
<url>%2F2019%2F01%2F02%2FJava%E7%AE%80%E5%8D%95%E7%88%AC%E8%99%AB%2F</url>
<content type="text"><![CDATA[总结内容:使用Java引用 jsoup 等一系列的第三方包,实现一个简单的爬虫。 思路我打算爬取的网站是 Readhub,是一个新闻类的聚合资讯网站。每一篇新闻的页面,除了有新闻标题以及简单的新闻概要之外,还有类似的新闻集合。那么我要爬取的就是新闻标题、新闻概要以及类似的新闻。 那么一个简单的流程就出现了: 设置一个种子地址,作为爬虫的入口 startUrl;设置一个队列,用来存放待爬取的地址 newsQueue;设置最大的爬取数 maximumUrl;设置一个用于记录已经访问过的地址的集合 visted;设置一个用于保存新闻的数组 results; 将种子地址添加到队列里面; 在队列为空且计数已经超过 maximumUrl 之前,从队列中拉取一个目标地址进行访问,获取新闻标题、新闻概要以及额外的新闻集合 遍历额外的新闻集合,如果该新闻地址未曾出现在 visted,就添加到 visted 和 newsQueue 输出 results 里面的新闻第三方包 commons-io org.jsoup org.json(如果需要解析json)jsoup 快速入门快速入门中文文档 代码及注释 先从 model 包开始,包内存放的包括有 News类及其继承类、NewsReader及其继承类 、 urlNewsReader 类 和 Viewable接口。其中 News 父类继承了 Viewable接口。 News 类 其实是对新闻内容的抽象,里面有 title 和 content,而 UrlNews 还会额外多出 url。所有的 News 的 display 方法正式对 Viewable 接口的具体实现。 Viewable123456package com.orrz.spider.model;public interface Viewable { // 只有一个未实现的方法 display void display();} News1234567891011121314151617181920212223242526package com.orrz.spider.model;public class News implements Viewable { private String title; private String content; public News(String title, String content) { this.title = title; this.content = content; } // getter public String getTitle() { return title; } public String getContent() { return content; } // 实现 display 方法 @Override public void display() { System.out.println("--------------------------------------"); System.out.println("|Title| " + this.title); System.out.println("|Content| " + this.content); }} UrlNews1234567891011121314151617181920package com.orrz.spider.model;public class UrlNews extends News { private String Url; public UrlNews(String url, String title, String content) { super(title, content); this.Url = url; } public String getUrl() { return Url; } @Override public void display() { super.display(); System.out.println("|Url| " + this.getUrl()); }} UrlNews 继承自 News,增添了一个静态变量 Url NewsWithRelated1234567891011121314151617181920212223242526272829303132package com.orrz.spider.model;import java.util.HashMap;import java.util.Map;public class NewsWithRelated extends UrlNews { // relateds 是一个 哈希表,用于存放类似的新闻列表 private HashMap<String, String> relateds = new HashMap<String, String>(); public NewsWithRelated(String url, String title, String content) { super(url, title, content); } // 哈希表存的是新闻的标题和地址 public void addRelated(String title, String url) { this.relateds.put(title, url); } public HashMap<String, String> getRelateds() { return relateds; } // 这里的 display 在调用 父类的 display 之外,还输出哈希表的所有关联新闻 @Override public void display() { super.display(); System.out.println("|Related| "); for (Map.Entry<String, String> Entry : this.relateds.entrySet()) { System.out.println(Entry.getKey()); System.out.println(Entry.getValue()); } }} NewsReader12345678910111213package com.orrz.spider.model;import java.io.File;public abstract class NewsReader { protected File file; public NewsReader(File file) { this.file = file; } public abstract News read();} NewsReader 是一个抽象类,内含抽象方法 read,对于不同的继承类可以有不同的 read 方法。 JsonNewsReader1234567891011121314151617181920212223242526272829303132package com.orrz.spider.model;import org.apache.commons.io.FileUtils;import org.json.JSONException;import org.json.JSONObject;import java.io.File;import java.io.IOException;public class JsonNewsReader extends NewsReader { public JsonNewsReader(File file) { super(file); } @Override public News read() { News news = null; try { String jsonString = FileUtils.readFileToString(file, "UTF-8"); // 读取文件 JSONObject jsonObject = new JSONObject(jsonString); // 创建 jsonObject 对象,然后就可以调用内置方法来获取指定字段 String title = jsonObject.getString("title"); String content = jsonObject.getString("content"); news = new News(title, content); // 构造 News 对象 } catch (IOException e) { System.out.println("新闻读取出错"); // 这里抛出IO错误 } catch (JSONException e) { System.out.println("json解析错误"); // 这里抛出 json的错误 } return news; }} TextNewsReader123456789101112131415161718192021222324252627package com.orrz.spider.model;import java.io.BufferedReader;import java.io.FileReader;import java.io.File;public class TextNewsReader extends NewsReader { public TextNewsReader(File file) { super(file); } @Override public News read() { News news = null; try { BufferedReader reader = new BufferedReader(new FileReader(file)); String title = reader.readLine(); reader.readLine(); String content = reader.readLine(); news = new News(title, content); } catch (java.io.IOException e) { System.out.println("新闻读取出错"); } return news; }} TextNewsReader 跟 JsonNewsReader 的实现类似,思路就是从文件中获取到 title 和 content 的字段来组成 News类对象,如果出现错误就抛出。 UrlNewsReader123456789101112131415161718192021222324252627282930313233package com.orrz.spider.model;import org.jsoup.Jsoup;import org.jsoup.nodes.Document;import org.jsoup.nodes.Element;import org.jsoup.select.Elements;import java.io.IOException;public class UrlNewsReader { public static NewsWithRelated read(String url) throws IOException { // JSOUP 解析页面 Document doc = Jsoup.connect(url).get(); Elements titleElements = doc.select("title"); // 这里获取到的是标签名为 title 的 element 集合 String title = titleElements.first().text(); // 取第一个的文本内容 String content = doc.select("[class~=^summary]").text(); // 这里一般情况下输入的应该是 css类名选择器,但 Jsoup 提供了可以用正则来匹配。 由于 readhub网中不同的新闻里面的css类名也不同,所以只能用正则匹配上关键部分。 写法: 属性名~=正则表达式 NewsWithRelated news = new NewsWithRelated(url, title, content); // 新建一个NewsWithRelated对象,里面除了包含一条新闻以外还能存与之相关联的其他新闻 url 和 title Elements reatledElements = doc.select("[class~=^timeline__item]"); // 这里要匹配的内容应该要事先对网站进行分析后得到的,通过查看浏览器的开发者工具来查找 // 遍历添加 for (Element element : reatledElements) { String relatedTitle = element.select("[class~=^content-item]").text(); Elements children = element.children(); // 这里用到了 absUrl 方法,从 url属性获取绝对 url // 如果属性值已经是绝对的,并且它成功解析为 url,就直接返回 // 否则就会被视为相对地址,自动填补 String relatedUrl = children.get(3).child(0).absUrl("href"); news.addRelated(relatedTitle, relatedUrl); } return news; }} 为什么 UrlNewsReader 不继承 NewsReader ? 因为 UrlNewsReader 不需要解析文件,不存在需要解析 file的需求。 ListViewer1234567891011121314151617package com.orrz.spider.view;import com.orrz.spider.model.Viewable;import java.util.ArrayList;public class ListViewer { private ArrayList<Viewable> viewableList; public ListViewer(ArrayList<Viewable> viewableList) { this.viewableList = viewableList; } public void display() { for (Viewable viewableItem : viewableList) { System.out.println("------------------------------------"); viewableItem.display(); } }} ListViewer 比较简单,就是一个存放 实现了 Viewable 接口的对象的数组。这里的实现可以理解为 多态 的运用。 Main123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051package com.orrz.spider;import com.orrz.spider.model.NewsWithRelated;import com.orrz.spider.model.UrlNewsReader;import com.orrz.spider.model.Viewable;import com.orrz.spider.view.ListViewer;import java.io.IOException;import java.util.*;public class Main { static final int maximumUrl = 10; public static void main(String[] args) throws IOException { long startTime = System.currentTimeMillis(); // 广度优先搜搜 Queue<NewsWithRelated> newsQueue = new LinkedList<NewsWithRelated>(); String startUrl = "https://readhub.me/topic/5bMmlAm75lD"; // 起始地址 NewsWithRelated startNews = UrlNewsReader.read(startUrl); // 起始新闻 NewsWithRelated 对象是一个完整的 url新闻对象,因为里面还包含了其他的关联新闻 int count = 0; Set<String> visited = new HashSet<String>(); visited.add(startUrl); newsQueue.add(startNews); ArrayList<Viewable> results = new ArrayList<Viewable>(); // 开始爬取 while (!newsQueue.isEmpty() && count <= maximumUrl) { NewsWithRelated current = newsQueue.poll(); results.add(current); count += 1; // 遍历关联新闻并进行解析,如果还没有访问过就添加到 newsQueue 和 visted for (Map.Entry<String, String> entry : current.getRelateds().entrySet()) { String url = entry.getValue(); NewsWithRelated next = UrlNewsReader.read(url); if (!visited.contains(url)) { newsQueue.add(next); visited.add(url); } } } long endTime = System.currentTimeMillis(); // 将爬取到的结果统一展示 new ListViewer(results).display(); System.out.println("程序运行时间: " + (endTime - startTime) + "ms"); }} 总结设计这个小项目的重点在于: 如何精确地抽象对象 和 设计对象要实现怎样的功能?有什么属性和行文 选择合适的数据结构 我们要实现的功能是否已经有第三方包实现了?我们如果使用第三方包 附上效果图:]]></content>
<categories>
<category>后端</category>
</categories>
<tags>
<tag>Java</tag>
<tag>jsoup</tag>
<tag>爬虫</tag>
<tag>Maven</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hackerrank题解]]></title>
<url>%2F2018%2F12%2F24%2FHackerrank%E9%A2%98%E8%A7%A3%2F</url>
<content type="text"><![CDATA[这是一篇 hackerrank Java面向对象部分的题目解答笔记。 Java Inheritance I题目详情分析本题考查子类继承父类以后,如何拓展子类的方法。直接在子类声明里面声明方法即可。 12345678910111213141516171819202122232425262728293031323334import java.io.*;import java.util.*;import java.text.*;import java.math.*;import java.util.regex.*;class Animal{ void walk() { System.out.println("I am walking"); }}class Bird extends Animal{ void fly() { System.out.println("I am flying"); } // 直接在这里声明 sing 函数,按照要求完成输出内容即可 void sing() { System.out.println("I am singing"); }}public class Solution{ public static void main(String args[]){ Bird bird = new Bird(); bird.walk(); bird.fly(); bird.sing(); }} Java Inheritance II题目详情分析本题继续考查继承的知识点,根据题目给出的 Arithmetic 和 Adder 之间的描述得知两者是父子类的关系,且 add 方法是由父类来实现的。 123456789101112131415161718192021222324252627282930import java.io.*;import java.util.*;import java.text.*;import java.math.*;import java.util.regex.*;//Write your code hereclass Arithmetic { public int add(int a, int b) { return a + b; }}// 子类只需要继承父类即可class Adder extends Arithmetic {}public class Solution{ public static void main(String []args){ // 实例化一个 Adder 对象 Adder a = new Adder(); // Print the name of the superclass on a new line // 通过一些方法来取得父类的名字 System.out.println("My superclass is: " + a.getClass().getSuperclass().getName()); // Print the result of 3 calls to Adder's `add(int,int)` method as 3 space-separated integers: System.out.print(a.add(10,32) + " " + a.add(10,3) + " " + a.add(10,10) + "\n"); }} Java Abstract Class题目详情分析题目给出了一个 Book 的抽象类,内含一个抽象方法 setTitle 和一个放回 String 类型的 getTitle 方法。根据调用方法可以得知真正调用的是 MyBook 类的实例化对象 new_novel 的 setTitle方法来设置 title,通过 getTitle 方法来获取成员变量 title。那么只需要让 MyBook 类继承 Book 类,然后具体实现 setTitle 方法即可。需要注意的是,在子类当中是通过关键字super来访问父类。123456789101112131415161718192021222324252627282930import java.util.*;abstract class Book{ String title; abstract void setTitle(String s); String getTitle(){ return title; } }//Write MyBook class here// 继承抽象类,然后实现 setTitle 的方法class MyBook extends Book { void setTitle(String s) { super.title = s; // 通过super来访问父类 }}public class Main{ public static void main(String []args){ //Book new_novel=new Book(); This line prHMain.java:25: error: Book is abstract; cannot be instantiated Scanner sc=new Scanner(System.in); String title=sc.nextLine(); MyBook new_novel=new MyBook(); new_novel.setTitle(title); System.out.println("The title is: "+new_novel.getTitle()); sc.close(); }} Java Interface题目详情分析题目要求 MyCalculator 在继承 AdvancedArithmetic 接口的前提下,通过实现 divisor_sum 函数来满足既定的需求(输入一个整数 n ,返回能被 n 整除的整数之和)。 12345678910111213141516171819202122232425262728293031323334353637383940414243import java.util.*;interface AdvancedArithmetic{ int divisor_sum(int n);}//Write your code here// 继承 AdvancedArithmetic 接口class MyCalculator implements AdvancedArithmetic { public int divisor_sum(int n) { int result = 0; if (n != 0) { for (int i = 1; i <= n; i++) { if (n % i == 0) { result += i; } } } return result; }}class Solution{ public static void main(String []args){ MyCalculator my_calculator = new MyCalculator(); System.out.print("I implemented: "); ImplementedInterfaceNames(my_calculator); Scanner sc = new Scanner(System.in); int n = sc.nextInt(); System.out.print(my_calculator.divisor_sum(n) + "\n"); sc.close(); } /* * ImplementedInterfaceNames method takes an object and prints the name of the interfaces it implemented */ // 这个方法要求传入参数类型是对象 static void ImplementedInterfaceNames(Object o){ Class[] theInterfaces = o.getClass().getInterfaces(); for (int i = 0; i < theInterfaces.length; i++){ String interfaceName = theInterfaces[i].getName(); System.out.println(interfaceName); } }} Java Method Overriding题目详情分析根据例子重写父类的同名方法。 123456789101112131415161718192021222324252627282930313233343536import java.util.*;class Sports{ String getName(){ return "Generic Sports"; } void getNumberOfTeamMembers(){ System.out.println( "Each team has n players in " + getName() ); }}class Soccer extends Sports{ @Override String getName(){ return "Soccer Class"; } @Override void getNumberOfTeamMembers() { System.out.println("Each team has 11 players in " + getName()); }}public class Solution{ public static void main(String []args){ Sports c1 = new Sports(); Soccer c2 = new Soccer(); System.out.println(c1.getName()); c1.getNumberOfTeamMembers(); System.out.println(c2.getName()); c2.getNumberOfTeamMembers(); }} Java Method Overriding 2 (Super Keyword)题目详情分析掌握通过关键字 super 来访问父类,包括调用父类的方法。1234567891011121314151617181920212223242526272829import java.util.*;import java.io.*;class BiCycle{ String define_me(){ return "a vehicle with pedals."; }}class MotorCycle extends BiCycle{ String define_me(){ return "a cycle with an engine."; } MotorCycle(){ System.out.println("Hello I am a motorcycle, I am "+ define_me()); String temp=super.define_me(); //Fix this line System.out.println("My ancestor is a cycle who is "+ temp ); } }class Solution{ public static void main(String []args){ MotorCycle M=new MotorCycle(); }} Java Instanceof keyword题目详情分析考查使用instanceof关键字来确定对象是否是某一个类的实例,返回的是一个布尔类型的值。只要判断当前对象是三个类的哪一个,然后给计数+1即可。1234567891011121314151617181920212223242526272829303132333435363738import java.util.*;class Student{}class Rockstar{ }class Hacker{}public class InstanceOFTutorial{ static String count(ArrayList mylist){ int a = 0,b = 0,c = 0; for(int i = 0; i < mylist.size(); i++){ Object element=mylist.get(i); if(element instanceof Student) a++; if(element instanceof Rockstar) b++; if(element instanceof Hacker) c++; } String ret = Integer.toString(a)+" "+ Integer.toString(b)+" "+ Integer.toString(c); return ret; } public static void main(String []args){ ArrayList mylist = new ArrayList(); Scanner sc = new Scanner(System.in); int t = sc.nextInt(); for(int i=0; i<t; i++){ String s=sc.next(); if(s.equals("Student"))mylist.add(new Student()); if(s.equals("Rockstar"))mylist.add(new Rockstar()); if(s.equals("Hacker"))mylist.add(new Hacker()); } System.out.println(count(mylist)); }} Java Iterator题目详情分析本题考查的是对于 Java 迭代器 的基本认识。根据题目提示,需要找出标记为###字符串后面的所有字符串,而###之前的所有输入都不是String,那么配合上一题的 instanceof 找出 String的第一个实例(“###”)即可。 1234567891011121314151617181920212223242526272829303132333435import java.util.*;public class Main{ static Iterator func(ArrayList mylist){ Iterator it=mylist.iterator(); while(it.hasNext()){ Object element = it.next(); if(element instanceof String)//Hints: use instanceof operator break; } return it; } @SuppressWarnings({ "unchecked" }) public static void main(String []args){ ArrayList mylist = new ArrayList(); Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int m = sc.nextInt(); for(int i = 0;i<n;i++){ mylist.add(sc.nextInt()); } mylist.add("###"); for(int i=0;i<m;i++){ mylist.add(sc.next()); } Iterator it=func(mylist); while(it.hasNext()){ Object element = it.next(); System.out.println((String)element); } }} Java数据结构Maximum Element题目详情分析题目其实可以转转化分析为,如何快速查找栈中的最大值(最小值也同理)。我们通过使用双栈即可,一个记录数字的顺序,另一个则用于记录最大值(当遇到的新的输入值大于栈中顶部的数,则把新的数字放入栈中,以保证栈顶的数是最大值)。 当遇到删除指令,则需要加一层判断,看此数字是否在记录最大值的栈的顶部,如果是的话就要一并进行移除。 需要打印最大值的时候则使用 peek 命令来获取最大值栈的顶部数值。 123456789101112131415161718192021222324252627282930313233343536import java.io.*;import java.util.*;public class Solution { public static void main(String[] args) { /* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */ Scanner sc = new Scanner(System.in); Stack<Integer> stack = new Stack<>(); Stack<Integer> top = new Stack<>(); int step = sc.nextInt(); while(sc.hasNext()) { int type = sc.nextInt(); switch(type) { case 1: int t = sc.nextInt(); stack.push(t); if (top.empty() || t >= top.peek()){ top.push(t); } break; case 2: if (!stack.empty()) { int j = stack.pop(); if (j == top.peek()) { top.pop(); } } break; case 3: System.out.println(top.peek()); } } sc.close(); }} Balanced Brackets(括号平衡)题目详情分析这题也是考查对栈的应用。 首先要判断目标字符串的长度,如果小于2的话就可以直接返回false了(有的题目要求如果字符串为空的话,结果为true,这个也应该在一开始进行判断,就可以节省代码执行时间)。 遍历字符串的时候,遇到左括号就可以直接压入栈中;当遇到右括号的时候,先检查栈是否为空(为空的话就直接返回 false ,因为这时候没有左括号可供消除),必须是会与栈顶的左括号想匹配的(如果不匹配的话就可以返回 false 了),匹配的话就对执行栈执行 pop 操作。 遍历结束以后,如果栈的长度为空,说明已经消除了全部的左括号,否则就是还有左括号没有被平衡。 12345678910111213141516171819202122232425262728293031323334353637383940import java.io.*;import java.math.*;import java.security.*;import java.text.*;import java.util.*;import java.util.concurrent.*;import java.util.regex.*;public class Solution { // Complete the isBalanced function below. static String isBalanced(String s) { Stack<Character> stack = new Stack<>(); if (s.length() < 2) { return "NO"; } for(int i = 0; i< s.length(); i++) { char t = s.charAt(i); if ((t == '(') || (t == '[') || (t == '{')) { stack.push(t); } else { if (!stack.empty()) { // 这里的判断条件可以用一个 if 语句完成,这里分开写只是为了容易理解。 if (stack.peek() == '(' && t == ')') { stack.pop(); }else if (stack.peek() == '[' && t == ']') { stack.pop(); }else if (stack.peek() == '{' && t == '}') { stack.pop(); } else { return "NO"; } }else { return "NO"; } } } return stack.size() == 0 ? "YES" : "NO"; }} Equal Stacks题目详情分析贪心解决。 123456789101112131415161718192021222324252627282930313233343536373839404142public class Solution { static int equalStacks(int[] h1, int[] h2, int[] h3) { // 用三个栈按顺序存储输入的数组 Stack<Integer> s1 = new Stack<Integer>(); Stack<Integer> s2 = new Stack<Integer>(); Stack<Integer> s3 = new Stack<Integer>(); // 三个用于存储栈内数字总和的变量 int sum1 = 0; int sum2 = 0; int sum3 = 0; // 分别填充栈和计算总和 for (int i = h1.length-1; i >= 0; i--) { sum1 += h1[i]; s1.push(h1[i]); } for (int i = h2.length-1; i >= 0; i--) { sum2 += h2[i]; s2.push(h2[i]); } for (int i = h3.length-1; i >= 0; i--) { sum3 += h3[i]; s3.push(h3[i]); } // 其实这里的判断条件可以为 sum1 == sum2 && sum1 == sum3 // 但考虑到如果出现为0(也就是无法匹配里面的逻辑条件的话),就不需要继续 while 循环了,所以也可以用true // 处理方法就是,找到总数最大的栈,然后取出顶部数字,同时所对应的 sum* 变量也跟着自减 // 贪心处理就可以解决 while(true){ if (sum1 > sum2 || sum1 > sum3) { sum1 -= s1.pop(); }else if (sum2 > sum3 || sum2 > sum1) { sum2 -= s2.pop(); }else if (sum3 > sum1 || sum3 > sum2) { sum3 -= s3.pop(); }else { break; } } return sum1; } 方法二1234567891011121314151617181920212223242526272829303132333435363738public class Solution {static int equalStacks(int[] h1, int[] h2, int[] h3) { // 先用sum方法来计算数组总和 int sum1 = sum(h1); int sum2 = sum(h2); int sum3 = sum(h3); // 同来记录操作下标 int i1 = 0; int i2 = 0; int i3 = 0; // 这里改为用下标来当做上个方法中的栈的顶部指针 // 这样的方法还是比较巧妙,不直接使用栈,但利用了栈处理的思想 while (true) { if (sum1 > sum2 || sum1 > sum3) { sum1 -= h1[i1++]; } else if (sum2 > sum1 || sum2 > sum3) { sum2 -= h2[i2++]; } else if (sum3 > sum1 || sum3 > sum2) { sum3 -= h3[i3++]; } else { break; } } return sum1; } // 用来计算数组中的所有元素的总和 static int sum(int[] arr) { int result = 0; for (int i : arr) { result += i; } return result; }} Game of Two Stacks题目详情分析一开始使用的贪心最小策略,但是提交的时候有部分测试不通过。后来也想明白了原因,就是假如你专注于比较每次栈顶数字的大小,会错过后面的小数字的,就是说不应该使用贪心最小策略。 这题还有点像动态规划的样子,但其实要留意一点:每次从a栈或者b栈中取数字,都必须要从顶部开始。 这样的话我们可以先假设全部从b栈中取数字,看能取到什么程度。然后再把b栈已经取得的最后一位数字退回去,改用a栈的来代替。这样就可以尝试两边都取。最优解的结果只有一个,虽然有可能实现的路径有多个,正因为如此选择从a栈开始或者b栈开始都没有关系。 得好好琢磨一下才行。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748import java.io.*;import java.math.*;import java.text.*;import java.util.*;import java.util.regex.*;public class Solution { /* * Complete the twoStacks function below. */ static int twoStacks(int x, int[] a, int[] b) { int sum = 0; int ib = 0; // 可以不用栈,但应该有栈的特性(先进后出) // 所以应该有下标来记录栈顶的位置,同时也能记录操作次数 // 遍历数组b,在符合条件内将可以获取的数都取出来 // 用ib记录 while(ib < b.length && sum + b[ib] <= x) { sum += b[ib]; ib += 1; } // 经过上面的操作以后,先假设全部针对b数组的操作的次数是最大值 int max = ib; // 然后遍历数组a,先把b的顶部数取出来,把a的头部放进去,如果小于x,则继续。如果大于x,就中断 // 每次对比a的遍历次数 + b 的遍历次数 for (int ia = 1; ia <= a.length; ia++) { sum += a[ia-1]; // 这里比较关键,首先sum已经添加了a数组的最后一项 // 如果sum 比x大而且也仍从B中获取数字,就应该试一下从sum中减去b的顶部 while (sum > x && ib > 0) { // 因为在上面的while循环里面的判断跳出条件是 // ib > b.length; // 所以这里要对ib进行自减,才是b数组中已经取得的最后一位有效的数字(是已经拿走的数字队列中的最后一位,而不是数组的最后一位,类似于栈的顶部指针索引一样) ib -=1; sum -= b[ib]; } // 这里和上面并不冲突 if (sum > x) { break; } max = Math.max(max, ia + ib); } return max; }} Largest Rectangle(最大的矩形)题目详情分析先给高度数组尾部添加一个0(用于结束循环)。 遍历高度数组,当 i 所指向的高度的矩形比前一个矩形要高,就把其索引压入栈中(或者当栈是空的时候)。当遇到一个矩形的的高度小于前一个矩形高度的时候,先把栈的顶部元素排出并记录为top,就先计算top的矩形的面积,上一个矩形面积(高 X 宽,此时的宽是1,由遍历数组的 i 来计算的话,应该是 i - 当前栈顶的索引的矩形高度 - 1),然后跟存储的最大矩形面积相比,替换。此后步骤重复执行,每次执行结束以后,i–。 刚开始栈中为空,压入下标0;然后当i=1时,2<=1不成立,下标出栈,栈变为空,计算此时面积res=2; 依次将高度 1,5,6 所对应的索引 1,2,3 压入栈,直到i=4, 6 > 2,下标3要出栈,此时计算的面积就是6(单一个矩形的面积),宽是如何确定的呢?i (4) - stack.peek() (2) - 1 = 4 - 2 - 1 栈顶元素为2,其对应的高度为5>2(此时,还要和下标i=4的高度比较),所以,栈顶元素2出栈,计算面积为res=10; 栈顶元素是2,对应的高度 1 < 2,就把 i= 4 入栈,直到了最后一个高度为0的索引。当遍历到 i = 6,此高度为0,所有的元素都比这个大,便向前进行计算面积。 栈顶为 5,就计算此面积为4。 栈顶元素是4,对应的高度是2 > 0,把栈顶4 出栈,计算绿色部分的面积(最大的绿色矩形,从 2 => 5),得出8 栈顶元素是1,对应的高度是1 > 0,把栈顶1 出栈(此时为空栈了),计算面积方式就改为: 高度 x i,得出绿色方框部分的面积。 直到最后,最大面积是10,所以返回10。 12345678910111213141516171819202122232425262728public class LargestRectangle { static int solve(int[] h) { if (h.length < 1) { return 0; } Stack<Integer> stack = new Stack<>(); int maxArea = 0; int[] arr = Arrays.copyOf(h, h.length + 1); for (int i = 0; i < arr.length; i++) { if (stack.empty() || arr[i] >= arr[stack.peek()]) { stack.push(i); } else { int top = stack.pop(); int area = arr[top] * (stack.empty() ? i : i - stack.peek() - 1); maxArea = Math.max(area, maxArea); i--; } } return maxArea; }} public static void main(String[] args) { int[] a = new int[]{2, 1, 5, 6, 2, 3}; int k = solve(a); System.out.println(k); } 在LeetCode中看到有一种方法是这样的:12345678910111213141516171819202122232425262728293031323334353637383940414243444546 static int solve(int[] heights) {// 1、如果已知height数组是升序的,应该怎么做?// 比如1,2,5,7,8// 那么就是(1*5) vs. (2*4) vs. (5*3) vs. (7*2) vs. (8*1)// 也就是max(height[i]*(size-i))// 2、使用栈的目的就是构造这样的升序序列,按照以上方法求解。// 但是height本身不一定是升序的,应该怎样构建栈?// 比如2,1,5,6,2,3// (1)2进栈。s={2}, result = 0// (2)1比2小,不满足升序条件,因此将2弹出,并记录当前结果为2*1=2。// 将2替换为1重新进栈。s={1,1}, result = 2// (3)5比1大,满足升序条件,进栈。s={1,1,5},result = 2// (4)6比5大,满足升序条件,进栈。s={1,1,5,6},result = 2// (5)2比6小,不满足升序条件,因此将6弹出,并记录当前结果为6*1=6。s={1,1,5},result = 6// 2比5小,不满足升序条件,因此将5弹出,并记录当前结果为5*2=10(因为已经弹出的5,6是升序的)。s={1,1},result = 10// 2比1大,将弹出的5,6替换为2重新进栈。s={1,1,2,2,2},result = 10// (6)3比2大,满足升序条件,进栈。s={1,1,2,2,2,3},result = 10// 栈构建完成,满足升序条件,因此按照升序处理办法得到上述的max(height[i]*(size-i))=max{3*1, 2*2, 2*3, 2*4, 1*5, 1*6}=8<10// 综上所述,result=10 int res = 0; Stack<Integer> stack = new Stack<Integer>(); int count = 1; for (int i = 0; i < heights.length; i++) { if (stack.size() == 0 || stack.peek() <= heights[i]) { stack.push(heights[i]); } else { int counter = 0; while (stack.size() != 0 && stack.peek() > heights[i]) { counter++; res = Math.max(res, stack.peek() * counter); stack.pop(); } while (counter != 0) { stack.push(heights[i]); counter--; } stack.push(heights[i]); } } while (!stack.isEmpty()) { res = Math.max(res, stack.peek() * count); stack.pop(); count++; } return res; } 参考博客1很神奇的解法!怎么求柱状图中的最大矩形?参考博客2 Simple Text Editor题目详情分析利用栈存储每一次操作之后的字符串结果。 123456789101112131415161718192021222324252627282930313233343536373839404142434445import java.io.*;import java.util.*;public class Solution { public static void main(String[] args) { Scanner sc = new Scanner(System.in); Stack<String> st = new Stack<>(); String result = ""; st.push(result); int l = sc.nextInt(); for (int i = 0; i < l; i++) { int op = sc.nextInt(); switch (op) { case 1: result = st.peek() + sc.next(); st.push(result); break; case 2: if (!st.empty()) { int end = sc.nextInt(); result = result.substring(0, result.length() - end); st.push(result); } break; case 3: if (!st.empty()) { result = st.peek(); char x = result.charAt(sc.nextInt() - 1); System.out.println(x); } break; case 4: if (!st.empty()) { st.pop(); result = st.peek(); } break; } } sc.close(); }} Queue using Two Stacks题目详情分析使用两个栈(s1 和 s2)来实现队列的特性(先进先出)。一开始想这样:一个栈用于存储,另一个栈待命。当遇到了要 pop 或者 peek 的时候,就把 s1 所有元素 pop 到 s2,然后再进行操作,完成以后再全部转回去。 不出意外,超时了。。。 问题就出在了其实没有必要每次都进行栈内元素的转移,非常耗费时间的。换个角度,用一个类来模拟queue,实现三个方法(push、pop、peek)。当需要push进入队列,就正常 push 到 s1。pop与peek操作的对象其实是 s2,因为 s2 的存在是给我们存储的元素倒序的,假如s2 栈为空,就转移 s1 的元素到 s2 就好了。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869import java.io.*;import java.util.*;public class Solution { public static void main(String[] args) { /* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */ Scanner sc = new Scanner(System.in); int l = sc.nextInt(); QueueWithTwoStacks q = new QueueWithTwoStacks(); for (int i = 0; i < l; i++) { int op = sc.nextInt(); switch (op) { case 1: q.push(sc.nextInt()); break; case 2: q.pop(); break; case 3: System.out.println(q.peek()); break; default: break; } } sc.close(); } public static class QueueWithTwoStacks { private Stack<Integer> s1; private Stack<Integer> s2; public QueueWithTwoStacks() { super(); this.s1 = new Stack<Integer>(); this.s2 = new Stack<Integer>(); } public void push(int t) { if (s2.empty()) { while(!s1.empty()) { s2.push(s1.pop()); } } s1.push(t); } public int pop() { if (s2.empty()) { while (!s1.empty()) { s2.push(s1.pop()); } } return s2.pop(); } public int peek() { if (s2.empty()) { while (!s1.empty()) { s2.push(s1.pop()); } } return s2.peek(); } }}]]></content>
<categories>
<category>后端 - 基础知识</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Hackerrank</tag>
<tag>面向对象</tag>
</tags>
</entry>
<entry>
<title><![CDATA[基于Git分支的工作流程]]></title>
<url>%2F2018%2F11%2F30%2F%E5%9F%BA%E4%BA%8EGit%E5%88%86%E6%94%AF%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%2F</url>
<content type="text"><![CDATA[基于Git分支的工作流程是指在开发过程中,围绕git分支所展开的针对代码进行控制和管理的方法。在构建项目的过程中,假如需要实现A功能,那么先从master主分支签出一条新的分支feature A,用于在该分支上实现该A功能。好处是,这样不会影响到master主分支上既有的稳定的代码。 1$ git checkout -b feature-A 相当于执行了下面的命令:12$ git branch feature-A$ git checkout feature-A 然后就开始在feature-A分支上给功能A写代码了:当你觉得需要给已经完成了的文件进行提交保存的时候,是需要用到git add <file name> 命令给特定文件进行跟踪;跟踪状态可以通过git status命令,查看暂存区里面的信息;提交代码则是用git commit -m "your commit message",提交完成以后当前所在的分支将会新增了一个commit节点a,而master主分支上是没有这此commit的。 接下来当功能A已经完成并通过了测试,符合并入master主分支的要求了,开始进行分支合并: 切换回master主分支 ,使用git checkout master 使用git merge <name>命令合并指定分支到当前分支。 git merge feature-A 使用git status 查看暂存区状态,看看是否有因为冲突而导致无法提交的文件,如果没有,可以进行下一步 删除分支 git branch -d <name> 使用git merge进行的是指定分支与当前分支合并,如果遇到了冲突的话,git依旧会进行合并但不会提交,而是需要等你去处理冲突,使用git status查看冲突文件,冲突文件内已经被git加入了标记提醒你冲突的内容,解决方法无非是二选一和亲自手动编辑,记得要把git添加的冲突标记去除。解决以后使用git add <file name>把这些冲突文件标记为已经解决(即保存到暂存区),然后git commit提交这次冲突处理。 以上就是一次基本的git分支的工作流程一般的工作步骤以及使用到的命令。]]></content>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[初步了解Express(基于node.js的后端框架)]]></title>
<url>%2F2018%2F05%2F10%2F%E5%88%9D%E6%AD%A5%E4%BA%86%E8%A7%A3Express(%E5%9F%BA%E4%BA%8Enode.js%E7%9A%84%E5%90%8E%E7%AB%AF%E6%A1%86%E6%9E%B6)%2F</url>
<content type="text"><![CDATA[Express:基于node.js的后端框架。介绍 实现一个HELLO WORLD使用步骤:1.mkdir app , cd app npm init npm i express -s hello world123456789101112131415var express = require('express');//引入exrpessvar app = express();//创建实例//路由。当url匹配这里的路由,就交由后面的函数处理app.get('/', function (req, res) { res.send('Hello World!');});//创建servervar server = app.listen(3000, function () { var host = server.address().address; var port = server.address().port; console.log('Example app listening at http://%s:%s', host, port);}); 咋看上去跟使用node.js创建一个静态server有点像呢?emm因为express是基于node.js平台。上面本质上其实就是创建了一个本地server,监听3000端口。server获取的url的后缀,进行路由匹配,然后交给对应的函数进行处理。 Express应用生成器了解一下?上面的服务器看上去好像很单薄呀,如果我要实现登录注册等等岂不是要自己写一大堆?Express应用生成器(express-generator)了解一下 npm i express-generator -s 介绍 命令范例:Usage: express [options] [dir] express --ejs .(在当前目录下,创建脚手架,选择模板为.ejs) [图片上传失败…(image-568d2c-1525946563984)]这时候我们的目录下新增很多文件,检查一下package.json,发现我们新依赖了很多东西,先安装好各种依赖。npm i 执行命令启动服务器node bin/www,默认端口是3000,如果你想修改端口的话可以在命令前面指定端口PORT=XXXX 此时目录结构已经比刚刚的完善很多了。 了解一下app.js? bin/www 实际上是用来搭建server,监听端口。当收到请求的时候,则交给app.js来处理。 app.js 实际上是网站的主体,里面包括了这个网站依赖的内容、处理请求的逻辑和执行过程。app.js里面的app实际上就是一个express的一个实例。 设置视图引擎为ejs 根据业务逻辑设置中间件处理请求的流程。 设置静态资源目录,当请求匹配静态资源目录下的路径的时候,则会返回静态资源内容。(例如当请求xxx.com/css/style.css的时候,恰好我们的public目录下有css文件夹,里面存放着style.css。服务器就会返回这个文件) 根据请求后缀匹配不同的路由,而路由的处理需要我们提前require到app.js里面。 假如上述的各个路由都没有匹配上,我们就要返回404,你所请求的东西不存在。 什么是中间件?这里我们参考一下中文文档 中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数。下一个中间件函数通常由名为 next 的变量来表示。 中间件函数可以执行以下任务: 执行任何代码。 对请求和响应对象进行更改。 结束请求/响应循环。 调用堆栈中的下一个中间件函数。 如果当前中间件函数没有结束请求/响应循环,那么它必须调用 next(),以将控制权传递给下一个中间件函数。否则,请求将保持挂起状态。 了解一下Router方法?router方法本质上是一个匹配请求的函数,一般理解为Express的路由层中间件。当匹配给router方法的时候,先处理请求,返回响应数据给模板引擎进行渲染。同时我们看到里面还有一个参数next,这是是中间件的回电函数,如果他带有参数,则表示抛出一个错误,参数为错误的文本,例如: 123function Middleware(request, response, next) { next('出错了!');} 抛出错误以后,后面的中间件将不再执行,直到发现一个错误处理函数为止。如果没有调用next方法,后面注册的函数也是不会执行的。 使用模板引擎我们在一开始利用express-generator生成框架的时候,输入了ejs,这个ejs就是我指定了的模板引擎。通过模板引擎,我们不再把html里面的内容写死,而是通过router动态地把数据传给模板引擎,让它根据我们传入的数据动态地渲染页面,甚至我们可以通过判断语句来给不同的登录状态的用户渲染页面,通过循环语句给页面添加内容等等,上面的router图片中的1res.render('index', {title: 'Express'}) 就是我们给模板引擎传给参数,选择index模板,传入一个对象,key为title,value是一个字符串。然后我们只需要在views中的index.ejs里面,使用相应的语法即可动态渲染。]]></content>
<categories>
<category>后端 - Node.js</category>
</categories>
<tags>
<tag>JavaScript</tag>
<tag>Node.js</tag>
</tags>
</entry>
</search>