从1到N整数中1出现的次数--我的代码
来源:互联网 发布:大数据怎么算平均年龄 编辑:程序博客网 时间:2024/06/01 20:55
问题: 输入一个整数n,求从1 到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,1一共出现了5次。
这是《剑指offer 名企面试官精讲典型编程题》一书中的第32题。书中Herry给出了递归算法。我自己写的是循环算法。虽然有着不同写法,但大体上思路是一致的。我的算法并不比Herry的强。写下来只是提供另外的一个解法。
分析:要得到一个整数中出现的1的次数,其实就是算出1在个位,十位,百位等等中出现的次数总和。在个位上,1在0至9中出现的频率只是1次,其它023456789都和1无关。但是整个个位的循环次数是除去个位的整数值加1(或不加)。如,23, 个位的1出现的次数是3 (23去掉3再加1),即01,11,21;123个位的1出现次数是13(123去掉3加1),即01,11,21,31,41,51,61,71,81,91,101,111,121;以此类推。
再来看十位, 1在0至9中出现的频率也只是1次,但是和个位不同的是,十位中的1却要因为个位的原因至多循环10次(或不到10次),如10,11,12,13,14,15,16,17,18,19,所以整个十位的循环次数是除去个位和十位的整数值乘以10(或不乘)。
百位是也是如此,百位上的1要为十位和个位循环至多10*10次。
由此,我们就可以得出了算法。
时间复杂性是O(n)
private int GetOneOccurence( int piNum ) { String lsNum = piNum.ToString(); int liResult = 0; // calculate result from first digit to last one for ( int i = lsNum.Length - 1; i >= 0; i-- ) { liResult += GetOneOccurence( lsNum, i ); } return liResult; } private int GetOneOccurence( String psNum, int piLoopPosition ) { String lsSuffix; int liResult = 0; // current: digit in current position that is specified by piPos // Suffix: get following digits from current digit // Prefix: get digits before current digit // e.g. 456791, current number is 6, the lsSuffix is 791, prefix is 45. lsSuffix = psNum.Substring( piLoopPosition + 1 ); int liCurNum = int.Parse( psNum.Substring( piLoopPosition, 1 ) ) ; int liPrefix = ( piLoopPosition == 0 ? 0 : int.Parse( psNum.Substring( 0, piLoopPosition ) ) ); int liSuffix = ( String.IsNullOrEmpty( lsSuffix ) ? 0 : int.Parse( lsSuffix ) ); // Get power suffix of current number // e.g: 1,234 , power of suffix is 100 when current number is 2 int liSufPow = (int)Math.Pow( 10, lsSuffix.Length ); // if current number is great than 1, then 1 occurence in this position is ( prefix + 1 ) * power of suffix // e.g: 12345, the current number is 3 now. then 1 occurence in third position is ( 12 + 1 ) * 100 // that is: 01100 to 12100, plus 100 to 199 // if current number is equal to 1, then occurence is prefix * power of suffice + suffix + 1 // e.g: 21345, current number is 1. the value is 2 * 1000 + 345 + 1 // that is: 1000 to 1345, plus 11000 to 21000 // the last situation is that current number is equal to 0. So, occurence is simple to prefix * power of suffix // e.g: 12045. the result should be 12 * 100 // that is 00100 - 00199, 01100 - 01199, 02100 - 02199,... ,11100 - 11199. if ( liCurNum > 1 ) { liResult = ( liPrefix + 1 ) * liSufPow; } else if ( liCurNum == 1 ) { liResult = liPrefix * liSufPow + ( liSuffix + 1 ); } else { liResult = liPrefix * liSufPow; } return liResult; }
上述的算法基本上体现了分析思路, LeetCode有高手给出的最简化的程序。
算法中的循环是为了得到数字的位数,既个位,十位,百位,千位等等,那么我们可以用i * 10来替代。
For( long 1 = 1; i <= piNum; I *= 10 )
每次用 piNum / i 就能得到当前位数之前的数字,再 % 10 得到当前位数。(这里用的是long,不是int。i会超过最大的int值。)
这道题唯一要注意的是当前位数的数字大于1,等于1,还是小于1。用上面的23例子来说,个位的1出现的次数是3 (23去掉3得到2再2+1=3),既01,11,21。如果是20的话,就不能用20去掉0再2+1=3了,个位上的1不会出现21这个数字的,所以只能是01,11。LeetCode上用+8来解决这个问题, 既(23 + 8 )/10, (20 +8)/10 。为什么不用+9呢,因为当前数字如果是1的话要单独处理。
如果我们要处理213的十位,当前位数的数字是1。那么十位上的1不是(2+1)*10,这样就把214到219一并算进去了。只能先得到2*10,在把(2)10 至(2)13十位上出现的1加上去。得到2*10的计算公式是(21 + 8 )/10 * 10,如果用+9而不是+8就不对了。所以公式是(21 + 8 ) / 10 * 10 + ( 214 % 10 + 1 )。(别忘了210,所以最后要再加个1)。
public class Solution { public int CountDigitOne(int n) { long lintResult = 0; for( long i = 1; i <= n; i *= 10 ) { lintResult += ( n / i + 8 ) / 10 * i + ( ( n / i ) % 10 == 1 ? ( n % i + 1 ) : 0 ) ; } return (int)lintResult; }}
- 从1到N整数中1出现的次数--我的代码
- 整数中1出现的次数(从1到n整数中1出现的次数)
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到n整数中1出现的次数
- 从1到 n整数中1出现的次数
- Ubuntu下搭建嵌入式环境
- 《Programming Interviews Exposed》 中的一道递归题:Telephone number to words
- 分片(Sharding)的全局ID生成
- LeetCode - Pow
- 排序算法粗略总结
- 从1到N整数中1出现的次数--我的代码
- 继承
- VS2012如何通过ODBC连接到
- 继承中的构造与析构
- 【C++】切换灯的状态
- XML 命名空间(XML Namespaces)
- 【C++】判断是否为三角形
- 【C++】斐波那契数列
- LeetCode - Symmetric Tree