在学习过程中,遇到了两道题目。卡了自己一段时间,通过查阅资料,已经AC。下面贴一下这两道题目。
这道题是计算1~n的所有数字中,1出现的个数。这道题目是剑指offer中比较难的一道题目。首先考虑在计算过程中,有一定规律,但是在具体的coding过程中,总不能加各种if条件句判断吧。所以通过查资料学习了这样一种解决该问题的方法:
例如,n=abcde。首先,将n切分开来存放到数组之中。之后进行具体的逻辑判断。以具体数值c为例,需要考虑他的高位和低位情况,高位即ab的情况,低位即de的情况。
先不考虑c的具体值,先考虑c的高位的一个情况。00~ab-1,因为当考虑的ab时,需要看c的情况,所以先判断00~ab的一个情况,这时候其实可以直接算出来至少100种(00~99)之后需要考虑c的情况,如果c=0,则不考虑,如果c=1,则需要加上de+1(00~de)。如果c>1.则直接加100(00~99)。具体逻辑就是这样
/***
* 设数字为abcde,当前位数为c位,
c位1的个数即为高位个数+低位个数
高位范围为 00 ~ ab-1 :
有 ab*100 个(因为c在百位)
低位分为三种情况:
c = 0 ,有 0 个
c = 1 ,有 de + 1 个
c > 1 , 有 100 个 (因为c在百位)
依次遍历每一位数相加,即为总共1的个数
* @param n
* @return
*/
public static int numberOf1Between1AndN_Solution(int n) {
if(n==0) {
return 0;
}
ArrayList<Integer>list=new ArrayList<>();
while(n!=0){
list.add(n%10);
n=n/10;
}
int size=list.size();
int res=0;
for (int i=size-1;i>=0;i--){
int left=0,right=0,t=1;
//处理第i位之前的数
for (int j=size-1;j>i;j--){
left=left*10+list.get(j);
}
//处理第i位之后的数
for (int j=i-1;j>=0;j--) {
right=right*10+list.get(j);
t=t*10;
}
//00~ab-1的情况
res+=left*t;
if (list.get(i)==1) {
res += right + 1;
}else if (list.get(i)>1){
res+=t;
}
}
return res;
}
第二道题目计算第n位数的具体数值。这道题目数学的味道比较浓厚。需要我们找一找固定的规律。
首先1位数有10中情况,2位数有90中情况,3位数有900种情况。。。
其次,我们需要根据n的具体值,来判断第n位数所在的值是多少位数所组成。所以,需要根据具体的规律算出来。例如,确定好n在3位数中的某一个值的某一位上。需要对ceil(n/3)确定在第几个数字上。之后需要n对i取模,计算在该值的第几位上。
我们需要设置一个初始变量 i代表是几位数的情况,s代表i位数有多少个,base代表i位数的起始值。
需要注意的是,为了便于计算,我们规定起始值从1开始,这样1位数有9种情况,2位数有90种情况,3位数有900中情况。。。便于计算。最终在计算第几个数时,减去1即可。
public int digitAtIndex(int n) {
if (n==0) {
return 0;
}
/***
* i代表是几位数,base代表i位数时的起始值,s代表i位数的个数
*/
long i=1,s=9,base=1;
while (n>i*s){
n= (int) (n-i*s);
i+=1;
base*=10;
s=s*10;
}
/**
* 剩下的n代表第i位后面的情况,number用于计算剩下的n应该在base的什么位置,n/i需要向上取整
* 这里最后减1是因为需要考虑最开始的0.
* r用于计算n具体的位置。
*/
int number= (int) (base+(n+i-1)/i-1);
int r= (int) (n%i==0?i:n%i);
for (int j=0;j<i-r;j++){
number/=10;
}
return number%10;
}