前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >几道算法题记录

几道算法题记录

作者头像
meteoric
发布2021-11-19 15:52:09
1960
发布2021-11-19 15:52:09
举报
文章被收录于专栏:游戏杂谈游戏杂谈

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

代码语言:javascript
复制
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编码后的值,求原值。

代码语言:javascript
复制
/*
关于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
image
代码语言:javascript
复制
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值

代码语言:javascript
复制
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
image
代码语言:javascript
复制
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)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-11-17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档