• 几道算法题记录


    (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)

  • 相关阅读:
    [再寄小读者之数学篇](2015-06-24 积分不等式)
    揭秘:三国时能令诸葛亮自叹不如的奇才是谁?
    【大话三国】揭秘蜀汉五虎将的真相
    三国揭秘 诸葛亮为何重用张飞疏远关羽
    [再寄小读者之数学篇](2015-06-08 一个有意思的定积分计算)
    咏史---左思
    非洲雄狮捕猎未遂被野牛群追赶逃到树上
    诚信,聪明,快乐,地位与竞争
    [裴礼文数学分析中的典型问题与方法习题参考解答]4.3.26
    [裴礼文数学分析中的典型问题与方法习题参考解答]4.3.11
  • 原文地址:https://www.cnblogs.com/meteoric_cry/p/15567752.html
Copyright © 2020-2023  润新知