mysql主从复制实现读写分离(一)

RECOMMEND

一、题目描述: 给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。 一般来说,删除节点可分为两个步骤: 首先找到需要删除的节点; 如果找到了,删除它。 说明: 要求算法时间复杂度为 O(h),h 为树的高度。 示例: >root = [5,3,6,2,4,null,7] > key = 3 >![](https://img-blog.csdnimg.cn/20210115133530614.png) >给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。 > >一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。 > ![](https://img-blog.csdnimg.cn/20210115133552237.png) >另一个正确答案是 [5,2,6,null,4,null,7]。 > ![](https://img-blog.csdnimg.cn/20210115133608480.png) 二、解答 分析 解题思路: 首先,这个题目可以根据删除的节点的左右节点来判断。 而找到该节点是非常简单的,因为这棵树是二叉搜索树,而二叉搜索树的特性,左节点的值一定小于该节点值,右节点的值一定大于该节点的值,所以直接搜索就可以找到该值。 所以重点在于怎么判断该节点的左右节点的情况。 大致可以分为四种: 1. 该节点没有左节点,也没有右节点 2. 该节点没有左节点,但有右节点 3. 该节点有左节点,但没有右节点 4. 该节点有左节点,也有右节点 第一种:对于第一种情况,直接将该节点删除即可。 第二种:对于第二种情况,直接删除节点,将左节点代替该节点。 第三种:对于第三种情况:直接删除节点,将右节点代替该节点。 第四种:对于第四种情况,又可以分为三种情况: 1. 该节点的左节点没有右节点,将左节点代替该节点。 2. 该节点的右节点没有左节点,将右节点代替该节点。 3. 对于都有的情况,为了保证二叉搜索树的结构,我们 ① :可以用该节点的左节点最右节点的值代替该节点;②:也可以用该节点的右节点的最左节点的值代替该节点。 >而对于最后的情况,也就是第四种情况的第三种情况, >需要注意 >①中,如果最右节点还有左节点,我们可以用最右节点的左节点的值代替最右节点所在的位置; >②中,如果最左节点还有右节点,我们可以用最左节点的右节点的值代替最左节点所在的位置。 **再一次总结归纳:** 其实,最后第四种情况的第三种就包括了前面所有的方面, 在找到该节点后: 1. 如果该节点的左节点不为空,我们用该节点的左节点最右节点的值代替该节点; 2. 否则,如果该节点的右节点不为空,我们可以用该节点的右节点的最左节点的值代替该节点。 3. 否则,将该节点置空。 找到该节点,非常容易,因为左节点的值一定小于该节点值,右节点的值一定大于该节点的值。 所以,从根节点开始遍历 1. 如果遍历到的节点的值大于该值,该值一定处于该节点的右子树,往右遍历即可。 2. 否则,如果遍历到的节点的值小于该值,该值一定处于该节点的左子树,往左遍历即可。 3. 否则,就是找到了该值,在进行上述操作即可。 >时间复杂度:O(h),其中 n 为树的高度。 代码 ```java /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { public TreeNode deleteNode(TreeNode root, int key) { if(root!=null){ if(root.val == key){ if(root.left != null){ root.val = leftMax(root); root.left = deleteNode(root.left, root.val); } else if(root.right != null){ root.val = rightMin(root); root.right = deleteNode(root.right, root.val); } else { root = null; } }else if(root.val>key){ root.left = deleteNode(root.left,key); }else{ root.right = deleteNode(root.right,key); } return root; } return null; } public int rightMin(TreeNode root) { root = root.right; while (root.left != null) root = root.left; return root.val; } public int leftMax(TreeNode root) { root = root.left; while (root.right != null) root = root.right; return root.val; } } ``` > 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户 内存消耗:39.2 MB, 在所有 Java 提交中击败了8.92%的用户 三、官方解答 ```java class Solution { /* One step right and then always left */ public int successor(TreeNode root) { root = root.right; while (root.left != null) root = root.left; return root.val; } /* One step left and then always right */ public int predecessor(TreeNode root) { root = root.left; while (root.right != null) root = root.right; return root.val; } public TreeNode deleteNode(TreeNode root, int key) { if (root == null) return null; // delete from the right subtree if (key > root.val) root.right = deleteNode(root.right, key); // delete from the left subtree else if (key < root.val) root.left = deleteNode(root.left, key); // delete the current node else { // the node is a leaf if (root.left == null && root.right == null) root = null; // the node is not a leaf and has a right child else if (root.right != null) { root.val = successor(root); root.right = deleteNode(root.right, root.val); } // the node is not a leaf, has no right child, and has a left child else { root.val = predecessor(root); root.left = deleteNode(root.left, root.val); } } return root; } } ``` > 参考: > 1、[题目](https://leetcode-cn.com/problems/delete-node-in-a-bst/) > 2、[官方解答](https://leetcode-cn.com/problems/delete-node-in-a-bst/solution/shan-chu-er-cha-sou-suo-shu-zhong-de-jie-dian-by-l/) >本文首发于CSDN,作者:lomtom >原文链接:**[https://blog.csdn.net/qq_41929184/article/details/112662236](https://blog.csdn.net/qq_41929184/article/details/112662236)** >个人网站:**[https://lomtom.top](https://lomtom.top)**,公众号:**博思奥园**,同步更新。 > > **你的支持就是我最大的动力。** ![](https://img-blog.csdnimg.cn/20200405094243147.png)
2021-01-17
一、题目描述: 给定一个无重复元素的有序整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。 列表中的每个区间范围 [a,b] 应该按如下格式输出: - "a->b" ,如果 a != b - "a" ,如果 a == b 示例 示例 1: >输入:nums = [0,1,2,4,5,7] 输出:["0->2","4->5","7"] 解释:区间范围是: [0,2] --> "0->2" [4,5] --> "4->5" [7,7] --> "7" 示例 2: >输入:nums = [0,2,3,4,6,8,9] 输出:["0","2->4","6","8->9"] 解释:区间范围是: [0,0] --> "0" [2,4] --> "2->4" [6,6] --> "6" [8,9] --> "8->9" 示例 3: >输入:nums = [] 输出:[] 示例 4: >输入:nums = [-1] 输出:["-1"] 示例 5: >输入:nums = [0] 输出:["0"] 提示: >0 <= nums.length <= 20 -231 <= nums[i] <= 231 - 1 nums 中的所有值都 互不相同 nums 按升序排列 二、解答 分析 解题思路: 1. 遍历整个数组,当相邻的数只相差1时,构成一个区间。 2. 当相邻的数相差大于1时,开始一个新的区间。 3. 当判断开始一个新的区间时,我们需要保存前面的区间。 4. 保存一个区间需要两个值分别记录区间开始的值,当前数的前一个数的值。 5. 唯一值得注意的是处理边界问题。 >时间复杂度:O(n),其中 n 为数组的长度。我们只需要遍历一次数组即可。 空间复杂度:O(1)。只需要常数空间存放若干变量。 代码 ```cpp class Solution { public List<String> summaryRanges(int[] nums) { int length = nums.length; List<String> result = new ArrayList<>(); if(length == 0){ return result; } int start = nums[0]; int now = nums[0]; int i = 1; for(;i < length;i++){ if(now + 1 == nums[i]){ now++; } else{ result.add(start == now?String.valueOf(start):start+"->"+now); start = now = nums[i]; } } result.add(start == now?String.valueOf(start):start+"->"+now); return result; } } ``` > 执行用时:7 ms, 在所有 Java 提交中击败了82.54%的用户 内存消耗:36.9 MB, 在所有 Java 提交中击败了53.76%的用户 改进 1. 因为当前数的前一个数的值,我可以通过当前的下标来获取,所以该值完全没有必要记录,所以可以省略该值 2. 省略该值之后,我只要通过下标来判断即可,所以在循环里,我只要判断前一个数加一与当前值不相等即可 ```java public List<String> summaryRanges(int[] nums) { int length = nums.length; List<String> result = new ArrayList<>(); if(length == 0){ return result; } int start = nums[0]; int i = 1; for(;i < length;i++){ if(nums[i - 1] + 1 != nums[i]){ result.add(start == nums[i - 1]?String.valueOf(start):start+"->"+nums[i - 1]); start = nums[i]; } } result.add(start == nums[i - 1]?String.valueOf(start):start+"->"+nums[i - 1]); return result; } ``` 这样做的好处就是节省了一定的空间。 三、官方解答 官方给出的题解也是一次遍历,但是是用双指针的方法,也就是使用维护下标low 和 high 分别记录区间的起点和终点,和我的大致思想也差不多。 ```cpp class Solution { public List<String> summaryRanges(int[] nums) { List<String> ret = new ArrayList<String>(); int i = 0; int n = nums.length; while (i < n) { int low = i; i++; while (i < n && nums[i] == nums[i - 1] + 1) { i++; } int high = i - 1; StringBuffer temp = new StringBuffer(Integer.toString(nums[low])); if (low < high) { temp.append("->"); temp.append(Integer.toString(nums[high])); } ret.add(temp.toString()); } return ret; } } ``` > 参考: > 1、[题目](https://leetcode-cn.com/problems/summary-ranges/) > 2、[官方解答](https://leetcode-cn.com/problems/summary-ranges/solution/hui-zong-qu-jian-by-leetcode-solution-6zrs/) >本文首发于CSDN,作者:lomtom 原文链接:**[https://blog.csdn.net/lomtom/article/details/112306554](https://blog.csdn.net/lomtom/article/details/112306554)** 个人网站:**[https://lomtom.top](https://lomtom.top)**,公众号:**博思奥园**,同步更新。 你的支持就是我最大的动力。 ![](https://img-blog.csdnimg.cn/20200405094243147.png)
2021-01-10
一、swagger是什么? swagger是一种基于Rest 风格的api文档开发工具,我们常常应用于前后端分离项目,解决由于前后端分离导致的数据接口不一致问题,有效的减少前端程序员与后端程序员的打斗次数。 swagger自己是这样介绍swagger的: 1. Swagger是一组功能强大且易于使用的API开发人员工具套件,适用于团队和个人,可在整个API生命周期(从设计和文档到测试和部署)中进行开发。 2. Swagger由开放源代码,免费和市售工具共同组成,它使任何人(从技术工程师到街头智能产品经理)都可以构建每个人都喜欢的惊人API。 3. Swagger由SmartBear Software构建,后者是团队软件质量工具的领导者。SmartBear落后于软件领域的一些知名企业,包括Swagger,SoapUI和QAComplete。 ![](https://img-blog.csdnimg.cn/20201002212241455.png) 二、使用swagger 1、引入依赖 在`pom.xml`文件中加入依赖 ```cpp <!--swagger2--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency> ``` 2、配置 主要从这几个方面来配置swagger > 1、配置swagger配置 > 2、配置swagger扫描 > 3、配置swagger2设置分组 > 4、实体类设置 2.1、配置swagger配置 首先若要使用swagger需要在配置文件上加上`@EnableSwagger2`注解,使用swagger2生效。 以下就是最简单的配置方法 ```cpp @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket docket(Environment environment){ return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()); } /** * 配置swagger信息 */ public ApiInfo apiInfo(){ return new ApiInfo( "教科文", "即使再小的帆也能远航", "1.0", "urn:tos", new Contact("lomtom", "", "lomtom@qq.com"), "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<>()); } } ``` 如果你不知道ApiInfo的字段含义,我们可以查看他的构造方法,里面分别是它所代表的含义,这个相信会一点英文的都能够看得懂。 而ApiInfo并没有自己属性的set方法,所以我们只能使用构造方法来进行注入。 ```cpp 1、ApiInfo构造方法 public ApiInfo(String title, String description, String version, String termsOfServiceUrl, Contact contact, String license, String licenseUrl, Collection<VendorExtension> vendorExtensions) { this.title = title; this.description = description; this.version = version; this.termsOfServiceUrl = termsOfServiceUrl; this.contact = contact; this.license = license; this.licenseUrl = licenseUrl; this.vendorExtensions = Lists.newArrayList(vendorExtensions); } 2、其中的Contact构造方法 public Contact(String name, String url, String email) { this.name = name; this.url = url; this.email = email; } ``` 此时无需配置其他的我们也可以直接使用swagger。 打开[http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html). 在我的项目里一共有三个Controller:`LoginController、RegisterController、OssController` ![](https://img-blog.csdnimg.cn/20201002222742819.png) 其中1、红色框框内就是我配置的一些信息,而标注的①②③是我自己的`controller`,额外的④是默认的`controller`,也就是我们访问出错的`/error`页面 ![](https://img-blog.csdnimg.cn/20201002223044875.png) 2.2、配置swagger扫描 若我们在开发中不想看到默认的controller或者需要隐藏其他的controller,我们可以使用以下配置 ```cpp @Bean public Docket docket(Environment environment){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() //指定要扫描的包 .apis(RequestHandlerSelectors.basePackage("com.lomtom.website")) //过滤路径 //.paths(PathSelectors.ant("/admin/**")) .build(); } ``` 其中,apis参数中RequestHandlerSelectors一共有五种配置: ```cpp //1、basePackage:指定要扫描的包 //2、any():扫描全部 //3、none():都不扫描 //4、withClassAnnotation():类上的 //5、withMethodAnnotation():方法的 ``` 而.paths(PathSelectors.ant("/admin/**")),指的是我只扫描`/admin/`路径下的所有请求。 2.3、配置swagger2设置分组 当我们在实际的开发中,一个项目往往由多个开发人员共同协作完成的,而swagger恰好可以在这方面解决这一问题。 ```cpp @Bean public Docket docket1(Environment environment) { return new Docket(DocumentationType.SWAGGER_2) .groupName("ou"); } @Bean public Docket docket2(Environment environment) { return new Docket(DocumentationType.SWAGGER_2) .groupName("yang"); } ``` 我们再在原来的基础上加上两个Docket,当然实际中我们需要配置的往往不会这么简单,这里只是举例说明。 可以看到,在原来的分组中多了两个组,这样我们的程序员就可以只查看自己的负责的接口了。 ![](https://img-blog.csdnimg.cn/20201002224135724.png) 2.4、实体类设置 我们可以在实体类中对我们的model对象进行一些说明。`@ApiModel`对实体类的说明,` @ApiModelProperty`对类的属性的说明。 ```cpp @Data @TableName("admin_user") @ApiModel("管理员用户类") public class AdminUserEntity { @TableId(type = IdType.AUTO) private Long id; @ApiModelProperty("用户名") private String userName; private String password; private Date creatTime; } ``` 除此之外: **swagger的常用API** 1. api标记 Api 用在类上,说明该类的作用。可以标记一个Controller类做为swagger 文档资源,使用方式: ```cpp @Api(value = "/user", description = "Operations about user") ``` 2. ApiOperation标记 ApiOperation:用在方法上,说明方法的作用,每一个url资源的定义,使用方式: ```cpp @ApiOperation( value = "Find purchase order by ID", notes = "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", response = Order, tags = {"Pet Store"}) ``` 3. ApiParam标记 ApiParam请求属性,使用方式: ```cpp public R save(@RequestBody @ApiParam(value = "save user", required = true) User user) ``` 4. ApiResponse ApiResponse:响应配置,使用方式: @ApiResponse(code = 400, message = "Invalid user supplied") 5. ApiResponses ApiResponses:响应集配置,使用方式: @ApiResponses({ @ApiResponse(code = 400, message = "Invalid Order") }) 6. ResponseHeader 响应头设置,使用方法 @ResponseHeader(name="head1",description="response head conf") 例如:我在我的上传文件的controller上加上注解说明 ```cpp @Api(tags = "OSS管理") @RestController public class OssController { @ApiOperation("获取上传图片的api凭证") @GetMapping("/oss/upload") protected R poliocy(HttpServletRequest request, HttpServletResponse response) { } } ``` 这样就可以对我们的接口进行中文说明。 ![](https://img-blog.csdnimg.cn/20201002225133390.png) 2.5、接口测试 swagger还为程序员提供了接口的测试功能,例如:测试login接口,填上需要的信息,点击下方的`Try it out`进行测试。 ![](https://img-blog.csdnimg.cn/20201002225438341.png) 从显示的数据中可以清晰地到看到我们所需要的信息:请求地址、请求头、请求体、状态码、回应头信息。 ![](https://img-blog.csdnimg.cn/20201002225639935.png) 三、拓展 这里有这样一个问题,也常常会作为面试题出现: ```cpp 问题:怎么使swagger在开发环境下使用,发布时不使用? ``` 答:需要建立好几个`springboot`配置文件,例如`application.yml`、`application-pro.yml`、`application-dev.yml`,分别用于默认、部署后、开发的环境中。在`application.yml`指定当前使用的是哪一个 ```yml spring: profiles: active: dev ``` 然后,再在swagger配置中配置,如果当前环境使用的是dev的配置文件,就是用`enable(flag)`,来关闭swagger功能。 这样就能使项目上线后不暴露接口,有效的提高安全性。 ```cpp @Bean public Docket docket(Environment environment){ Profiles profiles = Profiles.of("dev"); boolean flag = environment.acceptsProfiles(profiles); return new Docket(DocumentationType.SWAGGER_2) .groupName("lomtom") .apiInfo(apiInfo()) .enable(flag); } ``` > 总的来说,swagger是一个很好用的api文档工具,并且上手难度不高,最关键的是能减少前后端程序员的打斗次数,你值得拥有。 > > [1、swagger 介绍及两种使用方法](https://blog.csdn.net/weixin_37509652/article/details/80094370)
2021-01-04

CONTACT ME

You can contact me in the following ways

Copyright © 2019-2020 Made with love By LomTom | 湘ICP备19023870号 | 经历风雨 666 天 6 小时 6 分 6 秒