Open KieSun opened 5 years ago
归并排序使用算法中的分治思想,将一个大数组拆分成 2 个小数组,直到最后拆分为 1 个元素的数组,最后再将每个小数组按顺序合并成大数组。
将二个有序数组合并也十分简单,只需要从二个数组的第一个数开始比较,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数组为空,那直接将另一个数组的数据依次取出即可。
归并排序的效率是比较高的,设数列长为 N,将数列分开成小数列一共要 logN 步,每步都是一个合并有序数列的过程,时间复杂度可以记为 O(N),故一共为 O(N*logN)。
// 归并排序算法
function mergeSort(array) {
// 避免污染传入的数组
const temp = [...array];
splitArray(temp, 0, array.length - 1);
return temp;
}
// 将大数组拆分成两个小数组
function splitArray(array, start, end) {
if (start < end) {
const mid = Math.floor((start + end) / 2);
splitArray(array, 0, mid);
splitArray(array, mid + 1, end);
mergeArray(array, start, mid, end);
}
}
// 合并两个排序好的数组
function mergeArray(array, start, mid, end) {
var i = start;
var j = mid + 1;
var k = 0;
var temp = [];
while (i <= mid && j <= end) {
if (array[i] <= array[j]) {
temp[k] = array[i];
i++;
} else {
temp[k] = array[j];
j++;
}
k++;
}
while (i <= mid) {
temp[k] = array[i];
i++;
k++;
}
while (j <= end) {
temp[k] = array[j];
j++;
k++;
}
for (let index = 0; index < k; index++) {
array[index + start] = temp[index];
}
}
var array = [4, 3, 1, 9, 6, 2, 7, 8, 5];
console.warn('归并排序', mergeSort(array));
Math.trunc(13.37) // 13
...没有进位有啥用。。
不知道啊,有这个API,应该有它的用处吧
JS 函数对象参数的陷阱
上周在实现某个弹层功能的时候,用到了rc-util
里的 contains
方法函数, 结果 code-review
的时候同事对该代码提出了疑问:
export default function contains(root, n) {
let node = n;
while (node) {
if (node === root) {
return true;
}
node = node.parentNode;
}
return false;
}
上述代码是 antd
内部抽象的一个工具方法,用来判断某个dom是否为另一个dom的祖先节点。
同事疑问的是 let node = n;
这段代码是不是多余的?
首先一开始的理解是 函数参数 n
是一个对象,一个dom
节点对象。
如果用 node
保存 n
的值,防止 node = node.parentNode
这段代码执行的时候,会改变传入的实参 n
对应的值。
毕竟以下的代码我们都很熟悉:
function contains(root, n) {
if(n) {
n.a = 3
}
}
const A = {a:1};
const B = {a:2};
contains(A,B)
console.log(B) // {a:3}
即当实参为对象时,函数内部是可以改变该对象的值从而影响函数之外的实参。
但是测试另外一段代码,发现和理解的不一样:
function contains(root, n) {
if(n) {
n = {a:3}
}
}
const A = {a:1};
const B = {a:2}
contains(A,B)
console.log(B) // {a:2}
即 n.a = 3
和 n = {a:3}
这两段代码是不一样的。
网上也有相关资料,其实可以简单的理解为: 当函数一开始执行时,n
是指向实参 B
的一个引用.
n.a = 3
是在引用上再关联了一个属性,此时和 B
还是同一个引用,因此会改变实参B
的值。
而 n = {a:3}
则使得 n
不再指向实参 B
, 而是指向一个新对象{a:3}
,也就是 n
与 B
彻底断绝了关系,因此不会改变实参 B
的值。
是不是可以给蚂蚁的团队提个issue建议删除该代码,不过有这句代码也不会有什么bug~
开始学习数据结构与算法 Java版
共用时2个小时,视频+思考+敲代码
// 裴波那契数列指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……
// value = 前两个数的和 表达式F(n)=F(n-1)+F(n-2)
// 算法1
public static long fib(long n) {
if(n <= 1) return n; // 执行1+1=2次数 n <= 1 一次 return n 一次
return fib(n-1)+fib(n-2); // 0.5*2^n次
}
// 算法2
public static long fib(long n) {
if(n <= 1) return n; // 2
long first = 0; // 1
long second = 1; // 1
for (long i = 0; i < n - 1; i++) { // n
long sum = first + second; // n
first = second; // n
second = sum; // n
}
return second; // 1
}
/*
(1)阵列元素相加为2n+3 = O(n)
(2)矩阵相加为2n^2+2n+1 = O(n^2)
(3)矩阵相乘为2n^3+4n^2+2n+2 = O(n^3)
(4) 执行次数为常数 2 = O(1)
算法2的时间复杂度是O(n)
2+1+1+4n+1 = 4n+5 取最高阶 n O(n)
算法1的时间复杂度是O(2^n)
比如:fib(5)
图1
可以看到fib(5) , 第一行是1个,第二行是2个(fib(4) 和 fib(3)), 第三行是 4个(fib(3),fib(2),fib(2),fib(1)) , 第四行是 6个。第五行 2个。
1 + 2 + 4 + 8
= 2 ^0 + 2 ^ 1 + 2^2 + 2^3
= 2^4 - 1
= 2^(n - 1) - 1
= 0.5 * 2^n
所以时间复杂度是 O(2^n)
链接:https://blog.csdn.net/M316625387/article/details/89467298
*/
(图1)csdn博客
2019/07/22
01 graphql 中多级分页查询时的 N+1 问题
此处只能在客户端避免多层分页查询,而当有恶意查询时会加大服务器压力。可以使用 Hash Query 避免此类问题,避免暴露原始 Query,同时也在生产环境禁掉
introspection
{ users (page: 1, pageSize: 3) { id todos (page: 1, pageSize: 3) { id name } } }
select id from users limit 3 select id, name from todo where user_id = 1 limit 3 select id, name from todo where user_id = 2 limit 3 select id, name from todo where user_id = 3 limit 3
02 在前端中把 gql 统一管理减小打包体积
准确说,已经把 gql 统一管理为
query.gql
,通过 loader 引入,会被打入common.js
。 而在个别页面又单独通过graphql-tag
引入,被按需加载打包,而此时graphql-tag
又依赖graphql
,gql 统一管理后,此时的两个模块的依赖也就没有了。通过 webpack 分析,
gzip
后的总体积变化: 191.64KB -> 176.51KB,体积缩减还是比较可观的。03 获取当前 commit hash
可以通过
npm scripts
注入到项目中,方便集成到sentry
,logger
等工具组件中git rev-parse --verify HEAD | cut -c 1-8
后来找到更简单的命令
git rev-parse --short HEAD
2019/07/23
01 本地更好的流式日志格式化
服务器端的日志很重要,一般设置为
jsonl
格式,方便在 elk 上查询或者使用 spark 分析。而在本地调试时就不是很直观了,而日志又是 stream 的数据。此时可以使用jq
对日志进行美化# 实时查看日志,并且只关注 message 字段 tail -f logs/db.log | jq '{message}'
此时你不仅有了更好的可视化,而且只需要关注你自己关心的字段就可以了,体验很棒
02
Intersection Observel
做路由页面预加载与图片懒加载由于我的网站使用了
next.js
的技术栈,最近对 next 升级到了 v9,对 next 一直有所关注。在今天读了 Next.9 features,其中有一个点:Next.js 9 will automatically prefetch components as they appear in-viewport.
其中原理便是使用
Intersection Observel
来监听元素是否出现在当前可视窗口内。Next.js
可以对新的页面资源做懒加载,举一反三,我们也可以使用它对图片做懒加载。03 了解一个 APP 从开发到盈利所经历的事
2019/07/24
01 在
React
中使用React.Fragment
或者<></>
包含多个子元素恩,这个是查看某项目代码时看到的,虽然一直知道了
react
目前已经可以包含多元素,但也没有细究,今天偶然得知,受用很多。( <> <td>Hello</td> <td>World</td> </> );
02
sar
(System Activity Monitor)
sar
与vmstat
,iostat
相比,不仅可以实时监控 linux 系统 IO/memory/cpu 等的信息,而且可以 获取一段时间内的系统统计信息,比如他可以获取一天时间内的 CPU 使用率。我也把它记录在了我的博客里03 Apollo Federation & GraphQL Gateway
简而言之,
Apollon Federation
可以把多个graphql service
组合成一个 API Gateway。如果公司的所有业务与微服务都使用了 GraphQL,则它正好可以做一个不错的服务整合,这还挺令人兴奋的。2019/07/25
总结了LF 与 CRLF 的一篇文章
了解一些底层知识,视野会开阔很多。如本篇文章可以通过系统调用来看换行符到底是什么。
用户增长模型 RARRA
我的理解,用户留存比较重要,应作为着力点
2019/07/26
了解 vuepress 如何写插件,并给我的博客写了一个归档的插件
https://github.com/shfshanyue/blog/blob/master/.vuepress/config.js
2019/07/27
尝试使用 netlify 新建了我的博客
博客地址 https://shanyue.netlify.com/
至于我现在的博客还是使用 hugo 构建,不得不说 hugo 的构建速度真是很快。另外使用自建的 gitlab,gitlab-ci,docker-compose 来做自动部署,不得不说运维这些还是有些成本的。而
netlify
就简单很多了,这些运维工作都不需要你来关心。云是一种趋势,以后考虑能够 SAAS 化的服务全部 SAAS 化。
graphql-code-generator 自动为前端生成 graphql schema 相对应的 ts 的 type
毫无疑问,前端不用手动配置关于接口的 interface,大大解放了前端的生产力,而且减少了失误率。
大佬每天都记录了学习的内容,真的很棒!
Math.trunc(13.37) // 13
...没有进位有啥用。。
不知道啊,有这个API,应该有它的用处吧
要进位的话就用Math.round(), 这个就是用来去除小数部分的,还会隐式把参数转为数字。
20190728 study
mkdir qingcheng
npm init
npm login
npm publish --access=public // 设置 --access=public才能发布@开头的包
首先执行下 npm adduser ,输入相应的 Username 、 Password 、 Email。 中途遇到一个问题,没有发布成功,是镜像的原因, Logged in as 您的Username on https://registry.npmjs.org/. 如果 on 后面不是 https://registry.npmjs.org/ ,而是其他的镜像,比如我们大家常见的淘宝镜像: http://registry.npm.taobao.org/ 那么首先替换成原来的,替换成原来执行如下命令: npm config set registry https://registry.npmjs.org/ 最后,替换完毕再执行 npm adduser 、 npm publish
2019-07-27
JS 函数对象参数的陷阱
1. 正文
上周在实现某个弹层功能的时候,用到了
rc-util
里的contains
方法函数, 结果code-review
的时候同事对该代码提出了疑问:export default function contains(root, n) { let node = n; while (node) { if (node === root) { return true; } node = node.parentNode; } return false; }
上述代码是
antd
内部抽象的一个工具方法,用来判断某个dom是否为另一个dom的祖先节点。同事疑问的是
let node = n;
这段代码是不是多余的?首先一开始的理解是 函数参数
n
是一个对象,一个dom
节点对象。 如果用node
保存n
的值,防止node = node.parentNode
这段代码执行的时候,会改变传入的实参n
对应的值。毕竟以下的代码我们都很熟悉:
function contains(root, n) { if(n) { n.a = 3 } } const A = {a:1}; const B = {a:2}; contains(A,B) console.log(B) // {a:3}
即当实参为对象时,函数内部是可以改变该对象的值从而影响函数之外的实参。
但是测试另外一段代码,发现和理解的不一样:
function contains(root, n) { if(n) { n = {a:3} } } const A = {a:1}; const B = {a:2} contains(A,B) console.log(B) // {a:2}
即
n.a = 3
和n = {a:3}
这两段代码是不一样的。网上也有相关资料,其实可以简单的理解为: 当函数一开始执行时,
n
是指向实参B
的一个引用.
n.a = 3
是在引用上再关联了一个属性,此时和B
还是同一个引用,因此会改变实参B
的值。而
n = {a:3}
则使得n
不再指向实参B
, 而是指向一个新对象{a:3}
,也就是n
与B
彻底断绝了关系,因此不会改变实参B
的值。是不是可以给蚂蚁的团队提个issue建议删除该代码,不过有这句代码也不会有什么bug~
2. 相关资料
分析思路没啥问题, 不过这么做的真正原因其实是因为一条规则: 不对函数的参数进行赋值, 规则见 eslint
@lllllllqw
学习了,原来是这样, 看了下utils
最终是基于 airbnb做的eslint规范,确实 airbnb规范存在这条规则。
移动端打开指定App或者下载App
navToDownApp() { let u = navigator.userAgent if (/MicroMessenger/gi.test(u)) { // 如果是微信客户端打开,引导用户在浏览器中打开 alert('请在浏览器中打开') } if (u.indexOf('Android') > -1 || u.indexOf('Linux') > -1) { // Android if (this.openApp('en://startapp')) { this.openApp('en://startapp') // 通过Scheme协议打开指定APP } else { //跳转Android下载地址 } } else if (u.indexOf('iPhone') > -1) { if (this.openApp('ios--scheme')) { this.openApp('ios--scheme') // 通过Scheme协议打开指定APP } else { // 跳转IOS下载地址 } } }, openApp(src) { // 通过iframe的方式试图打开APP,如果能正常打开,会直接切换到APP,并自动阻止a标签的默认行为 // 否则打开a标签的href链接 let ifr = document.createElement('iframe') ifr.src = src ifr.style.display = 'none' document.body.appendChild(ifr) window.setTimeout(function() { // 打开App后移出这个iframe document.body.removeChild(ifr) }, 2000) }
欢迎一起讨论,如果有更好的写法欢迎指教
😁推荐个库 callapp-lib
...没有进位有啥用。。