算法学习记录(三十一)

149 阅读2分钟

问:

  1. 有一个函数f
    • 若它的作用是返回1 ~ 5内的随机数。请加工出可以返回1 ~ 7随机数的函数g;
    • 若它的作用是返回a ~ b内的随机数。请加工出可以返回c ~ d随机数的函数g;
    • 若它的作用是以p的概率返回0,1-p的概率返回1。请加工出等概率返回0,1的函数g
  2. 给定一个正整数N,代表二叉树节点个数,请返回它能形成多少种二叉树结构
  3. 假设有一个字符串,只有'('和')',如果括号是闭合的,比如:'()()()','((()))','(()(()))'。如果不闭合,比如:'())()'。问这个字符串最少需要加多少个字符可以使之闭合。

解: 1.

// 第一问
function f() {
    // ...
}
// 首先加工出等概率返回0,1的函数
function getOneOrZero() {
    const num = f()
    if (num === 3) {
        return getOneOrZero()
    }
    if (num < 3) {
        return 0
    }
    if (num > 3) {
        return 1
    }
}
// 结果函数
function g() {
    // 7可以用3个二进制位表示,所以通过随机返回0,1的函数,生成3个0|1。
    // 这是一个0~7的随机数
    const res = (getOneOrZero() << 2) + (getOneOrZero() << 1) + getOneOrZero()
    // 如果随机到7了,重新随
    if (res === 7) {
        return g()
    }
    // 所以res实际上是0~6的随机数
    // 返回res+1就是1~7的随机数
    return res + 1
}

// 第二问
const a = 5,b = 14,c = 13,d=27
function f() {
    // ...
}
// 5  ~  14  5 6 7 8 9 10 11 12 13 14
// 还是加工出随机返回0,1的函数
function getOneOrZero() {
    const num = f()
    // 中间数
    const mid = (a+b) / 2
    // 如果mid是整数,那么随到mid就重来
    if (Number.isInteger(mid) && num === mid) {
        return getOneOrZero()
    }
    if (num <= mid) {
        return 0
    }
    if (num > mid) {
        return 1
    }
}
function g() {
    let needBit = 1
    // 计算需要多少二进制位
    while (2 ** (needBit - 1) < d - c) {
        needBit++
    }
    let res = 0
    while (needBit) {
        const temp = getOneOrZero()
        res += temp << (needBit - 1)
        needBit--
    }
    if (res > d - c){
        return g()
    }
    return res + c
}
// 第三问
// 随两个数,随到0,1和1,0的概率相同,所以这两种情况返回0或者1。其他情况都重新随
function g() {
    const num1 = f()
    const num2 = f()
    if (num1 === 0 && num2 === 1) {
        return 1
    }
    if (num1 === 1 && num2 === 0) {
        return 0
    }
    return g()
}
  1. 从左子树节点数量分析,它的范围是 0 ~ N-1(相对的,右子树节点数量就是 N - 1 ~ 0)。遍历递归求累加和。
// 递归
function getAllTree(N) {
    if (N === 0 || N === 1) return 1
    if (N === 2) return 2
    let res = 0
    // 假设左子树的节点数量从0开始,直到左子树的节点数量变成 N - 1
    for (let leftNodes = 0; leftNodes <= N - 1;leftNodes++) {
        // 左右子树各自可能性的乘积的累加和
        res += getAllTree(leftNodes) * getAllTree(N - 1 - leftNodes)
    }
    return res
}
// 递归改dp
function getAllTreeDP(N) {
    const dp = []
    dp[0] = 1
    for (let i = 1; i <= N;i++) {
        dp[i] = 0
        for (let leftNodes = 0; leftNodes < i;leftNodes++) {
            dp[i] += dp[leftNodes] * dp[i - 1 - leftNodes]
        }
    }
    return dp[N]
}
  1. 定义变量count用于统计多余的'('数量,遍历字符串,若碰到'('就增加一次,碰到')'就减少一次。定义变量res用于统计需要匹配的数量,每当减少到-1时,说明有一个')'没有被匹配上,必须添加一个'('用于匹配。所以res增加。当遍历结束后,可能会有多余的'('。所以返回count+res。
function getMinChangeChar(str) {
    let count = 0
    let res = 0
    for (let i = 0; i < str.length; i++) {
        if (str[i] === '(') {
            count++
        }
        if (str[i] === ')') {
            count--
        }
        if (count === -1) {
            res++
            count--
        }
    }
    res += count
    return res
}