iOS LeetCode ☞ 第 N 为数字

给你一个整数 n ,请你在无限的整数序列 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...] 中找出并返回第 n 位上的数字。

示例 1:

输入:n = 3
输出:3

示例 2:

输入:n = 11
输出:0
解释:第 11 位数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... 里是 0 ,它是 10 的一部分。

提示:

  • 1 <= n <= 231 - 1
  • n 位上的数字是按计数单位(digit)从前往后数的第 n 个数,参见 示例 2

解题思路

为了得到无限整数序列中的第 n 位数字,需要知道第 n 位数字是哪一个整数的第几位。如果知道第 n 位数字所在整数是几位数,就能计算得到第 n 位数字是哪一个整数的第几位。

假设第 n 位数字所在整数是 d 位数,则所有位数不超过 d - 1 的整数的位数之和小于 n,且所有位数不超过 d 的整数的位数之和大于等于 n。由于所有位数不超过 x 的整数的位数之和关于 x 单调递增,因此可以使用二分查找确定上述 d 的值。对于某个 x,如果所有位数不超过 x 的整数的位数之和小于 n,则 d > x,否则 d <= x,以此调整二分查找的上下界。

由于任何整数都至少是一位数,因此 d 的最小值是 1。对于 d 的上界,可以通过找规律的方式确定。

  • 1 位数的范围是 1 到 9,共有 9 个数,所有 1 位数的位数之和是 1 * 9 = 9。
  • 2 位数的取值范围是 10 到 99,共有 90 个数,所有 2 位数的位数之和是 2 * 90 = 180。
  • 3 位数的取值范围是 100 到 999,共有 900 个数,所有 3 位数的位数之和是 3 × 900 = 2700。
  • ……

推广到一般情形,x 位数的范围是 10^x - 1^ 到 10x - 1,共有 10x - 1 - 10^x - 1^ + 1 = 9 * 10^x - 1^ 个数,所有 x 位数的位数之和是 x * 9 * 10^x - 1^ 。

由于 n 的最大值为 231 - 1 ,约为 2 * 109 ,当 x = 9 时,x * 9 * 10^x - 1^ = 8.1 * 109 > 231 - 1 ,因此第 n 位数字所在整数最多是 9 位数,令 d 的上界为 9 即可。

在得到 d 的值之后,可以根据上述规律计算得到所有位数不超过 d - 1 的整数的位数之和,然后得到第 n 位数在所有 d 位数的序列中的下标,为了方便计算,将下标转换成从 0 开始记数。具体而言,用 prevDigits 表示所有位数不超过 d - 1 的整数的位数之和,则第 n 位数在所有 d 位数的序列中的下标是 index = n - prevDigits - 1,index 的最小可能取值是 0。

得到下标 index 之后,可以得到无限整数序列中的第 n 位数字是第 ⌊ index / d ⌋ 个 d 位数的第 index mod d 位,注意编号都从 0 开始。

由于最小的 d 位数是 10^d - 1^ ,因此第 n 位数字所在的整数是 10^d - 1^ +⌊index / d⌋,该整数的右边第 d - (index mod d) - 1 位(计数从 0 开始)即为无限整数序列中的第 n 位数字。

代码

	// 400. 第 N 位数字
    func findNthDigit(_ n: Int) -> Int {
        
        func totalDigits(_ length: Int) -> Int {
            var digits = 0, curLength = 1, curCount = 9
            while curLength <= length {
                digits += curLength * curCount
                curLength += 1
                curCount *= 10
            }
            return digits
        }
        
        var low = 1, high = 9
        while low < high {
            let mid = (high - low) / 2 + low
            if totalDigits(mid) < n {
                low = mid + 1
            } else {
                high = mid
            }
        }
        let d = low
        let prevDigits = totalDigits(d - 1)
        let index = n - prevDigits - 1
        let start = Int(pow(10.0, Double(d - 1)))
        let num = start + index / d
        let digitIndex = index % d
        let digit = (num / Int(pow(10.0, Double(d - digitIndex - 1)))) % 10
        return digit
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值