├── 深入理解计算机网络 ├── README.md └── HTTP请求子过程日志 │ ├── docment │ ├── chrome_F12.png │ ├── chrome_http.png │ └── whole_http.jpg │ ├── pom.xml │ ├── README.md │ └── src │ └── main │ └── OKHTTP3_LOG.java ├── 深入理解JAVA并发与集合 ├── 抽象队列同步器.md ├── 源码分析 │ └── ThreadPoolExecutor.md ├── 思维导图 │ └── 集合容器.png ├── 并发三大特性.md ├── 并发与集合总结.md └── README.md ├── 深入理解分布式微服务 └── README.md ├── 深入理解数据库原理 ├── InnoDB索引与算法.md ├── InnoDB锁与事务.md ├── 数据库基础.md └── README.md ├── 深入理解设计模式 └── README.md ├── 深入理解JAVA虚拟机 ├── 思维导图 │ ├── 类加载机制.png │ ├── 类加载机制.xmind │ ├── 运行时数据区域.png │ └── 运行时数据区域.xmind ├── ByteCodeExample │ ├── getstatic.md │ ├── invokestatic.md │ ├── new.md │ ├── putstatic.md │ └── anewarray.md ├── JVM总结.md └── README.md ├── 深入理解数据结构与算法 ├── leetcode思维导图.jpg ├── FromBook │ ├── problem1_优雅的逆序一个栈 │ │ ├── README.md │ │ └── solve.java │ ├── problem2_求0到N中数字2出现的次数 │ │ ├── README.md │ │ └── Solution.java │ └── problem3_求二叉搜索树的所有生成数组 │ │ ├── README.md │ │ ├── Solution.java │ │ └── Solution2.java ├── Codeforces │ ├── contest1187_problemD_任意升序排序数列A的子串能否变成B │ │ ├── data │ │ │ ├── 1.png │ │ │ └── 1.pages │ │ ├── 题解.md │ │ ├── README.md │ │ └── solve.cpp │ ├── contest1266_problemC_基于最大公约数的矩阵构造题 │ │ ├── README.md │ │ └── solve.cpp │ ├── contest1272_problemF_输出串是两个输入串的母序列且规则 │ │ ├── README.md │ │ ├── solve2.cpp │ │ └── solve1.cpp │ ├── contest1282_problemB2_商品打折券的最大化使用 │ │ ├── README.md │ │ └── solve.cpp │ └── contest1266_problemA_全排列能否被60整除 │ │ ├── README.md │ │ └── solve.cpp ├── Leetcode │ ├── problem31_求解next_permutation │ │ ├── solve1.cpp │ │ ├── README.md │ │ └── solve2.cpp │ ├── problem446_求数列中长度大于2的等差子序列个数 │ │ ├── solve.go │ │ └── README.md │ ├── problem162_求数组的一个极大值 │ │ ├── README.md │ │ └── solve.go │ ├── problem233_求1到X中数字1出现的次数 │ │ ├── README.md │ │ └── solve.cpp │ ├── problem248_求a到b中有多少个翻转对称数 │ │ ├── README.md │ │ └── solve.cpp │ ├── problem15_求和为零的三元组的去重解 │ │ ├── README.md │ │ └── solve.cpp │ ├── problem296_最佳碰头地点 │ │ ├── README.md │ │ └── solve.cpp │ ├── problem815_多条环路公交车线两点间最少上车次数 │ │ ├── README.md │ │ ├── solve1.cpp │ │ └── solve2.cpp │ ├── problem23_合并且排序K个指针表 │ │ ├── README.md │ │ ├── solve.go │ │ └── solve.cpp │ ├── weekly-contest-221_problem2_吃苹果的最大数目 │ │ ├── README.md │ │ └── Solution.java │ ├── problem10_经典模糊串匹配问题 │ │ ├── README.md │ │ └── Solution.java │ ├── weekly-contest-221_problem4_离线求有条件的异或最大值 │ │ └── README.md │ ├── problem4_求两个升序数列的中位数 │ │ ├── README.md │ │ ├── solve1.go │ │ ├── solve2.go │ │ └── solve3.go │ ├── problem514_旋转字符串匹配 │ │ ├── README.md │ │ └── Solution.java │ ├── problem1242_多线程爬虫程序 │ │ ├── README.md │ │ └── Solution.java │ ├── weekly-contest-221_problem3_球会落何处 │ │ ├── README.md │ │ └── Solution.java │ ├── problem65_求一个数是否是有效数 │ │ ├── README.md │ │ └── solve.cpp │ ├── problem1473_求指定条件下房子涂色的最小花费 │ │ ├── README.md │ │ └── solve.go │ └── weekly-contest-205_problem4_计算三次最小生成树 │ │ ├── README.md │ │ └── solve.cpp ├── UESTC │ ├── problem838_线段树单点加法区间求和 │ │ ├── README.md │ │ └── solve.cpp │ └── problem839_线段树区间加法区间求和 │ │ ├── README.md │ │ └── solve.cpp ├── UVA │ └── problem1395_Kruskal算法求连通图的自定义函数解 │ │ ├── README.md │ │ └── solve.cpp ├── 数据结构与算法总结 │ └── binarytree │ │ ├── BinarySearchTree.java │ │ ├── TreeMapDecorator.java │ │ ├── BSTTree.java │ │ ├── AVLTree.java │ │ └── RBTree.java └── README.md ├── README.md └── Linux命令问题记录 └── README.md /深入理解计算机网络/README.md: -------------------------------------------------------------------------------- 1 | # 深入理解计算机网络 2 | -------------------------------------------------------------------------------- /深入理解JAVA并发与集合/抽象队列同步器.md: -------------------------------------------------------------------------------- 1 | 2 | # 抽象队列同步器 3 | -------------------------------------------------------------------------------- /深入理解分布式微服务/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 深入理解分布式微服务 3 | 4 | -------------------------------------------------------------------------------- /深入理解数据库原理/InnoDB索引与算法.md: -------------------------------------------------------------------------------- 1 | 2 | # InnoDB索引与算法 3 | -------------------------------------------------------------------------------- /深入理解数据库原理/InnoDB锁与事务.md: -------------------------------------------------------------------------------- 1 | 2 | # InnoDB锁与事务 3 | -------------------------------------------------------------------------------- /深入理解JAVA并发与集合/源码分析/ThreadPoolExecutor.md: -------------------------------------------------------------------------------- 1 | 2 | # ThreadPoolExecutor 3 | -------------------------------------------------------------------------------- /深入理解设计模式/README.md: -------------------------------------------------------------------------------- 1 | # 深入理解设计模式 2 | 3 | ### 设计模式核心思想 4 | 5 | ### 常用设计模式 6 | 7 | ### 不常用设计模式 8 | 9 | -------------------------------------------------------------------------------- /深入理解JAVA并发与集合/思维导图/集合容器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解JAVA并发与集合/思维导图/集合容器.png -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/思维导图/类加载机制.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解JAVA虚拟机/思维导图/类加载机制.png -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/思维导图/类加载机制.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解JAVA虚拟机/思维导图/类加载机制.xmind -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/思维导图/运行时数据区域.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解JAVA虚拟机/思维导图/运行时数据区域.png -------------------------------------------------------------------------------- /深入理解数据结构与算法/leetcode思维导图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解数据结构与算法/leetcode思维导图.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 由于Github国内访问受限等原因,本Notebook不再更新,但保留历史内容,笔记记录已转移至印象笔记,博文已转移至CSDN:https://blog.csdn.net/UESTC_peterpan 2 | -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/思维导图/运行时数据区域.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解JAVA虚拟机/思维导图/运行时数据区域.xmind -------------------------------------------------------------------------------- /深入理解计算机网络/HTTP请求子过程日志/docment/chrome_F12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解计算机网络/HTTP请求子过程日志/docment/chrome_F12.png -------------------------------------------------------------------------------- /深入理解计算机网络/HTTP请求子过程日志/docment/chrome_http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解计算机网络/HTTP请求子过程日志/docment/chrome_http.png -------------------------------------------------------------------------------- /深入理解计算机网络/HTTP请求子过程日志/docment/whole_http.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解计算机网络/HTTP请求子过程日志/docment/whole_http.jpg -------------------------------------------------------------------------------- /深入理解数据结构与算法/FromBook/problem1_优雅的逆序一个栈/README.md: -------------------------------------------------------------------------------- 1 | # 优雅的逆序一个栈 2 | 3 | 逆序一个栈,额外空间复杂度要求O(1) 4 | 5 | 比如 1 2 3 4 5,栈顶为5,变为 5 4 3 2 1,栈顶为1 6 | 7 | 来源:《程序员代码面试指南》 8 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1187_problemD_任意升序排序数列A的子串能否变成B/data/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解数据结构与算法/Codeforces/contest1187_problemD_任意升序排序数列A的子串能否变成B/data/1.png -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1187_problemD_任意升序排序数列A的子串能否变成B/data/1.pages: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteryuanpan/notebook/HEAD/深入理解数据结构与算法/Codeforces/contest1187_problemD_任意升序排序数列A的子串能否变成B/data/1.pages -------------------------------------------------------------------------------- /深入理解数据库原理/数据库基础.md: -------------------------------------------------------------------------------- 1 | 2 | # 数据库基础 3 | 4 | ### 常用SQL记录 5 | 6 | 查询表结构 7 | 8 | ```sql 9 | select * from information_schema.columns where table_schema = '' and table_name = ''; 10 | ``` 11 | -------------------------------------------------------------------------------- /深入理解JAVA并发与集合/并发三大特性.md: -------------------------------------------------------------------------------- 1 | 2 | # 并发三大特性 3 | 4 | > 本来是计划写两篇文章的,分别是JMM内存模型、线程安全原理,前者解释了为何并发编程中会存在线程不安全问题,后者解释了如何解决线程不安全问题,但沉淀了一段时间后,个人认为二者可以何为一篇文章,且并发编程中谈来谈去,都离不开并发的三大特性:原子性、可见性、有序性。因此就直接以“并发三大特性”作为文章标题了 5 | 6 | > 本文目标:从并发三大特性角度去谈“线程不安全”、“如何保证线程安全”。其中会涉及到JMM内存模型,volatile,CAS,sychronized,ReentrantLock,还会涉及到Redis分布式锁 7 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem31_求解next_permutation/solve1.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | void nextPermutation(vector& nums) { 4 | bool hasNext = next_permutation(nums.begin(), nums.end()); 5 | if (!hasNext) { 6 | sort(nums.begin(), nums.end()); 7 | } 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem446_求数列中长度大于2的等差子序列个数/solve.go: -------------------------------------------------------------------------------- 1 | func numberOfArithmeticSlices(A []int) int { 2 | f := make([]map[int]int, len(A)) 3 | for i := 0; i < len(A); i ++ { 4 | f[i] = make(map[int]int) 5 | } 6 | sum := 0 7 | for i := 0; i < len(A); i ++ { 8 | for j := 0; j < i; j ++ { 9 | d := A[j] - A[i] 10 | f[i][d] += f[j][d] + 1 11 | sum += f[j][d] 12 | } 13 | } 14 | return sum 15 | } 16 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem162_求数组的一个极大值/README.md: -------------------------------------------------------------------------------- 1 | # 求数组的一个极大值 2 | 3 | ### 题意 4 | ``` 5 | 给一个数组A,长度为N,求一个位置 i ,要求比位置 i-1 的元素大 且 比位置 i+1 的元素大 6 | 7 | 数组相邻两个位置元素不相等,可以假设 -1 和 n 两个边界位置是负无穷 8 | 9 | 如果有多个答案,返回任意一个即可 10 | ``` 11 | 12 | ### 条件范围 13 | ``` 14 | 要求算法复杂度为O(log) 15 | ``` 16 | 17 | ### 样例输入 18 | ``` 19 | [1,2,3,1] 20 | [1,2,1,3,5,6,4] 21 | ``` 22 | 23 | ### 样例输出 24 | ``` 25 | 2 26 | 1或5 27 | ``` 28 | 29 | ### 关联链接 30 | 原题:https://leetcode-cn.com/problems/find-peak-element/ 31 | 32 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/FromBook/problem2_求0到N中数字2出现的次数/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 求0到N中数字2出现的次数 3 | 4 | ### 题意 5 | ``` 6 | 编写一个方法,计算从 0 到 n (含 n) 中数字 2 出现的次数。 7 | ``` 8 | 9 | ### 条件范围 10 | ``` 11 | n <= 10^9 12 | ``` 13 | 14 | ### 样例输入 15 | ``` 16 | 25 17 | ``` 18 | 19 | ### 样例输出 20 | ``` 21 | 9 22 | ``` 23 | 24 | ### 样例解释 25 | ``` 26 | (2, 12, 20, 21, 22, 23, 24, 25)(注意 22 应该算作两次) 27 | ``` 28 | 29 | ### 关联链接 30 | 31 | 原题:https://leetcode-cn.com/problems/number-of-2s-in-range-lcci/ 32 | 33 | 来自《程序员面试金典(第6版)》 34 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem233_求1到X中数字1出现的次数/README.md: -------------------------------------------------------------------------------- 1 | # 求1到X中数字1出现的次数 2 | 3 | ### 题意 4 | 5 | 给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。 6 | 7 | ### 条件范围 8 | 9 | n: [0, 2^32) 10 | 11 | ### 样例输入1 12 | ``` 13 | 13 14 | ``` 15 | 16 | ### 样例输出1 17 | ``` 18 | 6 19 | ``` 20 | 21 | ### 样例解释1 22 | ``` 23 | 1、10、11、12、13,一共出现了6次1 24 | ``` 25 | 26 | ### 样例输入2 27 | ``` 28 | 51321 29 | ``` 30 | 31 | ### 样例输出2 32 | ``` 33 | 30795 34 | ``` 35 | 36 | ### 关联链接 37 | 原题:https://leetcode-cn.com/problems/number-of-digit-one/ 38 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem248_求a到b中有多少个翻转对称数/README.md: -------------------------------------------------------------------------------- 1 | # 求a到b中有多少个翻转对称数 2 | 3 | ### 题意 4 | 5 | 中心对称数是指一个数字在旋转了 180 度之后看起来依旧相同的数字(或者上下颠倒地看)。 6 | 7 | 写一个函数来计算范围在 [a, b] 之间中心对称数的个数。 8 | 9 | ### 条件范围 10 | 11 | a : [0, VERY LARGE] 12 | 13 | b : [0, VERY LARGE] 14 | 15 | 由于范围可能很大,所以 a 和 b 都用字符串表示。 16 | 17 | ### 样例输入1 18 | ``` 19 | 50 20 | 10 21 | ``` 22 | ### 样例输出1 23 | ``` 24 | 3 25 | ``` 26 | ### 样例解释1 27 | ``` 28 | 69,88 和 96 是三个在该范围内的中心对称数 29 | ``` 30 | ### 关联链接 31 | 原题:https://leetcode-cn.com/problems/strobogrammatic-number-iii/ 32 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem15_求和为零的三元组的去重解/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 求和为零的三元组的去重解 3 | 4 | ### 题意 5 | 6 | 给一个数列A,长度为N 7 | 8 | 求出所有的三元组(A,B,C),使得 A + B + C = 0 9 | 10 | 求得结果要去重(若存在两个三元组内部按大小排序后相同,则认为它两相同) 11 | 12 | ### 条件范围 13 | 14 | N : [1, 5000] 15 | 16 | A[i] : int整形 17 | 18 | ### 样例输入1 19 | 20 | ``` 21 | -1 0 1 2 -1 -4 22 | ``` 23 | 24 | ### 样例输出1 25 | 26 | ``` 27 | -1 0 1 28 | -1 -1 2 29 | ``` 30 | 31 | ### 样例解释1 32 | 33 | [[-1, 0, 1],[-1, -1, 2]] 及 [[1, -1, 0],[2, -1, -1]] 都算正解,三元组内部顺序不重要 34 | 35 | ### 关联链接 36 | 37 | 原题:https://leetcode.com/problems/3sum/ 38 | -------------------------------------------------------------------------------- /Linux命令问题记录/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Linux命令问题记录 3 | 4 | ### git clone很慢 5 | 6 | 问题 7 | - git clone URL,下载很慢 8 | 9 | 解决办法 10 | - 用https方式,并且域名由github.com改为github.com.cnpmjs.org,这样能加速git clone 11 | - 比如 git clone https://github.com.cnpmjs.org/peteryuanpan/notebook.git 12 | - 但是这样后,git push权限验证可能会受阻,可以用git remote set-url origin git@github.com:peteryuanpan/notebook.git,改回git方式 13 | 14 | ### root用户无法ssh登陆 15 | 16 | 问题 17 | - ssh root@,密码正确但登陆失败 18 | 19 | 解决办法 20 | - sudo vi /etc/ssh/sshd_config 21 | - 找到 # Authentication,在后面添加一行 PermitRootLogin yes,wq保存退出 22 | - sudo service ssh restart 23 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1187_problemD_任意升序排序数列A的子串能否变成B/题解.md: -------------------------------------------------------------------------------- 1 | 2 | ![1.png](data/1.png) 3 | 4 | 如图所示 5 | 6 | 1 9 5 5 3 7 7 | 8 | 1 5 5 3 7 9 9 | 10 | 可以通过如下变换 11 | 12 | [1 9 5 5 3 7] => [1 5 5 9 3 7] => [1 5 5 3 9 7] => [1 5 5 3 7 9] 13 | 14 | 而 15 | 16 | 1 7 5 5 3 9 17 | 18 | 1 5 5 3 9 7 19 | 20 | 则无法实现 21 | 22 | 原因是无论如何排序,都无法令 7 9 变为 9 7 23 | 24 | 因此猜想算法如下: 25 | 26 | 1. 对于 i : [1, N],确认 A[i] 对应的 B[j],即图中的箭头关系。不妨令 r[i] = j。若存在 A[i] 无法对应一个 B[j],则 NO。 27 | 28 | 2. 对于 i : [1, N],若存在 k : [1, i - 1] 且 A[k] : [1, A[i] - 1] 且 r[k] > r[i],则 NO。 29 | 30 | 3. 1、2 无 NO,则 YES。 31 | 32 | 与前缀最大值有关,可使用树状数组实现 33 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/FromBook/problem3_求二叉搜索树的所有生成数组/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 求二叉搜索树的所有生成数组 3 | 4 | ### 题意 5 | ``` 6 | 从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。给定一个由不同节点组成的二叉搜索树,输出所有可能生成此树的数组 7 | ``` 8 | 9 | ### 条件范围 10 | ``` 11 | 未知,但N估计很小,N为节点总数 12 | ``` 13 | 14 | ### 样例输入 15 | ``` 16 | [] 17 | [1] 18 | [2,1,3] 19 | [3,1,2,4,5] 20 | ``` 21 | 22 | ### 样例输出 23 | ``` 24 | [[]] 25 | [[1]] 26 | [[2,1,3],[2,3,1]] 27 | [[3,1,2,4,5],[3,1,2,5,4],[3,1,4,5,2],[3,1,4,2,5],[3,1,5,2,4],[3,1,5,4,2],[3,2,1,4,5],[3,2,1,5,4]] 28 | ``` 29 | 30 | ### 关联链接 31 | 32 | 原题:https://leetcode-cn.com/problems/bst-sequences-lcci/ 33 | 34 | 来自《程序员面试金典(第6版)》 35 | 36 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1266_problemC_基于最大公约数的矩阵构造题/README.md: -------------------------------------------------------------------------------- 1 | # 基于最大公约数的矩阵构造题 2 | 3 | ### 题意 4 | 5 | 构造一个矩阵A,长为R,宽为C 6 | 7 | 设R行及C列的最大公约函数组合成一个数组B(长度为R+C) 8 | 9 | 满足几个条件 10 | - 要求B是distinct的,即不能有重复数字 11 | - 要求B的最大值最小 12 | - 要求A的数值范围在[1, 10^9] 13 | 14 | 如果构造不出来,输出 0 15 | 16 | ### 条件范围 17 | 18 | R : [1, 500] 19 | 20 | C : [1, 500] 21 | 22 | ### 样例输入1 23 | 24 | ``` 25 | 2 2 26 | ``` 27 | 28 | ### 样例输出1 29 | 30 | ``` 31 | 4 12 32 | 2 9 33 | ``` 34 | 35 | ### 样例输入2 36 | 37 | ``` 38 | 1 1 39 | ``` 40 | 41 | ### 样例输出2 42 | 43 | ``` 44 | 0 45 | ``` 46 | 47 | ### 关联链接 48 | 49 | 原题:http://codeforces.com/contest/1266/problem/C 50 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem31_求解next_permutation/README.md: -------------------------------------------------------------------------------- 1 | # 求解next_permutation 2 | 3 | ### 题意 4 | 5 | 给一个排列A(可能含有相同数字),求其全排列(按字典序)中的下一个,即next_permutation 6 | 7 | 如果A是全排列中最后一个,则求全排列第一个 8 | 9 | ### 条件范围 10 | 11 | N : 未知,可以认为是 10^5 12 | 13 | Ai : 未知,可以认为是 int32 14 | 15 | ### 样例输入1 16 | ``` 17 | [1,2,3] 18 | ``` 19 | 20 | ### 样例输出1 21 | ``` 22 | [1,3,2] 23 | ``` 24 | 25 | ### 样例输入2 26 | ``` 27 | 1 3 4 2 28 | ``` 29 | 30 | ### 样例输出2 31 | ``` 32 | 1 4 2 3 33 | ``` 34 | 35 | ### 样例输入3 36 | ``` 37 | 1 3 3 1 38 | ``` 39 | 40 | ### 样例输出3 41 | ``` 42 | 3 1 1 3 43 | ``` 44 | 45 | ### 关联链接 46 | 47 | 原题:https://leetcode.com/problems/next-permutation/ 48 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem296_最佳碰头地点/README.md: -------------------------------------------------------------------------------- 1 | # 最佳碰头地点 2 | 3 | ### 题意 4 | 5 | ``` 6 | 有一队人(两人或以上)想要在一个地方碰面,他们希望能够最小化他们的总行走距离。 7 | 8 | 给你一个 2D 网格,其中各个格子内的值要么是 0,要么是 1。 9 | 10 | 1 表示某个人的家所处的位置。这里,我们将使用 曼哈顿距离 来计算,其中 distance(p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|。 11 | ``` 12 | 13 | ### 条件范围 14 | 15 | n * m : [0, 10^6] 16 | 17 | ### 样例输入1 18 | ``` 19 | [[1,0,0,0,1],[0,0,0,0,0],[0,0,1,0,0]] 20 | ``` 21 | ### 样例输出1 22 | ``` 23 | 6 24 | ``` 25 | ### 样例解释1 26 | ``` 27 | 1 - 0 - 0 - 0 - 1 28 | | | | | | 29 | 0 - 0 - 0 - 0 - 0 30 | | | | | | 31 | 0 - 0 - 1 - 0 - 0 32 | ``` 33 | ### 关联链接 34 | 原题:https://leetcode-cn.com/problems/best-meeting-point/ 35 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/UESTC/problem838_线段树单点加法区间求和/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 线段树单点加法区间求和 3 | 4 | ### 题意 5 | 6 | 给一个数列A,长度为N 7 | 8 | M次操作,type=0或1 9 | 10 | type=0时,给l、r,求A[l] + A[l+1] + ... + A[r]的结果 11 | 12 | type=1时,给x、v,令A[x] = A[x] + v 13 | 14 | ### 条件范围 15 | 16 | N, M : [1, 100000] 17 | 18 | A[i] : [0, 100000] 19 | 20 | type : 0 | 1 21 | 22 | l : [1, r] 23 | 24 | r : [l, N] 25 | 26 | x : [1, N] 27 | 28 | v : [0, 10000] 29 | 30 | ### 样例输入1 31 | 32 | ``` 33 | 5 4 34 | 1 2 3 4 5 35 | 1 2 3 36 | 0 2 4 37 | 1 4 1 38 | 0 1 5 39 | ``` 40 | 41 | ### 样例输出1 42 | 43 | ``` 44 | 12 45 | 19 46 | ``` 47 | 48 | ### 关联链接 49 | 50 | 原题:https://acm.uestc.edu.cn/problem/mu-yi-tian-xia/description 51 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem815_多条环路公交车线两点间最少上车次数/README.md: -------------------------------------------------------------------------------- 1 | # 多条环路公交车线两点间最少上车次数 2 | ``` 3 | 给多条环路公交车线routes,每条公交车线routes[i]都有一辆公交车在行驶,且是环路的,比如[1,2,7],则1=>2=>7=>1=>2... 4 | routes[i]表示的是第i辆公交车的线路 5 | routes[i][j]表示的是站点下标 6 | 给一个S,T,都是站点下标,分别表示起点和终点 7 | 求问S到T最少需要上车多少次 8 | ``` 9 | 10 | ### 题意 11 | ### 条件范围 12 | ``` 13 | 1 <= routes.length <= 500. 14 | 1 <= routes[i].length <= 10^5. 15 | 0 <= routes[i][j] < 10 ^ 6. 16 | ``` 17 | 18 | ### 样例输入1 19 | ``` 20 | [[1, 2, 7], [3, 6, 7]] 21 | 1 22 | 6 23 | ``` 24 | 25 | ### 样例输出1 26 | ``` 27 | 2 28 | ``` 29 | 30 | ### 样例解释1 31 | ``` 32 | 1=>2=>7,上车1次 33 | 7=>3=>6,上次1次 34 | 一共2次 35 | ``` 36 | 37 | ### 关联链接 38 | 原题:https://leetcode-cn.com/problems/bus-routes/ 39 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem23_合并且排序K个指针表/README.md: -------------------------------------------------------------------------------- 1 | # 合并且排序K个指针表 2 | 3 | ### 题意 4 | 5 | 给K个指针表,每个指针表是排好序的 6 | 7 | 要求合并它们,且合并后指针表也是排序的 8 | 9 | 指针表结构体 及 类函数 声明如下 10 | 11 | ``` 12 | /** 13 | * Definition for singly-linked list. 14 | * struct ListNode { 15 | * int val; 16 | * ListNode *next; 17 | * ListNode(int x) : val(x), next(NULL) {} 18 | * }; 19 | */ 20 | class Solution { 21 | public: 22 | ListNode * mergeKLists(vector& lists) { 23 | } 24 | }; 25 | ``` 26 | 27 | ### 条件范围 28 | 29 | K : 未知,可认为10^5范围 30 | 31 | ### 样例输入1 32 | ``` 33 | [[1,4,5],[1,3,4],[2,6]] 34 | ``` 35 | 36 | ### 样例输出1 37 | ``` 38 | [1,1,2,3,4,4,5,6] 39 | ``` 40 | 41 | ### 关联链接 42 | 43 | 原题:https://leetcode-cn.com/problems/merge-k-sorted-lists/ 44 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1272_problemF_输出串是两个输入串的母序列且规则/README.md: -------------------------------------------------------------------------------- 1 | # 输出串是两个输入串的母序列且规则 2 | 3 | ### 题意 4 | 5 | 给定两个串A、B,长度分别为L1、L2 6 | 7 | 要求输出一个串C,满足两个条件 8 | 9 | - A、B均是C的子序列 10 | - C是规则的 11 | 12 | 一个串是规则的,定义如下 13 | 14 | - ()是规则的 15 | - 如果一个串S是规则的,则(S)是规则的 16 | - 如果串S、T均是规则的,则ST是规则的 17 | 18 | 如果有多个解,输出任意一个即可 19 | 20 | ### 条件范围 21 | 22 | L1、L2:[1, 200] 23 | 24 | ### 样例输入1 25 | ``` 26 | (())(() 27 | ()))() 28 | ``` 29 | 30 | ### 样例输出1 31 | ``` 32 | (())()() 33 | ``` 34 | 35 | ### 样例输入2 36 | ``` 37 | ) 38 | (( 39 | ``` 40 | 41 | ### 样例输出2 42 | ``` 43 | ((())) 44 | ``` 45 | 46 | ### 样例输入3 47 | ``` 48 | ()) 49 | (()(()(()( 50 | ``` 51 | 52 | ### 样例输出3 53 | ``` 54 | (()()()(()())) 55 | ``` 56 | 57 | ### 关联链接 58 | 59 | 原题:http://codeforces.com/contest/1272/problem/F 60 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem162_求数组的一个极大值/solve.go: -------------------------------------------------------------------------------- 1 | func toLeft(i int, nums []int) int { 2 | if i == 0 || nums[i-1] < nums[i] { 3 | return 1 4 | } 5 | return -1 6 | } 7 | func toRight(i int, nums []int) int { 8 | if i + 1 == len(nums) || nums[i] > nums[i+1] { 9 | return 1 10 | } 11 | return -1 12 | } 13 | func findPeakElement(nums []int) int { 14 | l := 0 15 | r := len(nums) - 1 16 | for { 17 | if l == r { 18 | break 19 | } 20 | m := (l + r) / 2 21 | tl := toLeft(m, nums) 22 | tr := toRight(m, nums) 23 | if tl > 0 && tr > 0 { 24 | return m 25 | } 26 | if tl > 0 && tr < 0 { 27 | l = m + 1 28 | } else { 29 | r = m 30 | } 31 | } 32 | return l 33 | } 34 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem446_求数列中长度大于2的等差子序列个数/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 求数列中长度大于2的等差子序列个数 3 | 4 | # 题目 5 | ### 题意 6 | ``` 7 | 给一个数列A,长度为N 8 | 求有多少个子序列满足 9 | (1)长度大于2 10 | (2)是等差数列 11 | ``` 12 | 13 | ### 条件范围 14 | ``` 15 | N: [0, 1000] 16 | A[i]: [-2^31, 2^31-1] 17 | 保证答案小于 2^31 - 1 18 | ``` 19 | 20 | ### 样例输入1 21 | ``` 22 | [2, 4, 6, 8, 10] 23 | ``` 24 | 25 | ### 样例输出1 26 | ``` 27 | 7 28 | ``` 29 | 30 | ### 样例解释1 31 | ``` 32 | [2, 4, 6] 33 | [4, 6, 8] 34 | [6, 8, 10] 35 | [2, 4, 6, 8] 36 | [4, 6, 8, 10] 37 | [2, 4, 6, 8, 10] 38 | [2, 6, 10] 39 | ``` 40 | 41 | ### 样例输入2 42 | ``` 43 | [3, 2, 1, 1, 2, 3] 44 | ``` 45 | 46 | ### 样例输出2 47 | ``` 48 | 4 49 | ``` 50 | 51 | ### 样例解释2 52 | ``` 53 | [3, 2, 1] 54 | [3, 2, 1] 55 | [1, 2, 3] 56 | [1, 2, 3] 57 | ``` 58 | 59 | ### 关联链接 60 | 原题:https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence/ 61 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/weekly-contest-221_problem2_吃苹果的最大数目/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 吃苹果的最大数目 3 | 4 | ### 题意 5 | ``` 6 | 有一棵特殊的苹果树,一连 n 天,每天都可以长出若干个苹果。在第 i 天,树上会长出 apples[i] 个苹果,这些苹果将会在 days[i] 天后(也就是说,第 i + days[i] 天时)腐烂,变得无法食用。也可能有那么几天,树上不会长出新的苹果,此时用 apples[i] == 0 且 days[i] == 0 表示。 7 | 8 | 你打算每天 最多 吃一个苹果来保证营养均衡。注意,你可以在这 n 天之后继续吃苹果。 9 | 10 | 给你两个长度为 n 的整数数组 days 和 apples ,返回你可以吃掉的苹果的最大数目。 11 | ``` 12 | 13 | ### 条件范围 14 | ``` 15 | apples.length == n 16 | days.length == n 17 | 1 <= n <= 2 * 10e4 18 | 0 <= apples[i], days[i] <= 2 * 10e4 19 | 只有在 apples[i] = 0 时,days[i] = 0 才成立 20 | ``` 21 | 22 | ### 样例输入 23 | ``` 24 | [1,2,3,5,2] 25 | [3,2,1,4,2] 26 | [3,0,0,0,0,2] 27 | [3,0,0,0,0,2] 28 | ``` 29 | 30 | ### 样例输出 31 | ``` 32 | 7 33 | 5 34 | ``` 35 | 36 | ### 关联链接 37 | 原题:https://leetcode-cn.com/contest/weekly-contest-221/problems/maximum-number-of-eaten-apples/ 38 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem10_经典模糊串匹配问题/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 经典模糊串匹配问题 3 | 4 | ### 题意 5 | ``` 6 | 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配 7 | 8 | '.' 匹配任意单个字符 9 | '*' 匹配零个或多个前面的那一个元素 10 | 所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串 11 | ``` 12 | 13 | ### 条件范围 14 | ``` 15 | 0 <= s.length <= 20 16 | 0 <= p.length <= 30 17 | s 可能为空,且只包含从 a-z 的小写字母。 18 | p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 * 19 | 保证每次出现字符 * 时,前面都匹配到有效的字符 20 | ``` 21 | 22 | ### 样例输入 23 | ``` 24 | "aa" 25 | "a" 26 | "aa" 27 | "a*" 28 | "ab" 29 | ".*" 30 | "aab" 31 | "c*a*b" 32 | "mississippi" 33 | "mis*is*p*." 34 | "aaaa" 35 | "**" 36 | "aaa" 37 | ".*" 38 | "aaa" 39 | "a*" 40 | "a" 41 | "b*a" 42 | ``` 43 | 44 | ### 样例输出 45 | ``` 46 | false 47 | true 48 | true 49 | true 50 | false 51 | false 52 | true 53 | true 54 | true 55 | ``` 56 | 57 | ### 关联链接 58 | 原题:https://leetcode-cn.com/problems/regular-expression-matching/ 59 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/weekly-contest-221_problem4_离线求有条件的异或最大值/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 离线求有条件的异或最大值 3 | 4 | ### 题意 5 | ``` 6 | 给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries ,其中 queries[i] = [xi, mi] 。 7 | 8 | 第 i 个查询的答案是 xi 和任何 nums 数组中不超过 mi 的元素按位异或(XOR)得到的最大值。换句话说,答案是 max(nums[j] XOR xi) ,其中所有 j 均满足 nums[j] <= mi 。如果 nums 中的所有元素都大于 mi,最终答案就是 -1 。 9 | 10 | 返回一个整数数组 answer 作为查询的答案,其中 answer.length == queries.length 且 answer[i] 是第 i 个查询的答案。 11 | ``` 12 | 13 | ### 条件范围 14 | ``` 15 | 1 <= nums.length, queries.length <= 10e5 16 | queries[i].length == 2 17 | 0 <= nums[j], xi, mi <= 10e9 18 | ``` 19 | 20 | ### 样例输入 21 | ``` 22 | [0,1,2,3,4] 23 | [[3,1],[1,3],[5,6]] 24 | [5,2,4,6,6,3] 25 | [[12,4],[8,1],[6,3]] 26 | ``` 27 | 28 | ### 样例输出 29 | ``` 30 | [3,3,7] 31 | [15,-1,5] 32 | ``` 33 | 34 | ### 关联链接 35 | 原题:https://leetcode-cn.com/contest/weekly-contest-221/problems/maximum-xor-with-an-element-from-array/ 36 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1187_problemD_任意升序排序数列A的子串能否变成B/README.md: -------------------------------------------------------------------------------- 1 | # 任意升序排序数列A的子串能否变成B 2 | 3 | ### 题意 4 | 5 | 给一个数列A,长度为N 6 | 7 | 给一个数列B,长度为N 8 | 9 | 你可以选择A[l..r](A的一个连续子串)进行任意次数的升序排序 10 | 11 | 请问可否使得A变为B? 12 | 13 | ### 条件范围 14 | 15 | T组输入,T : [1, 3 x 10^5] 16 | 17 | N : [1, 3 x 10^5] 18 | 19 | 所有组的N之和 : [1, 3 x 10^5] 20 | 21 | A[i] : [1, 3 x 10^5] 22 | 23 | B[i] : [1, 3 x 10^5] 24 | 25 | l : [1, N] 26 | 27 | r : [l, N] 28 | 29 | ### 样例输入1 30 | 31 | ``` 32 | 4 33 | 7 34 | 1 7 1 4 4 5 6 35 | 1 1 4 4 5 7 6 36 | 5 37 | 1 1 3 3 5 38 | 1 1 3 3 5 39 | 2 40 | 1 1 41 | 1 2 42 | 3 43 | 1 2 3 44 | 3 2 1 45 | ``` 46 | 47 | ### 样例输出1 48 | 49 | ``` 50 | YES 51 | YES 52 | NO 53 | NO 54 | ``` 55 | 56 | ### 样例解释1 57 | 58 | A = [1, 7, 1, 4, 4, 5, 6],先排序A[1..5],变成[1, 1, 4, 4, 7, 5, 6],再排序A[5..6],变成[1, 1, 4, 4, 5, 7, 6],即是B 59 | 60 | ### 关联链接 61 | 62 | 原题:https://codeforces.com/contest/1187/problem/D 63 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem4_求两个升序数列的中位数/README.md: -------------------------------------------------------------------------------- 1 | # 求两个升序数列的中位数 2 | 3 | ### 题意 4 | ``` 5 | 给两个升序数列A,B,长度分别为N1,N2,求两个数列的中位数 6 | 暴力的做法就是:将两个数列合并后,排序,求中位数 7 | 请用O(log)时间复杂度解决 8 | ``` 9 | 10 | ### 条件范围 11 | ``` 12 | N1, N2 : [0, 1000] 13 | N1 + N2 : [1, 2000] 14 | A[i], B[i] : [-10^6, 10^6] 15 | ``` 16 | 17 | ### 样例输入 18 | ``` 19 | [1,3] 20 | [2] 21 | [1, 2] 22 | [3, 4] 23 | [0, 0] 24 | [0, 0] 25 | [] 26 | [1] 27 | [2] 28 | [] 29 | [1, 3, 7, 9, 10] 30 | [1, 2, 3, 4, 5] 31 | [1, 2, 3, 4, 5] 32 | [1, 3, 7, 9, 10] 33 | [1, 2, 3, 4, 5, 6, 7] 34 | [1] 35 | [] 36 | [2, 3] 37 | [3] 38 | [-2, -1] 39 | [100001] 40 | [100000] 41 | [100000] 42 | [100001] 43 | ``` 44 | 45 | ### 样例输出 46 | ``` 47 | 2.00000 48 | 2.50000 49 | 0.00000 50 | 1.00000 51 | 2.00000 52 | 3.50000 53 | 3.50000 54 | 3.50000 55 | 2.50000 56 | -1.00000 57 | 100000.50000 58 | 100000.50000 59 | ``` 60 | 61 | ### 关联链接 62 | 63 | 原题:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/ 64 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/UESTC/problem839_线段树区间加法区间求和/README.md: -------------------------------------------------------------------------------- 1 | # 线段树区间加法区间求和 2 | 3 | ### 题意 4 | 5 | 给一个数列A,长度为N 6 | 7 | M次操作,type=0或1 8 | 9 | type=0时,给l、r,求A[l] + A[l+1] + ... + A[r]的结果 10 | 11 | type=1时,给l、r、v,令A[l] + A[l+1] + ... + A[r]的值都加v 12 | 13 | ### 条件范围 14 | 15 | N, M : [1, 100000] 16 | 17 | A[i] : [0, 10000] 18 | 19 | type : 0 | 1 20 | 21 | l : [1, r] 22 | 23 | r : [l, N] 24 | 25 | v : [0, 10000] 26 | 27 | ### 样例输入1 28 | 29 | ``` 30 | 5 4 31 | 1 2 3 4 5 32 | 1 2 3 2 33 | 0 3 4 34 | 1 4 5 3 35 | 0 2 4 36 | ``` 37 | 38 | ### 样例输出1 39 | 40 | ``` 41 | 9 42 | 16 43 | ``` 44 | 45 | ### 样例输入2 46 | 47 | ``` 48 | 10 10 49 | 0 0 0 0 0 0 0 0 0 0 50 | 1 1 10 1 51 | 1 1 2 3 52 | 0 1 10 53 | 1 1 10 1 54 | 1 5 7 2 55 | 0 3 9 56 | 1 10 10 10 57 | 1 2 6 3 58 | 0 3 7 59 | 0 2 9 60 | ``` 61 | 62 | ### 样例输出2 63 | 64 | ``` 65 | 16 66 | 20 67 | 28 68 | 40 69 | ``` 70 | 71 | ### 关联链接 72 | 73 | 原题:https://acm.uestc.edu.cn/problem/dong-feng-bu-yu-zhou-lang-bian/description 74 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1282_problemB2_商品打折券的最大化使用/README.md: -------------------------------------------------------------------------------- 1 | # 商品打折券的最大化使用 2 | 3 | ### 题意 4 | 5 | T组输入 6 | 7 | 有N个商品,每个商品价格为Ai(块),手上有P块钱 8 | 9 | 有任意多个打折券,功能是选择 K 个商品,只付最贵的商品钱 10 | 11 | 因此,购买方式有两种(可以使用任意次数),如下: 12 | 13 | - 不使用打折券,则一次买一个商品,买完后 P := P - Ai 14 | - 使用打折券,则选择 K 个商品,只付其中最贵的,买完后 P := P - Amax(Amax是选定的K个商品中最高价) 15 | 16 | 要求使用最优策略,令购买到的商品数量最多 17 | 18 | ### 条件范围 19 | 20 | T : [1, 10^4] 21 | 22 | N : [2, 2 * 10^5] 23 | 24 | SUM(N) for all T : [2, 2 * 10^5] 25 | 26 | Ai : [1, 10^4] 27 | 28 | P : [1, 2 * 10^9] 29 | 30 | K : [2, N] 31 | 32 | ### 样例输入1 33 | ``` 34 | 8 35 | 5 6 2 36 | 2 4 3 5 7 37 | 5 11 2 38 | 2 4 3 5 7 39 | 3 2 3 40 | 4 2 6 41 | 5 2 3 42 | 10 1 3 9 2 43 | 2 10000 2 44 | 10000 10000 45 | 2 9999 2 46 | 10000 10000 47 | 4 6 4 48 | 3 2 3 2 49 | 5 5 3 50 | 1 2 2 1 2 51 | ``` 52 | 53 | ### 样例输出1 54 | ``` 55 | 3 56 | 4 57 | 1 58 | 1 59 | 2 60 | 0 61 | 4 62 | 5 63 | ``` 64 | 65 | ### 关联链接 66 | 67 | 原题:http://codeforces.com/contest/1282/problem/B2 68 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem514_旋转字符串匹配/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 旋转字符串匹配 3 | 4 | ### 题意 5 | ``` 6 | 电子游戏“辐射4”中,任务“通向自由”要求玩家到达名为“Freedom Trail Ring”的金属表盘,并使用表盘拼写特定关键词才能开门。 7 | 8 | 给定一个字符串 ring,表示刻在外环上的编码;给定另一个字符串 key,表示需要拼写的关键词。您需要算出能够拼写关键词中所有字符的最少步数。 9 | 10 | 最初,ring 的第一个字符与12:00方向对齐。您需要顺时针或逆时针旋转 ring 以使 key 的一个字符在 12:00 方向对齐,然后按下中心按钮,以此逐个拼写完 key 中的所有字符。 11 | 12 | 旋转 ring 拼出 key 字符 key[i] 的阶段中: 13 | 14 | 您可以将 ring 顺时针或逆时针旋转一个位置,计为1步。旋转的最终目的是将字符串 ring 的一个字符与 12:00 方向对齐,并且这个字符必须等于字符 key[i] 。 15 | 如果字符 key[i] 已经对齐到12:00方向,您需要按下中心按钮进行拼写,这也将算作 1 步。按完之后,您可以开始拼写 key 的下一个字符(下一阶段), 直至完成所有拼写。 16 | ``` 17 | 18 | ### 条件范围 19 | ``` 20 | ring 和 key 的字符串长度取值范围均为 1 至 100; 21 | 两个字符串中都只有小写字符,并且均可能存在重复字符; 22 | 字符串 key 一定可以由字符串 ring 旋转拼出。 23 | ``` 24 | 25 | ### 样例输入 26 | ``` 27 | "g" 28 | "gggggg" 29 | "godding" 30 | "gd" 31 | "awefwebaerfgwcewefwevweafwedc" 32 | "abcdef" 33 | ``` 34 | 35 | ### 样例输出 36 | ``` 37 | 6 38 | 4 39 | 23 40 | ``` 41 | 42 | ### 关联链接 43 | 原题:https://leetcode-cn.com/problems/freedom-trail/ 44 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/FromBook/problem1_优雅的逆序一个栈/solve.java: -------------------------------------------------------------------------------- 1 | package algorithm; 2 | 3 | import java.util.Stack; 4 | 5 | public class StackReverse { 6 | 7 | public static void dfs(Stack s) { 8 | if (s.empty()) 9 | return; 10 | int t = dfs2(s); 11 | dfs(s); 12 | s.push(t); 13 | } 14 | 15 | public static int dfs2(Stack s) { 16 | int t = s.pop(); 17 | if (!s.empty()) { 18 | int r = dfs2(s); 19 | s.push(t); 20 | return r; 21 | } else 22 | return t; 23 | } 24 | 25 | public static void main(String[] args) { 26 | Stack s = new Stack<>(); 27 | s.push(1); 28 | s.push(2); 29 | s.push(3); 30 | s.push(4); 31 | s.push(5); 32 | 33 | dfs(s); 34 | 35 | while (!s.empty()) { 36 | System.out.println(s.pop()); 37 | } 38 | } 39 | } 40 | 41 | /* output 42 | 1 43 | 2 44 | 3 45 | 4 46 | 5 47 | * */ 48 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1187_problemD_任意升序排序数列A的子串能否变成B/solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define lowbit(x) x&-x 8 | using namespace std; 9 | const int N=3e5+50; 10 | int t,n,a[N],b[N],p[N],f[N]; 11 | std::vectorv[N]; 12 | 13 | void change(int x,int k){ 14 | for(;x<=n;x+=lowbit(x))f[x]=max(f[x],k); 15 | } 16 | 17 | int ask(int x){ 18 | int ret=0; 19 | for(;x;x^=lowbit(x))ret=max(ret,f[x]); 20 | return ret; 21 | } 22 | 23 | int main(){ 24 | scanf("%d",&t); 25 | while(t--){ 26 | scanf("%d",&n);bool flag=0; 27 | for(int i=1;i<=n;i++)scanf("%d",&a[i]),v[a[i]].clear(); 28 | for(int i=1;i<=n;i++)scanf("%d",&b[i]),v[b[i]].push_back(i); 29 | for(int i=n;i;i--){ 30 | if(!v[a[i]].size())flag=1; 31 | else p[i]=v[a[i]][v[a[i]].size()-1],v[a[i]].pop_back(); 32 | } 33 | for(int i=1;i<=n;i++)f[i]=0; 34 | for(int i=1;i<=n;i++){ 35 | if(ask(a[i]-1)>p[i])flag=1; 36 | change(a[i],p[i]); 37 | } 38 | puts(flag?"NO":"YES"); 39 | } 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/ByteCodeExample/getstatic.md: -------------------------------------------------------------------------------- 1 | # getstatic 2 | 3 | 获取指定类的静态域,并将其值压入栈顶 4 | 5 | ### code1 6 | 7 | ```java 8 | package com.peter.jvm.example; 9 | 10 | public class ByteCodeGetStaticTest1 { 11 | 12 | public static void main(String[] args) { 13 | String a = ByteCodeGetStaticTest11.a; 14 | String b = ByteCodeGetStaticTest11.a; 15 | } 16 | } 17 | 18 | class ByteCodeGetStaticTest11 { 19 | 20 | static { 21 | System.out.println("11"); 22 | } 23 | 24 | public static String a = "22"; 25 | 26 | static { 27 | System.out.println("33"); 28 | } 29 | } 30 | ``` 31 | 32 | 输出结果 33 | ``` 34 | 11 35 | 33 36 | ``` 37 | 38 | 字节码 39 | ``` 40 | 0 getstatic #2 41 | 3 astore_1 42 | 4 getstatic #2 43 | 7 astore_2 44 | 8 return 45 | ``` 46 | 47 | 解释 48 | 49 | String a = ByteCodeGetStaticTest11.a; 对应 getstatic ByteCodeGetStaticTest11.a,会触发ByteCodeGetStaticTest11的类加载,输出11、33 50 | 51 | String b = ByteCodeGetStaticTest11.a; 对应 getstatic ByteCodeGetStaticTest11.a,会尝试ByteCodeGetStaticTest11的类加载,发现加载过了,就不加载了 52 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1266_problemA_全排列能否被60整除/README.md: -------------------------------------------------------------------------------- 1 | # 全排列能否被60整除 2 | 3 | ### 题意 4 | 5 | T组输入,给一串数字,长度为N,字符集集合为C,请问全排列中是否存在一个排列能被60整除 6 | 7 | ### 条件范围 8 | 9 | T : [1, 418] 10 | 11 | N : [2, 100] 12 | 13 | C : {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 14 | 15 | ### 样例输入1 16 | 17 | ``` 18 | 6 19 | 603 20 | 006 21 | 205 22 | 228 23 | 1053 24 | 0000000000000000000000000000000000000000000000 25 | ``` 26 | 27 | ### 样例输出1 28 | 29 | ``` 30 | red 31 | red 32 | cyan 33 | cyan 34 | cyan 35 | red 36 | ``` 37 | 38 | ### 样例解释1 39 | 40 | ``` 41 | In the first example, there is one rearrangement that yields a number divisible by 60, and that is 360. 42 | In the second example, there are two solutions. One is 060 and the second is 600. 43 | In the third example, there are 6 possible rearrangments: 025, 052, 205, 250, 502, 520. None of these numbers is divisible by 60. 44 | In the fourth example, there are 3 rearrangements: 228, 282, 822. 45 | In the fifth example, none of the 24 rearrangements result in a number divisible by 60. 46 | In the sixth example, note that 000…0 is a valid solution. 47 | ``` 48 | 49 | ### 关联链接 50 | 51 | 原题:http://codeforces.com/contest/1266/problem/A 52 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem31_求解next_permutation/solve2.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | void nextPermutation(vector& nums) { 4 | bool hasNext = impl_next_permutation(nums); 5 | if (!hasNext) { 6 | sort(nums.begin(), nums.end()); 7 | } 8 | } 9 | 10 | bool impl_next_permutation(vector& nums) { 11 | for (int i = nums.size() - 2; i >= 0; i --) { 12 | if (nums[i] < nums[i+1]) { 13 | // 从右往左找到第一个大于nums[i[的数,与其交换 14 | int l = i + 1, r = nums.size() - 1; 15 | while (l < r) { 16 | int mid = (l + r) / 2 + 1; 17 | if (nums[mid] <= nums[i]) r = mid - 1; 18 | else l = mid; 19 | } 20 | swap(nums[i], nums[l]); 21 | // 将 nums[i](不包括)往后的数列 Reverse 一下 22 | l = i + 1, r = nums.size() - 1; 23 | while (l < r) { 24 | swap(nums[l], nums[r]); 25 | l ++, r --; 26 | } 27 | return true; 28 | } 29 | } 30 | return false; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1266_problemA_全排列能否被60整除/solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | bool solve(char * s) { 11 | int lens = (int) strlen(s); 12 | int zero_i = -1; 13 | for (int i = 0; i < lens; i ++) { 14 | if (s[i] == '0') { 15 | zero_i = i; 16 | break; 17 | } 18 | } 19 | if (zero_i == -1) return false; 20 | int m = 0; 21 | for (int i = 0; i < lens; i ++) { 22 | if (i == zero_i) continue; 23 | int t = s[i] - '0'; 24 | m = (m + (t * 4)) % 6; 25 | } 26 | for (int i = 0; i < lens; i ++) { 27 | if (i == zero_i) continue; 28 | int t = s[i] - '0'; 29 | int x = (-3 * t) % 6; 30 | if ((m + x) % 6 == 0) return true; 31 | } 32 | return false; 33 | } 34 | 35 | int main() { 36 | int n; 37 | cin >> n; 38 | for (int ii = 0; ii < n; ii ++) { 39 | char s[105]; 40 | cin >> s; 41 | printf("%s\n", solve(s) ? "red" : "cyan"); 42 | } 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/ByteCodeExample/invokestatic.md: -------------------------------------------------------------------------------- 1 | 2 | # invokestatic 3 | 4 | 调用静态方法 5 | 6 | ### code1 7 | 8 | ```java 9 | package com.peter.jvm.example; 10 | 11 | public class ByteCodeInvokeStaticTest1 { 12 | 13 | public static void main(String[] args) { 14 | ByteCodeInvokeStaticTest11.aa(); 15 | } 16 | } 17 | 18 | class ByteCodeInvokeStaticTest11 { 19 | 20 | { 21 | System.out.println("33"); 22 | } 23 | 24 | static { 25 | System.out.println("44"); 26 | } 27 | 28 | static void aa() { 29 | System.out.println(a); 30 | } 31 | 32 | static { 33 | System.out.println("66"); 34 | } 35 | 36 | public static String a = new String("11"); 37 | 38 | public String b = new String("22"); 39 | 40 | void bb() { 41 | System.out.println("55"); 42 | } 43 | } 44 | ``` 45 | 46 | 输出结果 47 | ``` 48 | 44 49 | 66 50 | 11 51 | ``` 52 | 53 | 字节码 54 | ``` 55 | 0 invokestatic #2 56 | 3 return 57 | ``` 58 | 59 | 解释 60 | 61 | ByteCodeInvokeStaticTest11.aa(); 对应着 invokestatic ByteCodeInvokeStaticTest11.aa,会先触发对ByteCodeInvokeStaticTest11类加载,输出44、66,再invoke aa,输出11 62 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1282_problemB2_商品打折券的最大化使用/solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | const int N = 200010; 11 | 12 | int a[N]; 13 | int n, p, k; 14 | 15 | int dp[N]; 16 | 17 | int solve() { 18 | sort(a + 1, a + 1 + n); 19 | for (int i = 1; i <= n; i ++) { 20 | dp[i] = -1; 21 | } 22 | dp[0] = p; 23 | for (int i = 1; i <= n; i ++) { 24 | int t; 25 | t = dp[i-1] - a[i]; 26 | if (t >= 0) dp[i] = max(dp[i], t); 27 | if (i-k >= 0) { 28 | t = dp[i-k] - a[i]; 29 | if (t >= 0) dp[i] = max(dp[i], t); 30 | } 31 | } 32 | int m = 0; 33 | for (int i = n; i >= 1; i --) { 34 | if (dp[i] >= 0) m = max(m, i); 35 | } 36 | return m; 37 | } 38 | 39 | int main() { 40 | int t; 41 | cin >> t; 42 | while (t --) { 43 | cin >> n >> p >> k; 44 | for (int i = 1; i <= n; i ++) { 45 | scanf("%d", a + i); 46 | } 47 | cout << solve() << endl; 48 | } 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/ByteCodeExample/new.md: -------------------------------------------------------------------------------- 1 | # new 2 | 3 | 创建一个对象,并将其引用值压入栈顶 4 | 5 | ### code1 6 | 7 | ```java 8 | package com.peter.jvm.example; 9 | 10 | public class ByteCodeNewTest1 { 11 | 12 | public static void main(String[] args) { 13 | ByteCodeNewTest1 t = new ByteCodeNewTest1(); 14 | } 15 | } 16 | 17 | class ByteCodeNewTest12 { 18 | 19 | static { 20 | System.out.println("11"); 21 | } 22 | 23 | ByteCodeNewTest12() { 24 | System.out.println(a); 25 | a = "22"; 26 | System.out.println(a); 27 | } 28 | 29 | static String a = "33"; 30 | 31 | static { 32 | System.out.println(a); 33 | a = "44"; 34 | System.out.println(a); 35 | } 36 | } 37 | ``` 38 | 39 | 输出结果 40 | ``` 41 | 11 42 | 33 43 | 44 44 | 44 45 | 22 46 | ``` 47 | 48 | 字节码 49 | ``` 50 | 0 new #2 51 | 3 dup 52 | 4 invokespecial #3 > 53 | 7 astore_1 54 | 8 return 55 | ``` 56 | 57 | 解释 58 | 59 | ByteCodeNewTest12 t = new ByteCodeNewTest12(); 对应 new ByteCodeNewTest12,ByteCodeNewTest12,输出11、a、a,第一个a是33,第二个a是44,然后调用构造函数,invokespecial ByteCodeNewTest12.init,输出a、a,第一个a是44,第二个a是22 60 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem1242_多线程爬虫程序/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 多线程爬虫程序 3 | 4 | ### 题意 5 | ``` 6 | 给你一个初始地址 startUrl 和一个 HTML 解析器接口 HtmlParser,请你实现一个 多线程的网页爬虫,用于获取与 startUrl 有 相同主机名 的所有链接。 7 | htmlParser.getUrls(startUrl) 可以返回 List urls,表示爬虫结果 8 | 爬虫过程是一个BFS搜索的过程 9 | 爬虫过程中,只请求与 startUrl 同域名的链接,即若遇到非同域名的URL,则不继续爬虫 10 | 请返回以List的形式,返回爬虫结果 11 | ``` 12 | 13 | ### 条件范围 14 | ``` 15 | 1 <= urls.length <= 1000 16 | 1 <= urls[i].length <= 300 17 | startUrl 是 urls 中的一个 18 | 主机名的长度必须为 1 到 63 个字符(包括点 . 在内),只能包含从 “a” 到 “z” 的 ASCII 字母和 “0” 到 “9” 的数字,以及中划线 “-”。 19 | 主机名开头和结尾不能是中划线 “-” 20 | 参考资料:https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames 21 | 你可以假设路径都是不重复的 22 | ``` 23 | 24 | ### 样例输入 25 | ``` 26 | ["http://news.yahoo.com","http://news.yahoo.com/news","http://news.yahoo.com/news/topics/","http://news.google.com","http://news.yahoo.com/us"] 27 | [[2,0],[2,1],[3,2],[3,1],[0,4]] 28 | "http://news.yahoo.com/news/topics/" 29 | ``` 30 | 31 | ### 样例输出 32 | ``` 33 | ["http://news.yahoo.com","http://news.yahoo.com/news","http://news.yahoo.com/news/topics/","http://news.yahoo.com/us"] 34 | ``` 35 | 36 | ### 样例解释 37 | ``` 38 | ``` 39 | 40 | ### 关联链接 41 | 原题:https://leetcode-cn.com/problems/web-crawler-multithreaded/ 42 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/weekly-contest-221_problem3_球会落何处/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 球会落何处 3 | 4 | ### 题意 5 | ``` 6 | 用一个大小为 m x n 的二维网格 grid 表示一个箱子。你有 n 颗球。箱子的顶部和底部都是开着的。 7 | 8 | 箱子中的每个单元格都有一个对角线挡板,跨过单元格的两个角,可以将球导向左侧或者右侧。 9 | 10 | 将球导向右侧的挡板跨过左上角和右下角,在网格中用 1 表示。 11 | 将球导向左侧的挡板跨过右上角和左下角,在网格中用 -1 表示。 12 | 在箱子每一列的顶端各放一颗球。每颗球都可能卡在箱子里或从底部掉出来。如果球恰好卡在两块挡板之间的 "V" 形图案,或者被一块挡导向到箱子的任意一侧边上,就会卡住。 13 | 14 | 返回一个大小为 n 的数组 answer ,其中 answer[i] 是球放在顶部的第 i 列后从底部掉出来的那一列对应的下标,如果球卡在盒子里,则返回 -1 15 | ``` 16 | 17 | ### 条件范围 18 | ``` 19 | m == grid.length 20 | n == grid[i].length 21 | 1 <= m, n <= 100 22 | grid[i][j] 为 1 或 -1 23 | ``` 24 | 25 | ### 样例输入 26 | ``` 27 | [[1,1,1,-1,-1],[1,1,1,-1,-1],[-1,-1,-1,1,1],[1,1,1,1,-1],[-1,-1,-1,-1,-1]] 28 | ``` 29 | 30 | ### 样例输出 31 | ``` 32 | [1,-1,-1,-1,-1] 33 | ``` 34 | 35 | ### 样例解释 36 | ``` 37 | 解释:示例如图: 38 | b0 球开始放在第 0 列上,最终从箱子底部第 1 列掉出。 39 | b1 球开始放在第 1 列上,会卡在第 2、3 列和第 1 行之间的 "V" 形里。 40 | b2 球开始放在第 2 列上,会卡在第 2、3 列和第 0 行之间的 "V" 形里。 41 | b3 球开始放在第 3 列上,会卡在第 2、3 列和第 0 行之间的 "V" 形里。 42 | b4 球开始放在第 4 列上,会卡在第 2、3 列和第 1 行之间的 "V" 形里。 43 | ``` 44 | 45 | ![image](https://user-images.githubusercontent.com/10209135/103167011-74e39980-4862-11eb-8136-1e0f03dd4b95.png) 46 | 47 | ### 关联链接 48 | 原题:https://leetcode-cn.com/problems/where-will-the-ball-fall/ 49 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem23_合并且排序K个指针表/solve.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * type ListNode struct { 4 | * Val int 5 | * Next *ListNode 6 | * } 7 | */ 8 | func merge2Lists(a *ListNode, b *ListNode) *ListNode { 9 | var c *ListNode = new(ListNode); 10 | var d *ListNode = c; 11 | for { 12 | if a == nil && b == nil { 13 | break 14 | } 15 | if b == nil || (a != nil && a.Val < b.Val) { 16 | c.Next = a 17 | c = c.Next 18 | a = a.Next 19 | } else { 20 | c.Next = b 21 | c = c.Next 22 | b = b.Next 23 | } 24 | } 25 | return d.Next; 26 | } 27 | func mergeKLists(lists []*ListNode) *ListNode { 28 | if len(lists) == 0 { 29 | return nil 30 | } 31 | if len(lists) == 1 { 32 | return lists[0] 33 | } 34 | var i int 35 | var merges []*ListNode 36 | for i = 0; i < len(lists); i += 2 { 37 | if i + 1 == len(lists) { 38 | merges = append(merges, lists[i]) 39 | } else { 40 | var c *ListNode = merge2Lists(lists[i], lists[i+1]) 41 | merges = append(merges, c) 42 | } 43 | } 44 | return mergeKLists(merges) 45 | } 46 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem65_求一个数是否是有效数/README.md: -------------------------------------------------------------------------------- 1 | # 求一个数是否是有效数 2 | 3 | ### 题意 4 | ``` 5 | 验证给定的字符串是否可以解释为十进制数字。 6 | 7 | 例如: 8 | 9 | "0" => true 10 | " 0.1 " => true 11 | "abc" => false 12 | "1 a" => false 13 | "2e10" => true 14 | " -90e3   " => true 15 | " 1e" => false 16 | "e3" => false 17 | " 6e-1" => true 18 | " 99e2.5 " => false 19 | "53.5e93" => true 20 | " --6 " => false 21 | "-+3" => false 22 | "95a54e53" => false 23 | 24 | 说明: 我们有意将问题陈述地比较模糊。在实现代码之前,你应当事先思考所有可能的情况。这里给出一份可能存在于有效十进制数字中的字符列表: 25 | 数字 0-9 26 | 指数 - "e" 27 | 正/负号 - "+"/"-" 28 | 小数点 - "." 29 | 当然,在输入中,这些字符的上下文也很重要。 30 | ``` 31 | 32 | ### 条件范围 33 | 34 | ### 样例输入1(多组) 35 | ``` 36 | 0.1 37 | 123 38 | 01 39 | 001 40 | 000 41 | 000e0 42 | +0 43 | +1 44 | -0 45 | +2e2 46 | .0 47 | 0. 48 | -.0 49 | -.1e2 50 | 1e+0 51 | -.0e-0 52 | +0.e1 53 | -.1e2 54 | "1 " 55 | " -.0e-0" 56 | ``` 57 | 58 | ### 样例输出1(多组) 59 | ``` 60 | true 61 | ``` 62 | 63 | ### 样例输入2(多组) 64 | ``` 65 | a1 66 | 1+ 67 | +-1 68 | 0.1e 69 | +e 70 | -e 71 | +1e 72 | ee 73 | 1e1e2 74 | .e1 75 | e1 76 | 2e0.1 77 | 3e1.1 78 | . 79 | +. 80 | -. 81 | + 82 | - 83 | 4e+ 84 | "1 e 2" 85 | " -.0e -0" 86 | " " 87 | "" 88 | ``` 89 | 90 | ### 样例输出2(多组) 91 | ``` 92 | false 93 | ``` 94 | 95 | ### 关联链接 96 | 原题:https://leetcode-cn.com/problems/valid-number/ 97 | -------------------------------------------------------------------------------- /深入理解计算机网络/HTTP请求子过程日志/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | com.mycompany.app 7 | my-app 8 | jar 9 | 1.0-SNAPSHOT 10 | Maven Quick Start Archetype 11 | http://maven.apache.org 12 | 13 | 14 | junit 15 | junit 16 | 4.13.1 17 | test 18 | 19 | 20 | 21 | com.squareup.okhttp3 22 | okhttp 23 | 3.14.2 24 | 25 | 26 | 27 | javax.servlet 28 | servlet-api 29 | 2.5 30 | provided 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1266_problemC_基于最大公约数的矩阵构造题/solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | const int M = 505; 11 | int result[M][M]; 12 | 13 | void build(int r, int c) { 14 | int t = 1; 15 | for (int j = 1; j <= c; j ++) { 16 | t ++; 17 | result[1][j] = t; 18 | } 19 | for (int i = 2; i <= r; i ++) { 20 | t ++; 21 | for (int j = 1; j <= c; j ++) { 22 | result[i][j] = t * result[1][j]; 23 | } 24 | } 25 | } 26 | 27 | int main() { 28 | int r, c; 29 | cin >> r >> c; 30 | 31 | if (r == 1 && c == 1) { 32 | printf("0\n"); 33 | return 0; 34 | } 35 | 36 | if (r <= c) { 37 | build(r, c); 38 | for (int i = 1; i <= r; i ++) { 39 | for (int j = 1; j <= c; j ++) { 40 | printf("%d%c", result[i][j], j == c ? '\n' : ' '); 41 | } 42 | } 43 | } 44 | else { 45 | build(c, r); 46 | for (int j = 1; j <= r; j ++) { 47 | for (int i = 1; i <= c; i ++) { 48 | printf("%d%c", result[i][j], i == c ? '\n' : ' '); 49 | } 50 | } 51 | } 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem1473_求指定条件下房子涂色的最小花费/README.md: -------------------------------------------------------------------------------- 1 | # 求指定条件下房子涂色的最小花费 2 | 3 | ### 题意 4 | ``` 5 | M个房子,N种颜色,颜色编号为1到N 6 | 7 | houses 数组,houses[i] 表示第 i 个房子(i 从 0 开始)涂了哪一种颜色,houses[i] = 0 表示没有涂颜色 8 | 9 | 将连续相同颜色尽可能多的房子称为一个街区,比方说 houses = [1,2,2,3,3,2,1,1] ,它包含 5 个街区 [{1}, {2,2}, {3,3}, {2}, {1,1}] 10 | 11 | 有一个 M * N 的矩阵 cost,表示 将第 i 个房子涂成颜色 j + 1 的花费 12 | 13 | 求把所有房子涂上颜色后,恰好有 T 个街区的最小花费,如果办不到,输出 -1 14 | ``` 15 | 16 | ### 条件范围 17 | ``` 18 | M == houses.length == cost.length 19 | N == cost[i].length 20 | M : [1, 100] 21 | N : [1, 20] 22 | T : [1, M] 23 | houses[i] : [0, N] 24 | cost[i][j] : [1, 10^4] 25 | ``` 26 | 27 | ### 样例输入 28 | ``` 29 | [0,0,0,0,0] 30 | [[1,10],[10,1],[10,1],[1,10],[5,1]] 31 | 5 32 | 2 33 | 3 34 | [0,2,1,2,0] 35 | [[1,10],[10,1],[10,1],[1,10],[5,1]] 36 | 5 37 | 2 38 | 3 39 | [0,0,0,0,0] 40 | [[1,10],[10,1],[1,10],[10,1],[1,10]] 41 | 5 42 | 2 43 | 5 44 | [3,1,2,3] 45 | [[1,1,1],[1,1,1],[1,1,1],[1,1,1]] 46 | 4 47 | 3 48 | 3 49 | ``` 50 | 51 | ### 样例输出 52 | ``` 53 | 9 54 | 11 55 | 5 56 | -1 57 | ``` 58 | 59 | ### 样例解释 60 | ``` 61 | 1.此方案包含 target = 3 个街区,分别是 [{1}, {2,2}, {1,1}],涂色的总花费为 (1 + 1 + 1 + 1 + 5) = 9 62 | 2.有的房子已经被涂色了,在此基础上涂色方案为 [2,2,1,2,2],此方案包含 target = 3 个街区,分别是 [{2,2}, {1}, {2,2}],给第一个和最后一个房子涂色的花费为 (10 + 1) = 11 63 | 3.无 64 | 4.房子已经被涂色并组成了 4 个街区,分别是 [{3},{1},{2},{3}] ,无法形成 target = 3 个街区 65 | ``` 66 | 67 | ### 关联链接 68 | 原题:https://leetcode-cn.com/problems/paint-house-iii/ 69 | -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/ByteCodeExample/putstatic.md: -------------------------------------------------------------------------------- 1 | # putstatic 2 | 3 | 为指定的类的静态域赋值 4 | 5 | ### code1 6 | 7 | ```java 8 | package com.peter.jvm.example; 9 | 10 | public class ByteCodePutStaticTest1 { 11 | 12 | public static void main(String[] args) { 13 | ByteCodePutStaticTest11.a = "44"; 14 | String a = ByteCodePutStaticTest11.a; 15 | System.out.println(a); 16 | } 17 | } 18 | 19 | class ByteCodePutStaticTest11 { 20 | 21 | static { 22 | System.out.println("11"); 23 | //System.out.print(a); // 编译不过 24 | } 25 | 26 | public static String a = "22"; 27 | 28 | static { 29 | System.out.println("33"); 30 | System.out.println(a); 31 | } 32 | } 33 | ``` 34 | 35 | 输出结果 36 | ``` 37 | 11 38 | 33 39 | 22 40 | 44 41 | ``` 42 | 43 | 字节码 44 | ``` 45 | 0 ldc #2 <44> 46 | 2 putstatic #3 47 | 5 getstatic #3 48 | 8 astore_1 49 | 9 getstatic #4 50 | 12 aload_1 51 | 13 invokevirtual #5 52 | 16 return 53 | ``` 54 | 55 | 解释 56 | 57 | ByteCodePutStaticTest1.a = "44"; 对应 putstatic ByteCodePutStaticTest1.a,会触发对ByteCodePutStaticTest1类加载,输出11、33、a,此时a还是22 58 | 59 | String a = ByteCodePutStaticTest1.a; 对应 getstatic ByteCodePutStaticTest1.a,会尝试对ByteCodePutStaticTest1类加载,由于已经加载过了,不加载,a变为44 60 | 61 | System.out.println(a); 输出44 62 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/weekly-contest-221_problem2_吃苹果的最大数目/Solution.java: -------------------------------------------------------------------------------- 1 | class Solution { 2 | 3 | class Data { 4 | int apples; 5 | int from; 6 | int to; 7 | public Data(int apples, int from, int to) { 8 | this.apples = apples; 9 | this.from = from; 10 | this.to = to; 11 | } 12 | }; 13 | PriorityQueue q = new PriorityQueue(new Comparator() { 14 | @Override 15 | public int compare(Data d1, Data d2) { 16 | return d1.to - d2.to; 17 | } 18 | }); 19 | 20 | public int eatenApples(int[] apples, int[] days) { 21 | int ans = 0; 22 | int i = 1; 23 | while (true) { 24 | if (i-1 < apples.length) { 25 | Data d = new Data(apples[i-1], i, i + days[i-1] - 1); 26 | if (d.apples > 0) 27 | q.add(d); 28 | } 29 | while (!q.isEmpty()) { 30 | Data d = q.poll(); 31 | if (i >= d.from && i <= d.to) { 32 | d.apples --; 33 | ans ++; 34 | if (d.apples > 0) 35 | q.add(d); 36 | break; 37 | } 38 | } 39 | i ++; 40 | if (i > apples.length && q.isEmpty()) 41 | break; 42 | } 43 | 44 | return ans; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem4_求两个升序数列的中位数/solve1.go: -------------------------------------------------------------------------------- 1 | func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { 2 | n1 := len(nums1) 3 | n2 := len(nums2) 4 | if n1 == 0 { 5 | return findMedianSortedArrays(nums2, nums1) 6 | } 7 | if (n1 + n2) % 2 == 1 { 8 | return cal(nums1, nums2, (n1 + n2) / 2 + 1) 9 | } else { 10 | ans1 := cal(nums1, nums2, (n1 + n2) / 2) 11 | ans2 := cal(nums1, nums2, (n1 + n2) / 2 + 1) 12 | return float64(ans1 + ans2) / 2 13 | } 14 | } 15 | func cal(nums1 []int, nums2 []int, tnum int) float64 { 16 | n1 := len(nums1) 17 | n2 := len(nums2) 18 | l := 0 19 | r := n1 - 1 20 | for { 21 | if l == r { 22 | break 23 | } 24 | m := (l + r) / 2 25 | index1 := m 26 | index2 := tnum - (index1 + 1) - 1 27 | if index2 < 0 { 28 | r = m 29 | } else if index2 >= n2 { 30 | l = m + 1 31 | } else if nums1[index1] < nums2[index2] { 32 | l = m + 1 33 | } else { 34 | r = m 35 | } 36 | } 37 | index1 := l 38 | index2 := tnum - (index1 + 1) - 1 39 | if index2 >= 0 && index2 < n2 && nums1[index1] < nums2[index2] { 40 | return cal(nums2, nums1, tnum) 41 | } 42 | if index2 + 1 >= 0 && index2 + 1 < n2 && nums1[index1] > nums2[index2+1] { 43 | return cal(nums2, nums1, tnum) 44 | } 45 | return float64(nums1[index1]) 46 | } 47 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/FromBook/problem3_求二叉搜索树的所有生成数组/Solution.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * public class TreeNode { 4 | * int val; 5 | * TreeNode left; 6 | * TreeNode right; 7 | * TreeNode(int x) { val = x; } 8 | * } 9 | */ 10 | class Solution { 11 | 12 | private List> res = new ArrayList<>(); 13 | private Deque deque = new ArrayDeque<>(); 14 | private Stack stack = new Stack<>(); 15 | 16 | private void dfs() { 17 | if (deque.isEmpty()) { 18 | res.add(new ArrayList<>(stack)); 19 | return; 20 | } 21 | int size = deque.size(); 22 | for (int i = 0; i < size; i ++) { 23 | TreeNode node = deque.pollFirst(); 24 | if (node.left != null) 25 | deque.addLast(node.left); 26 | if (node.right != null) 27 | deque.addLast(node.right); 28 | stack.add(node.val); 29 | 30 | dfs(); 31 | 32 | stack.pop(); 33 | if (node.right != null) 34 | deque.pollLast(); 35 | if (node.left != null) 36 | deque.pollLast(); 37 | deque.addLast(node); 38 | } 39 | } 40 | 41 | public List> BSTSequences(TreeNode root) { 42 | if (root == null) 43 | res.add(new ArrayList<>()); 44 | else { 45 | deque.addLast(root); 46 | dfs(); 47 | } 48 | return res; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem15_求和为零的三元组的去重解/solve.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> threeSum(vector& nums) { 4 | vector> ans; 5 | 6 | sort(nums.begin(), nums.end()); 7 | 8 | for (int i = 0; i < nums.size(); i ++) { 9 | if (i > 0 && nums[i] == nums[i-1]) continue; 10 | 11 | for (int j = i + 1; j < nums.size(); j ++) { 12 | if (j > i + 1 && nums[j] == nums[j-1]) continue; 13 | 14 | int l = j + 1; 15 | int r = nums.size() - 1; 16 | while (l < r) { 17 | int mid = (l + r) / 2; 18 | int sum = nums[i] + nums[j] + nums[mid]; 19 | if (sum == 0) { 20 | l = r = mid; 21 | break; 22 | } 23 | if (sum < 0) l = mid + 1; 24 | else r = mid - 1; 25 | } 26 | 27 | if (l == r && l >= 0 && l < nums.size()) { 28 | if (nums[i] + nums[j] + nums[l] == 0) { 29 | vector t; 30 | t.push_back(nums[i]); 31 | t.push_back(nums[j]); 32 | t.push_back(nums[l]); 33 | ans.push_back(t); 34 | } 35 | } 36 | } 37 | } 38 | 39 | return ans; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/weekly-contest-221_problem3_球会落何处/Solution.java: -------------------------------------------------------------------------------- 1 | class Solution { 2 | 3 | public int[] findBall(int[][] grid) { 4 | int m = grid.length; 5 | int n = grid[0].length; 6 | int[] ans = new int[n]; 7 | for (int j = 0; j < n; j ++) { 8 | int x = 0, y = j; 9 | while (x < m) { 10 | if (y + 1 == n) { 11 | if (y != 0 && grid[x][y] == -1 && grid[x][y-1] == -1) { 12 | x ++; 13 | y --; 14 | } else { 15 | break; 16 | } 17 | continue; 18 | } 19 | if (y == 0) { 20 | if (y + 1 != n && grid[x][y] == 1 && grid[x][y+1] == 1) { 21 | x ++; 22 | y ++; 23 | } else { 24 | break; 25 | } 26 | continue; 27 | } 28 | if (grid[x][y] == 1 && grid[x][y+1] == 1) { 29 | x ++; 30 | y ++; 31 | } else if (grid[x][y] == 1 && grid[x][y+1] == -1) { 32 | break; 33 | } else if (grid[x][y] == -1 && grid[x][y-1] == -1) { 34 | x ++; 35 | y --; 36 | } else if (grid[x][y] == -1 && grid[x][y-1] == 1) { 37 | break; 38 | } 39 | } 40 | ans[j] = x == m ? y : -1; 41 | } 42 | return ans; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem514_旋转字符串匹配/Solution.java: -------------------------------------------------------------------------------- 1 | class Solution { 2 | 3 | private int lenRing; 4 | private int lenKey; 5 | private int[][] dp; 6 | 7 | private int min(int a, int b) { 8 | return a == -1 ? b : (b == -1 ? a : Integer.min(a, b)); 9 | } 10 | 11 | private int dis(int i, int j) { 12 | return min(Math.abs(i - j), lenRing - Math.abs(i - j)); 13 | } 14 | 15 | public int findRotateSteps(String ring, String key) { 16 | lenRing = ring.length(); 17 | lenKey = key.length(); 18 | dp = new int[lenKey][]; 19 | for (int i = 0; i < lenKey; i ++) 20 | dp[i] = new int[lenRing]; 21 | for (int i = 0; i < lenKey; i ++) { 22 | for (int j = 0; j < lenRing; j ++) 23 | dp[i][j] = -1; 24 | } 25 | 26 | for (int j = 0; j < lenRing; j ++) { 27 | if (key.charAt(0) == ring.charAt(j)) 28 | dp[0][j] = dis(0, j); 29 | } 30 | for (int i = 1; i < lenKey; i ++) { 31 | for (int j0 = 0; j0 < lenRing; j0 ++) { 32 | if (dp[i-1][j0] != -1) { 33 | for (int j1 = 0; j1 < lenRing; j1 ++) { 34 | if (key.charAt(i) == ring.charAt(j1)) 35 | dp[i][j1] = min(dp[i][j1], dp[i-1][j0] + dis(j0, j1)); 36 | } 37 | } 38 | } 39 | } 40 | 41 | int ans = -1; 42 | for (int j = 0; j < lenRing; j ++) { 43 | ans = min(ans, dp[lenKey-1][j]); 44 | } 45 | ans = ans + lenKey; 46 | return ans; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem1242_多线程爬虫程序/Solution.java: -------------------------------------------------------------------------------- 1 | class Solution { 2 | 3 | private final int nThreads = 30; 4 | private final ThreadPoolExecutor thp = new ThreadPoolExecutor(nThreads, nThreads, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); 5 | private final Set urlSet = new HashSet<>(1024); 6 | private final AtomicInteger nMission = new AtomicInteger(0); 7 | private String startDomain; 8 | private HtmlParser htmlParser; 9 | 10 | private void crawl(String startUrl) { 11 | List urls = htmlParser.getUrls(startUrl); 12 | for (String url : urls) { 13 | String domain = url.split("/", 4)[2]; 14 | if (startDomain.equals(domain)) { 15 | if (!urlSet.contains(url)) { 16 | synchronized (this) { 17 | if (!urlSet.contains(url)) { 18 | urlSet.add(url); 19 | nMission.incrementAndGet(); 20 | thp.submit(() -> crawl(url)); 21 | } 22 | } 23 | } 24 | } 25 | } 26 | nMission.decrementAndGet(); 27 | } 28 | 29 | public List crawl(String startUrl, HtmlParser htmlParser) { 30 | this.htmlParser = htmlParser; 31 | urlSet.add(startUrl); 32 | startDomain = startUrl.split("/", 4)[2]; 33 | nMission.incrementAndGet(); 34 | thp.submit(() -> crawl(startUrl)); 35 | while (nMission.get() > 0) { 36 | Thread.yield(); 37 | } 38 | thp.shutdown(); 39 | return new ArrayList<>(urlSet); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem4_求两个升序数列的中位数/solve2.go: -------------------------------------------------------------------------------- 1 | func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { 2 | n1 := len(nums1) 3 | n2 := len(nums2) 4 | if n1 == 0 { 5 | return findMedianSortedArrays(nums2, nums1) 6 | } 7 | if ((n1 + n2) % 2 == 1) { 8 | return cal(nums1, nums2, (n1 + n2) / 2 + 1) 9 | } else { 10 | ans1 := cal(nums1, nums2, (n1 + n2) / 2) 11 | ans2 := cal(nums1, nums2, (n1 + n2) / 2 + 1) 12 | return (ans1 + ans2) / 2 13 | } 14 | } 15 | func cal(nums1 []int, nums2 []int, target_n int) float64 { 16 | l := 0 17 | r := len(nums1) - 1 18 | for { 19 | if l == r { 20 | break 21 | } 22 | m := (l + r) / 2 23 | y := findFirstLargerIndex(nums2, nums1[m]) 24 | if m + 1 + y == target_n { 25 | return float64(nums1[m]) 26 | } else if m + 1 + y > target_n { 27 | r = m 28 | } else { 29 | l = m + 1 30 | } 31 | } 32 | y := findFirstLargerIndex(nums2, nums1[l]) 33 | if l + 1 + y < target_n { 34 | return cal(nums2, nums1, target_n) 35 | } 36 | if l + 1 + y > target_n { 37 | d := l + 1 + y - target_n 38 | y = y - d 39 | return float64(nums2[y]) 40 | } 41 | return float64(nums1[l]) 42 | } 43 | func findFirstLargerIndex(nums []int, target_x int) int { 44 | l := 0 45 | r := len(nums) 46 | for { 47 | if l == r { 48 | break 49 | } 50 | m := (l + r) / 2 51 | if nums[m] <= target_x { 52 | l = m + 1 53 | } else { 54 | r = m 55 | } 56 | } 57 | return l 58 | } 59 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem815_多条环路公交车线两点间最少上车次数/solve1.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | static const int N = 1000010; 4 | static const int M = 505; 5 | //vector rb[N]; 6 | map> rb; 7 | queue q; 8 | bool vis[M]; 9 | int numBusesToDestination(vector>& routes, int S, int T) { 10 | if (S == T) return 0; 11 | for (int i = 0; i < routes.size(); i ++) { 12 | for (auto j : routes[i]) { 13 | //rb[j].push_back(i); 14 | rb[j].insert(i); 15 | } 16 | } 17 | for (int i = 0; i < M; i ++) { 18 | vis[i] = false; 19 | } 20 | for (auto i : rb[S]) { 21 | q.push(i); 22 | vis[i] = true; 23 | } 24 | int step = 0; 25 | while (!q.empty()) { 26 | step ++; 27 | int sz = q.size(); 28 | for (int i = 0; i < sz; i ++) { 29 | int x = q.front(); 30 | q.pop(); 31 | for (auto j : routes[x]) { 32 | if (j == T) return step; 33 | for (auto k : rb[j]) { 34 | if (!vis[k]) { 35 | q.push(k); 36 | vis[k] = true; 37 | } 38 | } 39 | } 40 | } 41 | } 42 | return -1; 43 | } 44 | }; 45 | 46 | // 换一个思路 47 | // routes[i] 表示第i条公交车线路 48 | // queue q 表示正在枚举的公交车线路 49 | // vis[i] 表示第i条线路被访问过 50 | // rb[i] 第i个点拥有的线路 51 | // 不是很明白为何rb用map就能过,用vector就会T,也许是因为N太大了? 52 | 53 | //结果 54 | /* 55 | 45 / 45 个通过测试用例 56 | 状态:通过 57 | 执行用时:340 ms 58 | 内存消耗:40.5 MB 59 | */ 60 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem23_合并且排序K个指针表/solve.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * struct ListNode { 4 | * int val; 5 | * ListNode *next; 6 | * ListNode(int x) : val(x), next(NULL) {} 7 | * }; 8 | */ 9 | class Solution { 10 | public: 11 | ListNode * merge2Lists(ListNode * l1, ListNode * l2) { 12 | ListNode * head = NULL; 13 | ListNode * l3 = NULL; 14 | while (l1 != NULL || l2 != NULL) { 15 | if (l2 == NULL || (l1 != NULL && l1->val <= l2->val)) { 16 | if (l3 == NULL) { 17 | l3 = l1; 18 | head = l3; 19 | } else { 20 | l3->next = l1; 21 | l3 = l3->next; 22 | } 23 | l1 = l1->next; 24 | } else { 25 | if (l3 == NULL) { 26 | l3 = l2; 27 | head = l3; 28 | } else { 29 | l3->next = l2; 30 | l3 = l3->next; 31 | } 32 | l2 = l2->next; 33 | } 34 | } 35 | return head; 36 | } 37 | 38 | ListNode * mergeKLists(vector& lists) { 39 | int len = lists.size(); 40 | if (len == 0) return NULL; 41 | if (len == 1) return lists[0]; 42 | sort(lists.begin(), lists.end()); 43 | vector harf_lists; 44 | for (int i = 0; i < len - 1; i += 2) { 45 | ListNode * m = merge2Lists(lists[i], lists[i+1]); 46 | harf_lists.push_back(m); 47 | } 48 | if (len % 2 == 1) harf_lists.push_back(lists[len-1]); 49 | return mergeKLists(harf_lists); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/FromBook/problem2_求0到N中数字2出现的次数/Solution.java: -------------------------------------------------------------------------------- 1 | class Solution { 2 | 3 | final int N = 12; 4 | int[][] dp = new int[N][N]; 5 | int[] a = new int[N]; 6 | int[] ten = new int[N]; 7 | 8 | public Solution() { 9 | for (int i = 0; i < N; i ++) { 10 | for (int j = 0; j < N; j ++) 11 | dp[i][j] = 0; 12 | } 13 | for (int i = 0; i < N; i ++) { 14 | a[i] = 0; 15 | } 16 | int t = 1; 17 | for (int i = 0; i < N; i ++) { 18 | ten[i] = t; 19 | t = t * 10; 20 | } 21 | } 22 | 23 | private int length(int n) { 24 | if (n == 0) 25 | return 1; 26 | int res = 0; 27 | while (n != 0) { 28 | res ++; 29 | a[res] = n % 10; 30 | n = n / 10; 31 | } 32 | return res; 33 | } 34 | 35 | public int numberOf2sInRange(int n) { 36 | // init 37 | dp[1][2] = 1; 38 | // dp 39 | for (int i = 2; i <= 10; i ++) { 40 | int sum = 0; 41 | for (int k = 0; k <= 9; k ++) { 42 | sum += dp[i-1][k]; 43 | } 44 | for (int j = 0; j <= 9; j ++) { 45 | dp[i][j] = sum + (j == 2 ? ten[i-1] : 0); 46 | } 47 | } 48 | // ans 49 | int ans = 0; 50 | int l = length(n); 51 | int num2 = 0; 52 | for (int i = l; i >= 1; i --) { 53 | for (int j = 0; j < a[i]; j ++) { 54 | ans += dp[i][j]; 55 | ans += num2 * ten[i-1]; 56 | } 57 | if (a[i] == 2) { 58 | num2 += 1; 59 | } 60 | } 61 | ans += num2; 62 | return ans; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem1473_求指定条件下房子涂色的最小花费/solve.go: -------------------------------------------------------------------------------- 1 | const N = 25 2 | const M = 105 3 | var dp [M][N][M]int 4 | func min(a int, b int) int { 5 | if b == -1 || (a != -1 && a < b) { 6 | return a 7 | } 8 | return b 9 | } 10 | func minCost(houses []int, cost [][]int, m int, n int, target int) int { 11 | for i := 0; i < M; i ++ { 12 | for j := 0; j < N; j ++ { 13 | for k := 0; k < M; k ++ { 14 | dp[i][j][k] = -1 15 | } 16 | } 17 | } 18 | 19 | if houses[0] == 0 { 20 | for j := 1; j <= n; j ++ { 21 | dp[0][j][1] = cost[0][j-1] 22 | } 23 | } else { 24 | dp[0][houses[0]][1] = 0 25 | } 26 | 27 | for i := 1; i < m; i ++ { 28 | for j0 := 1; j0 <= n; j0 ++ { 29 | for k := 1; k <= target; k ++ { 30 | if dp[i-1][j0][k] == -1 { 31 | continue 32 | } 33 | for j1 := 1; j1 <= n; j1 ++ { 34 | if houses[i] != 0 && j1 != houses[i] { 35 | continue 36 | } 37 | var c int 38 | if houses[i] == 0 { 39 | c = cost[i][j1-1] 40 | } else { 41 | c = 0 42 | } 43 | if j0 == j1 { 44 | dp[i][j1][k] = min(dp[i][j1][k], dp[i-1][j0][k] + c) 45 | } else { 46 | dp[i][j1][k+1] = min(dp[i][j1][k+1], dp[i-1][j0][k] + c) 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | ans := -1 54 | for j := 1; j <= n; j ++ { 55 | ans = min(ans, dp[m-1][j][target]) 56 | } 57 | return ans 58 | } 59 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/weekly-contest-205_problem4_计算三次最小生成树/README.md: -------------------------------------------------------------------------------- 1 | # 计算三次最小生成树 2 | ### 题意 3 | ``` 4 | Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边: 5 | 6 | 类型 1:只能由 Alice 遍历。 7 | 类型 2:只能由 Bob 遍历。 8 | 类型 3:Alice 和 Bob 都可以遍历。 9 | 10 | 给你一个数组 edges ,其中 edges[i] = [typei, ui, vi] 表示节点 ui 和 vi 之间存在类型为 typei 的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。 11 | 12 | 返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。 13 | ``` 14 | ### 条件范围 15 | ``` 16 | 1 <= n <= 10^5 17 | 1 <= edges.length <= min(10^5, 3 * n * (n-1) / 2) 18 | edges[i].length == 3 19 | 1 <= edges[i][0] <= 3 20 | 1 <= edges[i][1] < edges[i][2] <= n 21 | 所有元组 (typei, ui, vi) 互不相同 22 | ``` 23 | ### 样例输入1 24 | ``` 25 | 4 26 | [[3,1,2],[3,2,3],[1,1,3],[1,2,4],[1,1,2],[2,3,4]] 27 | ``` 28 | ### 样例输出1 29 | ``` 30 | 2 31 | ``` 32 | ### 样例解释1 33 | ![image](https://user-images.githubusercontent.com/10209135/92988571-433b0280-f4ff-11ea-9200-1cc4b8101191.png) 34 | ``` 35 | 如果删除 [1,1,2] 和 [1,1,3] 这两条边,Alice 和 Bob 仍然可以完全遍历这个图。再删除任何其他的边都无法保证图可以完全遍历。所以可以删除的最大边数是 2 。 36 | ``` 37 | ### 样例输入2 38 | ``` 39 | 4 40 | [[3,1,2],[3,2,3],[1,1,4],[2,1,4]] 41 | ``` 42 | ### 样例输出2 43 | ``` 44 | 0 45 | ``` 46 | ### 样例解释2 47 | ![image](https://user-images.githubusercontent.com/10209135/92988579-5057f180-f4ff-11ea-82d4-bbcf62f0e190.png) 48 | ``` 49 | 注意,删除任何一条边都会使 Alice 和 Bob 无法完全遍历这个图。 50 | ``` 51 | ### 样例输入3 52 | ``` 53 | 4 54 | [[3,2,3],[1,1,2],[2,3,4]] 55 | ``` 56 | ### 样例输出3 57 | ``` 58 | -1 59 | ``` 60 | ### 样例解释3 61 | ![image](https://user-images.githubusercontent.com/10209135/92988581-58b02c80-f4ff-11ea-9796-61e0e8d3aa24.png) 62 | ``` 63 | 在当前图中,Alice 无法从其他节点到达节点 4 。类似地,Bob 也不能达到节点 1 。因此,图无法完全遍历。 64 | ``` 65 | ### 关联链接 66 | 原题:https://leetcode-cn.com/contest/weekly-contest-205/problems/remove-max-number-of-edges-to-keep-graph-fully-traversable/ 67 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/UVA/problem1395_Kruskal算法求连通图的自定义函数解/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Kruskal算法求连通图的自定义函数解 3 | 4 | ### 题意 5 | 6 | 给一个简单图(无重编、无自环),N个点,M条边,每条边边权是Wi 7 | 8 | 求一个最小生成树,使得树中【Wi的最大值 - Wi的最小值】的结果最小,输出该结果 9 | 10 | 如果图构成不了一棵树,输出 -1 11 | 12 | 多CASE,以0 0结尾 13 | 14 | ### 条件范围 15 | 16 | N :[2, 100] 17 | 18 | M : [0, N * (N-1) / 2] 19 | 20 | Wi : [1, 10000] 21 | 22 | ### 样例输入1 23 | ``` 24 | 4 5 25 | 1 2 3 26 | 1 3 5 27 | 1 4 6 28 | 2 4 6 29 | 3 4 7 30 | ``` 31 | 32 | ### 样例输出1 33 | ``` 34 | 1 35 | ``` 36 | 37 | ### 样例输入2 38 | ``` 39 | 4 6 40 | 1 2 10 41 | 1 3 100 42 | 1 4 90 43 | 2 3 20 44 | 2 4 80 45 | 3 4 40 46 | ``` 47 | 48 | ### 样例输出2 49 | ``` 50 | 20 51 | ``` 52 | 53 | ### 样例输入3 54 | ``` 55 | 2 1 56 | 1 2 1 57 | ``` 58 | 59 | ### 样例输出3 60 | ``` 61 | 0 62 | ``` 63 | 64 | ### 样例输入4 65 | ``` 66 | 3 0 67 | ``` 68 | 69 | ### 样例输出4 70 | ``` 71 | -1 72 | ``` 73 | 74 | ### 样例输入5 75 | ``` 76 | 3 1 77 | 1 2 1 78 | ``` 79 | 80 | ### 样例输出5 81 | ``` 82 | -1 83 | ``` 84 | 85 | ### 样例输入6 86 | ``` 87 | 3 3 88 | 1 2 2 89 | 2 3 5 90 | 1 3 6 91 | ``` 92 | 93 | ### 样例输出6 94 | ``` 95 | 1 96 | ``` 97 | 98 | ### 样例输入7 99 | ``` 100 | 5 10 101 | 1 2 110 102 | 1 3 120 103 | 1 4 130 104 | 1 5 120 105 | 2 3 110 106 | 2 4 120 107 | 2 5 130 108 | 3 4 120 109 | 3 5 110 110 | 4 5 120 111 | ``` 112 | 113 | ### 样例输出7 114 | ``` 115 | 0 116 | ``` 117 | 118 | ### 样例输入8 119 | ``` 120 | 5 10 121 | 1 2 9384 122 | 1 3 887 123 | 1 4 2778 124 | 1 5 6916 125 | 2 3 7794 126 | 2 4 8336 127 | 2 5 5387 128 | 3 4 493 129 | 3 5 6650 130 | 4 5 1422 131 | ``` 132 | 133 | ### 样例输出8 134 | ``` 135 | 1686 136 | ``` 137 | 138 | ### 样例输入9 139 | ``` 140 | 5 8 141 | 1 2 1 142 | 2 3 100 143 | 3 4 100 144 | 4 5 100 145 | 1 5 50 146 | 2 5 50 147 | 3 5 50 148 | 4 1 150 149 | ``` 150 | 151 | ### 样例输出9 152 | ``` 153 | 50 154 | ``` 155 | 156 | ### 关联链接 157 | 158 | 原题:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4141 159 | 160 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/UESTC/problem838_线段树单点加法区间求和/solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | const int N = 100000 + 100; 8 | 9 | int a[N]; 10 | 11 | long long tree[N * 4]; 12 | 13 | void build(int id, int l, int r) { 14 | if (l == r) { 15 | tree[id] = a[l]; 16 | return; 17 | } 18 | int mid = (l + r) >> 1; 19 | build(id<<1, l, mid); 20 | build(id<<1|1, mid + 1, r); 21 | tree[id] = tree[id<<1] + tree[id<<1|1]; 22 | } 23 | 24 | void update(int id, int l, int r, int x, int v) { 25 | if (x == l && x == r) { 26 | tree[id] += v; 27 | return; 28 | } 29 | int mid = (l + r) >> 1; 30 | if (x <= mid) update(id<<1, l, mid, x, v); 31 | else update(id<<1|1, mid + 1, r, x, v); 32 | tree[id] = tree[id<<1] + tree[id<<1|1]; 33 | } 34 | 35 | long long query(int id, int ql ,int qr, int l, int r) { 36 | if (ql == l && qr == r) { 37 | return tree[id]; 38 | } 39 | int mid = (l + r) >> 1; 40 | if (qr <= mid) return query(id<<1, ql, qr, l, mid); 41 | else if (mid + 1 <= ql) return query(id<<1|1, ql, qr, mid + 1, r); 42 | else { 43 | long long sum = 0; 44 | sum += query(id<<1, ql, mid, l, mid); 45 | sum += query(id<<1|1, mid + 1, qr, mid + 1, r); 46 | return sum; 47 | } 48 | } 49 | 50 | int main() { 51 | int n, m; 52 | scanf("%d%d", &n, &m); 53 | for (int i = 1; i <= n; i ++) { 54 | scanf("%d", a + i); 55 | } 56 | 57 | build(1, 1, n); 58 | 59 | for (int i = 1; i <= m; i ++) { 60 | int type; 61 | scanf("%d", &type); 62 | if (type == 0) { 63 | int ql, qr; 64 | scanf("%d%d", &ql, &qr); 65 | long long ans = query(1, ql, qr, 1, n); 66 | printf("%lld\n", ans); 67 | } else { // type == 1 68 | int x, v; 69 | scanf("%d%d", &x, &v); 70 | update(1, 1, n, x, v); 71 | } 72 | } 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/weekly-contest-205_problem4_计算三次最小生成树/solve.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | static const int N = 100010; 4 | int fa1[N]; 5 | int fa2[N]; 6 | int get_fa(int * fa, int v) { 7 | if (fa[v] == v) return v; 8 | else { 9 | fa[v] = get_fa(fa, fa[v]); 10 | return fa[v]; 11 | } 12 | } 13 | int maxNumEdgesToRemove(int n, vector>& edges) { 14 | for (int i = 1; i <= n; i ++) { 15 | fa1[i] = i; 16 | } 17 | int max_remove = 0; 18 | for (int i = 0; i < edges.size(); i ++) { 19 | if (edges[i][0] == 3) { 20 | int f1 = get_fa(fa1, edges[i][1]); 21 | int f2 = get_fa(fa1, edges[i][2]); 22 | if (f1 != f2) { 23 | fa1[f1] = f2; 24 | } else { 25 | max_remove += 1; 26 | } 27 | } 28 | } 29 | for (int i = 1; i <= n; i ++) { 30 | fa2[i] = fa1[i]; 31 | } 32 | for (int i = 0; i < edges.size(); i ++) { 33 | if (edges[i][0] == 1) { 34 | int f1 = get_fa(fa1, edges[i][1]); 35 | int f2 = get_fa(fa1, edges[i][2]); 36 | if (f1 != f2) { 37 | fa1[f1] = f2; 38 | } else { 39 | max_remove += 1; 40 | } 41 | } 42 | } 43 | for (int i = 1; i <= n; i ++) { 44 | if (get_fa(fa1, 1) != get_fa(fa1, i)) return -1; 45 | } 46 | for (int i = 0; i < edges.size(); i ++) { 47 | if (edges[i][0] == 2) { 48 | int f1 = get_fa(fa2, edges[i][1]); 49 | int f2 = get_fa(fa2, edges[i][2]); 50 | if (f1 != f2) { 51 | fa2[f1] = f2; 52 | } else { 53 | max_remove += 1; 54 | } 55 | } 56 | } 57 | for (int i = 1; i <= n; i ++) { 58 | if (get_fa(fa2, 1) != get_fa(fa2, i)) return -1; 59 | } 60 | return max_remove; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem815_多条环路公交车线两点间最少上车次数/solve2.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | static const int N = 1000010; 4 | int a[N]; 5 | vector b[N]; 6 | queue q; 7 | static const int M = 505; 8 | bool vis[M]; 9 | void build(vector>& routes) { 10 | for (int i = 0; i < routes.size(); i ++) { 11 | for (int j = 0; j < routes[i].size(); j ++) { 12 | int x = routes[i][j]; 13 | b[x].push_back(i); 14 | } 15 | } 16 | } 17 | void init() { 18 | for (int i = 0; i < N; i ++) { 19 | a[i] = -1; 20 | } 21 | for (int i = 0; i < M; i ++) { 22 | vis[i] = false; 23 | } 24 | } 25 | int numBusesToDestination(vector>& routes, int s, int t) { 26 | if (s == t) return 0; 27 | build(routes); 28 | init(); 29 | a[s] = 0; 30 | q.push(s); 31 | while(!q.empty()) { 32 | int x = q.front(); 33 | if (x == t) return a[x]; 34 | q.pop(); 35 | for (int i = 0; i < b[x].size(); i ++) { 36 | int bxi = b[x][i]; 37 | if (!vis[bxi]) { 38 | vis[bxi] = true; 39 | int ax = a[x]; 40 | for (int j = 0; j < routes[bxi].size(); j ++) { 41 | int y = routes[bxi][j]; 42 | if (a[y] == -1 || ax + 1 < a[y]) { 43 | a[y] = ax + 1; 44 | q.push(y); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | a[s] = 1; 51 | return a[t]; 52 | } 53 | }; 54 | 55 | // a[x]表示起点S到节点x所需要乘坐的最少公交车数量 56 | // 假设起点不存在公交线路,则为-1 57 | // 假设起点存在公交线路,公交线路上的所有点到达起点的A值都为1 58 | // routes[i] => r[i] 59 | // i 表示第i辆公交车 60 | // r[i]第i辆公交车的路线 61 | // r[i][j]第i辆公交车第j个站点的站点标志 62 | // s表示起点站点标志 63 | // t表示终点站点标志 64 | // 第一件事,建图 65 | // 最短路问题,权重每次都最多+X(X是非负数且固定值)的话,不需要优先队列 66 | // b[i]表示从第i点能经过的公交车标志(从下到大) 67 | 68 | //结果 69 | /* 70 | 45 / 45 个通过测试用例 71 | 状态:通过 72 | 执行用时:1352 ms 73 | 内存消耗:52 MB 74 | */ 75 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem10_经典模糊串匹配问题/Solution.java: -------------------------------------------------------------------------------- 1 | class Solution { 2 | 3 | boolean dp[][]; 4 | int n, m; 5 | 6 | public boolean isMatch(String s, String p) { 7 | // init 8 | s = "a" + s; 9 | p = "a" + p; 10 | n = s.length(); 11 | m = p.length(); 12 | dp = new boolean[n][]; 13 | for (int i = 0; i < n; i ++) { 14 | dp[i] = new boolean[m]; 15 | for (int j = 0; j < m; j ++) 16 | dp[i][j] = false; 17 | } 18 | // dp 19 | dp[0][0] = true; 20 | for (int i = 0; i < n; i ++) { 21 | for (int j = 0; j < m; j ++) { 22 | if (dp[i][j]) { 23 | char s0 = s.charAt(i); 24 | char p0 = p.charAt(j); 25 | if (i + 1 < n && j - 1 >= 0) { // case0 26 | char s1 = s.charAt(i+1); 27 | char p_1 = p.charAt(j-1); 28 | if (p0 == '*' && (p_1 == '.' || p_1 == s1)) 29 | dp[i+1][j] = true; 30 | } 31 | if (i + 1 < n && j + 1 < m) { // case1 32 | char s1 = s.charAt(i+1); 33 | char p1 = p.charAt(j+1); 34 | if (p1 == '*') { 35 | if (p0 != '*' && (p0 == '.' || p0 == s1)) 36 | dp[i+1][j+1] = true; 37 | } else if (p1 == '.' || p1 == s1) 38 | dp[i+1][j+1] = true; 39 | } 40 | if (j + 1 < m) { // case2 41 | char p1 = p.charAt(j+1); 42 | if (p0 != '*' && p1 == '*') 43 | dp[i][j+1] = true; 44 | if (j + 2 < m) { // case3 45 | char p2 = p.charAt(j+2); 46 | if (p1 != '*' && p2 == '*') 47 | dp[i][j+2] = true; 48 | } 49 | } 50 | } 51 | } 52 | } 53 | // result 54 | return dp[n-1][m-1]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/UVA/problem1395_Kruskal算法求连通图的自定义函数解/solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | const int N = 105; 9 | 10 | struct Edge { 11 | int a; 12 | int b; 13 | int w; 14 | } edge[N * N]; 15 | 16 | int compare(Edge e1, Edge e2) { 17 | return e1.w < e2.w; 18 | } 19 | 20 | int fa[N]; 21 | 22 | int get_father(int x) { 23 | if (fa[x] == x) return fa[x]; 24 | else { 25 | fa[x] = get_father(fa[x]); 26 | return fa[x]; 27 | } 28 | } 29 | 30 | void union_xy(int x, int y) { 31 | int fax = get_father(x); 32 | int fay = get_father(y); 33 | fa[fax] = fay; 34 | } 35 | 36 | int main () { 37 | int n, m; 38 | while (scanf("%d %d", &n, &m) != EOF) { 39 | if (n == 0 && m == 0) break; 40 | 41 | for (int i = 1; i <= m; i ++) { 42 | int a, b, w; 43 | scanf("%d %d %d", &a, &b, &w); 44 | edge[i] = Edge {a, b, w}; 45 | } 46 | 47 | sort(edge + 1, edge + 1 + m, compare); 48 | 49 | int ans = INT_MAX; 50 | for (int i = 1 ; i <= m; i += 1) { 51 | for (int j = 1; j <= n; j ++) fa[j] = j; 52 | int minw = INT_MAX; 53 | int maxw = INT_MIN; 54 | for (int j = i; j <= m; j += 1) { 55 | int faa = get_father(edge[j].a); 56 | int fab = get_father(edge[j].b); 57 | if (faa != fab) { 58 | union_xy(edge[j].a, edge[j].b); 59 | minw = min(minw, edge[j].w); 60 | maxw = max(maxw, edge[j].w); 61 | } 62 | } 63 | bool flag = true; 64 | for (int j = 1; j <= n; j ++) { 65 | if (get_father(j) != get_father(1)) { 66 | flag = false; 67 | break; 68 | } 69 | } 70 | if (flag) { 71 | int minusw = maxw - minw; 72 | ans = min(ans, minusw); 73 | } 74 | } 75 | if (ans == INT_MAX) ans = -1; 76 | printf("%d\n", ans); 77 | } 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/数据结构与算法总结/binarytree/BinarySearchTree.java: -------------------------------------------------------------------------------- 1 | package binarytree; 2 | 3 | import java.util.List; 4 | 5 | public interface BinarySearchTree { 6 | 7 | /** 8 | * Get value if key exists or return null 9 | * @param key 10 | * @return 11 | */ 12 | V get(K key); 13 | 14 | /** 15 | * If key not exists, put value and return
16 | * If key exists, overwrite old value and return new value
17 | * It is not allowed to put null key 18 | * @param key 19 | * @param value 20 | * @return 21 | */ 22 | V put(K key, V value); 23 | 24 | /** 25 | * Put only if key is absent 26 | * @param key 27 | * @param value 28 | * @return 29 | */ 30 | V putIfAbsent(K key, V value); 31 | 32 | /** 33 | * Put key, key 34 | * @param key 35 | * @return V 36 | */ 37 | default V put(K key) { 38 | return put(key, (V) key); 39 | } 40 | 41 | /** 42 | * Put key, key only if key is absent 43 | * @param key 44 | * @return 45 | */ 46 | default V putIfAbsent(K key) { 47 | return putIfAbsent(key, (V) key); 48 | } 49 | 50 | /** 51 | * Remove key and return value if key exists or return null 52 | * @param key 53 | * @return 54 | */ 55 | V remove(K key); 56 | 57 | /** 58 | * Clear all nodes, size -> 0 59 | */ 60 | void clear(); 61 | 62 | /** 63 | * Return numbers of nodes 64 | * @return 65 | */ 66 | int size(); 67 | 68 | /** 69 | * Return height of tres 70 | * @return 71 | */ 72 | int height(); 73 | 74 | /** 75 | * Return list of Entry K, V in Pre-order 76 | * @return 77 | */ 78 | List> entryList(); 79 | 80 | /** 81 | * number of rotation 82 | * @return 83 | */ 84 | int rotateCount(); 85 | 86 | /** 87 | * Entry K, V 88 | * @param 89 | * @param 90 | */ 91 | interface Entry { 92 | 93 | K key(); 94 | 95 | V value(); 96 | 97 | Entry left(); 98 | 99 | Entry right(); 100 | 101 | Entry father(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /深入理解数据库原理/README.md: -------------------------------------------------------------------------------- 1 | - [深入理解数据库原理](#深入理解数据库原理) 2 | - [前言](#前言) 3 | - [数据库的定义](#数据库的定义) 4 | - [如何学习数据库](#如何学习数据库) 5 | - [文章目录](#文章目录) 6 | - [第0章:数据库基础](#第0章数据库基础) 7 | - [第1章:InnoDB数据结构](#第1章InnoDB数据结构) 8 | - [第2章:InnoDB索引与算法](#第2章InnoDB索引与算法) 9 | - [第3章:InnoDB锁与事务](#第3章InnoDB锁与事务) 10 | 11 | # 深入理解数据库原理 12 | 13 | ### 前言 14 | 15 | 开始写本文时,已经是2020年12月底了,在此之前,我写了两大篇幅文章:[深入理解JAVA虚拟机](../深入理解JAVA虚拟机)、[深入理解JAVA并发与集合](../深入理解JAVA并发与集合),这两篇文章都是JAVA基础,固然非常重要,但个人感觉十分缺少实战经验 16 | 17 | 在不断学习的过程中,我接触到了数据库,一个是存储主要业务数据的关系型数据库MySQL(以InnoDB存储引擎为主),一个是主要负责缓存与分布式锁的非关系型数据库Redis,我相信以后还有更多的数据库等着我探索,如MongoDB。我希望把学习过程中的知识点记录下来,以后总会有用 18 | 19 | 前两篇文章写的较为详细,这也意味着要花费较多的时间,但本文篇幅的内容主要是“记录”,记录实战部分、核心理论部分,不将过多冗杂的概念写入文章篇幅中 20 | 21 | **声明** 22 | 23 | 转载请注明出处:https://github.com/peteryuanpan/notebook/blob/master/深入理解数据库原理 24 | 25 | 本文部分内容来自鲁班学院的课程,感谢老师们以及同学们对我的帮助,这里尤其感谢一下周瑜老师 26 | 27 | 参考的书籍:《MySQL技术内幕:InnoDB存储引擎》 28 | 29 | 参考的优秀博文:[行无际的博客](https://www.cnblogs.com/itwild) 30 | 31 | ### 数据库的定义 32 | 33 | 数据库是按照数据结构来组织、存储和管理数据的仓库,是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合 34 | 35 | > A database is an organized collection of data, generally stored and accessed electronically from a computer system. From https://en.wikipedia.org/wiki/Database 36 | 37 | ### 如何学习数据库 38 | 39 | 学习方法 40 | - 看书以及视频,辩证地理解清楚多个基础概念,多做笔记 41 | - 多动手实战,多做项目,模拟调优案例,从应用中理解 42 | 43 | ### 文章目录 44 | 45 | #### 第0章:数据库基础 46 | - [数据库基础](数据库基础.md) 47 | - TODO 48 | 49 | #### 第1章:InnoDB数据结构 50 | - [InnoDB数据结构](InnoDB数据结构.md) 51 | - [文件类型](InnoDB数据结构.md#文件类型) 52 | - [参数文件](InnoDB数据结构.md#参数文件) 53 | - [日志文件](InnoDB数据结构.md#日志文件) 54 | - [错误日志文件](InnoDB数据结构.md#错误日志文件) 55 | - [慢查询日志文件](InnoDB数据结构.md#慢查询日志文件) 56 | - [InnoDB存储引擎文件](InnoDB数据结构.md#InnoDB存储引擎文件) 57 | - [分析工具](InnoDB数据结构.md#分析工具) 58 | - [py_innodb_page_info](InnoDB数据结构.md#py_innodb_page_info) 59 | - [hexdump](InnoDB数据结构.md#hexdump) 60 | - [索引组织表](InnoDB数据结构.md#索引组织表) 61 | - [InnoDB逻辑存储结构](InnoDB数据结构.md#InnoDB逻辑存储结构) 62 | - [InnoDB数据页结构](InnoDB数据结构.md#InnoDB数据页结构) 63 | - [InnoDB行记录格式](InnoDB数据结构.md#InnoDB行记录格式) 64 | - [Compact行记录格式](InnoDB数据结构.md#Compact行记录格式) 65 | - [Redundant行记录格式](InnoDB数据结构.md#Redundant行记录格式) 66 | - [Compressed与Dynamic行格式记录](InnoDB数据结构.md#Compressed与Dynamic行格式记录) 67 | 68 | #### 第2章:InnoDB索引与算法 69 | - [InnoDB索引与算法](InnoDB索引与算法.md) 70 | - TODO 71 | 72 | #### 第3章:InnoDB锁与事务 73 | - [InnoDB锁与事务](InnoDB锁与事务.md) 74 | - TODO 75 | -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/ByteCodeExample/anewarray.md: -------------------------------------------------------------------------------- 1 | # anewarray 2 | 3 | ### code1 4 | 5 | ```java 6 | package com.luban.ziya.newtest; 7 | 8 | public class ANewArrayTest1 { 9 | 10 | public static void main(String[] args) { 11 | Test2[] a = new Test2[3]; 12 | } 13 | 14 | } 15 | 16 | class Test2 { 17 | 18 | static { 19 | System.out.println("11"); 20 | } 21 | 22 | public static String a = "22"; 23 | 24 | static { 25 | System.out.println(a); 26 | System.out.println("33"); 27 | } 28 | 29 | } 30 | ``` 31 | 32 | 输出结果 33 | 34 | 无 35 | 36 | 字节码 37 | ``` 38 | 0 iconst_3 39 | 1 anewarray #2 40 | 4 astore_1 41 | 5 return 42 | ``` 43 | 44 | 解释 45 | 46 | Test2[] a = new Test2[3]; 对应 iconst_3 好 anewarray Test2,这一步是创建一个引用类型数组变量,并不会去进行类加载,因此无输出 47 | 48 | ### code2 49 | 50 | ```java 51 | package com.luban.ziya.newtest; 52 | 53 | public class ANewArrayTest1 { 54 | 55 | public static void main(String[] args) { 56 | Test2[] a = new Test2[3]; 57 | System.out.println("00"); 58 | for (int i = 0; i < 3; i ++) { 59 | a[i].a = "44"; 60 | } 61 | } 62 | 63 | } 64 | 65 | class Test2 { 66 | 67 | static { 68 | System.out.println("11"); 69 | } 70 | 71 | public static String a = "22"; 72 | 73 | static { 74 | System.out.println(a); 75 | System.out.println("33"); 76 | } 77 | 78 | } 79 | ``` 80 | 81 | 输出结果 82 | ``` 83 | 00 84 | 11 85 | 22 86 | 33 87 | ``` 88 | 89 | 字节码 90 | ``` 91 | 0 iconst_3 92 | 1 anewarray #2 93 | 4 astore_1 94 | 5 getstatic #3 95 | 8 ldc #4 <00> 96 | 10 invokevirtual #5 97 | 13 iconst_0 98 | 14 istore_2 99 | 15 iload_2 100 | 16 iconst_3 101 | 17 if_icmpge 35 (+18) 102 | 20 aload_1 103 | 21 iload_2 104 | 22 aaload 105 | 23 pop 106 | 24 ldc #6 <44> 107 | 26 putstatic #7 108 | 29 iinc 2 by 1 109 | 32 goto 15 (-17) 110 | 35 return 111 | ``` 112 | 113 | 解释 114 | 115 | code2在code1基础上多了 116 | ``` 117 | System.out.println("00"); 118 | for (int i = 0; i < 3; i ++) { 119 | a[i].a = "44"; 120 | } 121 | ``` 122 | 123 | Test2[] a = new Test2[3]; 并不会类加载,因此不会输出 124 | 125 | System.out.println("00"); 输出00 126 | 127 | for循环内 a[0].a = "44"; 会触发Test2类加载,因此输出11、a、33,此时a是22 128 | 129 | 继续,a[1].a = "44"; ... a[2].a = "44"; 会尝试类加载,但由于Test2类加载过了,不会再加载,也就没输出了 130 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/UESTC/problem839_线段树区间加法区间求和/solve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | const int N = 100000 + 100; 8 | 9 | int a[N]; 10 | 11 | long long tree[N * 4]; 12 | long long lazy[N * 4]; 13 | 14 | void build(int id, int l, int r) { 15 | if (l == r) { 16 | tree[id] = a[l]; 17 | lazy[id] = 0; 18 | return; 19 | } 20 | int mid = (l + r) >> 1; 21 | build(id<<1, l, mid); 22 | build(id<<1|1, mid + 1, r); 23 | tree[id] = tree[id<<1] + tree[id<<1|1]; 24 | } 25 | 26 | void lazy_update(int id, int l, int r) { 27 | int mid = (l + r) >> 1; 28 | tree[id<<1] += 1LL * (mid - l + 1) * lazy[id]; 29 | lazy[id<<1] += lazy[id]; 30 | tree[id<<1|1] += 1LL * (r - (mid + 1) + 1) * lazy[id]; 31 | lazy[id<<1|1] += lazy[id]; 32 | lazy[id] = 0; 33 | } 34 | 35 | void update(int id, int ql, int qr, int l, int r, int v) { 36 | if (ql == l && qr == r) { 37 | tree[id] += 1LL * (r - l + 1) * v; 38 | lazy[id] += v; 39 | return; 40 | } 41 | 42 | lazy_update(id, l, r); 43 | 44 | int mid = (l + r) >> 1; 45 | if (qr <= mid) update(id<<1, ql, qr, l, mid, v); 46 | else if (mid + 1 <= ql) update(id<<1|1, ql, qr, mid + 1, r, v); 47 | else { 48 | update(id<<1, ql, mid, l, mid, v); 49 | update(id<<1|1, mid + 1, qr, mid + 1, r, v); 50 | } 51 | tree[id] = tree[id<<1] + tree[id<<1|1]; 52 | } 53 | 54 | long long query(int id, int ql ,int qr, int l, int r) { 55 | if (ql == l && qr == r) { 56 | return tree[id]; 57 | } 58 | 59 | lazy_update(id, l, r); 60 | 61 | int mid = (l + r) >> 1; 62 | if (qr <= mid) return query(id<<1, ql, qr, l, mid); 63 | else if (mid + 1 <= ql) return query(id<<1|1, ql, qr, mid + 1, r); 64 | else { 65 | long long sum = 0; 66 | sum += query(id<<1, ql, mid, l, mid); 67 | sum += query(id<<1|1, mid + 1, qr, mid + 1, r); 68 | return sum; 69 | } 70 | } 71 | 72 | int main() { 73 | int n, m; 74 | scanf("%d%d", &n, &m); 75 | for (int i = 1; i <= n; i ++) { 76 | scanf("%d", a + i); 77 | } 78 | 79 | build(1, 1, n); 80 | 81 | for (int i = 1; i <= m; i ++) { 82 | int type; 83 | scanf("%d", &type); 84 | if (type == 0) { 85 | int ql, qr; 86 | scanf("%d%d", &ql, &qr); 87 | long long ans = query(1, ql, qr, 1, n); 88 | printf("%lld\n", ans); 89 | } else { // type == 1 90 | int ql, qr, v; 91 | scanf("%d%d%d", &ql, &qr, &v); 92 | update(1, ql, qr, 1, n, v); 93 | } 94 | } 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem233_求1到X中数字1出现的次数/solve.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | long long f[20], s[20], ten[20]; 4 | int c[20]; 5 | int count_length(int n) { 6 | if (n == 0) return 1; 7 | int l = 0; 8 | while (n != 0) { 9 | l ++; 10 | c[l] = n % 10; 11 | n = n / 10; 12 | } 13 | return l; 14 | } 15 | int countDigitOne(int n) { 16 | f[1] = 1; 17 | s[1] = f[1]; 18 | ten[0] = 1; 19 | ten[1] = 10; 20 | for (int i = 2; i <= 10; i ++) { 21 | ten[i] = ten[i-1] * 10; 22 | f[i] = 9 * s[i-1] + ten[i-1]; 23 | s[i] = s[i-1] + f[i]; 24 | } 25 | int l = count_length(n); 26 | long long ans = s[l-1]; 27 | int t = 0; 28 | for (int i = l; i >= 1; i --) { 29 | for (int j = 0; j <= c[i]; j ++) { 30 | if (i == l && j == 0) continue; 31 | if (i != 1 && j == c[i]) continue; 32 | if (j == 1) ans = ans + s[i-1] + (t + 1) * ten[i-1]; 33 | else ans = ans + s[i-1] + t * ten[i-1]; 34 | } 35 | if (c[i] == 1) t = t + 1; 36 | } 37 | return (int) ans; 38 | } 39 | }; 40 | 41 | 42 | /* 43 | 首先,先要求出一个s函数 44 | s是f函数的总和,即s(x)=f(1)+f(2)+...+f(x) 45 | f函数的定义是,长度x的自然数(不包括前导0)中,一共有多少个1 46 | 显然f(1)=1 47 | 可推导f(x)=8*s(x-1)+s(x-1)+10^(x-1)=9*s(x-1)+10^(x-1) 48 | 通过一个例子来解释f(x)的推导公式 49 | 比如说已经求得了f(4)了,现在要求f(5) 50 | x形如ABBBB 51 | 其中A:{1,2,...,9},B:{0,1,...,9},A不可以为0 52 | 起初f(5)=0 53 | 当A=2时,f(5)=f(5)+f(4) 54 | 当A=3时,f(5)=f(5)+f(4) 55 | ... 56 | 当A=9时,f(5)=f(5)+f(4) 57 | 所以f(x)=8*f(x) 58 | 当A=1时,f(5)=f(5)+f(4)+10^4 59 | 因此f(x)=8*s(x-1)+s(x-1)+10^(x-1)=9*s(x-1)+10^(x-1) 60 | 61 | 这样子,假设l是数字X的长度 62 | ans = s(l) 是答案的一部分了 63 | 还剩下长度为n且小于等于X的数字中1的个数了 64 | 65 | 最后,简单说一下「求长度为l且小于等于X的数字中1的个数」思路 66 | 比如说51321 67 | 我已经求出了{1,2,...,9999}的答案了 68 | 我还需要计算{10000,10001,...,51321}的答案 69 | 这里假设一个t,t表示历史及当前1出现的次数(从高位到低位循环时) 70 | {10000,10001,...,19999}可以一并算出来,即 ans = ans + s(4) + 1 * 10^4,此时 t = 1,因为当前位是1 71 | {20000,20001,...,29999}可以一并算出来,即 ans = ans + s(4) + 0 * 10^4,此时 t = 0 72 | {30000,30001,...,39999}可以一并算出来,即 ans = ans + s(4) + 0 * 10^4,此时 t = 0 73 | {40000,40001,...,49999}可以一并算出来,即 ans = ans + s(4) + 0 * 10^4,此时 t = 0 74 | 还差{50000,50001,...,51321} 75 | {50000,50001,...,50999}可以一并算出来,即 ans = ans + s(3) + 0 * 10^3,此时 t = 0 76 | 由于第二个数是1,则 t = t + 1 = 1 77 | 还差{51000,51001,...,51321} 78 | {51000,51001,...,51099}可以一并算出来,即 ans = ans + s(2) + 1 * 10^2,此时 t = 1,因为第二个数是1,t加了个1 79 | {51100,51101,...,51199}可以一并算出来,即 ans = ans + s(2) + 2 * 10^2,此时 t = 2,当前位是1 80 | {51200,51201,...,51299}可以一并算出来,即 ans = ans + s(2) + 1 * 10^2,此时 t = 1 81 | 还差{51300,51301,...,51321} 82 | 用同样方法可以把后面的都推导出来了,上面这段例子建议结合代码来看 83 | */ 84 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem296_最佳碰头地点/solve.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int n, m; 4 | vector> grid; 5 | int ** ns, ** ls, ** rs; 6 | int * nn, * ln, * rn; 7 | void build() { 8 | ns = new int * [n]; 9 | ls = new int * [n]; 10 | rs = new int * [n]; 11 | for (int i = 0; i < n; i ++) { 12 | ns[i] = new int [m]; 13 | ls[i] = new int [m]; 14 | rs[i] = new int [m]; 15 | for (int j = 0; j < m; j ++) { 16 | ns[i][j] = ls[i][j] = rs[i][j] = 0; 17 | } 18 | } 19 | nn = new int [m]; 20 | ln = new int [m]; 21 | rn = new int [m]; 22 | for (int j = 0; j < m; j ++) { 23 | nn[j] = ln[j] = rn[j] = 0; 24 | } 25 | } 26 | void init() { 27 | for (int j = 0; j < m; j ++) { 28 | int sum = 0, num = 0, is_one = 0; 29 | for (int i = 0; i < n; i ++) { 30 | sum += num + is_one; 31 | num += is_one; 32 | ns[i][j] += sum; 33 | is_one = grid[i][j]; 34 | } 35 | sum = 0, num = 0, is_one = 0; 36 | for (int i = n - 1; i >= 0; i --) { 37 | sum += num + is_one; 38 | num += is_one; 39 | ns[i][j] += sum; 40 | is_one = grid[i][j]; 41 | } 42 | for (int i = 0; i < n; i ++) { 43 | nn[j] += grid[i][j]; 44 | } 45 | } 46 | } 47 | int solve() { 48 | int ans = INT_MAX; 49 | for (int i = 0; i < n; i ++) { 50 | for (int j = 1; j < m; j ++) { 51 | ls[i][j] = ls[i][j-1] + ln[j-1] + ns[i][j-1] + nn[j-1]; 52 | ln[j] = ln[j-1] + nn[j-1]; 53 | } 54 | for (int j = m - 2 ; j >= 0; j --) { 55 | rs[i][j] = rs[i][j+1] + rn[j+1] + ns[i][j+1] + nn[j+1]; 56 | rn[j] = rn[j+1] + nn[j+1]; 57 | } 58 | for (int j = 0; j < m; j ++) { 59 | ans = min(ans, ls[i][j] + ns[i][j] + rs[i][j]); 60 | } 61 | } 62 | return ans; 63 | } 64 | int minTotalDistance(vector>& grid_) { 65 | grid = grid_; 66 | n = grid.size(); 67 | if (n == 0) return 0; 68 | m = grid[0].size(); 69 | build(); 70 | init(); 71 | int ans = solve(); 72 | return ans; 73 | } 74 | }; 75 | 76 | /* 77 | 维护一个ns, nn, ls, ln, rs, rn 78 | ns: nowsum 当前位置列所有1到达当前位置的步数 79 | nn: nownum 当前位置列包含1的个数 80 | ls: leftsum 当前位置列左边(不包含当前列,下同)所有1达到当前位置的步数 81 | ln: leftnow 当前位置列左边包含1的个数 82 | rs: rightsum 当前位置列右边所有1达到当前位置的步数 83 | rn: rightnum 当前位置列右边包含1的个数 84 | 先求出 ns 和 nn 85 | 对于每一列,从上往下扫,再从下往上扫,就可以求出ns 86 | 对于每一列,从上往下扫,就可以求出nn 87 | 对于每一行从左往右扫求出 ls ln 88 | 起初 ls = 0, ln = 0 89 | ls = ls + ln + ns + nn 90 | ln = ln + nn 91 | 对于每一行从右往左扫求出 rs rn 92 | 起初 rs = 0, rn = 0 93 | rs = rs + rn + ns + nn 94 | rn = rn + nn 95 | 对于每一行 96 | ans = min(ans, ls + ns + rs) 97 | */ 98 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1272_problemF_输出串是两个输入串的母序列且规则/solve2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | const int N = 202; 12 | 13 | struct DP { 14 | int i, j, k; 15 | }; 16 | queue q; 17 | 18 | int dp[N][N][2*N]; 19 | int in_q[N][N][2*N]; 20 | DP from[N][N][2*N]; 21 | 22 | char s1[N], s2[N]; 23 | int l1, l2; 24 | 25 | void update(int t, int i, int j, int k, DP f) { 26 | if (t < dp[i][j][k]) { 27 | dp[i][j][k] = t; 28 | from[i][j][k] = f; 29 | if (in_q[i][j][k] == 0) { 30 | struct DP x = {i, j, k}; 31 | q.push(x); 32 | in_q[i][j][k] = 1; 33 | } 34 | } 35 | } 36 | 37 | void spfa() { 38 | while(!q.empty()) q.pop(); 39 | 40 | struct DP x = {0, 0, 0}; 41 | q.push(x); 42 | dp[0][0][0] = 0; 43 | in_q[0][0][0] = 1; 44 | 45 | while (!q.empty()) { 46 | DP x = q.front(); 47 | q.pop(); 48 | in_q[x.i][x.j][x.k] = 0; 49 | 50 | if (x.i == l1 && x.j == l2 && x.k == 0) break; 51 | 52 | int t = dp[x.i][x.j][x.k] + 1; 53 | 54 | /* for ( */ 55 | if (x.i < l1 && s1[x.i] == '(' && x.j < l2 && s2[x.j] == '(') { 56 | update(t, x.i+1, x.j+1, x.k+1, x); 57 | } 58 | if (x.i < l1 && s1[x.i] == '(') { 59 | update(t, x.i+1, x.j, x.k+1, x); 60 | } 61 | if (x.j < l2 && s2[x.j] == '(') { 62 | update(t, x.i, x.j+1, x.k+1, x); 63 | } 64 | update(t, x.i, x.j, x.k+1, x); 65 | 66 | /* for ) */ 67 | if (x.k > 0) { 68 | if (x.i < l1 && s1[x.i] == ')' && x.j < l2 && s2[x.j] == ')') { 69 | update(t, x.i+1, x.j+1, x.k-1, x); 70 | } 71 | if (x.i < l1 && s1[x.i] == ')') { 72 | update(t, x.i+1, x.j, x.k-1, x); 73 | } 74 | if (x.j < l2 && s2[x.j] == ')') { 75 | update(t, x.i, x.j+1, x.k-1, x); 76 | } 77 | update(t, x.i, x.j, x.k-1, x); 78 | } 79 | } 80 | } 81 | 82 | int main() { 83 | cin >> s1 >> s2; 84 | l1 = (int) strlen(s1); 85 | l2 = (int) strlen(s2); 86 | 87 | for (int i = 0; i < N; i ++) { 88 | for (int j = 0; j < N; j ++) { 89 | for (int k = 0; k < N; k ++) { 90 | dp[i][j][k] = INT_MAX; 91 | in_q[i][j][k] = 0; 92 | } 93 | } 94 | } 95 | 96 | spfa(); 97 | 98 | string ans; 99 | int i = l1, j = l2, k = 0; 100 | while (i != 0 || j !=0 || k != 0) { 101 | DP f = from[i][j][k]; 102 | ans = ans + (f.k < k ? "(" : ")"); 103 | i = f.i; 104 | j = f.j; 105 | k = f.k; 106 | } 107 | reverse(ans.begin(), ans.end()); 108 | cout << ans << endl; 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem4_求两个升序数列的中位数/solve3.go: -------------------------------------------------------------------------------- 1 | func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { 2 | n1 := len(nums1) 3 | n2 := len(nums2) 4 | if n1 == 0 { 5 | return findMedianSortedArrays(nums2, nums1) 6 | } 7 | if ((n1 + n2) % 2 == 1) { 8 | return cal(nums1, nums2, (n1 + n2) / 2 + 1) 9 | } else { 10 | ans1 := cal(nums1, nums2, (n1 + n2) / 2) 11 | ans2 := cal(nums1, nums2, (n1 + n2) / 2 + 1) 12 | return (ans1 + ans2) / 2 13 | } 14 | } 15 | func cal(nums1 []int, nums2 []int, target_n int) float64 { 16 | l := 0 17 | r := len(nums1) - 1 18 | for { 19 | if l == r { 20 | break 21 | } 22 | m := (l + r) / 2 23 | y := findFirstLargerIndex(nums2, nums1[m]) 24 | if m + 1 + y == target_n { 25 | return float64(nums1[m]) 26 | } else if m + 1 + y > target_n { 27 | r = m 28 | } else { 29 | l = m + 1 30 | } 31 | } 32 | type Pair struct { 33 | nums []int 34 | index int 35 | } 36 | var pairs [2]Pair 37 | pairs[0] = Pair { 38 | nums: nums1, 39 | index: l, 40 | } 41 | pairs[1] = Pair { 42 | nums: nums2, 43 | index: findFirstLargerIndex(nums2, nums1[l]) - 1, 44 | } 45 | for { 46 | sum := 0 47 | for i := 0; i < len(pairs); i ++ { 48 | sum += pairs[i].index + 1 49 | } 50 | if sum == target_n { 51 | break 52 | } 53 | if sum > target_n { 54 | var max_pair *Pair = nil 55 | for i := 0; i < len(pairs); i ++ { 56 | if pairs[i].index >= 0 && pairs[i].index < len(pairs[i].nums) { 57 | if max_pair == nil || pairs[i].nums[pairs[i].index] > max_pair.nums[max_pair.index] { 58 | max_pair = &pairs[i] 59 | } 60 | } 61 | } 62 | max_pair.index -- 63 | } else { 64 | var min_pair *Pair = nil 65 | for i := 0; i < len(pairs); i ++ { 66 | if pairs[i].index + 1 >= 0 && pairs[i].index + 1 < len(pairs[i].nums) { 67 | if min_pair == nil || pairs[i].nums[pairs[i].index+1] < min_pair.nums[min_pair.index+1] { 68 | min_pair = &pairs[i] 69 | } 70 | } 71 | } 72 | min_pair.index ++ 73 | } 74 | } 75 | var max_pair *Pair = nil 76 | for i := 0; i < len(pairs); i ++ { 77 | if pairs[i].index >= 0 && pairs[i].index < len(pairs[i].nums) { 78 | if max_pair == nil || pairs[i].nums[pairs[i].index] > max_pair.nums[max_pair.index] { 79 | max_pair = &pairs[i] 80 | } 81 | } 82 | } 83 | return float64(max_pair.nums[max_pair.index]) 84 | } 85 | func findFirstLargerIndex(nums []int, target_x int) int { 86 | l := 0 87 | r := len(nums) 88 | for { 89 | if l == r { 90 | break 91 | } 92 | m := (l + r) / 2 93 | if nums[m] <= target_x { 94 | l = m + 1 95 | } else { 96 | r = m 97 | } 98 | } 99 | return l 100 | } 101 | -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/JVM总结.md: -------------------------------------------------------------------------------- 1 | - [JVM总结](#JVM总结) 2 | - [为什么说JAVA是一种半编译半解释型语言](#为什么说JAVA是一种半编译半解释型语言) 3 | - [什么情况下需要破坏双亲委派模型](#什么情况下需要破坏双亲委派模型) 4 | - [GCRoots的作用以及哪些引用可以作为GCRoots](#GCRoots的作用以及哪些引用可以作为GCRoots) 5 | - [说一下对象内存分配及进入老年代的策略](#说一下对象内存分配及进入老年代的策略) 6 | - [说下常用垃圾收集器的工作原理](#说下常用垃圾收集器的工作原理) 7 | - [如何选择一款垃圾收集器](#如何选择一款垃圾收集器) 8 | - [GC调优策略经验总结](#GC调优策略经验总结) 9 | 10 | # JVM总结 11 | 12 | ### 为什么说JAVA是一种半编译半解释型语言 13 | 14 | JAVA代码并不像C++那样,是一步将代码编译成机器码,交由CPU处理器执行的,而是先编译成class文件,然后交由虚拟机运行,虚拟机还需要翻译字节码成机器码,这就是“半”的含义 15 | 16 | 体现在两个方面,一是 JAVAC 编译,JAVA 运行,二是解释器解释执行,编译器编译执行 17 | 18 | JAVA代码通过JAVAC编译器编译成class文件,虚拟机运行时通过类加载读入class文件,转化为方法区中的类的元信息数据,同时启动Main类的main方法,通过字节码执行引擎执行字节码 19 | 20 | 字节码是不能被CPU处理器识别的指令,它需要被翻译成汇编码和机器码,翻译的过程最初只有解释器逐行解释执行,后来演化出了编译器将热点代码直接编译成汇编码或机器码的方式来编译执行,还演化出了如栈上分配、指令重排序等优化技术 21 | 22 | ### 什么情况下需要破坏双亲委派模型 23 | 24 | ClassLoader 类中的 loadClass 方法负责进行类加载,其中的关键逻辑是,如果存在父类加载器,优先交给父类加载器加载,若加载失败,则交由 findClass 方法加载,这就是双拼委派模型的实现 25 | 26 | 双亲委派模型是对系统关键类的保护机制,防止JDK中的类被子类加载器给加载了,它应当就只能被启动类加载器加载 27 | 28 | 而有些场景下,需要让父类加载器去加载非它管辖范围内的类时,就需要打破双亲委派了,比如 java.sql.DriverManager 是由启动类加载器管辖的,但各厂商对 java.sql.Driver 有不同的实现类,此时启动类加载器就需要委托应用程序类加载器加载实现类了,这就破坏了双亲委派模型。这里具体代码可见 DriverManager 类的 getConnection、isDriverAllowed 方法 29 | 30 | ### GCRoots的作用以及哪些引用可以作为GCRoots 31 | 32 | GCRoots 与可达性分析算法有关,可达性分析算法是用于扫描已创建的对象哪些是没有被直接或间接引用,可以被回收的了,算法中采用了类似图论的方式,将众多引用到对象的有向边联结起来,构建成一张图,那么图的许多起点就是 GCRoots,从 GCRoots 开始能到达的对象说明存在引用关系,而不能到达的对象认为可以被回收 33 | 34 | 可以作为GCRoots的引用有 35 | - 栈帧的局部变量表中的引用 36 | - 方法区中类变量的引用 37 | - 字符串常量池中的引用 38 | - synchonized持有的对象引用 39 | 40 | ### 说一下对象内存分配及进入老年代的策略 41 | 42 | TODO 43 | 44 | ### 说下常用垃圾收集器的工作原理 45 | 46 | TODO 47 | 48 | ### 如何选择一款垃圾收集器 49 | 50 | 多个角度:吞吐量、延迟、内存占用、硬件情况、JDK版本号 51 | 52 | 根据应用场景 53 | - 如果是数据分析、科学计算类的任务,目标是能尽快算出结果,那吞吐量就是主要关注点 54 | - 如果是SLA应用,那停顿时间直接影响服务质量,严重的甚至会导致事务超时,这样延迟就是主要关注点 55 | - 如果是客户端应用或者嵌入式应用,那垃圾收集的内存占用则是不可忽视的 56 | 57 | 根据经验值之谈 58 | - 在小内存应用上CMS的表现大概率仍然要会优于G1,而在大内存应用上G1则大多能发挥其优势,这个优劣势的Java堆容量平衡点通常在6GB至8GB之间 59 | - 如果应用目前采用的垃圾收集器没有出现问题,那就有必要更换,更没有必要去使用G1 60 | - 如果应用追求低延迟停顿,且应用内存足够满足G1要求,G1是一个可以作为尝试的选择,否则使用CMS 61 | - 如果应用追求吞吐量,用G1不会带来什么特别的好处,可以考虑ParallelGC 62 | - JDK9中默认垃圾收集器是G1,而JDK8中默认垃圾收集器是ParallelGC 63 | 64 | 当然,以上仅都是理论和经验的总结,实战中切不可纸上谈兵,根据系统实际情况去测试才是选择收集器的最终依据 65 | 66 | ### GC调优策略经验总结 67 | 68 | JVM调优经验基本可以等价于GC调优经验,试想,如果GC堆能稳定分配对象并清理垃圾,不会出现OOM、慢执行的情况,那么还有什么是需要调优的呢 69 | 70 | 因此,但凡谈到JVM调优,都可以往GC堆、GC策略、新生代、老年代方面去谈 71 | 72 | 下面是经验总结(供参考) 73 | - (1)如果满足这样的指标,则一般不需要进行 GC 优化。即 MinorGC 执行时间不到 50ms,Minor GC 执行不频繁,约 10秒 到 120秒一次(看具体业务)。Full GC 执行时间不到 1秒,Full GC 执行频率不算频繁,约 1小时一次(看具体业务) 74 | - (2)项目上线前,要设置好虚拟机堆区的最大值以及内部区域的比例值,通过监控工具(VisualVM等)测试观察堆区内存变化,是否会频繁发生Full GC,是否可能会内存溢出。同时要设置好虚拟机参数,比如 -XX:+HeapDumpOnOutOfMemoryError,以便能在OOM 时生成堆转储快照 75 | - (3)优化思路,将新对象预留在新生代,由于 Full GC 的成本远高于 Minor GC,因此尽可能将对象分配在新生代是明智的做法,实际项目中根据 GC 日志分析新生代空间大小分配是否合理,适当通过 -Xmn 命令调节新生代大小,最大限度降低新对象直接进入老年代的情况 76 | - (4)大对象进入老年代,虽然大部分情况下,将对象分配在新生代是合理的,但是对于大对象这种做法却值得商榷,大对象如果首次在新生代分配可能会出现空间不足导致很多年龄不够的小对象被分配的老年代,破坏新生代的对象结构,可能会出现频繁的 Full GC。因此,对于大对象,可以设置直接进入老年代(当然短命的大对象对于垃圾回收来说简直就是噩梦,写代码应避免之)。-XX:PretenureSizeThreshold 可以设置直接进入老年代的对象大小 77 | - (5)合理设置进入老年代对象的年龄,-XX:MaxTenuringThreshold 设置对象进入老年代的年龄大小(最高为15),减少老年代的内存占用,降低 Full GC 发生的频率。同时还要注意虚拟机的一个策略————动态年龄判断 78 | - (6)当出现 OOM 或 GC Overhead Limit Exceeded 时,生成内存堆转储快照或者GC日志,可以使用VisualVM来进行分析,判断内存泄漏还是内存溢出,如果是内存泄漏,则使用工具对GC Roots的引用链进行分析,判断为何垃圾收集器无法回收这些对象;如果是内存溢出,那就应当检查虚拟机的堆参数(-Xmx、-Xms、-Xmn),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗 79 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/FromBook/problem3_求二叉搜索树的所有生成数组/Solution2.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for a binary tree node. 3 | * public class TreeNode { 4 | * int val; 5 | * TreeNode left; 6 | * TreeNode right; 7 | * TreeNode(int x) { val = x; } 8 | * } 9 | */ 10 | class Solution2 { 11 | 12 | private final int N = 11; 13 | 14 | private class Mask { 15 | List mask = new ArrayList<>(); 16 | }; 17 | private Mask[][] masks = new Mask[N][N]; 18 | 19 | public Solution() { 20 | for (int i = 0; i < N; i ++) { 21 | for (int j = 0; j < N; j ++) { 22 | masks[i][j] = new Mask(); 23 | } 24 | } 25 | for (int l = 1; l < N; l ++) { 26 | for (int i = 0; i < (1<> dd; 39 | public Result() { 40 | dd = new ArrayList<>(); 41 | dd.add(new ArrayList<>()); 42 | } 43 | }; 44 | 45 | private Result dfs(TreeNode root) { 46 | Result res = new Result(); 47 | if (root != null) { 48 | Result left = dfs(root.left); 49 | Result right = dfs(root.right); 50 | int ln = left.dd.get(0).size(); 51 | int rn = right.dd.get(0).size(); 52 | res.dd.clear(); // important! 53 | if (ln == 0 && rn == 0) { 54 | List d = new ArrayList<>(); 55 | d.add(root.val); 56 | res.dd.add(d); 57 | } else if (ln == 0) { 58 | for (List rd : right.dd) { 59 | List d = new ArrayList<>(); 60 | d.add(root.val); 61 | d.addAll(rd); 62 | res.dd.add(d); 63 | } 64 | } else if (rn == 0) { 65 | for (List ld : left.dd) { 66 | List d = new ArrayList<>(); 67 | d.add(root.val); 68 | d.addAll(ld); 69 | res.dd.add(d); 70 | } 71 | } else { 72 | List mask = masks[ln][rn].mask; 73 | for (List ld : left.dd) { 74 | for (List rd : right.dd) { 75 | for (Integer i : mask) { 76 | List d = new ArrayList<>(); 77 | d.add(root.val); 78 | int tl = 0, tr = 0; 79 | for (int j = 0; j < (ln + rn); j ++) { 80 | if ((i & (1<> BSTSequences(TreeNode root) { 98 | return dfs(root).dd; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem248_求a到b中有多少个翻转对称数/solve.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | string sub_one(string s) { 4 | for (int i = s.length() - 1; i >= 0; i --) { 5 | if (s[i] == '0') { 6 | s[i] = '9'; 7 | } else { 8 | s[i] = (s[i] - '0' - 1) + '0'; 9 | break; 10 | } 11 | } 12 | while (s.length() > 1 && s[0] == '0') s.erase(0, 1); 13 | return s; 14 | } 15 | 16 | long long f[30][2]; 17 | void init() { 18 | f[1][1] = 2; // (1,8) 19 | f[1][0] = 3; // (0,1,8) 20 | f[2][1] = 4; // (11,88,69,96) 21 | f[2][0] = 5; // (11,88,69,96,00) 22 | for (int i = 3; i < 30; i ++) { 23 | f[i][1] = f[i-2][0] * 4; 24 | f[i][0] = f[i-2][0] * 5; 25 | } 26 | } 27 | 28 | int solve(string s) { 29 | int n = s.length(); 30 | long long ans = 0; 31 | for (int i = 1; i < n; i ++) { 32 | if (i == 1) ans = ans + f[i][0]; 33 | else ans = ans + f[i][1]; 34 | } 35 | bool ok = true; 36 | for (int i = 0; i <= (n - 1) / 2; i ++) { 37 | for (int j = 0; j < (s[i] - '0'); j ++) { 38 | if (n > 1 && i == 0 && j == 0) continue; 39 | if (i == (n - 1) / 2) { 40 | if (n % 2 == 1) { 41 | if (j == 0 || j == 1 || j == 8) ans = ans + 1; 42 | } else { 43 | if (j == 0 || j == 1 || j == 6 || j == 8 || j == 9) ans = ans + 1; 44 | } 45 | } else { 46 | if (j == 0 || j == 1 || j == 6 || j == 8 || j == 9) { 47 | ans = ans + f[n-2*(i+1)][0]; 48 | } 49 | } 50 | } 51 | int d1 = (s[i] - '0'); 52 | if (d1 != 0 && d1 != 1 && d1 != 6 && d1 != 8 && d1 != 9) { 53 | ok = false; 54 | break; 55 | } 56 | int d2 = (s[n-i-1] - '0'); 57 | if (d1 == 0 || d1 == 1 || d1 == 8) { 58 | if (d1 > d2) ok = false; 59 | } 60 | if (d1 == 6 && 9 > d2) ok = false; 61 | if (d1 == 9 && 6 > d2) ok = false; 62 | if (ok && i == (n - 1) / 2) { 63 | if (n % 2 == 1) { 64 | if (d1 == 0 || d1 == 1 || d1 == 8) ans = ans + 1; 65 | } else { 66 | ans = ans + 1; 67 | } 68 | } 69 | } 70 | return ans; 71 | } 72 | 73 | int strobogrammaticInRange(string low, string high) { 74 | init(); 75 | long long ans; 76 | if (low == "0") ans = solve(high); 77 | else ans = solve(high) - solve(sub_one(low)); 78 | return (int) max(ans, 0LL); 79 | } 80 | }; 81 | 82 | /* 83 | 这题,整体思路:由中心向两边拓展时动态规划 84 | 85 | 0 1 6 8 9 是关键数字,其余2 3 4 5 7都可忽略 86 | 可以考虑用DP的方法求解 87 | ans[low,high]=ans[0,high] - ans[0,low-1] 88 | ans[i]表示[0,i]中的答案 89 | 90 | 假设求1-X的答案,X的长度为n 91 | 那么长度为[1,n),即数值为[0,10^n)的数答案和都能很快求出来 92 | dp[i]表示长度为i时的答案和 93 | dp[1]和dp[2]可以拼接算出来 94 | 当i>=3时,可以通过dp[i-2]推得出dp[i],注意0的情况即可 95 | 为了考虑0的情况,我们多引入一维 96 | f[i][0]表示长度为i时包含前后导0的答案 97 | f[i][1]标示长度为i时不包含前后导0的答案 98 | 对于奇数 99 | f[1][1]=2 (1,8) 100 | f[1][0]=3 (0,1,8) 101 | f[3][1]=12 (101,609,808,906,111,619,818,916,181,689,888,986) 102 | f[3][0]=15 (上面的基础上,000,010,080) 103 | 对于偶数 104 | f[2][1]=4 (11,88,69,96) 105 | f[2][0]=5 (11,88,69,96,00) 106 | 那么 107 | f[i][1]=f[i-2][0]*4 108 | f[i][0]=f[i-2][0]*5 109 | dp[i]=f[1][0]+f[2][1]+...+f[i][1] 110 | 111 | 对于长度为n,且小于high的情况怎么办呢? 112 | 其实就是在上面的思路基础上,限制一下每个位置的字符集上限 113 | 相当于,之前的字符集是{0,1,...,9},现在是{0,1,...,x},x是对称两个位置数的最小值 114 | 需要分奇偶来判断,看代码吧 115 | */ 116 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Leetcode/problem65_求一个数是否是有效数/solve.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | bool isNumber(string s) { 4 | //cout << s << endl; 5 | int c, p, d; 6 | int n = (int) s.length(); 7 | // 空字符串 8 | if (n == 0) return false; 9 | // 去掉前导后导空格 10 | while (!s.empty() && s[0] == ' ') s.erase(0, 1); 11 | while (!s.empty() && s.back() == ' ') s.erase(s.length() - 1, 1); 12 | if (s.length() != n) return isNumber(s); 13 | // 判断字符集 14 | for (int i = 0; i < n; i ++) { 15 | if (!isdigit(s[i]) && s[i] != 'e' && s[i] != '.' && s[i] != '+' && s[i] != '-') { 16 | return false; 17 | } 18 | } 19 | // 求e出现的次数及位置 20 | c = 0; 21 | for (int i = 0; i < n; i ++) { 22 | if (s[i] == 'e') { 23 | c ++; 24 | p = i; 25 | } 26 | } 27 | // e最多出现一次 28 | if (c > 1) return false; 29 | if (c == 1) { 30 | // e后面不能出现小数点了 31 | for (int i = p + 1; i < n; i ++) { 32 | if (s[i] == '.') return false; 33 | } 34 | } 35 | if (c == 1) { 36 | // 根据 e 拆分 37 | // e前后必须有字符,不能为空:由于最开始判断了空字符串,这种情况也考虑了 38 | return isNumber(s.substr(0, p)) && isNumber(s.substr(p + 1, n)); 39 | } 40 | // 往下就没有 e 了 41 | // 求小数点出现的次数及位置 42 | c = 0; 43 | p = 0; 44 | for (int i = 0; i < n; i ++) { 45 | if (s[i] == '.') { 46 | c ++; 47 | p = i; 48 | } 49 | } 50 | // 小数点最多出现一次 51 | if (c > 1) return false; 52 | if (c == 1) { 53 | // 小数点前后至少有一个数字 54 | if (p == 0 && p == n - 1) return false; // 只有一个点 55 | else if ((p == 0 || !isdigit(s[p-1])) && (p == n - 1 || !isdigit(s[p+1]))) return false; 56 | // 如果只有一个时,前后缺的位置补上0 57 | if (p == 0 || !isdigit(s[p-1])) { // 往前补0 58 | s.insert(p, "0"); 59 | return isNumber(s); 60 | } 61 | if (p == n - 1 || !isdigit(s[p+1])) { // 往后补0 62 | s.insert(p + 1, "0"); 63 | return isNumber(s); 64 | } 65 | } 66 | // 求+-出现的次数及状态 67 | c = 0; 68 | d = 0; 69 | for (int i = 0; i < n; i ++) { 70 | if (s[i] == '+') { 71 | d |= 1; 72 | c ++; 73 | } 74 | else if (s[i] == '-') { 75 | d |= 2; 76 | c ++; 77 | } 78 | } 79 | // +-不能同时出现 80 | if (d == 3) return false; 81 | if (d == 1 || d == 2) { 82 | // +或-最多只能出现一次 83 | if (c > 1) return false; 84 | // +-如果出现,必须在第一位 85 | if (s[0] != '+' && s[0] != '-') return false; 86 | // 只有+-也不行 87 | if (s.length() == 1) return false; 88 | } 89 | // 都判断完了 90 | return true; 91 | } 92 | }; 93 | 94 | /* 95 | 0. 前导0是可以的 96 | 1. 空字符串不允许 97 | 2. 空格情况,去掉前导及后导空格再判断 98 | 3. 判断字符集[0,1,2,3,4,5,6,7,8,9,e,+,-,.],其他超过这个字符集的,都算false 99 | 4. e最多出现1次吗??是的,e的含义是指数,而不是数学中的那个e=2.71828... 100 | 5. e后面不能再出现小数点了,比如[2e0.1、3e1.1],都是false 101 | 102 | 考虑 103 | 一,+-如果出现,必须在第一位?不一定 104 | 比如[6e-1、+6e-2、-0e-2、-0.e-2],都是true,这就需要找到e的位置,拆分前后来判断了 105 | 二,+-不能同时出现?不一定 106 | 三,e前后必须有字符,且为数字?不一定 107 | 四,小数点最多出现一次?不一定 108 | 如下 109 | 6.首先,找到e的位置,前后拆分,独立判断 110 | 此时 111 | 7. e前后必须有字符,不能为空 112 | 8. 小数点最多出现一次 113 | 9. 小数点前后必须有字符,且为数字???不一定 114 | 有几种特殊情况 115 | 一、[.0、0.、-.0、+0.e1、-.1e2],都是true,可以有一个办法,即小数点前后不是数字时,默认补个0,这样就变为[0.0、0.0、-0.0、+0.0e1、-0.1e2] 116 | 二,但不能只有一个点,比如[.、+.、-.],结果都是false 117 | 因此可以总结为:小数点前后至少有一个数字,如果只有一个时,前后缺的位置补上0 118 | 119 | 10. +-不能同时出现 120 | 11. +或-最多只能出现一次 121 | 12. +-如果出现,必须在第一位 122 | 13. 只有+-也不行 123 | 124 | 测试集 125 | true: 126 | [0.1、123、01、001、000、000e0、+0、+1、-0、+2e2、.0、0.、-.0、-.1e2、1e+0、-.0e-0、+0.e1、-.1e2] 127 | ["1 "、" -.0e-0"] 128 | 129 | false: 130 | [a1、1+、+-1、0.1e、+e、-e、+1e、ee、1e1e2、.e1、e1、2e0.1、3e1.1、.、+.、-.、+、-、4e+] 131 | ["1 e 2"、" -.0e -0"、" "、""] 132 | 133 | */ 134 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/Codeforces/contest1272_problemF_输出串是两个输入串的母序列且规则/solve1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | const int N = 202; 12 | 13 | struct DP { 14 | int i, j, k; 15 | }; 16 | queue q; 17 | 18 | int dp[N][N][2*N]; 19 | int in_q[N][N][2*N]; 20 | 21 | char s1[N], s2[N]; 22 | int l1, l2; 23 | 24 | void update(int t, int i, int j, int k) { 25 | if (t < dp[i][j][k]) { 26 | dp[i][j][k] = t; 27 | if (in_q[i][j][k] == 0) { 28 | struct DP x = {i, j, k}; 29 | q.push(x); 30 | in_q[i][j][k] = 1; 31 | } 32 | } 33 | } 34 | 35 | void spfa() { 36 | struct DP x = {0, 0, 0}; 37 | q.push(x); 38 | dp[0][0][0] = 0; 39 | in_q[0][0][0] = 1; 40 | 41 | while (!q.empty()) { 42 | DP x = q.front(); 43 | q.pop(); 44 | in_q[x.i][x.j][x.k] = 0; 45 | 46 | int t = dp[x.i][x.j][x.k] + 1; 47 | 48 | /* for ( */ 49 | if (x.i < l1 && s1[x.i] == '(' && x.j < l2 && s2[x.j] == '(') { 50 | update(t, x.i+1, x.j+1, x.k+1); 51 | } 52 | if (x.i < l1 && s1[x.i] == '(') { 53 | update(t, x.i+1, x.j, x.k+1); 54 | } 55 | if (x.j < l2 && s2[x.j] == '(') { 56 | update(t, x.i, x.j+1, x.k+1); 57 | } 58 | update(t, x.i, x.j, x.k+1); 59 | 60 | /* for ) */ 61 | if (x.k > 0) { 62 | if (x.i < l1 && s1[x.i] == ')' && x.j < l2 && s2[x.j] == ')') { 63 | update(t, x.i+1, x.j+1, x.k-1); 64 | } 65 | if (x.i < l1 && s1[x.i] == ')') { 66 | update(t, x.i+1, x.j, x.k-1); 67 | } 68 | if (x.j < l2 && s2[x.j] == ')') { 69 | update(t, x.i, x.j+1, x.k-1); 70 | } 71 | update(t, x.i, x.j, x.k-1); 72 | } 73 | } 74 | } 75 | 76 | void find_ans(int i, int j, int k) { 77 | if (i == 0 && j == 0 && k == 0) { 78 | return; 79 | } 80 | 81 | /* for ( */ 82 | if (k > 0) { 83 | if (i > 0 && j > 0 && dp[i-1][j-1][k-1] + 1 == dp[i][j][k] && s1[i-1] == '(' && s2[j-1] == '(') { 84 | find_ans(i-1, j-1, k-1); 85 | printf("("); 86 | return; 87 | } 88 | if (i > 0 && dp[i-1][j][k-1] + 1 == dp[i][j][k] && s1[i-1] == '(') { 89 | find_ans(i-1, j, k-1); 90 | printf("("); 91 | return; 92 | } 93 | if (j > 0 && dp[i][j-1][k-1] + 1 == dp[i][j][k] && s2[j-1] == '(') { 94 | find_ans(i, j-1, k-1); 95 | printf("("); 96 | return; 97 | } 98 | if (dp[i][j][k-1] + 1 == dp[i][j][k]) { 99 | find_ans(i, j, k-1); 100 | printf("("); 101 | return; 102 | } 103 | } 104 | 105 | /* for ) */ 106 | if (i > 0 && j > 0 && dp[i-1][j-1][k+1] + 1 == dp[i][j][k] && s1[i-1] == ')' && s2[j-1] == ')') { 107 | find_ans(i-1, j-1, k+1); 108 | printf(")"); 109 | return; 110 | } 111 | if (i > 0 && dp[i-1][j][k+1] + 1 == dp[i][j][k] && s1[i-1] == ')') { 112 | find_ans(i-1, j, k+1); 113 | printf(")"); 114 | return; 115 | } 116 | if (j > 0 && dp[i][j-1][k+1] + 1 == dp[i][j][k] && s2[j-1] == ')') { 117 | find_ans(i, j-1, k+1); 118 | printf(")"); 119 | return; 120 | } 121 | if (dp[i][j][k+1] + 1 == dp[i][j][k]) { 122 | find_ans(i, j, k+1); 123 | printf(")"); 124 | return; 125 | } 126 | } 127 | 128 | int main() { 129 | cin >> s1 >> s2; 130 | l1 = (int) strlen(s1); 131 | l2 = (int) strlen(s2); 132 | 133 | for (int i = 0; i < N; i ++) { 134 | for (int j = 0; j < N; j ++) { 135 | for (int k = 0; k < N; k ++) { 136 | dp[i][j][k] = INT_MAX; 137 | in_q[i][j][k] = 0; 138 | } 139 | } 140 | } 141 | 142 | spfa(); 143 | 144 | find_ans(l1, l2, 0); 145 | 146 | return 0; 147 | } 148 | -------------------------------------------------------------------------------- /深入理解JAVA并发与集合/并发与集合总结.md: -------------------------------------------------------------------------------- 1 | - [并发与集合总结](#并发与集合总结) 2 | - [进程与线程的区别是什么](#进程与线程的区别是什么) 3 | - [线程与线程之间通信有哪些方式](#线程与线程之间通信有哪些方式) 4 | - [进程与进程之间通信有哪些方式](#进程与进程之间通信有哪些方式) 5 | - [说一下线程的6种状态及转换关系](#说一下线程的6种状态及转换关系) 6 | - [说一下线程中断机制](#说一下线程中断机制) 7 | - [说一下ThreadLocal的使用场景以及原理](#说一下ThreadLocal的使用场景以及原理) 8 | - [ThreadLocal为什么会导致内存泄漏以及如何避免](#ThreadLocal为什么会导致内存泄漏以及如何避免) 9 | - [JDK8中对并发做了哪些优化](#JDK8中对并发做了哪些优化) 10 | - [说一下并发中的三大特性](#说一下并发中的三大特性) 11 | - [说一下对volatile的理解](#说一下对volatile的理解) 12 | - [volatile为何不能保证原子性](#volatile为何不能保证原子性) 13 | - [synchronized与Lock锁的相同与不同点](#synchronized与Lock锁的相同与不同点) 14 | - [说一下AQS中同步队列与条件队列的原理](#说一下AQS中同步队列与条件队列的原理)、 15 | - [说一下几个实现了AQS应用的使用场景](#说一下几个实现了AQS应用的使用场景) 16 | - [线程池可解决的问题及适用的场景](#线程池可解决的问题及适用的场景) 17 | - [线程池为何能做到线程复用](#线程池为何能做到线程复用) 18 | - [说一下线程池中重要的参数及含义](#说一下线程池中重要的参数及含义) 19 | 20 | # 并发与集合总结 21 | 22 | ### 进程与线程的区别是什么 23 | 24 | 区别点 25 | - 进程是CPU资源分配的最小单位,线程是CPU调度和执行的最小单位 26 | - 线程也被称为轻量级进程,一个线程只能属于一个进程,而一个进程可以有一至多个线程 27 | - 进程与进程之间资源是相互独立的,线程与线程之间资源是共享的 28 | - 操作系统以进程为单位分配系统资源,硬盘上的代码和数据加载到独立的内存体供进程使用 29 | - 程序计数器被设计为线程私有的,用于保存线程执行位置,也侧面说明了线程是调度和执行的最小单位 30 | 31 | ### 线程与线程之间通信有哪些方式 32 | 33 | 线程中断机制、等待唤醒机制、还有吗 ? 34 | 35 | ### 进程与进程之间通信有哪些方式 36 | 37 | TODO 38 | 39 | ### 说一下线程的6种状态及转换关系 40 | 41 | ![image](https://user-images.githubusercontent.com/10209135/97520335-eecbe380-19d5-11eb-8f81-5a6ce563b594.png) 42 | 43 | NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 44 | 45 | RUNNALE 在 JVM 内部分为 READY(就绪)和 RUNNING(运行中)两种状态 46 | 47 | 线程新建后处于 NEW 状态,线程运行 start 方法或者被唤醒后,一般是先处于 READY 状态的,在获取了时间片后才处于 RUNNING 状态 48 | 49 | Object、Thread、Locksuport 均有方法能让线程进入 WAITING 状态或者 TIMED_WAITING 状态 50 | 51 | 线程在等待获取 synchonized 监视器锁时,是 BLOCKED 状态 52 | 53 | 平时我们口语所说的“阻塞”,往往包含了 BLOCKED 与 WAITING 两种状态,把 Object#wait()、LockSupport.park() 等动作进入的等待状态也称为了“阻塞”,口语上这样说无妨,只要正确的理解AVA线程生命周期中 BLOCKED 与 WAITING 状态的区别,在需要区分的时候不要弄混淆即可 54 | 55 | ### 说一下线程中断机制 56 | 57 | 线程中断机制是一种线程间优雅的协作模式,并不是直接中断运行中的线程,但会让等待中的线程抛 InterruptedException 58 | 59 | Thread#interrupt方法只是设置中断标志,Thread#isInterrupeted 不会清除中断标志,Thread.interrupted 会清除中断标志,而 Thread.interrupted 是检测的 currentThread 60 | 61 | 回忆:ThreadPoolExecutor 中 runWorker 方法,获取了 task 后,先进行判断,若线程池是 >= STOP 状态 且 Thread.interrupted() 且 !wt.isInterrupted(),则 wt.interrupt() 62 | 63 | 线程进入等待状态(WAITING 或 TIMED_WAITING)后,其他线程调用了该线程的 Thread#interrupt 方法后,都会被唤醒,进入运行状态(RUNNABLE),但有些情况会抛 InterruptedException,有些情况不会抛异常 64 | 65 | 会抛异常的情况:Object#wait、Thread#join、Thread.sleep;不会抛异常的情况:LockSupport.park 66 | 67 | ### 说一下ThreadLocal的使用场景以及原理 68 | 69 | TODO 70 | 71 | ### ThreadLocal为什么会导致内存泄漏以及如何避免 72 | 73 | TODO 74 | 75 | ### JDK8中对并发做了哪些优化 76 | 77 | TODO 78 | 79 | ### 说一下并发中的三大特性 80 | 81 | 原子性、可见性、有序性 82 | 83 | 可见性 84 | - 如果对一个变量执行 lock 操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量之前,需要重新执行 load 或 assign 操作初始化变量的值 85 | - 对一个变量执行 unlock 操作之前,必须先把此变量同步回主内存中,需要执行 store 或 write 操作 86 | - synchronized与Lock,都能保证执行代码块中变量的可见性 87 | 88 | ### 说一下对volatile的理解 89 | 90 | 两个方面 91 | - 加了volatile的变量,在底层汇编码指令后面会加一行 lock addl $0x0, (%esp),这是一个空操作,底层作用是让变量所在的CPU缓存行强制回写入主内存,且将其他CPU对应的缓存行状态无效化(Invalid),这样其他CPU在读取这个变量的缓存行时,就必须要回主内存读取,以此实现了可见性 92 | - volatile并不是内存屏障,而是实现了类似内存屏障的效果,方法是保证加了volatile变量的语句一定排在之前语句的最后,以此来达到之前语句都执行完了才执行该语句的效果,从而保证指令不乱排序,比如单例模式中DCL双重检测的例子 93 | 94 | ### volatile为何不能保证原子性 95 | 96 | TODO 97 | 98 | ### synchronized与Lock锁的相同与不同点 99 | 100 | 相同点 101 | - synchronized与Lock都能保证线程操作的原子性,保证线程安全 102 | - synchronized与Lock都是独占锁、可重入锁 103 | - synchronized有锁升级优化的过程,与Lock将线程打入等待队列类似,都有CAS自旋获取锁的过程 104 | - synchronized和Object#wait/notify配合可实现等待唤醒,与Lock和Condition#await/singal配合实现等待唤醒类似 105 | 106 | 不同点 107 | - synchronized是JAVA内置的,它使线程进入阻塞状态,Lock一般都用AQS与LockSupport.lock()实现,它使线程进入等待状态 108 | - synchronized进入阻塞的线程不可以被中断,Lock进入等待的线程可以被Thread.interrupt()中断,且可选择是否抛错 109 | - synchronized只能实现一个条件用于等待唤醒,而Lock可以通过多个Condition实现多个条件,典型例子是生产者消费者模型 110 | - synchronized只能是非公平锁,Lock既可以是非公平锁,也可以是公平锁 111 | - synchronized实现简单,JAVA团队会长期优化,Lock需要程序员记得使用上try finally,在finally中用lock.unlock()释放锁 112 | 113 | JDK6对synchronized优化后,synchronized与Lock性能上基本持平,总的来说,追求功能更全面使用Lock,而功能简单时使用synchonized也没有问题 114 | 115 | ### 说一下AQS中同步队列与条件队列的原理 116 | 117 | TODO 118 | 119 | ### 说一下几个实现了AQS应用的使用场景 120 | 121 | TODO 122 | 123 | ### 线程池可解决的问题及适用的场景 124 | 125 | 线程池可解决 持续不断任务 且 多线程 场景下,减少线程的开销,让多任务可复用,减少操作系统用户态与内核态的切换次数,提高程序运行效率 126 | 127 | 场景例子1:固定N个任务,用N个线程分别处理,这种场景没有必要使用线程池 128 | 129 | 场景例子2:固定N个任务,尤其是N很大,用最多50个线程处理,一个任务处理完了紧接着下一个任务,这种场景合适线程池 130 | 131 | 场景具体例子(关键词:持续不断):并发网络IO请求、生产者消费者模型、Tomcat 132 | 133 | ### 线程池为何能做到线程复用 134 | 135 | 看代码实现,这里有一处 task = getTask(),内部是从同步队列中获取 task 任务,而多个 task 任务是在同一个线程中进行的,换句话说,不需要每个任务都开辟一个线程来执行,实现了复用的效果 136 | 137 | ```java 138 | public class ThreadPoolExecutor extends AbstractExecutorService { 139 | 140 | final void runWorker(Worker w) { 141 | Thread wt = Thread.currentThread(); 142 | Runnable task = w.firstTask; 143 | w.firstTask = null; 144 | w.unlock(); // allow interrupts 145 | boolean completedAbruptly = true; 146 | try { 147 | while (task != null || (task = getTask()) != null) { 148 | w.lock(); 149 | ``` 150 | 151 | ### 说一下线程池中重要的参数及含义 152 | 153 | TODO 154 | -------------------------------------------------------------------------------- /深入理解JAVA并发与集合/README.md: -------------------------------------------------------------------------------- 1 | - [前言](#前言) 2 | - [并发与集合的定义](#并发与集合的定义) 3 | - [JAVA语言的水深](#java语言的水深) 4 | - [如何学习并发与集合](#如何学习并发与集合) 5 | - [项目成果](#项目成果) 6 | - [文章目录](#文章目录) 7 | - [第0章:源码分析](#第0章源码分析) 8 | - [第1章:JAVA线程基础](#第1章java线程基础) 9 | - [第2章:并发三大特性](#第2章并发三大特性) 10 | - [第3章:抽象队列同步器](#第3章抽象队列同步器) 11 | - [第4章:并发与集合总结](#第4章并发与集合总结) 12 | - [第5章:HashMap总结](#第5章hashmap总结) 13 | - [思维导图](#思维导图) 14 | - [并发编程](#并发编程) 15 | - [集合容器](#集合容器) 16 | 17 | # 深入理解JAVA并发与集合 18 | 19 | ### 前言 20 | 21 | 我希望写多篇文章,组合成一份文章集,包含关于JAVA并发与集合的入门原理、常见问题、代码例子证明等 22 | 23 | 读者阅读它,能像读小说一样,从第一篇文章,读到最后一篇文章,尽量把讳莫如深难懂的内容写得简单易懂,当然这也需要读者具有一定的JAVA功底 24 | 25 | 并发问题是比较抽象的,一个并发程序的执行结果可能会出乎程序员的预料,并发的不确定性让编程工作具有挑战性 26 | 27 | **声明** 28 | 29 | 转载请注明出处:https://github.com/peteryuanpan/notebook/blob/master/深入理解JAVA并发与集合 30 | 31 | 本文部分内容来自鲁班学院的课程,感谢老师们以及同学们对我的帮助,这里尤其感谢一下fox老师,您的课是讲得真好! 32 | 33 | 参考的书籍:《深入理解JAVA虚拟机》、《JAVA并发编程之美》 34 | 35 | 参考的优秀博文:[Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)、[深入浅出JAVA多线程](http://concurrent.redspider.group/) 36 | 37 | ### 并发与集合的定义 38 | 39 | 在计算机科学中,并发是一种能力,指在一个系统中,拥有多个计算,这些计算有同时执行、潜在交互的特性,但不影响计算结果 40 | 41 | > In computer science, concurrency is the ability of different parts or units of a program, algorithm, or problem to be executed out-of-order or in partial order, without affecting the final outcome. From https://en.wikipedia.org/wiki/Concurrency_(computer_science) 42 | 43 | 在计算机科学中,集合是一个抽象的概念,它的具象容器可装载零至多个,有相同类型且可放在一起操作的数据 44 | 45 | > In computer science, a collection or container is a grouping of some variable number of data items (possibly zero) that have some shared significance to the problem being solved and need to be operated upon together in some controlled fashion. A collection is a concept applicable to abstract data types, and does not prescribe a specific implementation as a concrete data structure. From https://en.wikipedia.org/wiki/Collection_(abstract_data_type) 46 | 47 | ### JAVA语言的水深 48 | 49 | JAVA这潭大湖,从上往下,一共有7层(人为定义的) 50 | 51 | - JAVA语法 52 | - JAVA字节码 53 | - JVM原理 54 | - JVM源码 55 | - 操作系统层 56 | - 汇编码层 57 | - CPU层 58 | 59 | 在[深入理解JAVA虚拟机](../深入理解JAVA虚拟机)文章集中,我强调了文章内容只包含1-3层,不考虑4层以下,且只在单线程的情况下总结JVM原理,而本篇文章集需要讨论的是多线程与并发问题 60 | 61 | ### 如何学习并发与集合 62 | 63 | 学习方向:基础概念 + 代码例子 => 源码阅读 + 图解分析 => 应用实战 64 | 65 | 学习方法 66 | - 看视频和文章,辩证地理解清楚多个基础概念,要有代码例子,记录笔记(点) 67 | - 自行阅读并发和集合类中的源码,同时画图分析,写成文章(线),形成文章集(面) 68 | - 做高并发相关的工具或项目,用到线程池、锁、原子类、集合等技术,从应用实战中理解(体) 69 | 70 | ### 项目成果 71 | 72 | 并发日志下载工具:https://github.com/peteryuanpan/qlogfetch2 73 | 74 | ### 文章目录 75 | 76 | #### 第0章:源码分析 77 | - [HashMap](源码分析/HashMap.md) 78 | - [JDK7.ConcurrentHashMap](源码分析/JDK7.ConcurrentHashMap.md) 79 | - [JDK8.ConcurrentHashMap](源码分析/JDK8.ConcurrentHashMap.md) 80 | - [ThreadPoolExecutor](源码分析/ThreadPoolExecutor.md) 81 | 82 | #### 第1章:JAVA线程基础 83 | - [JAVA线程基础](JAVA线程基础.md) 84 | - [进程与线程](JAVA线程基础.md#进程与线程) 85 | - [并发与并行](JAVA线程基础.md#并发与并行) 86 | - [启动线程](JAVA线程基础.md#启动线程) 87 | - [JAVA启动线程](JAVA线程基础.md#JAVA启动线程) 88 | - [Thread](JAVA线程基础.md#Thread) 89 | - [Runnable](JAVA线程基础.md#Runnable) 90 | - [Callable](JAVA线程基础.md#Callable) 91 | - [ExecutorService](JAVA线程基础.md#ExecutorService) 92 | - [JVM启动线程](JAVA线程基础.md#JVM启动线程) 93 | - [线程的生命周期](JAVA线程基础.md#线程的生命周期) 94 | - [等待唤醒机制](JAVA线程基础.md#等待唤醒机制) 95 | - [虚假唤醒](JAVA线程基础.md#虚假唤醒) 96 | - [Object等待唤醒](JAVA线程基础.md#Object等待唤醒) 97 | - [Thread等待唤醒](JAVA线程基础.md#Thread等待唤醒) 98 | - [LockSupport等待唤醒](JAVA线程基础.md#LockSupport等待唤醒) 99 | - [线程中断机制](JAVA线程基础.md#线程中断机制) 100 | - [并发基础概念](JAVA线程基础.md#并发基础概念) 101 | - [守护线程与用户线程](JAVA线程基础.md#守护线程与用户线程) 102 | - [线程上下文切换](JAVA线程基础.md#线程上下文切换) 103 | - [用户态与内核态](JAVA线程基础.md#用户态与内核态) 104 | - [多线程模型](JAVA线程基础.md#多线程模型) 105 | - [轻量级线程之协程](JAVA线程基础.md#轻量级线程之协程) 106 | 107 | #### 第2章:并发三大特性 108 | - [并发三大特性](并发三大特性.md) 109 | - TODO 110 | 111 | #### 第3章:抽象队列同步器 112 | - [抽象队列同步器](抽象队列同步器.md) 113 | - TODO 114 | 115 | #### 第4章:并发与集合总结 116 | - [并发与集合总结](并发与集合总结.md) 117 | - [进程与线程的区别是什么](并发与集合总结.md#进程与线程的区别是什么) 118 | - [线程与线程之间通信有哪些方式](并发与集合总结.md#线程与线程之间通信有哪些方式) 119 | - [进程与进程之间通信有哪些方式](并发与集合总结.md#进程与进程之间通信有哪些方式) 120 | - [说一下线程的6种状态及转换关系](并发与集合总结.md#说一下线程的6种状态及转换关系) 121 | - [说一下线程中断机制](并发与集合总结.md#说一下线程中断机制) 122 | - [说一下ThreadLocal的使用场景以及原理](并发与集合总结.md#说一下ThreadLocal的使用场景以及原理) 123 | - [ThreadLocal为什么会导致内存泄漏以及如何避免](并发与集合总结.md#ThreadLocal为什么会导致内存泄漏以及如何避免) 124 | - [JDK8中对并发做了哪些优化](并发与集合总结.md#JDK8中对并发做了哪些优化) 125 | - [说一下并发中的三大特性](并发与集合总结.md#说一下并发中的三大特性) 126 | - [说一下对volatile的理解](并发与集合总结.md#说一下对volatile的理解) 127 | - [volatile为何不能保证原子性](并发与集合总结.md#volatile为何不能保证原子性) 128 | - [synchronized与Lock锁的相同与不同点](并发与集合总结.md#synchronized与Lock锁的相同与不同点) 129 | - [说一下AQS中同步队列与条件队列的原理](并发与集合总结.md#说一下AQS中同步队列与条件队列的原理) 130 | - [说一下几个实现了AQS应用的使用场景](并发与集合总结.md#说一下几个实现了AQS应用的使用场景) 131 | - [线程池可解决的问题及适用的场景](并发与集合总结.md#线程池可解决的问题及适用的场景) 132 | - [线程池为何能做到线程复用](并发与集合总结.md#线程池为何能做到线程复用) 133 | - [说一下线程池中重要的参数及含义](并发与集合总结.md#说一下线程池中重要的参数及含义) 134 | 135 | #### 第5章:HashMap总结 136 | - [HashMap总结](HashMap总结.md) 137 | - [JDK7与JDK8中HashMap实现原理上的不同点](HashMap总结.md#JDK7与JDK8中HashMap实现原理上的不同点) 138 | - [JDK8中HashMap数组长度为何是2的幂次方](HashMap总结.md#JDK8中HashMap数组长度为何是2的幂次方) 139 | - [JDK8中HashMap数组什么时候扩容](HashMap总结.md#JDK8中HashMap数组什么时候扩容) 140 | - [JDK8中HashMap为什么要使用红黑树](HashMap总结.md#JDK8中HashMap为什么要使用红黑树) 141 | - [JDK8中HashMap什么时候将链表转化为红黑树](HashMap总结.md#JDK8中HashMap什么时候将链表转化为红黑树) 142 | - [JDK8中HashMap及TreeMap的红黑树实现原理](HashMap总结.md#JDK8中HashMap及TreeMap的红黑树实现原理) 143 | - [JDK8中HashMap如何实现序列化与反序列化](HashMap总结.md#JDK8中HashMap如何实现序列化与反序列化) 144 | - [JDK7中HashMap2个线程resize时循环链表问题](HashMap总结.md#JDK7中HashMap2个线程resize时循环链表问题) 145 | - [JDK8中HashMap2个线程同时put会发生什么](HashMap总结.md#JDK8中HashMap2个线程同时put会发生什么) 146 | - [JDK8中HashMap1个线程put1个线程迭代器遍历会发生什么](HashMap总结.md#JDK8中HashMap1个线程put1个线程迭代器遍历会发生什么) 147 | - [JDK7与JDK8中HashMap的快速失败机制](HashMap总结.md#JDK7与JDK8中HashMap的快速失败机制) 148 | - [JDK7中ConcurrentHashMap如何保证线程安全](HashMap总结.md#JDK7中ConcurrentHashMap如何保证线程安全) 149 | - [JDK8中ConcurrentHashMap如何保证线程安全](HashMap总结.md#JDK8中ConcurrentHashMap如何保证线程安全) 150 | - [JDK8中ConcurrentHashMap的CounterCell有什么作用](HashMap总结.md#JDK8中ConcurrentHashMap的CounterCell有什么作用) 151 | - [JDK7与JDK8中ConcurrentHashMap的安全失败机制](HashMap总结.md#JDK7与JDK8中ConcurrentHashMap的安全失败机制) 152 | 153 | ### 思维导图 154 | 155 | #### 并发编程 156 | 157 | TODO 158 | 159 | #### 集合容器 160 | 161 | ![image](思维导图/集合容器.png) 162 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/数据结构与算法总结/binarytree/TreeMapDecorator.java: -------------------------------------------------------------------------------- 1 | package binarytree; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.TreeMap; 7 | 8 | public class TreeMapDecorator implements BinarySearchTree { 9 | 10 | @Override 11 | public V get(K key) { 12 | return treeMap.get(key); 13 | } 14 | 15 | @Override 16 | public V put(K key, V value) { 17 | return treeMap.put(key, value); 18 | } 19 | 20 | @Override 21 | public V putIfAbsent(K key, V value) { 22 | return treeMap.putIfAbsent(key, value); 23 | } 24 | 25 | @Override 26 | public V remove(K key) { 27 | return treeMap.remove(key); 28 | } 29 | 30 | @Override 31 | public void clear() { 32 | treeMap.clear(); 33 | } 34 | 35 | @Override 36 | public int size() { 37 | return treeMap.size(); 38 | } 39 | 40 | @Override 41 | public int height() { 42 | return heightOf(buildTree(root())); 43 | } 44 | 45 | private int heightOf(Node x) { 46 | return x == null ? 0 : 1 + Integer.max(heightOf(x.left), heightOf(x.right)); 47 | } 48 | 49 | private final List> entryList = new ArrayList<>(); 50 | 51 | @Override 52 | public List> entryList() { 53 | Node root = buildTree(root()); 54 | entryList.clear(); 55 | checkAll(root); 56 | return new ArrayList<>(entryList); 57 | } 58 | 59 | private void checkAll(Node x) { 60 | if (x != null) { 61 | entryList.add(x); 62 | checkAll(x.left); 63 | checkAll(x.right); 64 | } 65 | } 66 | 67 | private Node buildTree(Object x) { 68 | if (x != null) { 69 | Node now = new Node(keyOf(x), valueOf(x), colorOf(x)); 70 | Node left = buildTree(leftOf(x)); 71 | Node right = buildTree(rightOf(x)); 72 | now.left = left; 73 | now.right = right; 74 | if (left != null) 75 | left.father = now; 76 | if (right != null) 77 | right.father = now; 78 | return now; 79 | } 80 | return null; 81 | } 82 | 83 | @Override 84 | public int rotateCount() { 85 | return -1; 86 | } 87 | 88 | public class Node implements Entry { 89 | 90 | private K key; 91 | private V value; 92 | private Node left; 93 | private Node right; 94 | private Node father; 95 | private boolean color; 96 | 97 | Node(K key, V value, Boolean color) { 98 | this.key = key; 99 | this.value = value; 100 | this.color = color; 101 | } 102 | 103 | @Override 104 | public K key() { 105 | return key; 106 | } 107 | 108 | @Override 109 | public V value() { 110 | return value; 111 | } 112 | 113 | @Override 114 | public Entry left() { 115 | return left; 116 | } 117 | 118 | @Override 119 | public Entry right() { 120 | return right; 121 | } 122 | 123 | @Override 124 | public Entry father() { 125 | return father; 126 | } 127 | 128 | public boolean getColor() { 129 | return color; 130 | } 131 | } 132 | 133 | private final TreeMap treeMap = new TreeMap<>(); 134 | 135 | private static Field rootField; 136 | private static Field keyField; 137 | private static Field valueField; 138 | private static Field leftField; 139 | private static Field rightField; 140 | private static Field colorField; 141 | 142 | private Object root() { 143 | try { 144 | return rootField.get(treeMap); 145 | } catch (IllegalAccessException e) { 146 | e.printStackTrace(); 147 | } 148 | return null; 149 | } 150 | 151 | @SuppressWarnings("unchecked") 152 | private K keyOf(Object x) { 153 | try { 154 | return (K) keyField.get(x); 155 | } catch (IllegalAccessException e) { 156 | e.printStackTrace(); 157 | } 158 | return null; 159 | } 160 | 161 | @SuppressWarnings("unchecked") 162 | private V valueOf(Object x) { 163 | try { 164 | return (V) valueField.get(x); 165 | } catch (IllegalAccessException e) { 166 | e.printStackTrace(); 167 | } 168 | return null; 169 | } 170 | 171 | private Object leftOf(Object x) { 172 | try { 173 | return leftField.get(x); 174 | } catch (IllegalAccessException e) { 175 | e.printStackTrace(); 176 | } 177 | return null; 178 | } 179 | 180 | private Object rightOf(Object x) { 181 | try { 182 | return rightField.get(x); 183 | } catch (IllegalAccessException e) { 184 | e.printStackTrace(); 185 | } 186 | return null; 187 | } 188 | 189 | private Boolean colorOf(Object x) { 190 | try { 191 | return (Boolean) colorField.get(x); 192 | } catch (IllegalAccessException e) { 193 | e.printStackTrace(); 194 | } 195 | return null; 196 | } 197 | 198 | static { 199 | try { 200 | Class treeMapClass = Class.forName("java.util.TreeMap"); 201 | rootField = treeMapClass.getDeclaredField("root"); 202 | rootField.setAccessible(true); 203 | Class[] declaredClasses = treeMapClass.getDeclaredClasses(); 204 | for (Class clazz : declaredClasses) { 205 | if ("java.util.TreeMap$Entry".equals(clazz.getName())) { 206 | keyField = clazz.getDeclaredField("key"); 207 | keyField.setAccessible(true); 208 | valueField = clazz.getDeclaredField("value"); 209 | valueField.setAccessible(true); 210 | leftField = clazz.getDeclaredField("left"); 211 | leftField.setAccessible(true); 212 | rightField = clazz.getDeclaredField("right"); 213 | rightField.setAccessible(true); 214 | colorField = clazz.getDeclaredField("color"); 215 | colorField.setAccessible(true); 216 | } 217 | } 218 | } catch (Exception e) { 219 | e.printStackTrace(); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/README.md: -------------------------------------------------------------------------------- 1 | - [深入理解数据结构与算法](#深入理解数据结构与算法) 2 | - [前言](#前言) 3 | - [数据结构与算法总结](#数据结构与算法总结) 4 | - [OnlineJudge或书籍](#OnlineJudge或书籍) 5 | - [刷题记录](#刷题记录) 6 | - [LeetCode思维导图](#LeetCode思维导图) 7 | - [标签与模板](#标签与模板) 8 | 9 | # 深入理解数据结构与算法 10 | 11 | ### 前言 12 | 13 | 大学里搞过2年ACM,基本上在解决的都是一些经典问题(基本都属于P类问题),比如线段树、树状数组、动态规划、KMP、AC自动机、BFS、DFS、最小生成树、图论问题等 14 | 15 | ACM中的问题,对个人编程思维有很大帮助(算法思想是核心),对拿到面试offer也是一块不错的敲门砖,但是,它的实际应用价值不大,你很难在实际项目中找到一个问题,需要用到ACM中的某个具体算法去解决的,而实际应用中最常见的也不是P类问题,而是NP类问题或者NPC问题 16 | 17 | P类问题、NP类问题、NPC问题 18 | > P类问题:所有可以在多项式时间内求解的判定问题构成P类问题。
19 | > NP类问题:所有的非确定性多项式时间可解的判定问题构成NP类问题。
20 | > 非确定性算法:非确定性算法将问题分解成猜测和验证两个阶段。算法的猜测阶段是非确定性的,算法的验证阶段是确定性的,它验证猜测阶段给出解的正确性。
21 | > NPC问题:NP中的某些问题的复杂性与整个类的复杂性相关联,这些问题中任何一个如果存在多项式时间的算法,那么所有NP问题都是多项式时间可解的,这些问题被称为NP-完全问题(NPC问题)。 22 | 23 | 总的来说,我希望在P类问题中能做一些自己的总结,兴趣为主,以后也会有用,而也希望在NP类和NPC问题中能有所探索 24 | 25 | 写这篇文章有三个出发点 26 | - 大学中做过的题并没有很好地记录下来,那么现在开始记录也为时不晚,我会将在各大OnlineJudge上(主要是leetcode、lintcode)刷过题,自认为有价值的,记录之 27 | - 经典数据结构与算法问题,比如经典排序算法、红黑树、B+树、KMP等,在面试中考核较多的,我希望记录下来,有用 28 | - NPC类问题有关算法的探索,比如拟退火算法、粒子群优化算法、遗传算法、蚁群优化算法等 29 | 30 | ### 数据结构与算法总结 31 | 32 | |名称|代码|测试|总结| 33 | |--|--|--|--| 34 | |线段树|||| 35 | |树状数组|||| 36 | |二叉查找树|[BSTTree](数据结构与算法总结/binarytree/BSTTree.java)|[BinarySearchTreeTest](数据结构与算法总结/binarytree/BinarySearchTreeTest.java)|| 37 | |平衡二叉查找树|[AVLTree](数据结构与算法总结/binarytree/AVLTree.java)|[BinarySearchTreeTest](数据结构与算法总结/binarytree/BinarySearchTreeTest.java)|| 38 | |红黑树|[RBTree](数据结构与算法总结/binarytree/RBTree.java)
[TreeMapDecorator](数据结构与算法总结/binarytree/TreeMapDecorator.java)|[BinarySearchTreeTest](数据结构与算法总结/binarytree/BinarySearchTreeTest.java)|| 39 | |B+树|||| 40 | |KMP算法|||| 41 | |AC自动机|||| 42 | 43 | ### OnlineJudge或书籍 44 | 45 | |名称|全称|主页| 46 | |--|--|--| 47 | |ICPC|International Collegiate Programming Contest|https://icpc.global/| 48 | |UESTC|University of Electronic Science and Technology of China|https://acm.uestc.edu.cn/home| 49 | |Leetcode|The World's Leading Online Programming Contest|https://leetcode-cn.com/| 50 | |Lintcode|A Powerful Coding Training System|https://www.lintcode.com/| 51 | |Codeforces|A Programming competitions and contests, programming community|https://codeforces.com/| 52 | |UVA|UVA Online Judge|https://onlinejudge.org/index.php| 53 | |FromBook|《程序员代码面试指南》、《程序员面试金典》|| 54 | 55 | ### 刷题记录 56 | 57 | |序号|题意|题目|来源|大标签|小标签|题解| 58 | |--|--|--|--|--|--|--| 59 | |001|任意升序排序数列A的子串能否变成B|[contest1187_problemD](Codeforces/contest1187_problemD_任意升序排序数列A的子串能否变成B)|codeforces|白题|排列|| 60 | |002|求和为零的三元组的去重解|[problem15](Leetcode/problem15_求和为零的三元组的去重解)|leetcode|白题|二分|| 61 | |003|Kruskal算法求连通图的自定义函数解|[problem1395](UVA/problem1395_Kruskal算法求连通图的自定义函数解)|uva|图论|最小生成树|| 62 | |004|线段树单点加法区间求和|[problem838](UESTC/problem838_线段树单点加法区间求和)|uestc|数据结构|线段树|| 63 | |005|线段树区间加法区间求和|[problem839](UESTC/problem839_线段树区间加法区间求和)|uestc|数据结构|线段树|| 64 | |006|全排列能否被60整除|[contest1266_problemA](Codeforces/contest1266_problemA_全排列能否被60整除)|codeforces|白题|排列|| 65 | |007|基于最大公约数的矩阵构造题|[contest1266_problemC](Codeforces/contest1266_problemC_基于最大公约数的矩阵构造题)|codeforces|白题|构造|| 66 | |008|输出串是两个输入串的母序列且规则|[contest1272_problemF](Codeforces/contest1272_problemF_输出串是两个输入串的母序列且规则)|codeforces|动态规划||| 67 | |009|商品打折券的最大化使用|[contest1282_problemB2](Codeforces/contest1282_problemB2_商品打折券的最大化使用)|codeforces|动态规划||| 68 | |010|合并且排序K个指针表|[problem23](Leetcode/problem23_合并且排序K个指针表)|leetcode|白题|指针|[视频](https://www.bilibili.com/video/BV17T4y1F7Jy)| 69 | |011|求解next_permutation|[problem31](Leetcode/problem31_求解next_permutation)|leetcode|白题|排列|| 70 | |012|求两个升序数列的中位数|[problem4](Leetcode/problem4_求两个升序数列的中位数)|leetcode|白题|二分|| 71 | |013|求1到X中数字1出现的次数|[problem233](Leetcode/problem233_求1到X中数字1出现的次数)|leetcode|动态规划|数论统计|| 72 | |014|求一个数是否是有效数|[problem65](Leetcode/problem65_求一个数是否是有效数)|leetcode|白题|条件判断|| 73 | |015|求a到b中有多少个翻转对称数|[problem248](Leetcode/problem248_求a到b中有多少个翻转对称数)|leetcode|动态规划|数论统计|| 74 | |016|最佳碰头地点|[problem296](Leetcode/problem296_最佳碰头地点)|leetcode|动态规划||| 75 | |017|多条环路公交车线两点间最少上车次数|[problem815](Leetcode/problem815_多条环路公交车线两点间最少上车次数)|leetcode|搜索|广度优先搜索|| 76 | |018|计算三次最小生成树|[weekly-contest-205_problem4](Leetcode/weekly-contest-205_problem4_计算三次最小生成树)|leetcode|图论|最小生成树|| 77 | |019|求数列中长度大于2的等差子序列个数|[problem446](Leetcode/problem446_求数列中长度大于2的等差子序列个数)|leetcode|动态规划||| 78 | |020|求数组的一个极大值|[problem162](Leetcode/problem162_求数组的一个极大值)|leetcode|白题|二分|| 79 | |021|求指定条件下房子涂色的最小花费|[problem1473](Leetcode/problem1473_求指定条件下房子涂色的最小花费)|leetcode|动态规划||| 80 | |022|优雅的逆序一个栈|[problem1](FromBook/problem1_优雅的逆序一个栈)|frombook|数据结构|栈|| 81 | |023|吃苹果的最大数目|[weekly-contest-221_problem2](Leetcode/weekly-contest-221_problem2_吃苹果的最大数目)|leetcode|数据结构|优先队列|| 82 | |024|球会落何处|[weekly-contest-221_problem3](Leetcode/weekly-contest-221_problem3_球会落何处)|leetcode|白题|模拟|| 83 | |025|离线求有条件的异或最大值|[weekly-contest-221_problem4](Leetcode/weekly-contest-221_problem4_离线求有条件的异或最大值)|leetcode|数据结构|字典树|| 84 | |026|多线程爬虫程序|[problem1242](Leetcode/problem1242_多线程爬虫程序)|leetcode|白题|多线程|| 85 | |027|经典模糊串匹配问题|[problem10](Leetcode/problem10_经典模糊串匹配问题)|leetcode|动态规划||| 86 | |028|求0到N中数字2出现的次数|[problem2](FromBook/problem2_求0到N中数字2出现的次数)|frombook|动态规划|数论统计|| 87 | |029|求二叉搜索树的所有生成数组|[problem3](FromBook/problem3_求二叉搜索树的所有生成数组)|frombook|搜索|DFS|| 88 | |030|旋转字符串匹配|[problem514](Leetcode/problem514_旋转字符串匹配)|leetcode|动态规划||| 89 | 90 | ### LeetCode思维导图 91 | 92 | 下面图来自LeetCode公众号 93 | 94 | ![image](leetcode思维导图.jpg) 95 | 96 | ### 标签与模板 97 | 98 | 以下是ACM题目中的标签,很多数据结构和算法问题没有实际应用价值,我现在也不会了————无奈的摊手。但不妨列出来,方便给上面的题目打标签 99 | 100 | ``` 101 | 白题 102 | |----数组、指针 103 | |----二分、三分 104 | |----构造、排列 105 | |----模拟、暴力 106 | |----条件判断 107 | |----多线程 108 | 109 | 动态规划 110 | |----(公式推导) 111 | |----数学三角形 112 | |----背包 113 | |----公共子串、子序列 114 | |----数论统计 115 | |----动态规划与其他结构结合 116 | 117 | 数据结构 118 | |----队列、优先队列 119 | |----栈、单调栈 120 | |----堆 121 | |----字典树 122 | |----线段树 123 | |----树状数组 124 | |----主席树 125 | |----平衡二叉查找树 126 | |----红黑树 127 | |----B树/B+树 128 | 129 | 字符串 130 | |----字典树 131 | |----KMP、拓展KMP 132 | |----AC自动机 133 | |----后缀数组、后缀树 134 | |----后缀自动机 135 | 136 | 图论 137 | |----有向无环图(DAG) 138 | |----并查集 139 | |----最小生成树 140 | |----斯坦纳树 141 | |----最短路 142 | |----Dikstra 143 | |----Bellman-Ford、SPFA 144 | |----Floyd 145 | |----tarjan 146 | |----网络流 147 | 148 | 搜索 149 | |----广度优先搜索(BFS)、深度优先搜索(DFS) 150 | |----启发式搜索 151 | |----A*算法 152 | |----Alpha-Beta算法 153 | |----模拟退火算法 154 | |----遗传算法 155 | |----蚁群算法 156 | |----粒子群优化算法 157 | 158 | 数学 159 | |----几何 160 | |----叉积 161 | |----凸包 162 | |----数论 163 | |----素数、素因数分解 164 | |----最大公约数(GCD)、最小公倍数(LCA) 165 | |----费马小定理 166 | |----数学函数 167 | |----概率、期望 168 | |----排列、组合 169 | |----导数、微积分 170 | |----快速傅里叶变换(FFT) 171 | |----卡特兰数、超级卡特兰数 172 | ``` 173 | 174 | 以下是新建一个题目时,README.md的模板,去掉"\" 175 | 176 | ``` 177 | # 题目 178 | 179 | ### 题意 180 | \``` 181 | \``` 182 | 183 | ### 条件范围 184 | \``` 185 | \``` 186 | 187 | ### 样例输入 188 | \``` 189 | \``` 190 | 191 | ### 样例输出 192 | \``` 193 | \``` 194 | 195 | ### 样例解释 196 | \``` 197 | \``` 198 | 199 | ### 关联链接 200 | 原题: 201 | ``` 202 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/数据结构与算法总结/binarytree/BSTTree.java: -------------------------------------------------------------------------------- 1 | package binarytree; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class BSTTree implements BinarySearchTree { 7 | 8 | @Override 9 | public V get(K key) { 10 | return get(root, key); 11 | } 12 | 13 | private V get(Node x, K key) { 14 | if (x != null) { 15 | int cmp = compare(key, x.key); 16 | if (cmp == 0) 17 | return x.value; 18 | if (cmp < 0) 19 | return get(x.left, key); 20 | else 21 | return get(x.right, key); 22 | } 23 | return null; 24 | } 25 | 26 | private Node getMaxNode(Node x) { 27 | if (x != null) { 28 | if (x.right != null) 29 | return getMaxNode(x.right); 30 | else 31 | return x; 32 | } 33 | return null; 34 | } 35 | 36 | private void updateRoot(Node x) { 37 | root = x; 38 | if (x != null) 39 | x.father = null; 40 | } 41 | 42 | private void updateLeft(Node f, Node x) { 43 | if (f != null) 44 | f.left = x; 45 | if (x != null) 46 | x.father = f; 47 | } 48 | 49 | private void updateRight(Node f, Node x) { 50 | if (f != null) 51 | f.right = x; 52 | if (x != null) 53 | x.father = f; 54 | } 55 | 56 | private void updateFather(Node x, Node v) { 57 | if (x.father == null) 58 | updateRoot(v); 59 | else if (x.father.left == x) 60 | updateLeft(x.father, v); 61 | else 62 | updateRight(x.father, v); 63 | } 64 | 65 | @Override 66 | public V put(K key, V value) { 67 | return put(key, value, false); 68 | } 69 | 70 | @Override 71 | public V putIfAbsent(K key, V value) { 72 | return put(key, value, true); 73 | } 74 | 75 | private V put(K key, V value, boolean onlyIfAbsent) { 76 | if (key == null) 77 | throw new NullPointerException("key can not be null"); 78 | Node x = root; 79 | if (x == null) { 80 | root = new Node(key, value, null); 81 | size ++; 82 | return value; 83 | } 84 | while (true) { 85 | int cmp = compare(key, x.key); 86 | if (cmp == 0) { 87 | if (!onlyIfAbsent) 88 | x.value = value; 89 | return value; 90 | } 91 | if (cmp < 0) { 92 | if (x.left == null) { 93 | x.left = new Node(key, value, x); 94 | size ++; 95 | return value; 96 | } 97 | x = x.left; 98 | } else { 99 | if (x.right == null) { 100 | x.right = new Node(key, value, x); 101 | size ++; 102 | return value; 103 | } 104 | x = x.right; 105 | } 106 | } 107 | } 108 | 109 | @Override 110 | public V remove(K key) { 111 | Node x = root; 112 | while (x != null) { 113 | int cmp = compare(key, x.key); 114 | if (cmp == 0) { 115 | if (x.left == null && x.right == null) 116 | updateFather(x, null); 117 | else if (x.left == null) 118 | updateFather(x, x.right); 119 | else if (x.right == null) 120 | updateFather(x, x.left); 121 | else { 122 | Node prev = getMaxNode(x.left); 123 | updateFather(prev, prev.left); // prev.right must be null 124 | x.key = prev.key; 125 | x.value = prev.value; 126 | } 127 | size --; 128 | return x.value; 129 | } 130 | if (cmp < 0) 131 | x = x.left; 132 | else 133 | x = x.right; 134 | } 135 | return null; 136 | } 137 | 138 | @Override 139 | public void clear() { 140 | root = null; 141 | size = 0; 142 | } 143 | 144 | @Override 145 | public int size() { 146 | return size; 147 | } 148 | 149 | @Override 150 | public int height() { 151 | return root == null ? 0 : heightOf(root); 152 | } 153 | 154 | private int heightOf(Node x) { 155 | return x == null ? 0 : 1 + Integer.max(heightOf(x.left), heightOf(x.right)); 156 | } 157 | 158 | private final List> entryList = new ArrayList<>(); 159 | 160 | @Override 161 | public List> entryList() { 162 | entryList.clear(); 163 | checkAll(null, root); 164 | return new ArrayList<>(entryList); 165 | } 166 | 167 | private void checkAll(Node f, Node x) { 168 | check(f, x); 169 | if (x != null) { 170 | entryList.add(x); 171 | checkAll(x, x.left); 172 | checkAll(x, x.right); 173 | } 174 | } 175 | 176 | private void check(Node f, Node x) { 177 | if (x != null) { 178 | if (f == null && x.father != null) 179 | throw new RuntimeException("f == null && x.father != null"); 180 | if (f != null && x.father != f) 181 | throw new RuntimeException("f != null && x.father != f"); 182 | if (x.left != null && compare(x.key, x.left.key) < 0) 183 | throw new RuntimeException("compare(x.key, x.left.key) < 0"); 184 | if (x.right != null && compare(x.key, x.right.key) > 0) 185 | throw new RuntimeException("compare(x.key, x.right.key) > 0"); 186 | } 187 | } 188 | 189 | @Override 190 | public int rotateCount() { 191 | return 0; 192 | } 193 | 194 | private Node root = null; 195 | private int size = 0; 196 | 197 | @SuppressWarnings("unchecked") 198 | private int compare(Object k1, Object k2) { 199 | return ((Comparable) k1).compareTo((K)k2); 200 | } 201 | 202 | public class Node implements Entry { 203 | 204 | private K key; 205 | private V value; 206 | private Node left; 207 | private Node right; 208 | private Node father; 209 | 210 | Node(K key, V value, Node father) { 211 | this.key = key; 212 | this.value = value; 213 | this.father = father; 214 | } 215 | 216 | @Override 217 | public K key() { 218 | return key; 219 | } 220 | 221 | @Override 222 | public V value() { 223 | return value; 224 | } 225 | 226 | @Override 227 | public Entry left() { 228 | return left; 229 | } 230 | 231 | @Override 232 | public Entry right() { 233 | return right; 234 | } 235 | 236 | @Override 237 | public Entry father() { 238 | return father; 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /深入理解计算机网络/HTTP请求子过程日志/README.md: -------------------------------------------------------------------------------- 1 | # HTTP请求子过程日志 2 | 3 | 包括URL、双端IP、各子过程请求时间、报错信息等 4 | 5 | 目录如下 6 | 7 | * [一次完整HTTP请求](#一次完整HTTP请求) 8 | * [基于CURL-C](#基于CURL-C) 9 | * [基于HTTPSTAT-GOLANG](#基于HTTPSTAT-GOLANG) 10 | * [基于OKHTTP-JAVA](#基于OKHTTP-JAVA) 11 | * [基于Google-Chrome](#基于Google-Chrome) 12 | * [Reference](#Reference) 13 | 14 | # 一次完整HTTP请求 15 | 16 | 一次完整的HTTP请求,包括 dns请求、tcp连接、ssl握手、首包等待、剩余包传输 17 | 18 | ![](docment/whole_http.jpg) 19 | 20 | # 基于CURL-C 21 | 22 | ``` 23 | curl -v -so /dev/null https://www.qiniu.com/ -w "dnslookup: %{time_namelookup} | connect: %{time_connect} | appconnect: %{time_appconnect} | pretransfer: %{time_pretransfer} | starttransfer: %{time_starttransfer} | total: %{time_total} | size: %{size_download}\n" 24 | 25 | (部分输出) 26 | * Connected to www.qiniu.com (58.222.55.69) port 443 (#0) 27 | * Server certificate: 28 | * subject: C=CN; L=Shanghai; O=Shanghai Qiniu Information Technologies Co., Ltd.; OU=IT Dept.; CN=*.qiniu.com 29 | * start date: 2018-02-02 00:00:00 GMT 30 | * expire date: 2020-07-20 12:00:00 GMT 31 | * subjectAltName: www.qiniu.com matched 32 | * issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=GeoTrust RSA CA 2018 33 | * SSL certificate verify ok. 34 | > GET / HTTP/1.1 35 | > User-Agent: curl/7.35.0 36 | > Host: www.qiniu.com 37 | > Accept: */* 38 | 39 | < HTTP/1.1 200 OK 40 | < Content-Type: text/html; charset=utf-8 41 | < Transfer-Encoding: chunked 42 | < Connection: keep-alive 43 | * Server nginx is not blacklisted 44 | < Server: nginx 45 | < X-Frame-Options: Allow-From https://hm.baidu.com 46 | < X-XSS-Protection: 1; mode=block 47 | < X-Content-Type-Options: nosniff 48 | < ETag: W/"a81e576c0ad437b6b211b073bc7aa651" 49 | < Cache-Control: max-age=0, private, must-revalidate 50 | 51 | (关键部分) 52 | dnslookup: 0.028 | connect: 0.035 | appconnect: 0.054 | pretransfer: 0.054 | starttransfer: 0.210 | total: 0.231 | size: 70348 53 | ``` 54 | 55 | # 基于HTTPSTAT-GOLANG 56 | 57 | 详见 Reference 关于 HTTPSTAT 58 | 59 | 一次完整的HTTP请求,包括 DNS Lookup、TCP Connection、TLS Handshake、Server Processing、Content Transfer 60 | 61 | ![](https://github.com/davecheney/httpstat/blob/master/screenshot.png) 62 | 63 | # 基于OKHTTP-JAVA 64 | 65 | 代码DEMO:[OKHTTP3_LOG.java](src/main/OKHTTP3_LOG.java) 66 | 67 | ### 日志例子 68 | 69 | 请求URL https://www.qiniu.com/?a=1&b=2 70 | 71 | 得到日志如下 72 | 73 | ``` 74 | call start. 75 | method: GET 76 | url: https://www.qiniu.com/?a=1&b=2 77 | schema: https 78 | host: www.qiniu.com 79 | encoded path: / 80 | encoded query: a=1&b=2 81 | dns start. 82 | domain: www.qiniu.com 83 | dns end, cost 0.018s. 84 | connect start. 85 | server ip: 61.147.234.139 86 | secure connect start. 87 | secure connect end, cost 0.167s. 88 | connect end, cost 0.193s. 89 | connect acquired. 90 | request headers start. 91 | request headers end. 92 | request headers end, cost 0.001s. 93 | request headers: [Host: www.qiniu.com, Connection: Keep-Alive, Accept-Encoding: gzip, User-Agent: okhttp/3.14.2, ] 94 | response headers start. 95 | response headers end, cost 0.281s. 96 | status code: 200 97 | response headers: [Date: Tue, 26 Nov 2019 20:48:57 GMT, Content-Type: text/html; charset=utf-8, Transfer-Encoding: chunked, Connection: keep-alive, Server: nginx, X-Frame-Options: Allow-From https://hm.baidu.com, X-XSS-Protection: 1; mode=block, X-Content-Type-Options: nosniff, ETag: W/"8c60a2ba20578d2f2ec53587974d3da5", Cache-Control: max-age=0, private, must-revalidate, Set-Cookie: _official_session=M1djMVhOcFgweWlmRnl6MWRlT3MwM2lJVXUrZTdvTUdtZ2Q1MU5ZaGNPNTFhRmFWd1R3Mk9HL1ZmeFRIdGNTMWhGbE5DdXcrVkxOVE44eTNZZFNpSlVWMXhsVy9iMzI3dGNMNkswUEx4R2djYVFCYjBWWkg5WXUyT1BPd0pYOXBsWjhzcDdqKzRZRXBRMjY0S0h6ZmR3PT0tLVM4WUVuODFucXFaeHQ2Z2h1WGdCY2c9PQ%3D%3D--15dab9bb2f600b7b2256f8abbed26db86259bd10; path=/; HttpOnly, X-Request-Id: 2e61a183-6842-4b06-bd36-3cf9baf3034c, X-Runtime: 0.033959, X-Ser: BC80_dx-lt-yd-jiangsu-taizhou-4-cache-4, BC139_dx-jiangsu-nantong-4-cache-5, X-Cache: MISS from BC139_dx-jiangsu-nantong-4-cache-5(baishan), ] 98 | response body start. 99 | response body end, cost 0.039s, count 0 bytes. 100 | connect released. 101 | call end, cost 0.567s. 102 | ``` 103 | 104 | ### 代码解读 105 | 106 | 通过衍生类 HttpEventListener(继承 EventListener),记录各个请求阶段的时间及日志 107 | 108 | 一次完整的HTTP请求,包括 dns、conncet(ssl)、request header、request body、response header、response body 几个过程 109 | 110 | ```Java 111 | class HttpEventListener extends EventListener { 112 | 113 | private long callStartNanos; 114 | private long dnsStartNanos; 115 | private long connectStartNanos; 116 | private long secureConnectStartNanos; 117 | private long requestHeadersStartNanos; 118 | private long requestBodyStartNanos; 119 | private long responseHeadersStartNanos; 120 | private long responseBodyStartNanos; 121 | 122 | @Override 123 | public void dnsStart(Call call, String domainName) { 124 | super.dnsStart(call, domainName); 125 | System.out.println("dns start."); 126 | System.out.println("domain: " + domainName); 127 | dnsStartNanos = System.nanoTime(); 128 | } 129 | 130 | @Override 131 | public void dnsEnd(Call call, String domainName, List inetAddressList) { 132 | super.dnsEnd(call, domainName, inetAddressList); 133 | long nanoTime = System.nanoTime(); 134 | System.out.printf("dns end, cost %.3fs.\n", (nanoTime - dnsStartNanos) / 1000000000d); 135 | } 136 | 137 | @Override 138 | public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) { 139 | super.connectStart(call, inetSocketAddress, proxy); 140 | System.out.println("connect start."); 141 | System.out.println("server ip: " + inetSocketAddress.getAddress().getHostAddress()); 142 | connectStartNanos = System.nanoTime(); 143 | } 144 | 145 | @Override 146 | public void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) { 147 | super.connectEnd(call, inetSocketAddress, proxy, protocol); 148 | long nanoTime = System.nanoTime(); 149 | System.out.printf("connect end, cost %.3fs.\n", (nanoTime - connectStartNanos) / 1000000000d); 150 | } 151 | 152 | @Override 153 | public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol, IOException ioe) { 154 | super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe); 155 | long nanoTime = System.nanoTime(); 156 | System.out.printf("connect failed for %s, cost %.3fs\n.", ioe.getMessage(), (nanoTime - connectStartNanos) / 1000000000d); 157 | } 158 | } 159 | ``` 160 | 161 | # 基于Google-Chrome 162 | 163 | 打开开发者模式(F12),找到 Network - Name - Timing,查看瀑布图 164 | 165 | ![](docment/chrome_F12.png) 166 | 167 | 一次完整的HTTP请求,包括 Queueing、Stalled、DNS Lookup、Initial Connection、SSL、Request sent、Waiting(TTFB)、Content Downloading 几个部分 168 | 169 | ![](docment/chrome_http.png) 170 | 171 | # Reference 172 | 173 | - [A Question of Timing](https://blog.cloudflare.com/a-question-of-timing/) 174 | - [OkHttp之网络请求耗时统计](https://blog.csdn.net/joye123/article/details/82115562) 175 | - [移动互联网时代,如何优化你的网络](https://yq.aliyun.com/articles/58967?spm=a2c4g.11186623.2.11.66f5702d0tveyr) 176 | - [github square/okhttp](https://github.com/square/okhttp/) 177 | - [github curl/curl](https://github.com/curl/curl/) 178 | - [github davecheney/httpstat](https://github.com/davecheney/httpstat/) 179 | -------------------------------------------------------------------------------- /深入理解计算机网络/HTTP请求子过程日志/src/main/OKHTTP3_LOG.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.net.InetAddress; 3 | import java.net.InetSocketAddress; 4 | import java.net.Proxy; 5 | import java.util.List; 6 | import java.util.concurrent.atomic.AtomicLong; 7 | 8 | import okhttp3.Call; 9 | import okhttp3.Connection; 10 | import okhttp3.EventListener; 11 | import okhttp3.Handshake; 12 | import okhttp3.HttpUrl; 13 | import okhttp3.OkHttpClient; 14 | import okhttp3.Protocol; 15 | import okhttp3.Request; 16 | import okhttp3.Response; 17 | 18 | public class OKHTTP3_LOG { 19 | 20 | public static void main(String[] args) { 21 | OkHttpClient client = new OkHttpClient.Builder() 22 | .eventListenerFactory(HttpEventListener.FACTORY) 23 | .build(); 24 | Request request = new Request.Builder() 25 | .url("https://www.qiniu.com/?a=1&b=2") 26 | .build(); 27 | Call call = client.newCall(request); 28 | try { 29 | Response response = call.execute(); 30 | //System.out.println(response.body().string()); 31 | response.close(); 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | 38 | class HttpEventListener extends EventListener { 39 | 40 | public static final Factory FACTORY = new Factory() { 41 | final AtomicLong nextCallId = new AtomicLong(1L); 42 | 43 | public EventListener create(Call call) { 44 | long callId = nextCallId.getAndIncrement(); 45 | return new HttpEventListener(callId, call.request().url(), System.nanoTime()); 46 | } 47 | }; 48 | 49 | private long callStartNanos; 50 | private long dnsStartNanos; 51 | private long connectStartNanos; 52 | private long secureConnectStartNanos; 53 | private long requestHeadersStartNanos; 54 | private long requestBodyStartNanos; 55 | private long responseHeadersStartNanos; 56 | private long responseBodyStartNanos; 57 | 58 | public HttpEventListener(long callId, HttpUrl url, long callStartNanos) { 59 | } 60 | 61 | @Override 62 | public void callStart(Call call) { 63 | super.callStart(call); 64 | System.out.println("call start."); 65 | System.out.println("method: " + call.request().method()); 66 | System.out.println("url: " + call.request().url().toString()); 67 | System.out.println("schema: " + call.request().url().scheme()); 68 | System.out.println("host: " + call.request().url().host()); 69 | System.out.println("encoded path: " + call.request().url().encodedPath()); 70 | System.out.println("encoded query: " + call.request().url().encodedQuery()); 71 | callStartNanos = System.nanoTime(); 72 | } 73 | 74 | @Override 75 | public void dnsStart(Call call, String domainName) { 76 | super.dnsStart(call, domainName); 77 | System.out.println("dns start."); 78 | System.out.println("domain: " + domainName); 79 | dnsStartNanos = System.nanoTime(); 80 | } 81 | 82 | @Override 83 | public void dnsEnd(Call call, String domainName, List inetAddressList) { 84 | super.dnsEnd(call, domainName, inetAddressList); 85 | long nanoTime = System.nanoTime(); 86 | System.out.printf("dns end, cost %.3fs.\n", (nanoTime - dnsStartNanos) / 1000000000d); 87 | } 88 | 89 | @Override 90 | public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) { 91 | super.connectStart(call, inetSocketAddress, proxy); 92 | System.out.println("connect start."); 93 | System.out.println("server ip: " + inetSocketAddress.getAddress().getHostAddress()); 94 | connectStartNanos = System.nanoTime(); 95 | } 96 | 97 | @Override 98 | public void secureConnectStart(Call call) { 99 | super.secureConnectStart(call); 100 | System.out.println("secure connect start."); 101 | secureConnectStartNanos = System.nanoTime(); 102 | } 103 | 104 | @Override 105 | public void secureConnectEnd(Call call, Handshake handshake) { 106 | super.secureConnectEnd(call, handshake); 107 | long nanoTime = System.nanoTime(); 108 | System.out.printf("secure connect end, cost %.3fs.\n", (nanoTime - secureConnectStartNanos) / 1000000000d); 109 | } 110 | 111 | @Override 112 | public void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) { 113 | super.connectEnd(call, inetSocketAddress, proxy, protocol); 114 | long nanoTime = System.nanoTime(); 115 | System.out.printf("connect end, cost %.3fs.\n", (nanoTime - connectStartNanos) / 1000000000d); 116 | } 117 | 118 | @Override 119 | public void connectFailed(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol, IOException ioe) { 120 | super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe); 121 | long nanoTime = System.nanoTime(); 122 | System.out.printf("connect failed for %s, cost %.3fs\n.", ioe.getMessage(), (nanoTime - connectStartNanos) / 1000000000d); 123 | } 124 | 125 | @Override 126 | public void connectionAcquired(Call call, Connection connection) { 127 | super.connectionAcquired(call, connection); 128 | System.out.println("connect acquired."); 129 | } 130 | 131 | @Override 132 | public void connectionReleased(Call call, Connection connection) { 133 | super.connectionReleased(call, connection); 134 | System.out.println("connect released."); 135 | } 136 | 137 | @Override 138 | public void requestHeadersStart(Call call) { 139 | super.requestHeadersStart(call); 140 | System.out.println("request headers start."); 141 | requestHeadersStartNanos = System.nanoTime(); 142 | } 143 | 144 | @Override 145 | public void requestHeadersEnd(Call call, Request request) { 146 | super.requestHeadersEnd(call, request); 147 | long nanoTime = System.nanoTime(); 148 | System.out.printf("request headers end, cost %.3fs.\n", (nanoTime - requestHeadersStartNanos) / 1000000000d); 149 | System.out.println("request headers: [" + request.headers().toString().replace("\n", ", ") + "]"); 150 | } 151 | 152 | @Override 153 | public void requestBodyStart(Call call) { 154 | super.requestBodyStart(call); 155 | System.out.println("request body start."); 156 | requestBodyStartNanos = System.nanoTime(); 157 | } 158 | 159 | @Override 160 | public void requestBodyEnd(Call call, long byteCount) { 161 | super.requestBodyEnd(call, byteCount); 162 | long nanoTime = System.nanoTime(); 163 | System.out.printf("request body end, cost %.3fs, count %d bytes.\n", (nanoTime - requestBodyStartNanos) / 1000000000d, byteCount); 164 | } 165 | 166 | @Override 167 | public void responseHeadersStart(Call call) { 168 | super.responseHeadersStart(call); 169 | System.out.println("response headers start."); 170 | responseHeadersStartNanos = System.nanoTime(); 171 | } 172 | 173 | @Override 174 | public void responseHeadersEnd(Call call, Response response) { 175 | super.responseHeadersEnd(call, response); 176 | long nanoTime = System.nanoTime(); 177 | System.out.printf("response headers end, cost %.3fs.\n", (nanoTime - responseHeadersStartNanos) / 1000000000d); 178 | System.out.println("status code: " + response.code()); 179 | System.out.println("response headers: [" + response.headers().toString().replace("\n", ", ") + "]"); 180 | } 181 | 182 | @Override 183 | public void responseBodyStart(Call call) { 184 | super.responseBodyStart(call); 185 | System.out.println("response body start."); 186 | responseBodyStartNanos = System.nanoTime(); 187 | } 188 | 189 | @Override 190 | public void responseBodyEnd(Call call, long byteCount) { 191 | super.responseBodyEnd(call, byteCount); 192 | long nanoTime = System.nanoTime(); 193 | System.out.printf("response body end, cost %.3fs, count %d bytes.\n", (nanoTime - responseBodyStartNanos) / 1000000000d, byteCount); 194 | } 195 | 196 | @Override 197 | public void callEnd(Call call) { 198 | super.callEnd(call); 199 | long nanoTime = System.nanoTime(); 200 | System.out.printf("call end, cost %.3fs.\n", (nanoTime - callStartNanos) / 1000000000d); 201 | } 202 | 203 | @Override 204 | public void callFailed(Call call, IOException ioe) { 205 | super.callFailed(call, ioe); 206 | long nanoTime = System.nanoTime(); 207 | System.out.printf("call failed for %s, cost %.3fs.\n", ioe.getMessage(), (nanoTime - callStartNanos) / 1000000000d); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/数据结构与算法总结/binarytree/AVLTree.java: -------------------------------------------------------------------------------- 1 | package binarytree; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class AVLTree implements BinarySearchTree { 7 | 8 | @Override 9 | public V get(K key) { 10 | return get(root, key); 11 | } 12 | 13 | private V get(Node x, K key) { 14 | if (x != null) { 15 | int cmp = compare(key, x.key); 16 | if (cmp == 0) 17 | return x.value; 18 | if (cmp < 0) 19 | return get(x.left, key); 20 | else 21 | return get(x.right, key); 22 | } 23 | return null; 24 | } 25 | 26 | private Node getMaxNode(Node x) { 27 | if (x != null) { 28 | if (x.right != null) 29 | return getMaxNode(x.right); 30 | else 31 | return x; 32 | } 33 | return null; 34 | } 35 | 36 | private void updateHeight(Node x) { 37 | x.height = 1 + Integer.max(heightOf(x.left), heightOf(x.right)); 38 | } 39 | 40 | private void updateRoot(Node x) { 41 | root = x; 42 | if (x != null) 43 | x.father = null; 44 | } 45 | 46 | private void updateLeft(Node f, Node x) { 47 | if (f != null) 48 | f.left = x; 49 | if (x != null) 50 | x.father = f; 51 | } 52 | 53 | private void updateRight(Node f, Node x) { 54 | if (f != null) 55 | f.right = x; 56 | if (x != null) 57 | x.father = f; 58 | } 59 | 60 | private void updateFather(Node x, Node v) { 61 | if (x.father == null) 62 | updateRoot(v); 63 | else if (x.father.left == x) 64 | updateLeft(x.father, v); 65 | else 66 | updateRight(x.father, v); 67 | } 68 | 69 | private void rotateLeft(Node x) { 70 | Node right = x.right; 71 | Node right_left = x.right.left; 72 | updateFather(x, right); 73 | updateLeft(right, x); 74 | updateRight(x, right_left); 75 | updateHeight(x); 76 | updateHeight(right); 77 | rotateCount ++; 78 | } 79 | 80 | private void rotateRight(Node x) { 81 | Node left = x.left; 82 | Node left_right = x.left.right; 83 | updateFather(x, left); 84 | updateRight(left, x); 85 | updateLeft(x, left_right); 86 | updateHeight(x); 87 | updateHeight(left); 88 | rotateCount ++; 89 | } 90 | 91 | @Override 92 | public V put(K key, V value) { 93 | return put(key, value, false); 94 | } 95 | 96 | @Override 97 | public V putIfAbsent(K key, V value) { 98 | return put(key, value, true); 99 | } 100 | 101 | private V put(K key, V value, boolean onlyIfAbsent) { 102 | if (key == null) 103 | throw new NullPointerException("key can not be null"); 104 | Node x = root; 105 | if (x == null) { 106 | root = new Node(key, value, null); 107 | size ++; 108 | return value; 109 | } 110 | while (true) { 111 | int cmp = compare(key, x.key); 112 | if (cmp == 0) { 113 | if (!onlyIfAbsent) 114 | x.value = value; 115 | return value; 116 | } 117 | if (cmp < 0) { 118 | if (x.left == null) { 119 | x.left = new Node(key, value, x); 120 | size ++; 121 | fixAfterPut(x); 122 | return value; 123 | } 124 | x = x.left; 125 | } else { 126 | if (x.right == null) { 127 | x.right = new Node(key, value, x); 128 | size ++; 129 | fixAfterPut(x); 130 | return value; 131 | } 132 | x = x.right; 133 | } 134 | } 135 | } 136 | 137 | private void fixAfterPut(Node x) { 138 | while (x != null) { 139 | int oldH = heightOf(x); 140 | updateHeight(x); 141 | if (oldH == heightOf(x)) 142 | break; 143 | Node f = x.father; 144 | int lh = heightOf(x.left); 145 | int rh = heightOf(x.right); 146 | if (lh - rh > 1) { 147 | int cmp = heightOf(x.left.left) - heightOf(x.left.right); 148 | if (cmp >= 0) 149 | rotateRight(x); 150 | else { 151 | rotateLeft(x.left); 152 | rotateRight(x); 153 | } 154 | } else if (lh - rh < -1) { 155 | int cmp = heightOf(x.right.right) - heightOf(x.right.left); 156 | if (cmp >= 0) 157 | rotateLeft(x); 158 | else { 159 | rotateRight(x.right); 160 | rotateLeft(x); 161 | } 162 | } 163 | x = f; 164 | } 165 | } 166 | 167 | @Override 168 | public V remove(K key) { 169 | Node x = root; 170 | while (x != null) { 171 | int cmp = compare(key, x.key); 172 | if (cmp == 0) { 173 | V v = x.value; 174 | if (x.left == null && x.right == null) 175 | updateFather(x, null); 176 | else if (x.left == null) 177 | updateFather(x, x.right); 178 | else if (x.right == null) 179 | updateFather(x, x.left); 180 | else { 181 | Node prev = getMaxNode(x.left); 182 | updateFather(prev, prev.left); // prev.right must be null 183 | x.key = prev.key; 184 | x.value = prev.value; 185 | x = prev; 186 | } 187 | size --; 188 | fixAfterRemove(x.father); 189 | return v; 190 | } 191 | if (cmp < 0) 192 | x = x.left; 193 | else 194 | x = x.right; 195 | } 196 | return null; 197 | } 198 | 199 | private void fixAfterRemove(Node x) { 200 | while (x != null) { 201 | int oldH = heightOf(x); 202 | updateHeight(x); 203 | Node f = x.father; 204 | int lh = heightOf(x.left); 205 | int rh = heightOf(x.right); 206 | if (lh - rh > 1) { 207 | int cmp = heightOf(x.left.left) - heightOf(x.left.right); 208 | if (cmp >= 0) 209 | rotateRight(x); 210 | else { 211 | rotateLeft(x.left); 212 | rotateRight(x); 213 | } 214 | } else if (lh - rh < -1) { 215 | int cmp = heightOf(x.right.right) - heightOf(x.right.left); 216 | if (cmp >= 0) 217 | rotateLeft(x); 218 | else { 219 | rotateRight(x.right); 220 | rotateLeft(x); 221 | } 222 | } else if (oldH == heightOf(x)) 223 | break; 224 | x = f; 225 | } 226 | } 227 | 228 | @Override 229 | public void clear() { 230 | root = null; 231 | size = 0; 232 | rotateCount = 0; 233 | } 234 | 235 | @Override 236 | public int size() { 237 | return size; 238 | } 239 | 240 | @Override 241 | public int height() { 242 | return heightOf(root); 243 | } 244 | 245 | private final List> entryList = new ArrayList<>(); 246 | 247 | @Override 248 | public List> entryList() { 249 | entryList.clear(); 250 | checkAll(null, root); 251 | return new ArrayList<>(entryList); 252 | } 253 | 254 | private void checkAll(Node f, Node x) { 255 | check(f, x); 256 | if (x != null) { 257 | entryList.add(x); 258 | checkAll(x, x.left); 259 | checkAll(x, x.right); 260 | } 261 | } 262 | 263 | private void check(Node f, Node x) { 264 | if (x != null) { 265 | if (f == null && x.father != null) 266 | throw new RuntimeException("f == null && x.father != null"); 267 | if (f != null && x.father != f) 268 | throw new RuntimeException("f != null && x.father != f"); 269 | if (x.left != null && compare(x.key, x.left.key) < 0) 270 | throw new RuntimeException("compare(x.key, x.left.key) < 0"); 271 | if (x.right != null && compare(x.key, x.right.key) > 0) 272 | throw new RuntimeException("compare(x.key, x.right.key) > 0"); 273 | int lh = heightOf(x.left); 274 | int rh = heightOf(x.right); 275 | if (x.height != (1 + Integer.max(lh, rh))) 276 | throw new RuntimeException("x.height != (1 + Integer.max(lh, rh))"); 277 | if (lh - rh > 1) 278 | throw new RuntimeException("lh - rh > 1"); 279 | if (lh - rh < -1) 280 | throw new RuntimeException("lh - rh < -1"); 281 | } 282 | } 283 | 284 | @Override 285 | public int rotateCount() { 286 | return rotateCount; 287 | } 288 | 289 | private Node root = null; 290 | private int size = 0; 291 | private int rotateCount = 0; 292 | 293 | private int heightOf(Node x) { 294 | return x == null ? 0 : x.height; 295 | } 296 | 297 | @SuppressWarnings("unchecked") 298 | private int compare(Object k1, Object k2) { 299 | return ((Comparable) k1).compareTo((K)k2); 300 | } 301 | 302 | public class Node implements Entry { 303 | 304 | private K key; 305 | private V value; 306 | private Node left; 307 | private Node right; 308 | private Node father; 309 | private int height; 310 | 311 | Node(K key, V value, Node father) { 312 | this.key = key; 313 | this.value = value; 314 | this.father = father; 315 | this.height = 1; 316 | } 317 | 318 | @Override 319 | public K key() { 320 | return key; 321 | } 322 | 323 | @Override 324 | public V value() { 325 | return value; 326 | } 327 | 328 | @Override 329 | public Entry left() { 330 | return left; 331 | } 332 | 333 | @Override 334 | public Entry right() { 335 | return right; 336 | } 337 | 338 | @Override 339 | public Entry father() { 340 | return father; 341 | } 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /深入理解JAVA虚拟机/README.md: -------------------------------------------------------------------------------- 1 | - [前言](#前言) 2 | - [JAVA虚拟机的定义](#java虚拟机的定义) 3 | - [JAVA语言的水深](#java语言的水深) 4 | - [如何学习JVM](#如何学习jvm) 5 | - [项目成果](#项目成果) 6 | - [文章目录](#文章目录) 7 | - [第0章:JVM基础概念](#第0章jvm基础概念) 8 | - [第1章:类加载机制与类加载器](#第1章类加载机制与类加载器) 9 | - [第2章:类文件结构与字节码指令](#第2章类文件结构与字节码指令) 10 | - [第3章:运行时数据区域](#第3章运行时数据区域) 11 | - [第4章:字节码执行引擎](#第4章字节码执行引擎) 12 | - [第5章:对象的生命周期](#第5章对象的生命周期) 13 | - [第6章:垃圾收集机制](#第6章垃圾收集机制) 14 | - [第7章:JVM性能调优](#第7章jvm性能调优) 15 | - [第8章:JVM总结](#第8章jvm总结) 16 | - [思维导图](#思维导图) 17 | - [类加载机制](#类加载机制) 18 | - [运行时数据区域](#运行时数据区域) 19 | - [垃圾收集机制](#垃圾收集机制) 20 | 21 | # 深入理解JAVA虚拟机-第一至三层 22 | 23 | ### 前言 24 | 25 | 我希望写多篇文章,组合成一份文章集,包含关于JAVA虚拟机的入门原理、常见问题、代码例子证明等 26 | 27 | 读者阅读它,能像读小说一样,从第一篇文章,读到最后一篇文章,尽量把讳莫如深难懂的内容写得简单易懂,当然这也需要读者具有一定的JAVA功底 28 | 29 | 引用《深入理解JAVA虚拟机》第二版6.3节的话:力求在保证逻辑准确的前提下,用尽量通俗的语言和案例去讲述虚拟机中与开发关系最为密切的内容 30 | 31 | **声明** 32 | 33 | 转载请注明出处:https://github.com/peteryuanpan/notebook/blob/master/深入理解JAVA虚拟机 34 | 35 | 本文部分内容来自鲁班学院的课程,感谢老师们以及同学们对我的帮助,特别感谢一下子牙老师 36 | 37 | 还有部分内容来自《深入理解JAVA虚拟机》第二版以及第三版,第二版是基于JDK7的,第三版是基于JDK8及以后的,本文内容是基于JDK8的(Hotspot实现),我会去对比第二版与第三版的差异点,并记录在文中 38 | 39 | ### JAVA虚拟机的定义 40 | 41 | JAVA虚拟机是一种能够运行JAVA字节码程序的虚拟机器 42 | 43 | > A Java virtual machine (JVM) is a virtual machine that enables a computer to run Java programs as well as programs written in other languages that are also compiled to Java bytecode. From https://en.wikipedia.org/wiki/Java_virtual_machine. 44 | 45 | 虚拟机是一个相对于物理机的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的,而虚拟机的执行引擎则是由软件自行实现的,因此可以不受物理条件制约地定制指令集与执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式 46 | 47 | ### JAVA语言的水深 48 | 49 | JAVA这潭大湖,从上往下,一共有7层(人为定义的) 50 | 51 | - JAVA语法 52 | - JAVA字节码 53 | - JVM原理 54 | - JVM源码 55 | - 操作系统层 56 | - 汇编码层 57 | - CPU层 58 | 59 | 笔者的能力是有限的,望读者见谅,这篇文章集能接触到的水深,只有第 1-3 层 + 第4层表面,第4层JVM源码往下,不会深入包含,且本篇文章集只在单线程的情况下总结JVM的原理,不涉及多线程 60 | 61 | 并发相关的概念将在[深入理解JAVA并发与集合](../深入理解JAVA并发与集合)文章集中总结 62 | 63 | ### 如何学习JVM 64 | 65 | 两个方向:内存模型、字节码 66 | 67 | 学习方法 68 | - 学习了一块知识点后,自己梳理成文章总结(这是第一步,更优先) 69 | - 然后画思维导图,将内容讲出来,一遍一遍地过,直到能让人听明白为止(这是第二步,十分重要) 70 | - 注意,写出来和说出来对于理解的提高是非常不一样的,写完后必须说出来,讲明白了,就通透了 71 | 72 | ### 项目成果 73 | 74 | 类文件结构解析:https://github.com/peteryuanpan/ParseClassFile 75 | 76 | ### 文章目录 77 | 78 | #### 第0章:JVM基础概念 79 | - [JVM基础概念](JVM基础概念.md) 80 | - [JAVA运行时环境逻辑图](JVM基础概念.md#JAVA运行时环境逻辑图) 81 | - [oop-klass模型](JVM基础概念.md#oop-klass模型) 82 | - [InstanceKlass](JVM基础概念.md#InstanceKlass) 83 | - [InstanceMirrorKlass](JVM基础概念.md#InstanceMirrorKlass) 84 | - [InstanceRefKlass](JVM基础概念.md#InstanceRefKlass) 85 | - [ArrayKlass](JVM基础概念.md#ArrayKlass) 86 | - [TypeArrayKlass](JVM基础概念.md#TypeArrayKlass) 87 | - [ObjArrayKlass](JVM基础概念.md#ObjArrayKlass) 88 | - [oopDesc](JVM基础概念.md#oopDesc) 89 | - [instanceOopDesc](JVM基础概念.md#instanceOopDesc) 90 | - [arrayOopDesc](JVM基础概念.md#arrayOopDesc) 91 | - [typeArrayOopDesc](JVM基础概念.md#typeArrayOopDesc) 92 | - [objArrayOopDesc](JVM基础概念.md#objArrayOopDesc) 93 | - [JVM源码及调试](JVM基础概念.md#JVM源码及调试) 94 | 95 | #### 第1章:类加载机制与类加载器 96 | - [类加载机制](类加载机制.md) 97 | - [类的定义](类加载机制.md#类的定义) 98 | - [类加载的定义](类加载机制.md#类加载的定义) 99 | - [类加载的输入和输出结果](类加载机制.md#类加载的输入和输出结果) 100 | - [类加载的五个过程](类加载机制.md#类加载的五个过程) 101 | - [加载](类加载机制.md#加载) 102 | - [验证](类加载机制.md#验证) 103 | - [准备](类加载机制.md#准备) 104 | - [解析](类加载机制.md#解析) 105 | - [初始化](类加载机制.md#初始化) 106 | - [类加载的时机](类加载机制.md#类加载的时机) 107 | - [加载mainClass](类加载机制.md#加载mainClass) 108 | - [new或getstatic或putstatic或invokestatic](类加载机制.md#new或getstatic或putstatic或invokestatic) 109 | - [优先加载父类](类加载机制.md#优先加载父类) 110 | - [反射](类加载机制.md#反射) 111 | - [类加载笔试题统一解法](类加载机制.md#类加载笔试题统一解法) 112 | - [类加载器](类加载器.md) 113 | - [类加载器的定义](类加载器.md#类加载器的定义) 114 | - [类加载的唯一性](类加载器.md#类加载的唯一性) 115 | - [双亲委派模型](类加载器.md#双亲委派模型) 116 | - [启动类加载器](类加载器.md#启动类加载器) 117 | - [拓展类加载器](类加载器.md#拓展类加载器) 118 | - [应用类程序加载器](类加载器.md#应用类程序加载器) 119 | - [深入理解双亲委派模型源码](类加载器.md#深入理解双亲委派模型源码) 120 | - [破坏双亲委派模型](类加载器.md#破坏双亲委派模型) 121 | - [自定义类加载器](类加载器.md#自定义类加载器) 122 | - [SPI机制与线程上下文类加载器](类加载器.md#SPI机制与线程上下文类加载器) 123 | - [类加载与Tomcat](类加载器.md#类加载与Tomcat) 124 | 125 | #### 第2章:类文件结构与字节码指令 126 | - [类文件结构](类文件结构.md) 127 | - [类文件的定义](类文件结构.md#类文件的定义) 128 | - [类文件的数据紧凑性](类文件结构.md#类文件的数据紧凑性) 129 | - [类文件的结构轮廓](类文件结构.md#类文件的结构轮廓) 130 | - [无符号数与表](类文件结构.md#无符号数与表) 131 | - [类文件例子](类文件结构.md#类文件例子) 132 | - [魔数](类文件结构.md#魔数) 133 | - [次版本号与主版本号](类文件结构.md#次版本号与主版本号) 134 | - [常量池](类文件结构.md#常量池) 135 | - [常量池的定义](类文件结构.md#常量池的定义) 136 | - [常量池容量计数器](类文件结构.md#常量池容量计数器) 137 | - [常量池的结构轮廓](类文件结构.md#常量池的结构轮廓) 138 | - [CONSTANT_Utf8_info](类文件结构.md#CONSTANT_Utf8_info) 139 | - [CONSTANT_Integer_info](类文件结构.md#CONSTANT_Integer_info) 140 | - [8种基本数据类型及自动转换关系](类文件结构.md#8种基本数据类型及自动转换关系) 141 | - [CONSTANT_Float_info](类文件结构.md#CONSTANT_Float_info) 142 | - [IEEE754二进制浮点数算术标准](类文件结构.md#IEEE754二进制浮点数算术标准) 143 | - [例子-十进制浮点数转32位二进制浮点数](类文件结构.md#例子-十进制浮点数转32位二进制浮点数) 144 | - [例子-32位二进制浮点数转十进制浮点数](类文件结构.md#例子-32位二进制浮点数转十进制浮点数) 145 | - [CONSTANT_Class_info](类文件结构.md#CONSTANT_Class_info) 146 | - [全部常量池项结构](类文件结构.md#全部常量池项结构) 147 | - [类访问标志](类文件结构.md#类访问标志) 148 | - [本类父类接口](类文件结构.md#本类父类接口) 149 | - [类索引](类文件结构.md#类索引) 150 | - [父类索引](类文件结构.md#父类索引) 151 | - [接口计数器](类文件结构.md#接口计数器) 152 | - [接口索引集合](类文件结构.md#接口索引集合) 153 | - [字段表](类文件结构.md#字段表) 154 | - [字段表计数器](类文件结构.md#字段表计数器) 155 | - [字段表结构](类文件结构.md#字段表结构) 156 | - [字段表访问标志](类文件结构.md#字段表访问标志) 157 | - [访问权限修饰符的作用域](类文件结构.md#访问权限修饰符的作用域) 158 | - [描述符](类文件结构.md#描述符) 159 | - [方法表](类文件结构.md#方法表) 160 | - [方法表计数器](类文件结构.md#方法表计数器) 161 | - [方法表结构](类文件结构.md#方法表结构) 162 | - [方法表访问标志](类文件结构.md#方法表访问标志) 163 | - [重写与重载](类文件结构.md#重写与重载) 164 | - [clinit与init](类文件结构.md#clinit与init) 165 | - [属性表](类文件结构.md#属性表) 166 | - [属性表计数器](类文件结构.md#属性表计数器) 167 | - [属性表通用结构](类文件结构.md#属性表通用结构) 168 | - [全部属性表项](类文件结构.md#全部属性表项) 169 | - [Code](类文件结构.md#Code) 170 | - [Exceptions](类文件结构.md#Exceptions) 171 | - [try-catch-finally语句逻辑](类文件结构.md#try-catch-finally语句逻辑) 172 | - [ConstantValue](类文件结构.md#ConstantValue) 173 | - [InnerClasses](类文件结构.md#InnerClasses) 174 | - [内部类访问标志](类文件结构.md#内部类访问标志) 175 | - [字节码指令](字节码指令.md) 176 | - [字节码指令的定义](字节码指令.md#字节码指令的定义) 177 | - [字节码指令与解释器](字节码指令.md#字节码指令与解释器) 178 | - [数据类型与操作类型](字节码指令.md#数据类型与操作类型) 179 | - [字节码指令分类](字节码指令.md#字节码指令分类) 180 | - [加载和存储指令](字节码指令.md#加载和存储指令) 181 | - [运算指令](字节码指令.md#运算指令) 182 | - [类型转换指令](字节码指令.md#类型转换指令) 183 | - [对象创建与访问指令](字节码指令.md#对象创建与访问指令) 184 | - [操作数栈管理指令](字节码指令.md#操作数栈管理指令) 185 | - [控制转移指令](字节码指令.md#控制转移指令) 186 | - [方法调用和返回指令](字节码指令.md#方法调用和返回指令) 187 | - [异常处理指令](字节码指令.md#异常处理指令) 188 | - [同步指令](字节码指令.md#同步指令) 189 | - [字节码指令表](字节码指令.md#字节码指令表) 190 | 191 | #### 第3章:运行时数据区域 192 | - [运行时数据区域](运行时数据区域.md) 193 | - [运行时数据区域的定义](运行时数据区域.md#运行时数据区域的定义) 194 | - [程序计数器](运行时数据区域.md#程序计数器) 195 | - [虚拟机栈与本地方法栈](运行时数据区域.md#虚拟机栈与本地方法栈) 196 | - [虚拟机栈与本地方法栈的定义](运行时数据区域.md#虚拟机栈与本地方法栈的定义) 197 | - [栈帧的概念](运行时数据区域.md#栈帧的概念) 198 | - [虚拟机栈与本地方法栈溢出](运行时数据区域.md#虚拟机栈与本地方法栈溢出) 199 | - [例子1-单线程调用方法过深导致栈深度溢出](运行时数据区域.md#例子1-单线程调用方法过深导致栈深度溢出) 200 | - [例子2-过多线程调用方法导致OutOfMemory](运行时数据区域.md#例子2-过多线程调用方法导致OutOfMemory) 201 | - [方法区](运行时数据区域.md#方法区) 202 | - [方法区的定义](运行时数据区域.md#方法区的定义) 203 | - [永久代及元空间](运行时数据区域.md#永久代及元空间) 204 | - [元空间取代永久代的理由](运行时数据区域.md#元空间取代永久代的理由) 205 | - [运行时常量池](运行时数据区域.md#运行时常量池) 206 | - [方法区溢出](运行时数据区域.md#方法区溢出) 207 | - [例子1-基于JDK6的字符串常量池溢出](运行时数据区域.md#例子1-基于JDK6的字符串常量池溢出) 208 | - [例子2-使用CGLib让方法区内存溢出](运行时数据区域.md#例子2-使用CGLib让方法区内存溢出) 209 | - [堆区](运行时数据区域.md#堆区) 210 | - [堆区的定义](运行时数据区域.md#堆区的定义) 211 | - [新生代与老年代与永久代](运行时数据区域.md#新生代与老年代与永久代) 212 | - [Eden区与两个Survivor区](运行时数据区域.md#Eden区与两个Survivor区) 213 | - [字符串常量池](运行时数据区域.md#字符串常量池) 214 | - [堆区溢出](运行时数据区域.md#堆区溢出) 215 | - [例子1-创建过多对象导致堆区溢出](运行时数据区域.md#例子1-创建过多对象导致堆区溢出) 216 | - [例子2-再看字符串常量池溢出](运行时数据区域.md#例子2-再看字符串常量池溢出) 217 | - [直接内存](运行时数据区域.md#直接内存) 218 | - [直接内存的定义](运行时数据区域.md#直接内存的定义) 219 | - [直接内存与元空间](运行时数据区域.md#直接内存与元空间) 220 | - [直接内存溢出例子](运行时数据区域.md#直接内存溢出例子) 221 | - [例子1-使用Unsafe让直接内存溢出](运行时数据区域.md#例子1-使用Unsafe让直接内存溢出) 222 | - [调优命令](运行时数据区域.md#调优命令) 223 | - [查看各区域内存大小](运行时数据区域.md#查看各区域内存大小) 224 | - [修改各区域内存大小](运行时数据区域.md#修改各区域内存大小) 225 | - [区域间的指向关系含义](运行时数据区域.md#区域间的指向关系含义) 226 | 227 | #### 第4章:字节码执行引擎 228 | - [字节码执行引擎](字节码执行引擎.md) 229 | - [字节码执行引擎的定义](字节码执行引擎.md#字节码执行引擎的定义) 230 | - [虚拟机栈与本地方法栈](字节码执行引擎.md#虚拟机栈与本地方法栈) 231 | - [类加载与方法运行](字节码执行引擎.md#类加载与方法运行) 232 | - [运行时栈帧结构](字节码执行引擎.md#运行时栈帧结构) 233 | - [局部变量表](字节码执行引擎.md#局部变量表) 234 | - [操作数栈](字节码执行引擎.md#操作数栈) 235 | - [动态链接](字节码执行引擎.md#动态链接) 236 | - [返回地址](字节码执行引擎.md#返回地址) 237 | - [方法调用](字节码执行引擎.md#方法调用) 238 | - [解析调用](字节码执行引擎.md#解析调用) 239 | - [分派调用](字节码执行引擎.md#分派调用) 240 | - [静态分派与方法重载](字节码执行引擎.md#静态分派与方法重载) 241 | - [动态分派与方法重写](字节码执行引擎.md#动态分派与方法重写) 242 | - [虚方法表与接口方法表](字节码执行引擎.md#虚方法表与接口方法表) 243 | - [invokedynamic指令](字节码执行引擎.md#invokedynamic指令) 244 | - [方法执行](字节码执行引擎.md#方法执行) 245 | - [基于字节码解释器的解释执行](字节码执行引擎.md#基于字节码解释器的解释执行) 246 | - [基于模板解释器及即时编译器的混合模式](字节码执行引擎.md#基于模板解释器及即时编译器的混合模式) 247 | - [即时编译技术](字节码执行引擎.md#即时编译技术) 248 | - [JAVA是半编译半解释型语言](字节码执行引擎.md#JAVA是半编译半解释型语言) 249 | 250 | #### 第5章:对象的生命周期 251 | - [对象的生命周期](对象的生命周期.md) 252 | - [对象的创建过程](对象的生命周期.md#对象的创建过程) 253 | - [字节码指令new](对象的生命周期.md#字节码指令new) 254 | - [字节码解释器与new](对象的生命周期.md#字节码解释器与new) 255 | - [类加载检查](对象的生命周期.md#类加载检查) 256 | - [内存分配](对象的生命周期.md#内存分配) 257 | - [指针碰撞](对象的生命周期.md#指针碰撞) 258 | - [空闲列表](对象的生命周期.md#空闲列表) 259 | - [TLAB技术](对象的生命周期.md#TLAB技术) 260 | - [初始化零值](对象的生命周期.md#初始化零值) 261 | - [设置对象头](对象的生命周期.md#设置对象头) 262 | - [执行init方法](对象的生命周期.md#执行init方法) 263 | - [对象的内存布局](对象的生命周期.md#对象的内存布局) 264 | - [对象头](对象的生命周期.md#对象头) 265 | - [MarkWord](对象的生命周期.md#MarkWord) 266 | - [类型指针](对象的生命周期.md#类型指针) 267 | - [数组长度](对象的生命周期.md#数组长度) 268 | - [实例数据](对象的生命周期.md#实例数据) 269 | - [对齐填充](对象的生命周期.md#对齐填充) 270 | - [计算对象大小](对象的生命周期.md#计算对象大小) 271 | - [指针压缩](对象的生命周期.md#指针压缩) 272 | - [对象的访问定位](对象的生命周期.md#对象的访问定位) 273 | - [句柄访问](对象的生命周期.md#句柄访问) 274 | - [直接指针](对象的生命周期.md#直接指针) 275 | 276 | #### 第6章:垃圾收集机制 277 | - [垃圾收集机制](垃圾收集机制.md) 278 | - [垃圾收集算法](垃圾收集机制.md#垃圾收集算法) 279 | - [分代收集理论](垃圾收集机制.md#分代收集理论) 280 | - [标记清除算法](垃圾收集机制.md#标记清除算法) 281 | - [标记整理算法](垃圾收集机制.md#标记整理算法) 282 | - [标记复制算法](垃圾收集机制.md#标记复制算法) 283 | - [引用计数算法](垃圾收集机制.md#引用计数算法) 284 | - [可达性分析算法](垃圾收集机制.md#可达性分析算法) 285 | - [GCRoots与安全点](垃圾收集机制.md#GCRoots与安全点) 286 | - [StopTheWorld](垃圾收集机制.md#StopTheWorld) 287 | - [垃圾收集器](垃圾收集机制.md#垃圾收集器) 288 | - [Serial收集器](垃圾收集机制.md#Serial收集器) 289 | - [ParNew收集器](垃圾收集机制.md#ParNew收集器) 290 | - [ParallelScavenge收集器](垃圾收集机制.md#ParallelScavenge收集器) 291 | - [CMS收集器](垃圾收集机制.md#CMS收集器) 292 | - [G1收集器](垃圾收集机制.md#G1收集器) 293 | - [理解GC日志](垃圾收集机制.md#理解GC日志) 294 | - [内存分配与回收策略](垃圾收集机制.md#内存分配与回收策略) 295 | - [对象优先在Eden区分配](垃圾收集机制.md#对象优先在Eden区分配) 296 | - [长期存活的对象进入老年代](垃圾收集机制.md#长期存活的对象进入老年代) 297 | - [大对象直接进入老年代](垃圾收集机制.md#大对象直接进入老年代) 298 | - [动态年龄判断](垃圾收集机制.md#动态年龄判断) 299 | - [空间分配担保](垃圾收集机制.md#空间分配担保) 300 | - [栈上分配策略](垃圾收集机制.md#栈上分配策略) 301 | 302 | #### 第7章:JVM性能调优 303 | - [JVM性能调优](JVM性能调优.md) 304 | - [基础故障处理工具](JVM性能调优.md#基础故障处理工具) 305 | - [jps-虚拟机进程状况工具](JVM性能调优.md#jps-虚拟机进程状况工具) 306 | - [jstat-虚拟机统计信息监视工具](JVM性能调优.md#jstat-虚拟机统计信息监视工具) 307 | - [jinfo-Java配置信息工具](JVM性能调优.md#jinfo-Java配置信息工具) 308 | - [jmap-Java内存映像工具](JVM性能调优.md#jmap-Java内存映像工具) 309 | - [jhat-虚拟机堆转储快照分析工具](JVM性能调优.md#jhat-虚拟机堆转储快照分析工具) 310 | - [jstack-Java堆栈跟踪工具](JVM性能调优.md#jstack-Java堆栈跟踪工具) 311 | - [进阶故障处理工具](JVM性能调优.md#进阶故障处理工具) 312 | - [HSDB-基于服务性代理的调试工具](JVM性能调优.md#HSDB-基于服务性代理的调试工具) 313 | - [JConsole-Java监视与管理控制台](JVM性能调优.md#JConsole-Java监视与管理控制台) 314 | - [VisualVM-多合一故障处理工具](JVM性能调优.md#VisualVM-多合一故障处理工具) 315 | - [Arthas-Alibaba开源的Java诊断工具](JVM性能调优.md#Arthas-Alibaba开源的Java诊断工具) 316 | - [性能调优记录](JVM性能调优.md#性能调优记录) 317 | - [程序死循环问题排查](JVM性能调优.md#程序死循环问题排查) 318 | - [程序死锁问题排查](JVM性能调优.md#程序死锁问题排查) 319 | - [生成与分析Dump文件](JVM性能调优.md#生成与分析Dump文件) 320 | - [OOM异常问题排查](JVM性能调优.md#OOM异常问题排查) 321 | - [线上业务日志切面](JVM性能调优.md#线上业务日志切面) 322 | - [CPU占用过高问题排查](JVM性能调优.md#CPU占用过高问题排查) 323 | - [亿级流量系统调优案例](JVM性能调优.md#亿级流量系统调优案例) 324 | 325 | #### 第8章:JVM总结 326 | - [JVM总结](JVM总结.md) 327 | - [为什么说JAVA是一种半编译半解释型语言](JVM总结.md#为什么说JAVA是一种半编译半解释型语言) 328 | - [什么情况下需要破坏双亲委派模型](JVM总结.md#什么情况下需要破坏双亲委派模型) 329 | - [GCRoots的作用以及哪些引用可以作为GCRoots](JVM总结.md#GCRoots的作用以及哪些引用可以作为GCRoots) 330 | - [说一下对象内存分配及进入老年代的策略](JVM总结.md#说一下对象内存分配及进入老年代的策略) 331 | - [说下常用垃圾收集器的工作原理](JVM总结.md#说下常用垃圾收集器的工作原理) 332 | - [如何选择一款垃圾收集器](JVM总结.md#如何选择一款垃圾收集器) 333 | - [GC调优策略经验总结](JVM总结.md#GC调优策略经验总结) 334 | 335 | ### 思维导图 336 | 337 | #### 类加载机制 338 | 339 | ![image](思维导图/类加载机制.png) 340 | 341 | #### 运行时数据区域 342 | 343 | ![image](思维导图/运行时数据区域.png) 344 | 345 | #### 垃圾收集机制 346 | 347 | TODO 348 | -------------------------------------------------------------------------------- /深入理解数据结构与算法/数据结构与算法总结/binarytree/RBTree.java: -------------------------------------------------------------------------------- 1 | package binarytree; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class RBTree implements BinarySearchTree { 7 | 8 | @Override 9 | public V get(K key) { 10 | return get(root, key); 11 | } 12 | 13 | private V get(Node x, K key) { 14 | if (x != null) { 15 | int cmp = compare(key, x.key); 16 | if (cmp == 0) 17 | return x.value; 18 | if (cmp < 0) 19 | return get(x.left, key); 20 | else 21 | return get(x.right, key); 22 | } 23 | return null; 24 | } 25 | 26 | private Node getMaxNode(Node x) { 27 | if (x != null) { 28 | if (x.right != null) 29 | return getMaxNode(x.right); 30 | else 31 | return x; 32 | } 33 | return null; 34 | } 35 | 36 | private Node getMinNode(Node x) { 37 | if (x != null) { 38 | if (x.left != null) 39 | return getMinNode(x.left); 40 | else 41 | return x; 42 | } 43 | return null; 44 | } 45 | 46 | private void updateRoot(Node x) { 47 | root = x; 48 | if (x != null) 49 | x.father = null; 50 | } 51 | 52 | private void updateLeft(Node f, Node x) { 53 | if (f != null) 54 | f.left = x; 55 | if (x != null) 56 | x.father = f; 57 | } 58 | 59 | private void updateRight(Node f, Node x) { 60 | if (f != null) 61 | f.right = x; 62 | if (x != null) 63 | x.father = f; 64 | } 65 | 66 | private void updateFather(Node x, Node v) { 67 | if (x.father == null) 68 | updateRoot(v); 69 | else if (x.father.left == x) 70 | updateLeft(x.father, v); 71 | else 72 | updateRight(x.father, v); 73 | } 74 | 75 | private void rotateLeft(Node x) { 76 | Node right = x.right; 77 | Node right_left = x.right.left; 78 | updateFather(x, right); 79 | updateLeft(right, x); 80 | updateRight(x, right_left); 81 | rotateCount ++; 82 | } 83 | 84 | private void rotateRight(Node x) { 85 | Node left = x.left; 86 | Node left_right = x.left.right; 87 | updateFather(x, left); 88 | updateRight(left, x); 89 | updateLeft(x, left_right); 90 | rotateCount ++; 91 | } 92 | 93 | @Override 94 | public V put(K key, V value) { 95 | return put(key, value, false); 96 | } 97 | 98 | @Override 99 | public V putIfAbsent(K key, V value) { 100 | return put(key, value, true); 101 | } 102 | 103 | private V put(K key, V value, boolean onlyIfAbsent) { 104 | if (key == null) 105 | throw new NullPointerException("key can not be null"); 106 | Node x = root; 107 | if (x == null) { 108 | root = new Node(key, value, null, BLACK); 109 | size ++; 110 | return value; 111 | } 112 | while (true) { 113 | int cmp = compare(key, x.key); 114 | if (cmp == 0) { 115 | if (!onlyIfAbsent) 116 | x.value = value; 117 | return value; 118 | } 119 | if (cmp < 0) { 120 | if (x.left == null) { 121 | x.left = new Node(key, value, x, RED); 122 | size ++; 123 | fixAfterPut(x.left); 124 | return value; 125 | } 126 | x = x.left; 127 | } else { 128 | if (x.right == null) { 129 | x.right = new Node(key, value, x, RED); 130 | size ++; 131 | fixAfterPut(x.right); 132 | return value; 133 | } 134 | x = x.right; 135 | } 136 | } 137 | } 138 | 139 | private void fixAfterPut(Node x) { 140 | while (x.father != null && x.father.color == RED) { 141 | if (x.father.father.left == x.father) { 142 | Node uncle = x.father.father.right; 143 | if (uncle == null || uncle.color == BLACK) { 144 | if (x.father.right == x) { 145 | x = x.father; 146 | rotateLeft(x); 147 | } 148 | x.father.father.color = RED; 149 | x.father.color = BLACK; 150 | rotateRight(x.father.father); 151 | break; 152 | } else { 153 | x.father.father.color = RED; 154 | x.father.color = BLACK; 155 | uncle.color = BLACK; 156 | x = x.father.father; 157 | } 158 | } else { 159 | Node uncle = x.father.father.left; 160 | if (uncle == null || uncle.color == BLACK) { 161 | if (x.father.left == x) { 162 | x = x.father; 163 | rotateRight(x); 164 | } 165 | x.father.father.color = RED; 166 | x.father.color = BLACK; 167 | rotateLeft(x.father.father); 168 | break; 169 | } else { 170 | x.father.father.color = RED; 171 | x.father.color = BLACK; 172 | uncle.color = BLACK; 173 | x = x.father.father; 174 | } 175 | } 176 | } 177 | if (root != null) 178 | root.color = BLACK; 179 | } 180 | 181 | @Override 182 | public V remove(K key) { 183 | Node x = root; 184 | while (x != null) { 185 | int cmp = compare(key, x.key); 186 | if (cmp == 0) { 187 | V v = x.value; 188 | if (x.left == null && x.right == null){ 189 | fixAfterRemove(x); 190 | updateFather(x, null); 191 | } else if (x.left == null) { // x must be BLACK Node, x.left must be RED Node 192 | updateFather(x, x.right); 193 | x.right.color = x.color; 194 | } else if (x.right == null) { // x must be BLACK Node, x.right must be RED Node 195 | updateFather(x, x.left); 196 | x.left.color = x.color; 197 | } else if (this.prev) { 198 | Node prev = getMaxNode(x.left); // previous.right must be null 199 | x.key = prev.key; 200 | x.value = prev.value; 201 | if (prev.left != null) { // previous must be BLACK Node, previous.left must be RED Node 202 | updateFather(prev, prev.left); 203 | prev.left.color = prev.color; 204 | } else { 205 | fixAfterRemove(prev); 206 | updateFather(prev, null); 207 | } 208 | } else { 209 | Node suc = getMinNode(x.right); // successor.left must be null 210 | x.key = suc.key; 211 | x.value = suc.value; 212 | if (suc.right != null) { // successor must be BLACK Node, successor.right must be RED Node 213 | updateFather(suc, suc.right); 214 | suc.right.color = suc.color; 215 | } else { 216 | fixAfterRemove(suc); 217 | updateFather(suc, null); 218 | } 219 | } 220 | size --; 221 | return v; 222 | } 223 | if (cmp < 0) 224 | x = x.left; 225 | else 226 | x = x.right; 227 | } 228 | return null; 229 | } 230 | 231 | private void fixAfterRemove(Node x) { 232 | while (x.father != null && x.color == BLACK) { 233 | if (x.father.left == x) { 234 | Node bro = x.father.right; // BLACK Node x must have a brother 235 | if (bro.color == RED) { // change to brother.color == BLACK 236 | x.father.color = RED; 237 | bro.color = BLACK; 238 | rotateLeft(x.father); 239 | bro = x.father.right; 240 | } 241 | boolean bro_left_black = (bro.left == null || bro.left.color == BLACK); 242 | boolean bro_right_black = (bro.right == null || bro.right.color == BLACK); 243 | if (bro_left_black && bro_right_black) { 244 | bro.color = RED; 245 | if (x.father.color == RED) { 246 | x.father.color = BLACK; 247 | break; 248 | } 249 | x = x.father; // continue because nBlocks has changed 250 | } else { 251 | if (bro_right_black) { // change to brother.right.color == RED 252 | bro.color = RED; 253 | bro.left.color = BLACK; 254 | rotateRight(bro); 255 | bro = x.father.right; 256 | } 257 | bro.color = x.father.color; 258 | bro.right.color = BLACK; 259 | x.father.color = BLACK; 260 | rotateLeft(x.father); 261 | break; 262 | } 263 | } else { 264 | Node bro = x.father.left; // BLACK Node x must have a brother 265 | if (bro.color == RED) { // change to brother.color == BLACK 266 | x.father.color = RED; 267 | bro.color = BLACK; 268 | rotateRight(x.father); 269 | bro = x.father.left; 270 | } 271 | boolean bro_left_black = (bro.left == null || bro.left.color == BLACK); 272 | boolean bro_right_black = (bro.right == null || bro.right.color == BLACK); 273 | if (bro_left_black && bro_right_black) { 274 | bro.color = RED; 275 | if (x.father.color == RED) { 276 | x.father.color = BLACK; 277 | break; 278 | } 279 | x = x.father; // continue because nBlocks has changed 280 | } else { 281 | if (bro_left_black) { // change to brother.left.color == RED 282 | bro.color = RED; 283 | bro.right.color = BLACK; 284 | rotateLeft(bro); 285 | bro = x.father.left; 286 | } 287 | bro.color = x.father.color; 288 | bro.left.color = BLACK; 289 | x.father.color = BLACK; 290 | rotateRight(x.father); 291 | break; 292 | } 293 | } 294 | } 295 | if (root != null) 296 | root.color = BLACK; 297 | } 298 | 299 | @Override 300 | public void clear() { 301 | root = null; 302 | size = 0; 303 | rotateCount = 0; 304 | } 305 | 306 | @Override 307 | public int size() { 308 | return size; 309 | } 310 | 311 | @Override 312 | public int height() { 313 | return heightOf(root); 314 | } 315 | 316 | private int heightOf(Node x) { 317 | return x == null ? 0 : 1 + Integer.max(heightOf(x.left), heightOf(x.right)); 318 | } 319 | 320 | private final List> entryList = new ArrayList<>(); 321 | 322 | @Override 323 | public List> entryList() { 324 | entryList.clear(); 325 | checkAll(null, root); 326 | return new ArrayList<>(entryList); 327 | } 328 | 329 | private int checkAll(Node f, Node x) { 330 | check(f, x); 331 | if (x != null) { 332 | entryList.add(x); 333 | int lnb = checkAll(x, x.left); 334 | int rnb = checkAll(x, x.right); 335 | if (lnb != rnb) 336 | throw new RuntimeException("lnb " + lnb + " != rnb " + rnb); 337 | return lnb + (x.color == BLACK ? 1 : 0); 338 | } 339 | return 0; 340 | } 341 | 342 | private void check(Node f, Node x) { 343 | if (x != null) { 344 | if (f == null && x.father != null) 345 | throw new RuntimeException("f == null && x.father != null"); 346 | if (f != null && x.father != f) 347 | throw new RuntimeException("f != null && x.father != f"); 348 | if (x.left != null && compare(x.key, x.left.key) < 0) 349 | throw new RuntimeException("compare(x.key, x.left.key) < 0"); 350 | if (x.right != null && compare(x.key, x.right.key) > 0) 351 | throw new RuntimeException("compare(x.key, x.right.key) > 0"); 352 | if (f != null && f.color == RED && x.color == RED) 353 | throw new RuntimeException("f.color == RED && x.color == RED"); 354 | } 355 | } 356 | 357 | @Override 358 | public int rotateCount() { 359 | return rotateCount; 360 | } 361 | 362 | private Node root = null; 363 | private int size = 0; 364 | private int rotateCount = 0; 365 | private final boolean prev; 366 | 367 | private static final boolean BLACK = true; 368 | private static final boolean RED = false; 369 | 370 | public RBTree() { 371 | this.prev = true; 372 | } 373 | 374 | public RBTree(boolean prev) { 375 | this.prev = prev; 376 | } 377 | 378 | @SuppressWarnings("unchecked") 379 | private int compare(Object k1, Object k2) { 380 | return ((Comparable) k1).compareTo((K)k2); 381 | } 382 | 383 | public class Node implements Entry { 384 | 385 | private K key; 386 | private V value; 387 | private Node left; 388 | private Node right; 389 | private Node father; 390 | private boolean color; 391 | 392 | Node(K key, V value, Node father, boolean color) { 393 | this.key = key; 394 | this.value = value; 395 | this.father = father; 396 | this.color = color; 397 | } 398 | 399 | @Override 400 | public K key() { 401 | return key; 402 | } 403 | 404 | @Override 405 | public V value() { 406 | return value; 407 | } 408 | 409 | @Override 410 | public Entry left() { 411 | return left; 412 | } 413 | 414 | @Override 415 | public Entry right() { 416 | return right; 417 | } 418 | 419 | @Override 420 | public Entry father() { 421 | return father; 422 | } 423 | 424 | public boolean getColor() { 425 | return color; 426 | } 427 | } 428 | } 429 | --------------------------------------------------------------------------------