原创

Linux安装pm2

15 6月 120
环境Linux: centOS7.6(amd64)pm2: 4.4.0安装pm2npm install -g pm2 #当然你也可以使用淘宝镜像 npm install -g cnpm --registry=https://registry.npm.taobao.org建立软连接(下面路径是我安装node的路径,安装的时候请注意自己的node目录)ln -s /usr/local/node-v14.0.0-linux-x64/lib/node_modules/pm2/bin/pm2 /usr/local/bin启动pm2 startuppm2 save查看版本,是否安装成功 pm2 -v启动nuxt项目(只需要线程名,这里填写nuxt项目的app名称就行)pm2 start npm --name "my-nuxt" -- run start常用pm2命令pm2 list :::::::::::::::::::::::::查看当前正在运行的进程pm2 start all :::::::::::::::::::::::::启动所有应用pm2 restart all :::::::::::::::::::::::::重启所有应用pm2 stop all :::::::::::::::::::::::::停止所有的应用程序pm2 delete all :::::::::::::::::::::::::关闭并删除所有应用pm2 logs :::::::::::::::::::::::::控制台显示所有日志pm2 start 0 :::::::::::::::::::::::::启动 id为 0的指定应用程序pm2 restart 0 :::::::::::::::::::::::::重启 id为 0的指定应用程序pm2 stop 0 :::::::::::::::::::::::::停止 id为 0的指定应用程序pm2 delete 0 :::::::::::::::::::::::::删除 id为 0的指定应用程序pm2 logs 0 :::::::::::::::::::::::::控制台显示编号为0的日志pm2 show 0 :::::::::::::::::::::::::查看执行编号为0的进程pm2 monit xxxxx:::::::::::::::::::::::::监控名称为xxxxx的进程
应用安装 1 108 0 发表
原创

最长公共前缀

15 6月 120
题目 最长公共前缀编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”。示例 1:输入: [“flower”,“flow”,“flight”]输出: “fl”示例 2:输入: [“dog”,“racecar”,“car”]输出: “”解释: 输入不存在公共前缀。说明:所有输入只包含小写字母 a-z 。解法其实这道题很简单的,我们只需要两两比对,先拿出前两个字符的公共前缀,再将这个公共前缀与第三个字符进行两两比对,依次取,这样就得到了他们的公共前缀。那么如何取两个字符的公共前缀呢?我们需要个计数器为0,对字符从计数器为0开始的位置进行判断它们是否相等,然后相等的话++,就取出了公共前缀;当然字符长度不一,防止下标越界,先拿出两个字符的最小长度为约束;代码+详解 public String longestCommonPrefix(String[] strs) { // 这里先数据检查 if (strs == null || strs.length == 0) { return ""; } // 我们默认第一个字符为公共前缀,然后从第1个位置遍历 String prefix = strs[0]; for (int i = 1; i <= strs.length - 1; i++) { prefix = getPrefix(prefix, strs[i]); } return prefix; } public String getPrefix(String seed1, String seed2) { // 这里取得约束为两个字符最小长度 int minLen = Math.min(seed1.length(), seed2.length()); // 计数器开始记录相同的下标次数 int count = 0; // 只要相同下标位置的字符相同,那么符合 while (count < minLen && seed1.charAt(count) == seed2.charAt(count)) { count++; } // 最后返回两个字符其中一个的substring就可以 return seed1.substring(0, count); }完成。
力扣每日一题 0 103 0 发表
原创

关于博客的琐碎

30 5月 120
个人博客的话也是在家里呆着没事做开发的,所以写文章也是在忙里偷闲的时候写写,呆在家里因为一些事情花去了我蛮多的时间,还有就是跟朋友出去玩啊QAQ。关于博客因为是用的SPA,所以非常的影响SEO的优化,但这个也是暂时的,博主在考虑用后端的freemarker还是继续使用前后分离的方式,最后还是决定使用SSR即nuxt来重构下博客,毕竟网站流量太小了,也想引点流注入到网站,所以会花点时间,文章也会陆陆续续的更新,有空的话也还是会发发力扣的题解等博客后续会开发实用的工具,等方方面面的技术有关的,当然,这也得慢慢发展,还望各位大佬能提出宝贵的意见,博主会一一改进滴最后还是祝大家身体健康,代码莫得bug_
博客&生活 1 99 0 发表
原创

字符串压缩

21 5月 120
题目字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串aabcccccaaa会变为a2b1c5a3。若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。示例 1:输入:“aabcccccaaa”输出:“a2b1c5a3”示例 2:输入:“abbccd”输出:“abbccd”解释:“abbccd"压缩后为"a1b2c2d1”,比原字符串长度更长。提示:1.字符串长度在[0, 50000]范围内。上代码 public String compressString(String S) { if (null == S || S.length() == 0) { return ""; } // 先拿出第一个元素 char tmp = S.charAt(0); // 使用一个计数器判断出现的次数 int num = 1; // 输出值 StringBuilder sb = new StringBuilder(); for (int i = 0; i < S.length(); i++) { char curr = S.charAt(i); if (i == 0) { sb.append(tmp); continue; } if (curr == tmp) { num++; } else { sb.append(num).append(curr); tmp = curr; num = 1; } } sb.append(num); if (sb.length() < S.length()) { return sb.toString(); } return S; }思路:因为需要判断字符出现的次数,所以需要一个tmp临时变量来存储每个不同的字符,还需要一个计数器来计出现的次数,因为每个字符有且仅有一个所以初始值为1,然后开始循环,拿aabcccccaaa字符举例,tmp初始化取的是第一位字符也就是a,循环第一次拿出的肯定是a那么直接append并continue,如果这个tmp(a)与下次遍历的字符(a)相同就计算次数,否则的话就把tmp(a)字符出现的次数num(2)append然后append下一个不同的字符(b)并且tmp(a)重置为tmp(b),num(2)重置;以此类推,结束循环后把最后一个字符出现的次数append,判断长度比原子符是否更长,返回就行。当然代码有优化的地方,比如循环没必要从0开始因为第一个字符已经取出,所以i=1,也就省去第一个if判断,等等。其他的暂时先不深究,(懒QAQ)
力扣每日一题 2 114 0 发表
原创

算法篇:斐波那契数列

19 5月 120
题目已知斐波那契数列是:1,1,2,3,5,8,13…编码求第n项斐波那契数规律:当n小于等于2时,斐波那契数为1当n大于2时,第n项 = 第n-1项 + 第n-2项编码1.如果按照第n项 = 第n-1项 + 第n-2项的思路,第一时间想到的代码为:// 常规写法// 传个n给我,如果发现小于等于2返回1,否则递归调用算n-1 + n-2static int fib(int n) { if (n <= 2) return 1; return fib(n - 1) + fib(n - 2);}这样的写法看起来少,美观。但其实是最不好的写法,效率极低我们传n = 47,运行发现耗时已经有大约9秒了:那如果我们传n=48,10秒!!!这也是我的机器在1.99GHz下的运行结果,如果极其差一点,n值是60,或是65,那么运行时间一天都是有可能的。2.那么上面的写法为什么效率低下呢?从图中可以看出,当n为6时,会计算5跟4的fib,然后5会计算4与3,以此类推,这样就会出现重复的计算,就是说我在计算过fib(5)的时候,fib(4),已经在另一条分支中计算过了,同样的fib(3)也是。当然这也是计算fib(6)的分解,如果n值更大,那么分支更多,重复计算也就更多,这样才会使得效率更低下。3.数据规模假设数据规模为n的时间消耗是T(n)数据规模为n-1的时间消耗是T(n - 1)数据规模为n-2的时间消耗是T(n - 2)那么T(n) = T(n - 1) + T(n - 2) + O(1)为什么要加O(1),这里的O(1)是指常数级别的操作,如刚刚代码中的if (n <= 2) return 1;这样根据数据规模就会得到一个fib函数的时间复杂度为:O(2^n)时间复杂度:用来描述算法的所消耗时间空间复杂度:用来描述算法的空间消耗(比如int[] arr = new int[n];假设int类型占4个字节,一共是n,所以需要4n个字节,所以空间复杂度为额外的O(4n),但一般来说常系数都会省略,所以为O(n)时间复杂度越小,所消耗时间越少;空间复杂度越小,所消耗空间越小复杂度对比: O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)优化用数组存放计算过的结果,避免重复计算 static int fib2(int n) { int[] array = new int[n + 1]; if (n <= 2) return 1; array[1] = array[2] = 1; return fib2(n, array); } static int fib2(int n, int[] array) { if (array[n] == 0) { array[n] = fib2(n - 1, array) + fib2(n - 2, array); } return array[n]; }因为Java中int数组new出来时,每个位置的默认值都是0,所以我们判断fib(n)的位置如果是0,那么代表没有计算过,没计算就计算,如果不为0,代表计算过,那么直接返回;看下fib(46)的运行结果,使用原先的方式,运行所需4.4秒,第一种优化方案只需0毫秒几乎不耗时,结果都是1836311903没错这样的话,每个n只会计算一次,那么这种方式的时间复杂度为O(n),相比于旧的方式O(2^n)更好,差别有多大,可以看上面的复杂度对比这种数组的写法,直接去除了重复计算,需要fib(6)直接拿出fib(7)的时候计算过的fib(6)的值,所以更加的优化。那么O(2^n)与O(n)的差别有多大呢如果有一台1GHz的机器,运算速度为109每秒,当n为64时O(n)大约耗时6.4*10-8秒O(2n)大约耗时584.94年2.上面的代码采用自顶向下的计算,而且使用了递归,所以还不是最好的编码,这次我们使用自低向上的计算方式,这样就不需要使用递归,算完fib(1)就算fib(2)接着算fib(3)以此类推向上计算,那我们就这样写: static int fib3(int n) { if (n <= 2) return 1; int[] array = new int[n + 1]; array[1] = array[2] = 1; for (int i = 3; i <= n; i++) { array[i] = array[i - 1] + array[i - 2]; } return array[n]; }运行结果:这样就不用递归,而是采用for循环进行计算,这种方式的时间复杂度与上面的也是一样为O(n)级别,那么为什么比上面的递归好一点呢,因为我们知道递归调用是消耗栈空间的,所以我们这种方式更好,应该说是空间复杂度更好3.接下来继续优化,从上面的就算中我们发现每次计算它只需要数组中的两个元素。如果计算fib(9000)时,那么前面的8千多项就已经没用了,空间已经浪费了。因为我们只需要两个元素所以我们怎么优化呢?使用滚动数组的方式。我们只需要创建大小为2的数组,初始化两个位置分别为1,前面两项的值1 + 1 = 2,将2赋给下标1的位置,下一步将数组中两个元素相加那么就是1 + 2 = 3,很明显不能把3赋给下标1的位置,所以把3赋给下标0的位置,这样再继续相加,3 + 2 = 5再把5赋给下标1的位置,以此类推,抽象点理解就如同是在滚动,所以这种方式就是滚动计算。 static int fib4(int n) { if (n <= 2) return 1; int[] array = new int[2]; array[0] = array[1] = 1; for (int i = 3; i <= n; i++) { array[i % 2] = array[(i - 1) % 2] + array[(i - 2) % 2]; } return array[n % 2]; }由于我们下标只能取0和1,所以我们使用取模的方式,这样得到的结果就只能是0和1,而且这样就可以很自然的重复利用数组中的两个元素4.在我们计算机编程领域里有个常识;乘,除,模运算效率较低,建议使用其他方式取代为什么这三种方式效率较低,因为牵扯到了寄存器,这里不做赘述,所以最好的方式是使用位运算替代在这里3 % 2 = 1;4 % 2 = 0;5 % 2 = 1其实就是看是不是奇数或偶数蛮,那么3的二级制数为11,4的二进制为100,5的二级制为101,从中我们可以发现取模运算也就是看二级制最后一位是什么。所以我们使用&运算,&运算是什么意思呢?就是两个都为1则为1,只要有一个是0都是0,那么我们如何取最后一位进行位运算呢,我们与1进行&运算,3 & 1 = 1,二级制:3:11,1:01 &运算得出连个都为1 所以结果为1;4 & 1 = 0,二级制: 4:100, 1:001 &运算得出有一个为0,一个为1,所以结果为0;get! static int fib5(int n) { if (n <= 2) return 1; int[] array = new int[2]; array[0] = array[1] = 1; for (int i = 3; i <= n; i++) { array[i & 1] = array[(i - 1) & 1] + array[(i - 2) & 1]; } return array[n & 1]; }5.那么上面的方式就是最优的吗,其实还有,因为我们计算只需要两个元素,就不用数组直接使用变量来计算 static int fib6(int n) { if (n <= 2) return 1; int first = 1; int second = 1; for (int i = 3; i <= n; i++) { second = first + second; first = second - first; } return second; }因为斐波那契数列是1,1,2,3,5,8,13…所以第一个first加second就是下一个second的值,那么first又是谁呢?我们思考比如first(2) + second(3) = 新的second(5),所以新的first是谁呢?就是以前的second的值,也就是second(3),所以新的first(3) = 新的second(5) - 旧的first(2);get!,至此就是最后的优化方式。因为这种方式没有开辟额外的堆空间,前面的还开启了堆空间,所以这种方式的时间复杂度为O(n),空间复杂度为O(1);
算法 3 116 0 发表
原文:http://www.2cto.com/os/201310/252970.html解决方法:sed -i 's/\r$//' filename问题产生场景在windows系统下编写shell脚本时,然后上传到linux下执行时会报错/bin/bash^M: 坏的解释器: 没有那个文件或目录/bin/bash^M: 坏的解释器: 没有那个文件或目录windows下每一行的结尾是\n\r,而在linux下文件的结尾是\n,那么你在windows下编辑过的文件在linux下打开看的时候每一行的结尾就会多出来一个字符\r使用cat -A filename命令会发现\r字符被显示为^Mcat -A 什么意思?A就是all的意思,就是所有的都显示出现,也就是说\n\r默认是不显示的。这时候只需要删除这个字符就可以了。怎么删掉呢?正则表达式sed -i 's/\r//'filename什么意思呢?-i插入s替代模式\r表示任何以\r结束的字符
shell脚本 0 79 0 发表
原创

Linux安装git

8 5月 120
1. 安装安装前检查linux系统是否安装yum插件安装了的话使用命令安装gityum -y install git2.查看版本号是否安装成功git --version使用yum安装的git 在/usr/libexec/git-core目录下使用yum安装的git版本会较低哦
应用安装 2 80 1 发表
原创

Linux安装maven及配置

8 5月 120
环境Linux: centOS7.6(amd64)maven: 3.6.3下载官网地址:http://maven.apache.org/download.cgi上传到服务器目录下(这个目录不是固定的,只是方便管理)cd /usr/local/ && rz // 我这边使用了上传下载插件lrzsz(rz上传sz下载)解压 tar -zxvf apache-maven-3.6.3-bin.tar.gz 配置环境变量1.vim /etc/profile2.添加以下: export MAVEN_HOME=/usr/local/apache-maven-3.6.3 export PATH=$MAVEN_HOME/bin:$PATH3.刷新配置source /etc/profile检查配置mvn -v修改镜像,毕竟默认的要翻墙,这里使用阿里镜像在maven目录下(/usr/local/apache-maven-3.6.3) 进入conf目录修改settings.xml配置,在mirrors标签中添加以下<mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url></mirror>修改本地仓库Repositorysettings.xml找到localRepository标签添加:<localRepository>自己的仓库地址</localRepository>
应用安装 1 68 0 发表
原创

Java运算符

5 5月 120
运算符算数运算符:+, - , * , /, %; 加,减,乘,除, 取余赋值运算符:+=, -=, *=, %=; 加,减,乘,除, 取余赋值;++i: 先计算后取值,i++: 先取值后计算比较运算符:> , < , ==; >=, <= 大于,小于, 等于 != 不等于逻辑运算符:&, |, !, &&, ||, ^ 逻辑与,逻辑或, 非,短位与,短位或,异或位运算符(都为二进制运算,补位整形int):<< 左移; 3<<2 =12 代表m<<n 为m*2n (m乘与2的n次方),空位补0,被移除的高位丢弃,空缺位补0>> 右移(有符号的);3>>1 = 1 代表m>>n为m*2-n(m乘与2的负n次方,相当于m除以2的n次方) 被位移的二进制最高位是0,右移后,空缺位补0,最高位是1,空缺位补1正数左移补位为0,负数右移补位为1, 31<<2 =7,反之-31>>2 =-8正数补位为0,负数补位为1有符号的右移是看右移之后的首位(最高位)是0还是1,是0补0反之补1无符号的右移,移动之后不管首位是0或1,都补0>>> 无符号右移 ; 正数的>>>和右移>>正数相同, 被位移的二级制最高位无论是0或者是1,空缺位都用0补& 与运算: 同位&运算时,都是1结果为1,其他情况为0,二进制位进行&运算,只有1&1时结果为1,否则是0 例: 12 & 5 = 4 12:00001100, 5:00000101,4:00000100| 或运算:同位|运算时,都是0结果为0,其他情况都为1,二级制|运算,只有0|0时结果为0,否则为1 例: 12 | 5 = 13 12:00001100,5:00000101, 13:00001101^异或运算: 同为^运算时,都是0或者都是1的时候结果为0,其他情况都是1 相同二进制位进行^运算,结果为0;1^1=0,0^0=0 不相同二进制位^运算结果是1;1^0=1,0^1=1 例: 12 ^ 5 = 9 12:00001100, 5:00000101, 9:00001001~反码运算: 把二进制同位反转,同位为0则变1,反之变0,正数取反,各二进制码按补码各位取反; 负数取反,各二进制码按补码各位取反; 例: ~12 = -13 12:1100,(这里补位为0) 13:0011(这里补位为1因为取反了)三目运算符:(表达式) ? true : false;
运算符 0 89 0 发表
原创

深入理解SPI机制

5 5月 120
什么是SPISPI ,全称为 Service Provider Interface,服务扩展接口,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。Servlet 3.0规范文档里面就有提及使用了SPI的地方我们先通过一个很简单的例子来看下它是怎么用的。举例常规使用首先定义一个接口,文档解析接口,里面定义一个解析方法public interface IParseDoc { /** * 解析方法 */ void parse();}2.定义两个实现类public class ExcelParse implements IParseDoc { @Override public void parse() { System.out.println("解析excel文档"); }}public class WordParse implements IParseDoc { @Override public void parse() { System.out.println("解析world文档"); }}3.如果我们要使用他们的的功能,那么就调用每个类的实现方法 public static void main(String[] args) {// IParseDoc parseDoc = new ExcelParse(); IParseDoc parseDoc = new WordParse(); parseDoc.parse(); }这样使用看起来没毛病,如果项目经理要求我们,嘿现在不用excel解析了,改为word解析,那么这时候就得改代码,然后打包发布。一般情况下工程打包发布有两种,一种为全量包,即改了代码后全包编译,打包,部署,重启服务。另一种增量包,只把改了的类编译,然后到对应的服务目录下替换就行,但是这种方式会有一个问题,假如同事在这个时候提交了代码,有类引用到使用,然后你只编译替换了这个类,导致代码不一致,启动服务异常,引发生产事故,这样就开始背锅的背锅,甩锅的甩锅QAQ这个时候最好的方式就是使用SPISPI的使用SPI它是一种规范(规定),那么如何使用呢?首先在工程下创建一个文件夹,文件夹名称必须为META-INF然后它有一个子目录,名称也必须为servicesservices里面有一个文件,名称为接口的全类名那么里面的内容是什么?就是这个接口的实现类的全类名,而且必须为一行一个全类名的规范空格和tab会被忽略,#号为注释假如我们现在使用的是解析excel那么该如何使用呢?它提供了一个类叫:ServiceLoader然后使用它的load方法,入参为我们使用的接口类即IParseDoc.class,那么为什么需要传class呢,下面会提到 public static void test() { // 把我们的接口类型保存到serviceloader, ServiceLoader<IParseDoc> iParseDocs = ServiceLoader.load(IParseDoc.class); // 返回加载的迭代器对象 Iterator<IParseDoc> iParseDocIterator = iParseDocs.iterator(); while (iParseDocIterator.hasNext()) { iParseDocIterator.next().parse(); } }4.拿到类,通过Class.forName,反射机制创建对象,调用对象的方法为什么会用META-INF/services/全类名文件这样的规范形式呢?其实在ServiceLoader类中就有我们查看ServiceLoader.load()的源码,然后往上看代码会发现,好家伙,直接写死的!他会拿到一个前缀,然后我们使用这个方法:ServiceLoader<IParseDoc> iParseDocs = ServiceLoader.load(IParseDoc.class);就会得到:META-INF/services/IParseDoc.class,然后使用这个类的getName()方法,就会得到完整的路径META-INF/services/这个类的全类名(demo.demo.study.spi.IParseDoc),这样就可以定位到具体的文件了!5. 我们来运行代码public class Main { public static void main(String[] args) {// IParseDoc parseDoc = new ExcelParse();// IParseDoc parseDoc = new WordParse();//// parseDoc.parse(); test(); } public static void test() { // 把我们的接口类型保存到serviceloader, ServiceLoader<IParseDoc> iParseDocs = ServiceLoader.load(IParseDoc.class); // 返回加载的迭代器对象 Iterator<IParseDoc> iParseDocIterator = iParseDocs.iterator(); while (iParseDocIterator.hasNext()) { iParseDocIterator.next().parse(); } }}得到结果:粗体这样就完成了,如果使用world解析,只需注释excel的注释即可,那么这种好处在哪里呢?使用SPI,我们就不用硬编码,只需修改配置文件就行,然后也不用编译,只需在服务器修改配置文件,重启就ojbk了,你说它不香吗?
SPI 0 67 0 发表
  • 1
  • 请选择
    跳至

公告栏

Yu Shan博客正式上线了,发布版本1.0,还望大家多多提bug留言,谢谢支持,祝您生活愉快。

最近留言

Author
站长 (7月30日)
[emoji_0]已经工作了呢
Lemon (7月30日)
[emoji_1]怎么样朋友~我还不知道你现在什么工作,是在上学吗,还是已经是程序员了
s (7月16日)
s
西瓜 (6月14日)
[emoji_0]
西瓜 (6月14日)
[emoji_0]
西瓜 (6月14日)
[emoji_0]
Author
站长 (6月5日)
已为你添加[emoji_3]
刘德华 (6月5日)
难道我昨天申请友链没有成功吗[emoji_4]
K (5月22日)
在路上,点进来考察的[emoji_0]
的方式 (5月21日)
咋发发呆