几道算法题记录

(1)给定一个十进制,求Protocol Buffers的 Varint编码;给定一个16进制的 ZigZag编码,求原码;

const readline = require('readline');
const rl = readline.createInterface({
    input : process.stdin,
    output : process.stdout
})

rl.question('请输入要转换的值:', function(answer) {
    
    if (/^\d+$/.test(answer)) {
        var str = encode(answer);
        console.log(answer + ' 的 varint 二进制是:' + str);       
    } else {
        console.log('input is error. please input again.')
    }   

    rl.close();
});

function encode(num) {
    // 0x80 的二进制表示是 1000 0000, 通过 | 运算最高位补 1 
    var MSB = 0x80;

    // 0x7F的二进制表示是 0111 1111
    var REST = 0x7F;
    var MSBALL = ~REST;

    var out = [];
    var offset = 0;

    while (num & MSBALL) {
        out[offset++] = ((num & 0xFF) | MSB).toString(2);
        num >>>= 7;
    }

    out[offset] = ('00000000' + (num | 0).toString(2)).slice(-8);

    return out;
}

参考链接:https://github.com/chrisdickinson/varint/blob/master/encode.js


给定ZigZag编码后的值,求原值。

/*
关于ZigZag编码:
正数
假设数据类型为byte的正数11,其二进制表示为: 00001011
数据左移一位: 00010110
符号位(正数的符号为0)放到最后一位: 00010110
负数
假设数据类型为byte的负数-11,其二进制在计算机中是用补码表示的,计算过程如下
正数原码: 00001011
反码: 11110100
补码(反码加1): 11110101
 
处理过程:
左移一位: 11101010
符号位放到最后一位: 11101011
除最后一位外全部取反: 00010101
*/

function encode32(n) {
    return (n << 1) ^ (n >> 31);
}

function decode32(n) {
    return (n >>> 1) ^ -(n & 1);
    // return (n >> 1) ^ (~(n & 1) + 1);
}

~(function() {
    var arr = [parseInt(0x268cb43, 16), parseInt(0x7ff, 16), parseInt(0x2b7b, 16), parseInt(0x123a, 16), parseInt(0xf6, 16), parseInt(0x282, 16)];
    // var arr = [parseInt('00010110', 2), parseInt('00010101', 2)];
    // var arr = [parseInt('1000000010000100010001000010001', 2)];

    for (var i = 0, len = arr.length; i < len; i++) {        
        var a = decode32(arr[i]);
        var b = encode32(a);

        console.log(a, b, arr[i])
    }
})();


(2)跳跃游戏 —— 对应 Leetcode 第55题

image

const readline = require('readline');
const rl = readline.createInterface({
    input : process.stdin,
    output : process.stdout
});

rl.question('请输入值:', function(answer) {
    rl.close();

    var str = answer.slice(1, -1);

    if (!/^\d.+\d$/.test(str)) {
        console.log('输入错误');
        return;
    }

    var arr = str.split(',');
    var result = canJump(arr);

    console.log(result ? 'true' : 'false');
});

// https://allenmistake.top/2019/04/20/leetcode55dp/
function canJump(arr) {
    for (var i = 0, len = arr.length; i < len; i++) {
        arr[i] = parseInt(arr[i], 10);
    }

    var lastPos = arr.length -1;
    for (var i = arr.length - 1; i >= 0; i--) {
        if (i + arr[i] >= lastPos) {
            lastPos = i;
        }
    }

    return lastPos == 0;
}


(3)伪随机分布算法(PRD preudo-random distribution)

P(N) = C · N,给定 P 或 C值 求对应的 C/P值

const readline = require('readline');
const rl = readline.createInterface({
    input : process.stdin,
    output : process.stdout
});

rl.question('请输入 P 的值:', function(answer) {
    rl.close();

    if (!/\%$/.test(answer)) {
        console.log('输入错误');
        return ;
    }   

    var result = P2C(answer);
    console.log(result)
});

// http://www.yoonper.com/post.php?id=104
function P2C(P) {
    // 先计算出次数
    P = parseFloat(P) * 0.01;
    
    var low = 0;
    var high = P;

    var last = 0;

    var min = 1 / Math.pow(10, 14);
    while (true) {
        // 二分查找
        var middle = (high + low) / 2;
        var pr = parseFloat(C2P(middle)) * 0.01;

        if (Math.abs(pr - last) <= min) break;
        
        pr > P ? (high = middle) : (low = middle);
        
        last = pr;
    }

    return Math.floor(middle * 100) + '%';
}

function C2P(C) {   
    var expectationP = 0;
    var successP = 0;
    var currentP = 0;

    // C * N >= 100;
    var times = Math.ceil(1 / C);
    for (var n = 1; n <= times; n++) {
        currentP = (1 - successP) * Math.min(1, C * n);
        successP += currentP;
        expectationP += currentP * n;
    }
    
    return Math.round(1 / expectationP * 100) + '%';
}


(4)给定16进制的输入,判断它是否为有效的 UTF-8序列;以及给定一串16进制,判断里面包含几个 Unicode字符

image

const readline = require('readline');
const rl = readline.createInterface({
    input : process.stdin,
    output : process.stdout
});

rl.question('请输入值:', function(answer) {
    rl.close();

    var arr = answer.split(',');
    var binaryArr = [];
    for (var i = 0, len = arr.length; i < len; i++) {
        binaryArr.push(parseInt(arr[i], 16));
    }

    var result = validUTF8(binaryArr);    
    console.log('result : ', result ? '1' : '0');

    var cnt = calcAmount(binaryArr);
    console.log('amount :', cnt);
});

// https://www.cnblogs.com/grandyang/p/5847597.html
// https://leetcode.com/problems/utf-8-validation/discuss/87464/Bit-Manipulation-Java-6ms
function validUTF8(arr) {
    if (arr.length < 1) return false;

    var left = 0;
    for (var i = 0, len = arr.length; i < len; i++) {
        var d = arr[i];

        if (left == 0) {
            if ((d >> 5) == 0b110) left = 1; // 后面跟一个字节  110xxxxx 10xxxxxx
            else if ((d >> 4) == 0b1110) left = 2;  // 后面跟二个字节 1110xxxx 10xxxxxx 10xxxxxx
            else if ((d >> 3) == 0b11110) left = 3; // 后面跟三个字节 11110xxx 10xxxxxx 10xxxxxx
            else if ((d >> 7) == 1) return false;
        } else {
            if ((d >> 6) != 0b10) return false; // 非标识符不是以 10 开头
            
            --left;
        }
    }
    
    return left == 0;
}

function calcAmount(arr) {
    var count = 0;

    for (var i = 0, len = arr.length; i < len; i++) {
        var d = arr[i];

        // 以 0 开头且
        if ((d >> 7) == 0) {
            count++;
            continue;
        }

        if ((d >> 5) == 0b110) {
            // 以110开头
            if ((i + 1 < arr.length) && (arr[i + 1] >> 6) == 0b10) {
                ++count;
                
                i += 1;
            }
        } else if ((d >> 4) == 0b1110) {
            // 以1110开头
            if (
                (i + 2 < arr.length) && 
                (arr[i + 1] >> 6) == 0b10 && 
                (arr[i + 2] >> 6) == 0b10
            ) {
                ++count;
                
                i += 2;
            }
        } else if ((d >> 3) == 0b11110) {
            if (
                (i + 3 < arr.length) && 
                (arr[i + 1] >> 6) == 0b10 && 
                (arr[i + 2] >> 6) == 0b10 && 
                (arr[i + 3] >> 6) == 0b10
            ) {
                ++count;

                i += 3;
            }
        }
    }

    return count;
}



(5)求某个数是否为2的整数次幂

最简单的核心代码 return (n & (n –1)) == 0

2的1次方为2,二进制表示为10

2的2次方为4,二进制表示为100

2的3次方为8,二进制表示为1000

也就是说2的次方,它最高位为1其它位是0,通过-1进行 & 运算,应该正好全部为0



=========================

刷完这些题目,将以前的基础知识再回顾一遍。

<< 左移运算符, <<1 相当于原值乘以2(正数)

>> 右移运算符,>>1 相当于原值除以2(正数)

>>> 无符号右移,忽略符号位


举例说明:

以 6 为位,对应二进制 110,左移一位 1100(对应整数12),右移一位 11(对应整数3)

-6,对应的二进制(32位表示)11111111 11111111 11111111 11111010


正数6的二进制(32位)

00000000 00000000 00000000 00000110

所有位取反

11111111 11111111 11111111 11111001

然后 +1(补码)

11111111 11111111 11111111 11111010


<< 左移,后边始终补0

>>如果是正数前面补0,如果是负数前面补1,-7 >> 1 = –4, -7 >> 2 = –2, –7 >> 3 = –1, -7 >> 4 = –1, –7 >> 5 = –1,最高位始终为1(即负数)

>>>无符号右移,无论正负数,前面都补0,负数无符号右移后变成一个很大的正数。 –7 >>> 1 = 2147483644(二进制表示为 01111111 11111111 11111111 11111100)

posted @ 2021-11-17 16:05  meteoric_cry  阅读(63)  评论(0编辑  收藏  举报