反转之后不同整数的数目 图解题解
这道题到底在问什么
- 输入
- nums = [1,13,10,12,31]
- 输出
- 6
- 输入
- nums = [2,2,2]
- 输出
- 1
先想最直接的笨办法
把数组 1、13、10、12、31 摆好,下标 0 到 4。右边这块是我们的集合,现在是空的。第一阶段的任务很简单:从左到右把这五个原数一个一个放进集合。(动画第 4 步)
最优解:一步一步想明白
- 3记牢这一句:原数入集合,反转值入集合,集合大小就是答案。集合帮我们自动去重,重复的数插进去它不涨。下面先把五个原数放进集合,再逐个反转。
- 4把数组 1、13、10、12、31 摆好,下标 0 到 4。右边这块是我们的集合,现在是空的。第一阶段的任务很简单:从左到右把这五个原数一个一个放进集合。
- 5指针走到下标 0,取出原数 1。前面标蓝的都是已经收进集合的原数。现在把 1 也送进去。
- 6原数 1 进集合,面板里多出这一行。这五个原数各不相同,所以每放一个集合就涨一个,现在集合里有 1 个数。假如原数里有重复的,集合会自动合并、不会重复计。
- 7指针走到下标 1,取出原数 13。前面标蓝的都是已经收进集合的原数。现在把 13 也送进去。
- 8原数 13 进集合,面板里多出这一行。这五个原数各不相同,所以每放一个集合就涨一个,现在集合里有 2 个数。假如原数里有重复的,集合会自动合并、不会重复计。
- 9指针走到下标 2,取出原数 10。前面标蓝的都是已经收进集合的原数。现在把 10 也送进去。
- 10原数 10 进集合,面板里多出这一行。这五个原数各不相同,所以每放一个集合就涨一个,现在集合里有 3 个数。假如原数里有重复的,集合会自动合并、不会重复计。
- 11指针走到下标 3,取出原数 12。前面标蓝的都是已经收进集合的原数。现在把 12 也送进去。
- 12原数 12 进集合,面板里多出这一行。这五个原数各不相同,所以每放一个集合就涨一个,现在集合里有 4 个数。假如原数里有重复的,集合会自动合并、不会重复计。
- 13指针走到下标 4,取出原数 31。前面标蓝的都是已经收进集合的原数。现在把 31 也送进去。
- 14原数 31 进集合,面板里多出这一行。这五个原数各不相同,所以每放一个集合就涨一个,现在集合里有 5 个数。假如原数里有重复的,集合会自动合并、不会重复计。
- 15第二阶段,逐个反转。轮到下标 0 的 1。反转就是一个小循环:从 0 开始,每次取出当前数的末位,把新数乘 10 再加上这个末位。1 走下来得到 1。
- 16把反转值 1 丢进集合,面板里已经有这一行了,是个重复值,集合不涨,还是 5 个。这一步印证了集合去重的好处:不用自己判断有没有,插进去就行。
- 17第二阶段,逐个反转。轮到下标 1 的 13。反转就是一个小循环:从 0 开始,每次取出当前数的末位,把新数乘 10 再加上这个末位。13 走下来得到 31。
- 18把反转值 31 丢进集合,面板里已经有这一行了,是个重复值,集合不涨,还是 5 个。这一步印证了集合去重的好处:不用自己判断有没有,插进去就行。
- 19第二阶段,逐个反转。轮到下标 2 的 10。反转就是一个小循环:从 0 开始,每次取出当前数的末位,把新数乘 10 再加上这个末位。10 走下来得到 1。注意 10 的末位是 0,反转过程里 0 在最前面,累到新数上等于没加,前导零就这样丢掉了,反转结果是 1。
- 20把反转值 1 丢进集合,面板里已经有这一行了,是个重复值,集合不涨,还是 5 个。这一步印证了集合去重的好处:不用自己判断有没有,插进去就行。
- 21第二阶段,逐个反转。轮到下标 3 的 12。反转就是一个小循环:从 0 开始,每次取出当前数的末位,把新数乘 10 再加上这个末位。12 走下来得到 21。
- 22把反转值 21 丢进集合,面板里之前没有它,这是一个新值,集合多出一行,涨到 6 个。它就是这道题里唯一一个反转后带来的新数字。
- 23第二阶段,逐个反转。轮到下标 4 的 31。反转就是一个小循环:从 0 开始,每次取出当前数的末位,把新数乘 10 再加上这个末位。31 走下来得到 13。
- 24把反转值 13 丢进集合,面板里已经有这一行了,是个重复值,集合不涨,还是 6 个。这一步印证了集合去重的好处:不用自己判断有没有,插进去就行。
- 25全部处理完。原数 1、13、10、12、31 进了集合,反转值里只有 21 是新的,其它 1、31、1、13 早就在集合里。数一数面板,一共 6 个不同的整数,这就是答案。整道题的核心只有一句:原数和反转值都塞进一个集合,看它最后有多大。
⚠️ 容易写错的地方
✗ 错:反转后忘了去掉前导零
✓ 对:用算术反转末位累加,或字符串反转后用整数解析
10 反转的字面是 01,必须当成 1。算术法从 0 起累加,高位的 0 乘进去等于没加,天然去零;字符串法靠转回整数去掉开头的 0
✗ 错:只统计反转值,漏掉原数
✓ 对:原数和反转值都要进集合
题目是把反转值追加到原数组末尾,最终数组同时含原数和反转值,两边都要参与去重计数
✗ 错:用下标或数组长度去重
✓ 对:按数值放进哈希集合去重
不同下标可能是相同数值,去重必须看值不看位置,集合按值判重最省事
✗ 错:担心反转后整数溢出
✓ 对:本题值域内 int 足够,更大值域才用 long
nums 不超过 10 的 6 次方,反转后仍不超过七位数,落在 int 范围内;若题目值域更大,反转前要考虑用更宽的整型
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def countDistinctIntegers(self, nums: List[int]) -> int:
s = set(nums)
for x in nums:
y = int(str(x)[::-1])
s.add(y)
return len(s)C++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
int countDistinctIntegers(vector<int>& nums) {
unordered_set<int> s(nums.begin(), nums.end());
for (int x : nums) {
int y = 0;
while (x) {
y = y * 10 + x % 10;
x /= 10;
}
s.insert(y);
}
return s.size();
}
};Java
import java.util.*;
class Solution {
public int countDistinctIntegers(int[] nums) {
Set<Integer> s = new HashSet<>();
for (int x : nums) {
s.add(x);
}
for (int x : nums) {
int y = 0;
while (x > 0) {
y = y * 10 + x % 10;
x /= 10;
}
s.add(y);
}
return s.size();
}
}复杂度
时间
O(n·L)
n 是数组长度,L 是每个数的位数。原数入集是 O(n);每个数反转要按位处理,约 L 次操作,这里数值不超过 10 的 6 次方,L 至多 7,是个小常数,所以整体近似 O(n) 线性
空间
O(n)
按峰值算。额外开销主要是那个集合,最坏情况原数和反转值互不相同,集合里最多装到 2n 个数,量级是 O(n);反转过程只用几个整数变量,是常数
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 反转之后不同整数的数目 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这题的核心思路一句话怎么说?+
把所有原数和它们的反转值都放进一个哈希集合,集合自动去重,最后集合的大小就是结果数组里不同整数的数目。不需要真的把数组接长。
数位反转具体怎么实现,有几种写法?+
两种常见写法。算术法用一个循环,每次 y 等于 y 乘 10 加上 x 对 10 取余,再把 x 整除 10,把末位一位位搬到新数,前导零天然消失。字符串法把数转成字符串、倒序、再转回整数,整数解析会去掉开头的 0。两种结果一致,算术法不额外分配字符串,稍快一点。
复杂度是多少,瓶颈在哪?+
时间上原数入集是 O(n),每个数反转是位数级的常数功夫,整体近似 O(n) 线性。空间上主要是那个集合,最坏原数和反转值互不相同,装到大约 2n 个,是 O(n)。瓶颈就是这个集合的存储。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 反转之后不同整数的数目 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。