├── .obsidian ├── app.json ├── appearance.json ├── core-plugins.json ├── hotkeys.json └── workspace ├── 01_Algo ├── 01_BasicKnowledge │ ├── 17~32 │ │ ├── 17_拓扑排序.md │ │ ├── 21_数组压缩&观察位置替代枚举行为.md │ │ ├── 22_动态规划rest相关.md │ │ ├── 23_各种累加和.md │ │ ├── 24_滑动窗口.md │ │ ├── 25_单调栈.md │ │ ├── 26_斐波那契举证快速幂.md │ │ ├── 27_KMP.md │ │ ├── 28_Manacher.md │ │ ├── 29_bfprt&pool.md │ │ ├── 30_MorrisTraversal.md │ │ ├── 31_线段树.md │ │ ├── 32_IndexTree_AC.md │ │ └── Untitled.md │ ├── 1~16 │ │ ├── 01_二分法.md │ │ └── 快排.md │ └── 33~47 │ │ ├── 33_Hash.md │ │ ├── 34_资源限制.md │ │ ├── 39_数据量相关.md │ │ ├── 40_数据量&卡特兰.md │ │ └── 43_状态压缩的动态规划.md ├── 02_forGreat │ └── NormalGreat │ │ ├── 22_.md │ │ ├── 27_.md │ │ ├── 46节.md │ │ ├── 50_.md │ │ └── last.md ├── 03_LeetCodeTop │ ├── 666.md │ ├── Classic.md │ ├── Classic2.md │ ├── DailyCode.md │ ├── Hot.md │ └── Int.md ├── 04_Java │ ├── Java List --remove(int index)与remove(Object o)方法的区别.md │ ├── Java String类中的compareTo方法.md │ ├── Java 中String、StringBuilder和StringBuffer的区别.md │ ├── Java中ArrayList 扩容的代价.md │ ├── Java中HashMap, TreeMap 等的元素遍历顺序.md │ ├── Java中Integer包装类的的128陷阱.md │ ├── Java中split函数.md │ ├── Java中位图的实现.md │ ├── Java中容器, 堆等对加入重复值的处理.md │ ├── Java中的Stack结构.md │ ├── Java中的值传递跟引用传递.md │ ├── Java中的哈希表.md │ ├── Java中的容器, 队列, 堆栈.md │ ├── Java中的左移, 右移操作.md │ ├── Java中的整型, 正数, 负数.md │ ├── Java中的有序表.md │ ├── Java中计时用System.nanoTime().md │ ├── 为什么Java中String类indexof不使用KMP.md │ └── 程序中的随机函数.md ├── 05_Math │ ├── Anecdote.md │ ├── 判断一个数是不是质数.md │ ├── 剃刀函数与约瑟夫环.md │ ├── 卡特兰数.md │ ├── 四平方和定理.md │ ├── 排列组合.md │ ├── 排序算法的传递性.md │ ├── 数和矩阵的N次方怎么算最快.md │ ├── 斐波那契数列.md │ ├── 最大公约数 & 最小公倍数.md │ ├── 求一个数以2为底的对数.md │ ├── 求最大公约数.md │ ├── 泰勒公式.md │ ├── 牛顿迭代法开根号.md │ ├── 科学计数法.md │ ├── 稀疏矩阵及矩阵乘法.md │ ├── 素数 & 质数.md │ ├── 素数筛法.md │ ├── 约瑟夫环.md │ ├── 计算一个数的平方根.md │ └── 鸽笼原理.md ├── 06_Template │ ├── Dijkstra算法代码.md │ ├── KMP算法代码.md │ ├── Kruskal算法代码.md │ ├── LFU内存替换算法的实现.md │ ├── LRU内存替换算法的实现.md │ ├── Manacher算法代码.md │ ├── Morris实现中序遍历.md │ ├── Morris实现前序遍历.md │ ├── Morris实现后序遍历.md │ ├── Morris遍历代码.md │ ├── Prim算法代码.md │ ├── SB树代码.md │ ├── bfprt代码.md │ ├── 一个简单的Hash函数.md │ ├── 不使用加减乘除算符实现加减乘除(仅位运算).md │ ├── 两个等长有序数组求上中位数.md │ ├── 冒泡排序.md │ ├── 单调栈代码.md │ ├── 后缀数组DC3算法实现.md │ ├── 哈希表实现前缀树.md │ ├── 图的拓扑排序算法.md │ ├── 图的统一表述结构.md │ ├── 基数排序代码.md │ ├── 堆排序.md │ ├── 字母表实现前缀树.md │ ├── 字符串的全排列代码.md │ ├── 完美洗牌算法代码.md │ ├── 对数器.md │ ├── 并查集代码.md │ ├── 归并排序代码.md │ ├── 快速排序.md │ ├── 怎么按对角线推导二维矩阵的格子.md │ ├── 手写堆代码.md │ ├── 插入排序.md │ ├── 改写快排的方法求无序数组中的第k小.md │ ├── 数组L...R范围上的累加和计算代码.md │ ├── 数组中离给定K值最近的子数组累加和代码.md │ ├── 枚举所有的i行到j行的情况.md │ ├── 比较器.md │ ├── 求数组中的逆序对数量代码.md │ ├── 求数组小和代码.md │ ├── 没有小括号的表达式结果计算.md │ ├── 线段树代码.md │ ├── 荷兰国旗问题代码.md │ ├── 计数排序.md │ ├── 选择排序.md │ ├── 长方形中任意矩阵的累加和代码.md │ └── 随机生成双向链表.md ├── GenerateForTest │ ├── ACMMod.md │ └── Test.md ├── README.md ├── Tips │ ├── FunnySolution.md │ ├── LeetCodeNote.md │ ├── Tip.md │ └── 递归设计原则.md ├── Trick │ ├── 0x3f3f3f3f.md │ └── 最长递增子序列(LIS).md └── weekly │ ├── 22.02.09.md │ ├── 22.02.18.md │ ├── skill.md │ └── start.md ├── 02_DesignPatterns ├── 01_Singleton.md ├── 02_Strategy.md ├── 03_Factory.md ├── 04_Mediator.md ├── 05_Decorator.md ├── 06_ChainOfResponsibility.md ├── 07_Observer.md ├── 08_Composite.md ├── 09_Flyweight.md ├── 10_Proxy.md ├── 11_Iterator.md ├── 12_Visitor.md ├── 13_Builder.md ├── 14_Adapter.md ├── 15_Bridge.md ├── 16_Command.md ├── 17_ProtoType.md ├── 18_Memento.md ├── 19_TemplateMethod.md ├── 20_State.md ├── 21_Interpreter.md └── 22_总结.md ├── 03_JavaWeb ├── 01_JavaWeb.md ├── 01_SpringBoot │ └── SpringBootNote.md ├── 02_MyBatis.md ├── 03_SpringMVC.md ├── 04_SpringBoot.md ├── Mybatis核心流程图.png └── indexTree.png ├── 04_JUC ├── BytecodeInstruction.png ├── JUC_1.md ├── JVM.png ├── JVM脑图.png ├── System_IO.png ├── 多线程与高并发.md └── 常见面试问题.md ├── 05_JVM └── Test.md ├── 06_Middleware ├── 01_Redis │ ├── 01Redis前无古人后无来者.jpg │ ├── 02REDIS集群知识点.jpg │ ├── ProcessOn.md │ ├── RedisNote │ │ ├── 01.md │ │ └── 02.md │ ├── RedisSummary.md │ └── RedisUse │ │ ├── 01_Jmeter.md │ │ └── 02_redis思路.md ├── 02_ZooKeeper │ ├── ZKTest.md │ └── ZOOKEEPER.png ├── 03_Nginx │ └── Load Blalancing.md ├── 04_SkyWalking │ └── basic.md ├── Git.md └── README.md ├── 07_Network ├── 01_计算机网络基础知识.md ├── 02_OAuth.md ├── 03_加密算法.md └── 04_socket.md ├── 08_OS ├── IO.md └── Linux.md ├── 09_SystemDesign ├── 01_Legend of Hero │ ├── 00_BUG.md │ ├── 00_LOH.md │ ├── 01_LOHSummary.md │ ├── 02_ProtoBuf.md │ ├── 03_ChannelPipeline.md │ └── 04_CmdHandlerFactory.md ├── 02_Taxi │ ├── 00_设计中重要的点.md │ ├── 00_需求.md │ ├── 01_项目概述-需求分析.md │ ├── 02_服务拆分-接口设计.md │ ├── 03_工程设计-eureka优化_01.md │ ├── 04-eureka-server源码-服务测算.md │ ├── 05_用户登陆&验证码.md │ ├── 06_order&计价.md │ ├── 10_派单逻辑.md │ ├── 11_订单状态.md │ ├── 12_其他.md │ ├── 13_灰度发布.md │ ├── 14_zuul工作中的问题.md │ ├── 15_网约车坐标系问题.md │ ├── 16_2pc3pc.md │ ├── 23-tcc .md │ ├── 27-seata落地.md │ ├── 29-rocket总结-分布式事务总结.md │ ├── 30-大纲 技术点.md │ ├── 99_数据库设计.md │ └── eureka源码结构.png ├── 03_MySQL │ ├── 09-mysql架构.pdf │ ├── BasicKnowledge │ │ ├── 00_Tip.md │ │ ├── 01_数据类型.md │ │ ├── 02_修改数据code.md │ │ ├── 03_完整性约束.md │ │ ├── 04_简单SQL查询.md │ │ ├── 05_函数.md │ │ ├── 06_连接.md │ │ ├── 07_子查询.md │ │ ├── 08_MySQL执行流程.md │ │ └── 08_MySQL锁机制.md │ ├── Linux下mysql5.7的彻底卸载.md │ ├── MYSQL performance schema详解.md │ ├── MYSQL5.7详细安装步骤.md │ ├── MySQL优化.md │ ├── Mysql调优.xmind │ ├── Tuning │ │ ├── 01_数据类型优化.md │ │ ├── 02_执行计划.md │ │ ├── 03_索引基础知识.md │ │ └── 04_索引细节知识.md │ ├── mysql主从复制原理.md │ ├── mysql主从复制安装配置.md │ ├── mysql事务测试.md │ ├── mysql执行计划.md │ ├── mysql数据结构选择.png │ ├── mysql数据结构选择.pos │ ├── mysql架构.jpg │ ├── mysql的锁机制.md │ ├── mysql索引系统 .png │ ├── mysql索引系统.pos │ ├── mysql练习题.md │ ├── mysql读写分离.md │ ├── sakila数据库说明.md │ ├── typora-user-images │ │ ├── 1570541665646.png │ │ ├── 1570541838485.png │ │ ├── 1570541946471.png │ │ ├── 1570542045332.png │ │ ├── 1570542254949.png │ │ ├── 1570542341604.png │ │ ├── 1570542415955.png │ │ ├── 1570542471948.png │ │ ├── 1570542688796.png │ │ ├── 1570604493708.png │ │ ├── 1570605309658.png │ │ ├── 1570605325400.png │ │ ├── 1570605553095.png │ │ ├── 1570703264912.png │ │ ├── 1570714549624.png │ │ ├── 1570714565647.png │ │ ├── 1570714576819.png │ │ ├── 1570714615915.png │ │ ├── 1570714660961.png │ │ ├── 1570776205802.png │ │ └── image-20191203125003597.png │ ├── 使用amoeba实现mysql读写分离.md │ ├── 分区表的底层原理.md │ ├── 前缀索引实例说明.md │ ├── 索引优化分析案例.md │ ├── 红黑树.png │ ├── 红黑树.pos │ ├── 范围分区.md │ └── 覆盖索引.md ├── 04_GameSystem │ ├── 01_COCSystem.md │ └── SLG │ │ ├── 01_SLGArchitecture.md │ │ ├── 02_SLGServer.md │ │ ├── 03_Sort.md │ │ └── 04_SLGData.md ├── 05_IMSystem.md ├── Untitled.md └── resources.md ├── 100_Other ├── 01_Golang │ ├── Go与Java.md │ ├── Go基础语法.md │ ├── Go设计模式.md │ └── Test.md ├── 01_JVM梳理.md ├── 02_Erlang │ └── 01_基础知识 └── 02_Redis梳理.md ├── 97_Pic ├── Pasted image 20220209212709.png ├── Pasted image 20220209215251.png ├── Pasted image 20220209215709.png ├── Pasted image 20220210011115.png └── Pasted image 20220220205114.png ├── 99_Test ├── 01_All │ ├── 00_ForResume.md │ ├── 00_Project.md │ ├── 01_Java.md │ ├── 02_JUC.md │ ├── 02_JUC2 │ ├── 03_MySQL.md │ ├── 04_OS.md │ ├── 05_Redis.md │ ├── 05_RedisSummary.md │ ├── 06_底层知识.md │ ├── 07_王道操作系统知识.md │ ├── 08_MQ.md │ ├── 09_Algo.md │ ├── 10_Network.md │ ├── 66_Soft.md │ └── 99_Required.md ├── 02_InterviewTest │ └── Interview.md ├── 03_AE │ ├── ForDeepRoute_G.md │ ├── HWOD.md │ ├── Haiy.md │ ├── HotorGames.md │ ├── ProjectZ.md │ ├── TaoMi.md │ ├── Tap4Fun.md │ ├── YouDao3.31.md │ ├── test.md │ ├── 万兴.md │ ├── 有可能的coding.md │ └── 有可能的场景题.md ├── ResumeTest.md ├── Tip.md ├── Untitled.md ├── test.md └── 互联网装逼词汇.md ├── README.md ├── TODO.md └── Word.md /.obsidian/app.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.obsidian/appearance.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseFontSize": 16 3 | } -------------------------------------------------------------------------------- /.obsidian/core-plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "file-explorer", 3 | "global-search", 4 | "switcher", 5 | "graph", 6 | "backlink", 7 | "page-preview", 8 | "note-composer", 9 | "command-palette", 10 | "editor-status", 11 | "markdown-importer", 12 | "word-count", 13 | "open-with-default-app", 14 | "file-recovery" 15 | ] -------------------------------------------------------------------------------- /.obsidian/hotkeys.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.obsidian/workspace: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "id": "a55d91e737ff53f0", 4 | "type": "split", 5 | "children": [ 6 | { 7 | "id": "d852d6d20fb82563", 8 | "type": "leaf", 9 | "state": { 10 | "type": "markdown", 11 | "state": { 12 | "file": "Algo/01_BasicKnowledge/17~32/17_拓扑排序.md", 13 | "mode": "source" 14 | } 15 | } 16 | } 17 | ], 18 | "direction": "vertical" 19 | }, 20 | "left": { 21 | "id": "a54fbee109714e5a", 22 | "type": "split", 23 | "children": [ 24 | { 25 | "id": "20c7cdbc015e5944", 26 | "type": "tabs", 27 | "children": [ 28 | { 29 | "id": "44f9a0f84cc921b9", 30 | "type": "leaf", 31 | "state": { 32 | "type": "file-explorer", 33 | "state": {} 34 | } 35 | }, 36 | { 37 | "id": "997d32aaf21a80ba", 38 | "type": "leaf", 39 | "state": { 40 | "type": "search", 41 | "state": { 42 | "query": "", 43 | "matchingCase": false, 44 | "explainSearch": false, 45 | "collapseAll": false, 46 | "extraContext": false, 47 | "sortOrder": "alphabetical" 48 | } 49 | } 50 | } 51 | ] 52 | } 53 | ], 54 | "direction": "horizontal", 55 | "width": 300 56 | }, 57 | "right": { 58 | "id": "ebf45960cf73b523", 59 | "type": "split", 60 | "children": [ 61 | { 62 | "id": "3a98c49e8a6b5df1", 63 | "type": "tabs", 64 | "children": [ 65 | { 66 | "id": "58fe96009cd6cc1e", 67 | "type": "leaf", 68 | "state": { 69 | "type": "backlink", 70 | "state": { 71 | "file": "Algo/01_BasicKnowledge/17~32/17_拓扑排序.md", 72 | "collapseAll": false, 73 | "extraContext": false, 74 | "sortOrder": "alphabetical", 75 | "showSearch": false, 76 | "searchQuery": "", 77 | "backlinkCollapsed": false, 78 | "unlinkedCollapsed": true 79 | } 80 | } 81 | } 82 | ] 83 | } 84 | ], 85 | "direction": "horizontal", 86 | "width": 300, 87 | "collapsed": true 88 | }, 89 | "active": "d852d6d20fb82563", 90 | "lastOpenFiles": [ 91 | "Algo/01_BasicKnowledge/17~32/17_拓扑排序.md" 92 | ] 93 | } -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/17~32/17_拓扑排序.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/01_Algo/01_BasicKnowledge/17~32/17_拓扑排序.md -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/17~32/23_各种累加和.md: -------------------------------------------------------------------------------- 1 | # 累加和 2 | 3 | 给定一个正数数组arr, 4 | 请把arr中所有的数分成两个集合,尽量让两个集合的累加和接近 5 | 返回: 6 | 最接近的情况下,较小集合的累加和 7 | 8 | 9 | 10 | ## 解题 11 | 12 | ```java 13 | arr[i...]可以自由选择,请返回累加和尽量接近rest,但不能超过rest的情况下,最接近的累加和是多少? 14 | ``` 15 | 16 | 就选择的结果, 累加和 < 17 | 18 | ```java 19 | // process(arr, 0, sum / 2); 20 | public static int process( int[] arr, int i, int rest ) { 21 | //走到头了, 情况无效 22 | if (i == arr.length) { 23 | return 0; 24 | } else { // 还有数,arr[i]这个数 25 | // 可能性1,不使用arr[i] 26 | int p1 = process(arr, i + 1, rest); 27 | // 可能性2,要使用arr[i] 28 | int p2 = 0; 29 | if (arr[i] <= rest) { 30 | //要当前的数字, 加上 后续的选择的和, 就是p2 31 | p2 = arr[i] + process(arr, i + 1, rest - arr[i]); 32 | } 33 | //为什么要取最大值? 在arr[i]>表示右移,如果该数为正,则高位补0,若为负数,则高位补1; 58 | 59 | \>>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。 60 | 61 | 62 | 63 | 最好是画出树图, 然后写递归, 逻辑比较清晰 ! ! ! 64 | 65 | 66 | 67 | # 累加和 - 进阶 - 字节面试 68 | 69 | ``` 70 | //给定一个正数数组arr,请把arr中所有的数分成两个集合 71 | //如果arr长度为偶数,两个集合包含数的个数要一样多 72 | //如果arr长度为奇数,两个集合包含数的个数必须只差一个 73 | //请尽量让两个集合的累加和接近 74 | //返回: 75 | //最接近的情况下,较小集合的累加和 76 | 77 | //(较大集合的累加和一定是所有数累加和减去较小集合的累加和) 78 | ``` 79 | 80 | ## 解题 81 | 82 | **①偶数** : 返回个数为 lenght/2 , 最接近 sum/2 的结果 83 | 84 | **②奇数** : 返回个数为 lenght/2 | (lenght/2)+1 , 最接近 sum/2 的结果 85 | 86 | 87 | 88 | ```java 89 | // arr[i....]自由选择,挑选的个数一定要是picks个,累加和<=rest, 离rest最近的返回 90 | public static int process(int[] arr, int i, int picks, int rest) { 91 | if (i == arr.length) { 92 | return picks == 0 ? 0 : -1; 93 | } else { 94 | int p1 = process(arr, i + 1, picks, rest); 95 | // 就是要使用arr[i]这个数 96 | int p2 = -1; 97 | int next = -1; 98 | if (arr[i] <= rest) { 99 | next = process(arr, i + 1, picks - 1, rest - arr[i]); 100 | } 101 | if (next != -1) { 102 | p2 = arr[i] + next; 103 | } 104 | return Math.max(p1, p2); 105 | } 106 | } 107 | ``` 108 | 109 | ## 总结 110 | 111 | while(index < arr.lenght){ 112 | 113 | 114 | 115 | //常用于从初始index执行到本数组最后一个,包括最后一个 116 | 117 | index++; 118 | 119 | continue; 120 | 121 | } 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/17~32/28_Manacher.md: -------------------------------------------------------------------------------- 1 | # Manacher(最长回文子串) 2 | 3 | https://www.jianshu.com/p/116aa58b7d81 预处理如下 4 | 5 | ![image-20211129113040133](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211129113040133.png) 6 | 7 | - 回文直径 8 | 9 | - 回文半径 10 | 11 | - 回文半径数组, 生成的帮助数组 12 | 13 | parr[ ] 14 | 15 | - 最右回文右边界, 过程中一直更新 16 | 17 | int R = -1 18 | 19 | - 最右回文中心点 20 | 21 | int C = 1 22 | 23 | ### 第一种情况:i没有被R罩住 24 | 25 | 在这种情况下,采用**普遍的解法**,将移动的位置为对称中心,向两边扩,同时更新回文半径数组,最右回文右边界R和最右回文右边界的对称中心C。 26 | 27 | ### 第二种情况:i被R罩住 28 | 29 | **用C左边的数据 求 C右边的数据!** 30 | 31 | 下一个要移动的位置就是最右回文右边界R或是在R的左边 32 | 33 | 在这种情况下又分为三种: 34 | 35 | 1、下一个要移动的位置p1**不在**最右回文右边界R右边,且cLpL。 48 | 49 | p2是p1以C为对称中心的对称点; 50 | 51 | pL是以p2为对称中心的回文子串的左边界; 52 | 53 | cL是以C为对称中心的回文子串的左边界。 54 | 55 | 这种情况下p1的回文半径就是**==p1到R==**的距离R-p1+1。 56 | 57 | ![image-20211129115557663](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211129115557663.png) 58 | 59 | 3、下一个要移动的位置票p1**不在**最右回文右边界R的右边,且cL=pL; 60 | 61 | p2是p1以C为对称中心的对称点; 62 | 63 | pL是以p2为对称中心的回文子串的左边界; 64 | 65 | cL是以C为对称中心的回文子串的左边界。 66 | 67 | 这种情况下p1的回文半径就还要继续往外扩,但是只需要从R之后往外扩就可以了,扩了之后更新R和C。 68 | 69 | ![image-20211129115607489](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211129115607489.png) 70 | 71 | 72 | 73 | # 例题 74 | 75 | 给定一个字符串, 在后面加字符, 求最加的最少字数使得整体成为回文. 76 | 77 | 78 | 79 | # KMP例题 80 | 81 | 检查两个字符串是否互为旋转串 82 | 83 | 把s1+s1然后看s2是否为s1+s1子串 84 | 85 | 86 | 87 | # 子树问题 88 | 89 | 序列化然后KMP 90 | 91 | -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/17~32/29_bfprt&pool.md: -------------------------------------------------------------------------------- 1 | # 在无序数组中求第K小的数 2 | 3 | 1)改写快排的方法O(N), 因为不回退 4 | 5 | 就是partition对数组做荷兰国旗问题的划分, 然后通过< = > 三个区域的边界, 6 | 看(i > L && i < R) 如果是, 则返回arr[i] 7 | 否则对k位数在的区间做partition 8 | 9 | 2)bfprt算法 10 | 11 | 12 | 13 | ![image-20211129163244085](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211129163244085.png) 14 | 15 | 16 | 17 | **选出相对平凡的数, 避免出现差情况** 18 | 19 | 20 | 21 | # top k问题 22 | 23 | 给定一个无序数组arr中,长度为N,给定一个正数k,返回top k个最大的数 24 | 25 | 不同时间复杂度三个方法: 26 | 27 | - **O(N*logN)** 28 | 29 | 直接排序取出前K个 30 | 31 | - **O(N + K*logN)O** 32 | 33 | 大根堆(从底往上建堆) + 弹出前K个元素 34 | 35 | - **O(N + k*logk)** 36 | 37 | 先求出第k大的数x, 然后过一遍, 把小于x的拿出来 => O(N) 38 | 39 | 一共k个,再排序就是k*logk 40 | 41 | 42 | 43 | # 蓄水池(巨型抽奖问题) 44 | 45 | 假设有一个源源吐出不同球的机器, 46 | 47 | 只有装下10个球的袋子,每一个吐出的球,**要么放入袋子,要么永远扔掉** (二选一) 48 | 49 | 如何做到机器吐出每一个球之后,所有吐出的球都等概率被放进袋子里 50 | 51 | (袋子里的球也可能被淘汰) 52 | 53 | 54 | 55 | ### 公平思路 56 | 57 | 小于10直接放入 58 | 59 | ==**大于10就以 10/i 的概率判断是否入袋, 然后如果入袋, 就从袋子里面随机淘汰一个**== 60 | 61 | 保证每个球等概率入袋, 并且实现一个一个入袋 62 | 63 | 64 | 65 | # uuid生成器 66 | 67 | ![image-20211129181337755](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211129181337755.png) 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/17~32/31_线段树.md: -------------------------------------------------------------------------------- 1 | # 线段树 2 | 3 | 参考这位巨佬的理解https://www.cnblogs.com/AKMer/p/9946944.html 4 | 树状数组和线段树都是用于维护数列信息的数据结构, 支持单点/区间修改, 单点/区间询问信息. 5 | 以增加权值与询问区间权值和为例, 其余的信息需要维护也都类似, 时间复杂度均为O(logn). 6 | 7 | 区间的修改., 更新, 查询一些东西, 怎么样才能比较快 8 | 可以叫 区间修改树更准确, 最早是解决线段重合问题 9 | 物理结构不是树, 用的时候跟树也关系不大 10 | 只是在逻辑概念上对应成了一棵树 11 | 12 | 13 | 14 | 1. **一种支持范围整体修改和范围整体查询的数据结构** 15 | 16 | 2. **解决的问题范畴:** 17 | **大范围信息可以==只由左、右两侧信息简单加工出==,** 18 | **而不必遍历左右两个子范围的具体状况** 19 | **比如: ==最大值, 最小值, 累加和==** 20 | 21 | 区间范围上要统一增加一个值,或者update一个值, 22 | 需要查询的一个值满足如下条件才能用线段树: 23 | **父范围的信息可以由左树右树的信息直接加工出来, 不需要具体调研左树和右树状况** 24 | 25 | 26 | 27 | 比如说找区间众数, 不能由左右子树加工, 只能全体整理信息 28 | 29 | 30 | 31 | 还是**分而治之**的思想 32 | 33 | 34 | 35 | # 例题 36 | 37 | 38 | 39 | 给定一个数组arr,用户希望你实现如下三个方法 40 | 1)void add(int L, int R, int V) : 让数组arr[L…R]上每个数都加上V 41 | 2)void update(int L, int R, int V) : 让数组arr[L…R]上每个数都变成V 42 | 3)int sum(int L, int R) :让返回arr[L…R]这个范围整体的累加和 43 | 怎么让这三个方法,时间复杂度都是O(logN) 44 | 45 | 46 | 47 | 48 | 49 | https://leetcode-cn.com/problems/falling-squares/ 50 | 51 | 想象一下标准的俄罗斯方块游戏,X轴是积木最终下落到底的轴线 52 | 下面是这个游戏的简化版: 53 | 1)只会下落正方形积木 54 | 2)[a,b] -> 代表一个边长为b的正方形积木,积木左边缘沿着X = a这条线从上方掉落 55 | 3)认为整个X轴都可能接住积木,也就是说简化版游戏是没有整体的左右边界的 56 | 4)没有整体的左右边界,所以简化版游戏不会消除积木,因为不会有哪一层被填满。 57 | 58 | 给定一个N* 2的二维数组matrix,可以代表N个积木依次掉落,返回每一次掉落之后的最大高度 59 | 60 | -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/17~32/Untitled.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/01_Algo/01_BasicKnowledge/17~32/Untitled.md -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/1~16/01_二分法.md: -------------------------------------------------------------------------------- 1 | ![image-20220327223241331](https://s2.loli.net/2022/03/27/i4YBg1e8EykTrsU.png) 2 | 3 | # 如果以后再想不到二分答案, 就给自己一个耳光 ! 4 | 5 | ![image-20220327223159627](https://s2.loli.net/2022/03/27/rDZEqyXAeGfCOo4.png) 6 | 7 | # 二分法 8 | 9 | 经常见到的类型是在一个有序数组上,开展二分搜索 10 | 11 | 但有序真的是所有问题求解时使用二分的必要条件吗? 12 | 13 | 14 | 15 | **==不, 只要能正确构建左右两侧的淘汰逻辑,你就可以二分。==** 16 | 17 | 18 | 19 | 甚至可以直接根据数据范围二分猜答案, 如果你的答案是一种极端逻辑 20 | 21 | 答案的左边一定是啥,右边一定是另外一种啥 22 | 23 | ### 1) 在一个有序数组中,找某个数是否存在 24 | 25 | ```java 26 | int L = 0; 27 | int R = sortedArr.length - 1; 28 | int mid = 0; 29 | // L..R 30 | while (L < R) { // L..R 至少两个数的时候 31 | mid = L + ((R - L) >> 1); 32 | if (sortedArr[mid] == num) { 33 | return true; 34 | } else if (sortedArr[mid] > num) { 35 | R = mid - 1; 36 | } else { 37 | L = mid + 1; 38 | } 39 | } 40 | return sortedArr[L] == num; 41 | ``` 42 | 43 | ### 2) 在一个有序数组中,找>=某个数最左侧的位置 44 | 45 | ```java 46 | public static int nearestIndex(int[] arr, int value) { 47 | int L = 0; 48 | int R = arr.length - 1; 49 | int index = -1; // 记录最左的对号 50 | while (L <= R) { [Test01二分1.java](C:\IDEA_workspace\coding-for-great-offer\src\a_trick\test\Test01二分1.java) // 至少一个数的时候 51 | int mid = L + ((R - L) >> 1); 52 | if (arr[mid] >= value) { 53 | index = mid; 54 | R = mid - 1; 55 | } else { 56 | L = mid + 1; 57 | } 58 | } 59 | return index; 60 | } 61 | ``` 62 | 63 | ### 3) 在一个有序数组中,找<=某个数最右侧的位置 64 | 65 | ### 4) 局部最小值问题, 可能有多个, 返回任意一个 66 | 67 | 找到任意一个谷底 V 68 | 69 | 无序数组 任意相邻数不等 70 | 71 | ```java 72 | public static int getLessIndex(int[] arr) { 73 | if (arr == null || arr.length == 0) { 74 | return -1; // no exist 75 | } 76 | if (arr.length == 1 || arr[0] < arr[1]) { 77 | return 0; 78 | } 79 | if (arr[arr.length - 1] < arr[arr.length - 2]) { 80 | return arr.length - 1; 81 | } 82 | int left = 1; 83 | int right = arr.length - 2; 84 | int mid = 0; 85 | while (left < right) { 86 | mid = (left + right) / 2; 87 | if (arr[mid] > arr[mid - 1]) { 88 | right = mid - 1; 89 | } else if (arr[mid] > arr[mid + 1]) { 90 | left = mid + 1; 91 | } else { 92 | return mid; 93 | } 94 | } 95 | return left; 96 | } 97 | ``` -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/1~16/快排.md: -------------------------------------------------------------------------------- 1 | # 快排1.0(每次搞定一个数) 2 | 3 | 直接选择最右侧的值为划分值. 4 | 5 | 小于等于放左边, 大于不管, 最后直接把划分值放到小于等于区的最右边 6 | 7 | 这是每次搞定一个数, 效率低. 8 | 9 | # 快排2.0(每次搞定一批数) 10 | 11 | 随机选择一个数, 与最后一个数交换, 作为划分值. 12 | 13 | 小于放左边, 等于放中间, 大于放右边, 然后递归左区和右区同样过程. 14 | 15 | 这个是每次搞定一批数, 这一批数就是划分值相等的所有元素. 16 | 17 | # bfprt快排(固定选划分值的快排) 18 | 19 | 线性时间的 固定选划分值的快排 它是MIT几个大学生研究出来的, 五个人首字母BFPRT 20 | 21 | 中位数的中位数, 使得选择的划分值打得更接近中间. -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/33~47/33_Hash.md: -------------------------------------------------------------------------------- 1 | # Hash 2 | 3 | in --> ∞ 4 | 5 | 6 | 7 | out--> 0 ~ (2^64) - 1 8 | 9 | ​ --> 0 ~ (2^128) - 1 10 | 11 | 所以一定会有hash碰撞 12 | 13 | 14 | 15 | 离散散列 16 | 17 | 18 | 19 | 20 | 21 | # HashMap 22 | 23 | 24 | 25 | ![img](https://s2.loli.net/2021/12/03/wOhoMRuUTBCKHA4.png) 26 | 27 | 28 | 29 | 17个桶空间 30 | 31 | 32 | 33 | - 当每个桶挂的树叶差不多到了阈值了 34 | 35 | 36 | 37 | - 就开始扩容 38 | 39 | 40 | 41 | - 扩容后每个节点都重新算hash值 重新算模% 42 | 43 | 44 | 45 | 当每个桶挂的树叶又差不多到了阈值了...... 46 | 47 | 48 | 49 | ## 时间复杂度? 50 | 51 | 就算最挫的时候也是单次O(1) ==> 一个桶, 不能挂任何枝叶 52 | 53 | 54 | 55 | 后面的红黑树什么优化都是小优化 56 | 57 | 58 | 59 | 60 | 61 | 原文链接:https://blog.csdn.net/LoveMyTail/article/details/107286727 62 | 63 | 不管插入还是查找,由key获取hash值然后定位到桶的时间复杂度都是O(1),那么真正决定时间复杂度的实际上是桶里面链表/红黑树的情况 64 | 65 | 如果桶里面没有元素,那么直接将元素插入/或者直接返回未查找到,时间复杂度就是O(1),如果里面有元素,那么就沿着链表进行遍历,时间复杂度就是O(n),链表越短时间复杂度越低,如果是红黑树的话那就O(logn) 66 | 67 | 所以平均复杂度很难说,只能说在最优的情况下是O(1) 68 | 69 | # 布隆过滤器 70 | 71 | 爬虫1 爬虫2 爬虫3 爬虫4...... ====> 爬之前映射到bitmap中 看看是否已经爬过了 72 | 73 | 74 | 75 | 然后能想到布隆过滤器的结构 76 | 77 | ## 布隆过滤器原理 78 | 79 | 1. 你的数据库里面有啥玩意 80 | 2. 你有啥先在我bitmap里面备案 81 | 3. 请求来了 我先算一下 如果你的请求没备案 就舍弃!!!(节省了服务器的开销啊) 82 | 4. 请求有可能被误标记 没关系 很少很少 83 | 5. 而且 省钱 成本低 84 | 85 | 86 | 87 | ![image-20211204003937770](https://s2.loli.net/2021/12/04/CL94MEankFA78Ni.png) 88 | 89 | 90 | 91 | 92 | 93 | **咱允许有失误率嘛?** 94 | 95 | 然后再讨论 聊 96 | 97 | ## 布隆过滤器重要的三个公式(==全文背诵==) 98 | 99 | 1. 假设数据量为n,预期的==失误率为p==(布隆过滤器大小和每个样本的大小无关) 100 | 101 | 2. 根据n和p,算出Bloom Filter一共需要多少个bit位,向上取整,记为m 102 | 103 | 3. 根据m和n,算出Bloom Filter需要多少个哈希函数,向上取整,记为k 104 | 105 | 4. 根据修正公式,算出真实的失误率p_true 106 | 107 | 108 | 109 | ![image-20211204005604194](https://s2.loli.net/2021/12/04/cAFaqbVeSu3RxsZ.png) 110 | 111 | ## HDFS 112 | 113 | 每块存储区域维护一个bloom 114 | 115 | 可以减少遍历 块数 116 | 117 | 118 | 119 | # 一致性哈希 120 | 121 | **机器增减自如, 也解决了分布不均的问题** 122 | 123 | ![image-20211204134405196](https://s2.loli.net/2021/12/04/RrK1svBcjQkglxN.png) 124 | 125 | **根据m机器的性能不同, 可以设置不同的虚拟节点数量** 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | C++反外挂 => 136 | 137 | unity 自带的工具... 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /01_Algo/01_BasicKnowledge/33~47/40_数据量&卡特兰.md: -------------------------------------------------------------------------------- 1 | ### 数据量例题 2 | 3 | 给定一个非负数组arr,和一个正数m 4 | 返回arr的所有**==子序列==**中累加和%m之后的最大值 5 | 6 | (找最接近并且 [小于] [m的倍数] 的序列之和 %m 所得值 ) 7 | 8 | 9 | 10 | #### 法1:(arr[i] 值不大, 但是m巨大10^12) 11 | 12 | 填boolean dp[i] [j] => 0到 i 下标的数字 自由选择 能不能搞出累加和为 j 13 | 14 | 15 | 16 | 转移: 17 | 18 | 不算上i位置的值 => dp[ i- 1] [ j ] 19 | 20 | 算上i位置的值 => dp[i - 1] [j - (i)] 21 | 22 | ```java 23 | public static int max2(int[] arr, int m) { 24 | int sum = 0; 25 | int N = arr.length; 26 | for (int i = 0; i < N; i++) { 27 | sum += arr[i]; 28 | } 29 | boolean[][] dp = new boolean[N][sum + 1]; 30 | for (int i = 0; i < N; i++) { 31 | dp[i][0] = true; 32 | } 33 | dp[0][arr[0]] = true; 34 | for (int i = 1; i < N; i++) { 35 | for (int j = 1; j <= sum; j++) { 36 | dp[i][j] = dp[i - 1][j]; 37 | if (j - arr[i] >= 0) { 38 | dp[i][j] |= dp[i - 1][j - arr[i]]; 39 | } 40 | } 41 | } 42 | int ans = 0; 43 | for (int j = 0; j <= sum; j++) { 44 | if (dp[N - 1][j]) {//拿dp表暴力模 45 | ans = Math.max(ans, j % m); 46 | } 47 | } 48 | return ans; 49 | } 50 | ``` 51 | 52 | #### 法2: arr[i] 值大 m小=>把模完之后的结果 写进dp 53 | 54 | dp[i] [j] => 0~i 所有的数字自由选择 得到的累加和 是否是 == j 55 | 56 | 转移: 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /01_Algo/02_forGreat/NormalGreat/27_.md: -------------------------------------------------------------------------------- 1 | ## 乐队演出的最少花费(分治) 2 | 3 | 每一个项目都有三个数,[a,b,c]表示项目a和b乐队参演,花费为c 4 | 给定很多个项目int[][] programs 5 | 每一个乐队可能在多个项目里都出现了,但是只能挑一次 6 | nums是可以挑选的项目数量,所以一定会有nums * 2只乐队被挑选出来 7 | 返回一共挑nums轮(也就意味着一定请到所有的乐队),最少花费是多少? 8 | 9 | nums < 9, programs长度小于500,每组测试乐队的全部数量一定是nums * 2,且标号一定是0 ~ nums * 2-1 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ## 股票问题 18 | 19 | 股民小 A 有一天穿越回到了 n 天前,他能记住某只股票连续 n 天的价格;他手上有足够多的启动资金,他可以在这 n 天内多次交易,但是有个限制2 20 | 如果已经购买了一个股票,在卖出它之前就不能再继续购买股票了。 21 | 到 n 天之后,小 A 不能再持有股票。 22 | 求问 这 n 天内,小 A 能够获得的利润的最大值 23 | 如果不需要手续费的话,求最大的利润 24 | function(number) { 25 | return number 26 | } 27 | 输入: prices = [3, 1, 2, 8, 5, 9] 28 | 输出: 11 29 | 30 | 31 | 32 | ## 企鹅的最少数量(781-森林兔子) 33 | 34 | 企鹅厂每年都会发文化衫,文化衫有很多种颜色,厂庆的时候,企鹅们都需要穿文化衫来拍照 35 | 一次采访中,记者随机遇到的企鹅,企鹅会告诉记者还有多少企鹅跟他穿一个颜色的文化衫 36 | 我们将这些回答放在 answers 数组里,返回鹅厂中企鹅的最少数量。 37 | 输入: answers = [1] 38 | 一个人回答,还有 1 个人跟他穿一样颜色的文化衫,所以最少是 2个人, 39 | 输出 2 40 | 输入: answers = [1, 1, 2] 41 | 输出: 5 42 | 43 | 44 | 45 | ## 俄罗斯信封套娃问题(最长递增子序列) 46 | 47 | WXG 的秘书有一堆的文件袋,现在秘书需要把文件袋嵌套收纳起来。请你帮他计算下,最大嵌套数量。 48 | 给你一个二维整数数组 folders ,其中 folders[i] = [wi, hi] ,表示第 i 个文件袋的宽度和长度 49 | 当某一个文件袋的宽度和长度都比这个另外一个文件袋大的时候,前者就能把后者装起来,称之为一组文件袋。 50 | 请计算,最大的一组文件袋的数量是多少。 51 | 实例 52 | 输入:[[6,10],[11,14],[6,1],[16,14],[13,2]] 53 | 输出: 3 54 | 55 | 56 | 57 | 58 | 59 | --- 60 | 61 | -------------------------------------------------------------------------------- /01_Algo/02_forGreat/NormalGreat/46节.md: -------------------------------------------------------------------------------- 1 | # 压缩数组 2 | 3 | ## 给一个矩阵,子矩形内<=k,并最接近K的画法 4 | 5 | 6 | 7 | ![image-20211014202743171](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211014202743171.png) 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /01_Algo/02_forGreat/NormalGreat/last.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ![](https://raw.githubusercontent.com/handsomeyi/Pics/master/%E8%85%BE%E8%AE%AF%E8%AF%BE%E5%A0%82%E5%9B%BE%E7%89%8720211104204942.png) 8 | 9 | 用固定窗口 10 | 11 | 或者用双指针 12 | 13 | 14 | 15 | ![image-20211104213107031](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211104213107031.png) 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /01_Algo/03_LeetCodeTop/666.md: -------------------------------------------------------------------------------- 1 | #### [825. 适龄的朋友](https://leetcode-cn.com/problems/friends-of-appropriate-ages/) 2 | 3 | 在社交媒体网站上有 n 个用户。给你一个整数数组 ages ,其中 ages[i] 是第 i 个用户的年龄。 4 | 5 | 如果下述任意一个条件为真,那么用户 x 将不会向用户 y(x != y)发送好友请求: 6 | 7 | age[y] <= 0.5 * age[x] + 7 8 | age[y] > age[x] 9 | age[y] > 100 && age[x] < 100 10 | 否则,x 将会向 y 发送一条好友请求。 11 | 12 | 注意,如果 x 向 y 发送一条好友请求,y 不必也向 x 发送一条好友请求。另外,用户不会向自己发送好友请求。 13 | 14 | 返回在该社交媒体网站上产生的好友请求总数。 15 | 16 | 17 | 18 | 桶排序 19 | 20 | ```java 21 | int N = 130; 22 | public int numFriendRequests(int[] ages) { 23 | int[] nums = new int[N]; 24 | for (int i : ages) nums[i]++; 25 | for (int i = 1; i < N; i++) nums[i] += nums[i - 1]; 26 | int ans = 0; 27 | for (int y = 1, x = 1; y < N; y++) { 28 | int a = nums[y] - nums[y - 1]; // 有 a 个 y 29 | if (a == 0) continue; 30 | if (x < y) x = y; 31 | while (x < N && check(x, y)) x++; 32 | int b = nums[x - 1] - nums[y - 1] - 1; // [y, x) 为合法的 x 范围,对于每个 y 而言,有 b 个 x 33 | if (b > 0) ans += b * a; 34 | } 35 | return ans; 36 | } 37 | boolean check(int x, int y) { 38 | if (y <= 0.5 * x + 7) return false; 39 | if (y > x) return false; 40 | if (y > 100 && x < 100) return false; 41 | return true; 42 | } 43 | ``` 44 | 45 | 46 | 47 | 48 | 49 | ```java 50 | public int numFriendRequests(int[] ages) { 51 | int[] cnt = new int[121]; 52 | for (int age : ages) { 53 | cnt[age]+; 54 | } 55 | int[] pre = new int[121]; 56 | for (int i = 1; i <= 120; i++) { 57 | pre[i] = pre[i - 1] + cnt[i]; 58 | } 59 | int ans = 0; 60 | for (int i = 15; i <= 120; i++) { 61 | if (cnt[i] > 0) { 62 | int bound = (int) (i * 0.5 + 8); 63 | ans += cnt[i] * (pre[i] - pre[bound - 1] - 1); 64 | } 65 | } 66 | return ans; 67 | } 68 | ``` 69 | 70 | 71 | 72 | 73 | # a* 迪杰斯特拉 74 | 当前代价 & 代价预估长度 结合维护小根堆 75 | 76 | 如果当前点到目标估计的距离 小于等于最优距离 -------------------------------------------------------------------------------- /01_Algo/03_LeetCodeTop/Classic2.md: -------------------------------------------------------------------------------- 1 | # 6 2 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java List --remove(int index)与remove(Object o)方法的区别.md: -------------------------------------------------------------------------------- 1 | ### Java List --remove(int index)与remove(Object o)方法的区别 2 | 3 | --- 4 | 5 | remove(int index):该方法删除位于index结点处的元素 6 | 7 | remove(Object o):删除特定Object元素 8 | 9 | 10 | 11 | 在大多数情况下,这两种方法的使用并不会存在歧义。 12 | 13 | 但是当我们将List的泛型参数设置为Integer时,如果只是传入一个参数, 只能使用前一种方式删除元素,也即是按结点位置的方法删除元素。因为无论传入的参数是int 类型还是Integer类型,都只会调用remove(int index)方法,而不会调用remove(Object o)方法。 除非调用Integer.valueOf()方法, 将数字转化为Interger对象 14 | 15 | ```java 16 | List testList = new ArrayList<>(); 17 | testList.add(1); 18 | testList.add(3); 19 | testList.add(4); 20 | testList.add(5); 21 | testList.remove(Integer.valueOf(3)); // 删除对象3 22 | //testList.remove(3); // 删除3位置的数 23 | 24 | for(int num : testList) { 25 | System.out.println(num + " "); 26 | } 27 | System.out.println(); 28 | ``` -------------------------------------------------------------------------------- /01_Algo/04_Java/Java String类中的compareTo方法.md: -------------------------------------------------------------------------------- 1 | # Java String类中的compareTo方法 2 | 3 | --- 4 | 5 | 按[[字典序]]序比较两个字符串。 6 | 7 | 1) 长度相等把字符串看成 256进制的数做比较: "abc" < "bck" 8 | 2) 长度不一样, 短的补成跟长的长度一样长, 用最低的ASCII来补, 补完之后两个再比 9 | "abc" < "b" "abcd" > "abc" 10 | 11 | 12 | ```java 13 | String str1 = "abc"; 14 | String str2 = "b"; 15 | System.out.println(str1.compareTo(str2)); 16 | ``` 17 | 18 | 19 | ```text 20 | int compareTo(Object o) 21 | int compareTo(String anotherString) 22 | 23 | 参数 24 | o -- 要比较的对象。 25 | anotherString -- 要比较的字符串。 26 | 27 | 返回值 28 | 返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符参数的第一个字符不等, 29 | 结束比较,返回他们之间的长度差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和 30 | 参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。 31 | 32 | 如果参数字符串等于此字符串,则返回值 0; 33 | 如果此字符串小于字符串参数,则返回一个小于 0 的值; 34 | 如果此字符串大于字符串参数,则返回一个大于 0 的值。 35 | 36 | ``` 37 | 38 | 39 | >该比较基于字符串中各个字符的 Unicode 值。按字典顺序将此 String 对象表示的字符序列与参数字符串所表示的字符序列进行比较。如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。如果这两个字符串相等,则结果为 0;compareTo 只在方法 equals(Object) 返回 true 时才返回 0。 40 | 这是字典排序的定义。如果这两个字符串不同,那么它们要么在某个索引处的字符不同(该索引对二者均为有效索引),要么长度不同,或者同时具备这两种情况。如果它们在一个或多个索引位置上的字符不同,假设 k 是这类索引的最小值;则在位置 k 上具有较小值的那个字符串(使用 < 运算符确定),其字典顺序在其他字符串之前。在这种情况下,compareTo 返回这两个字符串在位置 k 处两个char 值的差,即值: 41 | this.charAt(k)-anotherString.charAt(k) 42 | 如果没有字符不同的索引位置,则较短字符串的字典顺序在较长字符串之前。在这种情况下,compareTo 返回这两个字符串长度的差,即值: 43 | this.length()-anotherString.length() 44 | 45 | 46 | [[最大数]] 47 | 48 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java 中String、StringBuilder和StringBuffer的区别.md: -------------------------------------------------------------------------------- 1 | ## String、StringBuilder和StringBuffer的区别 2 | 3 | --- 4 | 5 | 6 | 区别:String为字符串常量,一旦被创建的话,就不能在改变了。 7 | 8 | StringBuilder和StringBuffer为字符串变量,创建后是可以被更改的 9 | 10 | 速度:StringBuilder>StringBuffer>String 11 | 12 | **区别:StringBuilder是线程不安全,StringBuffer是线程安全的** 13 | 14 | StringBuffer中很多带有=synchronized=关键字 15 | 16 | 17 | 18 | **String:适用于少量的字符串操作的情况** 19 | 20 | **StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况** 21 | 22 | **StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况** 23 | 24 | 25 | 26 | https://blog.csdn.net/yy_cly/article/details/87649885 -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中ArrayList 扩容的代价.md: -------------------------------------------------------------------------------- 1 | # Java中ArrayList 扩容的代价 2 | 3 | 4 | --- 5 | 6 | ArrayList 是一个可以扩容的东西, 7 | 一开始有一个初始长度, 往里加数, 加满了, 它会扩容 8 | 大小翻一倍 9 | 问: 即便算上扩容代价, 这个扩容代价均摊下来是 O(1) 10 | 底层就是一个连续的数组结构 11 | ArrayList中在index 位置插入一个number 代价是 O(n) 12 | 需要把后面所有东西整体向右挪一个位置, 腾出这个位置, 13 | 然后这个number进来, 这个是O(N)的 14 | 15 | ```text 16 | [ ] 1 17 | [ ] 2 18 | [ ] 4 19 | [ ] 8 20 | [ ] 16 21 | 22 | 如果要加入N个数, 扩容的代价是1+2+4+8+....+N 23 | 是一个等比数列优化以后是接近N的规模的 24 | 所以加入N个数扩容的总代价是O(N), 25 | 加入一个数, 这个扩容代价均摊下来是 O(1) 26 | 27 | ``` 28 | 所以我们说: 29 | 动态数组虽然有扩容, 但其实对它整体性能在时间复杂度的影响上 没有 30 | 它做到了个跟固定数组一样的好, 还支持动态扩容 31 | 动态数组有扩容, 当然会比固定数组要慢一点, 但这种慢只是常数时间的慢, 从复杂度的角度来说, 没影响! 在工程上几乎是感觉不到的. 32 | 33 | Python的List不一样, 是一颗有序树, 平衡搜索二叉树 34 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中HashMap, TreeMap 等的元素遍历顺序.md: -------------------------------------------------------------------------------- 1 | 2 | # Java中HashMap, TreeMap 等的元素遍历顺序 3 | 4 | --- 5 | 6 | 遍历HashMap里面的元素时, 得到的元素不是按照之前加入HashMap的顺序输出 7 | ```java 8 | Map hashMap = new HashMap(); 9 | 10 | for (Entry entry : hashMap.entrySet()) { 11 | MessageFormat.format("{0}={1}", entry.getKey(),entry.getValue()); 12 | } 13 | ``` 14 | 15 | 16 | **HashMap散列图、Hashtable散列表是按“有利于随机查找的散列(hash)的顺序”。并非按输入顺序。遍历时只能全部输出,而没有顺序**。甚至可以rehash()重新散列,来获得更利于随机存取的内部顺序。 17 | 18 | 总之,遍历HashMap或Hashtable时不要求顺序输出,即与顺序无关。 19 | 20 | ## 按加入时的顺序遍历 21 | 可以用**java.util.LinkedHashMap** 就是按加入时的顺序遍历了。 22 | 23 | `Map paramMap = new LinkedHashMap ();` 24 | 25 | 类似的还有 **java.util.LinkedHashSet** 26 | 27 | ## 按加入元素顺序遍历 28 | 如果想要得到的元素排序应该使用TreeMap 29 | `public TreeMap nextMap = new TreeMap<>();` 30 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中Integer包装类的的128陷阱.md: -------------------------------------------------------------------------------- 1 | # java 中Integer包装类的的128陷阱 2 | 3 | --- 4 | 5 | ## java 中Integer包装类的的128陷阱 6 | https://blog.csdn.net/qq_41644715/article/details/99981403 7 | 8 | java开发者认为 9 | 每次都要开辟新空间会占用大量的资源,因此他们规定在-128~127(因为Java设计者认为大家对数的使用大多在100以内)之间的Integer类型的变量,直接指向常量池中的缓存地址,不会new开辟出新的空间。 常量池存在于方法区,类加载时就有了。 10 | 11 | 在整数的包装类当中,在第一次创建 Integer 类的对象的时候,都会首先创建好缓存数组。当需要包装的值是在 IntegerCache 数组当中的元素的时候,就会返回数组当中的 Integer 对象。JVM 默认就会设置数组的范围为 -128 ~ 127 。除非设置 JVM 当中的 AutoBoxCacheMax 属性大小即可 12 | 13 | ``` 14 | private static class IntegerCache { 15 | // 这是 IntegerCache 初始化的最小值,注意跟虚拟机当中的属性没有任何关系。 16 | static final int low = -128; 17 | // 这是 IntegetCache 初始化的最大值,注意他是跟虚拟机的配置有直接的关系。 18 | static final int high; 19 | // 这是用来保存整数缓存池当中的元素 20 | static final Integer[] cache; 21 | static { 22 | // high value may be configured by property 23 | int h = 127; 24 | // 获得 JVM 当中设置缓存当中最大的数值。如果没有设置,JVM 默认大小即为 127。设置方式:在 JVM 的 AutoBoxCacheMax=需要设置的缓冲区保存的最大值。 25 | String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); 26 | if (integerCacheHighPropValue != null) { 27 | try { 28 | int i = parseInt(integerCacheHighPropValue); 29 | // 将从虚拟机当中获得的值与默认的最大值进行比较,筛选出里面的最大值 30 | i = Math.max(i, 127); 31 | // 取 i 与 Integer.MAX_VALUE - (-low) - 1 之间的最小值。因为这是保证缓存区的数组大小不大于 Integer.Max_Value。 32 | h = Math.min(i, Integer.MAX_VALUE - (-low) - 1); 33 | } catch (NumberFormatException nfe) { 34 | // 在 parseInt 方法当中如果转换失败,会捕捉到 NumberFormatException。对这个异常默认没有任何处理。 35 | } 36 | } 37 | high = h; 38 | // 创建缓存池当中的空间,取决于最大值与最小值之间的空间。* 39 | cache = new Integer[(high - low) + 1]; 40 | int j = low; 41 | for(int k = 0; k < cache.length; k++) 42 | cache[k] = new Integer(j++); 43 | 44 | // 最后通过断言,检查缓冲区的上界至少是 127。 45 | assert IntegerCache.high >= 127; 46 | } 47 | 48 | private IntegerCache() {} 49 | } 50 | ``` -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中split函数.md: -------------------------------------------------------------------------------- 1 | # Java中split函数 2 | 3 | #硬记 4 | 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | ```java 12 | public static void main(String\[\] args) { 13 | 14 | // "a\\b\\c" '\\' a,b,c 15 | String test \= "a\\\\b\\\\cd"; 16 | 17 | // "a\\b\\c" "\\" a,b,c 18 | String\[\] arr \= test.split("\\\\\\\\"); // \\\\\\\\ \\\\ \\ 19 | for(String str : arr) { 20 | System.out.println(str); 21 | } 22 | } 23 | 24 | ``` 25 | 26 | split函数中\\ 表示转义, 又表示正则 27 | 前两个表示为一个, 后两个表示为一个 28 | 又表示正则, 两个斜线化为一个斜线 29 | 30 | - [[打印目录结构]] -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中容器, 堆等对加入重复值的处理.md: -------------------------------------------------------------------------------- 1 | # Java中容器, 堆等对加入重复值的处理 2 | 3 | --- 4 | 5 | 6 | ## 堆里接受加入重复值 7 | 比如: Java里的 PriorityQueue 8 | 9 | 10 | ## 有序表不允许加入重复值 11 | 比如: Java里的 TreeMap 12 | 13 | 有序表如果想加入重复值, 需要在相等时把地址传入比较器 14 | 对于简单数据结果, 可以再包一层 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中的Stack结构.md: -------------------------------------------------------------------------------- 1 | # Java中的Stack结构 2 | 3 | 4 | ```java 5 | Stack stack = new Stack<>(); 6 | stack.add(1); 7 | stack.add(1); 8 | stack.add(1); 9 | while(!stack.isEmpty()) { 10 | System.out.println(stack.pop()); 11 | } 12 | ``` 13 | 14 | Java里自己的实现是比较慢的, 栈实现的东西, 如果想优化常数实际, 别用这个结构, 15 | 用LinkedList实现栈的功能: 每次都从尾部进, 从尾部出 16 | 就用双端队列替代了栈结构了, 还能快点 17 | 18 | ```java 19 | LinkedList stack = new LinkedList<>(); 20 | stack.addLast(1); 21 | stack.addLast(2); 22 | stack.addLast(3); 23 | 24 | while(!stack.isEmpty()) { 25 | System.out.println(stack.pollLast()); 26 | } 27 | ``` 28 | 29 | 30 | 如果你明确知道你数组的长度, 比如假如是100, 拿数组结构替代是最快的 31 | ```java 32 | int[] stack3 = new int[100]; 33 | int index = 0; 34 | // 加入 35 | stack3[index++] = 1; 36 | stack3[index++] = 2; 37 | stack3[index++] = 3; 38 | // 弹出 39 | System.out.println(stack3[--index]); 40 | System.out.println(stack3[--index]); 41 | System.out.println(stack3[--index]); 42 | ``` 43 | 44 | 45 | 46 | ```java 47 | 48 | System.out.println("===========Start=========="); 49 | 50 | testTime = 1000000; 51 | Stack stack4 = new Stack<>(); 52 | start = System.currentTimeMillis(); 53 | for(int i = 0; i < testTime; i++) { 54 | stack4.add(i); 55 | } 56 | while(!stack4.isEmpty()) { 57 | stack4.pop(); 58 | } 59 | end = System.currentTimeMillis(); 60 | System.out.println(end-start); 61 | 62 | 63 | int[] stack6 = new int[testTime]; 64 | start = System.currentTimeMillis(); 65 | int index = 0; 66 | for(int i = 0; i < testTime; i++) { 67 | stack6[index++] = i; 68 | } 69 | while(index != 0) { 70 | int a = stack6[--index]; 71 | } 72 | end = System.currentTimeMillis(); 73 | System.out.println(end-start); 74 | System.out.println("===========End============"); 75 | 76 | ``` 77 | 78 | 79 | ```text 80 | ===========Start========== 81 | 36 82 | 5 83 | ===========End============ 84 | ``` 85 | 86 | 用数组实现一些Java中自有的数据结构来节省常数时间 -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中的值传递跟引用传递.md: -------------------------------------------------------------------------------- 1 | # Java中的值传递跟引用传递 2 | 3 | 4 | ## 基本类型和引用类型在内存中的保存 5 | https://www.cnblogs.com/binyue/p/3862276.html 6 | https://blog.csdn.net/bjweimengshu/article/details/79799485 7 | 8 | Java中数据类型分为两大类,基本类型和对象类型。 9 | 相应的,变量也有两种类型:基本类型和引用类型。 10 | 基本类型的变量保存原始值,即它代表的值就是数值本身; 11 | 而引用类型的变量保存引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身, 12 | 对象本身存放在这个引用值所表示的地址的位置。 13 | 14 | 基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress, 15 | 引用类型包括:类类型,接口类型和数组。 16 | 17 | 相应的,变量也有两种类型:基本类型和引用类型。 18 | 19 | (1)基本数据类型传值,对形参的修改不会影响实参; 20 | (2)引用类型传引用,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象; 21 | (3)String, Integer, Double等immutable的类型特殊处理,可以理解为传值,最后的操作不会修改实参对象。 22 | 23 | ## Java方法参数中的值传递和引用传递 24 | https://www.cnblogs.com/lingyejun/p/11028808.html 25 | 26 | 在Java方法中参数列表有两种类型的参数,基本类型和引用类型。 27 | 28 | 基本类型:值存放在局部变量表中,无论如何修改只会修改当前栈帧的值,方法执行结束对方法外不会做任何改变;此时需要改变外层的变量,必须返回主动赋值。 29 | 30 | 引用数据类型:指针存放在局部变量表中,调用方法的时候,副本引用压栈,赋值仅改变副本的引用。但是如果通过操作副本引用的值,修改了引用地址的对象,此时方法以外的引用此地址对象当然被修改。(两个引用,同一个地址,任何修改行为2个引用同时生效)。 31 | 32 | 这两种类型都是将外面的参数变量拷贝一份到局部变量中,基本类型为值拷贝,引用类型就是将引用地址拷贝一份。 33 | 34 | 35 | 结论:当方法参数为基本类型时,是将外部变量值拷贝到局部变量中而进行逻辑处理的,故方法是不能修改原基本变量的。 36 | 37 | 对于引用类型的方法参数,会将外部变量的引用地址,复制一份到方法的局部变量中,两个地址指向同一个对象。所以如果通过操作副本引用的值,修改了引用地址的对象,此时方法以外的引用此地址对象也会被修改。(两个引用,同一个地址,任何修改行为2个引用同时生效)。 38 | 39 | ref: 40 | https://www.cnblogs.com/xiximayou/p/12040285.html 41 | 42 | 43 | ### [[Java中Integer包装类的的128陷阱]] 44 | 45 | 46 | ## More... 47 | 48 | [[收集达标路径和#什么时候需要考虑恢复现场]] 49 | 50 | ![[Pasted image 20210203205557.png]] 51 | 52 | ### 为什么pre = pre.next的时候, head没有动? 53 | 54 | ![[Pasted image 20210203223850.png]] 55 | 56 | 内存里建出一个Node, pre指向这块内存 57 | pre 指向一个内存区域, head也指向这块内存区域 58 | 59 | pre作为返回值(等号右边)的时候: 指的是它背后的东西 60 | 当一个引用是等号左边, 是一个赋值的时候, 表示我指向谁 61 | 后面pre自己动, Head还是指向这块内存区域, 不会动 62 | 不是Head 指向pre, 而是指向pre背后的东西 63 | 64 | 65 | 比如二叉树前序遍历 66 | ```java 67 | 68 | public static void pre(Node head) { 69 | System.out.print("pre-order: "); 70 | if (head != null) { 71 | Stack stack = new Stack(); 72 | stack.add(head); 73 | while (!stack.isEmpty()) { 74 | head = stack.pop(); 75 | System.out.print(head.value + " "); 76 | if (head.right != null) { 77 | stack.push(head.right); 78 | } 79 | if (head.left != null) { 80 | stack.push(head.left); 81 | } 82 | } 83 | } 84 | System.out.println(); 85 | } 86 | 87 | ``` 88 | 89 | 输入参数为链表头结点 90 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中的哈希表.md: -------------------------------------------------------------------------------- 1 | # Java中的哈希表/散列表 2 | 3 | --- 4 | 5 | 6 | ```text 7 | HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 8 | 1.通过keySet()获取键,再利用hashmap里面的.get(key)方法通过键获取值 9 | 2.通过Map.entry()获取键值对,可以同时利用迭代器直接遍历 10 | 3.通过父类Collection获取值 11 | 12 | Hashtable 13 | 数组+链表实现,key,value都不能为null,线程安全,实现方式是修改数据时锁住整个HashTable,效率低。 14 | 初始大小11,扩容\*2+1。 15 | 16 | HashMap 17 | 数组+链表实现,key,value都可以为null,线程不安全。 18 | 初始大小16,扩容\*2。 19 | 20 | ConcurrentHashMap 21 | 分段数组+链表实现,线程安全。用分离锁实现,允许多个修改操作并发。 22 | 跨段修改时按顺序锁定所有段,再按顺序释放。 23 | ``` 24 | 25 | 26 | Java中的哈希表分为 (内部)按值传递的哈希表 和 按 (内部)引用传递的哈希表, 区别很大 27 | 28 | String 在Java的其它地方有可能按引用传递, 但在哈希表内部是按值传递的 29 | 30 | Hashmap的 put 既是新增操作, 也是更新value的操作 31 | 32 | 哈希表不管存了多少数据, 增删改查都是常数时间, 但是这个常数时间是比较大的 33 | 它远远比数组寻址, 加减法等常数时间的操作要大的多 34 | 35 | 36 | 哈希表内部 Integer, Double, Float, String, Char全部按值传递 37 | ```java 38 | String test1 = "aaaa"; 39 | String test2 = "aaaa"; 40 | System.out.println(map.containsKey(test1)); 41 | System.out.println(map.containsKey(test2)); 42 | 43 | HashMap map2 = new HashMap<>(); 44 | map2.put(1234567, "我是1234567"); 45 | 46 | Integer a = 1234567; 47 | Integer b = 1234567; 48 | 49 | System.out.println(a == b); 50 | System.out.println(map2.containsKey(a)); 51 | System.out.println(map2.containsKey(b)); 52 | 53 | ``` 54 | 55 | 而非原生类型, 比如自己定义的, 内部按引用传递, 内存地址作为key 56 | ```java 57 | public static class Node { 58 | public int value; 59 | 60 | public Node(int v) { 61 | value = v; 62 | } 63 | } 64 | 65 | Node node1 = new Node(1); 66 | Node node2 = new Node(1); 67 | HashMap map3 = new HashMap<>(); 68 | map3.put(node1, "我进来了!"); 69 | System.out.println(map3.containsKey(node1)); 70 | System.out.println(map3.containsKey(node2)); 71 | 72 | ``` 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中的容器, 队列, 堆栈.md: -------------------------------------------------------------------------------- 1 | # Java中的容器, 队列, 堆, 栈 2 | 3 | --- 4 | 5 | 6 | 7 | ArrayList: 用作数组 8 | 9 | ## 队列 Queue 10 | 没有叫做Queue的类(它是个接口名字) 11 | Queue只是一个接口,当需要使用队列时也就首选ArrayDeque了(次选是LinkedList) 12 | 13 | LinkedList: 双向链表-->双端队列 14 | 15 | ```java 16 | LinkedList queue = new LinkedList<>(); 17 | 18 | ``` 19 | 20 | ## 栈: Stack 21 | 当需要使用栈时,Java已不推荐使用Stack,而是推荐使用更高效的ArrayDeque 22 | 23 | ```java 24 | Stack stack = new Stack<>(); 25 | 26 | 27 | Deque stack = new ArrayDeque(); 28 | ``` 29 | 30 | 31 | [[Java中的Stack结构]] 32 | 33 | 34 | 35 | ## 堆 36 | 37 | 优先队列 - PriorityQueue 38 | 如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头,字符串则按字典序排列。 39 | 如果需要大顶堆, 需要定义比较器 40 | 41 | 42 | ## String 转为 char 43 | 44 | 使用 String.toCharArray( ) 方法,将String 转化为 字符串数组。(返回值:char[] )  45 | 46 | char 转为 String: 47 | String s = String.valueOf('c'); 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中的左移, 右移操作.md: -------------------------------------------------------------------------------- 1 | # Java中的左移,右移操作 2 | 3 | --- 4 | 5 | 左移没有带符号的 6 | 右移有带符号右移跟无符号右移 7 | 8 | 一个数字左移1位, 等同于之前的数字 * 2, num<<1 等同于 num * 2 9 | 10 | 一个数字右移1位, 等同于之前的数字/ 2 11 | 12 | 右移: 13 | \>>: 带符号右移, 左侧新位置用符号位补全, 正数补0, 负数补1。 14 | \>>>: 无符号右移, 右移之后左侧新位置用0补全, 无论该数是正数还是负数 15 | 16 | 左移 <<: 17 | 左移不区分有符号和无符号, 都是左移之后右边补上0, 最左边的符号位也直接移走。 18 | 19 | 比如系统最小, 带符号右移\>>, 无符号右移\>>>是不一样的 20 | 21 | (-1) >> 1 = -1 22 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中的整型, 正数, 负数.md: -------------------------------------------------------------------------------- 1 | # Java中的整型, 正数, 负数 2 | 3 | 4 | --- 5 | 6 | Java 中提供了一个byte数据类型,并且是基本类型。 7 | 在Java中,byte类型的数据是8位带符号的二进制数。 8 | Java byte是做为最小的数字来处理的,因此它的值域被定义为-128~127,也就是signed byte 9 | https://www.cnblogs.com/zl181015/p/9435035.html 10 | ```java 11 | byte a = 127; 12 | a+=1; 13 | System.out.println(a); 14 | 15 | int b = 456; 16 | byte test = (byte) b; // 当int转成byte的时候,那么计算机会只保留最后8位 17 | System.out.println(test); // 最后会输出-56 18 | 19 | ``` 20 | 21 | 22 | Java 中整型是32位, long是64位 23 | 24 | 25 | 2147483647 26 | 27 | 整型最大值留着最高位1没有占用 28 | 29 | 一个整型系统中32为没有都用, 真正表示值的范围是从0~30位, 能表示的最大整数2^31-1 30 | 31 | 留着最高位是为了即表示整数, 又表示负数 32 | 33 | 34 | 无符号整数, C++里有 35 | 0~2^32-1 共有 2^32个数 36 | 37 | 在Java中, 是一个有符号数, 几乎一半给负的, 一半给正的 38 | - 2^31 ~ 2^32-1 共有 2^32个数 39 | 40 | 41 | ### 正数怎么表达? 42 | 如果符号位是0, 肯定不是负数, 非负数 43 | ### 负数怎么表达? 44 | 符号位是1, 值是后面的状态取反+1 45 | 46 | 比如: -1 的表示 47 | 1 1111111111111111111111111111111 48 | 49 | 符号1: 代表是负数, 50 | 后面取反, 变为0, 再加1, ==> -1 51 | 52 | 系统最小呢? 53 | 1 000000000000000000000000 54 | 符号1: 代表是负数, 55 | 后面取反, 变为1, 再加1, 后面都变0, 有一个进位是 2^31==> - 2^31 56 | 57 | 58 | ### 取反操作 59 | 60 | 符号~ 61 | ```java 62 | 63 | int b = 13213; 64 | int c = ~b; 65 | ``` 66 | 67 | 68 | - 为什么负值的极值是 1 000000000000000000000000 ? 69 | 70 | 负的最小: -2147483648 71 | 正的最大: 2147483647 72 | 73 | -2^31 ~ 2^31 - 1 74 | 75 | 整数的表达, 76 | 最高位0, 剩下的31位表示数值, 所以整数 2^31-1, 非负因为有0, 所以少了1个 77 | 78 | 负数最小: 79 | 1 000000000000000000000000 80 | 忽略1 取反后 都是1, 再+1, 到了 2^31, 没有0的负担 81 | 82 | 系统最小的绝对值, 比 系统最大的绝对值 多1 83 | 84 | 85 | ### 负数为什么要取反+1 86 | 87 | 加减乘除在系统底层内部都是二进制位运算来实现的 88 | 89 | a + b ==> 用位运算逻辑拼 90 | 如果 a是负数, 不想专门定制 91 | 只想加减乘除在系统底层内部实现是一套, 为了在实际执行的时候能够快一些 92 | 越底层应该越高效 93 | 94 | ### 怎么求一个数的相反数? 95 | 96 | int c = 5; 97 | int d = -c; 98 | 99 | ==还能怎么写? 100 | d = (~c + 1)== 101 | 102 | ### 最小的负数的相反数? 103 | 正数的相反数都有负数对应 104 | ==最小的负数没有正数对应, 取反+1 还是它自己== 105 | 106 | 如果真的需要, 用long类型! 107 | 108 | 负数取反, 符号位算不算都无关, 因为符号位1会变为0, 不影响 109 | 110 | ### 0 的相反数是谁? 111 | 溢出了, 不要, 还是它自己 112 | 113 | -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中的有序表.md: -------------------------------------------------------------------------------- 1 | # Java中的有序表 2 | --- 3 | 4 | TreeMap 5 | TreeSet 6 | 7 | 有序表是一种规范, 它是接口名 8 | key: 有序组织 9 | 增删改查: O(logN) 10 | 任何一个数据结构满足这些, 都是有序表 11 | AVL, SB树, 红黑树 都可以实现有序表, 性能指标几乎无差别 12 | 跳表也是 13 | 14 | 15 | java中有序表是红黑树 16 | 有序表可以被:红黑树、avl树、跳表、size-balanced-tree(SB树)实现 17 | 不同的实现有什么区别:在使用层次上和性能上看,没区别。只有常数时间的区别。 18 | 所有接口的性能O(logN) 19 | 设计细节:扩展班最后一节 20 | 21 | 22 | 有序表和哈希表的区别: 23 | 哈希表的所有功能有序表一定有,哈希表的key(散乱组织,哈希函数) 24 | 有序表所有的key有序组织,比哈希表的功能多。 25 | 哈希表所有操作,使用时认为时间复杂度O(1),有序表所有接口的性能O(logN) 26 | 27 | 28 | ```java 29 | TreeMap map = new TreeMap<>(); 30 | map.put(7, "我是7"); // (key,value) 所有的key按顺序组织 31 | map.put(3, "我是3"); 32 | map.put(9, "我是9"); 33 | map.put(2, "我是2"); 34 | map.put(8, "我是8"); 35 | map.put(5, "我是5"); 36 | 37 | map.put(5, "我还是5");// (5, 我还是5) 38 | 39 | 40 | System.out.println(map.containsKey(2)); 41 | System.out.println(map.get(7)); 42 | map.remove(9); 43 | 44 | 45 | 46 | 47 | System.out.println(map.firstKey()); 48 | System.out.println(map.lastKey()); 49 | // <= num 离num最近的key 50 | System.out.println(map.floorKey(6)); 51 | System.out.println(map.floorKey(-2)); 52 | // >= num 离num最近的东西 53 | System.out.println(map.ceilingKey(6)); 54 | System.out.println(map.ceilingKey(100)); 55 | 56 | // 时间复杂度全是O(logN)级别 57 | 58 | 59 | ``` 60 | 61 | 如果是Node类型, 有序表不能直接用, 因为要求key一定是要求可以比较的东西, 需要在构造参数里传递比较器 62 | 63 | ```java 64 | Node node3 = new Node(3); 65 | Node node4 = new Node(4); 66 | TreeMap treeMap2 = new TreeMap<>(); 67 | treeMap2.put(node3, "我是node3"); 68 | treeMap2.put(node4, "我是node4"); 69 | ``` -------------------------------------------------------------------------------- /01_Algo/04_Java/Java中计时用System.nanoTime().md: -------------------------------------------------------------------------------- 1 | bx_java 2 | 3 | https://www.cnblogs.com/_programmer/p/3396933.html 4 | 关于时间的帖子, **计时用System.nanoTime() ,计算程序运行的时间** 5 | 如果用 System.currentTimeMillis可能会导致很多时间都一样 -------------------------------------------------------------------------------- /01_Algo/04_Java/为什么Java中String类indexof不使用KMP.md: -------------------------------------------------------------------------------- 1 | # 为什么Java中String类indexof不使用KMP 2 | 3 | 4 | --- 5 | 6 | JDK的String类中的indexof方法的实现,仅仅只是用了暴力破解法,也就是最原始的实现,时间复杂度也到了O(n * m)。 7 | 8 | JDK的编写者们认为大多数情况下,字符串都不长,使用原始实现可能代价更低。因为KMP和Boyer-Moore算法都需要预先计算处理来获得辅助数组,需要一定的时间和空间,这可能在短字符串查找中相比较原始实现耗费更大的代价。而且一般大字符串查找时,程序员们也会使用其它特定的数据结构,查找起来更简单。这有点类似于排除特定情况下的快速排序了。不同环境选择不同算法。 9 | 10 | 11 | ```java 12 | /** 13 | * Code shared by String and StringBuffer to do searches. The 14 | * source is the character array being searched, and the target 15 | * is the string being searched for. 16 | * 17 | * @param source the characters being searched. 18 | * @param sourceOffset offset of the source string. 19 | * @param sourceCount count of the source string. 20 | * @param target the characters being searched for. 21 | * @param targetOffset offset of the target string. 22 | * @param targetCount count of the target string. 23 | * @param fromIndex the index to begin searching from. 24 | */ 25 | static int indexOf(char[] source, int sourceOffset, int sourceCount, 26 | char[] target, int targetOffset, int targetCount, 27 | int fromIndex) { 28 | if (fromIndex >= sourceCount) { 29 | return (targetCount == 0 ? sourceCount : -1); 30 | } 31 | if (fromIndex < 0) { 32 | fromIndex = 0; 33 | } 34 | if (targetCount == 0) { 35 | return fromIndex; 36 | } 37 | 38 | char first = target[targetOffset]; 39 | int max = sourceOffset + (sourceCount - targetCount); 40 | 41 | for (int i = sourceOffset + fromIndex; i <= max; i++) { 42 | /* Look for first character. */ 43 | if (source[i] != first) { 44 | while (++i <= max && source[i] != first); 45 | } 46 | 47 | /* Found first character, now look at the rest of v2 */ 48 | if (i <= max) { 49 | int j = i + 1; 50 | int end = j + targetCount - 1; 51 | for (int k = targetOffset + 1; j < end && source[j] 52 | == target[k]; j++, k++); 53 | 54 | if (j == end) { 55 | /* Found whole string. */ 56 | return i - sourceOffset; 57 | } 58 | } 59 | } 60 | return -1; 61 | } 62 | 63 | 64 | ``` 65 | 66 | http://stackoverflow.com/questions/19543547/why-jdks-string-indexof-does-not-use-kmp/ 67 | 68 | -------------------------------------------------------------------------------- /01_Algo/04_Java/程序中的随机函数.md: -------------------------------------------------------------------------------- 1 | # 程序中的随机函数 2 | 3 | 4 | --- 5 | 6 | ## Java 7 | 8 | ```java 9 | Math.random() [0,1) 等概率 10 | Math.random() * N [0,N) 11 | (int)(Math.random() * N) [0, N-1] 12 | (int)(Math.random() * (N +1)) [0, N] 13 | 14 | Random 对象的 nextInt(int) 方法: 生成 0 ~ 参数之间随机取值的整数 15 | Random rand = newRandom(); 16 | rand.nextInt(100); // [0, 100) 17 | rand.nextInt(100)+ 1; // [0, 100] 18 | rand.nextInt(90) + 10; // 随机两位整数 19 | rand.nextInt(900)+ 100; //随机三位整数 20 | rand.nextInt(MAX - MIN + 1) + MIN; // 一个MIN~MAX范围内的随机数 21 | ``` 22 | 23 | ```java 24 | // Math.random() -> double -> [0,1) 25 | int testTimes = 10000000; 26 | int count = 0; 27 | for (int i = 0; i < testTimes; i++) { 28 | if (Math.random() < 0.75) { 29 | count++; 30 | } 31 | } 32 | System.out.println((double) count / (double) testTimes); 33 | 34 | System.out.println("========="); 35 | 36 | // [0,1) -> [0,8) 37 | count = 0; 38 | for (int i = 0; i < testTimes; i++) { 39 | if (Math.random() * 8 < 5) { 40 | count++; 41 | } 42 | } 43 | System.out.println((double) count / (double) testTimes); 44 | System.out.println((double) 5 / (double) 8); 45 | 46 | int K = 9; 47 | // [0,K) -> [0,8] 48 | 49 | int[] counts = new int[9]; 50 | for (int i = 0; i < testTimes; i++) { 51 | int ans = (int) (Math.random() * K); // [0,K-1] 52 | counts[ans]++; 53 | } 54 | for (int i = 0; i < K; i++) { 55 | System.out.println(i + "这个数,出现了 " + counts[i] + " 次"); 56 | } 57 | ``` 58 | 59 | ## C/C++ 60 | 61 | https://www.cnblogs.com/xiaodingmu/p/8061200.html 62 | 63 | ```cpp 64 | rand() 无参数 65 | 它会返回一个从0到最大随机数的任意整数,最大随机数的大小通常是固定的一个大整数。 66 | 67 | 1) 给srand()提供一个种子,它是一个unsigned int类型; 68 | 2) 调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到RAND_MAX之间); 69 | 3) 根据需要多次调用rand(),从而不间断地得到新的随机数; 70 | 4) 无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的 71 | 输出结果。 72 | 73 | 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 74 | 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 75 | 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 76 | 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 77 | 78 | 要取得a到b之间的随机整数,另一种表示:a + (int)b * rand() / (RAND_MAX + 1)。 79 | 要取得0~1之间的浮点数,可以使用rand() / double(RAND_MAX)。 80 | 81 | 要取得随机到十分位的10个随机小数,则可以先取得0~10的整数,然后均除以10。 82 | 要取得随机到百分位的随机小数,则需要先得到0~100的10个整数,然后均除以100。 83 | 84 | int num = rand() % 100; // 0~99 85 | int num = rand() % 100 + 1; // 1~100 86 | ``` 87 | 88 | 89 | ## Python 90 | https://www.cnblogs.com/happyfool/p/8621880.html 91 | 92 | ```python 93 | random() 方法返回随机生成的一个实数,它在[0,1)范围内 94 | random.randint() 函数,返回指定范围的一个随机整数,包含上下限 95 | ``` 96 | 97 | 98 | -------------------------------------------------------------------------------- /01_Algo/05_Math/判断一个数是不是质数.md: -------------------------------------------------------------------------------- 1 | # 判断一个数是不是质数 2 | 3 | --- 4 | 5 | 判断一个数是不是质数, 精确得到一个数V所有的质数因子, O(V) 6 | 找到一个数所有的因子, 哪怕有冗余, 就用根号V的方法, 一次找一对 7 | 8 | 9 | ```java 10 | for (int i = 0; i < arr.length; i++) { 11 | int num = arr[i]; 12 | int limit = (int) Math.sqrt(num); // 1 ~ 根号num 13 | for (int j = 1; j <= limit; j++) { // j是现在试的因子 14 | if (num % j == 0) { // num含有j的因子 15 | if (j != 1) { // 这个因子不是1 16 | // j 17 | if (!fatorsMap.containsKey(j)) { // 当前数是含有j因子的第一个数 18 | fatorsMap.put(j, i); 19 | } else { 20 | unionFind.union(fatorsMap.get(j), i); 21 | } 22 | } 23 | int other = num / j; // other * j == num 24 | if (other != 1) { // num含有other的因子 25 | if (!fatorsMap.containsKey(other)) { 26 | fatorsMap.put(other, i); 27 | } else { 28 | unionFind.union(fatorsMap.get(other), i); 29 | } 30 | } 31 | } 32 | } 33 | } 34 | ``` -------------------------------------------------------------------------------- /01_Algo/05_Math/剃刀函数与约瑟夫环.md: -------------------------------------------------------------------------------- 1 | # 剃刀函数与约瑟夫环 2 | #约瑟夫环 3 | 4 | --- 5 | 坐标系移动原则: 6 | 左加右减, 上加下减 7 | 8 | 9 | ## 剃刀函数 10 | y = x % i 11 | i: 长度 12 | ![[Pasted image 20210115021332.png]] 13 | 14 | 15 | 编号 = (报数-1) % i + 1 16 | ![[Pasted image 20210115022303.png]] 17 | 18 | 旧 = (新 + S -1) % i + 1 19 | 20 | 21 | ### 局部放大图 22 | ![[Pasted image 20210115024734.png]] 23 | S是被杀死的 24 | 25 | S = (m - 1) % i + 1 26 | 27 | ==> 28 | 旧 = (新 + (m - 1) % i + 1 -1) % i + 1 29 | ==> 30 | 旧 = (新 + m - 1) % i + 1 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /01_Algo/05_Math/卡特兰数.md: -------------------------------------------------------------------------------- 1 | # 卡特兰数 2 | 3 | 4 | #卡特兰数 5 | 6 | --- 7 | 8 | 卡特兰数又称卡塔兰数,英文名Catalan number,是组合数学中一个常出现于各种计数问题中的数列。以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名,其前几项为(从第零项开始) : 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ... 9 | 10 | 11 | $k(0) = 1, k(1) = 1$时,如果接下来的项满足: 12 | 13 | $\large \begin{cases} 14 | k(n) = k(0) * k(n - 1) + k(1) * k(n - 2) + ... + k(n - 2) * k(1) + k(n - 1) * k(0) & (1)\\ 15 | or \\ 16 | k(n) = c_{2n}^n - c_{2n}^{n-1} & (2)\\ 17 | or \\ 18 | k(n) = \frac{c_{2n}^n}{n+1} & (3) 19 | \end{cases}$ 20 | 21 | 就说这个表达式,满足卡特兰数,常用的是范式(1)和(2),(3)几乎不会使用到 22 | 23 | 24 | 25 | 26 | 实例: 27 | 28 | - [[二叉树N个节点无差别能形成多少种不同的结构]] 29 | - [[N个0,N个1自由组合满足条件达标结果数]] 30 | >任何前缀上0的数量不少于1这个问题: 31 | 每年面试中以各种形式出现 32 | 很多题目可以转化为这个问题处理 33 | 34 | - 栈, 可以随时进数字, 可能能进出栈组合 35 | 36 | - 股票涨法, X轴以下无效 37 | 38 | 39 | 40 | 41 | 42 | ## 卡特兰数的算法原型在做题时怎么发现? 43 | 44 | 1)如果像题目一一样,这个太明显了,你一定能发现 45 | 46 | 2)看看题目是不是类题目二问题,比如: 47 | 48 | 人员站队问题 49 | 出栈入栈问题 50 | 51 | 需要敏感度! 52 | -------------------------------------------------------------------------------- /01_Algo/05_Math/四平方和定理.md: -------------------------------------------------------------------------------- 1 | # 四平方和定理 2 | 3 | --- 4 | 5 | 任何一个自然数,你拆平方数的项不会超过四项。 6 | 任何数消掉4的因子,结论不变 7 | 8 | ```java 9 | // 1 : 1, 4, 9, 16, 25, 36, ... 10 | // 4 : 7, 15, 23, 28, 31, 39, 47, 55, 60, 63, 71, ... 11 | // 规律解 12 | // 规律一:个数不超过4 13 | // 规律二:出现1个的时候,显而易见 14 | // 规律三:任何数 % 8 == 7,一定是4个 15 | // 规律四:任何数消去4的因子之后,剩下rest,rest % 8 == 7,一定是4个 16 | public static int numSquares2(int n) { 17 | int rest = n; 18 | while (rest % 4 == 0) { 19 | rest /= 4; 20 | } 21 | if (rest % 8 == 7) { 22 | return 4; 23 | } 24 | int f = (int) Math.sqrt(n); 25 | if (f * f == n) { 26 | return 1; 27 | } 28 | for (int first = 1; first * first <= n; first++) { 29 | int second = (int) Math.sqrt(n - first * first); 30 | if (first * first + second * second == n) { 31 | return 2; 32 | } 33 | } 34 | return 3; 35 | } 36 | ``` -------------------------------------------------------------------------------- /01_Algo/05_Math/排列组合.md: -------------------------------------------------------------------------------- 1 | # 排列组合 2 | 3 | --- 4 | 5 | 链接:https://www.jianshu.com/p/c2b89e716637 6 | 7 | 8 | 9 | 10 | ### **排列** 11 | 12 | **定义**: 从n个不同元素中,任取m(m≤n,m与n均为自然数,下同)个元素按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列;从n个不同元素中取出m(m≤n)个元素的所有排列的个数,叫做从n个不同元素中取出m个元素的排列数,用符号 A(n,m)表示 13 | 14 | ![img](https:////upload-images.jianshu.io/upload_images/3003216-5d5236bdf05c8d12.png?imageMogr2/auto-orient/strip|imageView2/2/w/888/format/webp) 15 | 16 | 17 | 18 | 19 | 20 | ### **组合** 21 | 22 | **定义**: 从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。用符号 C(n,m) 表示。 23 | 24 | ![img](https:////upload-images.jianshu.io/upload_images/3003216-e5525b66ba2cc5ae.png?imageMogr2/auto-orient/strip|imageView2/2/w/832/format/webp) 25 | 26 | 27 | 28 | ### 排列与组合的区别 29 | 30 | **排列就是先组合再排序** 31 | 32 | 举个例子, 从26个字母中选5个 : 33 | - 排列的话就是A(26,5)表示的是从26个字母中选5个排成一列 也就是说ABCDE与ACBDE与ADBCE等这些是不一样的 34 | - 组合的话就是C(26,5)表示的是从26个字母中选5个没有顺序 也就是说ABCDE与ACBDE与ADBCE等这些是一样的 35 | -------------------------------------------------------------------------------- /01_Algo/05_Math/排序算法的传递性.md: -------------------------------------------------------------------------------- 1 | # 排序算法的传递性 2 | 3 | 4 | --- 5 | 6 | 如果有{甲, 乙, 丙} 三个对象, 规定: 7 | 甲>乙 8 | 乙>丙 9 | 丙>甲 10 | 则: 这种排序没有传递性 11 | 12 | 13 | ## 排序算法的传递性证明 14 | 15 | 如果 16 | $\Large a.b <= b.a$, 则$\Large a$放在前面, 否则 $\Large b$放在前面 17 | 18 | 则, 根据排序算法的传递性: 19 | 数组中的元素根据字典序排序以后: 20 | [..前.........后.....] 21 | 任何两个元素结合, 则应该有: 22 | 前.后 <= 后.前 23 | 24 | ### 证明 25 | 已知: 26 | $\Large \begin{cases} 27 | a.b <= b.a \\ 28 | b.c <= c.b 29 | \end{cases}$ 30 | 31 | 求证: 32 | $\Large a.c <= c.a$ 33 | 34 | 字符串"ks" 拼接 字符串 "te" 组成新串 "kste", 我们认为字符串就是K进制的正数, "kste"是一个四位数, "ks"占据高位, "te"占据低位, 则: 35 | $\Large kste = ks * 26^2 + te$ 36 | 37 | 认为是k进制数: 38 | $\Large a* b = a * k^{b的长度} + b$ 39 | 40 | 定义: 41 | $\Large k^{x长度} = m(x)$, 则: 42 | 43 | $\Large \begin{cases} 44 | a.b <= b.a \Rightarrow a*m(b) + b <= b* m(a) + a & ① \\ 45 | b.c <= c.b \Rightarrow b*m(c) + c <= c* m(b) + b & ② 46 | \end{cases}$ 47 | 48 | ①两边同时 $\Large-b$ 再 $\Large*c$则有: 49 | 50 | $\Large a* m(b)* c <= b* m(a) *c + a*c -bc$ 51 | 52 | ②两边同时 $\Large-b$ 再 $\Large*a$则有: 53 | 54 | $\Large b* m(c)* a + c*a - b*a<= c* m(b) *a$ 55 | 56 |
57 | 58 | 由上面两式, 可以得到: 59 | $\Large b* m(a) *c + a*c -b*c >= b* m(c)* a + c*a - b*a$ 60 | 61 | $\Large \Rightarrow$ 62 | 63 | $\Large b* m(a) *c -b*c >= b * m(c)* a - b*a$ 64 | 65 | 66 | $\Large \Rightarrow$ 67 | 68 | $\Large m(a) *c -c >= m(c)* a - a$ 69 | 70 | $\Large \Rightarrow$ 71 | 72 | $\Large a.c <= c.a$ 73 | 74 | ### 任何一个在前的跟一个在后的交换不会得到更大的字典序 75 | 76 | $\Large [... a ... b...]$ 77 | 78 | 假设$\Large a,b$中有两个字符串, $\Large m_1, m_2$, 即: 79 | 80 | $\Large [... a m_1 m_2 b...]$,则任何一个在前的$\Large a$,跟在后的$\Large b$交换顺序, 拼起来的新的字符串的字典序一定比之前要大 81 | 82 | $\Large [... b m_1 m_2 a...]$ 83 | 84 | #### 证明 85 | 原序列中顺序为$\Large am_1 m_2 b$, 我们把$\Large a$跟$\Large m_1$交换顺序得到: 86 | 87 | $\Large [... m_1 a m_2 b...]$ 88 | 89 | 即: $\Large a.m_1 <= m_1.a$, 后面没有改变,则有: 90 | 91 | $\Large [... m_1 a m_2 b...] >= [... a m_1 m_2 b...]$ 92 | 我们继续交换$\Large a$跟$\Large m_2$, 93 | 94 | 即: $\Large a.m_2 <= m_2.a$ 95 | 则有: 96 | 97 | $\Large [... m_1 m_2 a b...] >= [... m_1 a m_2 b...]$ 98 | 我们继续交换$\Large a$跟$\Large b$, 99 | 则有: 100 | 101 | $\Large [... m_1 m_2 b a...] >= [... m_1 m_2 a b...]$ 102 | 103 | 将b跟$\Large m_2, m_1$依次交换, 由上相同可得: 104 | 105 | $\Large [... b m_1 m_2 a...] >= [... a m_1 m_2 b...]$ 106 | 107 | 108 | ## 题目 109 | 110 | [[最大数]] -------------------------------------------------------------------------------- /01_Algo/05_Math/数和矩阵的N次方怎么算最快.md: -------------------------------------------------------------------------------- 1 | # 数和矩阵的N次方怎么算最快? 2 | 3 | --- 4 | 5 | 比如$10^{75}$次方怎么算最快? 6 | 7 | 75 的 二进制 = 1001011 8 | ![[Pasted image 20210129214414.png|800]] 9 | t 跟自己玩一遍的过程中, 按位决定乘不乘到结果里去: 1+ 2 + 8 + 64 =75 10 | t 最多跟自己玩 $logN$次, 需要的时候乘进来, 不需要的时候不乘, 最多再来一个$logN$次 11 | 代价最多就是$2logN$次, 所以复杂度就是$O(logN)$ 12 | 13 | 这个思想的基础是二分 14 | ![[Pasted image 20210129220327.png]] 15 | 16 | 二进制是最优良的二分方式 17 | 自己去二分, 没有二进制去做好 18 | 19 | ## 矩阵|a|的N次方的求解 20 | 21 | ![[Pasted image 20210129221624.png]] -------------------------------------------------------------------------------- /01_Algo/05_Math/斐波那契数列.md: -------------------------------------------------------------------------------- 1 | # 斐波那契数列 Fibonacci sequence 2 | #递归 3 | 4 | --- 5 | 6 | ## 定义 7 | 8 | >斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、…… 9 | >在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*) 10 | 11 | 12 | 13 | ## 代码 14 | 15 | ```java 16 | public static int f1(int n) { 17 | if (n < 1) { 18 | return 0; 19 | } 20 | if (n == 1 || n == 2) { 21 | return 1; 22 | } 23 | return f1(n - 1) + f1(n - 2); 24 | } 25 | 26 | public static int f2(int n) { 27 | if (n < 1) { 28 | return 0; 29 | } 30 | if (n == 1 || n == 2) { 31 | return 1; 32 | } 33 | int res = 1; 34 | int pre = 1; 35 | int tmp = 0; 36 | for (int i = 3; i <= n; i++) { 37 | tmp = res; 38 | res = res + pre; 39 | pre = tmp; 40 | } 41 | return res; 42 | } 43 | 44 | public static int f3(int n) { 45 | if (n < 1) { 46 | return 0; 47 | } 48 | if (n == 1 || n == 2) { 49 | return 1; 50 | } 51 | // [ 1 ,1 ] 52 | // [ 1, 0 ] 53 | int[][] base = { 54 | { 1, 1 }, 55 | { 1, 0 } 56 | }; 57 | int[][] res = matrixPower(base, n - 2); 58 | return res[0][0] + res[1][0]; 59 | } 60 | 61 | public static int[][] matrixPower(int[][] m, int p) { 62 | int[][] res = new int[m.length][m[0].length]; 63 | for (int i = 0; i < res.length; i++) { 64 | res[i][i] = 1; 65 | } 66 | 67 | // res = 矩阵中的1 68 | int[][] tmp = m;// 矩阵1次方 69 | for (; p != 0; p >>= 1) { 70 | if ((p & 1) != 0) { 71 | res = muliMatrix(res, tmp); 72 | } 73 | tmp = muliMatrix(tmp, tmp); 74 | } 75 | return res; 76 | } 77 | 78 | 79 | ``` -------------------------------------------------------------------------------- /01_Algo/05_Math/最大公约数 & 最小公倍数.md: -------------------------------------------------------------------------------- 1 | # 最大公约数最小公倍数 2 | 3 | --- 4 | 5 | 没有最小公约数,只有最小公倍数。 6 | 定义: 7 | GCD:最大公约数(Greatest Common Divisor)。指两个或多个整数共有约数中最大的一个。也称最大公因数,最大公因子 8 | 9 | LCM:最小公倍数(Least Common Multiple)。两个或多个整数公有的倍数叫做它们的公倍数,其中除0以外最小的一个公倍数就叫做这几个整数的最小公倍数。 10 | 11 | 最小公倍数与最大公约数的关系: 12 | ```text 13 | LCM(A,B)×GCD(A,B)=A×B 14 | 15 | 其中LCM是最小公倍数,GCD是最大公约数。 16 | ``` 17 | 18 | 19 | ```java 20 | // 求最小公倍数 21 | public static int lcm(int m, int n) { 22 | return m * n / gcd(m, n); 23 | 24 | } 25 | ``` 26 | 27 | ```java 28 | // 求最大公约数 -- 辗转相除法 29 | public static int gcd(int m, int n) { 30 | return n == 0 ? m : gcd(n, m%n); 31 | } 32 | 33 | ``` 34 | 35 | - [[求最大公约数]] -------------------------------------------------------------------------------- /01_Algo/05_Math/求一个数以2为底的对数.md: -------------------------------------------------------------------------------- 1 | # 求一个数以2为底的对数 2 | 3 | --- 4 | 5 | 6 | 7 | ```java 8 | public static int log2N(int n) { 9 | int res = -1; 10 | while (n != 0) { 11 | res++; 12 | n >>>= 1; 13 | } 14 | return res; 15 | } 16 | ``` -------------------------------------------------------------------------------- /01_Algo/05_Math/求最大公约数.md: -------------------------------------------------------------------------------- 1 | 2 | # 辗转相除法求最大公约数(背诵!) 3 | #硬记 4 | 5 | --- 6 | 7 | 如果整数不是特别大的话, 可以认为复杂度是O(1) 8 | 9 | ```java 10 | public static int gcd(int m, int n) { 11 | return n == 0 ? m : gcd(n, m % n); 12 | } 13 | ``` -------------------------------------------------------------------------------- /01_Algo/05_Math/泰勒公式.md: -------------------------------------------------------------------------------- 1 | # 泰勒公式 2 | 3 | --- 4 | 5 | 泰勒公式是一个用函数在某点的信息描述其附近取值的公式。如果函数满足一定的条件,泰勒公式可以用函数在某一点的各阶导数值做系数构建一个多项式来近似表达这个函数。 6 | 7 | 任何一个函数, 都可以在其定义域中的任何一点$x_0$展开为以下形式: 8 | 9 | $\Large f(x) = f(x_0) + f'(x_0)(x-x_0) + \frac{1}{2}f''(x_0)(x-x_0)^2 + ... + \frac{f^n(x_0)}{n!}(x-x_0)^2$ 10 | 11 | 可以用来[[牛顿迭代法开根号]] 12 | 13 | 14 | 15 | ## 二项式的展开式 16 | 17 | 可以将(a+b)的任意此幂展开为和的形式: 18 | 19 | $\large (a+b)^n = C_n^0a^nb^0 + C_n^1a^{n-1}b^1 + C_n^2a^{n-2}b^2\ +\ ......\ +\ C_n^{n-1}a^1b^{n-1} + C_n^na^0b^n$ 20 | 21 | 通项公式: 22 | $\large T(K+1) = C_n^Ka^{n-k}b^k$ 23 | 24 | 25 | 由此可见,二项式的展开式一共有n+1项。 26 | 期中,二项式的系数(C(0,n), C(1,n).....C(n,n), )符合杨辉三角第n层的展示。 27 | ![[Pasted image 20210202224957.png]] 28 | 29 | -------------------------------------------------------------------------------- /01_Algo/05_Math/科学计数法.md: -------------------------------------------------------------------------------- 1 | # 科学计数法 2 | 3 | --- 4 | 5 | 1G内存: 1000 M \* 1000K \* 1000 = 1 \* 10^9 6 | 1亿个数: 1 \* 10^8 7 | 8 | int 无符号最大大概是42亿多 9 | int 有符号最大2147483647, 21亿多 10 | 11 | 市面上卖的硬盘空间计算大小是按1000来的, 而计算机里是1024来的 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /01_Algo/05_Math/稀疏矩阵及矩阵乘法.md: -------------------------------------------------------------------------------- 1 | # 稀疏矩阵 Sparse Matrix 2 | 3 | --- 4 | 5 | ## 定义 6 | 7 | >在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵;与之相反,若非0元素数目占大多数时,则称该矩阵为稠密矩阵。定义非零元素的总数比上矩阵所有元素的总数为矩阵的稠密度。 8 | 9 | 稀疏矩阵的乘法 10 | ![[Pasted image 20210131210545.png]] 11 | 计算规则是,第一个矩阵第一行的每个数字(2和1),各自乘以第二个矩阵第一列对应位置的数字(1和1),然后将乘积相加( 2 x 1 + 1 x 1),得到结果矩阵左上角的那个值3。 12 | 也就是说,结果矩阵第m行与第n列交叉位置的那个值,等于第一个矩阵第m行与第二个矩阵第n列,对应位置的每个值的乘积之和。 13 | 14 | https://www.cnblogs.com/alantu2018/p/8528299.html 15 | 16 | ## 代码 17 | https://www.lintcode.com/problem/sparse-matrix-multiplication/ 18 | 19 | ```java 20 | /** 21 | * @param A: a sparse matrix 22 | * @param B: a sparse matrix 23 | * @return: the result of A * B 24 | */ 25 | public int[][] muliMatrix(int[][] A, int[][] B) { 26 | // write your code here 27 | int[][] res = new int[A.length][B[0].length]; 28 | for (int i = 0; i < A.length; i++) { 29 | for (int j = 0; j < B[0].length; j++) { 30 | for (int k = 0; k < B.length; k++) { 31 | res[i][j] += A[i][k] * B[k][j]; 32 | } 33 | } 34 | } 35 | return res; 36 | } 37 | 38 | // 矩阵的n次方 39 | public static int[][] matrixPower(int[][] m, int p) { 40 | int[][] res = new int[m.length][m[0].length]; 41 | for (int i = 0; i < res.length; i++) { 42 | res[i][i] = 1; 43 | } 44 | 45 | // res = 矩阵中的1 46 | int[][] tmp = m;// 矩阵1次方 47 | for (; p != 0; p >>= 1) { 48 | if ((p & 1) != 0) { 49 | res = muliMatrix(res, tmp); 50 | } 51 | tmp = muliMatrix(tmp, tmp); 52 | } 53 | return res; 54 | } 55 | ``` 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /01_Algo/05_Math/素数筛法.md: -------------------------------------------------------------------------------- 1 | # 素数筛法 2 | 3 | --- 4 | 5 | ## 埃氏筛法 6 | 操作:先把所有整数列出来,然后把2的倍数全部剔除,然后是三的,以此类推,遍历所有素数,把倍数全部划去。 7 | 对于每个数字i,如果没被划去,它一定是素数,因为它不是任何2到i-1数字的倍数。然后就开始划它的倍数就好。 8 | 9 | 10 | ref: [[计数质数]] 11 | -------------------------------------------------------------------------------- /01_Algo/05_Math/约瑟夫环.md: -------------------------------------------------------------------------------- 1 | # 约瑟夫环 2 | 3 | #约瑟夫环 4 | 5 | 6 | --- 7 | 8 | 最后情况 9 | 只剩一个节点 10 | 这一个节点在自己的小链表中的编号就是1号节点 11 | 当只剩1个节点的时候, 活着的编号就是1 12 | 假设我有一个公式, 可以算出杀之前的编号 13 | 那最后一个节点1, 调一下这个函数, 就能算出2个节点时候 14 | 活着的编号是多少,再调一下这个函数, 就能算出3个节点时候 15 | 活着的编号是多少 16 | 依次往上调用, 直到N个节点, 一个节点也不少的时候, 算出来的就是 17 | 最后活着的编号 18 | 19 | 20 | 21 | 想说的是: 22 | 不要过度依赖你自己找规律的能力 23 | 一旦发现是剃刀感觉的函数, 你就看 24 | 最标准的 y = x % i, 怎么变成你现在找规律的这个图像 25 | 直接关系就能出来 26 | 不要过度依赖你自己找规律的能力, 你很有可能照不出来 27 | 而且也不够快 28 | 以后再遇到剃刀感觉的这种函数描述, 不要依赖你现场找规律的能力, 29 | 只要发现它是剃刀感觉的函数, 直接从最简单函数出发去变换 30 | 31 | ## 公式推导 32 | 33 | y = x % i 34 | i: 长度 35 | ![[Pasted image 20210115021332.png]] 36 | 37 | 38 | 编号 = (报数-1) % i + 1 39 | ![[Pasted image 20210115022303.png]] 40 | 41 | 旧 = (新 + S -1) % i + 1 42 | 43 | ![[Pasted image 20210115024734.png]] 44 | S是被杀死的 45 | 46 | S = (m - 1) % i + 1 47 | 48 | ==> 49 | 旧 = (新 + (m - 1) % i + 1 -1) % i + 1 50 | ==> 51 | 旧 = (新 + m - 1) % i + 1 52 | 53 | 54 | ## 代码 55 | 56 | ```java 57 | // 现在一共有i个节点,数到m就杀死节点,最终会活下来的节点, 58 | // 请返回它在有i个节点时候的编号 59 | // 旧 60 | // getLive(N, m) 61 | public static int getLive(int i, int m) { 62 | if (i == 1) { 63 | return 1; 64 | } 65 | // getLive(i - 1, m) 长度为i-1时候,活下来的编号 66 | return (getLive(i - 1, m) + m - 1) % i + 1; 67 | } 68 | 69 | 70 | public static Node josephusKill2(Node head, int m) { 71 | if (head == null || head.next == head || m < 1) { 72 | return head; 73 | } 74 | Node cur = head.next; 75 | int size = 1; // tmp -> list size 76 | while (cur != head) { 77 | size++; 78 | cur = cur.next; 79 | } 80 | int live = getLive(size, m); // tmp -> service node position 81 | while (--live != 0) { 82 | head = head.next; 83 | } 84 | head.next = head; 85 | return head; 86 | } 87 | ``` 88 | 89 | 最后返回的是: 90 | ![[Pasted image 20210115030800.png]] -------------------------------------------------------------------------------- /01_Algo/05_Math/计算一个数的平方根.md: -------------------------------------------------------------------------------- 1 | # 计算一个数的平方根 2 | 3 | --- 4 | 5 | 6 | 计算并返回 x 的平方根,其中 x 是非负整数。 7 | 8 | 9 | ## 二分法开根号 10 | 当 x≥2 时,它的整数平方根一定小于(等于) x/2 且大于 0,即 0 < a < x 。 11 | 由于 a 一定是整数,此问题可以转换成在有序整数集中寻找一个 12 | 特定值,因此可以使用二分查找。 13 | 14 | ```java 15 | public static int mySqrt(int x) { 16 | 17 | //if(x == 0 || x == 1) return x; 18 | if (x < 2) return x; 19 | 20 | //int l = 1, r =x, res; 21 | int l = 2, r = x/2; // 优化 22 | 23 | while (l <= r) { 24 | int m = l + (r - l) / 2; 25 | if (m == x/m) { // 测试数据有非常大的int, 如果测试 x 跟 m^2可能会越界 26 | return m; 27 | } else if ( m > x/m ) { 28 | r = m -1; 29 | } else { // 当数值小的时候,左侧+1,可能会比最终结果大,退出循环(l>r), 则结果为r 30 | l = m + 1; 31 | } 32 | } 33 | return r; 34 | } 35 | 36 | 37 | 38 | public static double mySqrtDouble(int x) { 39 | 40 | double l = 2.0, r = x/2.0; // 优化 41 | double ans = -1; 42 | 43 | while (Math.abs(r - l) > 1e-9) { 44 | double m = l + (r - l) / 2.0; 45 | if ( m * m <= x ) { 46 | l = m; 47 | ans = m; 48 | } else { 49 | r = m; 50 | } 51 | } 52 | return ans; 53 | } 54 | 55 | ``` 56 | 57 | 58 | ## [[牛顿迭代法开根号]] 59 | 60 | -------------------------------------------------------------------------------- /01_Algo/05_Math/鸽笼原理.md: -------------------------------------------------------------------------------- 1 | # 鸽笼原理 2 | 3 | --- 4 | 5 | 两个基本原理: 6 | 7 | 鸽笼原理基本形式一: 8 | 如果把n+1(n是正整数)个对象放入n个盒子里,那么至少有一个盒子中放入两个或者两个以上的对象。 9 | 10 | 鸽笼原理基本形式二: 11 | 如果把m个对象放到n个盒子里(m,n都是正整数),那么至少有一个盒子中放入[m-1/n]+1个的对象。注:[m-1/n]中的[]代表着m-1/n的整数部分。 12 | 13 | 原理一证明: 14 | 15 | 反证法:假设每个盒子中都少于两个对象,那么总数不可能为n+1个对象,与前提矛盾。 16 | 17 | 原理二证明: 18 | 19 | 反证法:假设每个盒子中都少于或者等于[m-1/n]对象,那么对象的总数将不会多于n*[m-1/n]个,从而少于或者等于m-1个,与前提矛盾。 20 | 21 | 22 | - [[40亿个无符号整数的文件中找到一个未出现过的数]] 23 | - [[不算差的平凡解杀死可能性的技巧]] 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /01_Algo/06_Template/KMP算法代码.md: -------------------------------------------------------------------------------- 1 | # KMP算法代码 2 | 3 | #KMP算法 4 | 5 | 6 | --- 7 | 8 | 9 | ```java 10 | // O(N) 11 | public static int getIndexOf(String s, String m) { 12 | if (s == null || m == null || m.length() < 1 || s.length() < m.length()) { 13 | return -1; 14 | } 15 | char[] str = s.toCharArray(); 16 | char[] match = m.toCharArray(); 17 | int x = 0; // str中当前比对到的位置 18 | int y = 0; // match中当前比对到的位置 19 | // M M <= N O(M) 20 | int[] next = getNextArray(match); // next[i] match中i之前的字符串match[0..i-1] 21 | // O(N) 22 | while (x < str.length && y < match.length) { 23 | if (str[x] == match[y]) { 24 | x++; 25 | y++; 26 | } else if (next[y] == -1) { // y == 0 27 | x++; 28 | } else { 29 | y = next[y]; 30 | } 31 | } 32 | return y == match.length ? x - y : -1; 33 | } 34 | 35 | // M O(M) 36 | public static int[] getNextArray(char[] match) { 37 | if (match.length == 1) { 38 | return new int[] { -1 }; 39 | } 40 | int[] next = new int[match.length]; 41 | next[0] = -1; 42 | next[1] = 0; 43 | int i = 2; 44 | // cn代表,cn位置的字符,是当前和i-1位置比较的字符 45 | int cn = 0; 46 | while (i < next.length) { 47 | if (match[i - 1] == match[cn]) { // 跳出来的时候 48 | next[i++] = ++cn; 49 | } else if (cn > 0) { 50 | cn = next[cn]; 51 | } else { 52 | next[i++] = 0; 53 | } 54 | } 55 | return next; 56 | } 57 | 58 | ``` 59 | 60 | -------------------------------------------------------------------------------- /01_Algo/06_Template/Kruskal算法代码.md: -------------------------------------------------------------------------------- 1 | # Kruskal算法代码 2 | 3 | #Kruskal 4 | 5 | --- 6 | 7 | 8 | ```java 9 | public static class EdgeComparator implements Comparator { 10 | 11 | @Override 12 | public int compare(Edge o1, Edge o2) { 13 | return o1.weight - o2.weight; 14 | } 15 | 16 | } 17 | 18 | public static Set kruskalMST(Graph graph) { 19 | UnionFind unionFind = new UnionFind(); 20 | unionFind.makeSets(graph.nodes.values()); 21 | PriorityQueue priorityQueue = new PriorityQueue<>(new EdgeComparator()); 22 | for (Edge edge : graph.edges) { // M 条边 23 | priorityQueue.add(edge); // O(logM) 24 | } 25 | Set result = new HashSet<>(); 26 | while (!priorityQueue.isEmpty()) { // M 条边 27 | Edge edge = priorityQueue.poll(); // O(logM) 28 | if (!unionFind.isSameSet(edge.from, edge.to)) { // O(1) 29 | result.add(edge); 30 | unionFind.union(edge.from, edge.to); 31 | } 32 | } 33 | return result; 34 | } 35 | 36 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/LFU内存替换算法的实现.md: -------------------------------------------------------------------------------- 1 | # LFU内存替换算法的实现 2 | 3 | 4 | #LFU 5 | #SuperHard 6 | 7 | 460.LFU 缓存 8 | 9 | 难度不在于算法上的设计, 难度在于Coding 10 | 11 | --- 12 | 13 | https://leetcode-cn.com/problems/lfu-cache 14 | 15 | 一个缓存结构需要实现如下功能 16 | ```text 17 | void set(int key, int value): 加入或修改key对应的value 18 | int get(int key) : 查询key对应的value值 19 | ``` 20 | 但是缓存中最多放K条记录,如果新的第K+1条记录要加入,就需要根据策略删掉一条记录,然后才能把新记录加入。 21 | 这个策略为: 22 | 在缓存结构的K条记录中,哪一个key从进入缓存结构的时刻开始,被调用set或者get的次数最少,就删掉这个key的记录; 23 | 如果调用次数最少的key有多个,上次调用发生最早的key被删除。 24 | 这就是LFU缓存替换算法。实现这个结构,K作为参数给出。 25 | 26 | --- 27 | 28 | 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。 29 | 30 | 实现 LFUCache 类: 31 | 32 | LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象 33 | int get(int key) - 如果键存在于缓存中,则获取键的值,否则返回 -1。 34 | void put(int key, int value) - 如果键已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。 35 | 注意「项的使用次数」就是自插入该项以来对其调用 get 和 put 函数的次数之和。使用次数会在对应项被移除后置为 0 。 36 | 37 | 为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。 38 | 39 | 当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。 40 | ``` 41 | 示例: 42 | 43 | 输入: 44 | ["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"] 45 | [[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]] 46 | 输出: 47 | [null, null, null, 1, null, -1, 3, null, -1, 3, 4] 48 | 49 | 解释: 50 | // cnt(x) = 键 x 的使用计数 51 | // cache=[] 将显示最后一次使用的顺序(最左边的元素是最近的) 52 | LFUCache lFUCache = new LFUCache(2); 53 | lFUCache.put(1, 1); // cache=[1,_], cnt(1)=1 54 | lFUCache.put(2, 2); // cache=[2,1], cnt(2)=1, cnt(1)=1 55 | lFUCache.get(1); // 返回 1 56 | // cache=[1,2], cnt(2)=1, cnt(1)=2 57 | lFUCache.put(3, 3); // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小 58 | // cache=[3,1], cnt(3)=1, cnt(1)=2 59 | lFUCache.get(2); // 返回 -1(未找到) 60 | lFUCache.get(3); // 返回 3 61 | // cache=[3,1], cnt(3)=2, cnt(1)=2 62 | lFUCache.put(4, 4); // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用 63 | // cache=[4,3], cnt(4)=1, cnt(3)=2 64 | lFUCache.get(1); // 返回 -1(未找到) 65 | lFUCache.get(3); // 返回 3 66 | // cache=[3,4], cnt(4)=1, cnt(3)=3 67 | lFUCache.get(4); // 返回 4 68 | // cache=[3,4], cnt(4)=2, cnt(3)=3 69 | ``` 70 | 71 | 提示: 72 | 73 | 0 <= capacity, key, value <= 10^4 74 | 最多调用 10^5 次 get 和 put 方法 75 |   76 | 77 | 进阶:你可以为这两种操作设计时间复杂度为 O(1) 的实现吗? 78 | -------------------------------------------------------------------------------- /01_Algo/06_Template/Manacher算法代码.md: -------------------------------------------------------------------------------- 1 | # Manacher算法代码 2 | 3 | #Manacher算法 4 | 5 | --- 6 | 7 | 8 | ```java 9 | public static int manacher(String s) { 10 | if (s == null || s.length() == 0) { 11 | return 0; 12 | } 13 | // "12132" -> "#1#2#1#3#2#" 14 | char[] str = manacherString(s); 15 | // 回文半径的大小 16 | int[] pArr = new int[str.length]; 17 | int C = -1; 18 | // 讲述中:R代表最右的扩成功的位置。coding:最右的扩成功位置的,再下一个位置 19 | int R = -1; 20 | int max = Integer.MIN_VALUE; 21 | for (int i = 0; i < str.length; i++) { 22 | // R第一个违规的位置,i>= R 23 | // i位置扩出来的答案,i位置扩的区域,至少是多大。 24 | pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1; 25 | while (i + pArr[i] < str.length && i - pArr[i] > -1) { 26 | if (str[i + pArr[i]] == str[i - pArr[i]]) 27 | pArr[i]++; 28 | else { 29 | break; 30 | } 31 | } 32 | if (i + pArr[i] > R) { 33 | R = i + pArr[i]; 34 | C = i; 35 | } 36 | max = Math.max(max, pArr[i]); 37 | } 38 | return max - 1; 39 | } 40 | 41 | public static char[] manacherString(String str) { 42 | char[] charArr = str.toCharArray(); 43 | char[] res = new char[str.length() * 2 + 1]; 44 | int index = 0; 45 | for (int i = 0; i != res.length; i++) { 46 | res[i] = (i & 1) == 0 ? '#' : charArr[index++]; 47 | } 48 | return res; 49 | } 50 | 51 | ``` 52 | -------------------------------------------------------------------------------- /01_Algo/06_Template/Morris实现中序遍历.md: -------------------------------------------------------------------------------- 1 | # Morris实现中序遍历 2 | 3 | #Morris遍历 4 | 5 | --- 6 | 7 | ```java 8 | public static void morrisIn(Node head) { 9 | if (head == null) { 10 | return; 11 | } 12 | Node cur = head; 13 | Node mostRight = null; 14 | while (cur != null) { 15 | mostRight = cur.left; 16 | if (mostRight != null) { 17 | while (mostRight.right != null && mostRight.right != cur) { 18 | mostRight = mostRight.right; 19 | } 20 | if (mostRight.right == null) { 21 | mostRight.right = cur; 22 | cur = cur.left; 23 | continue; 24 | } else { 25 | mostRight.right = null; 26 | } 27 | } 28 | System.out.print(cur.value + " "); 29 | cur = cur.right; 30 | } 31 | System.out.println(); 32 | } 33 | 34 | ``` 35 | -------------------------------------------------------------------------------- /01_Algo/06_Template/Morris实现前序遍历.md: -------------------------------------------------------------------------------- 1 | # Morris实现前序遍历 2 | 3 | #Morris遍历 4 | 5 | --- 6 | 7 | 8 | ```java 9 | public static void morrisPre(Node head) { 10 | if (head == null) { 11 | return; 12 | } 13 | Node cur1 = head; 14 | Node cur2 = null; 15 | while (cur1 != null) { 16 | cur2 = cur1.left; 17 | if (cur2 != null) { 18 | while (cur2.right != null && cur2.right != cur1) { 19 | cur2 = cur2.right; 20 | } 21 | if (cur2.right == null) { 22 | cur2.right = cur1; 23 | System.out.print(cur1.value + " "); 24 | cur1 = cur1.left; 25 | continue; 26 | } else { 27 | cur2.right = null; 28 | } 29 | } else { 30 | System.out.print(cur1.value + " "); 31 | } 32 | cur1 = cur1.right; 33 | } 34 | System.out.println(); 35 | } 36 | 37 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/Morris实现后序遍历.md: -------------------------------------------------------------------------------- 1 | # Morris实现后序遍历 2 | 3 | #Morris遍历 4 | 5 | --- 6 | 7 | ```java 8 | public static void morrisPos(Node head) { 9 | if (head == null) { 10 | return; 11 | } 12 | Node cur = head; 13 | Node mostRight = null; 14 | while (cur != null) { 15 | mostRight = cur.left; 16 | if (mostRight != null) { 17 | while (mostRight.right != null && mostRight.right != cur) { 18 | mostRight = mostRight.right; 19 | } 20 | if (mostRight.right == null) { 21 | mostRight.right = cur; 22 | cur = cur.left; 23 | continue; 24 | } else { 25 | mostRight.right = null; 26 | printEdge(cur.left); 27 | } 28 | } 29 | cur = cur.right; 30 | } 31 | printEdge(head); 32 | System.out.println(); 33 | } 34 | 35 | public static void printEdge(Node head) { 36 | Node tail = reverseEdge(head); 37 | Node cur = tail; 38 | while (cur != null) { 39 | System.out.print(cur.value + " "); 40 | cur = cur.right; 41 | } 42 | reverseEdge(tail); 43 | } 44 | 45 | public static Node reverseEdge(Node from) { 46 | Node pre = null; 47 | Node next = null; 48 | while (from != null) { 49 | next = from.right; 50 | from.right = pre; 51 | pre = from; 52 | from = next; 53 | } 54 | return pre; 55 | } 56 | 57 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/Morris遍历代码.md: -------------------------------------------------------------------------------- 1 | # Morris遍历代码 2 | 3 | #Morris遍历 4 | 5 | --- 6 | 7 | ```java 8 | 9 | public static void morris(Node head) { 10 | if (head == null) { 11 | return; 12 | } 13 | Node cur = head; 14 | Node mostRight = null; 15 | while (cur != null) { 16 | // cur有没有左树 17 | mostRight = cur.left; 18 | if (mostRight != null) { // 有左树的情况下 19 | // 找到cur左树上,真实的最右 20 | while (mostRight.right != null && mostRight.right != cur) { 21 | mostRight = mostRight.right; 22 | } 23 | // 从while中出来,mostRight一定是cur左树上的最右节点 24 | // mostRight 25 | if (mostRight.right == null) { 26 | mostRight.right = cur; 27 | cur = cur.left; 28 | continue; 29 | } else { // mostRight.right != null -> mostRight.right == cur 30 | mostRight.right = null; 31 | } 32 | } 33 | cur = cur.right; 34 | } 35 | } 36 | 37 | ``` 38 | 39 | - [[Morris实现前序遍历]] 40 | - [[Morris实现中序遍历]] 41 | - [[Morris实现后序遍历]] 42 | -------------------------------------------------------------------------------- /01_Algo/06_Template/Prim算法代码.md: -------------------------------------------------------------------------------- 1 | # Prim算法代码 2 | 3 | #Prim 4 | 5 | --- 6 | 7 | ```java 8 | public static class EdgeComparator implements Comparator { 9 | 10 | @Override 11 | public int compare(Edge o1, Edge o2) { 12 | return o1.weight - o2.weight; 13 | } 14 | 15 | } 16 | 17 | public static Set primMST(Graph graph) { 18 | // 解锁的边进入小根堆 19 | PriorityQueue priorityQueue = new PriorityQueue<>(new EdgeComparator()); 20 | 21 | // 哪些点被解锁出来了 22 | HashSet nodeSet = new HashSet<>(); 23 | Set result = new HashSet<>(); // 依次挑选的的边在result里 24 | for (Node node : graph.nodes.values()) { // 随便挑了一个点 25 | // node 是开始点 26 | if (!nodeSet.contains(node)) { 27 | nodeSet.add(node); 28 | for (Edge edge : node.edges) { // 由一个点,解锁所有相连的边 29 | priorityQueue.add(edge); 30 | } 31 | while (!priorityQueue.isEmpty()) { 32 | Edge edge = priorityQueue.poll(); // 弹出解锁的边中,最小的边 33 | Node toNode = edge.to; // 可能的一个新的点 34 | if (!nodeSet.contains(toNode)) { // 不含有的时候,就是新的点 35 | nodeSet.add(toNode); 36 | result.add(edge); 37 | for (Edge nextEdge : toNode.edges) { 38 | priorityQueue.add(nextEdge); 39 | } 40 | } 41 | } 42 | } 43 | // break; 44 | } 45 | return result; 46 | } 47 | 48 | // 请保证graph是连通图 49 | // graph[i][j]表示点i到点j的距离,如果是系统最大值代表无路 50 | // 返回值是最小连通图的路径之和 51 | public static int prim(int[][] graph) { 52 | int size = graph.length; 53 | int[] distances = new int[size]; 54 | boolean[] visit = new boolean[size]; 55 | visit[0] = true; 56 | for (int i = 0; i < size; i++) { 57 | distances[i] = graph[0][i]; 58 | } 59 | int sum = 0; 60 | for (int i = 1; i < size; i++) { 61 | int minPath = Integer.MAX_VALUE; 62 | int minIndex = -1; 63 | for (int j = 0; j < size; j++) { 64 | if (!visit[j] && distances[j] < minPath) { 65 | minPath = distances[j]; 66 | minIndex = j; 67 | } 68 | } 69 | if (minIndex == -1) { 70 | return sum; 71 | } 72 | visit[minIndex] = true; 73 | sum += minPath; 74 | for (int j = 0; j < size; j++) { 75 | if (!visit[j] && distances[j] > graph[minIndex][j]) { 76 | distances[j] = graph[minIndex][j]; 77 | } 78 | } 79 | } 80 | return sum; 81 | } 82 | 83 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/一个简单的Hash函数.md: -------------------------------------------------------------------------------- 1 | # 一个简单的Hash函数 2 | 3 | --- 4 | 5 | 假设字符都是小写字母 6 | 除余法实现: 7 | 8 | ```java 9 | class HashFunction { 10 | private int cap; 11 | private int seed; 12 | public HashFunction(int cap, int seed) { 13 | this.cap = cap; 14 | this.seed = seed; 15 | } 16 | 17 | int myHash(String str) { 18 | int hashcode = 0; 19 | int n = str.length(); 20 | for (int i = 0; i < n; i++) { 21 | hashcode += seed * hashcode + (str.charAt(i) - 'a'); 22 | hashcode %= cap; 23 | } 24 | return hashcode; 25 | } 26 | }; 27 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/不使用加减乘除算符实现加减乘除(仅位运算).md: -------------------------------------------------------------------------------- 1 | # 不使用加减乘除算符实现加减乘除(仅位运算) 2 | 3 | --- 4 | 5 | 29.[[两数相除]] 6 | 7 | ```java 8 | public static int add(int a, int b) { 9 | int sum = a; 10 | while (b != 0) { 11 | sum = a ^ b; 12 | b = (a & b) << 1; 13 | a = sum; 14 | } 15 | return sum; 16 | } 17 | 18 | public static int negNum(int n) { 19 | return add(~n, 1); 20 | } 21 | 22 | public static int minus(int a, int b) { 23 | return add(a, negNum(b)); 24 | } 25 | 26 | public static int multi(int a, int b) { 27 | int res = 0; 28 | while (b != 0) { 29 | if ((b & 1) != 0) { 30 | res = add(res, a); 31 | } 32 | a <<= 1; 33 | b >>>= 1; 34 | } 35 | return res; 36 | } 37 | 38 | public static boolean isNeg(int n) { 39 | return n < 0; 40 | } 41 | 42 | public static int div(int a, int b) { 43 | int x = isNeg(a) ? negNum(a) : a; 44 | int y = isNeg(b) ? negNum(b) : b; 45 | int res = 0; 46 | for (int i = 31; i > negNum(1); i = minus(i, 1)) { 47 | if ((x >> i) >= y) { 48 | res |= (1 << i); 49 | x = minus(x, y << i); 50 | } 51 | } 52 | return isNeg(a) ^ isNeg(b) ? negNum(res) : res; 53 | } 54 | 55 | public static int divide(int dividend, int divisor) { 56 | if (divisor == Integer.MIN_VALUE) { 57 | return dividend == Integer.MIN_VALUE ? 1 : 0; 58 | } 59 | // 除数不是系统最小 60 | if (dividend == Integer.MIN_VALUE) { 61 | if (divisor == negNum(1)) { 62 | return Integer.MAX_VALUE; 63 | } 64 | int res = div(add(dividend, 1), divisor); 65 | return add(res, div(minus(dividend, multi(res, divisor)), divisor)); 66 | } 67 | // dividend不是系统最小,divisor也不是系统最小 68 | return div(dividend, divisor); 69 | } 70 | // div(a,b) a和b都不能是系统最小 71 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/两个等长有序数组求上中位数.md: -------------------------------------------------------------------------------- 1 | # 两个等长有序数组求上中位数 2 | 3 | 重要的算法原型 4 | 5 | --- 6 | 7 | 8 | ```java 9 | 10 | // A[s1...e1] 11 | // B[s2...e2] 12 | // 这两段一定等长且都有序 13 | // 求这两段整体的上中位数,上中位数值返回 14 | public static int getUpMedian(int[] A, int s1, int e1, int[] B, int s2, int e2) { 15 | int mid1 = 0; 16 | int mid2 = 0; 17 | while (s1 < e1) { 18 | mid1 = (s1 + e1) / 2; 19 | mid2 = (s2 + e2) / 2; 20 | if (A[mid1] == B[mid2]) { 21 | return A[mid1]; 22 | } 23 | if (((e1 - s1 + 1) & 1) == 1) { // 奇数长度 24 | if (A[mid1] > B[mid2]) { 25 | if (B[mid2] >= A[mid1 - 1]) { 26 | return B[mid2]; 27 | } 28 | e1 = mid1 - 1; 29 | s2 = mid2 + 1; 30 | } else { // A[mid1] < B[mid2] 31 | if (A[mid1] >= B[mid2 - 1]) { 32 | return A[mid1]; 33 | } 34 | e2 = mid2 - 1; 35 | s1 = mid1 + 1; 36 | } 37 | } else { // 偶数长度 38 | if (A[mid1] > B[mid2]) { 39 | e1 = mid1; 40 | s2 = mid2 + 1; 41 | } else { 42 | e2 = mid2; 43 | s1 = mid1 + 1; 44 | } 45 | } 46 | } 47 | return Math.min(A[s1], B[s2]); 48 | } 49 | 50 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/冒泡排序.md: -------------------------------------------------------------------------------- 1 | # 冒泡排序 2 | 3 | #冒泡排序 4 | 5 | --- 6 | 7 | 过程: 8 | 在arr[0~N-1]范围上: 9 | arr[0]和arr[1],谁大谁来到1位置;arr[1]和arr[2],谁大谁来到2位置…arr[N-2]和arr[N-1],谁大谁来到N-1位置 10 | 11 | 在arr[0~N-2]范围上,重复上面的过程,但最后一步是arr[N-3]和arr[N-2],谁大谁来到N-2位置 12 | 在arr[0~N-3]范围上,重复上面的过程,但最后一步是arr[N-4]和arr[N-3],谁大谁来到N-3位置 13 | … 14 | 最后在arr[0~1]范围上,重复上面的过程,但最后一步是arr[0]和arr[1],谁大谁来到1位置 15 | 16 | 17 | 估算: 18 | 很明显,如果arr长度为N,每一步常数操作的数量,依然如等差数列一般 19 | 所以,总的常数操作数量 = $a*(N^2) + b*N + c$ (a、b、c都是常数) 20 | 21 | 所以冒泡排序的时间复杂度为O($N^2$)。 22 | 23 | --- 24 | 25 | 26 | ```java 27 | public static void bubbleSort(int[] arr) { 28 | if (arr == null || arr.length < 2) { 29 | return; 30 | } 31 | // 0 ~ N-1 32 | // 0 ~ N-2 33 | // 0 ~ N-3 34 | for (int e = arr.length - 1; e > 0; e--) { // 0 ~ e 35 | for (int i = 0; i < e; i++) { 36 | if (arr[i] > arr[i + 1]) { 37 | swap(arr, i, i + 1); 38 | } 39 | } 40 | } 41 | } 42 | 43 | ``` 44 | -------------------------------------------------------------------------------- /01_Algo/06_Template/单调栈代码.md: -------------------------------------------------------------------------------- 1 | # 单调栈代码 2 | 3 | 4 | --- 5 | 6 | 7 | ```java 8 | // 数组中没有重复值 9 | public static int[][] getNearLessNoRepeat(int[] arr) { 10 | int[][] res = new int[arr.length][2]; 11 | Stack stack = new Stack<>(); 12 | for (int i = 0; i < arr.length; i++) { 13 | while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { 14 | int popIndex = stack.pop(); 15 | int leftLessIndex = stack.isEmpty() ? -1 : stack.peek(); 16 | res[popIndex][0] = leftLessIndex; 17 | res[popIndex][1] = i; 18 | } 19 | stack.push(i); 20 | } 21 | while (!stack.isEmpty()) { 22 | int popIndex = stack.pop(); 23 | int leftLessIndex = stack.isEmpty() ? -1 : stack.peek(); 24 | res[popIndex][0] = leftLessIndex; 25 | res[popIndex][1] = -1; 26 | } 27 | return res; 28 | } 29 | ``` 30 | 31 | 32 | 33 | ```java 34 | // 数组中有重复值 35 | 36 | // arr [3, 2, 1, 4, 5] 37 | // 0 1 2 3 4 38 | 39 | // [ 40 | // 0 : [-1, 1 ] 41 | // 1 : [-1, 2 ] 42 | 43 | // ] 44 | // 45 | public static int[][] getNearLess(int[] arr) { 46 | int[][] res = new int[arr.length][2]; 47 | 48 | 49 | // List -> 放的是位置,同样值的东西,位置压在一起 50 | // 代表值 底 -> 顶 小 -> 大 51 | Stack> stack = new Stack<>(); 52 | for (int i = 0; i < arr.length; i++) { // i -> arr[i] 进栈 53 | // 底 -> 顶, 小 -> 大 54 | while (!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) { 55 | List popIs = stack.pop(); 56 | // 取位于下面位置的列表中,最晚加入的那个 57 | int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1); 58 | for (Integer popi : popIs) { 59 | res[popi][0] = leftLessIndex; 60 | res[popi][1] = i; 61 | } 62 | } 63 | // 相等的、比你小的 64 | if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]) { 65 | stack.peek().add(Integer.valueOf(i)); 66 | } else { 67 | ArrayList list = new ArrayList<>(); 68 | list.add(i); 69 | stack.push(list); 70 | } 71 | } 72 | while (!stack.isEmpty()) { 73 | List popIs = stack.pop(); 74 | // 取位于下面位置的列表中,最晚加入的那个 75 | int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1); 76 | for (Integer popi : popIs) { 77 | res[popi][0] = leftLessIndex; 78 | res[popi][1] = -1; 79 | } 80 | } 81 | return res; 82 | } 83 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/图的拓扑排序算法.md: -------------------------------------------------------------------------------- 1 | # 图的拓扑排序算法 2 | 3 | 4 | #拓扑排序 5 | 6 | 7 | --- 8 | 9 | 1)在图中找到所有入度为0的点输出 10 | 2)把所有入度为0的点在图中删掉,继续找入度为0的点输出,周而复始 11 | 3)图的所有点都被删除后,依次输出的顺序就是拓扑排序 12 | 13 | 要求:有向图且其中没有环 14 | 应用:事件安排、编译顺序 15 | 16 | 17 | ```java 18 | // directed graph and no loop 19 | public static List sortedTopology(Graph graph) { 20 | // key:某一个node 21 | // value:剩余的入度 22 | HashMap inMap = new HashMap<>(); 23 | // 剩余入度为0的点,才能进这个队列 24 | Queue zeroInQueue = new LinkedList<>(); 25 | 26 | for (Node node : graph.nodes.values()) { 27 | inMap.put(node, node.in); 28 | if (node.in == 0) { 29 | zeroInQueue.add(node); 30 | } 31 | } 32 | // 拓扑排序的结果,依次加入result 33 | List result = new ArrayList<>(); 34 | while (!zeroInQueue.isEmpty()) { 35 | Node cur = zeroInQueue.poll(); 36 | result.add(cur); 37 | for (Node next : cur.nexts) { 38 | inMap.put(next, inMap.get(next) - 1); 39 | if (inMap.get(next) == 0) { 40 | zeroInQueue.add(next); 41 | } 42 | } 43 | } 44 | return result; 45 | } 46 | ``` 47 | 48 | 49 | ## [[拓扑排序DFS解法]] 50 | 51 | 不常见, 比较冷 52 | -------------------------------------------------------------------------------- /01_Algo/06_Template/图的统一表述结构.md: -------------------------------------------------------------------------------- 1 | # 图的统一表述结构 2 | 3 | 4 | --- 5 | 6 | 摒弃邻接表,邻接矩阵,矩阵数组等多种图的表达方式, 统一正规化为以下表达方式. 7 | 8 | ## 点结构描述 9 | 10 | ```java 11 | // 点结构的描述 A 0 12 | public class Node { 13 | public int value; 14 | public int in; 15 | public int out; 16 | public ArrayList nexts; 17 | public ArrayList edges; 18 | 19 | public Node(int value) { 20 | this.value = value; 21 | in = 0; 22 | out = 0; 23 | nexts = new ArrayList<>(); 24 | edges = new ArrayList<>(); 25 | } 26 | } 27 | ``` 28 | 29 | ## 边的描述 30 | ```java 31 | public class Edge { 32 | public int weight; 33 | public Node from; 34 | public Node to; 35 | 36 | public Edge(int weight, Node from, Node to) { 37 | this.weight = weight; 38 | this.from = from; 39 | this.to = to; 40 | } 41 | } 42 | 43 | ``` 44 | 45 | ## 图的描述 46 | ```java 47 | public class Graph { 48 | public HashMap nodes; 49 | public HashSet edges; 50 | 51 | public Graph() { 52 | nodes = new HashMap<>(); 53 | edges = new HashSet<>(); 54 | } 55 | } 56 | ``` 57 | 58 | Example: 59 | ```java 60 | public class GraphGenerator { 61 | 62 | // matrix 所有的边 63 | // N*3 的矩阵 64 | // [weight, from节点上面的值,to节点上面的值] 65 | public static Graph createGraph(Integer[][] matrix) { 66 | Graph graph = new Graph(); 67 | for (int i = 0; i < matrix.length; i++) { 68 | // matrix[0][0], matrix[0][1] matrix[0][2] 69 | Integer weight = matrix[i][0]; 70 | Integer from = matrix[i][1]; 71 | Integer to = matrix[i][2]; 72 | if (!graph.nodes.containsKey(from)) { 73 | graph.nodes.put(from, new Node(from)); 74 | } 75 | if (!graph.nodes.containsKey(to)) { 76 | graph.nodes.put(to, new Node(to)); 77 | } 78 | Node fromNode = graph.nodes.get(from); 79 | Node toNode = graph.nodes.get(to); 80 | Edge newEdge = new Edge(weight, fromNode, toNode); 81 | fromNode.nexts.add(toNode); 82 | fromNode.out++; 83 | toNode.in++; 84 | fromNode.edges.add(newEdge); 85 | graph.edges.add(newEdge); 86 | } 87 | return graph; 88 | } 89 | } 90 | ``` 91 | -------------------------------------------------------------------------------- /01_Algo/06_Template/基数排序代码.md: -------------------------------------------------------------------------------- 1 | # 基数排序 2 | 3 | 4 | #基数排序 5 | 6 | 7 | --- 8 | 9 | 一般来讲,基数排序要求,样本是10进制的正整数 10 | 11 | 12 | ```java 13 | // only for no-negative value 14 | public static void radixSort(int[] arr) { 15 | if (arr == null || arr.length < 2) { 16 | return; 17 | } 18 | radixSort(arr, 0, arr.length - 1, maxbits(arr)); 19 | } 20 | 21 | public static int maxbits(int[] arr) { 22 | int max = Integer.MIN_VALUE; 23 | for (int i = 0; i < arr.length; i++) { 24 | max = Math.max(max, arr[i]); 25 | } 26 | int res = 0; 27 | while (max != 0) { 28 | res++; 29 | max /= 10; 30 | } 31 | return res; 32 | } 33 | 34 | // arr[l..r]排序 , digit 35 | // l..r 3 56 17 100 3 36 | public static void radixSort(int[] arr, int L, int R, int digit) { 37 | final int radix = 10; 38 | int i = 0, j = 0; 39 | // 有多少个数准备多少个辅助空间 40 | int[] help = new int[R - L + 1]; 41 | for (int d = 1; d <= digit; d++) { // 有多少位就进出几次 42 | // 10个空间 43 | // count[0] 当前位(d位)是0的数字有多少个 44 | // count[1] 当前位(d位)是(0和1)的数字有多少个 45 | // count[2] 当前位(d位)是(0、1和2)的数字有多少个 46 | // count[i] 当前位(d位)是(0~i)的数字有多少个 47 | int[] count = new int[radix]; // count[0..9] 48 | for (i = L; i <= R; i++) { 49 | // 103 1 3 50 | // 209 1 9 51 | j = getDigit(arr[i], d); 52 | count[j]++; 53 | } 54 | for (i = 1; i < radix; i++) { 55 | count[i] = count[i] + count[i - 1]; 56 | } 57 | for (i = R; i >= L; i--) { 58 | j = getDigit(arr[i], d); 59 | help[count[j] - 1] = arr[i]; 60 | count[j]--; 61 | } 62 | for (i = L, j = 0; i <= R; i++, j++) { 63 | arr[i] = help[j]; 64 | } 65 | } 66 | } 67 | 68 | public static int getDigit(int x, int d) { 69 | return ((x / ((int) Math.pow(10, d - 1))) % 10); 70 | } 71 | ``` 72 | -------------------------------------------------------------------------------- /01_Algo/06_Template/堆排序.md: -------------------------------------------------------------------------------- 1 | # 堆排序 2 | 3 | #堆排序 4 | 5 | --- 6 | 7 | 1,先让整个数组都变成大根堆结构,建立堆的过程: 8 | 1)从上到下的方法,时间复杂度为O(N x logN) 9 | 2)从下到上的方法,时间复杂度为O(N) 10 | 2,把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆,一直周而复始,时间复杂度为O(N * logN) 11 | 3,堆的大小减小成0之后,排序完成 12 | 13 | ```java 14 | // 堆排序额外空间复杂度O(1) 15 | public static void heapSort(int[] arr) { 16 | if (arr == null || arr.length < 2) { 17 | return; 18 | } 19 | // O(N*logN) 20 | // for (int i = 0; i < arr.length; i++) { // O(N) 21 | // heapInsert(arr, i); // O(logN) 22 | // } 23 | for (int i = arr.length - 1; i >= 0; i--) { 24 | heapify(arr, i, arr.length); 25 | } 26 | int heapSize = arr.length; 27 | swap(arr, 0, --heapSize); 28 | // O(N*logN) 29 | while (heapSize > 0) { // O(N) 30 | heapify(arr, 0, heapSize); // O(logN) 31 | swap(arr, 0, --heapSize); // O(1) 32 | } 33 | } 34 | 35 | // arr[index]刚来的数,往上 36 | public static void heapInsert(int[] arr, int index) { 37 | while (arr[index] > arr[(index - 1) / 2]) { 38 | swap(arr, index, (index - 1) / 2); 39 | index = (index - 1) / 2; 40 | } 41 | } 42 | 43 | // arr[index]位置的数,能否往下移动 44 | public static void heapify(int[] arr, int index, int heapSize) { 45 | int left = index * 2 + 1; // 左孩子的下标 46 | while (left < heapSize) { // 下方还有孩子的时候 47 | // 两个孩子中,谁的值大,把下标给largest 48 | // 1)只有左孩子,left -> largest 49 | // 2) 同时有左孩子和右孩子,右孩子的值<= 左孩子的值,left -> largest 50 | // 3) 同时有左孩子和右孩子并且右孩子的值> 左孩子的值, right -> largest 51 | int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left; 52 | // 父和较大的孩子之间,谁的值大,把下标给largest 53 | largest = arr[largest] > arr[index] ? largest : index; 54 | if (largest == index) { 55 | break; 56 | } 57 | swap(arr, largest, index); 58 | index = largest; 59 | left = index * 2 + 1; 60 | } 61 | } 62 | 63 | public static void swap(int[] arr, int i, int j) { 64 | int tmp = arr[i]; 65 | arr[i] = arr[j]; 66 | arr[j] = tmp; 67 | } 68 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/字符串的全排列代码.md: -------------------------------------------------------------------------------- 1 | # 字符串的全排列代码 2 | #DFS 3 | 4 | --- 5 | 6 | 深度优先遍历后记得要恢复数据, 防止脏数据影响后面的操作, 避免干扰平行分支里后面的选择 7 | 8 | 时间复杂度: O(n!) * O(M), M字符串的平均长度 9 | 10 | ```java 11 | // strs里放着所有的字符串 12 | // 已经使用过的字符串的下标,在use里登记了,不要再使用了 13 | // 之前使用过的字符串,拼接成了-> path 14 | // 用all收集所有可能的拼接结果 15 | public static void process(String[] strs, HashSet use, String path, ArrayList all) { 16 | if (use.size() == strs.length) { 17 | all.add(path); 18 | } else { 19 | for (int i = 0; i < strs.length; i++) { 20 | if (!use.contains(i)) { 21 | use.add(i); 22 | process(strs, use, path + strs[i], all); 23 | use.remove(i); 24 | } 25 | } 26 | } 27 | } 28 | 29 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/完美洗牌算法代码.md: -------------------------------------------------------------------------------- 1 | # 完美洗牌算法代码 2 | #完美洗牌 3 | 4 | --- 5 | 6 | 用的话,也就是调模板用,可能是笔试时候用, 或者是跟面试官聊的时候作为一个原型直接跟他说 7 | 跟岗位没关系, 跟公司有关系 8 | 9 | 10 | ```java 11 | // 数组的长度为len,调整前的位置是i,返回调整之后的位置 12 | // 下标不从0开始,从1开始 13 | public static int modifyIndex2(int i, int len) { 14 | return (2 * i) % (len + 1); 15 | } 16 | 17 | // 主函数 18 | // 数组必须不为空,且长度为偶数 19 | public static void shuffle(int[] arr) { 20 | if (arr != null && arr.length != 0 && (arr.length & 1) == 0) { 21 | shuffle(arr, 0, arr.length - 1); 22 | } 23 | } 24 | 25 | // 在arr[L..R]上做完美洗牌的调整(arr[L..R]范围上一定要是偶数个数字) 26 | public static void shuffle(int[] arr, int L, int R) { 27 | while (R - L + 1 > 0) { // 切成一块一块的解决,每一块的长度满足(3^k)-1 28 | int len = R - L + 1; 29 | int base = 3; 30 | int k = 1; 31 | // 计算小于等于len并且是离len最近的,满足(3^k)-1的数 32 | // 也就是找到最大的k,满足3^k <= len+1 33 | while (base <= (len + 1) / 3) { // base > (N+1)/3 34 | base *= 3; 35 | k++; 36 | } 37 | // 3^k -1 38 | // 当前要解决长度为base-1的块,一半就是再除2 39 | int half = (base - 1) / 2; 40 | // [L..R]的中点位置 41 | int mid = (L + R) / 2; 42 | // 要旋转的左部分为[L+half...mid], 右部分为arr[mid+1..mid+half] 43 | // 注意在这里,arr下标是从0开始的 44 | rotate(arr, L + half, mid, mid + half); 45 | // 旋转完成后,从L开始算起,长度为base-1的部分进行下标连续推 46 | cycles(arr, L, base - 1, k); 47 | // 解决了前base-1的部分,剩下的部分继续处理 48 | L = L + base - 1; // L -> [] [+1...R] 49 | } 50 | } 51 | 52 | // 从start位置开始,往右len的长度这一段,做下标连续推 53 | // 出发位置依次为1,3,9... 54 | public static void cycles(int[] arr, int start, int len, int k) { 55 | // 找到每一个出发位置trigger,一共k个 56 | // 每一个trigger都进行下标连续推 57 | // 出发位置是从1开始算的,而数组下标是从0开始算的。 58 | for (int i = 0, trigger = 1; i < k; i++, trigger *= 3) { 59 | int preValue = arr[trigger + start - 1]; 60 | int cur = modifyIndex2(trigger, len); 61 | while (cur != trigger) { 62 | int tmp = arr[cur + start - 1]; 63 | arr[cur + start - 1] = preValue; 64 | preValue = tmp; 65 | cur = modifyIndex2(cur, len); 66 | } 67 | arr[cur + start - 1] = preValue; 68 | } 69 | } 70 | 71 | // [L..M]为左部分,[M+1..R]为右部分,左右两部分互换 72 | public static void rotate(int[] arr, int L, int M, int R) { 73 | reverse(arr, L, M); 74 | reverse(arr, M + 1, R); 75 | reverse(arr, L, R); 76 | } 77 | 78 | // [L..R]做逆序调整 79 | public static void reverse(int[] arr, int L, int R) { 80 | while (L < R) { 81 | int tmp = arr[L]; 82 | arr[L++] = arr[R]; 83 | arr[R--] = tmp; 84 | } 85 | } 86 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/并查集代码.md: -------------------------------------------------------------------------------- 1 | # 并查集代码 2 | 3 | #并查集 4 | 5 | 6 | --- 7 | 8 | 9 | ```java 10 | public static class Node { 11 | V value; 12 | 13 | public Node(V v) { 14 | value = v; 15 | } 16 | } 17 | 18 | public static class UnionSet { 19 | public HashMap> nodes; 20 | public HashMap, Node> parents; 21 | public HashMap, Integer> sizeMap; 22 | 23 | public UnionSet(List values) { 24 | for (V cur : values) { 25 | Node node = new Node<>(cur); 26 | nodes.put(cur, node); 27 | parents.put(node, node); 28 | sizeMap.put(node, 1); 29 | } 30 | } 31 | 32 | // 从点cur开始,一直往上找,找到不能再往上的代表点,返回 33 | public Node findFather(Node cur) { 34 | Stack> path = new Stack<>(); 35 | while (cur != parents.get(cur)) { 36 | path.push(cur); 37 | cur = parents.get(cur); 38 | } 39 | // cur头节点 40 | while (!path.isEmpty()) { 41 | parents.put(path.pop(), cur); 42 | } 43 | return cur; 44 | } 45 | 46 | public boolean isSameSet(V a, V b) { 47 | if (!nodes.containsKey(a) || !nodes.containsKey(b)) { 48 | return false; 49 | } 50 | return findFather(nodes.get(a)) == findFather(nodes.get(b)); 51 | } 52 | 53 | public void union(V a, V b) { 54 | if (!nodes.containsKey(a) || !nodes.containsKey(b)) { 55 | return; 56 | } 57 | Node aHead = findFather(nodes.get(a)); 58 | Node bHead = findFather(nodes.get(b)); 59 | if (aHead != bHead) { 60 | int aSetSize = sizeMap.get(aHead); 61 | int bSetSize = sizeMap.get(bHead); 62 | Node big = aSetSize >= bSetSize ? aHead : bHead; 63 | Node small = big == aHead ? bHead : aHead; 64 | parents.put(small, big); 65 | sizeMap.put(big, aSetSize + bSetSize); 66 | sizeMap.remove(small); 67 | } 68 | } 69 | } 70 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/怎么按对角线推导二维矩阵的格子.md: -------------------------------------------------------------------------------- 1 | # 怎么按对角线推导二维矩阵的格子 2 | 3 | --- 4 | 5 | 6 | [[A,B玩家从左右两边拿纸牌,返回最后获胜者的分数]] 7 | 8 | ```java 9 | for (int startCol = 1; startCol < N; startCol++) { 10 | int L = 0; 11 | int R = startCol; 12 | while (R < N) { 13 | fmap[L][R] = Math.max(arr[L] + 14 | gmap[L + 1][R], arr[R] + gmap[L][R - 1]); 15 | gmap[L][R] = Math.min(fmap[L + 1][R], fmap[L][R - 1]); 16 | L++; 17 | R++; 18 | } 19 | } 20 | 21 | ``` 22 | -------------------------------------------------------------------------------- /01_Algo/06_Template/插入排序.md: -------------------------------------------------------------------------------- 1 | # 插入排序 2 | 3 | #插入排序 4 | 5 | --- 6 | 7 | 过程: 8 | 想让arr[0~0]上有序,这个范围只有一个数,当然是有序的。 9 | 想让arr[0~1]上有序,所以从arr[1]开始往前看,如果arr[1]= 0 && arr[j] > arr[j + 1]; j--) { 37 | swap(arr, j, j + 1); 38 | } 39 | } 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /01_Algo/06_Template/改写快排的方法求无序数组中的第k小.md: -------------------------------------------------------------------------------- 1 | # 改写快排的方法求无序数组中的第k小 2 | 3 | #快速排序 4 | 5 | --- 6 | 7 | 8 | ```java 9 | 10 | // 改写快排,时间复杂度O(N) 11 | public static int minKth2(int[] array, int k) { 12 | int[] arr = copyArray(array); 13 | return process2(arr, 0, arr.length - 1, k - 1); 14 | } 15 | 16 | public static int[] copyArray(int[] arr) { 17 | int[] ans = new int[arr.length]; 18 | for (int i = 0; i != ans.length; i++) { 19 | ans[i] = arr[i]; 20 | } 21 | return ans; 22 | } 23 | 24 | // arr 第k小的数 25 | // process2(arr, 0, N-1, k-1) 26 | // arr[L..R] 范围上,如果排序的话(不是真的去排序),找位于index的数 27 | // index [L..R] 28 | public static int process2(int[] arr, int L, int R, int index) { 29 | if (L == R) { // L = =R ==INDEX 30 | return arr[L]; 31 | } 32 | // 不止一个数 L + [0, R -L] 33 | int pivot = arr[L + (int) (Math.random() * (R - L + 1))]; 34 | 35 | // range[0] range[1] 36 | // L ..... R pivot 37 | // 0 1000 70...800 38 | int[] range = partition(arr, L, R, pivot); 39 | if (index >= range[0] && index <= range[1]) { 40 | return arr[index]; 41 | } else if (index < range[0]) { 42 | return process2(arr, L, range[0] - 1, index); 43 | } else { 44 | return process2(arr, range[1] + 1, R, index); 45 | } 46 | } 47 | 48 | public static int[] partition(int[] arr, int L, int R, int pivot) { 49 | int less = L - 1; 50 | int more = R + 1; 51 | int cur = L; 52 | while (cur < more) { 53 | if (arr[cur] < pivot) { 54 | swap(arr, ++less, cur++); 55 | } else if (arr[cur] > pivot) { 56 | swap(arr, cur, --more); 57 | } else { 58 | cur++; 59 | } 60 | } 61 | return new int[] { less + 1, more - 1 }; 62 | } 63 | 64 | public static void swap(int[] arr, int i1, int i2) { 65 | int tmp = arr[i1]; 66 | arr[i1] = arr[i2]; 67 | arr[i2] = tmp; 68 | } 69 | 70 | 71 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/数组L...R范围上的累加和计算代码.md: -------------------------------------------------------------------------------- 1 | # 数组L...R范围上的累加和计算代码 2 | 3 | #预处理数组 4 | 5 | --- 6 | 7 | 8 | ```java 9 | public static class RangeSum2 { 10 | 11 | private int[] preSum; 12 | 13 | public RangeSum2(int[] array) { 14 | int N = array.length; 15 | preSum = new int[N]; 16 | preSum[0] = array[0]; 17 | for (int i = 1; i < N; i++) { 18 | preSum[i] = preSum[i + 1] + array[i]; 19 | } 20 | } 21 | 22 | public int rangeSum(int L, int R) { 23 | return L == 0 ? preSum[R] : preSum[R] - preSum[L - 1]; 24 | } 25 | } 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /01_Algo/06_Template/数组中离给定K值最近的子数组累加和代码.md: -------------------------------------------------------------------------------- 1 | # 数组中离给定K值最近的子数组累加和代码 2 | 3 | --- 4 | 5 | 6 | ```java 7 | // 请返回arr中,求个子数组的累加和,是<=K的,并且是最大的。 8 | // 返回这个最大的累加和 9 | public static int getMaxLessOrEqualK(int[] arr, int K) { 10 | // 记录i之前的,前缀和,按照有序表组织 11 | TreeSet set = new TreeSet(); 12 | // 一个数也没有的时候,就已经有一个前缀和是0了 13 | set.add(0); 14 | 15 | int max = Integer.MIN_VALUE; 16 | int sum = 0; 17 | // 每一步的i,都求子数组必须以i结尾的情况下,求个子数组的累加和,是<=K的,并且是最大的 18 | for (int i = 0; i < arr.length; i++) { 19 | sum += arr[i]; // sum -> arr[0..i]; 20 | if (set.ceiling(sum - K) != null) { 21 | max = Math.max(max, sum - set.ceiling(sum - K)); 22 | } 23 | set.add(sum); // 当前的前缀和加入到set中去 24 | } 25 | return max; 26 | 27 | } 28 | 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /01_Algo/06_Template/枚举所有的i行到j行的情况.md: -------------------------------------------------------------------------------- 1 | # 枚举所有的i行到j行的情况 2 | 3 | 4 | --- 5 | 6 | - [[给定一个整型矩阵,返回子矩阵的最大累加和]] 7 | 8 | ```java 9 | public static int maxSum(int[][] m) { 10 | if (m == null || m.length == 0 || m[0].length == 0) { 11 | return 0; 12 | } 13 | int max = Integer.MIN_VALUE; 14 | int cur = 0; 15 | int[] s = null; 16 | for (int i = 0; i != m.length; i++) { // 开始的行号i 17 | s = new int[m[0].length]; // 18 | for (int j = i; j != m.length; j++) { // 结束的行号j,i~j行是我讨论的范围 19 | cur = 0; 20 | for (int k = 0; k != s.length; k++) { 21 | s[k] += m[j][k]; 22 | cur += s[k]; 23 | max = Math.max(max, cur); 24 | cur = cur < 0 ? 0 : cur; 25 | } 26 | } 27 | } 28 | return max; 29 | } 30 | 31 | 32 | ``` 33 | 34 | 35 | 36 | - [[返回数组中,有多少个独立的域]] 37 | 38 | ```java 39 | public static int largestComponentSize(int[] arr) { 40 | UnionFindSet1 set = new UnionFindSet1(arr.length); 41 | for (int i = 0; i < arr.length; i++) { 42 | for (int j = i + 1; j < arr.length; j++) { 43 | if (gcd(arr[i], arr[j]) != 1) { 44 | set.union(i, j); 45 | } 46 | } 47 | } 48 | return set.maxSize(); 49 | } 50 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/求数组中的逆序对数量代码.md: -------------------------------------------------------------------------------- 1 | # 求数组中的逆序对数量代码 2 | 3 | #归并排序 4 | 5 | --- 6 | 每个数右边有多少个数比他小, 所有比他小的数都能构成逆序对 7 | X作为右组姿态不产生逆序对 8 | X作为左组才产生逆序对 9 | 10 | 从右往左Merge, 相等的时候先拷贝右边的 11 | 12 | ```java 13 | public static void process(int[] originArr, int L, int R, int power, int[] record) { 14 | if (L == R) { 15 | return; 16 | } 17 | int mid = L + ((R - L) >> 1); 18 | process(originArr, L, mid, power - 1, record); 19 | process(originArr, mid + 1, R, power - 1, record); 20 | record[power] += merge(originArr, L, mid, R); 21 | } 22 | 23 | public static int merge(int[] arr, int L, int m, int r) { 24 | int[] help = new int[r - L + 1]; 25 | int i = 0; 26 | int p1 = L; 27 | int p2 = m + 1; 28 | int ans = 0; 29 | while (p1 <= m && p2 <= r) { 30 | ans += arr[p1] > arr[p2] ? (m - p1 + 1) : 0; 31 | help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]; 32 | } 33 | while (p1 <= m) { 34 | help[i++] = arr[p1++]; 35 | } 36 | while (p2 <= r) { 37 | help[i++] = arr[p2++]; 38 | } 39 | for (i = 0; i < help.length; i++) { 40 | arr[L + i] = help[i]; 41 | } 42 | return ans; 43 | } 44 | 45 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/求数组小和代码.md: -------------------------------------------------------------------------------- 1 | # 求数组小和代码 2 | 3 | --- 4 | 5 | 左组小, 拷贝左组, 产生小和, 看右组有多少个比左组当前大 6 | 右组拷贝不产生小和 7 | 相等时候先拷贝右组 ==> 一定要在右边找到一个大的才能结算 8 | 如果先拷贝左边的就求不出来了 9 | 10 | 一一个数左边比它小的数的小和 等于 11 | 新目标: 一个数右边有多少个数比他大的的数的和 12 | X 没有遭遇到新的右组(同一组内部不产生小和)之前不会有小和产生 13 | 14 | ```java 15 | public static int smallSum(int[] arr) { 16 | if (arr == null || arr.length < 2) { 17 | return 0; 18 | } 19 | return process(arr, 0, arr.length - 1); 20 | } 21 | 22 | // arr[L..R]既要排好序,也要求小和返回 23 | // 所有merge时,产生的小和,累加 24 | // 左 排序 merge 25 | // 右 排序 merge 26 | // merge 27 | public static int process(int[] arr, int l, int r) { 28 | if (l == r) { 29 | return 0; 30 | } 31 | // l < r 32 | int mid = l + ((r - l) >> 1); 33 | return 34 | process(arr, l, mid) 35 | + 36 | process(arr, mid + 1, r) 37 | + 38 | merge(arr, l, mid, r); 39 | } 40 | 41 | public static int merge(int[] arr, int L, int m, int r) { 42 | int[] help = new int[r - L + 1]; 43 | int i = 0; 44 | int p1 = L; 45 | int p2 = m + 1; 46 | int res = 0; 47 | while (p1 <= m && p2 <= r) { 48 | res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0; 49 | help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; 50 | } 51 | while (p1 <= m) { 52 | help[i++] = arr[p1++]; 53 | } 54 | while (p2 <= r) { 55 | help[i++] = arr[p2++]; 56 | } 57 | for (i = 0; i < help.length; i++) { 58 | arr[L + i] = help[i]; 59 | } 60 | return res; 61 | } 62 | 63 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/没有小括号的表达式结果计算.md: -------------------------------------------------------------------------------- 1 | # 没有小括号的表达式结果计算 2 | 3 | 4 | --- 5 | 6 | ```java 7 | public static int getNum(LinkedList que) { 8 | int res = 0; 9 | boolean add = true; 10 | String cur = null; 11 | int num = 0; 12 | while (!que.isEmpty()) { 13 | cur = que.pollFirst(); 14 | if (cur.equals("+")) { 15 | add = true; 16 | } else if (cur.equals("-")) { 17 | add = false; 18 | } else { 19 | num = Integer.valueOf(cur); 20 | res += add ? num : (-num); 21 | } 22 | } 23 | return res; 24 | } 25 | 26 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/荷兰国旗问题代码.md: -------------------------------------------------------------------------------- 1 | # 荷兰国旗问题代码 2 | 3 | #荷兰国旗问题 4 | 5 | --- 6 | 7 | 8 | ```java 9 | // arr[L...R] 玩荷兰国旗问题的划分,以arr[R]做划分值 10 | // arr[R] 11 | public static int[] netherlandsFlag(int[] arr, int L, int R) { 12 | if (L > R) { 13 | return new int[] { -1, -1 }; 14 | } 15 | if (L == R) { 16 | return new int[] { L, R }; 17 | } 18 | int less = L - 1; // < 区 右边界 19 | int more = R; // > 区 左边界 20 | int index = L; 21 | while (index < more) { 22 | if (arr[index] == arr[R]) { 23 | index++; 24 | } else if (arr[index] < arr[R]) { 25 | swap(arr, index++, ++less); 26 | } else { // > 27 | swap(arr, index, --more); 28 | } 29 | } 30 | // L...less less+1...more-1 more...R-1 R 31 | // L...less less+1.............more more+1... R 32 | swap(arr, more, R); 33 | return new int[] { less + 1, more }; 34 | } 35 | 36 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/计数排序.md: -------------------------------------------------------------------------------- 1 | # 计数排序 2 | 3 | #计数排序 4 | 5 | 6 | --- 7 | 8 | 计数排序要求,样本是整数,且范围比较窄 9 | 10 | 11 | ```java 12 | // only for 0~200 value 13 | public static void countSort(int[] arr) { 14 | if (arr == null || arr.length < 2) { 15 | return; 16 | } 17 | int max = Integer.MIN_VALUE; 18 | for (int i = 0; i < arr.length; i++) { 19 | max = Math.max(max, arr[i]); 20 | } 21 | int[] bucket = new int[max + 1]; 22 | for (int i = 0; i < arr.length; i++) { 23 | bucket[arr[i]]++; 24 | } 25 | int i = 0; 26 | for (int j = 0; j < bucket.length; j++) { 27 | while (bucket[j]-- > 0) { 28 | arr[i++] = j; 29 | } 30 | } 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /01_Algo/06_Template/选择排序.md: -------------------------------------------------------------------------------- 1 | # 选择排序 2 | 3 | #选择排序 4 | 5 | 6 | --- 7 | 8 | 过程: 9 | arr[0~N-1]范围上,找到最小值所在的位置,然后把最小值交换到0位置。 10 | arr[1~N-1]范围上,找到最小值所在的位置,然后把最小值交换到1位置。 11 | arr[2~N-1]范围上,找到最小值所在的位置,然后把最小值交换到2位置。 12 | … 13 | arr[N-1~N-1]范围上,找到最小值位置,然后把最小值交换到N-1位置。 14 | 15 | 估算: 16 | 很明显,如果arr长度为N,每一步常数操作的数量,如等差数列一般 17 | 所以,总的常数操作数量 = a*(N^2) + b*N + c (a、b、c都是常数) 18 | 19 | 所以选择排序的时间复杂度为O(N^2)。 20 | 21 | --- 22 | 23 | ```java 24 | public static void selectionSort(int[] arr) { 25 | if (arr == null || arr.length < 2) { 26 | return; 27 | } 28 | // 0 ~ N-1 29 | // 1 ~ N-1 30 | // 2 ~ N-1 31 | for (int i = 0; i < arr.length - 1; i++) { // i ~ N-1 32 | // 最小值在哪个位置上 i~n-1 33 | int minIndex = i; 34 | for (int j = i + 1; j < arr.length; j++) { // i ~ N-1 上找最小值的下标 35 | minIndex = arr[j] < arr[minIndex] ? j : minIndex; 36 | } 37 | if (minIndex != i) { 38 | swap(arr, i, minIndex); 39 | } 40 | } 41 | } 42 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/长方形中任意矩阵的累加和代码.md: -------------------------------------------------------------------------------- 1 | # 长方形中任意矩阵的累加和代码 2 | #预处理数组 3 | 4 | --- 5 | 6 | ```java 7 | // record[i][j] 左上角点0,0, 有效角点i、j , 该矩阵的累加和 8 | public static int[][] generateSumRecord(int[][] matrix) { 9 | int row = matrix.length; 10 | int col = matrix[0].length; 11 | int[][] record = new int[row][col]; 12 | record[0][0] = matrix[0][0]; 13 | for (int i = 1; i < row; i++) { 14 | record[i][0] = record[i - 1][0] + matrix[i][0]; 15 | } 16 | for (int j = 1; j < col; j++) { 17 | record[0][j] = record[0][j - 1] + matrix[0][j]; 18 | } 19 | for (int i = 1; i < row; i++) { 20 | for (int j = 1; j < col; j++) { 21 | record[i][j] = record[i][j - 1] + record[i - 1][j] - record[i - 1][j - 1] 22 | + matrix[i][j]; 23 | } 24 | } 25 | return record; 26 | } 27 | 28 | ``` -------------------------------------------------------------------------------- /01_Algo/06_Template/随机生成双向链表.md: -------------------------------------------------------------------------------- 1 | # 随机生成双向链表 2 | 3 | 4 | --- 5 | 6 | 7 | ```java 8 | public static class DoubleNode { 9 | public int value; 10 | public DoubleNode last; 11 | public DoubleNode next; 12 | 13 | public DoubleNode(int data) { 14 | value = data; 15 | } 16 | } 17 | 18 | public static DoubleNode generateRandomDoubleList(int len, int value) { 19 | int size = (int) (Math.random() * (len + 1)); 20 | if (size == 0) { 21 | return null; 22 | } 23 | size--; 24 | DoubleNode head = new DoubleNode((int) (Math.random() * (value + 1))); 25 | DoubleNode pre = head; 26 | while (size != 0) { 27 | DoubleNode cur = new DoubleNode((int) (Math.random() * (value + 1))); 28 | pre.next = cur; 29 | cur.last = pre; 30 | pre = cur; 31 | size--; 32 | } 33 | return head; 34 | } 35 | 36 | 37 | // 要求无环,有环别用这个函数 38 | public static boolean checkDoubleListEqual(DoubleNode head1, DoubleNode head2) { 39 | boolean null1 = head1 == null; 40 | boolean null2 = head2 == null; 41 | if (null1 && null2) { 42 | return true; 43 | } 44 | if (null1 ^ null2) { 45 | return false; 46 | } 47 | if (head1.last != null || head2.last != null) { 48 | return false; 49 | } 50 | DoubleNode end1 = null; 51 | DoubleNode end2 = null; 52 | while (head1 != null && head2 != null) { 53 | if (head1.value != head2.value) { 54 | return false; 55 | } 56 | end1 = head1; 57 | end2 = head2; 58 | head1 = head1.next; 59 | head2 = head2.next; 60 | } 61 | if (head1 != null || head2 != null) { 62 | return false; 63 | } 64 | while (end1 != null && end2 != null) { 65 | if (end1.value != end2.value) { 66 | return false; 67 | } 68 | end1 = end1.last; 69 | end2 = end2.last; 70 | } 71 | return end1 == null && end2 == null; 72 | } 73 | 74 | ``` -------------------------------------------------------------------------------- /01_Algo/GenerateForTest/ACMMod.md: -------------------------------------------------------------------------------- 1 | # 整体 2 | 3 | ```java 4 | import java.util.*; 5 | public class Main { 6 | public static void main(String[] args) { 7 | Scanner sc = new Scanner(System.in); 8 | int num = sc.nextInt();//num个测试样例 9 | for (int i = 0; i < num; i++) { 10 | //每个测试样例处理的模式 11 | //赋值给变量 12 | //......coding...... 13 | sysOut(args, args......) 14 | } 15 | } 16 | public static void sysOut(args, args......) { 17 | //......coding...... 18 | System.out.println(ans); 19 | } 20 | } 21 | ``` 22 | 23 | # 数组输入(分隔符为空格) 24 | 25 | 直接获取数量 => 循环内输入 26 | 27 | ```java 28 | Scanner sc = new Scanner(System.in); 29 | int N = sc.nextInt(); 30 | int[] numsN = new int[N]; 31 | for (int j = 0; j < N; j++) { 32 | numsN[j] = sc.nextInt(); 33 | } 34 | ``` 35 | 36 | # 数组输入(分隔符为,) 37 | 38 | 先用 **next().toString()** 获取输入字符串 39 | 40 | 然后用 **str.split(",")** 分割为字符传数组 41 | 42 | 然后用 **Integer.parseInt(arr[i])** 把字符串转化为int 43 | 44 | ```java 45 | // 例如: 输入1, 2, 3, 4, 5 46 | Scanner sc = new Scanner(System.in); 47 | String str = scanner.next().toString(); 48 | String[] arr = str.split(", "); 49 | int[] nums = new int[arr.length]; 50 | for (int i = 0; i < arr.length; i++) { 51 | nums[i] = Integer.parseInt(arr[i]); 52 | } 53 | //得到实际数组nums 54 | ``` 55 | 56 | -------------------------------------------------------------------------------- /01_Algo/GenerateForTest/Test.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 生成定长一维数组 4 | 5 | ```java 6 | // for test 7 | public static int[] generateArray(int len, int max) { 8 | int[] ans = new int[(int) (Math.random() * len) + 1]; 9 | for (int i = 0; i < ans.length; i++) { 10 | ans[i] = (int) (Math.random() * max); 11 | } 12 | return ans; 13 | } 14 | ``` 15 | 16 | # 生成随机长度一维数组 17 | 18 | ```java 19 | // for test 20 | public static int[] generateRandomArray(int maxSize, int maxValue) { 21 | int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; 22 | for (int i = 0; i < arr.length; i++) { 23 | arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); 24 | } 25 | return arr; 26 | } 27 | ``` 28 | 29 | # 生成二维数组 & 打印二维数组 30 | 31 | ```java 32 | public static int[][] generateArray(int row, int col, double maxValue) { 33 | int[][] ans = new int[row][col]; 34 | for (int i = 0; i < row; i++) { 35 | for (int j = 0; j < col; j++) { 36 | ans[i][j] = (int) (Math.random() * maxValue); 37 | } 38 | } 39 | return ans; 40 | } 41 | 42 | public static void printArray(int[][] array) { 43 | for (int[] ints : array) { 44 | int iMax = ints.length - 1; 45 | StringBuilder b = new StringBuilder(); 46 | b.append('['); 47 | for (int i = 0; i <= iMax; i++) { 48 | if (ints[i] < 10) b.append(" " + ints[i]); 49 | else b.append(ints[i]); 50 | if (i == iMax) b.append(']').toString(); 51 | else b.append(", "); 52 | } 53 | System.out.println(b); 54 | } 55 | } 56 | ``` -------------------------------------------------------------------------------- /01_Algo/Tips/递归设计原则.md: -------------------------------------------------------------------------------- 1 | # 面试中设计暴力递归过程的原则 2 | 3 | 1)每一个可变参数的类型,一定不要比int类型更加复杂 4 | 5 | 2)原则1)可以违反,让类型突破到一维线性结构,那必须是单一可变参数 6 | 7 | 3)如果发现原则1)被违反,但不违反原则2),只需要做到记忆化搜索即可 8 | 9 | 4)可变参数的个数,能少则少 10 | 11 | 12 | 13 | # 知道了面试中设计暴力递归过程的原则,然后呢? 14 | 15 | 一定要逼自己**==找到不违反原则情况下的暴力尝试==**! 16 | 17 | 如果你找到的暴力尝试,不符合原则,马上舍弃!找新的! 18 | 19 | 如果某个题目突破了设计原则,一定极难极难,面试中出现概率低于5%! 20 | 21 | 22 | 23 | # 常见的4种尝试模型 24 | 25 | 1)从左往右的尝试模型 26 | 27 | 2)范围上的尝试模型 28 | 29 | 3)多样本位置全对应的尝试模型 30 | 31 | 4)寻找业务限制的尝试模型 32 | 33 | 34 | 35 | # 暴力递归到动态规划的套路 36 | 37 | 1)你已经有了一个不违反原则的暴力递归,而且的确存在解的重复调用 38 | 39 | 2)找到哪些参数的变化会影响返回值,对每一个列出变化范围 40 | 41 | 3)参数间的所有的组合数量,意味着表大小 42 | 43 | 4)记忆化搜索的方法就是傻缓存,非常容易得到 44 | 45 | 5)规定好严格表的大小,分析位置的依赖顺序,然后从基础填写到最终解 46 | 47 | 6)对于有枚举行为的决策过程,进一步优化 48 | 49 | 50 | 51 | # 动态规划的进一步优化 52 | 53 | 1)空间压缩 54 | 55 | 2)状态化简(状态压缩) 56 | 57 | 3)四边形不等式 58 | 59 | 4)其他优化技巧 -------------------------------------------------------------------------------- /01_Algo/Trick/0x3f3f3f3f.md: -------------------------------------------------------------------------------- 1 | 在算法竞赛中,我们常常需要用到设置一个常量用来代表**“无穷大”**。 2 | 3 | 比如对于int类型的数,有的人会采用INT_MAX,即0x7fffffff作为无穷大。但是以INT_MAX为无穷大常常面临一个问题,即加一个其他的数会溢出。 4 | 5 | 而这种情况在**动态规划**,或者其他一些递推的算法中常常出现,很有可能导致算法出问题。 6 | 7 | 所以在算法竞赛中,我们常采用0x3f3f3f3f来作为无穷大。0x3f3f3f3f主要有如下好处: 8 | 9 | - 0x3f3f3f3f的十进制为1061109567,和INT_MAX一个数量级,即10^9数量级,而一般场合下的数据都是小于10^9的。 10 | - 0x3f3f3f3f * 2 = 2122219134,无穷大相加依然不会溢出。 11 | - 可以使用memset(array, 0x3f, sizeof(array))来为数组设初值为0x3f3f3f3f,因为这个数的每个字节都是0x3f。 -------------------------------------------------------------------------------- /01_Algo/weekly/22.02.18.md: -------------------------------------------------------------------------------- 1 | # 最短花费题 2 | 3 | # 吃橘子题 4 | 5 | -------------------------------------------------------------------------------- /01_Algo/weekly/skill.md: -------------------------------------------------------------------------------- 1 | # MRQ方法 不可修改的范围最大最小值查询 易于实现 2 | 3 | 比线段树的范围查询 **简单多了** 代码量也少很多 4 | 5 | ![image-20220629171007541](https://s2.loli.net/2022/06/29/g6iDJpbVRfhYMyx.png) 6 | 7 | 填表的思路就是 找到上一轮 的两个位置的值a,b 而max(a, b) => 就是这一轮的区间最大值 8 | 9 | ```java 10 | public static class RMQ { 11 | public int[][] max; 12 | 13 | // 下标一定要从1开始,没有道理!就是约定俗成! 14 | public RMQ(int[] arr) { 15 | // 长度! 16 | int n = arr.length; 17 | // 2的几次方,可以拿下n 18 | int k = power2(n); 19 | // n*logn 20 | max = new int[n + 1][k + 1]; 21 | for (int i = 1; i <= n; i++) { 22 | // i 0:从下标i开始,往下连续的2的0次方个数,中,最大值 23 | // 1...1个 24 | // 2...1个 25 | // 3...1个 26 | max[i][0] = arr[i - 1]; 27 | } 28 | for (int j = 1; (1 << j) <= n; j++) { 29 | // i...连续的、2的1次方个数,这个范围,最大值 30 | // i...连续的、2的2次方个数,这个范围,最大值 31 | // i...连续的、2的3次方个数,这个范围,最大值 32 | for (int i = 1; i + (1 << j) - 1 <= n; i++) { 33 | // max[10][3] 34 | // 下标10开始,连续的8个数,最大值是多少 35 | // 1) max[10][2] 36 | // 2) max[14][2] 37 | max[i][j] = Math.max( 38 | max[i][j - 1], 39 | max[i + (1 << (j - 1))][j - 1]); 40 | } 41 | } 42 | } 43 | 44 | public int max(int l, int r) { 45 | // l...r -> r - l + 1 -> 2的哪个次方最接近它! 46 | int k = power2(r - l + 1); 47 | return Math.max(max[l][k], max[r - (1 << k) + 1][k]); 48 | } 49 | 50 | private int power2(int m) { 51 | int ans = 0; 52 | while ((1 << ans) <= (m >> 1)) { 53 | ans++; 54 | } 55 | return ans; 56 | } 57 | 58 | } 59 | ``` 60 | 61 | -------------------------------------------------------------------------------- /02_DesignPatterns/03_Factory.md: -------------------------------------------------------------------------------- 1 | ## 简单工厂 2 | 3 | ```java 4 | /** 5 | * 简单工厂的可扩展性不好 6 | */ 7 | public class SimpleVehicleFactory { 8 | public Car createCar() { 9 | //before processing 10 | return new Car(); 11 | } 12 | 13 | public Broom createBroom() { 14 | return new Broom(); 15 | } 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## 工厂方法-产品维度扩展 22 | 23 | ```java 24 | public interface Moveable { 25 | void go(); 26 | } 27 | ``` 28 | 29 | ```java 30 | public class Car implements Moveable { 31 | //Car类 规定了car的规范和长相 实现了接口(Moveable) 32 | public void go() { 33 | System.out.println("Car go wuwuwuwuw...."); 34 | } 35 | } 36 | ``` 37 | 38 | ```java 39 | public class CarFactory { 40 | public Moveable create() { 41 | System.out.println("a car created!"); 42 | //制造一个车 让return返回一个Moveable对象 43 | return new Car(); 44 | } 45 | } 46 | ``` 47 | 48 | ```java 49 | public class Main { 50 | public static void main(String[] args) { 51 | //new一个CarFactory, 并执行create方法 52 | Moveable m = new CarFactory().create(); 53 | m.go(); 54 | } 55 | } 56 | ``` 57 | 58 | ## 抽象工厂-产品族扩展 59 | 60 | 意义: 方便扩展不同类型的实际工厂 61 | 62 | 63 | 64 | ![image-20211103102607040](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211103102607040.png) 65 | 66 | ## bean工厂 67 | 68 | ??? 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /02_DesignPatterns/04_Mediator.md: -------------------------------------------------------------------------------- 1 | # 调停者-Mediator 2 | 3 | 比如, tank会撞tank, tank撞墙, 子弹撞墙, 子弹撞子弹, 子弹撞tank...... 4 | 5 | 太复杂了, 所以需要一个大管家管理这么多逻辑 6 | 7 | 8 | 9 | 对外的Facade 10 | 11 | ![image-20211103105052401](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211103105052401.png) 12 | 13 | --- 14 | 15 | 对内的 16 | 17 | ![image-20211103105326625](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211103105326625.png) 18 | 19 | 20 | 21 | **消息中间件(MQ)** 22 | 23 | 24 | 25 | 26 | 27 | ## 游戏问题解决 28 | 29 | TF => Facade 30 | 31 | Frame => 展示 32 | 33 | GameModel => 内部逻辑计算 34 | 35 | 36 | 37 | GameObject 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /02_DesignPatterns/05_Decorator.md: -------------------------------------------------------------------------------- 1 | # 装饰器 2 | 3 | ![image-20211103110719025](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211103110719025.png) 4 | 5 | 6 | 7 | 8 | 9 | ![image-20211103111024061](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211103111024061.png) 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /02_DesignPatterns/07_Observer.md: -------------------------------------------------------------------------------- 1 | # 观察者模式 2 | 3 | Obverser 4 | 5 | Listener 6 | 7 | Hook 8 | 9 | Callback 10 | 11 | 12 | 13 | 具体实现 14 | 15 | -------------------------------------------------------------------------------- /02_DesignPatterns/08_Composite.md: -------------------------------------------------------------------------------- 1 | # Composite 组合-树状结构 2 | 3 | ![image-20211103205728627](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211103205728627.png) 4 | 5 | ```java 6 | abstract class Node { 7 | abstract public void p(); 8 | } 9 | 10 | class LeafNode extends Node { 11 | String content; 12 | public LeafNode(String content) {this.content = content;} 13 | 14 | @Override 15 | public void p() { 16 | System.out.println(content); 17 | } 18 | } 19 | 20 | class BranchNode extends Node { 21 | List nodes = new ArrayList<>(); 22 | 23 | String name; 24 | public BranchNode(String name) {this.name = name;} 25 | 26 | @Override 27 | public void p() { 28 | System.out.println(name); 29 | } 30 | 31 | public void add(Node n) { 32 | nodes.add(n); 33 | } 34 | } 35 | 36 | 37 | public class Main { 38 | public static void main(String[] args) { 39 | 40 | BranchNode root = new BranchNode("root"); 41 | BranchNode chapter1 = new BranchNode("chapter1"); 42 | BranchNode chapter2 = new BranchNode("chapter2"); 43 | Node r1 = new LeafNode("r1"); 44 | Node c11 = new LeafNode("c11"); 45 | Node c12 = new LeafNode("c12"); 46 | BranchNode b21 = new BranchNode("section21"); 47 | Node c211 = new LeafNode("c211"); 48 | Node c212 = new LeafNode("c212"); 49 | 50 | root.add(chapter1); 51 | root.add(chapter2); 52 | root.add(r1); 53 | chapter1.add(c11); 54 | chapter1.add(c12); 55 | chapter2.add(b21); 56 | b21.add(c211); 57 | b21.add(c212); 58 | 59 | tree(root, 0); 60 | 61 | } 62 | 63 | static void tree(Node b, int depth) { 64 | for(int i=0; i WarmGift ColdGift WildGift 25 | GiftImpl -> Flower Ring Car 26 | ``` 27 | 28 | ![image-20211105212559928](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211105212559928.png) 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | ```java 37 | public abstract class Gift { 38 | GiftImpl impl; 39 | } 40 | ``` 41 | 42 | ```java 43 | public class GiftImpl { 44 | } 45 | ``` 46 | 47 | ```java 48 | public class WarmGift extends Gift { 49 | public WarmGift(GiftImpl impl) { 50 | this.impl = impl; 51 | } 52 | } 53 | ``` 54 | 55 | ```java 56 | public class WildGift extends Gift { 57 | public WildGift(GiftImpl impl) { 58 | this.impl = impl; 59 | } 60 | } 61 | ``` 62 | 63 | ### 实体 64 | 65 | ```java 66 | public class Book extends GiftImpl { 67 | } 68 | ``` 69 | 70 | ```java 71 | public class Flower extends GiftImpl { 72 | } 73 | ``` 74 | 75 | ### GG & MM 76 | 77 | ```java 78 | public class MM { 79 | String name; 80 | } 81 | ``` 82 | 83 | ```java 84 | public class GG { 85 | public void chase(MM mm) { 86 | Gift g = new WarmGift(new Flower()); 87 | give(mm, g); 88 | } 89 | 90 | public void give(MM mm, Gift g) { 91 | System.out.println(g + "gived!"); 92 | } 93 | 94 | } 95 | ``` 96 | 97 | 98 | 99 | ==gift 就可以直接通过下面这种方式来创建== 100 | 101 | ```java 102 | Gift g = new WarmGift(new Flower()); 103 | ``` 104 | 105 | 来创建各种各样的类 106 | 107 | -------------------------------------------------------------------------------- /02_DesignPatterns/16_Command.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Command(封装命令) 4 | 5 | **别名: Action / Transaction** 6 | 7 | 结合cor实现undo 8 | 9 | 10 | 11 | ![image-20211105215212046](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211105215212046.png) 12 | 13 | 14 | 15 | ## 结合运用 16 | 17 | **宏命令** 18 | 19 | Command + Composite 20 | 21 | **多次undo** 22 | 23 | Command + ChainOfResponsibility 24 | 25 | **transaction回滚** 26 | 27 | Command + Memento 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /02_DesignPatterns/17_ProtoType.md: -------------------------------------------------------------------------------- 1 | # Prototype-原型模式 2 | 3 | Object.clone ( ) 4 | 5 | ![image-20211105220324741](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211105220324741.png) 6 | 7 | 8 | 9 | ![image-20211105220403230](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211105220403230.png) 10 | 11 | 12 | 13 | 14 | 15 | 深克隆 里面的**属性类也得实现**Cloneable **==使得P2的location引用有属于自己的引用!!!==** 16 | 17 | 18 | 19 | ==**String不需要深克隆.**== 20 | 21 | 两个引用指向同一个常量池中的string, 22 | 23 | 如果一个引用的string改了, 另一个引用代表的不会变, 因为修改引用那个会新开一个常量池条目 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /02_DesignPatterns/18_Memento.md: -------------------------------------------------------------------------------- 1 | # Memento-备忘录 2 | 3 | 记录快照(瞬时状态) 4 | 5 | 存盘 6 | 7 | 存档 8 | 9 | 10 | 11 | **注意区分 Command** 12 | 13 | Menento是直接跳跃到目的 14 | 15 | Command是一步一步redo到目的地 16 | 17 | 18 | 19 | ## tank 存档 20 | 21 | GameObject实现Serializable 22 | 23 | 24 | 25 | 法1° transient 修饰符 表示这个对象不序列化 ( load出来就不包含了 ) 26 | 27 | 法2° 把TankFireObserver 也继承 Serializable 28 | 29 | 30 | 31 | save() 方法存档 ==> ObjectOutputStream( new FileOutputStream ) 32 | 33 | 34 | 35 | load就把save反过来就行啦 ! 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /02_DesignPatterns/19_TemplateMethod.md: -------------------------------------------------------------------------------- 1 | # TemplateMethod模板方法-勾子函数 2 | 3 | ![image-20211107201550733](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107201550733.png) 4 | 5 | 6 | 7 | ![image-20211107201704355](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107201704355.png) 8 | 9 | 10 | 11 | ```java 12 | public class Main { 13 | public static void main(String[] args) { 14 | F f = new C1(); 15 | f.m(); 16 | } 17 | } 18 | 19 | abstract class F { 20 | public void m() { 21 | op1(); 22 | op2(); 23 | } 24 | 25 | abstract void op1(); 26 | abstract void op2(); 27 | } 28 | 29 | class C1 extends F { 30 | 31 | @Override 32 | void op1() { 33 | System.out.println("op1"); 34 | } 35 | 36 | @Override 37 | void op2() { 38 | System.out.println("op2"); 39 | } 40 | } 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /02_DesignPatterns/20_State.md: -------------------------------------------------------------------------------- 1 | # State状态模式 2 | 3 | ![image-20211107202612905](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107202612905.png) 4 | 5 | 6 | 7 | GOF.eg 8 | 9 | 10 | 11 | ```java 12 | public abstract class MMState { 13 | abstract void smile(); 14 | abstract void cry(); 15 | abstract void say(); 16 | } 17 | ``` 18 | 19 | 20 | 21 | ```java 22 | public class MMHappyState extends MMState { 23 | @Override 24 | void smile() { 25 | System.out.println("happy smile"); 26 | } 27 | 28 | @Override 29 | void cry() { 30 | System.out.println("happy cry" ); 31 | } 32 | 33 | @Override 34 | void say() { 35 | System.out.println("happy say"); 36 | } 37 | } 38 | ``` 39 | 40 | 41 | 42 | ```java 43 | public class MMNervousState extends MMState { 44 | @Override 45 | void smile() { 46 | } 47 | 48 | @Override 49 | void cry() { 50 | } 51 | 52 | @Override 53 | void say() { 54 | } 55 | } 56 | ``` 57 | 58 | 59 | 60 | ```java 61 | public class MMSadState extends MMState { 62 | @Override 63 | void smile() { 64 | } 65 | 66 | @Override 67 | void cry() { 68 | } 69 | 70 | @Override 71 | void say() { 72 | } 73 | } 74 | ``` 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ![image-20211107203848403](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107203848403.png) 83 | 84 | CarState 85 | 86 | Car 87 | 88 | 89 | 90 | 91 | 92 | ## 有限状态机 StateMachine 93 | 94 | ![image-20211107203402563](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107203402563.png) 95 | -------------------------------------------------------------------------------- /02_DesignPatterns/21_Interpreter.md: -------------------------------------------------------------------------------- 1 | # Intepreter脚本语言解释器 2 | 3 | -------------------------------------------------------------------------------- /02_DesignPatterns/22_总结.md: -------------------------------------------------------------------------------- 1 | # 总结 2 | 3 | ## 四人帮分类 4 | 5 | ![image-20211107204809687](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107204809687.png) 6 | 7 | 8 | 9 | ## 设计指导思想 10 | 11 | ![image-20211107205116805](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107205116805.png) 12 | 13 | 14 | 15 | ## 单一职责原则 16 | 17 | ![image-20211107205334339](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107205334339.png) 18 | 19 | 20 | 21 | ## 开闭原则 22 | 23 | ![image-20211107205346212](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107205346212.png) 24 | 25 | 26 | 27 | ## 里氏替换原则-子类能完全替代父类 28 | 29 | ![image-20211107205412992](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107205412992.png) 30 | 31 | 32 | 33 | ## 依赖倒置原则-面向接口原则 34 | 35 | ![image-20211107205653168](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107205653168.png) 36 | 37 | 38 | 39 | ## 接口隔离原则 40 | 41 | ![image-20211107205743908](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107205743908.png) 42 | 43 | 44 | 45 | ## 迪米特法则 46 | 47 | ![image-20211107205932198](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107205932198.png) 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ## 总结 56 | 57 | ![image-20211107205907705](https://raw.githubusercontent.com/handsomeyi/Pics/master/image-20211107205907705.png) 58 | 59 | -------------------------------------------------------------------------------- /03_JavaWeb/01_SpringBoot/SpringBootNote.md: -------------------------------------------------------------------------------- 1 | # 1 2 | 3 | ### 基本结构 4 | 5 | spring-boot-starter-parent 依赖 spring-boot-dependencies 6 | spring-boot-dependencies里面有< properties >标签 存了各种组件工具的版本 的 默认规定版本. 7 | 8 | 各种jpa jdbc mongodb redis等工具 的依赖, 都能在官方文档里面找到. 9 | 10 | springboot内嵌tomcat. 11 | 12 | Controller包里面的 注解@Controller = @RestController 表示是一个Controller 13 | @RequestMapping("/hello") 表示一个请求路径 应该做的方法. 14 | 15 | @SpringBootApplication表示启动类, ComponentScan的就是当前目录. 16 | 如果启动类放错了位置, 就用@ComponentScan("/启动类应该的位置"). 17 | 18 | ### 部署 19 | 20 | 通过maven的Lifecycle的package生成jar包, 服务器上java -jar [path]运行 21 | 22 | ### SpringBoot配置文件 23 | 24 | 用yaml比较好 25 | 26 | 配置文件优先级 27 | ![image.png](https://s2.loli.net/2022/07/04/gqGvXC3S14rc7OZ.png) 28 | 29 | ### 注解 30 | 31 | 注解就是一个Java5开始引入的特性, 辅助作用, Annontation, 将任何信息或metadata与程序关联. 32 | 33 | 注解的原理就是**反射**. => 反射应用原理 => Java SPI 34 | **SPI**: 是JDK内置的一种服务提供发现机制, SPI是一种动态替换发现的机制, 比如有个接口, 想运行时动态的给它添加实现, 你只需要添加一个实现. 35 | 36 | ![image.png](https://s2.loli.net/2022/07/04/hfsREWTBKQe4GIz.png) 37 | 38 | **元注解** 39 | 40 | ![image.png](https://s2.loli.net/2022/07/04/8BnjGtWeTUZxP2E.png) 41 | 42 | # 2 43 | 44 | ### 内嵌Servlet容器支持 45 | 46 | SpringBoot还能用Servlet, 虽然一般用不上, 因为Boot自动就会给你实现. 47 | 但是某些场景下还是要自己写Servlet, 例如用Druid数据源的时候得用. 48 | 就是将自定义的Servlet添加到SpringBoot容器中. 49 | 50 | 在启动类上添加@ServletComponentScan表示我要添加Servlet. 51 | @WebServlet(name = "myServlet",urlPatterns = "/srv") 表示自定义的Servlet类. 52 | 53 | 详细步骤在原始笔记. 54 | 55 | # 4 56 | 57 | ### 启动过程 58 | 59 | 应用入口SpringBootApplication 60 | 61 | ```java 62 | @SpringBootApplication 63 | public class StartupApplication { 64 | public static void main(String[] args) { 65 | SpringApplication.run(StartupApplication.class, args); 66 | } 67 | } 68 | ``` 69 | 70 | ```java 71 | // 为什么是用 Class.run 而不是 new Class().run 呢? 72 | 静态成员***通过实例对象访问, 显示通过类实例而不是类本身调用方法和属性。 73 | 74 | 现有一个类Test,有静态方法methods和静态属性fields。 75 | 对于静态变量或方法,推荐使用的方式是Test.fields,而不是new Test().fields。 76 | 当然,使用this.fields也是不行的!因为this也指向一个实例对象。 77 | 如果出现以上告警,那一定是对于java不推荐的方式使用了静态元素。 78 | // 分析: 79 | 可能是考虑到实例会被回收 80 | ``` 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 监听器的启动 (观察者模式) -------------------------------------------------------------------------------- /03_JavaWeb/04_SpringBoot.md: -------------------------------------------------------------------------------- 1 | # spring-boot-starter-parent 2 | 3 | ```xml 4 | 5 | org.springframework.boot 6 | spring-boot-starter-parent 7 | 2.3.12.RELEASE 8 | 9 | 10 | ``` 11 | 12 | Maven用户可以继承[spring-boot](https://so.csdn.net/so/search?from=pc_blog_highlight&q=spring-boot)-starter-parent项目,来获取最佳依赖。这个父项目提供了以下几个功能: 13 | 14 | - 默认Java 1.6编译 15 | - UTF-8编码格式 16 | - 依赖管理部分,可让你对公共依赖省略version标签。继承自spring-boot-dependencies POM。 17 | - 良好的资源过滤 18 | - 良好的插件配置s 19 | - 对于application.properties和application.yml包括profile-specific文件,良好的资源过滤 20 | 21 | 22 | 23 | 24 | 25 | # 主程序和注解 26 | 27 | ```java 28 | @SpringBootConfiguration 29 | @EnableAutoConfiguration 30 | @ComponentScan( 31 | excludeFilters = {@Filter( 32 | type = FilterType.CUSTOM, 33 | classes = {TypeExcludeFilter.class} 34 | ), @Filter( 35 | type = FilterType.CUSTOM, 36 | classes = {AutoConfigurationExcludeFilter.class} 37 | )} 38 | ) 39 | public @interface SpringBootApplication { 40 | // ...... 41 | } 42 | ``` 43 | 44 | ## @SpringBootApplication 45 | 46 | 这个类是SpringBoot的主配置, 运行这个类的main方法来启动 47 | 48 | ## @ComponentScan 49 | 50 | 自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中 51 | 52 | ## @SpringBootConfiguration 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ### Controller 61 | 62 | Controller 应该只负责 跳转 参数 取数f据 63 | 64 | 其他的复杂业务应该放到service里面 65 | 66 | -------------------------------------------------------------------------------- /03_JavaWeb/Mybatis核心流程图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/03_JavaWeb/Mybatis核心流程图.png -------------------------------------------------------------------------------- /03_JavaWeb/indexTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/03_JavaWeb/indexTree.png -------------------------------------------------------------------------------- /04_JUC/BytecodeInstruction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/04_JUC/BytecodeInstruction.png -------------------------------------------------------------------------------- /04_JUC/JUC_1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ==多线程与高并发== 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /04_JUC/JVM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/04_JUC/JVM.png -------------------------------------------------------------------------------- /04_JUC/JVM脑图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/04_JUC/JVM脑图.png -------------------------------------------------------------------------------- /04_JUC/System_IO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/04_JUC/System_IO.png -------------------------------------------------------------------------------- /05_JVM/Test.md: -------------------------------------------------------------------------------- 1 | # OopMap 2 | 在HotSpot中,对象的类型信息里有记录自己的OopMap,记录了在该类型的对象内什么偏移量上是什么类型的数据。所以从对象开始向外的扫描可以是准确的;这些数据是在类加载过程中计算得到的。 3 | 4 | 可以把oopMap简单理解成是调试信息。 在源代码里面每个变量都是有类型的,但是编译之后的代码就只有变量在栈上的位置了。oopMap就是一个附加的信息,告诉你栈上哪个位置本来是个什么东西。 这个信息是在JIT编译时跟机器码一起产生的。因为只有编译器知道源代码跟产生的代码的对应关系。 每个方法可能会有好几个oopMap,就是根据safepoint把一个方法的代码分成几段,每一段代码一个oopMap,作用域自然也仅限于这一段代码。 循环中引用多个对象,肯定会有多个变量,编译后占据栈上的多个位置。那这段代码的oopMap就会包含多条记录。 5 | 6 | 每个被JIT编译过后的方法也会在一些特定的位置记录下OopMap, 7 | 记录了执行到该方法的某条指令的时候,栈上和寄存器里哪些位置是引用。 8 | 这样GC在扫描栈的时候就会查询这些OopMap就知道哪里是引用了。这些特定的位置主要在: 9 | **1、循环的末尾 10 | 2、方法临返回前 / 调用方法的call指令后 11 | 3、可能抛异常的位置** 12 | 13 | 这种位置被称为“安全点”(safepoint)。之所以要选择一些特定的位置来记录OopMap,是因为如果对每条指令(的位置)都记录OopMap的话,这些记录就会比较大,那么空间开销会显得不值得。选用一些比较关键的点来记录就能有效的缩小需要记录的数据量,但仍然能达到区分引用的目的。因为这样,HotSpot中GC不是在任意位置都可以进入,而只能在safepoint处进入。 14 | 而仍然在解释器中执行的方法则可以通过解释器里的功能自动生成出OopMap出来给GC用。 15 | 16 | 平时这些OopMap都是压缩了存在内存里的;在GC的时候才按需解压出来使用。  17 | HotSpot是用“解释式”的方式来使用OopMap的,每次都循环变量里面的项来扫描对应的偏移量。 18 | 19 | 对Java线程中的**JNI方法**,它们既不是由JVM里的解释器执行的,也不是由JVM的JIT编译器生成的,所以会缺少OopMap信息。那么GC碰到这样的栈帧该如何维持准确性呢?  20 | HotSpot的解决方法是:所有经过JNI调用边界(调用JNI方法传入的参数、从JNI方法传回的返回值)的引用都必须用“句柄”(handle)包装起来。JNI需要调用[Java](http://lib.csdn.net/base/java "Java 知识库") API的时候也必须自己用句柄包装指针。在这种实现中,JNI方法里写的“jobject”实际上不是直接指向对象的指针,而是先指向一个句柄,通过句柄才能间接访问到对象。这样在扫描到JNI方法的时候就不需要扫描它的栈帧了——只要扫描句柄表就可以得到所有从JNI方法能访问到的GC堆里的对象。 21 | 但这也就意味着调用JNI方法会有句柄的包装/拆包装的开销,是导致JNI方法的调用比较慢的原因之一。 -------------------------------------------------------------------------------- /06_Middleware/01_Redis/01Redis前无古人后无来者.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/06_Middleware/01_Redis/01Redis前无古人后无来者.jpg -------------------------------------------------------------------------------- /06_Middleware/01_Redis/02REDIS集群知识点.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/06_Middleware/01_Redis/02REDIS集群知识点.jpg -------------------------------------------------------------------------------- /06_Middleware/01_Redis/ProcessOn.md: -------------------------------------------------------------------------------- 1 | 笔记基本上在流程图记录 2 | 3 | https://www.processon.com/view/link/61c347f01efad45a2b3e0fb7 4 | 5 | -------------------------------------------------------------------------------- /06_Middleware/01_Redis/RedisNote/01.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/06_Middleware/01_Redis/RedisNote/01.md -------------------------------------------------------------------------------- /06_Middleware/01_Redis/RedisUse/01_Jmeter.md: -------------------------------------------------------------------------------- 1 | # Jmeter使用 2 | 线程组的两种用法 3 | 1. 发多少次 4 | 2. 发多久 5 | ![[Pasted image 20220220205114.png]] 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /06_Middleware/01_Redis/RedisUse/02_redis思路.md: -------------------------------------------------------------------------------- 1 | # 秒杀 抢100个商品 2 | 思路: 请求先到达redis(减少无效请求去DB) 3 | 4 | ### 法1° 5 | CacheService.class 6 | 7 | decrNum(Interger itemID) { 8 | //减少redis的值 9 | //如果减完大于零 => true 10 | //否则 => false 11 | } 12 | 13 | ### 法2° 14 | 思路: 因为做的是数值计算, String类型的val 15 | encoding 是 int 16 | redis6.x的源码 17 | 18 | 可以通过2进制的操作 19 | X 0 0 0 0 0 0 0 => 正常减 20 | 1 0 0 0 0 0 0 0 => 报错 21 | 就是到了0之后 callback 4个操作   然后后来的就报错了 22 | 23 | ### 法3° 24 | 线程池隔离 25 | 单一商品肯定是串行减少的 26 | 介入==线程池资源隔离==的方式 27 | 28 | TPoolConfig 29 | TPoolimp 30 | 每个业务有自己独立的Pool 里面方式的 31 | 32 | 减少redis的无效请求: 33 | 不用的话会出现: -1 -2 并发去提交redis事务 34 | 如果用了: 会让redis事务O(1) -------------------------------------------------------------------------------- /06_Middleware/02_ZooKeeper/ZOOKEEPER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/06_Middleware/02_ZooKeeper/ZOOKEEPER.png -------------------------------------------------------------------------------- /06_Middleware/Git.md: -------------------------------------------------------------------------------- 1 | # error: failed to push some refs to如何解决 2 | 3 | ![[Pasted image 20220210011115.png]] 4 | 5 | 我们在创建仓库的时候,都会勾选“使用Reamdme文件初始化这个仓库”这个操作初识了一个README文件并配置添加了忽略文件。但是这两份内容并没有联系, 6 | 7 | - 对于error: failed to push some refsto‘远程仓库地址’ 8 | 1 使用如下命令 9 | ```git pull --rebase origin master``` 10 | 11 | 2 然后再进行上传: 12 | ```git push -u origin master``` 13 | 14 | -------------------------------------------------------------------------------- /06_Middleware/README.md: -------------------------------------------------------------------------------- 1 | 什么是中间件? 2 | 是一类能够为一种或多种应用程序合作互通、资源共享,同时还能够为该应用程序提供相关的服务的软件。中间件是一类软件统称,而非一种软件;中间件不仅仅实现互连,还要实现应用之间的互操作。 3 | 4 | 5 | 6 | 常见中间件: 7 | 网关:Nginx、Kong、Zuul 8 | 缓存:Redis、MemCached、OsCache、EhCache 9 | 搜索:ElasticSearch、Solr 10 | 熔断:Hystrix、resilience4j 11 | 负载均衡:DNS、F5、LVS、Nginx、OpenResty、HAproxy 12 | 注册中心:Eureka、Zookeeper、Redis、Etcd、Consul 13 | 认证鉴权:JWT、SpringSecurity 14 | 消费队列:RabbitMQ、Kafka、RocketMQ、ActiveMQ、Redis 15 | 系统监控:Grafana、Prometheus、Influxdb、Telegraf、Lepus 16 | 文件系统:OSS、NFS、FastDFS、MogileFS 17 | RPC框架: Dubbo、Motan、Thrift、grpc 18 | 构建工具:Maven、Gradle 19 | 集成部署:Docker、Jenkins、Git、Maven 20 | 分布式配置:Disconf、Apollo、Spring Cloud Config、Diamond 21 | 压测:LoadRunner、JMeter、AB、webbench 22 | 数据库:MySQL、Redis、MongoDB、PostgreSQL、Memcache、HBase 23 | 网络:专用网络VPC、弹性公网IP、CDN 24 | 数据库中间件:DRDS、Mycat、360 Atlas、Cobar 25 | 分布式框架:Dubbo、Motan、Spring-Could 26 | 分布式任务:XXL-JOB、Elastic-Job、Saturn、Quartz 27 | 分布式追踪:Pinpoint、CAT、zipkin 28 | 分布式日志:elasticsearch、logstash、Kibana 、redis、kafka 29 | 30 | -------------------------------------------------------------------------------- /07_Network/02_OAuth.md: -------------------------------------------------------------------------------- 1 | # OAuth 2 | 3 | 是一个验证授权(Authorization)的开放标准, 所有人都有基于这个标准实现自己的OAuth. 4 | 5 | 使用 **`access token`** 来进行身份验证 6 | 7 | 普遍使用OAuth 2.0 8 | 9 | # Why? 10 | 11 | OAuth之前用HTTP Basic Authentication, 直接传输用户名密码验证. => **不安全** 12 | 13 | OAuth能使得第三方应用对资源访问更安全. 14 | 15 | ```xml 16 | (1)"云冲印"为了后续的服务,会保存用户的密码,这样很不安全。 17 | (2)Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。 18 | (3)"云冲印"拥有了获取用户储存在Google所有资料的权力,用户没法限制"云冲印"获得授权的范围和有效期。 19 | (4)用户只有修改密码,才能收回赋予"云冲印"的权力。但是这样做,会使得其他所有获得用户授权的客户端应用程序全部失效。 20 | (5)只要有一个客户端应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。 21 | ``` 22 | 23 | 24 | 25 | 26 | 27 | # What? 28 | 29 | ## 一些名词解释 30 | 31 | (1) **Third-party application**:第三方应用程序,本文中又称"客户端"(client),即上一节例子中的"云冲印"。 32 | 33 | (2)**HTTP service**:HTTP服务提供商,本文中简称"服务提供商",即上一节例子中的Google。 34 | 35 | (3)**Resource Owner**:资源所有者,本文中又称"用户"(user)。也就是一个人... 36 | 37 | (4)**User Agent**:用户代理,本文中就是指浏览器。 38 | 39 | (5)**Authorization server**:认证服务器,即服务提供商专门用来处理认证的服务器。 40 | 41 | (6)**Resource server**:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。 42 | 43 | ## 运行流程 44 | 45 | OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。 46 | 47 | 也可以理解为认证服务器. 48 | 49 | ![OAuth运行流程](https://s2.loli.net/2022/03/04/COhLIMkVP1KGaAw.png) 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | # OAuth中心组件 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 参考(阮一峰的网络日志): http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 76 | 77 | -------------------------------------------------------------------------------- /07_Network/03_加密算法.md: -------------------------------------------------------------------------------- 1 | # 加密算法 2 | 3 | ## AES(Advanced Encryption Standard) 4 | 5 | 高级加密标准, 最为常见的对称加密算法, **加密解密的密钥相同** 6 | 7 | **明文 ==> 密钥+AES函数 ==> 密文(网络传输) ==> 密钥+AES函数 ==> 明文** 8 | 9 | ## RSA(3 man name) 10 | 11 | 非对称加密算法, 基于大数因式分解的原理, 用模幂算法. 12 | 13 | ![image-20220304095853371](https://s2.loli.net/2022/03/04/GNbkceovjPlRAry.png) 14 | 15 | # CRC(eCyclic Redundancy Check) 16 | 17 | **循环冗余检验**: 数据产生简 短固定位数校验码的一种散列函数, 用除法及余数的原理. 18 | 19 | # MD5 20 | 21 | hash算法 22 | 23 | 敏感信息校验 身份校验 数字签名 -------------------------------------------------------------------------------- /07_Network/04_socket.md: -------------------------------------------------------------------------------- 1 | 什么是Socket? 2 | 3 | # TCP连接时的socket文件 4 | 5 | tcp连接时,服务端会创建socket文件,然后调用 bind 函数将套接字绑定到一个地址,listen通知套接字被动地等待客户端连接请求的到来。该函数创建一个等待队列,将所有希望建立连接的(远程)进程放置在该队列上,accept函数接受等待队列上第一个客户端的连接请求。在队列为空时,该函数将阻塞,直至有一个想要进行连接的客户端到来。客户端可以通过 connect 函数发起连接。先在参数中指明要连接的 IP 地址和端口号,然后开始发起三次握手。内核会给客户端分配一个临时的端口。一旦握手成功,服务端的 accept 就会返回另一个 Socket。一个叫作监听 Socket,一个叫作已连接 Socket。 6 | 7 | 说 TCP 的 Socket 就是一个文件流,是非常准确的。因为,Socket 在Linux中就是以文件的形式存在的。除此之外,还存在文件描述符。写入和读出,也是通过文件描述符。在内核中,Socket 是一个文件,那对应就有文件描述符。每一个进程都有一个数据结构 task_struct,里面指向一个文件描述符数组,来列出这个进程打开的所有文件的文件描述符。文件描述符是一个整数,是这个数组的下标。这个数组中的内容是一个指针,指向内核中所有打开的文件的列表。既然是一个文件,就会有一个 inode,只不过 Socket 对应的 inode 不像真正的文件系统一样,保存在硬盘上的,而是在内存中的。在这个 inode 中,指向了 Socket 在内核中的 Socket 结构。由于 Socket 是文件描述符,因而某个线程盯的所有的 Socket,都放在一个文件描述符集合 fd_set 中,这就是项目进度墙,然后调用 select 函数来监听文件描述符集合是否有变化。一旦有变化,就会依次查看每个文件描述符。那些发生变化的文件描述符在 fd_set 对应的位都设为 1,表示 Socket 可读或者可写,从而可以进行读写操作,然后再调用 select,接着盯着下一轮的变化。 8 | 9 | select 函数还是有问题的,因为每次 Socket 所在的文件描述符集合中有 Socket 发生变化的时候,都需要通过轮询的方式,也就是需要将全部项目都过一遍的方式来查看进度,这大大影响了一个项目组能够支撑的最大的项目数量。因而使用 select,能够同时盯的项目数量由 FD_SETSIZE 限制。如果改成事件通知的方式,情况就会好很多,项目组不需要通过轮询挨个盯着这些项目,而是当项目进度发生变化的时候,主动通知项目组,然后项目组再根据项目进展情况做相应的操作。能完成这件事情的函数叫 epoll,它在内核中的实现不是通过轮询的方式,而是通过注册 callback 函数的方式,当某个文件描述符发送变化的时候,就会主动通知。 10 | 11 | 假设进程打开了 Socket m, n, x 等多个文件描述符,现在需要通过 epoll 来监听是否这些 Socket 都有事件发生。其中 epoll_create 创建一个 epoll 对象,也是一个文件,也对应一个文件描述符,同样也对应着打开文件列表中的一项。在这项里面有一个红黑树,在红黑树里,要保存这个 epoll 要监听的所有 Socket。当 epoll_ctl 添加一个 Socket 的时候,其实是加入这个红黑树,同时红黑树里面的节点指向一个结构,将这个结构挂在被监听的 Socket 的事件列表中。当一个 Socket 来了一个事件的时候,可以从这个列表中得到 epoll 对象,并调用 call back 通知它。这种通知方式使得监听的 Socket 数据增加的时候,效率不会大幅度降低,能够同时监听的 Socket 的数目也非常的多了。上限就为系统定义的、进程打开的最大文件描述符个数。因而,epoll 被称为解决 C10K 问题的利器。 12 | 13 | ## sockaddr & sockaddr_in 14 | 15 | struct sockaddr 和 struct sockaddr_in 这两个结构体用来操作网络通信的地址. 16 | 17 | 18 | 19 | 因为sockaddr用了char sa_data[14]存储目标地址与端口, 不方便操作. 20 | 21 | 所以出现了 sockaddr_in 更加详细的结构体. 22 | 23 | ![这里写图片描述](https://s2.loli.net/2022/03/10/bOgZE5Cu3qVvszK.png) -------------------------------------------------------------------------------- /08_OS/IO.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/08_OS/IO.md -------------------------------------------------------------------------------- /09_SystemDesign/01_Legend of Hero/00_BUG.md: -------------------------------------------------------------------------------- 1 | # 用PackageUtil工具类空格问题 2 | 3 | 背景: 用packageUtil把一个包里的所有类, 放到一个set里面, 方便初始化, 易于扩展. 4 | 5 | 在利用这个工具类的时候获取clazzSet, 就是想把所有类型的消息处理器放到一个set里头 6 | 结果在**debug的时候发现** => 源码意外里面发现本地path的url => 里面有一个 %20 7 | 之前在利用protobuf生成java代码的时候也遇见过, 所以就在path上找问题 8 | 因为这个util没有对空格形式特殊处理, 所以set一直没找到, 消息一直没被处理. 9 | 我又认为这个命名不规范, 我索性就把文件夹名字去掉了空格. 10 | 11 | 一定要规范命名 12 | 13 | # 低级错误 14 | 15 | 把build好的msg对象弃之而不用, 只用它做了一个判空处理. (而且idea也没提示, 因为我用到了msg对象) 16 | 然后实际该用它的时候填错了, 填的是原本的消息体(一个字节数组) => 一直出错 17 | 也是debug的时候发现了... 18 | 19 | # MySQL连接问题(折磨王) 20 | 21 | 1. **时区问题** => UTC GMT 22 | 23 | 2. **驱动问题** 24 | 25 | mysql-connector-java 6.0+ => com.mysql.cj.jdbc.Driver 26 | 27 | mysql-connector-java 6.0- => com.mysql.jdbc.Driver 28 | 29 | 3. **数据库版本问题** 30 | 31 | -------------------------------------------------------------------------------- /09_SystemDesign/01_Legend of Hero/04_CmdHandlerFactory.md: -------------------------------------------------------------------------------- 1 | # 如何实现工厂模式 2 | 3 | 4 | 5 | ![image-20220409110842201](https://s2.loli.net/2022/04/09/dNeBSiLovjJwIsV.png) 6 | 7 | 咱们这个CmdHandlerFactory 用的是通过if else判断 instance of 来决定返回某种类型的handler 8 | 后来发现咱们可以用一个HashMap来存放 9 | 10 | ```java 11 | // 更快, 性能优良, O(1)的复杂度拿到相应cmdHandler 12 | new HashMap, IcmdHandler> 13 | ``` 14 | 15 | 16 | 17 | ```java 18 | public final class CmdHandlerFactory { 19 | // handlerMap字典 20 | // key为消息类, value为消息处理器 => _handlerMap<消息类,实现类> 21 | static private Map, ICmdHandler> _handlerMap = new HashMap<>(); 22 | // 私有类构造器 23 | private CmdHandlerFactory() { 24 | } 25 | // 初始化处理器 handlerMap 26 | static public void init() { 27 | _handlerMap.put(GameMsgProtocol.UserEntryCmd.class, new UserEntryCmdHandler()); 28 | _handlerMap.put(GameMsgProtocol.WhoElseIsHereCmd.class, new WhoElseIsHereCmdHandler()); 29 | _handlerMap.put(GameMsgProtocol.UserMoveToCmd.class, new UserMoveToCmdHandler()); 30 | } 31 | // 根据msg的Class类型 从 {@_handlerMap} 里面直接取 32 | static public ICmdHandler create(Class msgClazz) { 33 | if (msgClazz == null) return null; 34 | return _handlerMap.get(msgClazz); 35 | } 36 | } 37 | ``` -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/01_项目概述-需求分析.md: -------------------------------------------------------------------------------- 1 | # 项目过程 2 | 3 | ## 启动 4 | 5 | 可行性分析,立项。项目背景:**面试:(背景,为什么做?残障人士(后备箱有轮椅),孕妇(开车比较稳司机),小孩(儿童座椅))。** 6 | 7 | 8 | 9 | ## 计划阶段: 10 | 11 | 进度安排,资源计划,成本估计,质量保证计划,风险,实施。 12 | 13 | **面试:(1 加班,2加人,加资源,3 功能排优先级,重要的先做保证能用,后面再迭代。)** 14 | 15 | 16 | 17 | ## 实施控制阶段: 18 | 19 | 开发,测试,等等。 20 | 21 | 22 | 23 | ## 收尾 24 | 25 | 验收。产品验收。 26 | 27 | 28 | 29 | # 项目和产品 30 | 31 | 矩阵式(开发:1组,2组,产品1组,2组),项目组。 32 | 33 | 34 | 35 | # 人员安排 36 | 37 | 项目管理:3(1高级项目经理,2助理) 38 | 39 | 技术总监:1 40 | 41 | 运维:2 42 | 43 | 能力层:10 44 | 45 | 业务层:12 46 | 47 | 产品:10,(乘客,司机,boss,h5) 48 | 49 | 安卓:4 50 | 51 | ios:3 52 | 53 | h5:5 54 | 55 | 测试:20(功能,自动化测试,接口测试,安全测试) 56 | 57 | 运营,市场,大客户关系:未知。 58 | 59 | ## 你在项目中的职责 60 | 61 | 组长:接口定义,工程结构设计,代码review,各方沟通(产品,测试),核心功能开发。 62 | 63 | 组员:**实现了简单的具体开发实现**。核心功能开发。 64 | 65 | 66 | 67 | 68 | 69 | ---- 70 | 71 | Kick Off 72 | 73 | 各方参与,齐聚一堂。启动会。动员大会。 74 | 75 | 76 | 77 | --- 78 | 79 | 80 | 81 | # 实现的需求 82 | 83 | ## 乘客端: 84 | 85 | 1. 发送验证码。 86 | 87 | ```sh 88 | 三挡验证。技术人员防止恶意发短信。 89 | ``` 90 | 91 | 92 | 93 | 2. 登录/注册。 94 | 95 | 3. 查看开通区域 96 | 97 | ```sh 98 | 高德围栏 99 | ``` 100 | 101 | 4. 预估价格。 102 | 5. 下单 103 | 6. (司机流程) 104 | 7. 支付(分布式事务:订单,支付,积分) 105 | 8. 评价。 106 | 107 | # 司机端 108 | 109 | 1. 发送验证码 110 | 2. 登录,注册 111 | 3. 查看,改变司机状态。 112 | 4. 司机抢单(分布式锁) 113 | 5. 订单状态变更。 114 | 6. 发起收款 115 | 116 | # boss 117 | 118 | 运营。 119 | 120 | 121 | 122 | # 微服务设计原则 123 | 124 | 架构,模式,拆分,隔离。目标:**隔离系统的变化点**。 125 | 126 | 127 | 128 | ## 具体原则: 129 | 130 | 高内聚,低耦合。 131 | 132 | 高度自治:开发,测试,构建,部署,运行,发布。(无状态) 133 | 134 | 以业务为中心。 135 | 136 | 弹性设计。(容错,隔离,降级)。 137 | 138 | 自动化。持续集成,持续交付。 139 | 140 | 粒度把控:没有标准。任何一个服务,不要因为自己的开发和维护,影响其他服务。 141 | 142 | 143 | 144 | api组,service组。 145 | 146 | 业务层,能力层。 147 | 148 | 149 | 150 | ## AKF: 151 | 152 | x轴:水平复制。A A A A A。 153 | 154 | y轴:A 功能划分 155 | 156 | z轴:数据分片:手机,衣服,家电。; 杭州,北京。盘古。 157 | 158 | 159 | 160 | --- 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 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 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/03_工程设计-eureka优化_01.md: -------------------------------------------------------------------------------- 1 | 上节课,服务拆分,接口设计问题。 2 | 3 | --- 4 | 5 | # 微服务项目结构 6 | 7 | 项目在独立仓库中。 8 | 9 | 整体 10 | 11 | ```sh 12 | |--online-taxi-three 13 | !-- 项目A 14 | |-- 项目B 15 | ``` 16 | 17 | 单独项目 18 | 19 | ```sh 20 | |--pom 21 | |--src 22 | |--controller 23 | |--service 24 | impl 25 | 接口 26 | |--dao 27 | entity 28 | mapper 29 | |--manager 30 | |--constant常量 31 | |--request 接受的参数bean 32 | |--response返回参数bean 33 | |--resource 34 | |--mapper 35 | |--xxxxMapper.xml 36 | yml 37 | ``` 38 | 39 | 40 | 41 | ## 异常 42 | 43 | dao层的异常:不用打日志。catch。跑上去。 44 | 45 | service:打日志,详细信息。时间,参数, 46 | 47 | controller: 异常包装成 状态码。 48 | 49 | 50 | 51 | 公司maven私服: 52 | 53 | UserBean。 54 | 55 | dto:common。二方库。 56 | 57 | 58 | 59 | 60 | 61 | application-dev.yml 62 | 63 | application-qa.yml 64 | 65 | application-prd.yml 66 | 67 | 68 | 69 | ## eureka-server优化 70 | 71 | ``` 72 | @EnableEurekaServer 73 | ``` 74 | 75 | 和pom 76 | 77 | 78 | 79 | 组成eureka-server。 80 | 81 | 82 | 83 | 84 | 85 | 10个微服务:7个,= 3 70%,4 ,不开。 86 | 87 | 80% 88 | 89 | 1000 7 3(网络抖动) 93%。开。 90 | 91 | 92 | 93 | **不同数量服务的自我保护** 94 | 95 | **快速下线。** 96 | 97 | 98 | 99 | map<服务名,map<实例id,实例信息>> 100 | 101 | ``` 102 | ConcurrentHashMap>> 103 | ``` 104 | 105 | 106 | 107 | # cap 108 | 109 | eureka 为什么ap。 110 | 111 | ## 三级缓存 112 | 113 | 114 | 115 | registry 116 | 117 | # 优化 118 | 119 | ```sh 120 | server: 121 | # 自我保护,看服务多少。 122 | enable-self-preservation: false 123 | # 自我保护阈值 124 | renewal-percent-threshold: 0.85 125 | # 剔除服务时间间隔 126 | eviction-interval-timer-in-ms: 1000 127 | # 关闭从readOnly读注册表 128 | use-read-only-response-cache: false 129 | # readWrite 和 readOnly 同步时间间隔。 130 | response-cache-update-interval-ms: 1000 131 | ``` 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/04-eureka-server源码-服务测算.md: -------------------------------------------------------------------------------- 1 | ## eureka结构 2 | 3 | ![image-20211213163550237](https://s2.loli.net/2021/12/13/ZJbHFM6OB9hVREn.png) 4 | 5 | ## cap 6 | 7 | 1.三级缓存。 8 | 9 | 2.从其他peer拉取注册表。peer。int registryCount = this.registry.syncUp(),没有满足C的地方。 10 | 11 | 3.P:网络不好的情况下,还是可以拉取到注册表进行调用的。服务还可以调用。 12 | 13 | 14 | 15 | 100 80, 20挂了。1个没挂,但是网络抖动了。 16 | 17 | 不剔除这1个了。 18 | 19 | 20 | 21 | ## 自我保护剔除:eureka 优化 22 | 23 | 1. 开关 24 | 2. 阈值 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ## server源码。 33 | 34 | 剔除(本质也是下面的下线)。长时间没有心跳的服务,eureka server将它从注册表剔除。√ 35 | 36 | 注册。√ 37 | 38 | 续约。√ 39 | 40 | 下线。√ 41 | 42 | 集群间同步。√ 43 | 44 | 拉取注册表。(all-apps,apps-delta,服务名) 45 | 46 | ```sh 47 | Lease租约 InstanceInfo服务实例 48 | ``` 49 | 50 | 51 | 52 | 收到服务实例,保存。心跳时间,xxx时间。 53 | 54 | 55 | 56 | 服务实例,有 xxxx时间。 57 | 58 | 59 | 60 | class 租约{ 61 | 62 | ​ long 到期time; 63 | 64 | ​ long 续约时间time; 65 | 66 | ​ long 心跳时间time; 67 | 68 | ​ T 服务实例 holder。setter 69 | 70 | } 71 | 72 | 73 | 74 | 后面续约:频繁。 75 | 76 | ## 服务测算。 77 | 78 | 20个服务 每个服务部署5个。 eureka client:100个。 79 | 80 | 1分钟 200。 81 | 82 | 心跳。向server发送我们活着。 83 | 84 | 几十万次。对server而言。 85 | 86 | 87 | 88 | 1个拉一次,注册一次。100 ,200. 89 | 90 | 91 | 92 | 10M,cpu hz。 100. 93 | 94 | ---- 95 | 96 | 增量拉取。 97 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/06_order&计价.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ### 合并IO 12 | 13 | 例如09-计价 里的 service-order 尽量合并IO在一个微服务中 14 | 15 | 16 | 17 | 18 | 19 | ### 早晚高峰削峰 20 | 21 | 创建订单后提交消息给MQ,任务完成后再异步发送给客户 22 | 23 | 24 | 25 | ### 独立设计每个服务的数据库 26 | 27 | - 例如: 计费规则是在boss端设置 **然后存在数据库** 要用的时候就去service-order的数据库取 28 | 29 | 然后计算价格(**==无状态的服务更好水平扩容,存在数据库,是活的==**) 30 | 31 | 32 | 33 | ### 计价用什么数据结构? 34 | 35 | **BigDecimal** 36 | 37 | https://www.cnblogs.com/YangJavaer/p/6056394.html 38 | 39 | (1)**==商业计算使用BigDecimal==**。 40 | 41 | (2)尽量使用参数类型为**==String的构造函数==**。 42 | 43 | (3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。 44 | 45 | (4)我们往往容易忽略JDK底层的一些实现细节,导致出现错误,需要多加注意。 46 | 47 | 48 | 49 | ![image-20211220202931717](https://s2.loli.net/2021/12/20/pqyQ6cJjDRwz7uH.png) 50 | 51 | 52 | 53 | ### YAPI接口文档 54 | 55 | 查阅 调用哪些服务要 用到哪些表 56 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/10_派单逻辑.md: -------------------------------------------------------------------------------- 1 | 分布式锁抢单 2 | 3 | 4 | 5 | # 派单逻辑 6 | 7 | ## 推送给司机的方式 8 | 9 | 首轮20s随机筛选6公里内X名司机 第二轮20s推送6公里内未推送司机 10 | 11 | - **两周上线**: 循环 轮询 12 | 13 | - **最终版本**: Task + 队列 => schedule 14 | 15 | 16 | 17 | # 看10-派单设计 18 | 19 | 设计的时候要隔离系统变化点... 20 | 21 | 所有流程都封装成任务 然后在code中切换执行 22 | 23 | 获取订单前要**去重** 因为服务是无状态的 24 | 25 | ## TaskManager.java 26 | 27 | 然后拿到orderid , concurrentHashMap 存订单号和任务 28 | 29 | 然后各种派单流程 分支 30 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/11_订单状态.md: -------------------------------------------------------------------------------- 1 | # 减压 2 | 3 | oss减少我们的主要流量压力 我们只处理订单信息 (Object Storage Service) 4 | 5 | ![image-20220216162102128](https://s2.loli.net/2022/02/16/mpEiTWO2GQsKkfg.png) 6 | 7 | # 三级等保要求 8 | 9 | 电话号码 188xxxx0772 10 | 11 | **对称加密**存入数据库 12 | 13 | # 派单 14 | 15 | 使用redis : "业务名称" + driverID , value 16 | 17 | 解决每个司机要处理的抢单 18 | 19 | # 5个司机抢单-分布式锁 20 | 21 | 且听下回分解 22 | 23 | # 订单状态机 24 | 25 | 订单状态: 操作前状态 & 事件 26 | 27 | 需要注意的点: 28 | 29 | - 日志记录, 记录状态变化过程 int 30 | 31 | - 状态只能前进或停止 32 | 33 | **订单状态** 34 | 35 | ```java 36 | 订单预估 0 ----------> 取消订单 90 37 | ↓ 38 | 订单开始 1 ----------> 取消订单 91 39 | ↓ 40 | 司机接单 2 ----------> 取消订单 92 41 | ↓ 42 | 去接乘客 3 ----------> 取消订单 93 43 | ↓ 44 | 到上车点 4 ----------> 取消订单 94 45 | ↓ 46 | 接到乘客 5 ----------> 取消订单 95 47 | ↓ 48 | 开始行程 6 49 | ↓ 50 | 结束行程 7 --> (司机决定)(走一遍计价流程) 51 | ↓ 52 | 发起收款 8 53 | ↓ 54 | 乘客支付 9 55 | ↓ 56 | 乘客评价 10 57 | ``` 58 | 59 | 60 | 61 | # 柔性事务 seata 62 | 63 | ![image-20220216171007089](https://s2.loli.net/2022/02/16/sHCDgu4JMVN2vfq.png) 64 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/12_其他.md: -------------------------------------------------------------------------------- 1 | # 支付细节 2 | 3 | **pay-service** 4 | 5 | ali & wechat支付回调功能 6 | 7 | # 电话隐私保护 8 | 9 | 阿里云实现 10 | 11 | 并且会有录音 ==> OSS存储 12 | 13 | # 监管系统 14 | 15 | **goverment-service & goverment-upload** 16 | 17 | 业务数据 ==> 消息队列 ==> 上报系统(**每天执行 不影响主系统性能**) 18 | 19 | **业务数据:** 20 | 21 | - 公司业务数据(公司信息, 计价规则......) 22 | 23 | - 乘客信息 24 | - 司机信息 25 | - 车辆信息 26 | - ...... 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/15_网约车坐标系问题.md: -------------------------------------------------------------------------------- 1 | # 坐标系问题(基于lbs公司) 2 | 3 | **高德**: 火星坐标(中国国测局GCJ-02) 4 | **地球坐标**: (WGS-84, World Geoxx System) 5 | **百度坐标**: BD-09 在火星坐标上做了二次加密(偏移) 6 | 7 | # 如何解决? 8 | 9 | 国家法规要求所有地图类产品都必须使用国家测绘局的一种加偏移的算法,对地图的真实坐标进行加偏移处理,之后才可能通过审批准许上市。 => 生成了火星坐标. 10 | 11 | 并且**国内所有地图**都是GCJ-02坐标系. 12 | 13 | 但是水货GPS获得的坐标系就和GCJ-02坐标系有一定范围的偏差(500m) 14 | 15 | 国产的GPS设备可以返回GCJ-02坐标, 目前不知道如何实现. 16 | 17 | # 所以 18 | 19 | 都用国产地图就行啦, 不知道咱们公司是怎么解决的? -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/16_2pc3pc.md: -------------------------------------------------------------------------------- 1 | # 单体事务 2 | 3 | A => 原子性 => 注重**事情的过程**, 做的过程要么全成功, 要么全失败 4 | C => 一致性 => 注重**事情的结果**, 做的这些事的结果不要错乱. 5 | I => 隔离性 => 四个事务隔离级别 => 导致的脏读, 重复读, 幻读, 6 | D => 持久性 => 就是落库, 数据持久化. 7 | 8 | # 分布式事务 9 | 10 | 分布式事务与分布式锁的区别? 11 | 分布式锁 => 分布式资源抢占 12 | 分布式事务 => 解决流程化提交问题. 13 | 14 | ![image-20220407153945134](https://s2.loli.net/2022/04/07/YcmNLMfDw4XQCqP.png) 15 | 16 | ### 名词解释 17 | 18 | - 事务:事务是由一组操作构成的可靠的独立的工作单元,事务具备ACID的特性,即原子性、一致性、隔离性和持久性。 19 | - 本地事务:当事务由资源管理器本地管理时被称作本地事务。本地事务的优点就是支持严格的ACID特性,高效,可靠,状态可以只在资源管理器中维护,而且应用编程模型简单。但是本地事务不具备分布式事务的处理能力,隔离的最小单位受限于资源管理器。 20 | - 全局事务:当事务由全局事务管理器进行全局管理时成为全局事务,事务管理器负责管理全局的事务状态和参与的资源,协同资源的一致提交回滚。 21 | - **TX协议**:**应用或者应用服务器与事务管理器的接口**。 22 | - **XA协议**:**全局事务管理器与资源管理器的接口**。XA是由X/Open组织提出的分布式事务规范。该规范主要定义了全局事务管理器和局部资源管理器之间的接口。主流的数据库产品都实现了XA接口。XA接口是一个双向的系统接口,在事务管理器以及多个资源管理器之间作为通信桥梁。之所以需要XA是因为在分布式系统中从理论上讲两台机器是无法达到一致性状态的,因此引入一个单点进行协调。由全局事务管理器管理和协调的事务可以跨越多个资源和进程。全局事务管理器一般使用XA二阶段协议与数据库进行交互。 23 | - 24 | - **AP:应用程序**,可以理解为使用DTP(Data Tools Platform)的程序。 25 | - **RM:资源管理器**,这里可以是一个DBMS或者消息服务器管理系统,应用程序通过资源管理器对资源进行控制,资源必须实现XA定义的接口。资源管理器负责控制和管理实际的资源。 26 | - **TM:事务管理器**,负责协调和管理事务,提供给AP编程接口以及管理资源管理器。事务管理器控制着全局事务,管理事务的生命周期,并且协调资源。 27 | - **两阶段提交协议**:XA用于在全局事务中协调多个资源的机制。TM和RM之间采取两阶段提交的方案来解决一致性问题。两节点提交需要一个协调者(TM)来掌控所有参与者(RM)节点的操作结果并且指引这些节点是否需要最终提交。两阶段提交的局限在于协议成本,准备阶段的持久成本,全局事务状态的持久成本,潜在故障点多带来的脆弱性,准备后,提交前的故障引发一系列隔离与恢复难题。 28 | - **BASE理论**:BA指的是基本业务可用性,支持分区失败,S表示柔性状态,也就是允许短时间内不同步,E表示最终一致性,数据最终是一致的,但是实时是不一致的。原子性和持久性必须从根本上保障,为了可用性、性能和服务降级的需要,只有降低一致性和隔离性的要求。 29 | - **CAP定理**:对于共享数据系统,最多只能同时拥有CAP其中的两个,任意两个都有其适应的场景,真是的业务系统中通常是ACID与CAP的混合体。分布式系统中最重要的是满足业务需求,而不是追求高度抽象,绝对的系统特性。C表示一致性,也就是所有用户看到的数据是一样的。A表示可用性,是指总能找到一个可用的数据副本。P表示分区容错性,能够容忍网络中断等故障。 30 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/29-rocket总结-分布式事务总结.md: -------------------------------------------------------------------------------- 1 | # rocketmq使用 2 | 3 | 4 | 5 | rocketmq-all-4.5.0-bin-release 6 | 7 | rocketmq-externals 8 | 9 | 10 | 11 | 启动顺序 12 | 13 | namesrv 14 | 15 | ```sh 16 | start mqnamesrv.cmd 17 | ``` 18 | 19 | 20 | 21 | broker 22 | 23 | ```sh 24 | start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true 25 | ``` 26 | 27 | 28 | 29 | externals 30 | 31 | ```sh 32 | java -jar rocketmq-console-ng-1.0.1.jar 33 | ``` 34 | 35 | 36 | 37 | 测试: 38 | 39 | ![image-20200827203507295](29-课上.assets/image-20200827203507295.png) 40 | 41 | 42 | 43 | 项目 44 | 45 | rocket-tx 46 | 47 | producer(改造成我们的服务) 48 | 49 | consumer(改造成我们的服务) 50 | 51 | 52 | 53 | 19-29。20个小时。2小时。 54 | 55 | 56 | 57 | # 总结分布式事务 58 | 59 | 2pc(协调者超时 回滚,占用连接,) 60 | 61 | 3pc (2pc的第一阶段 拆成了 2个阶段,协调者和参与者都超时,pre超时是回滚,do 超时是提交)。 62 | 63 | tcc(2pc的第二阶段 拆成了2个阶段,不占用连接,性能高,但是麻烦)(简单业务可以tcc)。 64 | 65 | lcn(lcn,tcc)(代码) 66 | 67 | seata(at,tcc)(代码) 68 | 69 | 消息队列+本地事件表(代码) 70 | 71 | 最大努力通知 72 | 73 | 可靠消息服务 74 | 75 | 消息事务(代码) 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/30-大纲 技术点.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 论文 4 | 5 | ### 架构图 6 | 7 | 熟悉需求:==业务架构图== 8 | 9 | 熟悉技术栈:==技术架构图== 10 | 11 | 时序图流程 12 | 13 | 14 | 15 | ### 关键技术点 16 | 17 | **验证码 - 图** 18 | 19 | ​ 验证码优化手段 20 | 21 | 22 | 23 | 24 | 25 | **eureka: ap** 26 | 27 | ​ 三级缓存,集群间同步 28 | 29 | ​ 加快服务剔除速度 30 | 31 | ​ 避免无效eureka(=3个) 32 | 33 | ​ 乱序url 分担压力 34 | 35 | 36 | 37 | **派单** 38 | 39 | **抢单** 40 | 41 | **订单状态变化** 42 | 43 | **支付** 44 | 45 | ​ 分布式锁 46 | 47 | ​ 支付分布式事务 48 | 49 | 50 | 51 | **如何提高QPS,TPS** 52 | 53 | 54 | 55 | 使用了哪些**第三方**服务: 56 | 57 | ​ 公司maven私服 58 | 59 | ​ dto:common 二方库 60 | 61 | 62 | 63 | **链路追踪** 64 | 65 | ​ 安全性 66 | 67 | ### 关键代码 68 | 69 | **计价代码** 解析 70 | 71 | 72 | 73 | 相关==接口**表格**== 74 | 75 | 代码描述 & ==【UML图】== 76 | 77 | 78 | 79 | 80 | 81 | ### 测试 82 | 83 | 灰度(金丝雀)发布: 网关层面zuul区分 服务间ribbon区分 (**手敲**) 84 | 85 | AB测试 86 | 87 | 88 | 89 | Postman 90 | 91 | ### 系统测试 92 | 93 | 94 | 95 | # PPT 96 | 97 | 13-业务总结mu 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | # 其他 108 | 109 | ### 第三方 110 | 111 | 阿里云隐私保护_云通信:AXB中间号 112 | 113 | 阿里云OSS (Object Storage Service) 114 | 115 | 116 | 117 | 118 | 119 | 任务估算:2h为单位 一天8h 120 | 121 | -------------------------------------------------------------------------------- /09_SystemDesign/02_Taxi/eureka源码结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/02_Taxi/eureka源码结构.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/09-mysql架构.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/09-mysql架构.pdf -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/BasicKnowledge/00_Tip.md: -------------------------------------------------------------------------------- 1 | # 主键是自然有索引的😂 -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/BasicKnowledge/01_数据类型.md: -------------------------------------------------------------------------------- 1 | # 整数类型 2 | 3 | ![image-20220227151115699](https://s2.loli.net/2022/02/27/HPjpqciEXL9Q3GV.png) 4 | 5 | MySQL支持**显示宽度**(例如: INT(4)), 6 | 7 | 主键自增: 不使用序列, 通过auto_increment, 要求是整数类型 8 | 9 | # 浮点数类型 10 | 11 | ![image-20220227151256599](https://s2.loli.net/2022/02/27/5SaiHzlEkLh8U6m.png) 12 | 13 | 浮点数类型的宽度不会自动扩充 14 | 15 | score double(4,1)--小数部分为1位, 总宽度4位, 并且不会自动扩充. 16 | 17 | # 字符串类型 18 | 19 | ![image-20220227151533454](https://s2.loli.net/2022/02/27/udYKNJLksRpPtM3.png) 20 | 21 | ==CHAR类型长度固定, VARCHAR类型的长度可变.== 22 | 23 | 不确定数据长度时, 用VARCHAR省空间. 24 | 25 | # 日期和时间类型 26 | 27 | ![image-20220227151805727](https://s2.loli.net/2022/02/27/3udFBhXxsvD4mk2.png) 28 | 29 | TIMESTEMP类型的数据指定方式与DATETIME基本相同, 两者的不同之处在于以下几点: 30 | (1) 数据的取值范围不同, TIMESTEMP类型的取值范围更小. 31 | (2) 如果我们对TIMESTAMP类型的字段没有明确赋值, 或是被赋与了NULL值, MySQL会自动将该字段赋值为系统当前的日期与时间. 32 | (3) TIMESTEMP类型还可以使用CURRENT_TIMESTAMP来获取系统当前时间. 33 | (4) TIMESTEMP类型有一个很大的特点, 那就是时间是根据时区来显示的.例如, 在东八区插入的TIMESTEMP数据为2017-07-11 16:43:25, 在东七区显示时, 时间部分就变成了15:43:25, 在东九区显示时, 时间部分就变成了17:43:25. -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/BasicKnowledge/02_修改数据code.md: -------------------------------------------------------------------------------- 1 | # code 2 | 3 | ```sql 4 | -- 修改表中数据 5 | update t_student set sex = '女' ; 6 | update t_student set sex = '男' where sno = 10 ; 7 | UPDATE T_STUDENT SET AGE = 21 WHERE SNO = 10; 8 | update t_student set CLASSNAME = 'java01' where sno = 10 ; 9 | update t_student set CLASSNAME = 'JAVA01' where sno = 9 ; 10 | update t_student set age = 29 where classname = 'java01'; 11 | -- 删除操作: 12 | delete from t_student where sno = 2; 13 | ``` 14 | 15 | 关键字,表名,字段名不区分大小写 16 | 17 | 删除操作**from关键字**不可缺少 18 | 19 | ```sql 20 | -- 查看数据: 21 | select * from t_student; 22 | -- 修改表的结构: 23 | -- 增加一列: 24 | alter table t_student add score double(5,2) ; -- 5:总位数 2:小数位数 25 | update t_student set score = 123.5678 where sno = 1 ; 26 | -- 增加一列(放在最前面) 27 | alter table t_student add score double(5,2) first; 28 | -- 增加一列(放在sex列的后面) 29 | alter table t_student add score double(5,2) after sex; 30 | -- 删除一列: 31 | alter table t_student drop score; 32 | -- 修改一列: 33 | alter table t_student modify score float(4,1); -- modify修改是列的类型的定义,但是不会改变列的名字 34 | alter table t_student change score score1 double(5,1); -- change修改列名和列的类型的定义 35 | -- 删除表: 36 | drop table t_student; 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/BasicKnowledge/08_MySQL执行流程.md: -------------------------------------------------------------------------------- 1 | # MySQL执行流程图 2 | 3 | https://www.processon.com/view/link/621c4c570e3e745d894d3a42 4 | 5 | # LRU缓存机制 (Buffer Pool) 6 | 7 | 详解: https://www.cnblogs.com/better-farther-world2099/articles/14768929.html 8 | 9 | OS, MemCache: 都是用的LRU普通缓存机制 10 | 11 | LRU就是一个定长链表 12 | 13 | 最近用过的页放在头部 14 | 15 | 超出了缓冲池长度的移除 => 太久没用过的页就被淘汰了 16 | 17 | (预读就是读一页数据加载到buffer) 18 | 19 | ============= 预读失败? ============ 20 | 21 | 预读失败: 没从加载到buffer的页读取数据 22 | 23 | MySQL: 24 | 25 | 设置了 新生代(new sublist) -> 老生代(old sublist) 26 | 27 | 7 : 3 28 | 29 | 新数据从老生代头插入 如果新数据真的需要就再拎到新生代头 30 | 31 | =============缓冲池污染?============= 32 | 33 | 例如查找字符串: 扫描大量数据时,大量热数据被淘汰 34 | 35 | ![image-20220228121622209](https://s2.loli.net/2022/02/28/tjcdhrSgGHQAy9l.png) -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/Linux下mysql5.7的彻底卸载.md: -------------------------------------------------------------------------------- 1 | # Linux下mysql的彻底卸载 2 | 3 | ### 1、查看mysql的安装情况 4 | 5 | ``` 6 | rpm -qa | grep -i mysql 7 | ``` 8 | 9 | ![1570605325400](D:\sourse\1ForInternetArchitect\MySQL\typora-user-images\1570605325400.png) 10 | 11 | ### 2、删除上图安装的软件 12 | 13 | ``` 14 | rpm -ev mysql-community-libs-5.7.27-1.el6.x86_64 --nodeps 15 | ``` 16 | 17 | ### 3、都删除成功之后,查找相关的mysql的文件 18 | 19 | ``` 20 | find / -name mysql 21 | ``` 22 | 23 | ![1570605553095](D:\sourse\1ForInternetArchitect\MySQL\typora-user-images\1570605553095.png) 24 | 25 | ### 4、删除全部文件 26 | 27 | ``` 28 | rm -rf /var/lib/mysql 29 | rm -rf /var/lib/mysql/mysql 30 | rm -rf /etc/logrotate.d/mysql 31 | rm -rf /usr/share/mysql 32 | rm -rf /usr/bin/mysql 33 | rm -rf /usr/lib64/mysql 34 | ``` 35 | 36 | ### 5、再次执行命令 37 | 38 | ```shell 39 | rpm -qa | grep -i mysql 40 | #如果没有显式则表示卸载完成 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/MySQL优化.md: -------------------------------------------------------------------------------- 1 | ``` 2 | 1、mysql基础层次 3 | 2、mysql性能监控01 4 | 3、mysql性能监控02 5 | 4、mysql性能监控03 6 | 5、更小的通常更好 7 | 6、简单就好 8 | 7、尽量避免null 9 | 8、实际类型的优化_整形 10 | 8、实际类型的优化_字符型 11 | 10、实际类型的优化_字符型_BLOB_TEXT 12 | 11、实际类型的优化_时间戳 13 | 12、实际类型的优化_枚举类 14 | 13、实际类型的优化_特殊类型 15 | 14、合理适用范式和反范式 16 | 15、主键的选择 17 | 16、字符集的选择 18 | 17、存储引擎的选择 19 | 18、适当的数据冗余 20 | 19、适当拆分 21 | 20、mysql_执行计划 22 | 21、mysql_通过索引进行优化01 23 | 22、mysql_通过索引进行优化02_B树 24 | 23、mysql_通过索引进行优化03_数据库引擎 25 | 24、mysql_通过索引进行优化04_索引基本知识 26 | 25、mysql_通过索引进行优化05_索引匹配方式 27 | 26、mysql_通过索引进行优化06_ 哈希索引 28 | 27、mysql_通过索引进行优化07_ 组合索引 29 | 28、聚簇索引与非聚簇索引 30 | 29、mysql_通过索引进行优化09_覆盖索引 31 | 30、优化小细节_索引扫描 32 | 31、优化小细节_union all,in,or索引 33 | 32、优化小细节_范围列可以用到索引 34 | 33、优化小细节_强制类型转换会全表扫描 35 | 34、更新频繁,数据区分度不高字段不宜建立索引 36 | 35、列的索引不许为null三张表join 37 | 36、优化小细节_答疑 38 | 37、优化小细节_limit能够提高效率 39 | 38、优化小细节_单表索引建议控制在5个以内 40 | 39、创建索引的错误概念 41 | 40、mysql_通过索引进行优化20_索引监控 42 | 41、mysql_查询优化01_查询慢的原因 43 | 42、mysql_查询优化02_执行过程的优化 44 | 43、执行过程的优化_优化器策略 45 | 44、执行过程的优化_关联与排序优化 46 | 45、执行过程的优化_优化特定类型的查询 47 | 46、执行过程的优化_其他优化 48 | 47、分区表的应用场景 49 | 48、分区表的原理与类型 50 | 49、如何使用分区表 51 | 50、在使用分区表的时候需要注意的问题 52 | 51、服务器参数设置01 53 | 52、服务器参数设置02 54 | 53、服务器参数设置03_cache 55 | 54、服务器参数设置04_INNODB 56 | 55、mysql锁 57 | ``` 58 | 59 | 60 | 61 | 常见存储引擎及其功能 62 | 63 | https://www.jianshu.com/p/4bb9f78b4f6d -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/Mysql调优.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/Mysql调优.xmind -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/Tuning/04_索引细节知识.md: -------------------------------------------------------------------------------- 1 | # 索引失效情况 2 | 3 | 模 型 数 空 运 最 快 4 | 5 | 6 | 7 | ## 模: 8 | 9 | 模糊查询的意思. like的模糊查询以%开头, 索引失效. 比如: 10 | 11 | ```sql 12 | SELECT * FROM `user` WHERE `name` LIKE '%老猿'; 13 | ``` 14 | 15 | 16 | 17 | ## 型: 18 | 19 | 代表数据类型. 类型错误, 如字段类型为varchar, where条件用number, 索引也会失效. 比如: 20 | 21 | ```sql 22 | SELECT * FROM `user` WHERE height = 180; 23 | ``` 24 | 25 | height为varchar类型导致索引失效. 26 | 27 | 28 | 29 | ## 数: 30 | 31 | 是函数的意思. 对索引的字段使用内部函数, 索引也会失效. 这种情况下应该建立基于函数的索引. 比如: 32 | 33 | ```sql 34 | SELECT * FROM `user` WHERE DATE(create_time) = '2020-09-03'; 35 | ``` 36 | 37 | create_time字段设置索引, 那就无法使用函数, 否则索引失效. 38 | 39 | 40 | 41 | ## 空: (新版mysql上NULL也不会失效) 42 | 43 | 是Null的意思. 索引不存储空值, 如果不限制索引列是not null, 数据库会认为索引列有可能存在空值, 所以不会按照索引进行计算. 比如: 44 | 45 | ```sql 46 | SELECT * FROM `user` WHERE address IS NULL --不走索引. 47 | 48 | SELECT * FROM `user` WHERE address IS NOT NULL;--走索引. 49 | ``` 50 | 51 | 建议大家这设计字段的时候, 如果没有必要的要求必须为NULL, 那么最好给个默认值空字符串, 这可以解决很多后续的麻烦(切记). 52 | 53 | 54 | 55 | ## 运: 56 | 57 | 是运算的意思. 对索引列进行(+, -, *, /, !, !=, <>)等运算, 会导致索引失效. 比如: 58 | 59 | ```sql 60 | SELECT * FROM `user` WHERE age - 1 = 20; 61 | ``` 62 | 63 | 64 | 65 | ## 最: (并不是失效只是不能用) 66 | 67 | 是最左原则. 在复合索引中索引列的顺序至关重要. 如果不是按照索引的最左列开始查找, 则无法使用索引. 68 | 69 | 70 | 71 | ## 快: (并不是失效只是不能用) 72 | 73 | 全表扫描更快的意思. 如果数据库预计使用全表扫描要比使用索引快, 则不使用索引. -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/mysql主从复制安装配置.md: -------------------------------------------------------------------------------- 1 | # mysql主从复制安装配置 2 | 3 | ### 1、基础设置准备 4 | 5 | ```shell 6 | #操作系统: 7 | centos6.5 8 | #mysql版本: 9 | 5.7 10 | #两台虚拟机: 11 | node1:192.168.85.111(主) 12 | node2:192.168.85.112(从) 13 | ``` 14 | 15 | ### 2、安装mysql数据库 16 | 17 | ```shell 18 | #详细安装和卸载的步骤参考对应的文档 19 | ``` 20 | 21 | ### 3、在两台数据库中分别创建数据库 22 | 23 | ```sql 24 | --注意两台必须全部执行 25 | create database msb; 26 | ``` 27 | 28 | ### 4、在主(node1)服务器进行如下配置: 29 | 30 | ```shell 31 | #修改配置文件,执行以下命令打开mysql配置文件 32 | vi /etc/my.cnf 33 | #在mysqld模块中添加如下配置信息 34 | log-bin=master-bin #二进制文件名称 35 | binlog-format=ROW #二进制日志格式,有row、statement、mixed三种格式,row指的是把改变的内容复制过去,而不是把命令在从服务器上执行一遍,statement指的是在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。mixed指的是默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。 36 | server-id=1 #要求各个服务器的id必须不一样 37 | binlog-do-db=msb #同步的数据库名称 38 | ``` 39 | 40 | ### 5、配置从服务器登录主服务器的账号授权 41 | 42 | ```sql 43 | --授权操作 44 | set global validate_password_policy=0; 45 | set global validate_password_length=1; 46 | grant replication slave on *.* to 'root'@'%' identified by '123456'; 47 | --刷新权限 48 | flush privileges; 49 | ``` 50 | 51 | ### 6、从服务器的配置 52 | 53 | ```shell 54 | #修改配置文件,执行以下命令打开mysql配置文件 55 | vi /etc/my.cnf 56 | #在mysqld模块中添加如下配置信息 57 | log-bin=master-bin #二进制文件的名称 58 | binlog-format=ROW #二进制文件的格式 59 | server-id=2 #服务器的id 60 | ``` 61 | 62 | ### 7、重启主服务器的mysqld服务 63 | 64 | ```shell 65 | #重启mysql服务 66 | service mysqld restart 67 | #登录mysql数据库 68 | mysql -uroot -p 69 | #查看master的状态 70 | show master status; 71 | ``` 72 | 73 | ![1570703264912](E:\lian\oracle\typora-user-images\1570703264912.png) 74 | 75 | ### 8、重启从服务器并进行相关配置 76 | 77 | ```shell 78 | #重启mysql服务 79 | service mysqld restart 80 | #登录mysql 81 | mysql -uroot -p 82 | #连接主服务器 83 | change master to master_host='192.168.85.11',master_user='root',master_password='123456',master_port=3306,master_log_file='master-bin.000001',master_log_pos=154; 84 | #启动slave 85 | start slave 86 | #查看slave的状态 87 | show slave status\G(注意没有分号) 88 | ``` 89 | 90 | ### 9、此时可以在主服务器进行相关的数据添加删除工作,在从服务器看相关的状态 -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/mysql数据结构选择.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/mysql数据结构选择.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/mysql架构.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/mysql架构.jpg -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/mysql索引系统 .png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/mysql索引系统 .png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/mysql读写分离.md: -------------------------------------------------------------------------------- 1 | # mysql读写分离 2 | 3 | ![1570776205802](D:\sourse\1ForInternetArchitect\MySQL\typora-user-images\mysql-proxy.jpg) 4 | 5 | ### 1、读写分离的介绍 6 | 7 | ![](D:\sourse\1ForInternetArchitect\MySQL\typora-user-images\读写分离.jpg) 8 | 9 | ​ MySQL读写分离基本原理是让master数据库处理写操作,slave数据库处理读操作。master将写操作的变更同步到各个slave节点。 10 | 11 | ​ MySQL读写分离能提高系统性能的原因在于: 12 | 13 | ​ 1、物理服务器增加,机器处理能力提升。拿硬件换性能。 14 | 15 | ​ 2、主从只负责各自的读和写,极大程度缓解X锁和S锁争用。 16 | 17 | ​ 3、slave可以配置myiasm引擎,提升查询性能以及节约系统开销。 18 | 19 | ​ 4、master直接写是并发的,slave通过主库发送来的binlog恢复数据是异步。 20 | 21 | ​ 5、slave可以单独设置一些参数来提升其读的性能。 22 | 23 | ​ 6、增加冗余,提高可用性。 24 | 25 | ### 2、读写分离的配置 26 | 27 | ##### 1、硬件配置 28 | 29 | ``` 30 | master 192.168.85.11 31 | slave 192.168.85.12 32 | proxy 192,168.85.14 33 | ``` 34 | 35 | ##### 2、首先在master和slave上配置主从复制 36 | 37 | ##### 3、进行proxy的相关配置 38 | 39 | ```shell 40 | #1、下载mysql-proxy 41 | https://downloads.mysql.com/archives/proxy/#downloads 42 | #2、上传软件到proxy的机器 43 | 直接通过xftp进行上传 44 | #3、解压安装包 45 | tar -zxvf mysql-proxy-0.8.5-linux-glibc2.3-x86-64bit.tar.gz 46 | #4、修改解压后的目录 47 | mv mysql-proxy-0.8.5-linux-glibc2.3-x86-64bit mysql-proxy 48 | #5、进入mysql-proxy的目录 49 | cd mysql-proxy 50 | #6、创建目录 51 | mkdir conf 52 | mkdir logs 53 | #7、添加环境变量 54 | #打开/etc/profile文件 55 | vi /etc/profile 56 | #在文件的最后面添加一下命令 57 | export PATH=$PATH:/root/mysql-proxy/bin 58 | #8、执行命令让环境变量生效 59 | source /etc/profile 60 | #9、进入conf目录,创建文件并添加一下内容 61 | vi mysql-proxy.conf 62 | 添加内容 63 | [mysql-proxy] 64 | user=root 65 | proxy-address=192.168.85.14:4040 66 | proxy-backend-addresses=192.168.85.11:3306 67 | proxy-read-only-backend-addresses=192.168.85.12:3306 68 | proxy-lua-script=/root/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua 69 | log-file=/root/mysql-proxy/logs/mysql-proxy.log 70 | log-level=debug 71 | daemon=true 72 | #10、开启mysql-proxy 73 | mysql-proxy --defaults-file=/root/mysql-proxy/conf/mysql-proxy.conf 74 | #11、查看是否安装成功,打开日志文件 75 | cd /root/mysql-proxy/logs 76 | tail -100 mysql-proxy.log 77 | #内容如下:表示安装成功 78 | 2019-10-11 21:49:41: (debug) max open file-descriptors = 1024 79 | 2019-10-11 21:49:41: (message) proxy listening on port 192.168.85.14:4040 80 | 2019-10-11 21:49:41: (message) added read/write backend: 192.168.85.11:3306 81 | 2019-10-11 21:49:41: (message) added read-only backend: 192.168.85.12:3306 82 | 2019-10-11 21:49:41: (debug) now running as user: root (0/0) 83 | 84 | ``` 85 | 86 | ##### 4、进行连接 87 | 88 | ```shell 89 | #mysql的命令行会出现无法连接的情况,所以建议使用客户端 90 | mysql -uroot -p123 -h192.168.85.14 -P 4040 91 | ``` 92 | 93 | -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570541665646.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570541665646.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570541838485.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570541838485.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570541946471.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570541946471.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570542045332.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570542045332.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570542254949.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570542254949.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570542341604.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570542341604.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570542415955.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570542415955.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570542471948.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570542471948.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570542688796.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570542688796.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570604493708.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570604493708.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570605309658.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570605309658.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570605325400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570605325400.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570605553095.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570605553095.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570703264912.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570703264912.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570714549624.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570714549624.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570714565647.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570714565647.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570714576819.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570714576819.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570714615915.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570714615915.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570714660961.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570714660961.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/1570776205802.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/1570776205802.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/typora-user-images/image-20191203125003597.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/typora-user-images/image-20191203125003597.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/分区表的底层原理.md: -------------------------------------------------------------------------------- 1 | # 分区表的底层原理 2 | 3 | ​ 分区表由多个相关的底层表实现,这个底层表也是由句柄对象标识,我们可以直接访问各个分区。存储引擎管理分区的各个底层表和管理普通表一样(所有的底层表都必须使用相同的存储引擎),分区表的索引知识在各个底层表上各自加上一个完全相同的索引。从存储引擎的角度来看,底层表和普通表没有任何不同,存储引擎也无须知道这是一个普通表还是一个分区表的一部分。 4 | 5 | ​ 分区表的操作按照以下的操作逻辑进行: 6 | 7 | ​ **select查询** 8 | 9 | ​ 当查询一个分区表的时候,分区层先打开并锁住所有的底层表,优化器先判断是否可以过滤部分分区,然后再调用对应的存储引擎接口访问各个分区的数据 10 | 11 | ​ **insert操作** 12 | 13 | ​ 当写入一条记录的时候,分区层先打开并锁住所有的底层表,然后确定哪个分区接受这条记录,再将记录写入对应底层表 14 | 15 | ​ **delete操作** 16 | 17 | ​ 当删除一条记录时,分区层先打开并锁住所有的底层表,然后确定数据对应的分区,最后对相应底层表进行删除操作 18 | 19 | ​ **update操作** 20 | 21 | ​ 当更新一条记录时,分区层先打开并锁住所有的底层表,mysql先确定需要更新的记录再哪个分区,然后取出数据并更新,再判断更新后的数据应该再哪个分区,最后对底层表进行写入操作,并对源数据所在的底层表进行删除操作 22 | 23 | ​ 有些操作时支持过滤的,例如,当删除一条记录时,MySQL需要先找到这条记录,如果where条件恰好和分区表达式匹配,就可以将所有不包含这条记录的分区都过滤掉,这对update同样有效。如果是insert操作,则本身就是只命中一个分区,其他分区都会被过滤掉。mysql先确定这条记录属于哪个分区,再将记录写入对应得曾分区表,无须对任何其他分区进行操作 24 | 25 | ​ 虽然每个操作都会“先打开并锁住所有的底层表”,但这并不是说分区表在处理过程中是锁住全表的,如果存储引擎能够自己实现行级锁,例如innodb,则会在分区层释放对应表锁。 -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/前缀索引实例说明.md: -------------------------------------------------------------------------------- 1 | # 前缀索引实例说明 2 | 3 | ​ 有时候需要索引很长的字符串,这会让索引变的大且慢,通常情况下可以使用某个列开始的部分字符串,这样大大的节约索引空间,从而提高索引效率,但这会降低索引的选择性,索引的选择性是指不重复的索引值和数据表记录总数的比值,范围从1/#T到1之间。索引的选择性越高则查询效率越高,因为选择性更高的索引可以让mysql在查找的时候过滤掉更多的行。 4 | 5 | ​ 一般情况下某个列前缀的选择性也是足够高的,足以满足查询的性能,但是对应BLOB,TEXT,VARCHAR类型的列,必须要使用前缀索引,因为mysql不允许索引这些列的完整长度,使用该方法的诀窍在于要选择足够长的前缀以保证较高的选择性,通过又不能太长。 6 | 7 | 案例演示: 8 | 9 | ```sql 10 | --创建数据表 11 | create table citydemo(city varchar(50) not null); 12 | insert into citydemo(city) select city from city; 13 | 14 | --重复执行5次下面的sql语句 15 | insert into citydemo(city) select city from citydemo; 16 | 17 | --更新城市表的名称 18 | update citydemo set city=(select city from city order by rand() limit 1); 19 | 20 | --查找最常见的城市列表,发现每个值都出现45-65次, 21 | select count(*) as cnt,city from citydemo group by city order by cnt desc limit 10; 22 | 23 | --查找最频繁出现的城市前缀,先从3个前缀字母开始,发现比原来出现的次数更多,可以分别截取多个字符查看城市出现的次数 24 | select count(*) as cnt,left(city,3) as pref from citydemo group by pref order by cnt desc limit 10; 25 | select count(*) as cnt,left(city,7) as pref from citydemo group by pref order by cnt desc limit 10; 26 | --此时前缀的选择性接近于完整列的选择性 27 | 28 | --还可以通过另外一种方式来计算完整列的选择性,可以看到当前缀长度到达7之后,再增加前缀长度,选择性提升的幅度已经很小了 29 | select count(distinct left(city,3))/count(*) as sel3, 30 | count(distinct left(city,4))/count(*) as sel4, 31 | count(distinct left(city,5))/count(*) as sel5, 32 | count(distinct left(city,6))/count(*) as sel6, 33 | count(distinct left(city,7))/count(*) as sel7, 34 | count(distinct left(city,8))/count(*) as sel8 35 | from citydemo; 36 | 37 | --计算完成之后可以创建前缀索引 38 | alter table citydemo add key(city(7)); 39 | 40 | --注意:前缀索引是一种能使索引更小更快的有效方法,但是也包含缺点:mysql无法使用前缀索引做order by 和 group by。 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/红黑树.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/03_MySQL/红黑树.png -------------------------------------------------------------------------------- /09_SystemDesign/03_MySQL/覆盖索引.md: -------------------------------------------------------------------------------- 1 | # 覆盖索引 2 | 3 | 1、当发起一个被索引覆盖的查询时,在explain的extra列可以看到using index的信息,此时就使用了覆盖索引 4 | 5 | ```sql 6 | mysql> explain select store_id,film_id from inventory\G 7 | *************************** 1. row *************************** 8 | id: 1 9 | select_type: SIMPLE 10 | table: inventory 11 | partitions: NULL 12 | type: index 13 | possible_keys: NULL 14 | key: idx_store_id_film_id 15 | key_len: 3 16 | ref: NULL 17 | rows: 4581 18 | filtered: 100.00 19 | Extra: Using index 20 | 1 row in set, 1 warning (0.01 sec) 21 | 22 | ``` 23 | 24 | 2、在大多数存储引擎中,覆盖索引只能覆盖那些只访问索引中部分列的查询。不过,可以进一步的进行优化,可以使用innodb的二级索引来覆盖查询。 25 | 26 | 例如:actor使用innodb存储引擎,并在last_name字段又二级索引,虽然该索引的列不包括主键actor_id,但也能够用于对actor_id做覆盖查询 27 | 28 | ```sql 29 | mysql> explain select actor_id,last_name from actor where last_name='HOPPER'\G 30 | *************************** 1. row *************************** 31 | id: 1 32 | select_type: SIMPLE 33 | table: actor 34 | partitions: NULL 35 | type: ref 36 | possible_keys: idx_actor_last_name 37 | key: idx_actor_last_name 38 | key_len: 137 39 | ref: const 40 | rows: 2 41 | filtered: 100.00 42 | Extra: Using index 43 | 1 row in set, 1 warning (0.00 sec) 44 | 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /09_SystemDesign/04_GameSystem/SLG/01_SLGArchitecture.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/09_SystemDesign/04_GameSystem/SLG/01_SLGArchitecture.md -------------------------------------------------------------------------------- /09_SystemDesign/04_GameSystem/SLG/02_SLGServer.md: -------------------------------------------------------------------------------- 1 | # 服务器架构文档 2 | 3 | ## 一、服务器架构图 4 | 5 | 根据需求,可将服务器大致分为登录服务器、逻辑服务器、文件服务器、支付服务器、国战服务器和聊天服务器 6 | 7 | ### 1.服务器间进程通信 8 | 9 | 服务器之间的进程通信使用json-rpc,json-rpc的底层为短连接实现,通信为json数据格式 10 | 11 | ### 2.游戏服务器 12 | 13 | | 服务器\属性 | 外网ip | 内网ip | 端口 | 管理端口 | 14 | | :---------- | :-------- | :-------- | :--- | -------- | 15 | | 登录服务器 | 127.0.0.1 | 127.0.0.1 | 0 | 0 | 16 | | 文件服务器 | 127.0.0.1 | 127.0.0.1 | 0 | 0 | 17 | | 国战服务器 | 127.0.0.1 | 127.0.0.1 | 0 | 0 | 18 | | 支付服务器 | 127.0.0.1 | 127.0.0.1 | 0 | 0 | 19 | | 逻辑服务器 | 127.0.0.1 | 127.0.0.1 | 0 | 0 | 20 | | 聊天服务器 | 暂无 | 暂无 | 暂无 | 暂无 | 21 | 22 | ### 3.Redis服务器集群 23 | 24 | | 服务器\属性 | 外网ip | 内网ip | 端口 | 密码 | 25 | | :----------------- | :-------- | :-------- | :--- | ------ | 26 | | Redis Sentinel集群 | 127.0.0.1 | 127.0.0.1 | 0 | 无 | 27 | | Redis主服务器1 | 127.0.0.1 | 127.0.0.1 | 0 | 123456 | 28 | | Redis从服务器1 | 127.0.0.1 | 127.0.0.1 | 0 | 无 | 29 | | Redis主服务器2 | 127.0.0.1 | 127.0.0.1 | 0 | 123456 | 30 | | Redis从服务器2 | 127.0.0.1 | 127.0.0.1 | 0 | 无 | 31 | 32 | ### 4.Memcache服务器 33 | 34 | | 服务器\属性 | 外网ip | 内网ip | 端口 | 35 | | :---------- | :-------- | :-------- | :--- | 36 | | Memcache | 127.0.0.1 | 127.0.0.1 | 0 | 37 | 38 | ### 5.MySQL服务器 39 | 40 | ```java 41 | |服务器\属性|外网代理ip|内网ip|外网端口|账号|密码| 42 | |:-------- |:-------------|:-----------|:---| 43 | |MySQL |无|127.0.0.1|0|root|123456| 44 | ``` 45 | 46 | - 备注:开发阶段所有服务器部署在同一台物理服务器 47 | 48 | ## 二、逻辑服务器系统架构 49 | 50 | 其中,游戏逻辑服务器的系统架构如下: 51 | 52 | 1. 游戏客户端为Cocos2d,与服务器交互采用Http通信,数据传输采用Json格式字符串 53 | 2. 服务器端的网络层使用基于Netty实现的Http服务器 54 | 3. 通过Netty接入客户端请求,根据请求数据中的协议号,调用服务器中相对应的逻辑模块 55 | 4. 逻辑模块处理消息,若要处理游戏数据则调用Jedis或Hibernate处理,若触发某事件,则调用事件处理器 56 | 5. 通过Netty的ChannelHandlerContext返回处理结果 57 | 6. 客户端与服务器交互的数据通过XXTea+Base64进行加密处理 58 | 59 | ## 三、服务器启动 60 | 61 | 服务器启动顺序如下: 62 | 63 | 1. MySQL服务器 64 | 2. Memcache服务器 65 | 3. Redis Sentinel 66 | 4. Redis Masters 67 | 5. Redis Slaves 68 | 6. 登录服务器 69 | 7. 支付服务器 70 | 8. 逻辑服务器 71 | 9. 文件服务器 72 | 10. 聊天服务器 73 | 11. 国战服务器 -------------------------------------------------------------------------------- /09_SystemDesign/04_GameSystem/SLG/03_Sort.md: -------------------------------------------------------------------------------- 1 | # 如果给100万人排序怎么排? 2 | 3 | 5000个桶, 实时记录分数变化, 特殊处理前两百名. 每个人排名就是之前桶的总和. 4 | 5 | 参考: https://blog.codingnow.com/2014/03/mmzb_db_2.html -------------------------------------------------------------------------------- /09_SystemDesign/04_GameSystem/SLG/04_SLGData.md: -------------------------------------------------------------------------------- 1 | # SLG手游Java服务器数据管理 2 | 3 | https://www.cnblogs.com/hjcenry/p/5856941.html -------------------------------------------------------------------------------- /09_SystemDesign/05_IMSystem.md: -------------------------------------------------------------------------------- 1 | # TCP or UDP? 2 | 3 | http://www.52im.net/thread-33-1-1.html 4 | 5 | ## 路由器的端口映射老化时间? 6 | 7 | 由于路由器端口映射的存在,加上智能终端频繁、长时间的休眠,TCP长连接的实用性在移动互联网情况下极大地打了折扣。 8 | 9 | 也因为如此,移动端IM、推送系统必须实现所谓的心跳包机制,以保持端口映射关系的老化时间不会减少到0而被回收,从而避免连接中断。 10 | 11 | ## 服务端承载能力 12 | 13 | 从承载能力来想的话, TCP必然是最保险的实现, 但是其计算, 网络资源的消耗也是巨大的. 14 | 15 | 百万级别的TCP连接的网络服务, 其编程, 程序复杂度, 调试难度, 运维成本, 网络成本都远高于UDP. 16 | 17 | 所以如果当某个服务不是基于流的服务, 可以考虑用UDP来实现, 可能更合适. 18 | 19 | ## 高级应用网络通讯要求 20 | 21 | 移动端IM系统、推送系统一方面提供终端在线服务,另外一方面也需要考虑内容信息的完整性和安全性。毕竟信息的丢失,或者通讯的被窃听,都是难以接受的。而TCP不管在网络层的可靠性控制,还是在应用层的安全支持(例如HTTPS),都为应用提供无法替代的强大功能和便利。 22 | 23 | ## 结论 24 | 25 | 现在的移动端IM、推送系统,既面对移动互联网的不确定性,又面对智能终端频繁的系统休眠、网络切换,还要考虑服务端的承载成本,对于在线服务而言UDP是比TCP更适合的方式。但是由于数据完整性、安全性的需要,又不应完全放弃TCP的可靠与安全。 26 | 27 | 个人认为, 更恰当的方式应该是:**两种通信协议同时使用,各有侧重。** 28 | UDP: ==**大量终端的在线与控制**==(发收心跳包) 29 | TCP: ==**应用与业务**== 30 | 31 | -------------------------------------------------------------------------------- /09_SystemDesign/Untitled.md: -------------------------------------------------------------------------------- 1 | ​ -------------------------------------------------------------------------------- /09_SystemDesign/resources.md: -------------------------------------------------------------------------------- 1 | I read this book on a friend’s recommendation who cleared FANG interviews. I’ll share pros and cons of this book, but overall, I will highly recommend it. This book is a must-have for all junior and mid-level engineers; for senior engineers, you will need more. 2 | 3 | I went through other resources like books, youtube videos, blogs, online courses, etc., I’ll try to list the best ones here to augment the studies (you can end up wasting your time on not-so-good resources, learn from my experience). 4 | 5 | Pros: 6 | - This book is concise and easy to read. You can easily finish it in a week. 7 | -This book provides a step-by-step process to answer system design questions. This process was the best thing they have done. I will recommend this for everyone (senior/junior). 8 | -The other best part of this book is the number of case studies they have discussed; for that alone, this book is worth every penny. In an interview, make sure you can explain these solutions. 9 | -Each case study has reasonable depth; it is enough for up to mid-level engineers. 10 | 11 | 12 | Cons: 13 | - Senior engineers (me being one of them) have to read more. The same authors have an advanced system design online course (designgurus dot org); I went through it, easy read, highly recommend for senior engineers. More resources at the end. 14 | - The book covers important system design concepts like Load Balancing, Cashing, Proxies, etc. Some parts of it are great like Consistent Hashing, CAP/PACELC theorems, Bloom Filters, etc., but for some I had to read elsewhere to find details. 15 | - I felt a lack of details in a few case studies. Probably they wanted to make it a small book; see below resources that helped me the most to cover this. 16 | 17 | 18 | Other best resources 19 | - Free github repo: system design primer 20 | - Free online book: Distributed systems for fun and profit 21 | - Website: highscalability dot com 22 | - Book: Designing Data-Intensive Application (big book, focus on chapters 3, 5, 6, and 9) 23 | - Youtube MIT 6.824: Distributed Systems 24 | 25 | 26 | 27 | 28 | 29 | 1年内 主要是筑基: 健全和完善基本技术能力, 做到能够独立完成上级发布的任务. 30 | 3年内 成长为高级工程师: 准备开始走设计路线,比如系统设计等, 在游戏行业达到资深水平. 31 | 5年内 持续学习, 在专业领域能够独当一面. 32 | 5年以后 向架构师的方向前进, 做到在行业内有前瞻性, 能统筹规划, 推动业务创新. -------------------------------------------------------------------------------- /100_Other/01_Golang/Go基础语法.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/100_Other/01_Golang/Go基础语法.md -------------------------------------------------------------------------------- /100_Other/01_Golang/Go设计模式.md: -------------------------------------------------------------------------------- 1 | # Golang设计模式参考 2 | 3 | Golang常用设计模式: http://t.csdn.cn/0Q88z 4 | 5 | -------------------------------------------------------------------------------- /100_Other/01_Golang/Test.md: -------------------------------------------------------------------------------- 1 | **引导** 2 | 3 | => Java JVM IO Linux内核 4 | 5 | 6 | 7 | 8 | 9 | => Redis 10 | 11 | => MySQL => MySQL锁 -- MVCC? -- InnoDB? MyISAM? 12 | 13 | => NetWork 14 | 15 | 不要聊项目 16 | 17 | **实在不行** 18 | 19 | 20 | 21 | SpringCloud 扯一扯架构 22 | 23 | 验证码生成的过程10倍加速 => JWT生成token 24 | 25 | (用System.nanoTime() System.currentTimeMillis() 测的) 他们原来怎么不行 慢? 我怎么快? 26 | 27 | Redis => 验证码 28 | 29 | 灰度发布 => Ribbon&Zuul 30 | 31 | 计价规则 => .... 32 | 33 | 34 | 35 | (登陆注册模块 36 | 37 | 定义热点数据并缓存在Redis, 38 | 39 | 用Redis存储登录ticket和验证码, 解决分布式session问题. 40 | 41 | 用JVM指令排查出GC问) 42 | 43 | # CV 44 | 45 | Java基础 => 扯一下JVM => GC 46 | 47 | 数据结构算法 => 排序 树 (拓扑) 48 | 49 | 常见设计模式 50 | 51 | BIO NIO epoll => IO-Main => OS的细节 => Netty(epoll & select区别) => epoll_ctl, eopll_create, epoll_wait => 中断? 52 | 53 | TCP UDP HTTP1.0 HTTP1.1 HTTP2 HTTPS 54 | 55 | MySQL => 索引 => 优化 => 执行过程 56 | 57 | Redis => 源码大概架构 => 图 => 分片 => AKF 58 | 59 | 分布式锁 分布式事务 60 | 61 | 62 | 63 | OAuth JWT => 了解 64 | 65 | 不是特别重要 SpringCloud => (Eureka,Ribbon,Zuul) 66 | 67 | 68 | 69 | # AI C Go Python 70 | 71 | HTTPS 72 | 73 | OS内存管理 74 | 75 | GC? 76 | 77 | 78 | 79 | # Java -> Go 80 | 81 | **项目** 82 | 83 | zk? 强一致性 选举 读写分离? 84 | 85 | 数组 链表区别? 86 | 87 | 进程信息 88 | 89 | PCB? 90 | 91 | ## OS => Linux 92 | 93 | mmu? 94 | 95 | 页表 96 | 97 | 98 | 99 | 100 | 101 | 手写LRU 102 | 103 | AVL树 快排? 104 | 105 | ConcurrentHashMap 106 | 107 | TCP UDP 108 | 109 | 三挥四握 110 | 111 | 网址到显示过程 => DNS? 112 | 113 | 僵尸 孤儿进程 114 | 115 | 死锁 死锁条件? 116 | 117 | MySQL锁 118 | 119 | MVCC? 120 | 121 | InnoDB? MyISAM? 122 | 123 | git? 124 | 125 | 快速排序 126 | 127 | 128 | 129 | 130 | 131 | # Go高并发 Java高并发 132 | 133 | map 134 | 135 | channel 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /100_Other/01_JVM梳理.md: -------------------------------------------------------------------------------- 1 | new => -------------------------------------------------------------------------------- /100_Other/02_Erlang/01_基础知识: -------------------------------------------------------------------------------- 1 | # Erlang 2 | 3 | 上帝说: 要有一门面向未来的语言, 于是有了erlang 4 | https://zhuanlan.zhihu.com/p/26341437 -------------------------------------------------------------------------------- /100_Other/02_Redis梳理.md: -------------------------------------------------------------------------------- 1 | 说一下你在项目中的redis的应用场景? 2 | 3 | ``` 4 | 1,5大value类型:根据我的redis课有场景的介绍 5 | 2,基本上就是缓存~! 6 | 3,为的是服务无状态,延申思考,看你的项目有哪些数据结构或对象,在单机里需要单机锁,在多机需要分布式锁,抽出来放入redis中; 7 | 4,无锁化 8 | ``` 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /97_Pic/Pasted image 20220209212709.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/97_Pic/Pasted image 20220209212709.png -------------------------------------------------------------------------------- /97_Pic/Pasted image 20220209215251.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/97_Pic/Pasted image 20220209215251.png -------------------------------------------------------------------------------- /97_Pic/Pasted image 20220209215709.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/97_Pic/Pasted image 20220209215709.png -------------------------------------------------------------------------------- /97_Pic/Pasted image 20220210011115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/97_Pic/Pasted image 20220210011115.png -------------------------------------------------------------------------------- /97_Pic/Pasted image 20220220205114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/97_Pic/Pasted image 20220220205114.png -------------------------------------------------------------------------------- /99_Test/01_All/05_Redis.md: -------------------------------------------------------------------------------- 1 | # Redis为什么快? 2 | 3 | # TPS/QPS是多少? 4 | 5 | 10w 6 | 7 | # 解决了项目什么问题? 8 | 9 | - 为什么快? 10 | 11 | 基于单线程 12 | 13 | - 14 | 15 | # Redis rehash 16 | 17 | rehash有2种工作模式 18 | 19 | lazy rehashing:在每次对dict进行操作的时候执行一个slot的rehash 20 | 21 | active rehashing:每100ms里面使用1ms时间进行rehash。 22 | 23 | 这两者==都是渐进式hash==, 这是rehash的两种模式, 24 | 前者是主动渐进, 比如100ms拿出1ms用来rehash; 25 | 而后者则是请求来的的那个数据顺便rehash一下. 26 | 27 | -------------------------------------------------------------------------------- /99_Test/01_All/07_王道操作系统知识.md: -------------------------------------------------------------------------------- 1 | # 王道考研操作系统知识点整理 2 | 3 | https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/24.html 4 | 5 | -------------------------------------------------------------------------------- /99_Test/01_All/09_Algo.md: -------------------------------------------------------------------------------- 1 | # Hash 2 | 3 | Hash算法解决冲突的方法一般有以下几种常用的解决方法 4 | 5 | ## 开放定址法(使用散列公式计算寻址下一个散列地址) 6 | 7 | 所谓的开放定址法就是一旦发生了冲突, 就去寻找下一个空的散列地址, 只要散列表足够大, 空的散列地址总能找到, 并将记录存入 8 | 公式为:fi(key) = (f(key)+di) MOD m (di=1,2,3,……,m-1) 9 | ※ 用开放定址法解决冲突的做法是:当冲突发生时, 使用某种探测技术在散列表中形成一个探测序列. 沿此序列逐个单元地查找, 直到找到给定的关键字, 或者 10 | 碰到一个开放的地址(即该地址单元为空)为止(若要插入, 在探查到开放的地址, 则可将待插入的新结点存人该地址单元). 查找时探测到开放的地址则表明表 11 | 中无待查的关键字, 即查找失败. 12 | 比如说, 我们的关键字集合为{12,67,56,16,25,37,22,29,15,47,48,34},表长为12. 我们用散列函数f(key) = key mod l2 13 | 当计算前S个数{12,67,56,16,25}时, 都是没有冲突的散列地址, 直接存入: 14 | 15 | ![image-20220419204202560](https://s2.loli.net/2022/04/19/6vnh9BwXTq1M3lY.png) 16 | 计算key = 37时, 发现f(37) = 1, 此时就与25所在的位置冲突. 17 | 于是我们应用上面的公式f(37) = (f(37)+1) mod 12 = 2. 于是将37存入下标为2的位置: 18 | ![image-20220419204213859](https://s2.loli.net/2022/04/19/OfYpGle4SUdCI1B.png) 19 | 20 | ## 再哈希法(换一个哈希函数) 21 | 22 | 再哈希法又叫双哈希法, 有多个不同的Hash函数, 当发生冲突时, 使用第二个, 第三个, …., 等哈希函数 23 | 计算地址, 直到无冲突. 虽然不易发生聚集, 但是增加了计算时间. 24 | 25 | ## 链地址法(基础的HashMap做法) 26 | 27 | 链地址法的基本思想是:每个哈希表节点都有一个next指针, 多个哈希表节点可以用next指针构成一个单向链表, 被分配到同一个索引上的多个节点可以用这个单向 28 | 链表连接起来, 如: 29 | 键值对k2, v2与键值对k1, v1通过计算后的索引值都为2, 这时及产生冲突, 但是可以通道next指针将k2, k1所在的节点连接起来, 这样就解决了哈希的冲突问题 30 | ![这里写图片描述](https://s2.loli.net/2022/04/19/6tcD2G7LwVXb9Q8.jpg) 31 | 32 | ## 建立公共溢出区() 33 | 34 | 这种方法的基本思想是:将哈希表分为基本表和溢出表两部分, 凡是和基本表发生冲突的元素, 一律填入溢出表 35 | 36 | # 一致性哈希 与 哈希槽 37 | 38 | 上来先说一个误区,**Redis 集群没有使用一致性hash, 而是引入了哈希槽slots的概念。**可以参考我的另一篇文章《redis系列之——高可用(主从、哨兵、集群)》。 39 | 40 | 我们说的一致性hash都不是缓存机器自身的功能,而是**==集群前置的代理或客户端实现的==**。 41 | 而redis官方的集群是集群本身通过slots实现了数据分片。 42 | 43 | ==**redis集群是3.0版本才出现的**==,出现的比较晚,在集群模式出现之前,很多公司都做了自己的redis集群了。这些自研的redis集群的实现方式有多种,比如在redis的jedis客户端jar包就是实现了一致性hash算法(客户端模式),或者在redis集群前面加上一层前置代理如Twemproxy也实现了hash一致性算法(代理模式)。Twemproxy,是 Twitter 开源出来的 Redis 和 Memcached 代理,使用这种代理模式搭建的集群,我们的客户端连接只需要连接代理服务器即可,不用连接代理后面具体的redis机器。Twemproxy具体使用哪一种hash算法也是可以通过配置文件指定的。 44 | -------------------------------------------------------------------------------- /99_Test/01_All/66_Soft.md: -------------------------------------------------------------------------------- 1 | 面试前准备的四要素: 2 | 简历,面经,知己知彼,录音设备 3 | 面试: 4 | 5 | 1. 不会的不要写,可以事先制定理想简历,面向简历学习 6 | 2. 有经验准备未写期望薪资的简历交给技术面试官 7 | 面经:临时抱佛脚 8 | 知己知彼:主要业务,主打产品和公司发展历程,准备一个和产品业务相关的问题 9 | 录音:面试录音,复盘 10 | 1. 错题 11 | 2. 沟通表达能力 12 | 自我介绍三要素: 13 | 3. 秀优势,自认为优秀但怕被忽视的点 14 | 4. 套近乎,几句话把对公司的信息收集用起来 15 | 3. 摆技术,讲擅长的技术 16 | 提问环节: 17 | 6. 基础知识和算法,运气+实力 18 | 7. 框架中间件,源码问题中有不会的,可以往自己会的地方引导 19 | 3. 项目相关,必须能阐明主要业务,亮点,遇到的问题,如何解决的,获得的经验和方法论 20 | hr面试: 21 | 9. 职业生涯规划 22 | 2. 表现出有深耕本地长久发展打算的意向 23 | 24 | # 总分总 25 | 26 | # 答问题的时候把相似的东西说出来 27 | 28 | => **==讲故事, 引用场景, 优缺点==** 29 | 30 | 31 | 32 | ![](https://s2.loli.net/2022/03/30/dHl4IQJMN1orXSP.png) 33 | 34 | 35 | 36 | ### **理想**: 37 | 38 | 我还没真的仔细聊过这件事, 您要是不问我, 我都很少审视自己的理想, 我现在个人的一个想法就是先踏踏实实的走完技术这条路, 在专业方面成为一个牛人 39 | 生活上就是做一个普通人, 对社会做出自己的一个贡献, 大概就是这样. 40 | 41 | ### **职业规划**: 42 | 43 | 没有那么长, 三五年之内, 把自己的技术提升一个台阶, 本身对技术感兴趣, 对于业务, 架构, 底层知识做一个深入研究, 期望以后能为公司在技术层面做出自己的贡献. 44 | 45 | ### **缺点**: 46 | 47 | 自尊心过强, 觉得自己做的决定是理性的对的, 但是在大学生活的一个过程中, 我发现所有人的看法或者建议都是有道理的, 并且任何事情也应该多角度思考, 渐渐的已经对自己有一些不同的认知了. 48 | 49 | 50 | 51 | ### **优点**: 52 | 53 | 挺刻苦, 能在一个方向专注坚持, 学习能力还算不错 54 | 也比较喜欢分享知识, 和人交流, 合群. 55 | 容易交流 56 | 57 | ### **反问**: 58 | 59 | 我了解到咱们是一个 XX公司 嘛, 我想了解一下现在面试的一个组是什么, 这个方便聊聊吗? 60 | 面试评价, 如果进入二面会更注重考察哪方面的知识或者说能力呢? 61 | 作为一个新人, 如果进入了贵公司,你们对校招生的要求有哪些呢? 62 | 63 | 64 | 65 | ### 你手里还有其他offer吗? 66 | 67 | 我现在还有几个正在面试, 都在差不多这个环节了, 如果这边offer能尽快下来的话, 我还是更倾向于贵公司, 我也会停止其他两家的流程. 68 | 69 | 70 | 71 | ### 在学校最有成就感的事情是什么? 72 | 73 | 大一的时候积极帮助班级事务, 选上了军训负责人就是负责军训时期的各种事情,刚开学嘛, 事情也挺多的, 那时候特别忙, 但也很充实. 74 | 75 | 大二的,一个比较小的, 就是两天内就学会了滑板的一个根翻动作, 这也是我第一个翻板动作, 虽然那时候练的身上都是淤青...但是那时候太开心了, 因为有的学长练了很久都没出来, 76 | 77 | 大三的时候排球小白, 那时候还从来没打过, 然后经过一个星期的训练, 我位置是副攻,负责拦网和配合主攻进攻, 比赛的时候还给队伍贡献了几个好球, 最后拿到了全校第二的奖, 很开心. 78 | 79 | 现在的话就是如果能拿到心仪公司的offer才算最有成就感的事情了. 80 | 81 | 82 | 83 | ### 为什么想进游戏行业? 84 | 85 | 因为游戏行业未来应该会平稳发展, 个人对于游戏开发有了一定的经验, 也对其感兴趣,也是有一种情怀吧, 所以来了咱们淘米, 我小时候还真想过来工作, 就挺倾向于咱们公司的. 86 | 87 | ### 你对于游戏开发的优势? 88 | 89 | 我对于游戏服务端开发有一定的经验, 也经常参与一些游戏开发社群的交流, 我对游戏开发也非常感兴趣. 90 | 91 | ### 你不会C++? 92 | 93 | C++之前也有一些了解, Java也是面向对象的嘛, C语言大学期间也学过, 而且Redis源码就是C写的, 再者我的学习能力自认为还不错, 所以上手起来我相信也会非常快. 94 | 95 | ### 游戏行业的未来发展 96 | 97 | 目前看来的话, 最近游戏版号开放, 对于游戏行业整体来说恢复了一些元气, 然后由于疫情原因户外活动减少了许多, 所以游戏行业也会有一定的利好, 未来也一定会稳定发展, 最终我认为应该元宇宙应该是游戏的最终发展形式. 98 | -------------------------------------------------------------------------------- /99_Test/03_AE/Haiy.md: -------------------------------------------------------------------------------- 1 | springboot核心注解 2 | 3 | springcloud zuul eureka能干啥 能做什么 4 | 5 | -------------------------------------------------------------------------------- /99_Test/03_AE/HotorGames.md: -------------------------------------------------------------------------------- 1 | 四核八线程的原理 2 | 32位操作系统和64位操作系统的区别 3 | HashMap如何实现的 => 具体流程 具体数据结构 4 | HashMap是线程安全的吗? 5 | ConcurrentHashMap如何实现线程安全的? 6 | select和epoll的区别? 为什么要epoll? 如何实现的? 7 | 操作系统权限管理 => root权限如何设置的? 8 | chmod和chown 9 | Redis里面String是如何查找的... 10 | Redis如何实现各种数据结构, zsocre命令如何使用的, 使用原理是什么... 11 | 计算机网络...三次握手握手 四次分手过程 12 | ping命令的实现? 13 | 进程线程区别 => 线程是如何调度的 14 | 说说你对协程的了解 15 | id pid 把扁平结构树化 -------------------------------------------------------------------------------- /99_Test/03_AE/ProjectZ.md: -------------------------------------------------------------------------------- 1 | ### 职位描述 2 | 3 | \- 职位职责: 4 | \- 参与公司产品服务器端研发和产品迭代 5 | \- 参与产品的技术实现讨论和Code Review 6 | \- 参与产品的性能、架构优化 7 | \- 职位要求: 8 | \- 熟悉Go语言开发,1年以上开发经验(应届不要求经验),有多语言开发经验者优先(Java, Python, Golang) 9 | \- 掌握mysql,redis等主流数据库的使用方式 10 | \- 熟悉网络、操作系统知识、常用的数据结构、算法,并能将这些知识应用在实际开发中 11 | \- 具备良好的编码风格,在开发过程中有良好的单测和自测习惯 12 | \- 具备REST、RPC以及微服务架构设计、开发能力 13 | \- 有 IM(Instant Messaging即时通讯) 相关经验优先 14 | \- 具备良好的团队合作精神,有解决问题、钻研新技术的兴趣和能力,对业内新技术及趋势有较强的敏感度 15 | \- 思路清晰,具备良好理解能力,有强烈的责任心,抗压能力强 16 | \- 计算机及相关专业本科以上学历 17 | 18 | ### 公司介绍 19 | 20 | 我们是一家成立于上海的元宇宙社交产品公司,我们正在努力创造一种更沉浸式、趣味感十足的年轻人的元宇宙社交新方式。我们的愿景是降低社交门槛,让全世界有相同兴趣爱好的年轻人更快速地找到志同道合的小伙伴,轻松玩转各类线上活动。 21 | 这里汇聚了来自谷歌,微软,阿里,百度,京东等国内外知名互联网公司背景的小伙伴。核心成员们都有过成功创业经历,熟悉海外市场,技术过硬。团队发展潜力巨大,目前已获得创新工场及其他知名一线基金投资。 22 | 产品主页: https://www.projz.com 23 | 24 | 登陆注册如何实现的, 整体流程. -------------------------------------------------------------------------------- /99_Test/03_AE/Tap4Fun.md: -------------------------------------------------------------------------------- 1 | https://www.nowcoder.com/discuss/655324?type=post&order=create&pos=&page=1&ncTraceId=&channel=-1&source_id=search_post_nctrack&subType=2 2 | 3 | tap4fun一面: 二面hr三面主管没怎么问技术,就只贴一面了 4 | 5 | - 项目 怎么优化sql语句 6 | - 数据库的索引结构 为什么是B+树 优点 7 | - 文档型数据库采用什么数据结构,为什么用哈希,为什么用B树,B树优点 8 | - hashmap concurrenthashmap 怎么实现hash方法 9 | - 一致性哈希的场景的场景题,为什么需要一致性哈希 10 | - volatile能保证线程安全吗,为什么 11 | - 网络安全,网络攻击 了解吗 12 | - 智力题:分金条 13 | - 最近看什么书 14 | - 反问 15 | 16 | 17 | 18 | 19 | 链接:https://www.nowcoder.com/discuss/756716?type=post&order=create&pos=&page=1&ncTraceId=&channel=-1&source_id=search_post_nctrack&subType=2 20 | 21 | 笔试都是选择题 22 | 9.24钉钉面试差不多50min 求一个offer 23 | 1.自我介绍 常用语言c++ c# 24 | 2.使用c++和c#的区别感受(以前没碰到过 没准备 然后只想到了多继承不同 实在是回答的很片面 25 | 3.c++ stl 数据结构vector 可变数组(2倍1.5倍扩容取决于系统?) list 链表 map set 红黑树 unorderedmap unorderedset 哈希表(散列表) 26 | 4.哈希表怎么实现 怎么解决冲突 拉链 27 | 5.协程(分时分部 迭代器 )具体的yield return 的暂停几帧,unity如何停下?毕竟是单线程(不是真正意义上的暂停) 28 | 6.委托和事件 (像一个函数组的指针?我说是对函数的封装) 29 | 7.智能指针相关 unique shared weak 30 | 8.c# unity中的gc如何判断当前的内存是可以回收的(提示我与智能指针shared一样 有个计数为0就处于失活状态 被惊艳到了 也是c++和c#的一大区别) 31 | 9.堆栈区别 32 | 10.tcp三次握手 四次挥手 为什么握手要三次 33 | 11.反射概念 缺点?(只是学过 没用过) 34 | 12.mvc(项目相关)最后发现其实本质也是事件中心。。。 35 | 13.常用设计模式 单例模式面向对象? unity是面向对象的吗?(自言自语了一堆,回答了是 ,一个物体一个类) 36 | 14.找链表的环 复杂度(回答了o(n),快慢指针循环两圈) 37 | 15.大数相乘 忘了。。。(最后告诉我两种方法 1.模拟笔算2.一个位数拆开的方法 具体忘了) 38 | 16.遇到的问题 如何解决 (说了上一次面试的 点击行走 添加了寻路相关的,这里面试官谈到 希望在这工作的同学能多了解底层(求求让我进)) 39 | 17.最近看了什么书 说了推理小说《黑睡莲》(真的很好看,不知道是不是太诚实了。。。) 40 | 18.反问 问了自己表现怎么样(这个问题一般不能问来着 我贼激动) 41 | 求求求offer 42 | 43 | (hr面就不写了) 44 | ———————————————————————- 45 | 10.11终面 20min(原来是是问技术吗,我流泪了) 46 | 这次以为不是技术 没有录音… 凭记忆写 47 | 面试官感觉好严肃 太难了 我直接一个怂 48 | 1.你对客户端的了解是什么(我说用户看见的都是客户端) 49 | 8.最近玩了什么游戏 (说奥日2)有什么实现困难的地方(说了基础的移动条约和流畅度的问题,太难了) 50 | 2.想学什么方向…为什么(网络) 51 | 3.项目中做了什么功能 对于恰好打到敌人那一瞬间扣血的功能……(我说在动画里写事件,貌似不对) 52 | 4.项目中mvc模式了解多少(讲了puremvc会用 内核不太知道 是事件中心) 53 | 5.对于寻路算法 了解多少 a*是深搜还是广搜(我觉得是广啊……)属于问傻我了 54 | 6.最近学了什么 2d骨骼动画(问了一下原理和好处)我哪知道啊…. 55 | 7.最后给了两个建议:1持续学习基础 不管是算法还是unity还是网络 2对学习的东西进行深入了解 56 | —————// 57 | 10.13 已offer 感谢各位 是心仪offer 结束秋招啦 58 | 59 | 60 | 61 | 62 | 63 | 作者:牛客240801010号 64 | 链接:https://www.nowcoder.com/discuss/637893?type=post&order=create&pos=&page=1&ncTraceId=&channel=-1&source_id=search_post_nctrack&subType=2 65 | 来源:牛客网 66 | 67 | 笔试是在猿圈上做的,做完4-5天就来了一面。 68 | 69 | 一面是技术面,先是自我介绍,还问了平时玩的游戏后,就开始正式的技术面了。 70 | 71 | C++:指针,多态,深拷贝,浅拷贝,大端小端,字节对齐,以前的项目中有过什么困难及怎么解决的。 72 | 还问有设计模式相关的问题,学校学过但忘完了根本答不上来。 73 | 我以前主要用UE,询问我是否愿意用unity。 74 | 75 | 现在收到二面通知,不想去,技术面给我整怕了,我是没信心了。 -------------------------------------------------------------------------------- /99_Test/03_AE/YouDao3.31.md: -------------------------------------------------------------------------------- 1 | ## 游戏项目 2 | 3 | 如何实现同步? 4 | 帧同步 状态同步? 5 | 困难的问题是什么? 6 | 如何解决粘包问题? 7 | 你说用到了Netty, 如果如果地图太大, 如何解决这个并发问题? 8 | 9 | ## 微服务项目 10 | 11 | 最困难的问题是什么? 12 | 验证码生成过程? 13 | 订单生成的流程? 14 | JWT如何实现的? 15 | Restful Api是什么? 16 | 你对SpringCloud有什么了解? 17 | 你对RPC有什么了解? 18 | 服务发现, 服务注册中心用的什么? 19 | 你对分布式有什么了解? 20 | 21 | ## JVM 22 | 23 | 类加载过程? 24 | GC算法有哪些? 具体细节? 25 | 26 | ## Java 27 | 28 | 线程池有没有用过? 线程池优点? 节省系统资源, 节省的到底是什么? -------------------------------------------------------------------------------- /99_Test/03_AE/test.md: -------------------------------------------------------------------------------- 1 | 背景:社招 不到一年 2 | 网易有道一面 3 | 4 | spring 有那些特性? 5 | 6 | spring ioc / di 是什么? 7 | 8 | spring aop 是怎么实现的? 9 | 10 | 依赖注入的两个注解的有什么区别? 11 | 12 | 如果使用@Autowired byType ,这个类型的bean不是唯一会怎么样?怎么解决? 13 | 14 | bean的循环引用怎么解决的? 15 | 16 | bean 作用域有哪些? 17 | 18 | 什么情况下会使用非单例的bean? 19 | 20 | 使用单例bean要注意什么问题? 21 | 22 | 分布式情况怎么加锁? 23 | 24 | redis分布式锁的原理? 25 | 26 | redis分布式锁,线程在拿到锁之后挂掉了。会不会造成死锁?怎么解决? 27 | 28 | 有用到spring的事物吗? 29 | 30 | 常用的集合有哪些? 31 | 32 | 当中线程安全的有哪些? 33 | 34 | concurrenthashmap怎么实现的线程安全? 35 | 36 | cas的过程是什么?cas有什么缺点? 37 | 38 | synchronized 和 reentrantlock 的比较,哪个性能好? 39 | 40 | synchronized实现的原理是什么?怎么做到可重入的? 41 | 42 | cookie 和 session 。。。 43 | 44 | Java 1.8 的新特性有什么? 45 | 46 | stream的foreach 和 直接手写的for那个效率高?为什么? 47 | 48 | 算法题:字符串是否存在重复字符。 49 | 50 | 51 | 52 | 53 | 54 | # 网易互联网Java一面面经 55 | 56 | **一面 时间:9.26 形式:[网易]()网页视频 时长:30min** 57 | 58 | 1.自我介绍 59 | 2.为什么方法一般不建议长递归? 60 | 3.volatile关键字有什么作用? 61 | 4.synchronized锁升级的策略是什么? 62 | 5.G1和CMS垃圾回收器的区别? 63 | 6.CMS垃圾回收过程中哪几个步骤会发生stop the world? 64 | 7.NIO实现的模型是什么?是同步还是异步的?和异步IO的区别是什么? 65 | 8.Spring Boot的starter的自动装配机制? 66 | 9.@Resource和@Autowired有什么区别? 67 | 10.TCP三次握手流程?TCP的冷启动? 68 | 11.HTTP2.0相对于HTTP1.0有哪些变化? 69 | 12.MySQL事务的隔离级别有哪些?出现脏读的是哪种?出现幻读的是哪种? 70 | 13.explain一般会出现几种索引扫描类型?filesort出现在哪种场景下? 71 | 14.项目 72 | 15.Rocket MQ的存储模型,即持久化机制是什么? 73 | 16.Redis的持久化策略有哪些? 74 | 17.Redis集群模式?部署模式是中心化还是非中心化的部署模式? 75 | 18.反问 76 | 77 | **总结** :提前批的时候投的[网易有道](),笔试A了3道挂了,正式批果断选了一个不那么卷的部门智慧企业事业部,顺利拿到了面试,但是感觉自己已经心有余而力不足了,面不动了 😔整个面试的节奏的还是比较快的,一个问题接着一个问题,答的很一般,有的问题自己甚至都没听过 -------------------------------------------------------------------------------- /99_Test/03_AE/万兴.md: -------------------------------------------------------------------------------- 1 | 万兴 => 全问的项目细节 2 | 3 | 4 | Hash冲突的解决办法?为什么线性寻址法慢? 5 | MySQL存储引擎有哪些? 6 | B+树是什么? 如何实现的? 为什么用B+树? 7 | 内存泄露是什么? 8 | 五层网络的结构全部说一遍, ISP网络运营商在哪几层? 9 | 局部性原理是什么? 10 | 随机IO和顺序IO谁快? 为什么? 磁盘寻道为什么慢? 11 | Kafka结构是什么? 零拷贝是什么? 12 | 13 | 项目亮点有什么? 14 | 排行榜系统为什么用到了RocketMQ, 有什么好处? 15 | RocketMQ使用的目的是什么? MQ能用来干嘛? 16 | JSON是反射, Protobuf是生成的代码, 为什么Protobuf快? -------------------------------------------------------------------------------- /99_Test/03_AE/有可能的coding.md: -------------------------------------------------------------------------------- 1 | 排列组合如何计算? 2 | 3 | ![image-20220329234838709](https://s2.loli.net/2022/03/29/MrZJEwFOmozXi2d.png) 4 | 5 | 6 | 7 | # 快排(partition) 8 | 9 | 10 | 11 | # 由1, 2, 3, 4组成的三位数中, 有多少数是无重复且互不相等的? 12 | 13 | A43 return 24; // A43 14 | 15 | 递归回溯选择; 16 | 17 | 还可以迭代选择 18 | 19 | # m*n的二维数组, 初始地址为100, 每个数据大小s, 找坐标数组下标i,j的数据地址 20 | 21 | retrun i * s + (j + 1) * s + 100; // 公式计算起始地址 -------------------------------------------------------------------------------- /99_Test/03_AE/有可能的场景题.md: -------------------------------------------------------------------------------- 1 | # 如何防止模拟恶意请求? 2 | 3 | 如何防止模拟的http的恶意请求: http://t.csdn.cn/TUWfx 4 | 5 | nginx 屏蔽恶意请求: http://t.csdn.cn/BORgr 6 | 7 | springboot项目中接口防止恶意请求多次: http://t.csdn.cn/02l6a, https://www.jianshu.com/p/6901f854e3c0 8 | 9 | springboot 自定义拦截器 防止恶意请求: https://www.cnblogs.com/wanjun-top/p/12974538.html 10 | 11 | # 微信扫描pc端二维码登录的步骤 12 | 13 | 这个得用到微信的开放接口吧? 待会去找找看. 14 | 15 | 16 | 17 | SQL语句, 子查询, 联合查询... -------------------------------------------------------------------------------- /99_Test/ResumeTest.md: -------------------------------------------------------------------------------- 1 | # 易堞 - 后端工程师 - Java 2 | 3 | ::: left 4 | icon:info **男/2000.12** 5 | **[icon:github https://github.com/handsomeyi](https://github.com/handsomeyi)** 6 | ::: 7 | 8 | ::: right 9 | [icon:email ](mailto:deeyees@126.com)**** 10 | icon:school **湖南财政经济学院/本科/计算机科学与技术** 11 | ::: 12 | 13 | ## 专业技能 14 | 15 | * 具有扎实Java基础, 熟悉常见的**数据结构与算法**, 了解常见设计模式. 16 | 17 | * 掌握**Java虚拟机**基本原理, 包括内存模型, 垃圾回收机制, 类加载机制等. 18 | 19 | * 熟悉**IO模型**, 深入理解**BIO**, **NIO**, **epoll**. 20 | 21 | * 掌握计算机网络, 熟悉TCP, UDP, HTTP等协议. 22 | 23 | * 熟悉**Redis**, 持久化, 主从复制, 哨兵, 分片集群. 24 | 25 | * 熟悉**MySQL**, 熟悉存储引擎, 索引机制, 查询机制, 对SQL优化有一定了解与使用. 26 | 27 | * 了解**SpringCloud**及其常用组件, 注册中心Eureka, 负载均衡器Ribbon, 分布式锁, 分布式事务, 网关Zuul Gateway. 28 | 29 | * 熟悉Restful API开发规范, 熟悉三方授权OAuth协议, JWT协议. 30 | 31 | * 熟悉**Hadoop**的HDFS存储技术, **Spark**大数据计算框架. 32 | 33 | ## 项目经历 34 | 35 | ### 英雄传说(多人在线网游) (2020.06一2021.02) 36 | 37 | `Netty` `GoogleProtobuf` `Redis` `Groovy ` `RocketMQ` `ELK` 38 | 39 | - f 40 | 41 | 42 | ### 逸品专车(网约车) (2021.05一2022.01) 43 | 44 | `SpringBoot` `SpringCloud` `Redis` `Ribbon` `Zuul` `RocketMQ` 45 | 46 | * **项目描述**: 此项目是一款网约车软件, 主要功能包括: 乘客端: 登录注册, 下单, 支付, 评价, 维护个人信息等. 司机端: 登录, 出车收车, 接乘客到订单完成, 发起收款等. 47 | 48 | 地图模块: 主要是接入第三方地图实现. 车辆同步, 车辆调度, 里程查询, 轨迹点查询, 路径规划, 距离计算等. 49 | 50 | 派单引擎: 实时单派单, 预约单派单, 抢单等. 51 | 52 | 订单系统: 订单创建, 修改, 状态变更等. 53 | 54 | 计价系统: 基础计价, 动态调价, 分时段计价. 55 | 56 | 支付系统: 接入微信支付, 支付宝支付. 余额充值, 资金冻结, 订单退款等. 57 | 58 | 账号系统: 乘客注册登录, 乘客信息维护, 司机录入, 司机登录, 司机信息维护等. 59 | 60 | * **项目职责** 61 | 62 | 1. 乘客和司机端登陆模块: 生成验证码, 调用第三方服务发送验证码, 校验验证码, 通过JWT生成token, 在网关进行校验. 63 | 64 | 2. 灰度发布(为了支持A/B testing): 参与了灰度发布的实现, 利用Zuul和Ribbon, 通过网关与服务之间, 服务与服务之间两种方式进行灰度发布. 65 | 66 | 3. 预估价格模块: 收藏路线, 创建订单, 获取地图资源, 获取计价规则, 存储计价规则. 67 | 68 | 4. 优化: 发现生成验证码可以优化10倍速度; 利用策略模式对代码进行重构. 69 | 70 | ### -------------------------------------------------------------------------------- /99_Test/Tip.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### 下载文件的协议:HTTP、FTP、P2P 4 | 5 | - 下载一个文件可以使用 HTTP 或 FTP,这两种都是集中下载的方式,而 P2P 则换了一种思路,采取非中心化下载的方式 6 | - P2P 也是有两种,一种是依赖于 tracker 的,也即元数据集中,文件数据分散;另一种是基于分布式的哈希算法,元数据和文件数据全部分散 7 | 8 | https://www.cnblogs.com/kumata/p/9242978.html 9 | 10 | -------------------------------------------------------------------------------- /99_Test/Untitled.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomeyi/Notes/f83eb9bcdc5c5a8d6491325783be2545c61a8078/99_Test/Untitled.md -------------------------------------------------------------------------------- /99_Test/互联网装逼词汇.md: -------------------------------------------------------------------------------- 1 | ```java 2 | 包装 闭环 布局 场景 沉淀 抽离 抽离透传 抽象 串联 垂直领域 刺激 打法 倒逼 点线面 迭代 定性定量 对标 对齐 发力 反哺 方法论 附能 复盘 复用打法 赋能 格局 跟进 关键路径 规模 话术 3 | 击穿心智:了解用户需求,从而提高用户体验 价值 价值转化 兼容 交互 接地气 结果导向 精细化 矩阵 聚焦 颗粒 颗粒度 快速响应 宽松 拉通 联动 量化 漏斗 履约 落地 纽带 强化认知 强化心智去中心化 认知 商业 式 生命周期 生态 梳理 输出 体系 同步 痛点 挖掘 完善逻辑 维度 细分 下钻 响应 协同 引爆点 影响力 中台 4 | 重组 抓手 资源倾斜 组合拳 边界 5 | ``` 6 | 7 | 8 | 9 | ```java 10 | // 2013年 11 | 互联网思维 用户痛点 刚性需求 自媒体 战略性亏损 BAT 标准化产品 咖啡馆 颠覆 逼格 12 | 13 | // 2014年 14 | 大数据 粉丝经济 生态系统 O2O IP 跨界 投资人 垂直 专车 用户体验 15 | 16 | // 2015年 17 | 互联网+ 从0到1 创业孵化平台 联席CEO 互联网4.0 上门服务 多元化 联合创始人 互联网峰会 快速迭代 18 | 19 | // 2016年 20 | 下半场 首席某某某 黑科技 网红 国民某某 闭环 独角兽 护城河 pro和plus 风口 21 | 22 | // 2017年 23 | 共享 赋能 无人 新零售 泛娱乐 AI 全球化 天使投资 边际 平台战略 24 | 25 | // 2018年 26 | 区块链 打法 优化 渠道下沉 社交电商 壁垒 全面屏 消费降级 解决方案 场景 27 | 28 | // 2019年 29 | 硬核 私域流量 5G/5G+ 新XX模式 基础设施 用户画像 正式商用 趋于理性 赛道 电商直播 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 图解 2 | 3 | ## JVM 4 | 5 | https://www.processon.com/view/link/62161daa1efad407c7c9d7ec 6 | 7 | ## IO 8 | 9 | https://www.processon.com/view/link/621838aa079129079ae1de16 10 | 11 | ## MySQL 12 | 13 | https://www.processon.com/view/link/621c4c570e3e745d894d3a42 14 | 15 | ## SpringCloud 16 | 17 | https://www.processon.com/view/link/621838ea079129079ae1df40 18 | 19 | ## MyBatis 20 | 21 | https://www.processon.com/view/link/62183928079129079ae1e07f 22 | 23 | # Notes 24 | 25 | 有些图片可能加载较慢 或加载不出 26 | 27 | 要用代理才能加载 28 | 29 | # Resource 30 | 31 | ### 以下资源来自互联网 32 | 33 | 知识仓库: https://github.com/doocs/advanced-java 34 | 35 | 基础知识: https://www.zhihu.com/question/414988138/answer/2083841630 36 | 37 | 架构知识: https://www.cnblogs.com/crazymakercircle/p/13917517.html 38 | 39 | 游戏架构: https://blog.codingnow.com/ 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ![image-20220405164723274](https://s2.loli.net/2022/04/05/t52MovYNGgiBbVJ.png) 50 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # JUC to ProcessOn 2 | 0% 3 | 4 | --- 5 | # JVM to ProcessOn 6 | 50% 7 | 8 | --- 9 | # 兵器库 10 | 1% 11 | 12 | --- 13 | # Spring Cloud 14 | ## PO 15 | 90% 16 | ## KnowLedge(经验) 17 | 10% 18 | 19 | --- 20 | # taxi 21 | 30% 22 | 23 | --- 24 | # IO ~ NetWork ~ Netty 25 | 30%~ 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | --------------------------------------------------------------------------------