算法:计算数字k在0到n中的出现的次数,k可能是0~9的一个值

一道算法题,计算数字k在0到n中的出现的次数,k可能是0~9的一个值

要求:计算数字k在0到n中的出现的次数,k可能是0~9的一个值

(只写原创的小菜鸟一枚)

老规矩,还是直接先上干货

function f (n, k) {
    let digit = String(n).length
    let count = 0, n2 = String(n)
    while (digit > 2) { // while里面处理3位数以上的(就是大于等于100)
        let base = (digit - 1) * 10 ** (digit - 2)
        let firstNum = +n2.substr(-digit, 1)
        if (firstNum === k) {
            count += +n2.substr(-(digit - 1))
            count++
        } else if (firstNum > k) {
            count += 10 ** (digit - 1)
        }
        digit--
        count += firstNum * base
    } // 之后的处理99到0
    if (digit === 2) {
        let firstNum = +n2.substr(-digit, 1)
        if (firstNum > k) {
            count += 10
        } else if (firstNum === k) {
            let lastNum = +n2.substr(-1, 1)
            count += lastNum + 1
        }
        count += firstNum
    }
    if (+n2.substr(-1) >= k) count++
    return count
}

看懂的小伙伴就不用看下面的思路了

先来个最笨拙的办法,也是最简单实用的

function ff (n, k) { // 这么简单的,就不用注释了吧
  let str = ''
  for (let i = 0; i <= n; i++) {
    str += i
  }
  let count = 0
  for(let i = 0; i < str.length; i++) {
    if (+str[i] === k) {
      count++
    }
  }
  return count
}

算法就是发现规律,用上自己的思维总结方法

下面开始摆上自己的思考的过程,先说一声,非常墨迹,非常墨迹,非常墨迹

自己不想每一位数字都遍历,这样太慢了,也上网查了一些的算法,大多是高位数,当前位数,低位数,代码是断了,但是不方便理解。就想着用自己思维思考一下。

(吐槽下,各种论坛,社区各种抄袭,或者照搬算法书上的,骗浏览量,能不能自己写一下自己的思路,代码开源是干嘛的?抄袭代码?是交流思维和逻辑的)

好了,开始说思路

思路是不管高位数,当前位数,低位数,我只看当前这一位数的数字,减少一些复杂度

直接用上面的第二个函数ff来看结果就行了
ff(1, 2)       // 0
ff(10, 2)      // 1
ff(100, 2)     // 20
ff(1000, 2)    // 300
ff(10000, 2)   // 4000
ff(100000, 2)  // 50000
ff(1000000, 2) // 600000
...

本人就发现一个规律

3位数或者3位数以上的规律:
位数用digit来表示 例如10是2位数,那么digit=2; 9999是4位数,那么digit=4
100     是 3位数 ,结果是20          20 = 2乘以(10的1次方) (digit - 1) * Math.pow(10, digit - 2)
1000    是 4位数 ,结果是300        300 = 3乘以(10的2次方) (digit - 1) * Math.pow(10, digit - 2)
10000   是 5位数 ,结果是4000      4000 = 4乘以(10的3次方) (digit - 1) * Math.pow(10, digit - 2)
100000  是 6位数 ,结果是50000    50000 = 5乘以(10的4次方) (digit - 1) * Math.pow(10, digit - 2)
1000000 是 7位数 ,结果是600000  600000 = 6乘以(10的5次方) (digit - 1) * Math.pow(10, digit - 2)

发现以上规律

可以写出  (n - 1) * Math.pow(10, n - 2)

再来几个另外的数字,再找找3位数和3位数以上的数字的规律

ff(100, 2) // 20
ff(200, 2) // 41
ff(300, 2) // 160
ff(400, 2) // 180
ff(500, 2) // 200
ff(600, 2) // 220

不难发现

1.当百位数的数字不等于k时候,结果是百位数上的数字乘以(digit - 1) * Math.pow(10, digit - 2)

2.当百位数的数字等于k时,结果是上一点的结果,然后再加1(因为百位数上的数字是k,所以多出现了一次)

3.当百位数上的数字大于k时,再加上Math.pow(10, digit - 1)(例如,n=300, k = 2,那么200, 201, 202...一直到299,百位数上的数字都是2)
现在写下目前发现的规律

10 ** 2就是Math.pow(10, 2),不要纠结
function f (n, k) {
    let count = 0
    let digit = String(n).length
    let n2 = String(n)                         // 把数字n转为字符串,方便根据下标查找firstNum
    let base = (digit - 1) * 10 ** (digit - 2) // base就是百位数上的数字为1的时候
    let firstNum = +n2.substr(-digit, 1)       // firstNum是百位数上的数字
    if (firstNum === k) {
        count += +n2.substr(-(digit - 1))
        count++
    }
    else if (firstNum > k) count += 10 ** (digit - 1)
    count += firstNum * base
    return count
}

写好了之后我们来试一下

f(100, 2) // 20
f(200, 2) // 41
f(300, 2) // 160
f(400, 2) // 180
f(500, 2) // 200
f(600, 2) // 220

结果和我们想象的一样,完美,接下来就是处理99到0的数字了
剩下的数字不多,我们直接用思维推理

先处理个位数

lastNum是个位数,k是一位数
lastNum 小于 k, 结果是0
lastNum 不小于 结果是1
写下代码
if (lastNum >= k) count++

再处理十位数的数字

(这里用firstNum代替十位数的数字,用b代替个位数的数字)
如果firstNum小于k,例如24和3,满足的数字有13,23                 count += 2 。(3这个数不管,因为放到个位数的逻辑中去处理)
如果firstNum等于k,例如34和3,满足的数字有13,23,30,31,32,33,34  count += 8。 (3这个数不管,因为放到个位数的逻辑中去处理,33是2次)
如果firstNum大约k,例如43和3,满足的数字有13,23,30,31,32,33,34,35,36,37,38,39,43  count += 14。 (3这个数不管,因为放到个位数的逻辑中去处理,33是2次)
写下代码
// n2是数字转成字符串,方便根据下标查找对应位数的数字
let firstNum = +n2.substr(-digit, 1)
let lastNum = +n2.substr(-1, 1)
if (firstNum > k) {
  count += 10
} else if (firstNum === k) {
  count += lastNum + 1
}
count += firstNum

最后把所有的逻辑整合

function f (n, k) {
    let digit = String(n).length
    let count = 0, n2 = String(n)
    while (digit > 2) { // while里面处理3位数以上的(就是大于等于100)
        let base = (digit - 1) * 10 ** (digit - 2)
        let firstNum = +n2.substr(-digit, 1)
        if (firstNum === k) {
            count += +n2.substr(-(digit - 1))
            count++
        } else if (firstNum > k) {
            count += 10 ** (digit - 1)
        }
        digit--
        count += firstNum * base
    } // 之后的处理99到0
    if (digit === 2) {
        let firstNum = +n2.substr(-digit, 1)
        if (firstNum > k) {
            count += 10
        } else if (firstNum === k) {
            let lastNum = +n2.substr(-1, 1)
            count += lastNum + 1
        }
        count += firstNum
    }
    if (+n2.substr(-1) >= k) count++
    return count
}
时间比较
ff(9999999, 7) // 结果是7000000,用时2953ms
f(9999999, 7)  // 结果是7000000,用时3ms
运算时间差了几千倍

降龙十巴掌,打完收工。感谢观看

你可能感兴趣的:(算法:计算数字k在0到n中的出现的次数,k可能是0~9的一个值)