題目原型
要求:
在無限的整數序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …中找到第 n 個數字。
注意:
n 是正數且在32爲整形範圍內 ( n < 2的31次方)。
輸入:11
輸出:0
說明:
第11個數字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, … 裏是0,它是10的一部分。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/nth-digit
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
初級想法
最簡單的方法,一個數一個數的數。
int GetNNum(int n)
{
int sum = 0;
int i = 1;
int tmp = 0;
int j = 0;
// 當前對輸入的要求並不嚴格,輸入過大,會導致數字越界,程序陷入死循環
for (; i < 0x0fffffff; i = i*10; j++)
{
int flag = 1;
int index = 0;
// 獲取序列中某個數中包含多少個數字
while (i/flag >= 1)
{
flag *= 10;
index++;
}
tmp = sum;
sum += index;
if (sum >= n)
break;
}
// 獲取輸出在目標數字中的第幾位
int n_num = n - tmp;
char buf[256] = {0};
sprintf(buf, "%d", i);
return(buf[n_num - 1] - '0');
}
優化代碼
本問題的關鍵在於尋找序列中的目標數字,有了大體思路就可以對代碼進行優化,發現中間過程一個數一個數的去找目標數字的工作是沒有必要的,可以定位目標數字所在區間在進行,用目標數字在區間內的相對位置與區間底數的和獲取目標數字。前一版本雖然效率低下且問題巨大,但可以作爲後續版本測試的模版,往往越笨的方法得出的結果越正確,只是效率有待提升。
int findNthDigit(int n) {
long long sum = 0;
long long i = 1;
long long tmp = 0;
double j = 1;
//找到數字的範圍區間
for (; ; i = i*10, j++)
{
tmp = sum;
// 以求和的方式往往容易造成越界
sum += (long long)((pow(10, j) - pow(10, j-1)) * j);
if (sum >= (long long)n || (i * 10) == 0)
break;
}
//獲取到目標數字
int k = (n - tmp)/j;
if ((long long)(n - tmp)%(long long)j != 0)
k++;
/*while(1)
{ //捨棄運行超時方案,優化效率90%,已經達到精就已滿足,不是自己想不到,是對自己的要求不夠高
if (tmp + j * k >=n)
break;
k++;
}*/
// 獲取輸出在目標數字的相對位置
int n_num = j - ((tmp + j*k) - n);
char buf[256] = {0};
// 將數字轉字符串,以數組的形式獲取輸出
sprintf(buf, "%d", i+k-1);
printf("%d\n", buf[n_num - 1] - '0');
return(buf[n_num - 1] - '0');
}
模版
int findNthDigit(int n) {
int i =1;
// 循環條件獲取區間
while(n>i*(pow(10,i-1))*9)
{
// 1- 9是i =1的區間,10-99是i=2的區間,以此類推
// 減去區間外的數字,以獲取正確區間,所有變量都小於n,不會越界
n-=i*(pow(10,i-1))*9;
i++; //標誌位++;
}
// pow(10,i-1)區間基數,(n-1)/i區間相對位置
int am_num=(n-1)/i+pow(10,i-1);
string a=to_string(am_num);
if(n%i==0)return (a[i-1]-'0');
return (a[n%i-1]-'0');
}