笔记本

fuser -n tcp 23456   //????????????
lsof -i:23456       //????????????

'export PATH=$PATH:/home/ldh/node-v6.9.1-linux-x64/bin'>> ~/.bashrc

vscode 5812d4380e8636bb9816e30f9ad103adbeaabd2b

Marketplace (publish) 1year g2a3fyqmtz3w5hr37jkae2kjobu7axqgdb7nek3jnjoqvkfek4la

vscode

gist id 6ad3fe96e97570fba9b23fdd1e27328e

gist token ad80d9cde11eddec18279b13d56647a273dd6890

sudo apt-get  install  build-essential  # 安装gcc g++

export http_proxy=socks5://127.0.0.1:1080
export https_proxy=socks5://127.0.0.1:1080

# depot_tools
export PATH=$PATH:$HOME/softwares/depot_tools

alias hp="http_proxy=http://127.0.0.1:8124 && https_proxy=http://127.0.0.1:8124"
alias ehp="export http_proxy=http://127.0.0.1:8124 && export https_proxy=http://127.0.0.1:8124"

解决apt-get中Unmet dependencies问题

sudo apt --fix-broken install  
sudo apt-get update  
sudo apt-get upgrade  


function arrangement(n, num) {
  let res = [];
  select(n, num, [])
  function select(n, num, selected) {
    if (num === 0) { res.push(selected.slice(0)); return; }
    for (let i = 0; i < n; i++) {
      if (selected.includes(i)) {
        continue;
      }
      selected.push(i)
      select(n, num - 1, selected);
      selected.pop(i)
    }
  }
  return res;
}

function combination(n, num) {
  let res = [];
  select(n, num, [], 0)
  function select(n, num, selected, index) {
    if (num === 0) { res.push(selected.slice(0)); return; }
    for (let i = index; i < n; i++) {
      if (selected.includes(i)) {
        continue;
      }
      selected.push(i)
      select(n, num - 1, selected, i + 1);
      selected.pop(i)
    }
  }
  return res;
}

./build/install-build-deps.sh –no-chromeos-fonts

export const milliFormat = (() => {
    const DIGIT_PATTERN = /(^|\s)-?\d+(?=\.?\d*($|\s))/g;
    const MILLI_PATTERN = /(?=(?!\b)(\d{3})+$)/g;

    return (input) => input && input.toString()
        .replace(DIGIT_PATTERN, (m) => m.replace(MILLI_PATTERN, ','));
})();

670120plL

webgl学习4——画一个三角形并旋转缩放平移

shader如下
attribute vec4 position;  
uniform mat4 matrix;
void main(void){  
    gl_Position = matrix * position;
    gl_PointSize = 10.0;
}  

void main(void){  
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);  
}  

这里通过matrix 一个4*4矩阵来变换。通过uniform变量来传递。

uniform 变量

我们已经知道了如何从js中向顶点着色器的attribute变量传递数据,这里有个新的变量uniform,它用来从js中向顶点or片元着色器传输一致的(不变的)数据。如下使用

// 以z轴为中心,旋转90度
let mat4Angles = new Mat4().setFromEulerAngles(0, 0, 90);
// x,y缩放1.5倍
let mat4Scale = new Mat4().setScale(1.5, 1.5, 1);
// x,y位移0.2
let mat4Translate = new Mat4().setTranslate(0.2, 0.2, 0.2);
// 矩阵相乘
let mat4 = mat4Angles.mul(mat4Scale).mul(mat4Translate);
//获取uniform变量的下标
const matrix = gl.getUniformLocation(program, 'matrix');
// 赋值
gl.uniformMatrix4fv(matrix, false, mat4.data)

demo

demo
github

webgl学习5——有颜色的三角形

// vertex shader
attribute vec4 position;  
uniform mat4 matrix;
attribute vec4 a_Color;
varying vec4 v_Color;
void main(){  
    gl_Position = matrix * position;
    v_Color = a_Color;
}
// fragment shader
precision lowp float;
varying vec4 v_Color;                
void main(void) {                          
    gl_FragColor = v_Color;                
}

varying变量

向片元着色器传递数据(片元着色器不支持attribute变量),先声明attribute变量a_Color用以接受颜色数据,然后声明了新的varying变量v_Color,该变量负责将颜色值传给片元着色器。varying变量只能是float(以及像个的vec2 vec3 vec4 mat3 mat4)类型。

demo

demo
github

webgl学习3——画一个三角形

准备顶点的坐标

let vertices = new Float32Array([-0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.0, 0.5, 0.0])

创建顶点缓存VBO

createVbo(data: Float32Array): WebGLBuffer {
    let { gl } = this;
    // 创建缓存区对象
    let vbo = gl.createBuffer();
    // 将缓冲区对象绑定到目标
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
    // 想向缓冲区对象中写入数据
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    return vbo;
}

将顶点缓存传入顶点attibute变量

const a_Position = gl.getAttribLocation(program, 'position');
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);

vertexAttribPointer

gl.vertexAttribPointer(index, size, type, normalized, stride, offset)

index 指定要修改的顶点属性的索引。
size 指定每个顶点属性的数量。
type 数据类型.
   gl.BYTE: signed 8-bit integer, with values in [-128, 127]
   gl.SHORT: signed 16-bit integer, with values in [-32768, 32767]
   gl.UNSIGNED_BYTE: unsigned 8-bit integer, with values in [0, 255]
   gl.UNSIGNED_SHORT: unsigned 16-bit integer, with values in [0, 65535]
   gl.FLOAT: 32-bit IEEE floating point number
   When using a WebGL 2 context, the following values are available additionally:
      gl.HALF_FLOAT: 16-bit IEEE floating point number
normalized 指定整数数据值在被转换为浮点数时是否应归一化到一定范围内。
   对于类型gl.BYTE和gl.SHORT,如果为true,则将值标准化为[-1,1]。
   对于gl.UNSIGNED_BYTE和gl.UNSIGNED_SHORT类型,如果值为true,则将值标准化为[0,1]。
   对于gl.FLOAT和gl.HALF_FLOAT类型,此参数不起作用。
stride 指定连续顶点属性开始之间的偏移量(以字节为单位)。不能大于255.如果步幅为0,则假定属性紧密排列。
offset 指定顶点属性数组中第一个组件的字节偏移量。必须是类型的倍数。

enableVertexAttribArray

gl.enableVertexAttribArray(index);

开启指定索引的顶点属性

drawArrays

void gl.drawArrays(mode, first, count);


demo

demo
github

webgl学习2——鼠标画点

与上篇博客不同的是,这次试用了attribute变量,向shader中传参数。

let vertices = [0.0, 0.0];

let gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
let program = initShaders(gl, vert, frag);
const a_Position = gl.getAttribLocation(program, 'position');
canvas.addEventListener('click', (e) => {
    let rect = canvas.getBoundingClientRect();
    let x = e.clientX / rect.width * 2 - 1;
    let y = 1 - e.clientY / rect.height * 2;
    vertices.push(x, y)
    console.log(x, y);
    gl.clear(gl.COLOR_BUFFER_BIT);
    for (let i = 0; i < vertices.length; i = i + 2) {
        let x = vertices[i];
        let y = vertices[i + 1];
        gl.vertexAttrib3f(a_Position, x, y, 0)
        gl.drawArrays(gl.POINTS, 0, 1);
    }
})
gl.vertexAttrib3f(a_Position, vertices[0], vertices[1], 0.0)
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);

getAttribLocation

getAttribLocation(program: WebGLProgram | null, name: string): number;

返回属性位置的下标 GLint 数字,如果找不到该属性则返回-1。

WebGLRenderingContext.vertexAttrib[1234]f[v]()

可以为顶点attibute变量赋值。

void gl.vertexAttrib1f(index, v0);

void gl.vertexAttrib2f(index, v0, v1);

void gl.vertexAttrib3f(index, v0, v1, v2);

void gl.vertexAttrib4f(index, v0, v1, v2, v3);

void gl.vertexAttrib1fv(index, value);

void gl.vertexAttrib2fv(index, value);

void gl.vertexAttrib3fv(index, value);

void gl.vertexAttrib4fv(index, value);

const a_foobar = gl.getAttribLocation(shaderProgram, 'foobar');
//either set each component individually:
gl.vertexAttrib3f(a_foobar, 10.0, 5.0, 2.0);
//or provide a Float32Array:
const floatArray = new Float32Array([10.0, 5.0, 2.0]);
gl.vertexAttrib3fv(a_foobar, floatArray);

前3个参数默认补0,最后一位补1.

demo
github

webgl学习1——画一点

shader编译

export function loadShader(gl: WebGLRenderingContext, type: number, source: string) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
        console.log(gl.getShaderInfoLog(shader));
        return false;
    }
    return shader;
}

连接pragrom

export function createProgram(gl: WebGLRenderingContext, vshader: string, fshader: string) {
    const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
    const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!linked) {
       //获取连接状态
        console.log(gl.getProgramInfoLog(program));
        return false;
    }
    gl.useProgram(program)
    return program;
}

清楚画布

gl.clearColor(0.0, 0.0, 0.0, 1.0); 设置清除颜色
gl.clear(this.gl.COLOR_BUFFER_BIT);
程序
//顶点着色器
void main(void){  
    gl_Position = vec4(0.0,0.5,0.0,1.0);
    gl_PointSize = 10.0;
} 
直接在shader中,赋值坐标,直接画出点
gl.drawArrays(gl.POINTS, 0, 1);

demo
github

pageX,clientX,screenX,offsetX 区别

鼠标事件上坐标

pageX/pageY:

鼠标相对于整个页面的X/Y坐标。

注意,整个页面的意思就是你整个网页的全部,比如说网页很宽很长,宽2000px,高3000px,那pageX,pageY的最大值就是它们了。

特别说明:IE不支持!

clientX/clientY:

事件发生时鼠标在浏览器内容区域的X/Y坐标(不包含滚动条)。

浏览器内容区域即浏览器窗口中用来显示网页的可视区域,注意这个可视,也就是说需要拖动滚动条才能看到的区域不算。

当你将浏览器窗口缩小时,clientX/clientY的最大值也会缩小,但始终,它们的最大值不会超过你浏览器可视区域。

特别说明:IE下此属性不规范,它们的最小值不是0而是2,也就是说IE下的clientX/clientY比火狐下始终大2px。

screenX/screenY

鼠标在屏幕上的坐标。screenX,screenY的最大值不会超过屏幕分辨率。

offsetX/offsetY:

得出的结果跟pageX/pageY一样,既然如此,它有什么存在价值?因为:

特别说明:只有IE支持!相当于IE下的pageX,pageY。

e.clientX + document.documentElement.scrollLeft - document.documentElement.clientLeft = e.pageX

e.clientY + document.documentElement.scrollTop - document.documentElement.clientTop = e.pageY

滚动高度

为了跨浏览器兼容,请使用 window.pageYOffset 代替 window.scrollY;

IE(<9)两个属性都不支持;

safari不支持document.documentElement.scrollTop;

下面的兼容写法

let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;

齐次坐标理解

齐次坐标就是将一个原本是n维的向量用一个n+1维向量来表示,是指一个用于投影几何里的坐标系统,如同用于欧氏几何里的笛卡儿坐标一般。

二维点(x,y)的齐次坐标表示为(hx,hy,h),由此可以看出,一个向量的齐次表示是不唯一的,齐次坐标的h取不同的值都表示的是同一个点,比如齐次坐标(8,4,2)、(4,2,1)表示的都是二维点(4,2)。

给出点的齐次表达式[X Y H],就可求得其二维笛卡尔坐标,即[X Y H]→= [x y 1], 这个过程称为归一化处理。

引进齐次坐标有什么必要,它有什么优点呢?

许多图形应用涉及到几何变换,主要包括平移、旋转、缩放。以矩阵表达式来计算这些变换时,平移是矩阵相加,旋转和缩放则是矩阵相乘,综合起来可以表示为p’ = m1p+ m2(注:因为习惯的原因,实际使用时一般使用变化矩阵左乘向量)(m1旋转缩放矩阵, m2为平移矩阵, p为原向量 ,p’为变换后的向量)。引入齐次坐标的目的主要是合并矩阵运算中的乘法和加法,表示为p’ = pM的形式。即它提供了用矩阵运算把二维、三维甚至高维空间中的一个点集从一个坐标系变换到另一个坐标系的有效方法。

问题:两条平行线可以相交于一点

  • 在欧氏几何空间,同一平面的两条平行线不能相交,这是我们都熟悉的一种场景。
  • 然而,在透视空间里面,两条平行线可以相交,例如:火车轨道随着我们的视线越来越窄,最后两条平行线在无穷远处交于一点。

欧氏空间(或者笛卡尔空间)描述2D/3D几何非常适合,但是这种方法却不适合处理透视空间的问题(实际上,欧氏几何是透视几何的一个子集合),2维笛卡尔坐标可以表示为(x,y)。

如果一个点在无穷远处,这个点的坐标将会(∞,∞),在欧氏空间,这变得没有意义。平行线在透视空间的无穷远处交于一点,但是在欧氏空间却不能,数学家发现了一种方式来解决这个问题。

摘自

Javascript 中 atob 方法解码中文字符乱码

Uncaught DOMException: Failed to execute ‘btoa’ on ‘Window’: The string to be encoded contains characters outside of the Latin1 range.

1.借助 encodeURIComponent 和 decodeURIComponent 转义非中文字符

编码

> window.btoa(encodeURIComponent('中文'))
"JUU0JUI4JUFEJUU2JTk2JTg3"
解码

> decodeURIComponent(window.atob('JUU0JUI4JUFEJUU2JTk2JTg3'))
"中文"

2.第三方 Base64 工具

https://gitee.com/loonhxl/jbase64/blob/master/jbase64.js

linux

cat ~/.ssh/dadigua/id_rsa.pub | ssh root@dadigua.win "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

react学习

去年看了很多react底层实现的文章,写了一个mini版的react,还有svg渲染模式等没完善。今天过年来了把它完善了。主要实现react api,和其他类react的库对比,把合成事件加上了,兼容性很好(preact是没合成事件的)。

GITHUB

  • learn-react react
  • antd fork的antd源码,改了下配置跑起来,基本完美。(除了有一个用了jq的地方表现形式不一样。我的react在diff的时候,是直接跳过非自己渲染的dom)

监听路由跳转

最近要写一个前端统计的项目,需要监听页面跳转

 private captureRouteChange() {
    if (this.option.useHash) {
        window.addEventListener('hashchange', (e) => {
            this.changeURL({
                currURL: e.newURL,
                oldURL: e.oldURL
            });
        }, false);
    } else {
        ((history) => {
            // 覆盖 history.pushState 方法
            const pushState = history.pushState;
            history.pushState = (...args) => {
                let oldURL = window.location.href;
                const res = pushState.apply(history, args);
                this.changeURL({
                    currURL: window.location.href,
                    oldURL
                });
                return res;
            };
            const back = history.back;

            let oldURL = window.location.href;
            history.back = (...args) => {
                oldURL = window.location.href;
                const res = back.apply(history, args);
            };
            window.addEventListener('popstate', (event) => {
                this.changeURL({
                    currURL: window.location.href,
                    oldURL
                });
                oldURL = window.location.href;
            });
        })(window.history);

    }
}

leetcode --- 3Sum Closest

给定一个n个整数的数组s,找出s中的三个整数,使得总和最接近给定的数字.返回三个整数的总和。还是类似3Sum的解法。

func threeSumClosest(nums []int, target int) int {
    sort.Ints(nums)
    temp :=math.MaxInt32  
    result :=0
    for i := 0; i < len(nums); i++ {
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        if i >= len(nums)-2 {
            break
        }
        res:=twoSum(nums[i+1:],target-nums[i])
        if res+nums[i]==target {
             return target
        }
        absRes:=abs(target-nums[i]-res)
        if temp>absRes{
            temp=absRes
            result=res+nums[i]
        }
    }
    return result
}

func abs(x int) int {
    if x < 0 {
        return -x
    }
    return x
}

func twoSum(nums []int,target int) int {
    // fmt.Println(nums,target)
    l:=0
    r:=len(nums)-1
    temp:=math.MaxInt32 
    result:=0
    for   {
        if(r<=l){
            break
        }
        absRes:=abs(target-nums[l]-nums[r])
        if temp>absRes{
            temp=absRes
            result=nums[l]+nums[r]
        }
        if nums[l]+nums[r]<target {
            l++;
            continue;
        }
        if nums[l]+nums[r]>target {
            r--;
            continue;
        }
        if nums[l]+nums[r]==target {
            return nums[l]+nums[r]
        }
    }
    return result
}

leetcode--3Sum

func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    result := [][]int{}
    for i := 0; i < len(nums); i++ {
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        if nums[i] > 0 {
            break
        }
        for j := i + 1; j < len(nums); j++ {
            if j > i+1 && nums[j] == nums[j-1] {
                continue
            }
            if nums[i]+nums[j] > 0 {
                break
            }
            for z := j + 1; z < len(nums); z++ {

                if nums[i]+nums[j] > nums[z] {
                    break
                }
                if nums[i]+nums[j] == -nums[z] {
                    temp := []int{nums[i], nums[j], nums[z]}
                    result = append(result, temp)
                    break
                }
            }
        }
    }
    return result
}

上面的答案超时了.还是要转成twoSum来解决问题.

func twoSum(nums []int,target int) [][]int {
   // fmt.Println(nums,target)
    result := [][]int{}
    l:=0
    r:=len(nums)-1
    for   {
        if(r<=l){
            break
        }
        if nums[l]+nums[r]<target {
            l++;
            continue;
        }
        if nums[l]+nums[r]>target {
            r--;
            continue;
        }
        if nums[l]+nums[r]==target {
            if l!=0&&r!=len(nums)-1&&nums[l]==nums[l-1]&&nums[r]==nums[r+1] {
                l++;
                r--;
                continue
            }
            // fmt.Println(nums[l],nums[r],l,r)
            temp:=[]int{nums[l],nums[r]}
            result=append(result,temp)
            l++;
            r--;
            continue;
        }
    }
    return result
}
func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    result := [][]int{}
    for i := 0; i < len(nums); i++ {
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        if nums[i] > 0 {
            break
        }
        res:=twoSum(nums[i+1:],0-nums[i])
         // fmt.Println(res)
        for _,v:=range res{
            result=append(result,append(v, nums[i]))
        }
    }
    return result
}

PWA

Service Worker

生命周期

先来看一下一个service worker的运行周期

1. 安装install
2. 激活activate
3. 监听fetch和message事件

默认范围:

navigator.serviceWorker.register('/assets/sw.js').then(function (registration){
    console.log('service worker 注册成功', registration.scope);
}).catch(function (err) {
    console.log('servcie worker 注册失败')
});
// 默认scope是/assets

修改默认范围:

navigator.serviceWorker.register("/assets/sw.js", { scope: "/" })要设置scope='/'response返回sw.js要带上Service-Worker-Allowed='/'的头。这样才能注册成功

navigator.serviceWorker.register("/assets/sw.js", { scope: "/" }).catch(() => {
  console.log('servcie worker 注册失败')
});

不在范围内的请求,fetch拦截不到:

如scope=’/assets’,请求/index.js,fetch是拦截不到的。因此,这些请求不能通过fetch拦截来缓存。可以用cache.addAll()添加缓存。

fetch事件

在页面发起http请求时,service worker可以通过fetch事件拦截请求,并且给出自己的响应。

提供了Request、Response对象,如果做过后端开发,对Request、Response应该比较熟悉。前端要发起请求可以通过url发起,也可以使用Request对象发起,而且Request可以复用。但是Response用在哪里呢?在service worker出现之前,前端确实不会自己给自己发消息,但是有了service worker,就可以在拦截请求之后根据需要发回自己的响应,对页面而言,这个普通的请求结果并没有区别,这是Response的一处应用。而且权限很大,可以拦截chrome插件的网络请求(可以以此推断你装了什么插件)。对于我们开发,缓存插件的请求是没什么意义的,一般都过滤掉。

var hostReg = /(localhost|o8ci6tgz8.qnssl.com)/;

self.addEventListener('fetch', function (evt) {
  console.log(evt.request.url);
  evt.respondWith(
    caches.match(evt.request).then(function (response) {
      if (response) {
        return response;
      }
      var request = evt.request.clone();
      return fetch(request).then(function (response) {
        if (response && response.status === 200 && response.url.match(hostReg)) {
          var responseClone = response.clone();
          caches.open(VERSION).then(function (cache) {
            // console.log(evt.request.url);
            cache.put(evt.request, responseClone);
          });
        }
        return response;
      });
    })
  )
});

message事件

页面和serviceWorker之间可以通过posetMessage()方法发送消息,发送的消息可以通过message事件接收到。

这是一个双向的过程,页面可以发消息给service worker,service worker也可以发送消息给页面,由于这个特性,可以将service worker作为中间纽带,使得一个域名或者子域名下的多个页面可以自由通信。

离线缓存和CDN

现在,我们的网站一般都是单页应用。一般html放在服务器,其他资源文件放CDN。和平时不同,不要使用默认scope,而是设置CDN为scope。一般其他资源文件太多,全部列出来困难且不易维护,使用fetch拦截,来缓存。然后,只需指定缓存[‘/‘]。

安全

serviceWorker权限是太大的,因此我们要保证页面没有XSS漏洞。一旦注册了别人的serviceWorker,后果可想而知。不过,现在的框架都能帮我们处理XSS问题。

leetcode --- String to Integer (atoi)

1.需要考虑数字、符号和空格的情况。因为数字前面是可以有空格的。

2.非上面的直接返回0.

3.考虑边界问题32位有符号。 2147483647 ~ -2147483648

var myAtoi = function(str) {
    var n = str.length;
    var sign = 1;
    var num = 0;
    var isBegin = false;
    var end = 0;
    for(var i=0;i<n;i++){
        var c = str[i];
        if(c===' '){
            if(isBegin){
                break;
            }
        } else if(c>='0'&&c<='9'){
            var end = c - '0';
            num = num  * 10 + end;

            isBegin = true;
        } else {

            if((c==='+'||c==='-')&&!isBegin){
                sign = c === '-' ? -1 : 1;
                isBegin = true;

            } else {
                break;
            }
        }
    }
    var result = sign * num
    if(result>2147483647){
        result=2147483647;
    }
    if(result<-2147483648){
        result=-2147483648;
    }
    return result;
};

leetcode--Non-negative Integers without Consecutive Ones

这道题给了我们一个数字,让我们求不大于这个数字的所有数字中,其二进制的表示形式中没有连续1的个数。

这里可以先做个简单的题目,长度为n的二进制的表示形式中没有连续1的个数。

输入:N = 1
输出:2
// 3个字符串是0,1  (1+1)

输入:N = 2
输出:3
// 3个字符串是00,01,10 (2+1)

输入:N = 3
输出:5
// 5个字符串是000,001,010,100,101 (3+2)

输入:N = 4
输出:8
// 5个字符串是0000,0001,0010,0100,0101,1000,1001,1010 (5+3)

这个问题可以用动态规划来解决。假设a [i]是长度为i的二进制串的数目,它不包含任何两个连续的1并且以0结尾。同样,让b [i]是以1结尾的这样的串的数目。我们可以追加0或1到以0结尾的字符串,但是我们只能将0附加到以1结尾的字符串。这产生递归关系:

如当k=5时,二进制数的范围是00000-11111,我们可以将其分为两个部分,00000-01111和10000-10111,因为任何大于11000的数字都是不成立的,因为有开头已经有了两个连续1。而我们发现其实00000-01111就是f(4),而10000-10111就是f(3),所以f(5) = f(4) + f(3),这就是一个斐波那契数列啦.

var findIntegers = function(num) {
    let numStr=Array.from(num.toString(2)).reverse().join('');
    let n=numStr.length;
    let a=[1];
    let b=[1];
    for(var i=1;i<n;i++){
        a[i]=a[i-1]+b[i-1];
        b[i]=a[i-1];
    }
    var res=a[n-1]+b[n-1];
    for(var i=n-2;i>=0;i--){
        if(numStr[i]==='1'&&numStr[i+1]==='1') break;
        if(numStr[i]==='0'&&numStr[i+1]==='0') res-=b[i];
    }
    return res;
};
  • if(numStr[i]==='1'&&numStr[i+1]==='1') break;因为11xxxx内等于111111内,没有连续1的个数是一样的。
  • ‘00’减去fn(i)的个数。100xxx不能大于101111,去到1111内没有连续1的个数即可。