diff --git a/README.md b/README.md
index 8e5becc..1c7180e 100644
--- a/README.md
+++ b/README.md
@@ -10,37 +10,24 @@
👉 Java学习资源汇总(个人总结)
-- Java基础到Java实战全套学习视频教程,包括多个企业级实战项目:https://github.com/hello-go-maker/cs-learn-source
+- **Java基础到Java实战全套学习视频教程,包括多个企业级实战项目**
-- 面试算法资料,这是总结的算法资料,学完基本可以应付80%大厂:https://urlify.cn/N7vIj2 密码: ijoi
+- **面试算法资料,这是总结的算法资料,学完基本可以应付80%大厂**
-- 大厂面试资料,一年时间总结,覆盖Java所有技术点:https://urlify.cn/Vzmeqy 密码: j9t2
+- **大厂面试资料,一年时间总结,覆盖Java所有技术点**
-- 面试思维导图,手打总结: https://urlify.cn/vUNF7z 密码: adbo
+- **面试思维导图,手打总结**
-👉 Java各种电子书:如果你需要各种电子书,可以移步这个仓库 [Java电子书合集](https://github.com/hello-go-maker/cs-books)
+👉 **Java各种电子书:各种技术相关的电子书**
-👉 Java面试思维导图(手打)
+👉 **Java面试思维导图(手打)**,我靠这些导图拿到了一线互联网公司的offer,关注公众号,回复:`思维导图`;
-👉 这里再分享一些我总结的**Java面试思维导图**,我靠这些导图拿到了一线互联网公司的offer,预览在下方,先来瞧瞧。
-
+**划重点**:获取上面的资源,请关注我的公众号 `程序员的技术圈子`,**微信扫描下面二维码**,回复:`Java资料`,获取思维导图,绿色通道关注福利,等你拿。
+
-**划重点**:更多`Java面试思维导图`,请关注我的公众号 **程序员的技术圈子**,`微信扫描下面二维码`,回复:**思维导图**,获取思维导图,绿色通道关注福利,等你拿。
-
-
-
-
-
-
-
-
-
-
-[](https://github.com/OUYANGSIHAI/JavaInterview#%E8%81%94%E7%B3%BB%E6%88%91) [](https://github.com/OUYANGSIHAI/JavaInterview#%E5%85%AC%E4%BC%97%E5%8F%B7) [](https://juejin.im/user/5a672822f265da3e55380f0b) [](https://blog.csdn.net/sihai12345) [](https://space.bilibili.com/441147490)
-
### 目录(ctrl + f 查找更香:不能点击的,还在写)
@@ -155,51 +142,52 @@
#### 基础容器
-- ArrayList源码分析及真实大厂面试题精讲
-- LinkedList源码分析及真实大厂面试题精讲
-- HashMap源码分析及真实大厂面试题精讲
+- [ArrayList源码分析及真实大厂面试题精讲](https://blog.csdn.net/sihai12345/article/details/138413307?spm=1001.2014.3001.5501)
+- [LinkedList源码分析及真实大厂面试题精讲](https://blog.csdn.net/sihai12345/article/details/138413722?spm=1001.2014.3001.5501)
+- [HashMap源码分析及真实大厂面试题精讲](https://blog.csdn.net/sihai12345/article/details/138416578?spm=1001.2014.3001.5501)
- TreeMap源码分析及真实大厂面试题精讲
- TreeSet源码分析及真实大厂面试题精讲
- LinkedHashMap源码分析及真实大厂面试题精讲
#### 阻塞容器
-- ConcurrentHashMap源码分析及真实大厂面试题精讲
+- [ConcurrentHashMap源码分析及真实大厂面试题精讲](https://blog.csdn.net/sihai12345/article/details/138420403)
- ArrayBlockingQueue源码分析及真实大厂面试题精讲
- LinkedBlockingQueue源码分析及真实大厂面试题精讲
- PriorityBlockingQueue源码分析及真实大厂面试题精讲
### 并发
-- Synchronized关键字精讲及真实大厂面试题解析
-- Volitale关键字精讲及真实大厂面试题解析
+- [Synchronized关键字精讲及真实大厂面试题解析](https://blog.csdn.net/sihai12345/article/details/138420474)
+- [Volitale关键字精讲及真实大厂面试题解析](https://blog.csdn.net/sihai12345/article/details/138420521)
- 关于LRU的实现
-- ThreadLocal面试中会怎么提问呢?
-- 线程池的面试题,这篇文章帮你搞定它!
+- [ThreadLocal面试中会怎么提问呢?](https://blog.csdn.net/sihai12345/article/details/138420558)
+- [线程池的面试题,这篇文章帮你搞定它!](https://blog.csdn.net/sihai12345/article/details/138420591)
### JVM
- [深入理解Java虚拟机系列](https://mp.weixin.qq.com/s/SZ87s3fmKL3Kc_tAMcOFQw)
- [深入理解Java虚拟机系列--完全解决面试问题](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-xi-lie-jiao-cheng.html)
-- [深入理解Java虚拟机-Java内存区域透彻分析](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-java-nei-cun-qu-yu-tou-che-fen-xi.html)
-- [深入理解Java虚拟机-JVM内存分配与回收策略原理,从此告别JVM内存分配文盲](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-jvm-nei-cun-fen-pei-yu-hui-shou-ce-lue-yuan-li-cong-ci-gao-bie-jvm-nei-cun-fen-pei-wen-mang.html)
-- [深入理解Java虚拟机-常用vm参数分析](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-chang-yong-vm-can-shu-fen-xi.html)
-- [深入理解Java虚拟机-如何利用JDK自带的命令行工具监控上百万的高并发的虚拟机性能](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-ru-he-li-yong-jdk-zi-dai-de-ming-ling-xing-gong-ju-jian-kong-shang-bai-wan-de-gao-bing-fa-de-xu-ni-ji-xing-neng.html)
-- [深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-ru-he-li-yong-visualvm-dui-gao-bing-fa-xiang-mu-jin-xing-xing-neng-fen-xi.html)
-- [深入理解Java虚拟机-你了解GC算法原理吗](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-ni-liao-jie-gc-suan-fa-yuan-li-ma.html)
-- [几个面试官常问的垃圾回收器,下次面试就拿这篇文章怼回去!](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-chang-jian-de-la-ji-hui-shou-qi.html)
-- [面试官100%会严刑拷打的 CMS 垃圾回收器,下次面试就拿这篇文章怼回去!](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-cms-la-ji-hui-shou-qi.html)
+- [深入理解Java虚拟机-Java内存区域透彻分析](https://mp.weixin.qq.com/s/WuyxyelaXbU-lg-HVZ95TA)
+- [深入理解Java虚拟机-JVM内存分配与回收策略原理,从此告别JVM内存分配文盲](https://mp.weixin.qq.com/s/IG_zU5xa7y4BB6PVP0Fmow)
+- [深入理解Java虚拟机-常用vm参数分析](https://mp.weixin.qq.com/s/l8fsq07jI0svqBdBGxuOzA)
+- [深入理解Java虚拟机-如何利用JDK自带的命令行工具监控上百万的高并发的虚拟机性能](https://mp.weixin.qq.com/s/wPgA5SDURCAqPsWkZGGX0g)
+- [深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析](https://mp.weixin.qq.com/s/hhA9tI_rYNkJVbF-R45hbA)
+- [深入理解Java虚拟机-你了解GC算法原理吗](https://mp.weixin.qq.com/s/SZ87s3fmKL3Kc_tAMcOFQw)
+- [几个面试官常问的垃圾回收器,下次面试就拿这篇文章怼回去!](https://sihai.blog.csdn.net/article/details/105700527)
+- [面试官100%会严刑拷打的 CMS 垃圾回收器,下次面试就拿这篇文章怼回去!](https://sihai.blog.csdn.net/article/details/105808878)
- [JVM 面试题 87 题详解](https://sihai.blog.csdn.net/article/details/118737581)
### Java8
-- [Java8快速学习教程](https://blog.ouyangsihai.cn/java8-zui-xin-jiao-cheng-bu-yi-yang-de-java8.html)
-- [Java11的最新特性](https://blog.ouyangsihai.cn/java11-zheng-shi-fa-bu-liao-wo-men-gai-zen-me-ban.html)
-- [Java8 之 lambda 表达式、方法引用、函数式接口、默认方式、静态方法](https://blog.ouyangsihai.cn/java8-zhi-lambda-biao-da-shi-fang-fa-yin-yong-han-shu-shi-jie-kou-mo-ren-fang-shi-jing-tai-fang-fa.html)
-- [Java8之Consumer、Supplier、Predicate和Function攻略](https://blog.ouyangsihai.cn/java8-zhi-consumer-supplier-predicate-he-function-gong-lue.html)
-- [Java8 的 Stream 流式操作之王者归来](https://blog.ouyangsihai.cn/java8-de-stream-liu-shi-cao-zuo-zhi-wang-zhe-gui-lai.html)
+- [Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合](https://mp.weixin.qq.com/s/u042M2Sw2glBlevIDVoSXg)
+- [利用Java8新特征,重构传统设计模式,你学会了吗?](https://mp.weixin.qq.com/s/zZ6rWz_t_snYNiNyOtaGiQ)
+- [Java8 之 lambda 表达式、方法引用、函数式接口、默认方式、静态方法](https://mp.weixin.qq.com/s/FdzNWIsEmHVe9Nehxvfa3w)
+- [Java8之Consumer、Supplier、Predicate和Function攻略](https://sihai.blog.csdn.net/article/details/98193777)
+- [Java8 的 Stream 流式操作之王者归来](https://sihai.blog.csdn.net/article/details/100434684)
+- [Java11-17的最新特性](https://mp.weixin.qq.com/s/QPGdNn56mCCDIUS047_1cQ)
## 计算机网络
@@ -215,7 +203,7 @@
## Linux
-- [java工程师linux命令,这篇文章就够了](https://blog.ouyangsihai.cn/java-gong-cheng-shi-linux-ming-ling-zhe-pian-wen-zhang-jiu-gou-liao.html)
+- [java工程师linux命令,这篇文章就够了](https://mp.weixin.qq.com/s/bj28tvF9TwgwrH65OPjXZg)
- [linux常见面试题(基础版)](https://sihai.blog.csdn.net/article/details/118737736)
- [linux高频面试题](docs/operating-system/linux高频面试题.md)
- 常问的几个Linux面试题,通通解决它
@@ -224,9 +212,9 @@
### 数据结构
-- 跳表这种数据结构,你真的清楚吗,面试官可能会问这些问题!
+- [跳表这种数据结构,你真的清楚吗,面试官可能会问这些问题!](https://blog.csdn.net/sihai12345/article/details/138419109)
- 红黑树你了解多少,不会肯定会被面试官怼坏
-- [B树,B+树,你了解多少,面试官问那些问题?](https://blog.ouyangsihai.cn/mian-shi-guan-wen-ni-b-shu-he-b-shu-jiu-ba-zhe-pian-wen-zhang-diu-gei-ta.html)
+- [B树,B+树,你了解多少,面试官问那些问题?](https://segmentfault.com/a/1190000020416577)
- [这篇文章带你彻底理解红黑树](https://sihai.blog.csdn.net/article/details/118738496)
- 二叉树、二叉搜索树、二叉平衡树、红黑树、B树、B+树
@@ -248,14 +236,12 @@
### MySQL
-- [MySQL深入理解教程-解决面试中的各种问题](https://blog.ouyangsihai.cn/mysql-shen-ru-li-jie-jiao-cheng-mysql-de-yi-zhu-shi-jie.html)
-- [InnoDB与MyISAM等存储引擎对比](https://blog.ouyangsihai.cn/innodb-yu-myisam-deng-cun-chu-yin-qing-dui-bi.html)
-- [ 面试官问你B树和B+树,就把这篇文章丢给他](https://blog.ouyangsihai.cn/mian-shi-guan-wen-ni-b-shu-he-b-shu-jiu-ba-zhe-pian-wen-zhang-diu-gei-ta.html)
-- [MySQL的B+树索引的概念、使用、优化及使用场景](https://blog.ouyangsihai.cn/mysql-de-b-shu-suo-yin.html)
-- [ MySQL全文索引最强教程](https://blog.ouyangsihai.cn/mysql-quan-wen-suo-yin.html)
-- [ MySQL的又一神器-锁,MySQL面试必备](https://blog.ouyangsihai.cn/mysql-de-you-yi-shen-qi-suo.html)
-- [ MySQL事务,这篇文章就够了](https://blog.ouyangsihai.cn/mysql-shi-wu-zhe-pian-wen-zhang-jiu-gou-liao.html)
-- [ mysqldump工具命令参数大全](https://blog.ouyangsihai.cn/mysqldump-gong-ju-ming-ling-can-shu-da-quan.html)
+- [InnoDB与MyISAM等存储引擎对比](https://sihai.blog.csdn.net/article/details/100832158)
+- [MySQL:从B树到B+树到索引再到存储引擎](https://mp.weixin.qq.com/s/QmG1FyWPp23klTVkTJvcUQ)
+- [MySQL全文索引最强教程](https://blog.ouyangsihai.cn/mysql-quan-wen-suo-yin.html)
+- [MySQL的又一神器-锁,MySQL面试必备](https://sihai.blog.csdn.net/article/details/102680104)
+- [MySQL事务,这篇文章就够了](https://sihai.blog.csdn.net/article/details/102815801)
+- [mysqldump工具命令参数大全](https://blog.ouyangsihai.cn/mysqldump-gong-ju-ming-ling-can-shu-da-quan.html)
- [看完这篇MySQL备份的文章,再也不用担心删库跑路了](https://blog.ouyangsihai.cn/kan-wan-zhe-pian-mysql-bei-fen-de-wen-zhang-zai-ye-bu-yong-dan-xin-shan-ku-pao-lu-liao.html)
- 关于MySQL索引,面试中面试官会怎么为难你,一定得注意
- MySQL中的乐观锁、悲观锁,JDK中的乐观锁、悲观锁?
@@ -264,7 +250,8 @@
- [MySQL高频面试题](https://mp.weixin.qq.com/s/KFCkvfF84l6Eu43CH_TmXA)
- [MySQL查询优化过程](https://mp.weixin.qq.com/s/jtuLb8uAIHJNvNpwcIZfpA)
-- MySQL面试官会怎么死怼你呢,我告诉你回怼他
+- [面试官:MySQL 上亿大表,如何深度优化?](https://mp.weixin.qq.com/s/g-_Oz9CLJfBn_asJrzn6Yg)
+- [老司机总结的12条 SQL 优化方案(非常实用)](https://mp.weixin.qq.com/s/7QuASKTpXOm54CgLiHqEJg)
## 系统设计
@@ -295,14 +282,14 @@
#### SpringBoot
-- [springboot史上最全教程,11篇文章全解析](https://blog.ouyangsihai.cn/categories/springboot2-0%E6%9C%80%E6%96%B0%E6%95%99%E7%A8%8B/)
+- [springboot史上最全教程,11篇文章全解析](https://blog.csdn.net/sihai12345/category_7779682.html)
- [微服务面试相关资料](docs/microservice/微服务相关资料.md)
## 分布式
### dubbo
-- [dubbo入门实战教程,这篇文章真的再好不过了](https://blog.ouyangsihai.cn/dubbo-yi-pian-wen-zhang-jiu-gou-liao-dubbo-yu-dao-chu-lian.html)
+- [dubbo入门实战教程,这篇文章真的再好不过了](https://segmentfault.com/a/1190000019896723)
- [dubbo源码分析](http://cmsblogs.com/?p=5324)
- [dubbo面试题](https://mp.weixin.qq.com/s/PdWRHgm83XwPYP08KnkIsw)
- [dubbo面试题2](https://mp.weixin.qq.com/s/Kz0s9K3J9Lpvh37oP_CtCA)
@@ -310,13 +297,7 @@
### zookeeper
- [什么是zookeeper?](https://mp.weixin.qq.com/s/i2_c4A0146B7Ev8QnofbfQ)
-
-- [Zookeeper教程](http://cmsblogs.com/?p=4139)
-
-- [zookeeper源码分析](http://cmsblogs.com/?p=4190)
-
- [zookeeeper面试题](https://segmentfault.com/a/1190000014479433)
-
- [zookeeper面试题2](https://juejin.im/post/5dbac7a0f265da4d2c5e9b3b)
@@ -324,7 +305,6 @@
- [RocketMQ简单教程](https://juejin.im/post/5af02571f265da0b9e64fcfd)
- [RocketMQ教程](https://mp.weixin.qq.com/s/VAZaU1DuKbpnaALjp_-9Qw)
-- [RocketMQ源码分析](http://cmsblogs.com/?p=3236)
- [RocketMQ面试题](https://blog.csdn.net/dingshuo168/article/details/102970988)
### RabbitMQ
@@ -342,8 +322,6 @@
- [kafka面试题](https://blog.csdn.net/qq_28900249/article/details/90346599)
- [kafka面试题2](http://trumandu.github.io/2019/04/13/Kafka%E9%9D%A2%E8%AF%95%E9%A2%98%E4%B8%8E%E7%AD%94%E6%A1%88%E5%85%A8%E5%A5%97%E6%95%B4%E7%90%86/)
-- [分布式架构文章](https://blog.ouyangsihai.cn/fen-bu-shi-jia-gou-xi-lie-wen-zhang.html)
-
### 消息中间件
- [消息中间件面试题总结](docs/project/消息中间件面试题.md)
@@ -353,7 +331,6 @@
- [Redis设计与实现总结文章](https://blog.csdn.net/qq_41594698/category_9067680.html)
- [Redis面试题必备:基础,面试题](https://mp.weixin.qq.com/s/3Fmv7h5p2QDtLxc9n1dp5A)
- [Redis面试相关:其中包含redis知识](https://blog.csdn.net/qq_35190492/article/details/103105780)
-- [Redis源码分析](http://cmsblogs.com/?p=4570)
- [redis其他数据结构](https://blog.csdn.net/c_royi/article/details/82011208)
### 分布式系统
@@ -362,8 +339,8 @@
- [垃圾收集器ZGC](https://juejin.im/post/5dc361d3f265da4d1f51c670)
- [jvm系列文章](https://crowhawk.github.io/tags/#JVM)
- [一次JVM FullGC的背后,竟隐藏着惊心动魄的线上生产事故!](https://mp.weixin.qq.com/s/5SeGxKtwp6KZhUKn8jXi6A)
-- [Java虚拟机调优文章](https://blog.ouyangsihai.cn/categories/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Java%E8%99%9A%E6%8B%9F%E6%9C%BA/)
-- [利用VisualVM对高并发项目进行性能分析](https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-ru-he-li-yong-visualvm-dui-gao-bing-fa-xiang-mu-jin-xing-xing-neng-fen-xi.html#toc-heading-8)
+- [深入理解Java虚拟机-如何利用JDK自带的命令行工具监控上百万的高并发的虚拟机性能](https://mp.weixin.qq.com/s/wPgA5SDURCAqPsWkZGGX0g)
+- [深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析](https://mp.weixin.qq.com/s/hhA9tI_rYNkJVbF-R45hbA)
- [JVM性能调优](https://www.iteye.com/blog/uule-2114697)
- [百亿吞吐量服务的JVM性能调优实战](https://mp.weixin.qq.com/s?__biz=MzIwMzY1OTU1NQ==&mid=2247484236&idx=1&sn=b9743b2d7436f84e4617ff34e07abdd8&chksm=96cd4300a1baca1635a137294bc93c518c033ce01f843c9e012a1454b9f3ea3158fa1412e9da&scene=27&ascene=0&devicetype=android-24&version=26060638&nettype=WIFI&abtest_cookie=BAABAAoACwASABMABAAjlx4AUJkeAFmZHgBomR4AAAA%3D&lang=zh_CN&pass_ticket=%2F%2BLqr9N2EZtrEGLFo9vLA6Eqs89DSJ2CBKoAJFZ%2BBngphEP28dwmMQeSZcUB77qZ&wx_header=1)
- [一次线上JVM调优实践,FullGC40次/天到10天一次的优化过程](https://blog.csdn.net/cml_blog/article/details/81057966)
@@ -377,7 +354,7 @@
### Git
-- [实际开发中的git命令大全](https://blog.ouyangsihai.cn/wo-zai-shi-ji-gong-zuo-zhong-yong-de-zui-duo-de-git-ming-ling.html)
+- [实际开发中的git命令大全](https://sihai.blog.csdn.net/article/details/106418135)
### Docker
@@ -398,7 +375,7 @@
## Java书籍推荐
-- [从入门到拿大厂offer,必须看的数据结构与算法书籍推荐](https://blog.ouyangsihai.cn/cong-ru-men-dao-na-da-han-offer-bi-xu-kan-de-suan-fa-shu-ji-tui-jian-bu-hao-bu-tui-jian.html)
+- [从入门到拿大厂offer,必须看的数据结构与算法书籍推荐](https://blog.csdn.net/sihai12345/article/details/106011624)
- [全网最全电子书下载](https://github.com/hello-go-maker/cs-books)
## 实战项目推荐
@@ -410,12 +387,10 @@
## 程序人生
-- [我想是时候跟大学告别了](https://blog.ouyangsihai.cn/wo-xiang-shi-shi-hou-he-da-xue-gao-bie-liao.html)
-- [坚持,这两个字非常重要!](https://blog.ouyangsihai.cn/jian-chi-zhe-liang-ge-zi-fei-chang-chong-yao.html)
-- [2018年年终总结,你的呢?](https://blog.ouyangsihai.cn/zhe-shi-wo-de-2018-nian-zhong-zong-jie-ni-de-ni.html)
-- [多去了解了解自己](https://blog.ouyangsihai.cn/duo-wen-wen-zi-ji-xiang-cheng-wei-shi-me-yang-de-ren.html)
-- [关于考研,这是我给大家的经验](https://blog.ouyangsihai.cn/guan-yu-zhe-jian-shi-wo-you-hua-yao-shuo.html)
-- [从普通二本到研究生再到自媒体的年轻人,这是我的故事](https://blog.ouyangsihai.cn/cong-pu-ben-dao-zha-shuo-cong-da-xue-sheng-dao-zi-mei-ti-de-nian-qing-ren-wo-fen-xiang-wo-de-coding-sheng-huo.html)
+- [我想是时候跟大学告别了](https://blog.csdn.net/sihai12345/article/details/86934341)
+- [坚持,这两个字非常重要!](https://blog.csdn.net/sihai12345/article/details/89507366)
+- [关于考研,这是我给大家的经验](https://blog.csdn.net/sihai12345/article/details/88548630)
+- [从普通二本到研究生再到自媒体的年轻人,这是我的故事](https://segmentfault.com/a/1190000020317748)
## 说明
@@ -445,15 +420,12 @@
添加我的微信备注 **github**, 即可入群。
-
-
+
### 公众号
如果大家想要实时关注我更新的文章以及分享的干货的话,关注我的公众号 **程序员的技术圈子**。
-
-
-
+
diff --git a/assets/wx.jpg b/assets/wx.jpg
new file mode 100644
index 0000000..3fd7f11
Binary files /dev/null and b/assets/wx.jpg differ
diff --git "a/assets/347円250円213円345円272円217円345円221円230円346円212円200円346円234円257円345円234円210円345円255円220円.jpg" "b/assets/347円250円213円345円272円217円345円221円230円346円212円200円346円234円257円345円234円210円345円255円220円.jpg"
new file mode 100644
index 0000000..8561507
Binary files /dev/null and "b/assets/347円250円213円345円272円217円345円221円230円346円212円200円346円234円257円345円234円210円345円255円220円.jpg" differ
diff --git "a/docs/dataStructures-algorithms/351円253円230円351円242円221円347円256円227円346円263円225円351円242円230円347円233円256円346円200円273円347円273円223円.md" "b/docs/dataStructures-algorithms/351円253円230円351円242円221円347円256円227円346円263円225円351円242円230円347円233円256円346円200円273円347円273円223円.md"
index 1d1c4bc..3c616a3 100644
--- "a/docs/dataStructures-algorithms/351円253円230円351円242円221円347円256円227円346円263円225円351円242円230円347円233円256円346円200円273円347円273円223円.md"
+++ "b/docs/dataStructures-algorithms/351円253円230円351円242円221円347円256円227円346円263円225円351円242円230円347円233円256円346円200円273円347円273円223円.md"
@@ -5,68 +5,445 @@ Ctrl+Shift+P(MacOS:cmd+shift+p)呼出命令面板,输入Markdown Preview
-- [翻转链表](#翻转链表)
-- [实现二叉树先序,中序和后序遍历](#实现二叉树先序中序和后序遍历)
-- [设计LRU缓存结构](#设计lru缓存结构)
-- [两个链表的第一个公共结点](#两个链表的第一个公共结点)
-- [求平方根](#求平方根)
-- [寻找第K大](#寻找第k大)
-- [判断链表中是否有环](#判断链表中是否有环)
-- [合并有序链表](#合并有序链表)
-- [合并k个已排序的链表](#合并k个已排序的链表)
-- [数组中相加和为0的三元组](#数组中相加和为0的三元组)
-- [删除链表的倒数第n个节点](#删除链表的倒数第n个节点)
-- [二分查找](#二分查找)
-- [两个链表生成相加链表](#两个链表生成相加链表)
-- [二叉树的之字形层序遍历](#二叉树的之字形层序遍历)
-- [链表内指定区间反转](#链表内指定区间反转)
-- [二叉树的镜像](#二叉树的镜像)
-- [数组中只出现一次的数字](#数组中只出现一次的数字)
-- [最长的括号子串](#最长的括号子串)
-- [把二叉树打印成多行](#把二叉树打印成多行)
-- [合并两个有序的数组](#合并两个有序的数组)
-- [二叉树的最大路径和](#二叉树的最大路径和)
-- [买卖股票的最佳时机](#买卖股票的最佳时机)
-- [二叉树中是否存在节点和为指定值的路径](#二叉树中是否存在节点和为指定值的路径)
-- [设计getMin功能的栈](#设计getmin功能的栈)
-- [LFU缓存结构设计](#lfu缓存结构设计)
-- [N皇后问题](#n皇后问题)
-- [带权值的最小路径和](#带权值的最小路径和)
-- [反转数字](#反转数字)
-- [二叉搜索树的第k个结点](#二叉搜索树的第k个结点)
-- [子数组最大乘积](#子数组最大乘积)
-- [最长递增子序列](#最长递增子序列)
-- [在两个长度相等的排序数组中找到上中位数](#在两个长度相等的排序数组中找到上中位数)
-- [判断t1树中是否有与t2树拓扑结构完全相同的子树](#判断t1树中是否有与t2树拓扑结构完全相同的子树)
-- [反转字符串](#反转字符串)
-- [最大正方形](#最大正方形)
-- [链表中的节点每K个一组翻转](#链表中的节点每k个一组翻转)
-- [数组中的最长无重复子串的长度](#数组中的最长无重复子串的长度)
-- [判断链表是否为回文结构](#判断链表是否为回文结构)
-- [岛屿的数量](#岛屿的数量)
-- [在二叉树中找到两个节点的最近公共祖先](#在二叉树中找到两个节点的最近公共祖先)
-- [重复项数字的所有排列](#重复项数字的所有排列)
-- [最长回文子串的长度](#最长回文子串的长度)
-- [最长公共子序列](#最长公共子序列)
-- [最小编辑代价](#最小编辑代价)
-- [矩阵的最小路径和](#矩阵的最小路径和)
-- [顺时针旋转数组](#顺时针旋转数组)
-- [判断一棵树是否是搜索二叉树和完全二叉树](#判断一棵树是否是搜索二叉树和完全二叉树)
-- [连续子数组的最大和(sum < 0置为0)](#连续子数组的最大和sum-0置为0httpswwwnowcodercompractice459bd355da1549fa8a49e350bf3df484tpid13tqid11183rp1rutacoding-interviewsqrutacoding-interviewsquestion-ranking) -- [两数之和](#两数之和) -- [删除有序链表中重复出现的元素](#删除有序链表中重复出现的元素) -- [在转动过的有序数组中寻找目标值](#在转动过的有序数组中寻找目标值) -- [数组中未出现的最小正整数](#数组中未出现的最小正整数) -- [数组中最长连续子序列](#数组中最长连续子序列) -- [判断二叉树是否对称](#判断二叉树是否对称) -- [没有重复项数字的所有排列](#没有重复项数字的所有排列) -- [集合的所有子集](#集合的所有子集) +- [Java API整理](#java-api整理) +- [Go API整理](#go-api整理) +- [链表](#链表) + - [排序](#排序) + - [翻转链表, NC78](#翻转链表-nc78) + - [LFU缓存结构设计](#lfu缓存结构设计) + - [设计LRU缓存结构, NC93](#设计lru缓存结构-nc93) + - [合并有序链表, NC33](#合并有序链表-nc33) + - [链表中的节点每K个一组翻转](#链表中的节点每k个一组翻转) + - [判断链表中是否有环](#判断链表中是否有环) + - [链表中环的入口结点](#链表中环的入口结点) + - [删除链表的倒数第n个节点](#删除链表的倒数第n个节点) + - [两个链表的第一个公共结点](#两个链表的第一个公共结点) + - [两个链表生成相加链表](#两个链表生成相加链表) + - [合并k个已排序的链表](#合并k个已排序的链表) + - [单链表的排序,NC70](#单链表的排序nc70) + - [判断链表是否为回文结构](#判断链表是否为回文结构) + - [链表内指定区间反转](#链表内指定区间反转) + - [删除有序链表中重复出现的元素](#删除有序链表中重复出现的元素) + - [环形链表的约瑟夫问题](#环形链表的约瑟夫问题) + - [链表的奇偶重排](#链表的奇偶重排) + - [重排链表(1->n->2->n-1)](#重排链表1-n-2-n-1)
+ - [二叉搜索树与双向链表](#二叉搜索树与双向链表)
+- [队列、栈](#队列栈)
+ - [用两个栈实现队列](#用两个栈实现队列)
+ - [有效括号序列](#有效括号序列)
+ - [包含 min 函数的栈](#包含-min-函数的栈)
+ - [表达式求值](#表达式求值)
+ - [最长括号子串](#最长括号子串)
+ - [括号生成](#括号生成)
+- [二叉树](#二叉树)
+ - [实现二叉树先序,中序和后序遍历](#实现二叉树先序中序和后序遍历)
+ - [二叉树的层序遍历](#二叉树的层序遍历)
+ - [二叉树的之字形层序遍历](#二叉树的之字形层序遍历)
+ - [在二叉树中找到两个节点的最近公共祖先](#在二叉树中找到两个节点的最近公共祖先)
+ - [重建二叉树](#重建二叉树)
+ - [输出二叉树的右视图(先重建,再输出右视图)](#输出二叉树的右视图先重建再输出右视图)
+ - [二叉树的最大深度](#二叉树的最大深度)
+ - [判断是不是平衡二叉树](#判断是不是平衡二叉树)
+ - [二叉树根节点到叶子节点的所有路径和](#二叉树根节点到叶子节点的所有路径和)
+ - [二叉树中和为某一值的路径,返回所有路径](#二叉树中和为某一值的路径返回所有路径)
+ - [判断一棵二叉树是否为搜索二叉树和完全二叉树](#判断一棵二叉树是否为搜索二叉树和完全二叉树)
+ - [二叉树的最大路径和](#二叉树的最大路径和)
+ - [判断二叉树是否对称](#判断二叉树是否对称)
+ - [二叉树中是否存在节点和为指定值的路径](#二叉树中是否存在节点和为指定值的路径)
+ - [序列化二叉树](#序列化二叉树)
+ - [二叉搜索树的第k个结点](#二叉搜索树的第k个结点)
+ - [把二叉树打印成多行](#把二叉树打印成多行)
+ - [二叉树的镜像](#二叉树的镜像)
+ - [判断t1树中是否有与t2树拓扑结构完全相同的子树](#判断t1树中是否有与t2树拓扑结构完全相同的子树)
+ - [合并二叉树](#合并二叉树)
+ - [字典树的实现](#字典树的实现)
+ - [找到二叉搜索树中的两个错误节点](#找到二叉搜索树中的两个错误节点)
+- [堆](#堆)
+ - [最小的K个数](#最小的k个数)
+ - [字符串出现次数的TopK问题](#字符串出现次数的topk问题)
+ - [寻找第K大](#寻找第k大)
+- [双指针](#双指针)
+ - [最长无重复子数组的长度](#最长无重复子数组的长度)
+ - [滑动窗口的最大值](#滑动窗口的最大值)
+ - [合并区间(区间重叠)](#合并区间区间重叠)
+ - [反转字符串](#反转字符串)
+ - [数组中相加和为0的三元组](#数组中相加和为0的三元组)
+ - [接雨水问题](#接雨水问题)
+ - [最小覆盖子串(T包含S的最小子串)](#最小覆盖子串t包含s的最小子串)
+ - [两数之和](#两数之和)
+ - [最长重复子串(连续两个相同的字符串)](#最长重复子串连续两个相同的字符串)
+- [动态规划](#动态规划)
+ - [跳台阶](#跳台阶)
+ - [连续子数组的最大和(sum < 0置为0)](#连续子数组的最大和sum--0置为0) + - [最长公共子串(返回具体字符串/长度)](#最长公共子串返回具体字符串长度) + - [斐波那契数列](#斐波那契数列) + - [最长回文子串的长度](#最长回文子串的长度) + - [最长递增子序列](#最长递增子序列) + - [买卖股票的最佳时机](#买卖股票的最佳时机) + - [矩阵的最小路径和](#矩阵的最小路径和) + - [编辑距离](#编辑距离) + - [不同路径的数目](#不同路径的数目) + - [最长公共子序列](#最长公共子序列) + - [最长的括号子串](#最长的括号子串) + - [高空扔鸡蛋](#高空扔鸡蛋) + - [兑换零钱](#兑换零钱) + - [最大正方形](#最大正方形) + - [通配符匹配](#通配符匹配) + - [正则表达式匹配](#正则表达式匹配) + - [矩阵最长递增路径](#矩阵最长递增路径) + - [最长上升子序列](#最长上升子序列) + - [目标和(完全背包)](#目标和完全背包) + - [打家劫舍](#打家劫舍) + - [带权值的最小路径和](#带权值的最小路径和) + - [最长不含重复字符的子字符串](#最长不含重复字符的子字符串) + - [把数字翻译成字符串](#把数字翻译成字符串) +- [二分](#二分) + - [求平方根](#求平方根) + - [在旋转过的有序数组中寻找目标值](#在旋转过的有序数组中寻找目标值) + - [在两个长度相等的排序数组中找到上中位数](#在两个长度相等的排序数组中找到上中位数) + - [有序矩阵元素查找](#有序矩阵元素查找) + - [二分查找](#二分查找) + - [旋转数组的最小数字](#旋转数组的最小数字) + - [数字在升序数组中出现的次数](#数字在升序数组中出现的次数) + - [峰值](#峰值) +- [数组](#数组) + - [数组中只出现一次的数字](#数组中只出现一次的数字) + - [合并两个有序的数组](#合并两个有序的数组) + - [子数组最大乘积](#子数组最大乘积) + - [数组中最长连续子序列](#数组中最长连续子序列) + - [数组中未出现的最小正整数](#数组中未出现的最小正整数) + - [顺时针旋转数组](#顺时针旋转数组) + - [旋转数组](#旋转数组) + - [逆序对](#逆序对) + - [调整数组顺序使奇数位于偶数前面](#调整数组顺序使奇数位于偶数前面) + - [矩阵乘法](#矩阵乘法) +- [回溯](#回溯) + - [字符串的全排列](#字符串的全排列) + - [岛屿的数量](#岛屿的数量) + - [没有重复项数字的所有排列(全排列)](#没有重复项数字的所有排列全排列) + - [集合的所有子集](#集合的所有子集) + - [重复项数字的所有排列](#重复项数字的所有排列) + - [N皇后问题](#n皇后问题) + - [把数组字符串转换为 ip 地址](#把数组字符串转换为-ip-地址) + - [加起来和为目标值的组合](#加起来和为目标值的组合) +- [其他](#其他) + - [螺旋矩阵](#螺旋矩阵) + - [顺时针旋转矩阵](#顺时针旋转矩阵) + - [进制转换](#进制转换) + - [反转数字](#反转数字) + - [大数加法](#大数加法) + - [把字符串转换成整数(atoi)](#把字符串转换成整数atoi) + - [最长公共前缀](#最长公共前缀) + - [回文数字](#回文数字) + - [字符串变形(反序,大写)](#字符串变形反序大写) + - [最大值(数组拼接最大数)](#最大值数组拼接最大数) + - [验证ip地址](#验证ip地址) + - [二进制中1的个数](#二进制中1的个数) + - [第一个只出现一次的字符](#第一个只出现一次的字符) +- [其他编程题(golang、java)](#其他编程题golangjava) + - [单例模式](#单例模式) + - [实现线程安全的生产者消费者](#实现线程安全的生产者消费者) + - [一个10G的文件,里面全部是自然数,一行一个,乱序排列,对其排序。在32位机器上面完成,内存限制为 2G(bitmap原理知道吗?)](#一个10g的文件里面全部是自然数一行一个乱序排列对其排序在32位机器上面完成内存限制为-2gbitmap原理知道吗) + - [实现使用字符串函数名,调用函数](#实现使用字符串函数名调用函数) + - [负载均衡算法。(一致性哈希)](#负载均衡算法一致性哈希) + - [(Goroutine)有三个函数,分别打印"cat", "fish","dog"要求每一个函数都用一个goroutine,按照顺序打印100次](#goroutine有三个函数分别打印cat-fishdog要求每一个函数都用一个goroutine按照顺序打印100次) + - [两个协程交替打印10个字母和数字](#两个协程交替打印10个字母和数字) + - [启动 2个groutine 2秒后取消, 第一个协程1秒执行完,第二个协程3秒执行完。](#启动-2个groutine-2秒后取消-第一个协程1秒执行完第二个协程3秒执行完) + - [当select监控多个chan同时到达就绪态时,如何先执行某个任务?](#当select监控多个chan同时到达就绪态时如何先执行某个任务) +## Java API整理 +- api -### 翻转链表 +https://blog.csdn.net/qq_34756156/article/details/120713595 + +## Go API整理 + +- api + +https://www.pseudoyu.com/zh/2021/05/29/algorithm_data_structure_go/ +https://greyireland.gitbook.io/algorithm-pattern/ru-men-pian/golang + +- 刷题模板 + +https://greyireland.gitbook.io/algorithm-pattern/ + +## 链表 + +### 排序 + +```java +import java.util.*; + + +public class Solution { + + public int[] MySort(int[] arr) { +// 选择排序 +// return selectSort(arr); +// 冒泡排序 +// return bubbleSort(arr); +// 插入排序 +// return insertSort(arr); +// 希尔排序 +// return shellSort(arr); +// 归并排序 +// return mergeSort(arr,0,arr.length-1); +// 快速排序 +// quickSort(arr,0,arr.length-1); +// return arr; +// 计数排序 +// return countSort(arr); +// 基数排序 +// return radixSort(arr); +// 桶排序 + return bucketSort(arr); + } + // 选择排序---选择最小的数与当前数交换 + public int[] selectSort(int[] arr){ + if(arr.length<2)return arr; + for(int i=0;iarr[j])swap(arr,i,j);
+ }
+ }
+ return arr;
+ }
+
+ // 插入排序---与当前位置之前的所有元素比较,交换元素
+ public int[] insertSort(int[] arr){
+ if(arr.length<2)return arr; + for(int i=1;i0;j--){
+ if(arr[j]0;gap=(gap-1)/3){
+ for(int i=gap;i=0;j=j-gap){
+ if(arr[j]= right) return ;
+ int pivot = arr[left];
+ int i = left,j = right;
+ while(i < j){ + while(arr[j]>= pivot && j>i){
+ j--;
+ }
+ while(arr[i] <= pivot && i0){count++;temp = temp/10;}
+ if(count>max)max = count;
+ }
+
+ for(int m=0;m0){temp=temp/10;}
+ int result = temp%10;
+ for(int k=0;k0){
+ arr[k++] = countArr[i][j];
+ }
+ }
+ }
+ return arr;
+ }
+
+ // 桶排序---给定n个桶,找到最大数与最小数,
+ // 计算出每个桶能装的数的范围,将数分别放入符合条件的桶中,
+ // 对每个桶进行快速排序,最后合并
+ public int[] bucketSort(int[] arr){
+ // 设置桶的个数
+ int bucket = 4;
+ // 找到数组中的最大最小值
+ int min = arr[0],max=arr[0];
+ for(int i=0;imax)max=arr[i];
+ if(arr[i]= min && temp < min+range){ + for(int k =0;k= min+range && temp < min+2*range){ + for(int k =0;k= min+2*range && temp < max - range){ + for(int k =0;k= max - range && temp <= max){ + for(int k =0;k list1 = new ArrayList();
- ArrayList list2 = new ArrayList();
- ArrayList list3 = new ArrayList();
- front(root,list1,list2,list3);
- int[][] ints = new int[3][list1.size()];
- for (int i = 0; i < list1.size(); i++) { - ints[0][i] = list1.get(i); - ints[1][i] = list2.get(i); - ints[2][i] = list3.get(i); +class LFUCache { + Map cache; // 存储缓存的内容
+ Map> freqMap; // 存储每个频次对应的双向链表
+ int size;
+ int capacity;
+ int min; // 存储当前最小频次
+
+ public LFUCache(int capacity) {
+ cache = new HashMap (capacity);
+ freqMap = new HashMap();
+ this.capacity = capacity;
+ }
+
+ public int get(int key) {
+ Node node = cache.get(key);
+ if (node == null) {
+ return -1;
}
- return ints;
+ freqInc(node);
+ return node.value;
}
-
- public void front(TreeNode root,ArrayList list1,
- ArrayList list2,ArrayList list3){
- if(root == null){
+
+ public void put(int key, int value) {
+ if (capacity == 0) {
return;
}
-
- list1.add(root.val);
- front(root.left,list1,list2,list3);
- list2.add(root.val);
- front(root.right,list1,list2,list3);
- list3.add(root.val);
+ Node node = cache.get(key);
+ if (node != null) {
+ node.value = value;
+ freqInc(node);
+ } else {
+ if (size == capacity) {
+ Node deadNode = removeNode();
+ cache.remove(deadNode.key);
+ size--;
+ }
+ Node newNode = new Node(key, value);
+ cache.put(key, newNode);
+ addNode(newNode);
+ size++;
+ }
}
-}
-```
-
-- 非递归遍历
-
-- 前序遍历
-
-用栈来保存信息,但是遍历的时候,是:**先输出根节点信息,然后压入右节点信息,然后再压入左节点信息。**
-```java
-public void pre(Node head){
- if(head == null){
- return;
- }
- Stack stack = new Stack();
- stack.push(head);
- while(!stack.isEmpty()){
- head = stack.poll();
- System.out.println(head.value + " ");
- if(head.right != null){
- stack.push(head.right);
+ void freqInc(Node node) {
+ // 从原freq对应的链表里移除, 并更新min
+ int freq = node.freq;
+ LinkedHashSet set = freqMap.get(freq);
+ set.remove(node);
+ if (freq == min && set.size() == 0) {
+ min = freq + 1;
}
- if(head.left != null){
- stack.push(head.left);
+ // 加入新freq对应的链表
+ node.freq++;
+ LinkedHashSet newSet = freqMap.get(freq + 1);
+ if (newSet == null) {
+ newSet = new LinkedHashSet();
+ freqMap.put(freq + 1, newSet);
}
+ newSet.add(node);
}
- System.out.println();
-}
-```
-
-- 中序遍历
-中序遍历的顺序是**左中右**,先一直左节点遍历,并压入栈中,当做节点为空时,输出当前节点,往右节点遍历。
-
-```java
-public void inorder(Node head){
- if(head == null){
- return;
+ void addNode(Node node) {
+ LinkedHashSet set = freqMap.get(1);
+ if (set == null) {
+ set = new LinkedHashSet();
+ freqMap.put(1, set);
+ }
+ set.add(node);
+ min = 1;
}
- Stack stack = new Stack();
- stack.push(head);
- while(!stack.isEmpty() || head != null){
- if(head != null){
- stack.push(head);
- head = head.left
- } else {
- head = stack.poll();
- System.out.println(head.value + " ");
- head = head.right;
- }
+
+ Node removeNode() {
+ LinkedHashSet set = freqMap.get(min);
+ Node deadNode = set.iterator().next();
+ set.remove(deadNode);
+ return deadNode;
}
- System.out.println();
}
-```
-- 后序遍历
+class Node {
+ int key;
+ int value;
+ int freq = 1;
-用两个栈来实现,压入栈1的时候为**先左后右**,栈1弹出来就是**中右左**,栈2收集起来就是**左右中**。
+ public Node() {}
+
+ public Node(int key, int value) {
+ this.key = key;
+ this.value = value;
+ }
+}
+```
+### 设计LRU缓存结构, NC93
-### 设计LRU缓存结构
+- lru-k算法:https://blog.csdn.net/love254443233/article/details/82598381
```java
import java.util.*;
-
-
+
public class Solution {
/**
* lru design
@@ -284,119 +652,151 @@ class LRUCache{
}
```
-### 两个链表的第一个公共结点
+```go
+type LRUCache struct {
+ capacity int
+ m map[int]*Node
+ head, tail *Node
+}
-```java
-/*
-public class ListNode {
- int val;
- ListNode next = null;
-
- ListNode(int val) {
- this.val = val;
+type Node struct {
+ Key int
+ Value int
+ Pre, Next *Node
+}
+
+func (this *LRUCache) Get(key int) int {
+ if v, ok := this.m[key]; ok {
+ this.moveToHead(v)
+ return v.Value
}
-}*/
-public class Solution {
- public ListNode FindFirstCommonNode(ListNode pHead1,
- ListNode pHead2) {
- if(pHead1 == null || pHead2 == null){
- return null;
- }
-
- ListNode p1 = pHead1;
- ListNode p2 = pHead2;
-
- while(p1 != p2){
- p1 = p1.next;
- p2 = p2.next;
- if(p1 != p2){
- if(p1 == null) p1 = pHead2;
- if(p2 == null) p2 = pHead1;
- }
- }
-
- return p1;
-
+ return -1
+}
+
+func (this *LRUCache) moveToHead(node *Node) {
+ this.deleteNode(node)
+ this.addToHead(node)
+}
+
+func (this *LRUCache) deleteNode(node *Node) {
+ node.Pre.Next = node.Next
+ node.Next.Pre = node.Pre
+}
+
+func (this *LRUCache) removeTail() int {
+ node := this.tail.Pre
+ this.deleteNode(node)
+ return node.Key
+}
+
+func (this *LRUCache) addToHead(node *Node) {
+ this.head.Next.Pre = node
+ node.Next = this.head.Next
+ node.Pre = this.head
+ this.head.Next = node
+}
+
+func (this *LRUCache) Put(key int, value int) {
+ if v, ok := this.m[key]; ok {
+ v.Value = value
+ this.moveToHead(v)
+ return
+ }
+
+ if this.capacity == len(this.m) {
+ rmKey := this.removeTail()
+ delete(this.m, rmKey)
+ }
+
+ newNode := &Node{Key: key, Value: value}
+ this.addToHead(newNode)
+ this.m[key] = newNode
+}
+
+func Constructor(capacity int) LRUCache {
+ head, tail := &Node{}, &Node{}
+ head.Next = tail
+ tail.Pre = head
+ return LRUCache{
+ capacity: capacity,
+ m: map[int]*Node{},
+ head: head,
+ tail: tail,
}
}
```
-### 求平方根
+### 合并有序链表, NC33
```java
import java.util.*;
+/*
+ * public class ListNode {
+ * int val;
+ * ListNode next = null;
+ * }
+ */
public class Solution {
/**
*
- * @param x int整型
- * @return int整型
+ * @param l1 ListNode类
+ * @param l2 ListNode类
+ * @return ListNode类
*/
- public int sqrt (int x) {
- // write code here
- if(x < 2){ - return x; - } - int left = 1; - int right = x / 2; - while(left <= right){ - int mid = left + (right - left) / 2; - if(x / mid == mid){ - return mid; - } else if(x / mid < mid){ - right = mid - 1; - } else if(x / mid> mid){
- left = mid + 1;
+ public ListNode mergeTwoLists (ListNode l1, ListNode l2) {
+ ListNode node = new ListNode(0);
+ ListNode res = node;
+ while(l1 != null && l2 != null){
+ if(l1.val> l2.val){
+ node.next = l2;
+ l2 = l2.next;
+ } else {
+ node.next = l1;
+ l1 = l1.next;
}
+ node = node.next;
}
- return right;
+ if(l1 != null){
+ node.next = l1;
+ }
+
+ if(l2 != null){
+ node.next = l2;
+ }
+
+ return res.next;
}
}
```
-### 寻找第K大
+### 链表中的节点每K个一组翻转
```java
-import java.util.*;
-
-public class Finder {
- public int findKth(int[] a, int n, int K) {
- // write code here
- return find(a, 0, n-1, K);
- }
-
- public int find(int[] a, int low, int high, int K){
- int pivot = partition(a, low, high);
-
- if(pivot + 1 < K){ - return find(a, pivot + 1, high, K); - } else if(pivot + 1> K){
- return find(a, low, pivot - 1, K);
- } else {
- return a[pivot];
- }
- }
-
- int partition(int arr[], int startIndex, int endIndex){
- int small = startIndex - 1;
- for (int i = startIndex; i < endIndex; ++i) { - if(arr[i]> arr[endIndex]) {
- swap(arr,++small, i);
- }
- }
- swap(arr,++small,endIndex);
- return small;
+//明显递归解决,翻转第一组之后,以第二组的开头为头节点,继续翻转,转翻到最后,返回。
+public ListNode reverseKGroup(ListNode head, int k) {
+ if(head==null||head.next==null)
+ return head;
+ ListNode h=new ListNode(0);
+ h.next=head;
+ ListNode next=null,tmp=head,cur=head;
+ for(int i=1;i l2.val){
- node.next = l2;
- l2 = l2.next;
- } else {
- node.next = l1;
- l1 = l1.next;
- }
- node = node.next;
+ public ListNode removeNthFromEnd (ListNode head, int n) {
+ // write code here
+ ListNode dummyNode = new ListNode(0);
+ dummyNode.next = head;
+ ListNode fast = dummyNode;
+ ListNode slow = dummyNode;
+ for(int i = 0; i <= n; i++){ + fast = fast.next; } - if(l1 != null){ - node.next = l1; + while(fast != null){ + fast = fast.next; + slow = slow.next; } - if(l2 != null){ - node.next = l2; + slow.next = slow.next.next; + + return dummyNode.next; + } +} +``` + +### 两个链表的第一个公共结点 + +```java +/* +public class ListNode { + int val; + ListNode next = null; + + ListNode(int val) { + this.val = val; + } +}*/ +public class Solution { + public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { + if(pHead1 == null || pHead2 == null){ + return null; } - return res.next; + ListNode p1 = pHead1; + ListNode p2 = pHead2; + + while(p1 != p2){ + p1 = p1.next; + p2 = p2.next; + if(p1 != p2){ + if(p1 == null) p1 = pHead2; + if(p2 == null) p2 = pHead1; + } + } + + return p1; + } } +``` + +### 两个链表生成相加链表 +```java +import java.util.*; + +/* + * public class ListNode { + * int val; + * ListNode next = null; + * } + */ + +public class Solution { + /** + * + * @param head1 ListNode类 + * @param head2 ListNode类 + * @return ListNode类 + */ + public ListNode addInList (ListNode head1, ListNode head2) { + // write code here + if(head1==null) return head2; + if(head2==null) return head1; + ListNode l1=reverse(head1); + ListNode l2=reverse(head2); + ListNode result=new ListNode(0); + int c=0; + while(l1!=null||l2!=null||c!=0) + { + int v1=l1!=null?l1.val:0; + int v2=l2!=null?l2.val:0; + int val=v1+v2+c; + c=val/10; + ListNode cur=new ListNode(val%10); + cur.next=result.next; + result.next=cur; + if(l1!=null) + l1=l1.next; + if(l2!=null) + l2=l2.next; + } + return result.next; + } + + public ListNode reverse(ListNode node) + { + if(node==null) return node; + ListNode pre=null,next=null; + while(node!=null) + { + next=node.next; + node.next=pre; + pre=node; + node=next; + } + return pre; + } +} ``` ### 合并k个已排序的链表 @@ -540,227 +1061,134 @@ public class Solution { } ``` -### 数组中相加和为0的三元组 +### 单链表的排序,NC70 + +- 堆排序 ```java import java.util.*; - -public class Solution { - public ArrayList> threeSum(int[] num) {
- ArrayList> list = new ArrayList();
- Arrays.sort(num);
- int left,right,sum;
- for(int i = 0; i < num.length - 2; i++){ - if(i> 0 && num[i] == num[i-1]) continue;
- left = i + 1;
- right = num.length - 1;
- while(left < right){ - sum = num[i] + num[left] + num[right]; - if(sum == 0){ - ArrayList temp = new ArrayList();
- temp.add(num[i]);
- temp.add(num[left]);
- temp.add(num[right]);
- list.add(temp);
- right--;
- left++;
- while(left < right && num[left] == num[left-1]){ - left++; - } - while(left < right && num[right] == num[right+1]){ - right--; - } - } else if(sum < 0){ - left++; - } else { - right--; - } - } - } - return list; - } -} - -``` - -### 删除链表的倒数第n个节点 - -```java -import java.util.*; - -/* - * public class ListNode { - * int val; - * ListNode next = null; - * } - */ - + public class Solution { /** - * - * @param head ListNode类 - * @param n int整型 + * + * @param head ListNode类 the head node * @return ListNode类 */ - public ListNode removeNthFromEnd (ListNode head, int n) { + public ListNode sortInList (ListNode head) { // write code here - ListNode dummyNode = new ListNode(0); - dummyNode.next = head; - ListNode fast = dummyNode; - ListNode slow = dummyNode; - for(int i = 0; i <= n; i++){ - fast = fast.next; + PriorityQueue heap = new PriorityQueue((n1, n2) -> n1.val - n2.val);
+ while (head != null) {
+ heap.add(head);
+ head = head.next;
}
-
- while(fast != null){
- fast = fast.next;
- slow = slow.next;
+ ListNode dummy = new ListNode(-1);
+ ListNode cur = dummy;
+ while (!heap.isEmpty()) {
+ cur.next = heap.poll();
+ cur = cur.next;
}
-
- slow.next = slow.next.next;
-
- return dummyNode.next;
+ cur.next = null;
+ return dummy.next;
}
}
```
-### 二分查找
+- 归并排序
```java
import java.util.*;
-
-
public class Solution {
- /**
- * 二分查找
- * @param n int整型 数组长度
- * @param v int整型 查找值
- * @param a int整型一维数组 有序数组
- * @return int整型
- */
- public int upper_bound_ (int n, int v, int[] a) {
- // write code here
- int left = 0, right = n;
- while(left < right){ - int mid = left + (right - left) / 2; - if(a[mid] == v){ - right = mid; - } else if(a[mid]> v){
- right = mid;
- } else {
- left = mid + 1;
+ //合并两段有序链表
+ ListNode merge(ListNode pHead1, ListNode pHead2) {
+ //一个已经为空了,直接返回另一个
+ if(pHead1 == null)
+ return pHead2;
+ if(pHead2 == null)
+ return pHead1;
+ //加一个表头
+ ListNode head = new ListNode(0);
+ ListNode cur = head;
+ //两个链表都要不为空
+ while(pHead1 != null && pHead2 != null){
+ //取较小值的节点
+ if(pHead1.val <= pHead2.val){ + cur.next = pHead1; + //只移动取值的指针 + pHead1 = pHead1.next; + }else{ + cur.next = pHead2; + //只移动取值的指针 + pHead2 = pHead2.next; } + //指针后移 + cur = cur.next; } - return left+1; - } -} -``` - -### 两个链表生成相加链表 - -```java -import java.util.*; - -/* - * public class ListNode { - * int val; - * ListNode next = null; - * } - */ - -public class Solution { - /** - * - * @param head1 ListNode类 - * @param head2 ListNode类 - * @return ListNode类 - */ - public ListNode addInList (ListNode head1, ListNode head2) { - // write code here - if(head1==null) return head2; - if(head2==null) return head1; - ListNode l1=reverse(head1); - ListNode l2=reverse(head2); - ListNode result=new ListNode(0); - int c=0; - while(l1!=null||l2!=null||c!=0) - { - int v1=l1!=null?l1.val:0; - int v2=l2!=null?l2.val:0; - int val=v1+v2+c; - c=val/10; - ListNode cur=new ListNode(val%10); - cur.next=result.next; - result.next=cur; - if(l1!=null) - l1=l1.next; - if(l2!=null) - l2=l2.next; - } - return result.next; + //哪个链表还有剩,直接连在后面 + if(pHead1 != null) + cur.next = pHead1; + else + cur.next = pHead2; + //返回值去掉表头 + return head.next; } - - public ListNode reverse(ListNode node) - { - if(node==null) return node; - ListNode pre=null,next=null; - while(node!=null) - { - next=node.next; - node.next=pre; - pre=node; - node=next; + + public ListNode sortInList (ListNode head) { + //链表为空或者只有一个元素,直接就是有序的 + if(head == null || head.next == null) + return head; + ListNode left = head; + ListNode mid = head.next; + ListNode right = head.next.next; + //右边的指针到达末尾时,中间的指针指向该段链表的中间 + while(right != null && right.next != null){ + left = left.next; + mid = mid.next; + right = right.next.next; } - return pre; + //左边指针指向左段的左右一个节点,从这里断开 + left.next = null; + //分成两段排序,合并排好序的两段 + return merge(sortInList(head), sortInList(mid)); } } ``` -### 二叉树的之字形层序遍历 +### 判断链表是否为回文结构 ```java import java.util.*; - -/* - * public class TreeNode { - * int val = 0; - * TreeNode left = null; - * TreeNode right = null; - * } - */ - + public class Solution { /** * - * @param root TreeNode类 - * @return int整型ArrayList>
+ * @param head ListNode类 the head
+ * @return bool布尔型
*/
- public ArrayList> zigzagLevelOrder (TreeNode root) {
- // write code here
- Queue queue = new LinkedList
();
- ArrayList> list = new ArrayList();
- if(root != null) queue.add(root);
- while(!queue.isEmpty()){
- ArrayList temp = new ArrayList();
- for(int i = queue.size(); i> 0; i--){
- TreeNode node = queue.poll();
- temp.add(node.val);
- if(node.left != null){
- queue.add(node.left);
- }
- if(node.right != null){
- queue.add(node.right);
- }
- }
- if(list.size() % 2 == 1){
- Collections.reverse(temp);
+ public boolean isPail (ListNode head) {
+ ListNode slow = head;
+ ListNode fast = head;
+ while(fast != null && fast.next != null){
+ fast = fast.next.next;
+ slow = slow.next;
+ }
+
+ Stack stack = new Stack();
+ while(slow != null){
+ stack.add(slow.val);
+ slow = slow.next;
+ }
+
+ while(!stack.isEmpty()){
+ if(stack.pop() != head.val){
+ return false;
}
- list.add(temp);
+
+ head = head.next;
}
- return list;
+
+ return true;
}
}
-
```
### 链表内指定区间反转
@@ -808,1591 +1236,5650 @@ public class Solution {
return dummy.next;
}
}
-
```
-### 二叉树的镜像
+### 删除有序链表中重复出现的元素
```java
-public class Solution {
- public void Mirror(TreeNode root) {
- if(root == null){
- return;
- }
- if(root.left == null && root.right == null){
- return;
- }
- Stack stack = new Stack
();
- stack.push(root);
- while(!stack.isEmpty()){
- TreeNode node = stack.pop();
-
- if(node.left != null || node.right != null){
- TreeNode temp = node.left;
- node.left = node.right;
- node.right = temp;
- }
-
- if(node.left != null){
- stack.push(node.left);
- }
-
- if(node.right != null){
- stack.push(node.right);
+public ListNode deleteDuplicates (ListNode head) {
+ ListNode dummy=new ListNode(0);
+ dummy.next=head;
+ ListNode pre=dummy;
+ ListNode p=head;
+ while(p!=null&&p.next!=null){
+ if(p.val==p.next.val){
+ while(p.next!=null&&p.val==p.next.val){
+ p=p.next;
}
+ pre.next=p.next;
+ p=p.next;
+ }
+ else{
+ pre=p;
+ p=p.next;
+ }
+ }
+ return dummy.next;
+}
+```
+
+### 环形链表的约瑟夫问题
+
+```java
+public int ysf (int n, int m) {
+ // write code here
+ ListNode head = new ListNode(1) ,p1=head;
+ for(int i=2;i<=n;i++){ + ListNode temp = new ListNode(i); + p1.next=temp; + p1=p1.next; + } + p1.next=head; + while(n-->1){
+ int num=m;
+ while(num-->1){
+ p1=p1.next;
}
+ p1.next=p1.next.next;
}
+ return p1.val;
}
-
+```
+
+### 链表的奇偶重排
+
+```java
+import java.util.*;
+
/*
+ * public class ListNode {
+ * int val;
+ * ListNode next = null;
+ * }
+ */
+
public class Solution {
- public void Mirror(TreeNode root) {
- if(root == null){
- return;
- }
- if(root.left == null && root.right == null){
- return;
- }
-
- TreeNode temp = root.left;
- root.left = root.right;
- root.right = temp;
-
- if(root.left != null){
- Mirror(root.left);
- }
-
- if(root.right != null){
- Mirror(root.right);
- }
- }
-}
-*/
-
-```
-
-### 数组中只出现一次的数字
-
-```java
-public class Solution {
-
- public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
- int num = 0;
- for(int i = 0; i < array.length; i++){ - num^=array[i]; - } - - int count = 0; - // 标志位,记录num中的第一个1出现的位置 - for(;count < array.length; count++){ - if((num&(1< set = new HashSet();
- for(int i = 0; i < array.length; i++){ - if(!set.add(array[i])){ - set.remove(array[i]); - } - } - - Object[] temp = set.toArray(); - num1[0] = (int)temp[0]; - num2[0] = (int)temp[1]; - }*/ } - ``` -### 最长的括号子串 +### 重排链表(1->n->2->n-1)
```java
+import java.util.*;
public class Solution {
- /**
- *
- * @param s string字符串
- * @return int整型
- */
- public int longestValidParentheses (String s) {
- // write code here
- if(s == null || s.length() <= 0){ - return 0; - } - - Stack stack = new Stack();
- int last = -1;
- int maxLen = 0;
- for(int i = 0; i < s.length(); i++){ - if(s.charAt(i) == '('){ - stack.push(i); - } else { - if(stack.isEmpty()){ - last = i; - } else { - stack.pop(); - if(stack.isEmpty()){ - maxLen = Math.max(maxLen, i - last); - } else { - maxLen = Math.max(maxLen, i - stack.peek()); - } - } - } + public void reorderList(ListNode head) { + if (head == null || head.next == null) return; + List list = new ArrayList();
+ ListNode cur = head;
+ while (cur != null) {
+ list.add(cur);
+ cur = cur.next;
}
-
- return maxLen;
- }
-}
-
-// 动态规划
-public int longestValidParentheses2(String s) {
- if (s == null || s.length() == 0)
- return 0;
- int[] dp = new int[s.length()];
- int ans = 0;
- for (int i = 1; i < s.length(); i++) { - // 如果是'('直接跳过,默认为0 - if (s.charAt(i) == ')') { - if (s.charAt(i - 1) == '(') - dp[i] = (i>= 2 ? dp[i - 2] : 0) + 2;
- // 说明s.charAt(i - 1)==')'
- else if (i - dp[i - 1]> 0 &&
- s.charAt(i - dp[i - 1] - 1) == '(') {
- dp[i] = (i - dp[i - 1]> 1 ?
- dp[i - dp[i - 1] - 2] : 0) + dp[i - 1] + 2;
- // 因为加了一个左括号和一个右括号,所以是加2
- }
+ int l = 0, r = list.size() - 1;
+ while (l < r) { + list.get(l).next = list.get(r); + l++; + list.get(r).next = list.get(l); + r--; } - ans = Math.max(ans, dp[i]); + list.get(l).next = null; } - return ans; } ``` -### 把二叉树打印成多行 +### 二叉搜索树与双向链表 ```java -import java.util.*; - - -/* +/** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; - + public TreeNode(int val) { this.val = val; - + } - } */ public class Solution { - ArrayList> Print(TreeNode pRoot) {
- if(pRoot == null){
- return new ArrayList>();
- }
- ArrayList> list = new ArrayList();
-
- Queue queue = new LinkedList
();
- queue.add(pRoot);
- while(!queue.isEmpty()){
- ArrayList temp = new ArrayList();
- for(int i = queue.size(); i> 0; i--){
- TreeNode node = queue.poll();
- temp.add(node.val);
- if(node.left != null){
- queue.add(node.left);
- }
- if(node.right != null){
- queue.add(node.right);
- }
- }
- list.add(temp);
+ public TreeNode head = null;
+ public TreeNode dummy = null;
+ public TreeNode Convert(TreeNode pRootOfTree) {
+ ConvertSub(pRootOfTree);
+ return dummy;
+ }
+
+ public void ConvertSub(TreeNode root){
+ if(root == null) return;
+ ConvertSub(root.left);
+
+ if(head == null){
+ head = root;
+ dummy = root;
+ } else {
+ head.right = root;
+ root.left = head;
+ head = root;
}
-
- return list;
+
+ ConvertSub(root.right);
}
-
}
```
-### 合并两个有序的数组
+## 队列、栈
+
+### 用两个栈实现队列
```java
+import java.util.Stack;
public class Solution {
- public void merge(int A[], int m, int B[], int n) {
- int i = m-1, j = n-1, k = m+n-1;
- while(i>= 0 && j>= 0){
- if(A[i]> B[j]){
- A[k--] = A[i--];
- } else {
- A[k--] = B[j--];
+ Stack stack1 = new Stack();
+ Stack stack2 = new Stack();
+
+ public void push(int node) {
+ stack1.add(node);
+ }
+
+ public void pushToPop(){
+ if(stack2.isEmpty()){
+ while(!stack1.isEmpty()){
+ stack2.add(stack1.pop());
}
}
-
- while(j>= 0){
- A[k--] = B[j--];
- }
+ }
+
+ public int pop() {
+ pushToPop();
+ return stack2.pop();
}
}
```
-### 二叉树的最大路径和
+### 有效括号序列
```java
+import java.util.*;
public class Solution {
- int max = Integer.MIN_VALUE;
- /**
- *
- * @param root TreeNode类
- * @return int整型
- */
- public int maxPathSum (TreeNode root) {
+
+ public boolean isValid (String s) {
// write code here
- maxSum(root);
- return max;
- }
-
- public int maxSum(TreeNode root){
- if(root == null){
- return 0;
+ Stack stack = new Stack();
+ char[] chs = s.toCharArray();
+ for(int i = 0; i < chs.length; i++){ + if(stack.isEmpty()){ + stack.push(chs[i]); + } else if(chs[i] == '{' || chs[i] == '[' + || chs[i] == '('){ + stack.push(chs[i]); + } else if((chs[i] == '}' && stack.peek() == '{') || + (chs[i] == ']' && stack.peek() == '[') || + (chs[i] == ')' && stack.peek() == '(')){ + stack.pop(); + } } - //三种情况:1.包含一个子树和顶点,2.仅包含顶点,3.包含左子树和右子树以及顶点。 - int left = Math.max(maxSum(root.left),0); - int right = Math.max(maxSum(root.right),0); - - max = Math.max(max,left+right+root.val); - - //对于每一个子树,返回包含该子树顶点的深度方向的路径和的最大值。 - return root.val + Math.max(left,right); + return stack.isEmpty() ? true : false; } } ``` -### 买卖股票的最佳时机 - -base case: -dp[-1][k][0] = dp[i][0][0] = 0 -dp[-1][k][1] = dp[i][0][1] = -infinity - -状态转移方程: -dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]) -dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]) +### 包含 min 函数的栈 ```java +import java.util.Stack; + public class Solution { - /** - * - * @param prices int整型一维数组 - * @return int整型 - */ - public int maxProfit (int[] prices) { - if(prices.length == 0) return 0; - // write code here - int n = prices.length; - int[][] dp = new int[n][2]; - for(int i = 0; i < n; i++){ - if(i - 1 == -1){ - dp[i][0] = 0; - dp[i][1] = -prices[i]; - continue; - } - dp[i][0] = Math.max(dp[i-1][0], - dp[i-1][1] + prices[i]); - dp[i][1] = Math.max(dp[i-1][1],-prices[i]); + Stack minStack = new Stack();
+ Stack stack = new Stack();
+
+ public void push(int node) {
+ if(minStack.isEmpty()){
+ minStack.push(node);
}
- return dp[n-1][0];
+ if(node < minStack.peek().intValue()){ + minStack.push(node); + } else { + minStack.push(minStack.peek()); + } + + stack.push(node); + } + + public void pop() { + if(stack.isEmpty()){ + return; + } + stack.pop(); + minStack.pop(); + } + + public int top() { + return minStack.peek(); + } + + public int min() { + return minStack.peek(); } } ``` -### 二叉树中是否存在节点和为指定值的路径 +### 表达式求值 + +step 1:使用栈辅助处理优先级,默认符号为加号。 +step 2:遍历字符串,遇到数字,则将连续的数字字符部分转化为int型数字。 +step 3:遇到左括号,则将括号后的部分送入递归,处理子问题; +遇到右括号代表已经到了这个子问题的结尾,结束继续遍历字符串,将子问题的加法部分相加为一个数字,返回。 +step 4:当遇到符号的时候如果是+,得到的数字正常入栈,如果是-,则将其相反数入栈, +如果是*,则将栈中内容弹出与后一个元素相乘再入栈。 +step 5:最后将栈中剩余的所有元素,进行一次全部相加。 + ```java +import java.util.*; public class Solution { - /** - * - * @param root TreeNode类 - * @param sum int整型 - * @return bool布尔型 - */ - public boolean hasPathSum (TreeNode root, int sum) { - // write code here - if(root == null){ - return false; - } - - if(root.left == null && root.right == null){ - return sum - root.val == 0; + public ArrayList function(String s, int index){
+ Stack stack = new Stack();
+ int num = 0;
+ char op = '+';
+ int i;
+ for(i = index; i < s.length(); i++){ + //数字转换成int数字 + //判断是否为数字 + if(s.charAt(i)>= '0' && s.charAt(i) <= '9'){ + num = num * 10 + s.charAt(i) - '0'; + if(i != s.length() - 1) + continue; + } + //碰到'('时,把整个括号内的当成一个数字处理 + if(s.charAt(i) == '('){ + //递归处理括号 + ArrayList res = function(s, i + 1);
+ num = res.get(0);
+ i = res.get(1);
+ if(i != s.length() - 1)
+ continue;
+ }
+ switch(op){
+ //加减号先入栈
+ case '+':
+ stack.push(num);
+ break;
+ case '-':
+ //相反数
+ stack.push(-num);
+ break;
+ //优先计算乘号
+ case '*':
+ int temp = stack.pop();
+ stack.push(temp * num);
+ break;
+ }
+ num = 0;
+ //右括号结束递归
+ if(s.charAt(i) == ')')
+ break;
+ else
+ op = s.charAt(i);
}
-
- return hasPathSum(root.left,sum - root.val) ||
- hasPathSum(root.right,sum - root.val);
+ int sum = 0;
+ //栈中元素相加
+ while(!stack.isEmpty())
+ sum += stack.pop();
+ ArrayList temp = new ArrayList();
+ temp.add(sum);
+ temp.add(i);
+ return temp;
+ }
+ public int solve (String s) {
+ ArrayList res = function(s, 0);
+ return res.get(0);
}
}
```
-### 设计getMin功能的栈
+### 最长括号子串
+
+step 1:可以使用栈来记录左括号下标。
+step 2:遍历字符串,左括号入栈,每次遇到右括号则弹出左括号的下标。
+step 3:然后长度则更新为当前下标与栈顶下标的距离。
+step 4:遇到不符合的括号,可能会使栈为空,因此需要使用start记录上一次结束的位置,
+这样用当前下标减去start即可获取长度,即得到子串。
+step 5:循环中最后维护子串长度最大值。
```java
+import java.util.*;
public class Solution {
- /**
- * return a array which include all ans for op3
- * @param op int整型二维数组 operator
- * @return int整型一维数组
- */
- public int[] getMinStack (int[][] op) {
- // write code here
- LinkedList stack = new LinkedList();
- LinkedList minStack = new LinkedList();
- ArrayList ans = new ArrayList();
-
- for(int i = 0; i < op.length; i++){ - int type = op[i][0]; - if(type == 1){ - if(minStack.size() == 0){ - minStack.push(op[i][1]); - } else if(op[i][1] <= minStack.peek()){ - minStack.push(op[i][1]); + public int longestValidParentheses(String s) { + if (s == null || s.length() == 0) + return 0; + int[] dp = new int[s.length()]; + int ans = 0; + for (int i = 1; i < s.length(); i++) { + // 如果是'('直接跳过,默认为0 + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') + dp[i] = (i>= 2 ? dp[i - 2] : 0) + 2;
+ // 说明s.charAt(i - 1)==')'
+ else if (i - dp[i - 1]> 0
+ && s.charAt(i - dp[i - 1] - 1) == '(') {
+ dp[i] = (i - dp[i - 1]> 1
+ ? dp[i - dp[i - 1] - 2] : 0) + dp[i - 1] + 2;
+ // 因为加了一个左括号和一个右括号,所以是加2
}
- stack.push(op[i][1]);
- } else if(type == 2) {
- if(stack.peek().equals(minStack.peek())){
- minStack.pop();
- }
- stack.pop();
- } else {
- ans.add(minStack.peek());
}
+ ans = Math.max(ans, dp[i]);
}
-
- int[] res = new int[ans.size()];
- for(int i = 0; i < ans.size(); i++){ - res[i] = ans.get(i); - } - return res; + return ans; } } ``` -### LFU缓存结构设计 - ```java -// 解法一 - +import java.util.*; public class Solution { - /** - * lfu design - * @param operators int整型二维数组 ops - * @param k int整型 the k - * @return int整型一维数组 - */ - public int[] LFU (int[][] operators, int k) { - // write code here - if(operators == null) return new int[]{-1}; - HashMap map = new HashMap();// key -> value
- HashMap count = new HashMap(); // key -> count
- List list = new ArrayList();
- for(int[] ops : operators){
- int type = ops[0];
- int key = ops[1];
- if(type == 1){
- // set操作
- if(map.containsKey(key)){
- map.put(key,ops[2]);
- count.put(key,count.get(key)+1);
- } else {
- if(map.size() == k){
- int minKey = getMinKey(count);
- map.remove(minKey);
- count.remove(minKey);
- }
- map.put(key,ops[2]);
- if(count.containsKey(key)){
- count.put(key,count.get(key)+1);
- } else {
- count.put(key,1);
- }
- }
- } else if(type == 2) {
- if(map.containsKey(key)){
- int value = map.get(key);
- count.put(key,count.get(key)+1);
- list.add(value);
- } else {
- list.add(-1);
+ public int longestValidParentheses (String s) {
+ int res = 0;
+ //记录上一次连续括号结束的位置
+ int start = -1;
+ Stack st = new Stack();
+ for(int i = 0; i < s.length(); i++){ + //左括号入栈 + if(s.charAt(i) == '(') + st.push(i); + //右括号 + else{ + //如果右括号时栈为空,不合法,设置为结束位置 + if(st.isEmpty()) + start = i; + else{ + //弹出左括号 + st.pop(); + //栈中还有左括号,说明右括号不够,减去栈顶位置就是长度 + if(!st.empty()) + res = Math.max(res, i - st.peek()); + //栈中没有括号,说明左右括号行号,减去上一次结束的位置就是长度 + else + res = Math.max(res, i - start); } } } - - int[] ans = new int[list.size()]; - for(int i = 0; i < list.size(); i++){ - ans[i] = list.get(i); - } - return ans; - } - - public int getMinKey(HashMap map){
- int minCount = Integer.MAX_VALUE;
- int key = 0;
- for(Entry entry : map.entrySet()){
- if(entry.getValue() < minCount){ - minCount = entry.getValue(); - key = entry.getKey(); - } - } - return key; + return res; } } +``` -// 解法二 - -import java.util.*; - - -public -class Solution { +### 括号生成 - public class LFUCache { +对于括号的题,核心基本都是: +"一个字符串是合法的括号组合"的*充分必要*条件是: - private class Node { - int k; - int v; - int count; //调用次数 +1. 字符串中开口数等于闭口数 (这是废话) +2. 字符串的所有prefix都满足: 开口数>=闭口数
+举个栗子,比如 "()(())":
+prefix: "(", "()", "()(", "()((", "()(()", "()(())".
- public Node(int k, int v) {
- this.k = k;
- this.v = v;
- count = 1;
- }
- }
+那么对与这道题,为满足1,2, 每一个位置可以有的permutation就是:
- private int size;
- private int maxSize;
- private Map key2node;
- private Map> count2list; //<调用次数,对于调用次数的链表>
+1. 如果有多余的开口 -> 可以选开口
+2. 如果有多余未闭合的开口 -> 可以选闭口
- public LFUCache(int maxSize) {
- this.maxSize = maxSize;
- size = 0;
- key2node = new HashMap();
- count2list = new HashMap();
- }
+剩下的就是正常的递归+回溯了
+时间: O(2^n), 每一位最多2个permutation
+空间: O(n), 栈高是n
- public void set(int k, int v) {
- if (key2node.containsKey(k)) { //存在
- key2node.get(k).v = v;
- get(k); //get 一次用于改变调用次数
- return;
- }
- //不存在:建一个新节点
- Node node = new Node(k, v);
- //如果调用次数map中不存在,就添加一个新链表
- if (!count2list.containsKey(node.count))
- count2list.put(node.count, new LinkedList());
- LinkedList list = count2list.get(node.count);
- list.addFirst(node);//插入到该链表的头部,表示该调用次数中,是最近调用的,链表尾才是最久没有调动的
- key2node.put(k, node);//同时加入核心map
- size++;
- if (size> maxSize) {//超过容量了,删除一个
- key2node.remove(list.getLast().k);
- list.removeLast();
- size--;
- }
- }
+```java
+import java.util.*;
- public int get(int k) {
- if (!key2node.containsKey(k)) return -1;
- Node node = key2node.get(k);//获取之后
- //还需要更新调用次数:
- LinkedList oldList = count2list.get(node.count);
- oldList.remove(node);//原来的链表中删除
- if (oldList.isEmpty()) count2list.remove(node.count);
- node.count++;
- //建立并加入新链表
- if (!count2list.containsKey(node.count))
- count2list.put(node.count, new LinkedList());
- LinkedList list = count2list.get(node.count);
- list.addFirst(node);
- return node.v;
- }
+public class Solution {
+ ArrayList ans = new ArrayList();
+
+ public ArrayList generateParenthesis (int n) {
+ permute(n, n, 0, new StringBuilder());
+ return ans;
}
-
- public int[] LFU(int[][] operators, int k) {
- LFUCache cache = new LFUCache(k);
- List list = new ArrayList();
- for (int[] e : operators) {
- if (e[0] == 1) cache.set(e[1], e[2]);
- else list.add(cache.get(e[1]));
- }
- int[] res = new int[list.size()];
- for (int i = 0; i < res.length; i++) res[i] = list.get(i); - return res; + + void permute(int open, int close, int unclosedOpen, StringBuilder sb) { + // base case,开口闭口都用完了 + if (open == 0 && close == 0) { + ans.add(sb.toString()); + return; + } + + // always ok to pick an open bracket if there are any open-bracket + if (open> 0) {
+ sb.append("(");
+ permute(open-1, close, unclosedOpen+1, sb);
+ sb.deleteCharAt(sb.length()-1);
+ }
+ // can pick close bracket if there is any unclosed open-bracket
+ if (unclosedOpen> 0) {
+ sb.append(")");
+ permute(open, close-1, unclosedOpen-1, sb);
+ sb.deleteCharAt(sb.length()-1);
+ }
}
}
```
-### N皇后问题
+## 二叉树
+
+### 实现二叉树先序,中序和后序遍历
```java
+import java.util.*;
+
+/*
+ * public class TreeNode {
+ * int val = 0;
+ * TreeNode left = null;
+ * TreeNode right = null;
+ * }
+ */
+
public class Solution {
/**
- *
- * @param n int整型 the n
- * @return int整型
+ *
+ * @param root TreeNode类 the root of binary tree
+ * @return int整型二维数组
*/
- public int Nqueen (int n) {
+ public int[][] threeOrders (TreeNode root) {
// write code here
- List res=new ArrayList();
- char[][] chess=new char[n][n];
- for(int i=0;i list1 = new ArrayList();
+ ArrayList list2 = new ArrayList();
+ ArrayList list3 = new ArrayList();
+ front(root,list1,list2,list3);
+ int[][] ints = new int[3][list1.size()];
+ for (int i = 0; i < list1.size(); i++) { + ints[0][i] = list1.get(i); + ints[1][i] = list2.get(i); + ints[2][i] = list3.get(i); } - - backtrack(0,chess,res); - return res.size(); + return ints; } - - // 回溯 - public void backtrack(int row,char[][] chess,List res){
- if(row==chess.length){
- res.add(1);
+
+ public void front(TreeNode root,ArrayList list1,
+ ArrayList list2,ArrayList list3){
+ if(root == null){
return;
}
- for(int col=0;col=0&&j=0&&i>=0;i--,j--){
- if(chess[i][j]=='Q'){
- return false;
- }
- }
- return true;
+
+ list1.add(root.val);
+ front(root.left,list1,list2,list3);
+ list2.add(root.val);
+ front(root.right,list1,list2,list3);
+ list3.add(root.val);
}
}
```
-### 带权值的最小路径和
+- 非递归遍历
+
+- 前序遍历
+
+用栈来保存信息,但是遍历的时候,是:**先输出根节点信息,然后压入右节点信息,然后再压入左节点信息。**
```java
-public class Solution {
- /**
- *
- * @param grid int整型二维数组
- * @return int整型
- */
- public int minPathSum (int[][] grid) {
- // write code here
- int row = grid.length;
- int col = grid[0].length;
- int[][] dp = new int[row][col];
- dp[0][0] = grid[0][0];
- for(int i = 1; i < row; i++){ - dp[i][0] = dp[i-1][0] + grid[i][0]; - } - for(int j = 1; j < col; j++){ - dp[0][j] = dp[0][j-1] + grid[0][j]; +public void pre(Node head){ + if(head == null){ + return; + } + Stack stack = new Stack();
+ stack.push(head);
+ while(!stack.isEmpty()){
+ head = stack.poll();
+ System.out.println(head.value + " ");
+ if(head.right != null){
+ stack.push(head.right);
}
-
- for(int i = 1; i < row; i++){ - for(int j = 1; j < col; j++){ - dp[i][j] = Math.min(dp[i][j-1],dp[i-1][j]) + grid[i][j]; - } + if(head.left != null){ + stack.push(head.left); } - - return dp[row-1][col-1]; } + System.out.println(); } ``` -### 反转数字 +- 中序遍历 + +中序遍历的顺序是**左中右**,先一直左节点遍历,并压入栈中,当做节点为空时,输出当前节点,往右节点遍历。 ```java -public class Solution { - /** - * - * @param x int整型 - * @return int整型 - */ - public int reverse (int x) { - // write code here - int res = 0; - while(x != 0){ - // 获取最后一位 - int tail = x % 10; - int newRes = res * 10 + tail; - // 如果不等于,说明溢出 - if((newRes - tail) / 10 != res){ - return 0; - } - res = newRes; - x /= 10; +public void inorder(Node head){ + if(head == null){ + return; + } + Stack stack = new Stack();
+ stack.push(head);
+ while(!stack.isEmpty() || head != null){
+ if(head != null){
+ stack.push(head);
+ head = head.left
+ } else {
+ head = stack.poll();
+ System.out.println(head.value + " ");
+ head = head.right;
}
-
- return res;
}
+ System.out.println();
}
```
-### 二叉搜索树的第k个结点
+- 后序遍历
+
+用两个栈来实现,压入栈1的时候为**先左后右**,栈1弹出来就是**中右左**,栈2收集起来就是**左右中**。
```java
-public class Solution {
- int index = 0;
- TreeNode target = null;
- TreeNode KthNode(TreeNode pRoot, int k){
- getKthNode(pRoot,k);
- return target;
+// 后序遍历-迭代
+public void postIteOrders(TreeNode root, List postList) {
+ if (root == null) {
+ return;
}
-
- public void getKthNode(TreeNode pRoot, int k){
- if(pRoot == null){
- return;
+ // 用两个栈来实现
+ // 通过 stack1 和 stack2 来配合可以实现 左 - 右 - 中的顺序
+ Stack stack1 = new Stack
();
+ Stack stack2 = new Stack
();
+ stack1.push(root);
+ while (!stack1.isEmpty()) {
+ TreeNode node = stack1.pop();
+ stack2.push(node);
+ // 先入左节点
+ if (node.left != null) {
+ stack1.push(node.left);
}
- getKthNode(pRoot.left,k);
- index++;
- if(index == k){
- target = pRoot;
- return;
+ // 在入右节点
+ if (node.right != null) {
+ stack1.push(node.right);
}
- getKthNode(pRoot.right,k);
+
}
+ // 弹出元素
+ while (!stack2.isEmpty()) {
+ postList.add(stack2.pop().val);
+ }
+}
+```
+### 二叉树的层序遍历
+```java
+public ArrayList> levelOrder (TreeNode root) {
+ // write code here
+ ArrayList> result = new ArrayList();
+ if (root == null) {
+ return result;
+ }
+ // 队列,用于存储元素
+ Queue queue = new LinkedList
();
+ // 根节点先入队
+ queue.offer(root);
+ // 当队列不为空的时候
+ while(!queue.isEmpty()) {
+ // 队列的大小就是这一层的元素数量
+ int size = queue.size();
+ ArrayList list = new ArrayList();
+ // 开始遍历这一层的所有元素
+ for (int i = 0; i < size; i ++) { + TreeNode node = queue.poll(); + // 如果左节点不为空,则入队,作为下一层来遍历 + if(node.left != null) { + queue.offer(node.left); + } + // 同上 + if (node.right != null) { + queue.offer(node.right); + } + // 存储一层的节点 + list.add(node.val); + } + // 将一层所有的节点汇入到总的结果集中 + result.add(list); + } + return result; } ``` -### 子数组最大乘积 +### 二叉树的之字形层序遍历 ```java +import java.util.Queue; +import java.util.LinkedList; + public class Solution { - public double maxProduct(double[] arr) { - if(arr.length == 0 || arr == null){ - return 0.0; - } - double[] max = new double[arr.length]; - double[] min = new double[arr.length]; - max[0] = min[0] = arr[0]; - for(int i = 1; i < arr.length; i++){ - max[i] = Math.max(Math.max(max[i-1]*arr[i], - min[i-1]*arr[i]),arr[i]); - min[i] = Math.min(Math.min(max[i-1]*arr[i], - min[i-1]*arr[i]),arr[i]); - } - - double ans = max[0]; - for(int i = 0; i < max.length; i++){ - if(max[i]> ans){
- ans = max[i];
+
+ public ArrayList> Print(TreeNode root) {
+ ArrayList> res = new ArrayList();
+ if (root == null)
+ return res;
+ Queue queue = new LinkedList
();
+ queue.add(root);
+ boolean leftToRight = true;
+ while (!queue.isEmpty()) {
+ ArrayList level = new ArrayList();
+ //统计这一行有多少个节点
+ int count = queue.size();
+ //遍历这一行的所有节点
+ for (int i = 0; i < count; i++) { + //poll移除队列头部元素(队列在头部移除,尾部添加) + TreeNode node = queue.poll(); + //判断是从左往右打印还是从右往左打印。 + if (leftToRight) { + level.add(node.val); + } else { + level.add(0, node.val); + } + //左右子节点如果不为空会被加入到队列中 + if (node.left != null) + queue.add(node.left); + if (node.right != null) + queue.add(node.right); } + res.add(level); + leftToRight = !leftToRight; } - return ans; + return res; } } ``` -### 最长递增子序列 +### 在二叉树中找到两个节点的最近公共祖先 ```java -public int[] LIS (int[] arr) { - // write code here - if(arr == null || arr.length <= 0){ - return null; +import java.util.*; + +public class Solution { + public int lowestCommonAncestor (TreeNode root, int o1, int o2) { + // root为空则说明越过了叶子节点 + if(root == null) return -1; + // 如果root为o1或o2中任意一个,则root就是公共祖先 + if(root.val == o1 || root.val == o2) return root.val; + + //root不为o1或o2 + int left = lowestCommonAncestor(root.left, o1, o2); + int right = lowestCommonAncestor(root.right, o1, o2); + //如果left=-1,说明在左子树中一直找到叶子节点,也没找到最近公共祖先 + //所以最近公共祖先,必在右子树中,right即为最近公共祖先 + if(left == -1) return right; + //同理,最近公共祖先必在左子树中,left即为最近公共祖先 + else if(right == -1) return left; + //若left和right都不为-1,则说明o1,o2节点在root的异侧,则root为最近公共祖先 + else return root.val; } +} +``` - int len = arr.length; - int[] count = new int[len]; // 存长度 - int[] end = new int[len]; // 存最长递增子序列 +### 重建二叉树 - //init - int index = 0; // end 数组下标 - end[index] = arr[0]; - count[0] = 1; +```java - for(int i = 0; i < len; i++){ - if(end[index] < arr[i]){ - end[++index] = arr[i]; - count[i] = index; +import java.util.*; +public class Solution { + public TreeNode reConstructBinaryTree(int [] pre,int [] in) { + if(pre.length == 0||in.length == 0){ + return null; } - else{ - int left = 0, right = index; - while(left <= right){ - int mid = (left + right)>> 1;
- if(end[mid]>= arr[i]){
- right = mid - 1;
- }
- else{
- left = mid + 1;
- }
+ TreeNode node = new TreeNode(pre[0]);
+ for(int i = 0; i < in.length; i++){ + if(pre[0] == in[i]){ + node.left = reConstructBinaryTree( + Arrays.copyOfRange(pre, 1, i+1), + Arrays.copyOfRange(in, 0, i)); + node.right = reConstructBinaryTree( + Arrays.copyOfRange(pre, i+1, pre.length), + Arrays.copyOfRange(in, i+1,in.length)); } - end[left] = arr[i]; - count[i] = left; } + return node; } +} +``` - //因为返回的数组要求是字典序,所以从后向前遍历 - int[] res = new int[index + 1]; - for(int i = len - 1; i>= 0; i--){
- if(count[i] == index){
- res[index--] = arr[i];
+```java
+public TreeNode reConstructBinaryTree(int [] pre, int [] in) {
+ TreeNode root = rebuild(pre, 0, pre.length - 1,
+ in, 0, in.length - 1);
+ return root;
+}
+
+public TreeNode rebuild(int[] preorder, int preStart,
+ int preEnd, int[] inorder, int inStart, int inEnd) {
+ if (preStart < 0 || inStart < 0 || + preStart> preEnd || inStart> inEnd) {
+ return null;
+ }
+ TreeNode root = new TreeNode(preorder[preStart]);
+
+ int index = 0;
+ for (int i = 0; i < inorder.length; i++) { + if (inorder[i] == root.val) { + index = i; + break; } } - return res; + int leftLen = index - inStart; + int rightLen = inEnd - index; + root.left = rebuild(preorder, preStart + 1, + leftLen + preStart, inorder, inStart, index - 1); + root.right = rebuild(preorder, leftLen + preStart + 1, + preEnd, inorder, index + 1, inEnd); + return root; } ``` -### 在两个长度相等的排序数组中找到上中位数 +### 输出二叉树的右视图(先重建,再输出右视图) ```java -public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) { - // write code here - int n = arr1.length; - if(n==0){ - return 0; - } - //arr1左右两端 - int l1=0,r1=n-1; - //arr2左右两端 - int l2=0,r2=n-1; - int mid1,mid2; - //终止条件为l1=r1,即两个数组都只有一个元素,此时的上中位数为两数的最小值 - while(l1< r1){ - //arr1中位数 - mid1 = l1+((r1-l1)>>1);
- //arr2中位数
- mid2 = l2+((r2-l2)>>1);
- int k = r1-l1+1;
- if(arr1[mid1] == arr2[mid2]){ //若两数组中位数相等,整体中位数也是这个
- return arr1[mid1];
+public class Solution {
+ /**
+ * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
+ * 求二叉树的右视图
+ * @param xianxu int整型一维数组 先序遍历
+ * @param zhongxu int整型一维数组 中序遍历
+ * @return int整型一维数组
+ */
+ public int[] solve (int[] preorder, int[] inorder) {
+ // write code here
+ if (preorder == null || preorder.length < 1 + || inorder == null || inorder.length < 1) { + return new int[0]; } - else if(arr1[mid1]> arr2[mid2]){
- if(k%2 == 0){//区间元素个数为偶数
- r1 = mid1; //整体中位数在arr1左区间,包括mid1
- l2 = mid2+1; //整体中位数在arr2右区间,不包括mid2
- }
- else if(k%2 == 1){ //区间元素个数为奇数
- r1 = mid1; //整体中位数在arr1左区间,包括mid1
- l2 = mid2; //整体中位数在arr2右区间,包括mid2
+ TreeNode root = rebuild(preorder, 0, preorder.length - 1,
+ inorder, 0, inorder.length - 1);
+ LinkedList queue = new LinkedList
();
+ TreeNode cur = root;
+ queue.offer(cur);
+ List list = new ArrayList();
+ while (!queue.isEmpty()) {
+ int size = queue.size();
+ list.add(queue.peekLast().val);
+ for (int i = 0; i < size; i++) { + cur = queue.poll(); + if (cur.left != null) { + queue.offer(cur.left); + } + if (cur.right != null) { + queue.offer(cur.right); + } } } - else if (arr1[mid1] < arr2[mid2]){ - if(k%2 == 0){//区间元素个数为偶数 - r2 = mid2; //整体中位数在arr2左区间,包括mid2 - l1 = mid1+1; //整体中位数在arr1右区间,不包括mid1 - } - else if(k%2 == 1){ //区间元素个数为奇数 - r2 = mid2; //整体中位数在arr2左区间,包括mid2 - l1 = mid1; //整体中位数在arr1右区间,包括mid1 + int[] res = new int[list.size()]; + for (int i = 0; i < res.length; i++) { + res[i] = list.get(i); + } + return res; + } + + public TreeNode rebuild(int[] preorder, int preStart, + int preEnd, int[] inorder, int inStart, int inEnd) { + if (preStart < 0 || inStart < 0 || + preStart> preEnd || inStart> inEnd) {
+ return null;
+ }
+ TreeNode root = new TreeNode(preorder[preStart]);
+
+ int index = 0;
+ for (int i = 0; i < inorder.length; i++) { + if (inorder[i] == root.val) { + index = i; + break; } } + int leftLen = index - inStart; + int rightLen = inEnd - index; + root.left = rebuild(preorder, preStart + 1, + leftLen + preStart, inorder, inStart, index - 1); + root.right = rebuild(preorder, leftLen + preStart + 1, + preEnd, inorder, index + 1, inEnd); + return root; + } + + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int val) { + this.val = val; + } } - //当区间内只有一个元素时,两个区间中最小值即为整体中位数 - return Math.min(arr1[l1],arr2[l2]); } ``` -### 判断t1树中是否有与t2树拓扑结构完全相同的子树 +### 二叉树的最大深度 ```java -/** - * - * @param root1 TreeNode类 - * @param root2 TreeNode类 - * @return bool布尔型 - */ -public boolean isContains (TreeNode root1, TreeNode root2) { - // write code here - if(root1 == null || root2 == null){ - return false; - } - return recur(root1, root2) || isContains(root1.left,root2) - || isContains(root1.right,root2); +public int maxDepth(TreeNode root) { + return root==null? 0 : + Math.max(maxDepth(root.left), maxDepth(root.right))+1; } +``` -public boolean recur(TreeNode root1, TreeNode root2){ - if(root2 == null){ - return true; +### 判断是不是平衡二叉树 + +```java +import java.util.*; +public class Solution { + public boolean IsBalanced_Solution(TreeNode root) { + //可以分别求出左右子树的高度,然后进行对比 + return TreeDepth(root)>= 0;
}
- if(root1 == null || root1.val != root2.val){
- return false;
+ //求二叉树深度的方法
+ public int TreeDepth(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ int leftHeight = TreeDepth(root.left);
+ int rightHeight = TreeDepth(root.right);
+ if (leftHeight == -1 || rightHeight == -1
+ || Math.abs(leftHeight - rightHeight)> 1) {
+ return -1;
+ } else {
+ return Math.max(leftHeight, rightHeight) + 1;
+ }
}
- return recur(root1.left,root2.left) &&
- recur(root1.right,root2.right);
}
```
-### 反转字符串
+### 二叉树根节点到叶子节点的所有路径和
```java
-/**
-* 反转字符串
-* @param str string字符串
-* @return string字符串
-*/
-public String solve (String str) {
- // write code here
- if(str == null){
- return null;
+public class Solution {
+ /**
+ *
+ * @param root TreeNode类
+ * @return int整型
+ */
+ public int sumNumbers (TreeNode root) {
+ // 调用dfs
+ return dfs(root,0);
}
- char[] c = new char[str.length()];
- int left = 0, right = str.length() - 1;
-
- while(left <= right){ - c[left] = str.charAt(right); - c[right] = str.charAt(left); - left++; - right--; + //深度优先搜索 + public int dfs(TreeNode root,int sum){ + if(root==null){ + return 0; + } + int total = sum*10+root.val; + //已达到叶子节点,返回结果 + if(root.left==null && root.right==null){ + return total; + }else{ + //递归调用 + return dfs(root.left,total)+dfs(root.right,total); + } + } - return new String(c); } ``` -### 最大正方形 +### 二叉树中和为某一值的路径,返回所有路径 ```java -/** -* 最大正方形 -* @param matrix char字符型二维数组 -* @return int整型 -*/ -public int solve (char[][] matrix) { - // write code here - int m = matrix.length; - int n = matrix[0].length; - int dp[][] = new int [m][n]; - for(int i = 0; i < m; i++){ - dp[i][0] = matrix[i][0] - '0'; +import java.util.ArrayList; +public class Solution { + ArrayList> lists = new ArrayList();
+ ArrayList list = new ArrayList();
+ public ArrayList> FindPath(TreeNode root,int target) {
+ if(root == null){
+ return lists;
}
- for(int j = 0; j < n; j++){ - dp[0][j] = matrix[0][j] - '0'; - } - int max = 0; - for(int i = 1; i < m; i++){ - for(int j = 1; j < n; j++){ - if(matrix[i][j] == '1'){ - dp[i][j] = Math.min(Math.min(dp[i-1][j-1], - dp[i][j-1]),dp[i-1][j]) + 1; - max = Math.max(max,dp[i][j]); - } + list.add(root.val); + target -= root.val; + if(target == 0 && root.left == null + && root.right == null){ + lists.add(new ArrayList(list));
}
+ FindPath(root.left,target);
+ FindPath(root.right,target);
+ list.remove(list.size() - 1);
+ return lists;
}
- return max*max;
}
```
-### 链表中的节点每K个一组翻转
+### 判断一棵二叉树是否为搜索二叉树和完全二叉树
```java
-//明显递归解决,翻转第一组之后,以第二组的开头为头节点,继续翻转,转翻到最后,返回。
-public ListNode reverseKGroup(ListNode head, int k) {
- if(head==null||head.next==null)
- return head;
- ListNode h=new ListNode(0);
- h.next=head;
- ListNode next=null,tmp=head,cur=head;
- for(int i=1;i=root.val){
+ return false;
}
- head.next=reverseKGroup(next,k);
- return h.next;
+ num=root.val;
+ boolean right = isSearch(root.right);
+ return left && right;
}
-```
-### 数组中的最长无重复子串的长度
-
-```java
-public int maxLength (int[] arr) {
- int left = 0, right = 0;
- Set set = new HashSet();
- int res = 1;
- while(right < arr.length){ - if(!set.contains(arr[right])){ - set.add(arr[right]); - right++; +public boolean isFull(TreeNode root){ + Queue queue = new LinkedList
();
+ queue.add(root);
+ while(!queue.isEmpty()){
+ TreeNode node = queue.poll();
+ if(node==null){
+ flag=true;
}else{
- set.remove(arr[left]);
- left++;
+ if(flag){
+ return false;
+ }else{
+ queue.add(node.left);
+ queue.add(node.right);
+ }
}
- res = Math.max(res, set.size());
}
- return res;
+ return true;
}
```
-### 判断链表是否为回文结构
+### 二叉树的最大路径和
```java
-import java.util.*;
-
public class Solution {
+ int max = Integer.MIN_VALUE;
/**
- *
- * @param head ListNode类 the head
- * @return bool布尔型
+ *
+ * @param root TreeNode类
+ * @return int整型
*/
- public boolean isPail (ListNode head) {
- ListNode slow = head;
- ListNode fast = head;
- while(fast != null && fast.next != null){
- fast = fast.next.next;
- slow = slow.next;
+ public int maxPathSum (TreeNode root) {
+ // write code here
+ maxSum(root);
+ return max;
+ }
+
+ public int maxSum(TreeNode root){
+ if(root == null){
+ return 0;
}
- Stack stack = new Stack();
- while(slow != null){
- stack.add(slow.val);
- slow = slow.next;
- }
+ //三种情况:1.包含一个子树和顶点,2.仅包含顶点,3.包含左子树和右子树以及顶点。
+ int left = Math.max(maxSum(root.left),0);
+ int right = Math.max(maxSum(root.right),0);
- while(!stack.isEmpty()){
- if(stack.pop() != head.val){
- return false;
- }
-
- head = head.next;
- }
+ max = Math.max(max,left+right+root.val);
- return true;
+ //对于每一个子树,返回包含该子树顶点的深度方向的路径和的最大值。
+ return root.val + Math.max(left,right);
}
}
```
-### 岛屿的数量
+### 判断二叉树是否对称
```java
-class Solution {
- public int numIslands(char[][] grid) {
- int count = 0;
- for(int i = 0; i < grid.length; i++) { - for(int j = 0; j < grid[0].length; j++) { - if(grid[i][j] == '1'){ - bfs(grid, i, j); - count++; - } - } - } - return count; +public class Solution { + /** + * + * @param root TreeNode类 + * @return bool布尔型 + */ + public boolean isSymmetric (TreeNode root) { + // write code here + return isSymmetricNode(root,root); } - public void bfs(char[][] grid, int i, int j){ - Queue list = new LinkedList();
- list.add(new int[] { i, j });
- while(!list.isEmpty()){
- int[] cur = list.remove();
- i = cur[0]; j = cur[1];
- if(inArea(i,j,grid) && grid[i][j] == '1') {
- grid[i][j] = '0';
- list.add(new int[] { i + 1, j });
- list.add(new int[] { i - 1, j });
- list.add(new int[] { i, j + 1 });
- list.add(new int[] { i, j - 1 });
- }
+ public boolean isSymmetricNode(TreeNode node1, TreeNode node2){
+ if(node1 == null && node2 == null){
+ return true;
}
- }
-
- public boolean inArea(int i, int j, char[][] grid){
- return i>=0 && j>= 0 && i < grid.length - && j < grid[0].length; + if(node1 == null || node2 == null){ + return false; + } + if(node1.val != node2.val){ + return false; + } + return isSymmetricNode(node1.left,node2.right) + && isSymmetricNode(node1.right,node2.left); } } ``` -### 在二叉树中找到两个节点的最近公共祖先 +### 二叉树中是否存在节点和为指定值的路径 ```java public class Solution { /** * * @param root TreeNode类 - * @param o1 int整型 - * @param o2 int整型 - * @return int整型 + * @param sum int整型 + * @return bool布尔型 */ - public int lowestCommonAncestor (TreeNode root, int o1, int o2) { - if (root == null) { - return 0; - } - if (root.val == o1 || root.val == o2) { - return root.val; - } - int left = lowestCommonAncestor(root.left, o1, o2); - int right = lowestCommonAncestor(root.right, o1, o2); - if (left != 0 && right != 0) { - return root.val; - } - if (left == 0) { - return right; + public boolean hasPathSum (TreeNode root, int sum) { + // write code here + if(root == null){ + return false; } - if (right == 0) { - return left; + + if(root.left == null && root.right == null){ + return sum - root.val == 0; } - return 0; + + return hasPathSum(root.left,sum - root.val) || + hasPathSum(root.right,sum - root.val); } } ``` -### 重复项数字的所有排列 +### 序列化二叉树 ```java -public class Solution { - private ArrayList> res;
- private boolean[] visited;
-
- public ArrayList> permute(int[] nums) {
+import java.util.*;
+/*
+public class TreeNode {
+ int val = 0;
+ TreeNode left = null;
+ TreeNode right = null;
- res = new ArrayList();
- visited = new boolean[nums.length];
- List list = new ArrayList();
- backtrace(nums, list);
+ public TreeNode(int val) {
+ this.val = val;
- return res;
}
- private void backtrace(int[] nums, List list) {
-
- if (list.size() == nums.length) {
- res.add(new ArrayList(list));
- }
-
- for (int i = 0; i < nums.length; i++) { - - if (visited[i]) continue; +} +*/ +public class Solution { + private int index = -1; - visited[i] = true; - list.add(nums[i]); + public String Serialize(TreeNode root) { + StringBuilder builder = new StringBuilder(); + intervalSerialize(builder, root); + return builder.toString(); + } - backtrace(nums, list); + private void intervalSerialize(StringBuilder builder, TreeNode root) { + if (root == null) { + builder.append("#,"); + return; + } + builder.append(root.val).append(","); + intervalSerialize(builder, root.left); + intervalSerialize(builder, root.right); + } - visited[i] = false; - list.remove(list.size() - 1); + public TreeNode Deserialize(String str) { + if (str == null || str.isEmpty()) { + return null; + } + String[] words = str.split(","); + return internalDeserialize(words); + } + private TreeNode internalDeserialize(String[] words) { + index++; + if (index == words.length) { + return null; + } + if ("#".equals(words[index])) { + return null; } + TreeNode root = new TreeNode(Integer.parseInt(words[index])); + root.left = internalDeserialize(words); + root.right = internalDeserialize(words); + return root; } } ``` -### 最长回文子串的长度 +### 二叉搜索树的第k个结点 ```java -public class Palindrome { - public int getLongestPalindrome(String A, int n) { - // write code here - int max=0; - for (int i=1;i=0&&right=0&&right> Print(TreeNode pRoot) {
+ if(pRoot == null){
+ return new ArrayList>();
+ }
+ ArrayList> list = new ArrayList();
+
+ Queue queue = new LinkedList
();
+ queue.add(pRoot);
+ while(!queue.isEmpty()){
+ ArrayList temp = new ArrayList();
+ for(int i = queue.size(); i> 0; i--){
+ TreeNode node = queue.poll();
+ temp.add(node.val);
+ if(node.left != null){
+ queue.add(node.left);
+ }
+ if(node.right != null){
+ queue.add(node.right);
}
}
+ list.add(temp);
}
- return res.toString();
+
+ return list;
}
+
}
```
-### 最小编辑代价
+### 二叉树的镜像
```java
-import java.util.*;
-
-
public class Solution {
- /**
- * min edit cost
- * @param str1 string字符串 the string
- * @param str2 string字符串 the string
- * @param ic int整型 insert cost
- * @param dc int整型 delete cost
- * @param rc int整型 replace cost
- * @return int整型
- */
- public int minEditCost (String str1, String str2,
- int ic, int dc, int rc) {
- // write code here
- int len1=str1.length();
- int len2=str2.length();
- char[] char1=str1.toCharArray();
- char[] char2=str2.toCharArray();
- int[][] dp=new int[len1+1][len2+1];
- for(int i=1;i<=len1;i++){ - dp[i][0]=dp[i-1][0]+dc; + public void Mirror(TreeNode root) { + if(root == null){ + return; } - for(int i=1;i<=len2;i++){ - dp[0][i]=dp[0][i-1]+ic; + if(root.left == null && root.right == null){ + return; } - for(int i=0;i stack = new Stack();
+ stack.push(root);
+ while(!stack.isEmpty()){
+ TreeNode node = stack.pop();
+
+ if(node.left != null || node.right != null){
+ TreeNode temp = node.left;
+ node.left = node.right;
+ node.right = temp;
+ }
+
+ if(node.left != null){
+ stack.push(node.left);
+ }
+
+ if(node.right != null){
+ stack.push(node.right);
}
}
- return dp[len1][len2];
}
}
+
+/*
+public class Solution {
+ public void Mirror(TreeNode root) {
+ if(root == null){
+ return;
+ }
+ if(root.left == null && root.right == null){
+ return;
+ }
+
+ TreeNode temp = root.left;
+ root.left = root.right;
+ root.right = temp;
+
+ if(root.left != null){
+ Mirror(root.left);
+ }
+
+ if(root.right != null){
+ Mirror(root.right);
+ }
+ }
+}
+*/
+
```
-### 矩阵的最小路径和
+### 判断t1树中是否有与t2树拓扑结构完全相同的子树
```java
-import java.util.*;
-
+/**
+ *
+ * @param root1 TreeNode类
+ * @param root2 TreeNode类
+ * @return bool布尔型
+ */
+public boolean isContains (TreeNode root1, TreeNode root2) {
+ // write code here
+ if(root1 == null || root2 == null){
+ return false;
+ }
+ return recur(root1, root2) || isContains(root1.left,root2)
+ || isContains(root1.right,root2);
+}
-public class Solution {
- /**
- *
- * @param matrix int整型二维数组 the matrix
- * @return int整型
- */
- public int minPathSum (int[][] matrix) {
- // write code here
- int m = matrix.length, n = matrix[0].length;
- if (m == 0 || n == 0) return 0;
-
- for (int i = 1; i < m; i++) - matrix[i][0] += matrix[i-1][0]; - for (int i = 1; i < n; i++) - matrix[0][i] += matrix[0][i-1]; - - for (int i = 1; i < m; i++) { - for (int j = 1; j < n; j++) { - matrix[i][j] += - Math.min(matrix[i-1][j], matrix[i][j-1]); - } - } - return matrix[m-1][n-1]; +public boolean recur(TreeNode root1, TreeNode root2){ + if(root2 == null){ + return true; } + if(root1 == null || root1.val != root2.val){ + return false; + } + return recur(root1.left,root2.left) && + recur(root1.right,root2.right); } ``` -### 顺时针旋转数组 +### 合并二叉树 ```java -// 找规律:mat[i][j]被旋转到了mat[j][n-i-1]的位置 -public class Rotate { - public int[][] rotateMatrix(int[][] mat, int n) { - // write code here - int[][] temp=new int[n][n]; - - for(int i=0;i=max){
- return false;
+ String[] res=new String[len];
+ Trie trie=new Trie();
+ int id=0;
+
+ for(String[] opera:operators){
+ if(opera[0].equals("1")){
+ //添加单词
+ trie.insert(opera[1]);
+ }
+ else if(opera[0].equals("2")){
+ //删除单词
+ trie.delete(opera[1]);
+ }
+ else if(opera[0].equals("3")){
+ //查询单词是否存在
+ res[id++]=trie.search(opera[1])?"YES":"NO";
+ }
+ else if(opera[0].equals("4")){
+ //查找以word为前缀的单词数量
+ String preNumber=String.valueOf(trie.prefixNumber(opera[1]));
+ res[id++]=preNumber;
+ }
}
- return isBST(root.left,min,root.val) && isBST(root.right,root.val,max);
+ return res;
}
-
-
- private boolean isComplete(TreeNode root){
- Queue queue = new LinkedList
();
- queue.offer(root);
-
- while(!queue.isEmpty()){
- TreeNode tmp = queue.poll();
- if(tmp == null){
- break;
+
+ class Trie{
+ //构建字典树节点
+ class TrieNode{
+ //child数组记录所有子节点
+ TrieNode[] child;
+ //pre_number表示插入单词时,当前节点被访问次数
+ int pre_number;
+ //end表示当前节点是否是某个单词的末尾
+ boolean end;
+ TrieNode(){
+ child=new TrieNode[26];
+ pre_number=0;
+ end=false;
}
- queue.offer(tmp.left);
- queue.offer(tmp.right);
}
- while(!queue.isEmpty()){
- if(queue.poll() != null){
- return false;
+
+ Trie(){}
+
+ //初始化根节点
+ TrieNode root=new TrieNode();
+
+ //添加单词
+ void insert(String word){
+ TrieNode node=root;
+ char[] arr=word.toCharArray();
+ for(char c:arr){
+ //如果子节点不存在,则新建
+ if(node.child[c-'a']==null){
+ node.child[c-'a']=new TrieNode();
+ }
+ //往子节点方向移动
+ node=node.child[c-'a'];
+ node.pre_number++;
}
+ node.end=true;
+ }
+
+ void delete(String word){
+ TrieNode node=root;
+ char[] arr=word.toCharArray();
+ for(char c:arr){
+ //往子节点方向移动,将访问次数减一
+ node=node.child[c-'a'];
+ node.pre_number--;
+ }
+ //如果访问次数为0,说明不存在该单词为前缀的单词,以及该单词
+ if(node.pre_number==0){
+ node.end=false;
+ }
+ }
+
+ boolean search(String word){
+ TrieNode node=root;
+ char[] arr=word.toCharArray();
+ for(char c:arr){
+ //如果子节点不存在,说明不存在该单词
+ if(node.child[c-'a']==null){
+ return false;
+ }
+ node=node.child[c-'a'];
+ }
+
+ //如果前面的节点都存在,并且该节点末尾标识为true,则存在该单词
+ return node.end;
+ }
+
+ int prefixNumber(String pre){
+ TrieNode node=root;
+ char[] arr=pre.toCharArray();
+ for(char c:arr){
+ //如果子节点不存在,说明不存在该前缀
+ if(node.child[c-'a']==null){
+ return 0;
+ }
+ node=node.child[c-'a'];
+ }
+
+ //返回以该单词为前缀的数量
+ return node.pre_number;
}
- return true;
}
}
```
-### [连续子数组的最大和(sum < 0置为0)](https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484?tpId=13&&tqId=11183&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) +### 找到二叉搜索树中的两个错误节点 + + ```java +import java.util.*; +public class Solution { + /** + * + * @param root TreeNode类 the root + * @return int整型一维数组 + */ -// dp -public int FindGreatestSumOfSubArray(int[] array) { - if(array.length == 0){ - return 0; - } - - int max = Integer.MIN_VALUE; - int[] dp = new int[array.length]; - for(int i = 0; i < array.length; i++){ - dp[i] = array[i]; - } - for(int i = 1; i < array.length; i++){ - dp[i] = Math.max(dp[i-1] + array[i],dp[i]); - } - - for(int i = 0; i < dp.length; i++){ - if(dp[i]> max){
- max = dp[i];
+ //存储结果集的二维数组
+ int[] result = new int[2];
+ int index = 1;
+ TreeNode preNode;
+ public int[] findError (TreeNode root) {
+ // 特判
+ if(root == null) {
+ return result;
}
+ // 递归左子树,寻找该树符合条件的节点
+ findError(root.left);
+ if(preNode == null) {
+ preNode = root;
+ }
+ // 判断是否是出错的节点
+ if(index == 1 && root.val < preNode.val) { + result[index] = preNode.val; + index--; + } + if(index == 0 && root.val < preNode.val) { + result[index] = root.val; + } + preNode = root; + // 递归右子树,寻找该树符合条件的节点 + findError(root.right); + return result; } - return max; -} - -// sum < 0置为0 -public int FindGreatestSumOfSubArray(int[] array) { - if(array.length == 0){ - return 0; - } - - int max = Integer.MIN_VALUE; - int cur = 0; - for(int i = 0; i < array.length; i++){ - cur += array[i]; - max = Math.max(max,cur); - cur = cur < 0 ? 0 : cur; - } - return max; } - ``` -### 两数之和 +## 堆 + +### 最小的K个数 ```java -import java.util.HashMap; +import java.util.*; public class Solution { - public int[] twoSum(int[] nums, int target) { - HashMap map = new HashMap();
- for (int i = 0; i < nums.length; i++) { - if (map.containsKey(nums[i])){ - return new int[]{map.get(nums[i])+1,i+1}; + public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
+ ArrayList res = new ArrayList();
+ //排除特殊情况
+ if(k == 0 || input.length == 0)
+ return res;
+ //大根堆
+ PriorityQueue q =
+ new PriorityQueue((o1, o2)->o2.compareTo(o1));
+ //构建一个k个大小的堆
+ for(int i = 0; i < k; i++) + q.offer(input[i]); + for(int i = k; i < input.length; i++){ + //较小元素入堆 + if(q.peek()> input[i]){
+ q.poll();
+ q.offer(input[i]);
}
- map.put(target - nums[i],i);
}
- return null;
+ //堆中元素取出入数组
+ for(int i = 0; i < k; i++) + res.add(q.poll()); + return res; } } -``` - -### 删除有序链表中重复出现的元素 - -```java -public ListNode deleteDuplicates (ListNode head) { - ListNode dummy=new ListNode(0); - dummy.next=head; - ListNode pre=dummy; - ListNode p=head; - while(p!=null&&p.next!=null){ - if(p.val==p.next.val){ - while(p.next!=null&&p.val==p.next.val){ - p=p.next; +// 自己实现堆排序 +public class Solution { + public ArrayList
+ GetLeastNumbers_Solution(int [] input, int k) {
+ ArrayList list = new ArrayList();
+ if (input == null || input.length == 0
+ || k> input.length || k == 0)
+ return list;
+ int[] arr = new int[k + 1];//数组下标0的位置作为哨兵,不存储数据
+ //初始化数组
+ for (int i = 1; i < k + 1; i++) + arr[i] = input[i - 1]; + buildMaxHeap(arr, k + 1);//构造大根堆 + for (int i = k; i < input.length; i++) { + if (input[i] < arr[1]) { + arr[1] = input[i]; + adjustDown(arr, 1, k + 1);//将改变了根节点的二叉树继续调整为大根堆 } - pre.next=p.next; - p=p.next; - } - else{ - pre=p; - p=p.next; - } + } + for (int i = 1; i < arr.length; i++) { + list.add(arr[i]); + } + return list; + } + /** + * @Author: ZwZ + * @Description: 构造大根堆 + * @Param: [arr, length] length:数组长度 作为是否跳出循环的条件 + * @return: void + * @Date: 2020/1/30-22:06 + */ + public void buildMaxHeap(int[] arr, int length) { + if (arr == null || arr.length == 0 || arr.length == 1) + return; + for (int i = (length - 1) / 2; i> 0; i--) {
+ adjustDown(arr, i, arr.length);
+ }
+ }
+ /**
+ * @Author: ZwZ
+ * @Description: 堆排序中对一个子二叉树进行堆排序
+ * @Param: [arr, k, length]
+ * @return:
+ * @Date: 2020年1月30日-21:55
+ */
+ public void adjustDown(int[] arr, int k, int length) {
+ arr[0] = arr[k];//哨兵
+ for (int i = 2 * k; i <= length; i *= 2) { + if (i < length - 1 && arr[i] < arr[i + 1]) + i++;//取k较大的子结点的下标 + if (i> length - 1 || arr[0]>= arr[i])
+ break;
+ else {
+ arr[k] = arr[i];
+ k = i; //向下筛选
+ }
+ }
+ arr[k] = arr[0];
}
- return dummy.next;
}
```
-### 在转动过的有序数组中寻找目标值
+### 字符串出现次数的TopK问题
```java
-public int search (int[] a, int target) {
- // write code here
- if(a==null||a.length<=0){ - return -1; - } - int low = 0; - int high = a.length-1; - while(low<=high){ - int mid = low+(high-low)/2; - if(a[mid]==target){ - return mid; - }else if(a[mid] map = new HashMap();
+ for(int i = 0; i < strings.length; i++){ + if(map.containsKey(strings[i])){ + map.put(strings[i], map.get(strings[i]) + 1); }else{ - high = mid-1; + map.put(strings[i], 1); } - }else{ - if(a[low]<=target&&target> pq =
+ new PriorityQueue((o1,o2) -> o1.getValue().equals(o2.getValue())
+ ? o2.getKey().compareTo(o1.getKey()) : o1.getValue()-o2.getValue());
+ int size = 0;
+ //维护size为k的小根堆
+ for(Map.Entry m : map.entrySet()){
+ if(size < k){ + pq.offer(m); + size++; + } + //大于堆顶元素插入 + else if((m.getValue().equals(pq.peek().getValue()) + ? pq.peek().getKey().compareTo(m.getKey()) + : m.getValue() - pq.peek().getValue())> 0){
+ pq.poll();
+ pq.offer(m);
}
}
+ //取出堆中元素,从后向前放置
+ for(int i = k - 1; i>= 0; i--){
+ Map.Entry entry =(Map.Entry)pq.poll();
+ res[i][0] = entry.getKey();
+ res[i][1] = String.valueOf(entry.getValue());
+ }
+ return res;
}
- return -1;
}
```
-### 数组中未出现的最小正整数
+### 寻找第K大
+
+```java
+public int findKth(int[] a, int n, int K){
+ // 暂存K个较大的值,优先队列默认是自然排序(升序),
+ // 队头元素(根)是堆内的最小元素,也就是小根堆
+ PriorityQueue queue = new PriorityQueue(K);
+ // 遍历每一个元素,调整小根堆
+ for (int num : a) {
+ // 对于小根堆来说,只要没满就可以加入(不需要比较);
+ // 如果满了,才判断是否需要替换第一个元素
+ if (queue.size() < K) { + queue.add(num); + } else { + // 在小根堆内,存储着K个较大的元素,根是这K个中最小的, + // 如果出现比根还要大的元素,说明可以替换根 + if (num> queue.peek()) {
+ queue.poll(); // 高个中挑矮个,矮个淘汰
+ queue.add(num);
+ }
+ }
+ }
+ return queue.isEmpty() ? 0 : queue.peek();
+}
+```
```java
-public int minNumberdisappered (int[] arr) {
- int n=arr.length;
- for(int i=0;i=1&&arr[i]<=n&&arr[arr[i]-1]!=arr[i]){ - swap(arr,arr[i]-1,i); +import java.util.*; + +public class Finder { + public int findKth(int[] a, int n, int K) { + // write code here + return find(a, 0, n-1, K); + } + + public int find(int[] a, int low, int high, int K){ + int pivot = partition(a, low, high); + + if(pivot + 1 < K){ + return find(a, pivot + 1, high, K); + } else if(pivot + 1> K){
+ return find(a, low, pivot - 1, K);
+ } else {
+ return a[pivot];
}
}
- for(int i=0;i arr[endIndex]) {
+ swap(arr,++small, i);
+ }
}
+ swap(arr,++small,endIndex);
+ return small;
+ }
+
+ public void swap(int[] arr, int i, int j){
+ int temp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = temp;
}
- return n+1;
-}
-private void swap(int[] arr,int i,int j){
- int temp=arr[i];
- arr[i]=arr[j];
- arr[j]=temp;
}
+
```
-### 数组中最长连续子序列
+## 双指针
+
+### 最长无重复子数组的长度
```java
-public int MLS (int[] arr) {
- if(arr==null || arr.length==0)
+// 方法1
+public int maxLength (int[] arr) {
+ int left = 0, right = 0;
+ Set set = new HashSet();
+ int res = 1;
+ while(right < arr.length){ + if(!set.contains(arr[right])){ + set.add(arr[right]); + right++; + }else{ + set.remove(arr[left]); + left++; + } + res = Math.max(res, set.size()); + } + return res; +} + +// 方法2 +public int maxLength(int[] arr) { + if (arr.length == 0) return 0; - if(arr.length==1) - return 1; - Arrays.sort(arr); - int max = -1; - int count = 1; - for(int i = 0;i map = new HashMap();
+ int max = 0;
+ for (int i = 0, j = 0; i < arr.length; ++i) { + if (map.containsKey(arr[i])) { + j = Math.max(j, map.get(arr[i]) + 1); } + map.put(arr[i], i); + max = Math.max(max, i - j + 1); } - max=max maxInWindows(int [] num, int size)
+ {
+ ArrayList res = new ArrayList();
+ if(size == 0) return res;
+ int begin;
+ ArrayDeque q = new ArrayDeque();
+ for(int i = 0; i < num.length; i++){ + begin = i - size + 1; + if(q.isEmpty()) + q.add(i); + else if(begin> q.peekFirst())
+ q.pollFirst();
+
+ while((!q.isEmpty()) && num[q.peekLast()] <= num[i]) + q.pollLast(); + q.add(i); + if(begin>= 0)
+ res.add(num[q.peekFirst()]);
+ }
+ return res;
+ }
+}
+```
+
+### 合并区间(区间重叠)
+
+首先将各个区间进行排序,排序规则为首先根据start进行排序,
+如果start相等则根据end从小到大排序new一个新的List result存放结果,
+遍历给定的intervals,比较当前interval的start是否大于result中最后一个元素的end,
+若大于,说明从开了一个区间,若区间有重叠,则更新result中最后一个元素的end。
+
+```java
+import java.util.*;
+/**
+ * Definition for an interval.
+ * public class Interval {
+ * int start;
+ * int end;
+ * Interval() { start = 0; end = 0; }
+ * Interval(int s, int e) { start = s; end = e; }
+ * }
+ */
+public class Solution {
+ public ArrayList merge(ArrayList intervals) {
+ // 首先根据start排序,如果start相等,根据end排序
+ Collections.sort(intervals,
+ (o1, o2) -> (o1.start != o2.start
+ ? o1.start - o2.start : o1.end - o2.end));
+ ArrayList result = new ArrayList();
+ if(intervals.size() == 0) {
+ return result;
+ }
+ // 放入第一个区间
+ result.add(intervals.get(0));
+ int count = 0;
+ // 遍历后续区间,查看是否与末尾有重叠
+ for(int i = 1; i < intervals.size(); i++) { + Interval o1 = intervals.get(i); + Interval origin = result.get(result.size() - 1); + // 如果当前Interval的start比List里面最后一个元素的end大,说明从开一个区间 + if(o1.start> origin.end) {
+ result.add(o1);
+ } else { // 区间有重叠,更新结尾
+ if(o1.end> origin.end) {
+ result.get(result.size() - 1).end = o1.end;
+ }
+ }
+ }
+ return result;
+ }
+}
+```
+
+### 反转字符串
```java
+import java.util.*;
public class Solution {
/**
- *
- * @param root TreeNode类
- * @return bool布尔型
+ * 反转字符串
+ * @param str string字符串
+ * @return string字符串
*/
- public boolean isSymmetric (TreeNode root) {
+ public String solve (String str) {
// write code here
- return isSymmetricNode(root,root);
- }
-
- public boolean isSymmetricNode(TreeNode node1, TreeNode node2){
- if(node1 == null && node2 == null){
- return true;
- }
- if(node1 == null || node2 == null){
- return false;
+ if(str == null){
+ return null;
}
- if(node1.val != node2.val){
- return false;
+ char[] c = new char[str.length()];
+ int left = 0, right = str.length() - 1;
+
+ while(left <= right){ + c[left] = str.charAt(right); + c[right] = str.charAt(left); + left++; + right--; } - return isSymmetricNode(node1.left,node2.right) - && isSymmetricNode(node1.right,node2.left); + return new String(c); } } ``` -### 没有重复项数字的所有排列 +### 数组中相加和为0的三元组 ```java -public class Demo1 { - ArrayList> res;
-
- public ArrayList> permute(int[] nums) {
- res = new ArrayList>();
- if (nums == null || nums.length < 1) - return res; - //对数组元素进行从小到大排序 - Arrays.sort(nums); - ArrayList list = new ArrayList();
+import java.util.*;
+
+public class Solution {
+ public ArrayList> threeSum(int[] num) {
+ ArrayList> list = new ArrayList();
+ Arrays.sort(num);
+ int left,right,sum;
+ for(int i = 0; i < num.length - 2; i++){ + if(i> 0 && num[i] == num[i-1]) continue;
+ left = i + 1;
+ right = num.length - 1;
+ while(left < right){ + sum = num[i] + num[left] + num[right]; + if(sum == 0){ + ArrayList temp = new ArrayList();
+ temp.add(num[i]);
+ temp.add(num[left]);
+ temp.add(num[right]);
+ list.add(temp);
+ right--;
+ left++;
+ while(left < right && num[left] == num[left-1]){ + left++; + } + while(left < right && num[right] == num[right+1]){ + right--; + } + } else if(sum < 0){ + left++; + } else { + right--; + } + } + } + return list; + } +} +``` - solve(list, nums); +### 接雨水问题 - return res; - } +```java + public long maxWater(int[] arr) { + if (arr.length <= 2) + return 0; + //找到最高的柱子的下标 + int max = Integer.MIN_VALUE; + int maxIndex = -1; + for (int i = 0; i < arr.length; i++) { + if (arr[i]> max) {
+ max = arr[i];
+ maxIndex = i;
+ }
+ }
+
+ //统计最高柱子左边能接的雨水数量
+ int left = arr[0];
+ int right = 0;
+ long water = 0;
+ for (int i = 1; i < maxIndex; i++) { + right = arr[i]; + if (right> left) {
+ left = right;
+ } else {
+ water += left - right;
+ }
+ }
+
+ //统计最高柱子右边能接的雨水数量
+ right = arr[arr.length - 1];
+ for (int i = arr.length - 2; i> maxIndex; i--) {
+ left = arr[i];
+ if (arr[i]> right) {
+ right = left;
+ } else {
+ water += right - left;
+ }
+ }
+
+ //返回盛水量
+ return water;
+ }
+```
- private void solve(ArrayList list, int[] nums) {
- if (list.size() == nums.length) {
- res.add(new ArrayList(list));
- return;
- }
- for (int i = 0; i < nums.length; i++) { - if (!list.contains(nums[i])) { - list.add(nums[i]); - solve(list, nums); - list.remove(list.size() - 1); - } - } - } +```java +public long maxWater (int[] arr) { + int l = 0, r = arr.length-1; + int maxL = 0, maxR = 0; + long res = 0; + while(l < r){ + maxL = Math.max(arr[l],maxL); // 求出左边界的最大值 + maxR = Math.max(arr[r],maxR); // 求出右边界的最大值 + if(maxR> maxL){ // 如果
+ res += maxL - arr[l++];
+ }else{
+ res += maxR - arr[r--];
+ }
+ }
+ return res;
}
```
-### 集合的所有子集
+### 最小覆盖子串(T包含S的最小子串)
```java
import java.util.*;
+
public class Solution {
- public ArrayList> subsets(int[] S) {
- ArrayList> res=new ArrayList();
- Arrays.sort(S);
- LinkedList list=new LinkedList();
- dfs(res,list,0,S);
- return res;
- }
- public void dfs(ArrayList> res,
- LinkedList list, int k, int[] S){
- res.add(new ArrayList(list));
- for(int i=k;i window = new HashMap();
+ HashMap need = new HashMap();
+ for (int i = 0; i < t.length(); i++) { + Integer count = need.get(t.charAt(i)); + count = count == null ? 1 : ++count; + need.put(t.charAt(i),count); } + int left =0 , right = 0; + int vaild = 0; + int len = Integer.MAX_VALUE,start = 0; + //最小覆盖字串起始索引 + while (right < s.length()){ + char c = s.charAt(right); + right++; + if (need.containsKey(c)){ + Integer count = window.get(c); + count = count == null ? 1 : ++count; + window.put(c,count); + if (window.get(c).equals(need.get(c))){ + vaild++; + } + } + + //都包含了,right找到了,可以考虑收缩 + while (vaild == need.size()){ + if (right -left < len){ + start = left; + len = right - left; + } + //d是将要移出窗口的字符 + char d = s.charAt(left); + //左移窗口 + left++; + //数据更新 + if (need.containsKey(d)){ + if (window.get(d).equals(need.get(d))){ + vaild--; + } + window.put(d,window.get(d)-1); + } + } + } + return len == Integer.MAX_VALUE + ? "" : s.substring(start,start+len); } } +``` + +### 两数之和 + +```java +import java.util.HashMap; +public class Solution { + public int[] twoSum(int[] nums, int target) { + HashMap map = new HashMap