求數字序列中的第n位對應的數字

文章目錄

  • 題目
  • 思路
  • 代碼
  • 復雜度分析
  • 致謝


題目

數字以0123456789101112131415…的格式序列化到一個字符序列中。在這個序列中,第5位(從下標0開始計數)是5,第13位是1,第19位是4,等等。

請寫一個函數,求任意第n位對應的數字。


思路

我們注意到,如果:

  1. 將 01234567891? 中的每一位稱為 ,記為 n
  2. 將 10,11,12,? 稱為 數字 ,記為 num
  3. 數字 10 是一個兩位數,稱此數字的 位數2 ,記為 digit
  4. digit 位數的起始數字(即:1,10,100,?),記為 start
  5. 個位數共有 1 ~ 9 九個數字,稱個位數的 數字數量9 ,記為 num_count
  6. 十位數共有 10~99 90個數字,每個數字有兩位,共占序列化 90*2=180 位,稱十位數的 數位數量 為 180 , 記為 count

可以得到下表(個位數不計入0):

數字范圍位數數字數量共占序列化多少位
1~9199
10~99290180
100~99939002700
……………………
start~enddigit9*start9 * start * digit

可以得到三個公式:

  1. 位數遞推公式 digit = digit + 1
  2. 起始數字遞推公式 start= start * 10
  3. 數字數量計算公式 num_count = 9 * start
  4. 數位數量計算公式 count = 9 * start * digit

我們可以將求 第n位對應的數字 求解過程分為以下幾步:

  1. 確定 n 所在 數字位數digit
  2. 確定 n 所在的 數字num
  3. 確定 nnum 中的哪一位。

第一步:

在幾位數的范圍內?

while(n>count){n -= count;start *= 10;digit++;count = 9*start*digit;
}

第二步:

是哪個數字?

int num = start+(n-1)/digit;

為什么是 (n-1)/digit 而不是 n/digit

首先看一張圖:
在這里插入圖片描述

以兩位數為例,當執行完 n=n-9 之后,n 與各個兩位數數位的對應關系如上圖所示。以13為例,當我們知道 n=7(13中的1) 時,由 n/2=3 可得 n 是十位數起始數字之后的第三個,但是當 n=8(13中的3) 時,由 n/2=4 得到 n 對應的是十位數起始數字之后的第四個,這是錯誤的,十位數起始數字之后的第四個是 14 而非 13 ,因此計算時需要將 n 進行 減一操作,因為我們將起始數字看作 第0個數 而非 第1個數

為什么是 (n-1)/digit 而不是 n/digit-1

仍然以兩位數為例,當 n 對應非起始數字時,兩種算法是沒有區別的,但是當 n 對應起始數字時,不論 n=0 or n=1(n-1)/digit 的計算結果都為 0 ,而 n/digit-1 的計算結果都為 -1(n-1)/digit 對應的數字 num = start + 0 = 10 ,正確;而 n/digit-1 對應的數字 num = start + (-1) = 9 ,變成了個位數,錯誤。

總而言之,對 n 進行 減一操作 是因為 起始數字應視為 start + 0,雖然它是兩位數里面的 第一個 數字,但是我們在公式里說的 第幾個該數字相對于起始數字 而言的,因此要統統減一。而之所以不是 先除再減 而是 先減再除 是因為 要考慮邏輯順序對起始數字的影響

第三步:

處于對應數字的哪一位?

num = s[(n-1)%digit] - '0';

(n-1)%digit 計算的便是對應的 數位 ,n減一的原因和第二步中相同,不再贅述。


代碼

class Solution {
public:int findNthDigit(int n) {long long start=1, count=9, digit=1;while(n>count){n -= count;start *= 10;digit++;count = 9*start*digit;}int num = start+(n-1)/digit;//所在數字string s = to_string(num);num = s[(n-1)%digit] - '0';//處于所在num的哪一位return num;}
};

復雜度分析

時間復雜度 O(logn) : 所求數位 n 對應數字 num 的位數 digit 最大為 O(log n) ;第一步最多循環 O(log n) 次;第三步中將 num 轉化為字符串使用 O(log n) 時間;因此總體為 O(log n)

空間復雜度 O(logn) : 將數字 num 轉化為字符串 str(num) ,占用 O(logn) 的額外空間。


致謝

思路中的部分想法、復雜度分析源于K神的題解,鞠躬致謝。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443787.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443787.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443787.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一學就廢的歸并排序

文章目錄其他與排序有關的文章原理代碼實現復雜度分析其他與排序有關的文章 一學就廢的三種簡單排序【冒泡、插入、選擇】 原理 歸并排序(Merge sort): 歸并排序對元素 遞歸地 進行 逐層折半分組,然后從最小分組開始&#xff0c…

神奇的x -x,Lowbit函數的實現方式!

文章目錄-xx & -x,當x為偶數時x & -x,當x為奇數時x&-x 的實際用途-x -x 在二進制里表示對 x 的二進制按位取反(~x)之后再加 1 ,即 -x ~x1x & -x,當x為偶數時 在執行 x & -x 時,若 x 為偶數&am…

JAVA實現把指定文件夾下的所有文件壓縮成zip包

1.代碼如下: package cn.gov.csrc.base.util;import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import…

樹狀數組的相關知識 及 求逆序對的運用

文章目錄樹狀數組概念前綴和和區間和樹狀數組原理區間和——單點更新前綴和——區間查詢完整代碼離散化sort函數unique函數去重erase函數僅保留不重復元素通過樹狀數組求逆序對樹狀數組概念 樹狀數組又名二叉索引樹,其查詢與插入的復雜度都為 O(logN),其…

二叉搜索樹相關知識及應用操作

文章目錄概念查找二叉搜索樹的第k大節點概念 二叉查找樹(Binary Search Tree),(又名:二叉搜索樹,二叉排序樹)——它或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的…

二叉樹相關知識及求深度的代碼實現

文章目錄樹二叉樹滿二叉樹和完全二叉樹二叉樹的性質代碼實現求二叉樹的深度樹 樹是一種非線性的數據結構,它是由n個有限結點組成一個具有層次關系的集合。 樹的相關名詞: 根節點:沒有前驅結點的結點。父節點,子節點&#xff1a…

平衡樹相關知識及如何判斷一棵樹是否平衡

文章目錄概念代碼實現判斷一棵二叉樹是否為平衡樹概念 平衡樹(Balance Tree,BT) 指的是,任意節點的子樹的高度差都小于等于1。 常見的符合平衡樹的有: B樹(多路平衡搜索樹)AVL樹(二叉平衡搜索樹&#xf…

大端小端存儲模式詳解及判斷方法

文章目錄大小端模式的概念兩種模式出現原因兩種模式的優劣大小端的應用情景判斷機器的字節序大小端模式的概念 當我們查看數據在內存中的存儲情況時,我們經常會發現一個很奇怪的現象,什么現象呢? int main() {int i 12;return 0; }數據在內…

Linux 內存管理 | 物理內存、內存碎片、伙伴系統、SLAB分配器

文章目錄物理內存物理內存分配外部碎片內部碎片伙伴系統(buddy system)slab分配器物理內存 在Linux中,內核將物理內存劃分為三個區域。 在解釋DMA內存區域之前解釋一下什么是DMA: DMA(直接存儲器訪問) 使用物理地址訪問內存&am…

Linux 內存管理 | 虛擬內存管理:虛擬內存空間、虛擬內存分配

文章目錄虛擬地址空間用戶空間內核空間用戶空間內存分配malloc內核空間內存分配kmallocvmalloc虛擬地址空間 在早期的計算機中,程序是直接運行在物理內存上的,而直接使用物理內存,通常都會面臨以下幾種問題: 內存缺乏訪問控制&a…

Linux | 編譯原理、gcc的命令參數、自動化構建工具 make/Makefile

文章目錄編譯原理預處理編譯匯編鏈接gcc的常用命令參數make 和 Makefile 的概念make的運行通配符自動化變量偽目標.PHONE:【命令】編譯原理 在解釋 makefile 前,首先解釋一下 .c 文件變成 .exe 文件要經過的四個步驟——預處理、編譯、匯編和鏈接(參考來…

全排列變種:限定 排列的差值范圍 及 排列中的元素個數

文章目錄題目描述思路代碼實現題目描述 詳細描述:字節跳動2019春招研發部分編程題——萬萬沒想到之抓捕孔連順 輸入描述: 第一行包含空格分隔的兩個數字 N和D(1?≤?N?≤?1000000; 1?≤?D?≤?1000000) 第二行包含N個整數(取值區間為…

Linux | 進程概念、進程狀態(僵尸進程、孤兒進程、守護進程)、進程地址空間

文章目錄進程和程序操作系統如何控制和調度程序進程控制塊–PCB子進程進程狀態僵尸進程孤兒進程守護進程(精靈進程)進程地址空間引言頁表進程和程序 程序: 一系列有序的指令集合(就是我們寫的代碼)。進程:…

Linux 進程控制 :進程創建,進程終止,進程等待,程序替換

文章目錄進程創建進程等待程序替換進程終止進程創建 fork函數: 操作系統提供的創建新進程的方法,父進程通過調用 fork函數 創建一個子進程,父子進程代碼共享,數據獨有。 當調用 fork函數 時,通過 寫時拷貝技術 來拷貝…

Linux 內存管理 | 連續分配方式 和 離散分配方式

文章目錄前言連續分配單一連續分配分區式分配固定分區分配動態分區分配可重定位分區分配離散分配分段分頁多級頁表快表(TLB)段頁式Linux前言 Linux 內存管理 | 虛擬內存管理:虛擬內存空間、虛擬內存分配 Linux 內存管理 | 物理內存、內存碎片、伙伴系統、SLAB分配器…

操作系統 | 用戶態和內核態的切換(中斷、系統調用與過程(庫函數)調用)

文章目錄中斷過程調用系統調用過程調用和系統調用的區別中斷 用戶態、內核態之間的切換是怎么實現的? 用戶態→內核態 是通過中斷實現的。并且 中斷是唯一途徑 。核心態→用戶態 的切換是通過執行一個特權指令,將程序狀態字 (PSW) 的標志位設置為 用戶態 。 中斷…

管道實現父子進程的信息傳遞(二)【標準流和其文件描述符、fwrite函數、perror函數】

文章目錄代碼實現標準流 和 標準流文件描述符代碼中用到的函數fwrite()perror()在復習進程間的通信方式時又寫了一遍,和 管道實現父子進程的信息傳遞(一)【fork函數、pipe函數、write/read操作、wait函數】 的區別不是特別大,只是…

JAVA隨機生成文件名:當前年月日時分秒+五位隨機數

代碼如下: package cn.gov.csrc.util;import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random;public class RandomUtil {/*** 生成隨機文件名:當前年月日時分秒五位隨機數* * return*/public static String getRandomFile…

命名管道實現進程的信息傳遞【mkfifo函數、open函數】

文章目錄代碼實現mkfifo函數open函數代碼實現 #include<fcntl.h> // open() #include<sys/wait.h> // wait() #include<sys/types.h> // mkfifo() #include<sys/stat.h> // mkfifo() #include<iostream> #include<unistd.h> // fork()usi…

Linux 進程 | 進程間的通信方式

文章目錄管道匿名管道 pipe命名管道 FIFO共享內存共享內存的使用流程&#xff1a;消息隊列信號量套接字在之前的博客中講過&#xff0c;虛擬空間出現的其中一個目的就是解決 進程沒有獨立性&#xff0c;可能訪問同一塊物理內存 的問題。因為這種獨立性&#xff0c;進程之間無法…