JZ31 整数中1出现的次数(从1到n整数中1出现的次数)
(中等)
题目
描述
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数
例如,1~13中包含1的数字有1、10、11、12、13因此共出现6次
示例
输入:
13
返回值:
6
解题思路与过程
1 找规律:失败的思路
对于此题,我一开始是想找出规律再去做的,如下图
但是,100以内的规律很好找,但是到了100多,200多,…,1000多,…,发现这规律也不是那么容易找的,于是我便打算换一种想法。
2 二进制&运算:失败的思路
因为题目中出现了一句 “ 十进制表示 ”,因此我便想通过转换一下进制,转为二进制的思路来想,但是,这样只能对于个位数是 1 的数是很方便找的,用一个 1 与运算(&)上当前数,即可判断出个位上的 1 ,但是对于十位,百位,更高位,虽然我们可以每次循环将当前数除以10,但是对于像是 3 这样的数,它的二进制最低为是为 1 的,但它的十进制数中却没有 1,因此,将数转为二进制用 & 来判断这种方法是行不通的。
3 暴力解法一:成功
最后,确实没想到比较方便和巧妙的方法,于是还是不得已用了暴力解法,将遍历过程中的每个数,转为字符串后,记录里面共有多少个 1 ,因此也没什么技术含量了,直接看代码就可以了。
【实现】
public class JZ31整数中1出现的次数 {
public int NumberOf1Between1AndN_Solution(int n) {
if (n < 1) {
return 0;
}
int count = 0;
String numStr = null;
char[] numCharArray = null;
for (int i = 1; i <= n; i++) {
numStr = String.valueOf(i);
numCharArray = numStr.toCharArray();
for (char c : numCharArray) {
if (c == '1') {
count++;
}
}
}
return count;
}
}
4 暴力解法二:成功
在写完这种解法后,我便又想到了另一种暴力解法的实现形式,就是用求 10 的余数来判断,但其实也算是暴力解法,不过它的效率决定要高于第一种转字符的暴力解法,这里那我也顺便贴上代码吧。
【实现】
public class JZ31整数中1出现的次数 {
public int NumberOf1Between1AndN_Solution2(int n) {
if (n < 1) {
return 0;
}
int count = 0;
int x = 0;
for (int i = 1; i <= n; i++) {
x = i;
while (x != 0) {
if (x % 10 == 1) {
count++;
}
x /= 10;
}
}
return count;
}
}
可见,效率是高了不少
5 另一种方法
之后看题解时,发现了这样一个方法,确实没怎么看懂,而且它的执行速度,也和我的第二种方法差不多,不过看这位网友的代码,似乎很有研究价值,等以后哪天再刷一遍 剑指Offer 时再看吧。
JZ37 数字在升序数组中出现的次数
(中等)
题目
描述
统计一个数字在升序数组中出现的次数。
示例
输入:
[1,2,3,3,3,3,4,5],3
返回值:
4
思路
题目已经交代的是否清楚了,又是有序数组,又是查找某个数,因此十分自然,直接想到二分查找。
虽然待查找的数值的数量可能不止一个,但都是挨着的,因此我们用二分查出来的元素下标,向前、向后统计个数,再加上本来这二分中间这个,就是待求 k 值在数组中出现的次数。
(补充:关于四种基本查找算法,可参考之前文章:Java实现四种【查找算法】+图解+完整代码+分析)
实现
public class JZ37数字在升序数组中出现的次数 {
public int GetNumberOfK(int [] array , int k) {
if (array == null || array.length == 0) {
return 0;
}
int len = array.length;
int low = 0;
int high = len - 1;
int mid = 0;
while (low <= high) {
mid = (low + high) / 2;
if (k > array[mid]) {
low = mid + 1;
} else if (k < array[mid]) {
high = mid - 1;
} else {
break;
}
}
//判断是否是因为low和high而结束的循环
if (array[mid] != k) {
return 0;
}
//此时 array[mid] = k
int count = 1;
//再去 mid 之前和之后去统计 k 的个数
//--之前
int i = mid - 1;
while (i >= 0 && array[i] == k) {
count++;
i--;
}
//--之后
i = mid + 1;
while (i < len && array[i] == k) {
count++;
i++;
}
return count;
}
}