ARTS打卡

775 阅读24分钟

ARTS 引言

每周至少做一个 leetcode 的算法题、阅读并点评至少一篇英文技术文章、学习至少一个技术技巧、分享一篇有观点和思考的技术文章。(也就是 Algorithm、Review、Tip、Share 简称 ARTS)你需要坚持至少一年。

ARTS打卡第一周(2019-08-04)

Algorithm

今天的算法题目是“存在重复元素”,从今天这道简单的算法题中,被折磨的死去活来的,不知道从何下手,只会最简单的遍历,结果是报了超时了。随后,自己思考了一阵,发现有官方题解,去里面寻找了一下解体思路,一下子明朗起来。也发现了自己对于理论的不足!

Review

今日阅读文章:“Regex tutorial — A quick cheatsheet by examples”

关于正则表达式的:

总结:从中又再次复习了正则表达式的一些内容,因为英文的缘故,后面的一些有点理解有点吃力,基础部分的语法得到了复习,今后要继续加强对正则表达式的练习。

Tip

今天的tips就分享我自己写的经典快速排序吧(Java 8 默认的sort方法的排序性能已经很高了),按照其他人再加上自己的理解写出来的(性能还是有待提升的),如果有错误,还请大家帮忙纠正,

基于Java编写的:

public static void quickSort(int[] arr, int left, int right) {
        if (right - left > 0) {
            int left1 = left;
            int right1 = right;
            int pivot = arr[left];
            while(left1 <= right1) {
                while (pivot > arr[left1]) {
                    ++ left1;
                }

                while(pivot < arr[right1]) {
                    -- right1;
                }

                if (left1 <= right1) {
                    if (left1 == right1) {
                        arr[left] = arr[right1];
                        arr[right1] = pivot;
                    } else {
                        int temp = arr[right1];
                        arr[right1] = arr[left1];
                        arr[left1] = temp;
                    }

                    ++ left1;
                    -- right1;
                }
            }

            if (left < right1) {
                quickSort(arr, left, right1);
            }

            if (left1 < right) {
                quickSort(arr, left1, right);
            }

        }
    }

Share

juejin.cn/post/684490…

这是一篇关于快速排序的文章,因为今天的算法题,有考到关于排序的要点,所以自己就去检索了几篇关于快速排序的文章,这篇我认为是比较好的,也让我今天对快速排序有了更深的认识(我算法基础很差的)。希望也可以帮助到其他人!

ARTS打卡第二周(2019-08-12)

Algorithm 本周算法:136.只出现一次的数字 这次算法题因为受第一周算法的影响,以及它的题目描述中的“你可以不使用额外空间来实现吗?”,于是我就采用了排序加循环比较的方式。 我的题解思路:首先,先让数组进行排序,排序完后,进行循环比较,在每次循环开始前,我会设置一个值为false的Boolean标记,从索引0开始,将相邻的两个数同中间的一个数比较,如果相同将标记置为false,如果不同将标记置为true。在单次循环的最后判断标记,如果为true,则为答案,如果为false则继续循环。在这里要注意两个临界值,首先是索引0的左边界值,及索引为length - 1的右边界值。

Review 本周阅读文章:“The Key To Accelerating Your Coding Skills” 这篇文章主要讲述了,在学习编程的过程中,如果去快速的通过自己编程的拐点,通过这个拐点之后,自身的变成技能将会得到质的提升,但是在这个阶段时,会经历很多让你觉得很艰难的事情,甚至让你想放弃,但只要坚持下来,就一定可以通过的。

Tip 受本周算法的影响,我发现了二进制数真的很神奇,我自己的题解耗时9ms,但是使用二进制的异或运算竟然仅仅花费1ms。但是细想,这也合情合理,毕竟计算机本身就是二进制,这就是计算机最擅长的事情啊。 0异或任何数=任何数 1异或任何数-任何数取反 任何数异或自己=把自己置0

Share 启发很大,必须分享! The Key To Accelerating Your Coding Skills

ARTS打卡第三周(2019-08-19)

Algorithm 本周LeetCode 题目:350. 两个数组的交集 II 题解思路:首先,将两个数组进行排序。比较两个数组的长度,将长度较小的那个放在外层循环,长度较长的放在内层循环,这样的做法是可以减少检索的次数,当数组长度相差极大的时候,这种方式的优点就可以凸显。随后,进行遍历获取交集,假设刚开始的前n次遍历没有找到相等的数字,则下次遍历还会从0索引开始。如果在前n次遍历中,在第n次遍历,找到相等项,处理之后结束内层循环,则第n+1次会在第n次的索引i+1 处的地方继续进行遍历,这样得益于排序之后的好处。 题解代码:

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        List<Integer> result = new ArrayList<>();
        int start = 0;
        if(nums1.length <= nums2.length) {
            for (int i = 0; i < nums1.length; i++) {
                int find = nums1[i];
                for (int j = start; j < nums2.length; j++) {
                    if (find == nums2[j]) {
                        result.add(Integer.valueOf(find));
                        start = j + 1;
                        break;
                    }

                }
            }
        } else {
            for (int i = 0; i < nums2.length; i++) {
                int find = nums2[i];
                for (int j = start; j < nums1.length; j++) {
                    if (find == nums1[j]) {
                        result.add(Integer.valueOf(find));
                        start = j + 1;
                        break;
                    }
                }
            }
        }

        int[] nums3 = new int[result.size()];
        for (int i = 0; i < result.size(); i++) {
            nums3[i] = result.get(i);
        }
        return nums3;
    }
    
}

Review 本周文章:Why you should totally switch to Kotlin 个人见解;我自己的主语言是Java,这篇文章表达了许多有关于Kotlin这门编程语言的好处,的确有挺多解决Java语言之前的痛点的(个人基于JDK 8来阐述),比如两个对象的数据都相等的话,我们在现实生活中就会认为这两个对象是相等的,而在Java的语法中,在默认的equals方法,因为这两个对象所存放的内存地址是不同的,所以就会返回false,所以会让Java程序员重写了equals方法,但是在Kotlin中提供了==和===比较符,==只要数据相同就会返回true,而===的结果同默认的equals方法。这样省去了重复性的编码,但是Kotlin虽然是简化Java的代码风格,但是个人感觉语法风格上还是有些别扭,给我的感觉是基于Java和Python中间,Python的语法是真的让我感觉很简洁明了。

Tip 本周Tip:在Java中,方法的调用永远是值调用,而不像C++,Python有引用调用及值调用(基于JDK 8)

Share 最近迷上了一部美剧叫Stranger Things,中文译名:怪奇物语,非常好看。可以用来学习英语。

ARTS打卡第四周(2019-08-25)

Algorithm 本周题目:66.加一 题解思路:本道题目总共分为两种常规情况,和一种临界情况。 1.常规情况一:数组末位值加一,不发生进位,直接将末位值加一,返回数组即可。这是最简单的情况 2.常规情况二:数组末位值加一,发生进位,从本道题目来讲,发生进位的情况只有当前数组为9,当发生进位后,本位上的数值必定为0,进位后需要继续遍历该数组,移动当前索引减一,继续执行以上操作,如若继续发生进位,则继续如上操作,反之结束遍历,返回数组结果。 3.临界情况:此为常规情况二的临界情况,当数组元素全为9时,必定发生数组长度扩大一位,如果最后一次遍历时,发生进位情况,则出发此临界情况处理代码:数组长度扩大一位,首位填入1,其余用0填充,返回该构造数组。 题解代码:

class Solution {
    public int[] plusOne(int[] digits) {
        int begin = digits[digits.length - 1] + 1;
        if (begin > 9) {
            for (int i = digits.length - 1; i >= 0; i--) {
                int flag = digits[i] + 1;
                if (flag > 9) {

                    digits[i] = 0;
                    if (i == 0) {
                        int[] result = new int[digits.length + 1];
                        result[0] = 1;
                        for (int j = 1; j < digits.length; j++) {
                            result[j] = 0;
                        }

                        return result;
                    }
                } else {
                    digits[i] = digits[i] + 1;
                    break;
                }
            }
        } else {
            digits[digits.length - 1] = digits[digits.length - 1] + 1;
        }
        return digits;
    }
}

Review lambda表达式基础入门 这是一篇关于Lambda的基础用法的简介,Lambda表达式是Java8的重大变化之一,它的到来让Java进入了函数式编程的领域,使用更简洁的代码,完成更多的功能,同时也增加代码的灵活性,让程序员从冗余的代码块中解放出来。在我查找Lambda表达式的文章的时候,我发现了一篇关于讲述Lambda表达式让代码变慢的说法,我个人不赞同这种说法,首先Lambda是Java之后发展的方向选择,及时在一些特殊的情况下,的确会变慢一些些,但是这也需要业务场景的加持,在可接受的范围内就是OK的。之后,学到了较为底层的时候,我就会去研究对应的场景,看是否变慢。

Tip Spring 的 AOP机制,是基于Java的代理机制,Spring用了更为强大的框架CGLIB。

Share lambda表达式基础入门 这是一篇介绍关于Lambda的基础用法

ARTS打卡第五周(2019-09-01)

Algorithm 本周题目:283.移动零 解题思路:今天这道题目耗费了三个半小时还没解出来,因为一开始的思路错误了,导致一直在死胡同里面钻,之后要给自己解题设置时间限制,最多45分钟,如果没解出来就要去看别人的题解思路了。今天的这道题,我一直用二分查找的思想在想这道题,但是一直行不通,错了四次,然后我看了题解思路之后,豁然开朗,原来这道题的思路这么简单,我却花费了这么久还没解出来!!之后在解题的时候要多多转换思路,不能一直蒙着头一直往前冲。官方的题解思路是:这道题目可以分为两个子问题进行,1.不为0的数字移动至前面,2移动完成后在最后一位不为0的数字后的索引填充0。这样简单用几行代码就可以完成这道题目了。我用了数十行代码还没解决。 题解代码:

class Solution {
    public void moveZeroes(int[] nums) {
        int indexNotZero = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                nums[indexNotZero] = nums[i];
                indexNotZero++;
            }
        }
        
        for(int i = indexNotZero; i< nums.length; i++) {
            nums[i] = 0;
        }
    }
}

Review What I learned from doing 1000 code reviews 这篇文章告诉我们如何在编码中改掉一些坏习惯

Tip 编码思路:通过拆解功能模块为若干个子问题,将关注点移至某子问题上,而不受其他子问题的困扰。我在这种开发模式上获益很大,在逐一解决子问题的过程中,我发现我格外的轻松。考虑的方面仅仅只是子问题的部分,其他的子问题对我来说是不可见的,我是根本不用去考虑,子问题封装的方法,我在另一个子问题还可以复用,一举两得。跟我之前面对大问题去编码的情况有着质一样的变化。那时候我考虑这个考虑那个,最后把自己拧成了一团麻,不仅耗费时间,而且问题还得不到完善的解决。所以在面对一个模块,或者一个功能的时候,一定要三思而后行,将模块的需求,及主要的情况,以及面对临界值的考虑思考清楚,头脑中有了成型的思路以后,编码的速度奇快无比,而且还很轻松,千万不能一股脑的就立马开始敲代码。

Share 修改tomcat端口方法 让服务器可以启动多个tomcat

ARTS打卡第六周(2019-09-09)

Algorithm 本周算法题:1. 两数之和 题解思路:我在刚接触到这道题目的时候,又陷入了一种错误的思路中:把数组先进行排序... 这是多么错误的思路,出现这样的低级错误应该归根于我的算法基础薄弱,以及逻辑思维不够发散,往往会局限于一种解法中。缺少举一反三的能力,今后还是要多看书,写作,多多编写算法题以及进行思考。从各种不同的方面去思考。回归正题,于是我花费了半个小时的时间,在错误的思路中解题,结果,意料之中的没有解决。在上周的做题经历中吸取的教训,我重新转换了思路。于是,发现了新的解题思路,但也是最最最普通的题解思路:双重循环遍历。细节在这里,我就不进行概述了,大家应该都明白。在二刷的时候,我会回来的!!! Review How to write a good software design doc 这篇文章讲诉如何去编写一篇软件设计文档,文章内容分为了四个部分: 1.为什么要编写设计文档 2.设计文档中需要包含什么 3.怎么样去编写一篇设计文档 4.设计文档的总体流程是什么 一篇好的设计文档决定了一个项目的开头的好坏,如果在项目初期,对于项目的准备工作以及其相关的文档十分重视,那么这个项目在起点,就已经超过很多项目了,设计文档也是其中的一部分,它避免了开发人员去做一些错误的解决方案,大多数错误的解决方案在考虑设计项目架构的时候都可以被避免。然而一些公司不重视项目初期的构建 ,在项目后期,将会承受高返工率带来的巨大损失!制作软件产品也是一门手艺活,它需要开发团队去精雕细琢,而不是像快餐式的开发模式去对待,对自己开发产品付出的努力终将得到相对应的回报!

Tip 编码小贴士:信息隐藏。今天在《代码大全2》中阅读到这一部分,在这里简述我的思路:信息隐藏在程序设计中拥有至关重要的位置,它是作为面向对象设计原则的根基,例如,我现在所学习的Java语言中的一大特性——封装,就是这一原则的体现。信息隐藏可以降低类与类之间的耦合度,类把复杂的实现细节隐藏在类的内部,调用类无须知道其实现原理,当服务类修改内部的实现细节,其调用者将毫无察觉。信息隐藏可以将多变系统部分,固定在区域性的范围内,降低需求的变化对系统造成的影响。信息隐藏可以提高程序的伸缩性,将对应变化的部分隐藏至一个类或多个协作类中,如系统的硬件支持变化,只需更改对应类中的实现,实现改动量最小化,灵活性最大化!

Share 腾讯云入门中心 腾讯云入门中心里面有许多免费的教程,比如,如何搭建LAMP环境,Nginx环境等等,一些开发常用的环境。里面的教程相对于搜索引擎上个人编写的教程来说,还是比较清楚的。步骤详细,照着步骤去搭建,一般是不会出问题的。我个人之前搭建环境的时候,都是使用搜索引擎进行搜索,对各个搜索结果进行尝试,其中不乏有搭建失败,步骤模糊或者部署环境配置错误,因此花费了许多时间排错,这是非常可惜的。所以,在这里分享一下我现阶段的思路:首先,登陆对应的官网查找搭建文档,官网上都有对应的文档说明,但是诸如后端的主流开源框架,大部分都是英文的搭建文档,少部分提供中文(英文基础较差的同学要加紧了,包括我自己),照着官网文档搭建百分之九十九都会成功的。其次,就是登陆以上推荐的网站,查看是否有对应的教程。最后,如果以上两种都不管用的话,那就启动面向搜索引擎的模式吧!

ARTS打卡第七周(2019-09-16)

Algorithm 本周算法:36.有效的数独 题解思路:首先这道题目我没有自行解决,第一次遇到中等难度的题目,就被围困半小时告终。于是我查阅了官方题解,它使用了27个HashMap作为存储集合,每行,每列,每个框各有一个HashMap,对于我来说比较有难度的地方为,它使用了一个转换公式将每个框的索引确定,该转换公式为:

 int boxIndex = (i / 3) * 3 + j / 3;

随后开始遍历整个二维char数组,使用Hash表的特性进行判断。

题解代码:

class Solution {
    public boolean isValidSudoku(char[][] board) {
        // init data
        HashMap<Integer, Integer> [] rows = new HashMap[9];
        HashMap<Integer, Integer>[] columns = new HashMap[9];
        HashMap<Integer, Integer> [] boxes = new HashMap[9];
        for (int i = 0; i < 9; i++) {
            rows[i] = new HashMap<Integer, Integer>();
            columns[i] = new HashMap<Integer, Integer>();
            boxes[i] = new HashMap<Integer, Integer>();
        }

        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                int num = (int)board[i][j];
                if (num != '.') {
                    int boxIndex = (i / 3) * 3 + j / 3;
                    rows[i].put(num, rows[i].getOrDefault(num, 0) + 1);
                    columns[j].put(num, columns[j].getOrDefault(num, 0) + 1);
                    boxes[boxIndex].put(num, boxes[boxIndex].getOrDefault(num, 0) + 1);

                    if (rows[i].get(num) > 1 || columns[j].get(num) > 1 || boxes[boxIndex].get(num) > 1) {
                        return false;
                    }
                }
            }
        }

        return true;
    }
}

Review The Decorator Pattern — A simple guide 这边文章主要是介绍装饰器模式的一些用法

Tip 这是我收集的Linux 命令行的用法 1.find usage Use find from the command line to locate a specific file by name or extension. The following example searches for .err files in the /home/username/ directory and all sub-directories: find /home/username/ -name ".err"

Share 分享一篇我自己编写的 elasticdump使用方法入门

ARTS打卡第八周(2019-09-17)

Algorithm 本周算法:48.旋转图像(未解出) 题解思路:使用两次翻转来实现旋转效果,首先第一次翻转,将对应的行数字移动至它旋转后应在的数组中,但是位置并不一定正确,比如7原先在索引为2的数组,通过翻转将它移动至索引为0的数组中。第二次翻转则将每个一维数组进行反转操作,将对应数字移动到正确的位置。

题解代码

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        for (int i = 0; i < n; i ++) {
            for (int j = i; j < n; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
        
        for (int i = 0; i < n; i ++) {
           for (int j = 0; j < n / 2; j++) {
            int tmp = matrix[i][j];
            matrix[i][j] = matrix[i][n - j - 1];
            matrix[i][n - j - 1] = tmp;
           }
        }
    }
}

Review

Tip 以下是关于cp命令的使用 cp usage \cp -rf file1.txt file2.txt cp前加上\ 在存在相同文件时,进行覆盖而不提示 -r -R Copy directories recursively. 递归复制文件及文件夹 -f If an existing destination file cannot be opened, remove it and try again. This option has no effect if the -n/--no-clobber option is used. However, it applies independently of -i/--interactive; neither option cancels the effect of the other,如果目标文件存在,强制将其删除,并重新执行复制操作。

Share 这是一篇关于FTP服务器安装的过程,我已根据这篇文章成功搭建出对应的FTP服务器。 ftp安装

ARTS打卡第九周 (2019-10-06)

Algorithm 344. 反转字符串 题解思路:采用二分法的思想,将整个数组一分为二,使用中间变量,由外向内的进行首位与末位的循环替换。 题解代码:

class Solution {
    public void reverseString(char[] s) {
        int length = s.length;
        for (int i = 0; i < length / 2; i++) {
            char temp = s[i];
            s[i] = s[length - i - 1];
            s[length - i - 1] = temp;
        }
    }
}

Review
Callback Functions in JavaScript
这是关于JavaScript(以下简称为JS)回调函数的相关概念 1.JS中定义的函数可以传递代码块,随后在函数内部执行对应的函数 2.JS在函数体内部一定要判断作为回调函数参数的类型,因为JS是一种弱类型的语言,不像Java在定义参数的时候需要指定参数的类型,所以一定要做好参数检查的工作。 3.在函数体内部如果调用了异步函数,需要确认该异步函数与回调函数是否存在调用的顺序问题,如果存在,有一种解决方案:直接将回调函数放置异步函数的末尾。因为回调函数,极有可能在异步函数之前执行完毕。

Tip 在JS中应该避免使用var关键字变量,更改为let关键字定义变量,let 具有比var更为严格的语法要求,也是JS官方推荐使用。

Share 推荐MDN的JS学习指南,国庆后三天都在学习有关于JS方面的基础知识,准备学习好基础知识后去学习Vue.js框架,为正式入门的作业打下基础。

ARTS 打卡第10周(2019-11-03)

Algorithm 7.整数反转

题解代码

if (x == Integer.MAX_VALUE || x == Integer.MIN_VALUE) {
            return 0;
        }
        StringBuilder stringBuilder = new StringBuilder(String.valueOf(Math.abs(x)));
        long l = Long.parseLong(stringBuilder.reverse().toString());
        if (l > Integer.MAX_VALUE) {
            return 0;
        }
        if (x > 0) {
            return (int)l;
        } else {
            return (int)l * -1;
        }

题解思路:

首先进行判断传入参数的大小是否等于整数范围的最大值或者最小值,如若如此,则直接进行返回0。接着将参数的绝对值进行字符化,反转之后转化为long类型整数,将此与Integer的最大值进行比较,如果大于则返回0。如果符合转换要求。对参数的正负性进行判断。为负数时乘以-1。 Review RESTful API Design - Step by Step Guide

这篇文章主要谈论关于Restful API的设计准则,我阅读完这篇文章后,个人受到了极大的启发,纠正了我在之前工作时对于Restful API设计的错误观念。

1.RESTful API的命名规则尽量要简单并且能凸显提供的接口的功能性。在此之前,我对于API的命名主要以get为前缀开头,List为后缀结尾为主,伴随着中间一系列的动词及名词来表明此接口方法的功能性。这也是作者所提出的常见错误之一,比如关于产品方面的列表查询,我通常会使用productList,获取单一的产品,我会使用getProductById,虽然也能具体表明API的功能性,但是却带来了十分冗余的接口命名。而作者仅仅使用了products及products/id表明了我所需要接口的定义,却带来了及其简单的接口命名。

2.RESTful API需要版本控制。这一点再一次震撼到我,因为在我目前的工作生涯中,我从来都不知道RESTful API是需要进行版本控制的,我的通常做法是直接在当前版本上进行修改,如果变动过大,将导致调用者的代码报错,并且没有足够的时间去调整。

Tip Ubuntu 切换root用户不使用密码 sudo su - root

Share 今天分享的是Openstack的官方API文档,因为公司近期做的项目有设计虚拟化,所以自己也就在接触这方面的文档。但是Openstack的官方文档真的是非常的多。看完需要一定量的时间。原本想自己搭建一套Openstack的环境。结果被各种错误折磨的烦不胜烦,花费了7天时间。还是没有搭建出来,最后不得已放弃了,因为自己的进度计划上还有更重要的事情要完成。

Openstack 官方文档

11 ARTS 打卡(2019-11-12)

Algorithm 字符串中的第一个唯一字符

题解代码:

public int firstUniqChar(String s) {
        HashMap<Character, Integer> count = new HashMap<Character, Integer>();
        int n = s.length();
        // build hash map : character and how often it appears
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            count.put(c, count.getOrDefault(c, 0) + 1);
        }
        
        // find the index
        for (int i = 0; i < n; i++) {
            if (count.get(s.charAt(i)) == 1) 
                return i;
        }
        return -1;
        
    }

题解思路:

根据题目就可以得到使用Hash表数据结构去解题,使用两个for循环,第一个for循环统计字符串中各个字符出现的次数,第二个for循环重新遍历整个字符串,若得到字符的次数为1,则这个字符为第一个唯一的字符,返回其index。若整个字符串都没有唯一,则返回-1.

Review 本周阅读OpenStack官方文档,OpenStack作为云计算时代的主力之一,着实让人佩服。从刚开始接触OpenStack时(也就在两周前),那时它对我来说是蒙蒙胧胧的。在经过这两周的研究,发现网上的资料参次不齐,大多数都过时了。所以我就直接去啃官网的英文文档了,官方的文档十分完善,所以要接触或者学习OpenStack的同学们,不要去百度找中文资料,直接上官网查阅英文资料。在经过“痛苦”的两周后,这周开始封装OpenStack的Keystone client以及glance client。

Tip 根据OpenStack glance rest api用来创建镜像,创建完后,只是一条记录,这条记录的状态显示排队中,需要另外上传镜像文件。

Share docs.openstack.org/train/

12 ARTS 打卡(2019-11-24)

Algorithm
892.三维形体的表面积
题解思路:每个正方形方格中叠放的正方体,底部和顶部两面必定暴露,不存在其他方格中遮挡情况,剩下东西南北四面,判断是否有正方体存在,如果有则减去对应的遮挡面积,反之加上全面积。以此遍历每个方格,加上后则为总表面积。
题解代码:

class Solution {
    public int surfaceArea(int[][] grid) {

        // setting four direction
        int[] dc = new int[] {0, 0, 1, -1};
        int[] dt = new int[] {1, -1, 0, 0};

        int N = grid.length;
        int total = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                int v = grid[i][j];
                if (v > 0) {
                    total += 2;
                    for (int l = 0; l < 4; l++) {
                        int k = i + dc[l];
                        int h = j + dt[l];
                        int nv = 0;
                        if (0 <= k && k < N && 0 <= h && h < N) {
                            nv = grid[k][h];
                        }
                        total += Math.max(v - nv, 0);
                    }
                }
            }
        }
        return total;
    }

}

1114.按序打印
题解思路:采用Java关键字synchronized,用来控制线程打印的优先级,通过优先级高低依次获取锁,执行线程任务,随后再释放锁,唤醒等待线程。
题解代码:

class Foo {

    public boolean firstFinished = false;
    public boolean secondFinished = false;
    public Object lock = new Object();

    public Foo() {
        
    }

    public void first(Runnable printFirst) throws InterruptedException {
        
        // printFirst.run() outputs "first". Do not change or remove this line.
        synchronized(lock) {
            printFirst.run();
            firstFinished = true;
            lock.notifyAll();
        }
        
    }

    public void second(Runnable printSecond) throws InterruptedException {
        
        // printSecond.run() outputs "second". Do not change or remove this line.
        synchronized(lock) {
            while(!firstFinished) {
                lock.wait();
            }
            printSecond.run();
            secondFinished = true;
            lock.notifyAll();
        }
        
    }

    public void third(Runnable printThird) throws InterruptedException {
        
        // printThird.run() outputs "third". Do not change or remove this line.
        synchronized(lock) {
            while(!secondFinished) {
                lock.wait();
            }
            printThird.run();
        }
    }
}

443.压缩字符串
思路:采用遍历,在遍历的过程中比对字符的是否相等,并且统计相等字符的次数,这其中采用两种方式去处理相等字符及不相等情况。相等情况则sum加上1。不相等情况,sum如果大于1,则将sum转换为字符串,判断长度,遍历放入原数组中。最后处理最后一个字符串的临界值情况。
代码:

class Solution {
    public int compress(char[] chars) {
        int len = 0;
        int sum = 0;
        char original = chars[0];
        int j = 0;
        for (int i = 0; i < chars.length; i++) {
            char cha = chars[i];
            if (cha == original) {
                sum += 1;
            } else {
                len += 1;
                chars[j++] = chars[i - 1];
                if (sum > 1) {
                    String numStr = String.valueOf(sum);
                    len += numStr.length();
                    for (int k = 0; k < numStr.length(); k++) {
                        chars[j++] = numStr.charAt(k);
                    }
                }
                original = cha;
                sum = 1;
            }
        }

        len += 1;
        chars[j++] = chars[chars.length - 1];
        if (sum > 1) {
            String numStr = String.valueOf(sum);
            len += numStr.length();
            for (int k = 0; k < numStr.length(); k++) {
                chars[j++] = numStr.charAt(k);
            }
        }

        return len;
    }
}

53.最大子序和
思路:采用动态规划,遍历数组,如果sum大于0,则继续该次连续加法,如果sum 不大于0,则意味该次连续加法已不是最大和,重置连续加法,将sum至为当前的数字,每次加法操作,sum最后都需要与ans做比较。ans当前存放着连续的最大和,遍历结束后,ans里则为连续的最大和
代码:

class Solution {
    public int maxSubArray(int[] nums) {
        int ans = nums[0];
        int sum = 0;
        for (int num : nums) {
            if (sum > 0) {
                sum += num;
            } else {
                sum = num;
            }
            ans = Math.max(ans, sum);
        }

        return ans;
    }
}

206.反转链表
思路:采用遍历的方式,将链表的每个节点的的下一个节点指向上一个节点,返回最后的引用
代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while(curr != null) {
            ListNode nextTemp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }

        return prev;
    }
}

Review
Elasticsearch user auth
根据ES官方文档,ES的Xpack鉴权框架在6.8之前是需要收费,我将ES版本从6.5更新至6.8,没有发生任何不兼容的问题。6.8后的Xpach免费提供了基础的鉴权服务,对于普通的使用者而言,已经足够使用。
Tip
Previous operation has not finished; run 'cleanup' if it was interrupted
执行 sqlite3 .svn/wc.db "delete from work_queue". 把队列清空。
执行 sqlite3 .svn/wc.db "select * from work_queue".\ 确认一下是否已经清空队列,发现已经没有记录显示,说明已经清空了。
最后尝试svn cleanup。
Share
SVN fail to cleanup

13 ARTS 打卡(2019-11-26)

Algorithm
415.字符串相加
思路:采用遍历迭代,通过遍历两个字符串的各个字符位,将相对应的字符位进行相加操作,并且加上进位,处理相加和的价位,将剩下的值转换为字符串加入到StringBulider中,当遍历完成后,进行反转操作,即可得到最终的值。这里使用了一个小技巧,就是在代码num1.charAt(i) - '0'中,因为数字的字符减去0字符,即可得到它本身的整数值,根据ASCII表中的值来定义的。
代码:

class Solution {
    public String addStrings(String num1, String num2) {
        StringBuilder sb = new StringBuilder();
        int i = num1.length() - 1;
        int j = num2.length() - 1;
        int carry = 0;
        while(i >= 0 || j >= 0 ) {
            int k = i >= 0 ? num1.charAt(i) - '0' : 0;
            int h = j >= 0 ? num2.charAt(j) - '0' : 0;
            int temp = k + h + carry;
            carry = temp / 10;
            sb.append(String.valueOf(temp % 10));
            i--;
            j--;
        }
        if(carry == 1) {
            sb.append(carry);
        }
        return sb.reverse().toString();
    }
}

121.买卖股票的最佳时机
思路:这道题目中容易陷入查找最大值和最小值的思路误区内,题目的中心为查找最大差值,其中较小的值必须在较大值前。通过这种思路迭代,就可以得到以下代码
代码:

class Solution {
    public int maxProfit(int[] prices) {
        int min = Integer.MAX_VALUE;
        int max = 0;
        for (int i = 0; i < prices.length; i++) {
            int compress = prices[i];
            if (compress < min) {
                min = compress;
            } else if (compress - min > max) {
                max = compress - min;
            }
        }

        return max;
    }
}

Review
这周花费两天左右调研Docker的概念以及其入门操作,Docker是第三代虚拟化技术的成品,基于Linux容器封装,优点有很多,我这边就简要列几点。首先肯定就是轻量(Lightweight),相对于第二代虚拟化技术虚拟机而言,Docker不需要完整的整个操作系统以及独立的计算资源,所以启动一个Docker容器要比启动一个虚拟机快的多。其次就是提高物理资源的利用率,在单节点的Docker内,多个Docker容器共享着共同的物理计算资源,不像虚拟机闲置了许多物理计算资源。总之Docker的优点非常多,如果有兴趣的,可以登陆其官网,了解其用法。

Tip
Mac查看隐藏文件的快捷键为 command + shirft + . 关闭查看也是相同按键
Share
Docker官方文档

14 ARTS打卡 (2019-12-09)

Algorithm
226.翻转二叉树
思路:根据题目,我采用递归的方式去做这道题,将每个节点的子节点进行翻转操作即可。
代码:

class Solution {
    public TreeNode invertTree(TreeNode root) {
        translate(root);
        return root;
    }

    public void translate(TreeNode node) {
        if (node != null) {
            if (node.left !=null || node.right != null) {
                TreeNode temp = node.left;
                node.left = node.right;
                node.right = temp;
                translate(node.right);
                translate(node.left);
            }
        }
    }
}

Review
Docker 构建镜像入门这篇文章讲述了Docker构建镜像入门,使用Dockerfile构建。构建自己镜像的话一定要基于已有镜像,不要从零开始,就像平时的编程思想一样,接手一个新功能的话,一定要基于已有的基础框架开发,切勿从零开发。
Tip
使用Maven进行项目开发时,经常会遇到一个很棘手的问题,就是依赖冲突,Maven在在给我们的依赖提供遍历的同时,也带来了问题。Maven是把双刃剑,每当引入一个新的依赖时,一定要好好的看下依赖树,看是否存在版本上的冲突,或者是否之后会造成版本上的冲突。不是简简单单的复制一份Maven依赖的XML配置。Maven查看依赖树的方法为

mvn -Dverbose dependency:tree

Share
分享Maven shard插件的使用方法 www.elastic.co/cn/blog/to-…

15 ARTS打卡 (2019-12-11)

Algorithm
1071 字符串的最大公因子
思路:首先判断两个字符串的相似字符串项,然后求出两个字符串长度的最大公因子,最后即可得出最大相似字符串
代码:

class Solution {
    public String gcdOfStrings(String str1, String str2) {
        if (!str1.concat(str2).equals(str2.concat(str1))) {
            return "";
        }

        String subStr1 = innerGcdOfStrings(str1);
        String subStr2 = innerGcdOfStrings(str2);
        if (subStr1.equals(subStr2)) {
            int aLength = str1.length();
            int bLength = str2.length();
            int factor = aLength;
            int result = 0;
            while(factor != 0) {
                if(aLength % factor == 0 && bLength % factor == 0) {
                    result = factor;
                    break;
                } else {
                    factor--;
                }
            }
            
            if (result % subStr2.length() != 0) {
                return subStr2;
            } else {
                int multiple = result / subStr2.length();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < multiple; i++) {
                    sb.append(subStr2);
                }
                return sb.toString();

            }
            
        } else {
            return "";
        }
        
    }

    public String innerGcdOfStrings(String parent) {
        char start = parent.charAt(0);
        for (int i = 1; i < parent.length(); i++) {
            char compare = parent.charAt(i);
            if (compare == start) {
                if (2 * i > parent.length()) {
                    return parent;
                }
                boolean flag = true;
                for (int j = 1; j < i; j++) {
                    if (parent.charAt(j) != parent.charAt(i + j)) {
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    String sub = parent.substring(0, i);
                    if (parent.split(sub).length == 0) {
                        return sub;
                    }
                }
            }
        }

        return parent;
    }
}

203.移除链表元素
思路:采用迭代的方式,首先确立两个节点:当前节点以及当前节点的上一节点,如果当前节点的val与需移除的val相等时,将当前节点移除,即将上一节点的下一节点指向当前节点的下一节点,当前节点置为当前节点的下一节点,这里有种临界情况:头节点的val就为需要排除的val,此时便将头节点向后移动至下一节点即可。val不相等的情况,就将上一节点置为当前节点,当前节点的下一节点置为当前节点即可。最后,返回头节点。 代码:

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode current = head;
        ListNode prev = null;
        while(current != null) {
            if (current.val == val) {
                if (prev == null) {
                    current = current.next;
                    head = current;
                } else {
                    prev.next = current.next;
                    current = current.next;
                }
            } else {
                prev = current;
                current = current.next;
            }
        }

        return head;
    }
}

Review
这周的时间全部都花费在Docker容器化这方面的调研中,不得不说,Docker容器在未来绝对是主流,轻量,快速,程序员可以在自己的Mac上迅速搭建出微服务的应用,而且不用在为构建环境时带来的乱七八糟的错误所折磨,如果有经常玩服务器的人,这种感觉大家应该都懂。并且Docker节省了更多的物理资源,提高了资源的利用率,就像当初的Spring一统JavaEE Web框架一样,Docker也正在朝着自己的一统虚拟化进发。现在的Docker生态社区也十分火热,大多数的主流镜像基本齐全。

Tip
docker保存镜像至本地
语法

docker save image-name:tag > your path

例子:

docker save jackpanwudi/cheers2019:latest > /Users/jackpan/JackPanDocuments/temporary/cheer.tar

Share
WildFly 官网类似Tomcat的web容器

16 ARTS打卡 (2019-12-16)

Algorithm
1103.分糖果 II
思路: 采用双重迭代的方式解题。首先创建一个长度为num_people的数组,初始倍数为multiple = 0,根据题意可得出,如果当前糖果满足第一次分发给全部小朋友并且还有剩余糖果时,需要重新回到队伍起点,并且第一个朋友第二次分发糖果的数量为n+1,由此得出,每次分发糖果的数量为multiple * num_people + startstart为每次开始分发的数量,即为1,如果当前糖果不满足当前分发,则代表分发完毕,结束迭代即可。 代码:

class Solution {
    public int[] distributeCandies(int candies, int num_people) {
        int[] ans = new int[num_people];
        int multiple = 0;
        while(candies > 0) {
            int start = 1;
            for (int i = 0; i< ans.length; i++) {
                int num = multiple * num_people + start;
                if (candies > num) {
                    ans[i] += num;
                    candies -= num;
                } else {
                    ans[i] += candies;
                    candies = 0;
                    break;
                }
                start++;
                
            }
            multiple++;
        }
        return ans;
    }
}

594.最长和谐子序列
思路:采用两次迭代题解,首先第一次迭代整个数组,并使用TreeMap记录下每个数组对应值出现次数(并且TreeMap自动进行排序)。随后对TreeMap进行第二次迭代,用于统计最大和谐数组的长度,首先确保当前值,有对应的和谐最大值,如果有则进行统计相加,如果没有进行下一个数值的统计,最后保存一个最大值max,即最大和谐数组的长度。 代码:

class Solution {
    public int findLHS(int[] nums) {
        Map<Integer, Integer> sort = new TreeMap<>();
        for (int num : nums) {
            if (sort.get(num) != null) {
                sort.put(num, sort.get(num) + 1);
            } else {
                sort.put(num, 1);
            }
        }

        Set<Integer> keys = sort.keySet();
        int max = 0;
        for (Integer key : keys) {
            if (keys.contains(key + 1)) {
                int compare = sort.get(key) + sort.get(key + 1);
                if (compare > max) {
                    max = compare;
                }
            } else {
                continue;
            }
        }

        return max;
    }
}

572.另一个树的子树
思路:采用递归题解,分两次递归,首先内部递归判断两个子树是否相等,外部递从头节点开始,切换子树,直到找到相等子树,或者递归完成,无法找到相等子树。
代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        TreeNode start = s;
        if(compareTree(start, t)) {
            return true;
        } else {
            if (start.left != null) {
                if (isSubtree(start.left, t)) {
                    return true;
                }
            }

            if (start.right != null) {
                if (isSubtree(start.right, t)) {
                    return true;
                }
            }
            
            
        }
        
        return false;
        
    }

    public boolean compareTree(TreeNode master, TreeNode compare) {
        if (master != null && compare != null) {
            if (master.val == compare.val) {
                if(!compareTree(master.left, compare.left)) {
                    return false;
                }
                if(!compareTree(master.right, compare.right)) {
                    return false;
                }
            } else {
                return false;
            }
                
        } else if (master == null && compare != null) {
            return false;
        } else if (master != null && compare == null) {
            return false;
        }
        return true;
    }
}

Review
What exactly is Node.js?
读后感:这篇文章让我对Node.js有了初步对了解,Node.jsJavasScript不仅仅只是在浏览器中运行,类似与Java虚拟机一样,Node.js也有对应的运行引擎。让JavasScript代码可以像后端语言一样,在服务器上运行。让JavasScript具有脚本语言的能力。Node.js模块让JavaScript应用开发更加便捷,可以进行无缝引用外部依赖库,并且不会对应用代码造成影响,跟Java的Maven管理有些相似。
Tip
创建docker-compose.yml文件,在当前的文件夹内,让docker同时运行多容器。

docker-compose up -d

docker-compose.yml文件模板:

version: '3.3'
services:
  db:
    container_name: db
    image: mysql:8
    environment:
      MYSQL_DATABASE: employees
      MYSQL_USER: mysql
      MYSQL_PASSWORD: mysql
      MYSQL_ROOT_PASSWORD: supersecret
    ports:
      - 3307:3306
  web:
    image: arungupta/docker-javaee:dockerconeu17
    ports:
      - 8080:8080
      - 9990:9990
    depends_on:
      - db

Share
Docker Compose入门

17 ARTS打卡 (2019-12-23)

Algorithm
104.二叉树的最大深度
思路:采用递归的方式,深度优先搜索法(DFS),从左下至右下,依次搜索。直到最后没有值为止,回溯,继续搜索。由此获得最大值。 代码:

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        } else {
            int left_height = maxDepth(root.left);
            int right_height = maxDepth(root.right);
            return Math.max(left_height, right_height) + 1;
        }
    }
}

463.岛屿的周长
思路:采用迭代的方式,第一次迭代遍历每个方格,将方格的总长度计算出,行中左右相邻的方格需要减去重叠部分,此时的总长度必定大于真正的周长。随后便将列中上下相邻的方格减去重叠部分,迭代结束后得到周长。
代码:

class Solution {
    public int islandPerimeter(int[][] grid) {

        List<Boolean> list = new ArrayList<Boolean>(grid[0].length);
        for(int i = 0; i < grid[0].length; i++) {
            list.add(false);
        }
        int length = 0;
        for(int[] subGrid : grid) {
            int prev = 0;
            for(int i = 0; i < subGrid.length; i++) {
                if (subGrid[i] == 1) {
                    if (prev == 0) {
                        length += 4;
                        prev = 1;
                    } else {
                        length += 2;
                    }

                    if (list.get(i)) {
                        length -= 2;
                    }
                    list.set(i, true);
  
                } else {
                    prev = 0;
                    list.set(i, false);
                }
            }
        }

        return length;
        
    }
}

Review
Docker compose入门
读后感:这篇文章介绍了docker-compose命令的入门用法,在Docker for Linux上需要独自在安装docker-compose组件。在平常的使用中,都是操作单个镜像,生成对应的容器的。但是如果框架支持集群模式,该如何处理呢?没错,就是使用docker-compose命令,来构建更高的一层抽象(在container之上):service,使用docker-compose.yml文件来定义生成的service,如果是集群框架,可以在此文件内定义多个节点;如果是web应用,则可以在此文件内定义web服务器,web容器,数据库之间的关联关系。复杂繁琐的操作及关系都需要在docker-compose.yml文件内定义完成,此后在项目目录下就只需要执行docker-compose up,就可以生成出一个完整的集群服务或者是web服务!快速轻便并且强大!

Tip
Linux创建软链接

sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

Share
Docker Engine Api Exec
这篇文章主要描述的是Docker Engine Api的RestFul接口,其中Exec Api负责的是对容器进行Cmd,让我感到疑惑不解的是,在Docker的命令行工具内,可以通过Exec直接进入容器内部,并且在容器内执行Cmd,但是我在Api中并没有找到如何实现持久化的命令模式,现在也只是封装了单次执行命令的方式。

18 ARTS打卡 (2019-12-31)

Algorithm
118.杨辉三角
思路:首先,创建一个
代码:

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> rows = new ArrayList<>(numRows);
        List<Integer> start = null;
        for (int i = 0; i < numRows; i++) {
            List<Integer> list = new ArrayList(i + 1);
            list.add(1);
            if (start != null) {
                for (int j = 1; j < i; j++) {
                list.add(start.get(j) + start.get(j - 1));
            }
                list.add(1);
            }
            rows.add(list);
            start = list;
        }

        return rows;
    }
}

Review
Chapter one of The Little Prince
第一章讲述了主人公六岁的时候,画了一副画,这幅画是关于他从书中《真实的故事》获取的灵感,蟒蛇正在消化一只大象,但是当他把这幅画拿给大人看的时候,大人完全看不懂,以为这是一顶帽子。他对大人的认知感到很厌烦,于是他画了第二幅画,这幅画比之前更清晰,直接把蟒蛇肚子里的情况画的一清二楚,然后再拿去给大人看,但是大人不以为然,说他不误正业,应该把精力投入在学习中,于是主人公画家的梦想就这样被扼杀了。
在我们的生活中也有许多这样的例子,其实每个人出生后,都拥有独属于自身的闪光点。当事人往往不得而知,但是可以从某些人眼中可以得知,自己在成长的道路上也可以增加自己许多的闪光点,所以自信的人往往是有魅力的!

Tip
使用Docker创建Elasticsearch集群时,vm.max_map_count内核设置必须至少设置为262144,步骤如下:

  1. 查看当前设置
grep vm.max_map_count /etc/sysctl.conf
vm.max_map_count=262144
  1. 设置vm.max_map_count
sysctl -w vm.max_map_count=262144

Share
How To Make Your Weekend More Productive In 6 Ways 提高你周末生产力的六种方式,值得参考。

19 ARTS打卡 (2020-01-07)

Algorithm
思路:这道题可以采用迭代和额外的存储空间,这里需要注意的是,额外的存储空间是具有自动扩展的性能代价的,我们在开头就需要采用该题的最大值的存储空间,避免自动扩展带来的性能开销。其他部分比较简单,如代码所示:

class Solution {
    public ListNode middleNode(ListNode head) {
        List<ListNode> arr = new ArrayList<>(100);
        ListNode start = head;
        while(start != null) {
            arr.add(start);
            start = start.next;
        }

        int index = arr.size() / 2;
        return arr.get(index);
    }
}

Review
Try and try hard
这是一篇励志的文章,假如你要做的话,那么就进行到底,不要半途而废,如果没有坚持下去的勇气,那还不如别开始。在现在,在我周围的朋友圈中,很多人都是属于半途而废的人,包括我自己,我也在努力克服自己内心的懒惰,以及注意力不够专注,我往往会喜新厌旧,不能够坚持到底,目标飘忽不定,在2020年开始,我必须要修改掉我这些坏毛病,让自己变的更加优秀!

Tip
英文学习必须要注重听说读写,一个都不能少,一定要大量的听,听不懂没关系,认真的去感知,去听就OK了。

Share
推荐去找APP上分级阅读文章,一定要找到适合自己等级的文章,具体APP这里就不说了,避免打广告。

20 ARTS打卡 (2020-01-14)

Algorithm
225.用队列实现栈
思路:这题比较简单,查找JavaQueue的接口类,发现了Deque双端队列完美的符合该题的思路,直接使用该接口即可解决本题
代码:

class MyStack {

    private Deque<Integer> deque;

    /** Initialize your data structure here. */
    public MyStack() {
        deque = new LinkedList<>();
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
        deque.addFirst(x);
    }
    
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        return deque.removeFirst();
    }
    
    /** Get the top element. */
    public int top() {
        return deque.getFirst();
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return deque.size() == 0 ? true : false;
    }
}

Review
Annotations in Java
这是一篇比较初级的文章,介绍了Java内置的7个注解的使用说明,可以让初学者先对注解有个清晰的认知。

Tip
在继承或者实现接口的方法时一定要加上@Override注解,防止在之后变成方法的Overload,便于快速排错。

Share
Annotations in Java

21 ARTS打卡 (2020-01-21)

Algorithm
思路:首先判断前置条件根节点不为空,随后递归方法,进行多重判断,假如值相等,继续递归,因为是镜像对称,所以递归参数为左节点的左节点和右节点的右节点,以及左节点的右节点和右节点的左节点。
代码:

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root != null) {
            return check(root.left, root.right);
        } else {
            return true;
        }
        
    }

    private boolean check(TreeNode leftRoot, TreeNode rightRoot) {
        if (leftRoot != null && rightRoot != null) {
            if(leftRoot.val == rightRoot.val) {
                return check(leftRoot.left, rightRoot.right) && check (leftRoot.right, rightRoot.left);
            } else {
                return false;
            }
        } else if (leftRoot == null && rightRoot == null) {
            return true;
        } else {
            return false;
        }
    }
}

Review
Docker Tips : about /var/run/docker.sock
该文章快速介绍了docker.sock,以及赋予Docker的强大功能,特别是提供的Engine API,让开发者可以快速上手,拥抱Docker。
Tip
使用Engine API启动容器。

$ curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/fcb6...7d65/start

Share
Java注解文章

22 ARTS打卡 (2020-02-02)

Algorithm
思路:借助Java标准库解法。
代码:

class Solution {

    //
    public String reverseWords(String s) {
        if(s == null) {
            return s;
        }
        StringBuilder sb = new StringBuilder(s);
        String[] arr = sb.reverse().toString().split(" ");
        StringBuilder sb1 = new StringBuilder();
        for (int i = arr.length - 1; i >= 0; --i) {
            sb1.append(" " + arr[i]);
        }

        return sb1.toString().trim();
    }
}

Review
The Marks of a True Senior Developer
该文章讲述了高级工程师所具有的共同特征,是初级程序员在自己今后的编码中需要注意的,年限并不是高级工程师的前提。
Tip
少出门,勤洗手,多锻炼,多学习! Share
远程协作文化

23 ARTS打卡 (2020-02-09)

Algorithm
思路:采用格外空间的解法,首先遍历整个链表,确定额外数组的大小,然后再次遍历链表,装填数组,最后遍历一半数组,确定首尾回文相同。
代码:

class Solution {
    public boolean isPalindrome(ListNode head) {
        ListNode start = head;
        int num = 0;
        while (start != null) {
            ++num;
            start = start.next;
        }

        int[] values = new int[num];

        start = head;
        for (int i = 0; i < values.length; ++i) {
            values[i] = start.val;
            start = start.next;
        }

        start = head;
        for (int i = values.length - 1; i >= 0; --i) {
            if (start.val != values[i]) {
                return false;
            }
            start = start.next;
        }

        return true;
    }
}

155.最小栈
思路:借助Java标准库的LinkedList类,在push的时候记录最小的值,在pop的时候判断值是否为最小值,如果为最小值的话,遍历整个List更新最小值,最坏情况数值为倒序压入栈,每次pop都需要更新最小值,所以时间复杂度为O(n)。 代码:

class MinStack {

    private int min = Integer.MAX_VALUE;
    LinkedList<Integer> list = null;

    /** initialize your data structure here. */
    public MinStack() {
        list = new LinkedList<>();
    }
    
    public void push(int x) {
        if(x < min) {
            min = x;
        }
        list.push(x);
    }
    
    public void pop() {
        Integer num = list.removeFirst();
        if(num == min) {
            min = Integer.MAX_VALUE;
            for(Integer element : list) {
                if(element < min) {
                    min = element;
                }
            }
        }
    }
    
    public int top() {
        return list.getFirst();
    }
    
    public int getMin() {
        return min;
    }
}

507.完美数
704.二分查找
Review
如何编写一个好的设计文档
心得体会:在刚接触编程的时候,那时候完全没有先设计后编码的习惯,都是提前先思考下要如何解决这个问题,思考完毕后就直接开始一股脑的编码。以这种形式编写出来的代码,首先在健壮性欠缺,因为都是以正常的值去带入,很少对各种临界情况考虑周全。所以经过各种测试用例,必定会报错。在接触了更高的编程思想后,越来越发觉设计非常重要,先设计后编码可以避免绝大多数问题,特别是那种让你看到错误就羞愧难当的低级问题。设计可以让你有一个清晰的思路:“自己到底在干什么,需要怎么去做,去做好”,设计的过程中,就像在用自己的大脑编程,从核心出发让自己快速的构建从零至一的技术架构,发现各种临界情况,极端条件,技术难点,梳理登记,便于之后解决,总之先设计后编码,使用20%的时间发挥80%的效能,大脑是没有极限,至少至目前而言!
Tip
项目团队管理技巧:站在员工角度思考问题,以员工的切身利益出发,对其分析问题本质,以及带来的后果。提醒下次别再犯同样的错误。
Share
mac系统连接vpn

24 ARTS打卡 (2020-02-15)

Algorithm
232.用栈实现队列 1029.两地调度
Review
VI Java Api 入门
这篇文章是VI Java官网推荐的入门文章,可以了解到关于VMware虚拟化编程方面的基础概念,了解VM官方SDK的人都知道其复杂程度,获取虚拟机对象的属性都需要十分冗长的代码,于是就诞生了VI Java——基于VM SDK进行二次封装,其官网也强调了代码的简洁性,100行的VM SDK的功能代码,使用VI Java仅仅只有28行代码即可实现。所以还是很讨喜的,不幸的是,这个开源框架已经很久没有更新了。
Tip
centos 启动服务

sudo service docker start

Share
VI Java Api 入门

25 ARTS打卡 (2020-02-23)

Algorithm
949.给定数字能组成的最大时间
167.两数之和 II - 输入有序数组
Review
Kubernetes — Flat, NAT-less Networking
文章介绍了Kubernetes的不同worker下的Pods如何进行网络通信的原理和基础概念,在Docker中,宿主机之间是无法进行通信的,Docker宿主机只能利用本地资源,如果需要多台宿主机进行通信,就需要使用容器编排技术,Kubernetes便是容器编排技术中的一种,可以将多台宿主机联合在一起,做一层更高的抽象。让Docker支持水平扩容。并且能进行更高级一层的抽象管理。Kubernetes的网络通信技术让容器之间的无缝通信。
Tip
Mybatis真值判断

@Select("SELECT EXISTS(SELECT 1 FROM my_table WHERE email=#{email})")
boolean checkUserExists(@Param("email") String email);

Share
Kubernetes — Flat, NAT-less Networking
minikube start --vm-driver=virtualbox --registry-mirror=registry.docker-cn.com --image-mirror-country=cn --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers

26 ARTS打卡 (2020-03-29)

Algorithm
836.矩形重叠
地图分析
找出数组中的幸运数
腐烂的橘子
Review
本周周末主要在做算法题目,对于BFS的算法概念不清晰,从而导致在看题解的时候,根本就看不明白!于是重新翻开了算法图解这本书,重新复习了广度优先搜索的概念,于是今天一整天都是在刷关于优先搜索的几道题目。

Tip
国内的伙伴,在本地启动minikube时,虚拟机管理程序最好选择VirtualBox,比hyperkit好用多了。
Share
Solution of Question:VM is unable to access

周数 ARTS打卡 (yyyy-MM-dd)打卡模版

Algorithm\

Review
Tip
数据库更新字段的部分内容的更新语句

update user_information_copy1 set picture_address = replace(picture_address,'aa','bb')

Share\