训练题:整数中1出现的次数
题目:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/
总结规律。
//
// 设数字50162,high为高位,cur为当前位,low为低位,digit为cur的数位级别
// 如cur为1的位置,则high=50,low=62,digit=100(cur在百位)
//
// cur在个位
// 个位为1的可取范围:00001 ~ 50161,high = 5016, low = 0, digit = 1, low = digit - 1
// 0000 ~ 5016 = 5017 = high * digit + low + 1 = high * digit = (high + 1) * digit
//
// cur在十位
// 十位为1的可取范围:00010 ~ 50119, high = 501, low = 9, digit = 10, low = digit - 1
// 000 0 ~ 501 9 = 5020 = high * digit + low + 1 = (high + 1) * digit
//
// cur在百位
// 百位为1的可取范围:00100 ~ 50162, high = 50, low = 62, digit = 100
// 00 00 ~ 50 62 = 5063 = high * digit + low + 1
//
// cur在千位
// 千位为1的可取范围:01000 ~ 41999, high = 5, low = 999, digit = 1000,low = digit - 1
// 0 000 ~ 4 999 = 5000 = (high - 1) * digit + low + 1 = high * digit
//
// cur在万位
// 万位为1的可取范围:10000 ~ 19999, high=0, low = 9999, digit = 10000, low = digit - 1
// 0000 ~ 9999 = 10000 = high * digit + low + 1 = (high + 1) * digit
//
// 综上我们总结出规律,将数字拆分成 high cur low三个部分,如50162,high = 50, cur=1, low=62
// 我们令此时的digit = 10,即cur的数位(个、十、百等等)
// cur位为1的数字数量 = high*digit+low+1其实就是high和low拼接成新的数字,这个数字的大小。
// 这是通常情况,有特殊情况:即cur位置上的数是0和1的情况。说白了就是high和low的取值有特殊情况。
//
// cur位置上的数=0时,count = (high-1)*digit+low+1=high*digit,此时low=digit-1。
// cur位置上的数=1时,count = high*digit+low+1
// 其他情况,count = high*digit+low+1=(high+1)*digit,此时low=digit-1。
//
int countDigitOne(int n) {
int count = 0;
int high, low;
char cur;
long digit = 1;
for(int i = n; i; i /= 10, digit *= 10){
high = i / 10;
cur = i % 10;
if(cur == 0) count += high * digit;
else if(cur == 1) {
low = n % digit;
count += high * digit + low + 1;
}
else count += (high + 1) * digit;
}
return count;
}
训练题:数据流的中位数
题目:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof
维护两个堆(大堆和小堆),设为maxHeap和minHeap。两个各保存一半的数字。取根的平均值就是中位数。
当插入第奇数个数字时,插入到小堆中。
- 时间复杂度:
- 插入数字:O(logn)
- 返回中位数:O(1)
- 空间复杂度:O(n) ```cpp
class MedianFinder {
public:
using MaxHeap = priority_queue
public: /* initialize your data structure here. / MedianFinder() { }
void addNum(int num) {
if(maxHeap.size() == minHeap.size()){
// 注意特殊情况,num本来是要插入到minHeap中,但是如果num<maxHeap.top
// 表示num比左半部分的数都要小,那么要把maxHeap的根转移到minHeap中,再把num插入到maxHeap中
// 这样才能保证minHeap的所有元素都不小于maxHeap。
if(!maxHeap.empty() && num < maxHeap.top()) {
minHeap.push(maxHeap.top());
maxHeap.pop();
maxHeap.push(num);
}
else minHeap.push(num);
}
else {
if(num > minHeap.top()){
maxHeap.push(minHeap.top());
minHeap.pop();
minHeap.push(num);
}
else maxHeap.push(num);
}
}
double findMedian() {
if(minHeap.empty()) return 0;
if(maxHeap.size() == minHeap.size()) return (maxHeap.top() + minHeap.top()) / 2.0;
else return minHeap.top();
}
private: MaxHeap maxHeap; MinHeap minHeap; };
<a name="fb2tC"></a>
# 训练题:数字序列中某一位的数
题目:[https://leetcode-cn.com/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof/](https://leetcode-cn.com/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof/)
```cpp
int findNthDigit( int n )
{
if( n < 10 ) { return n; }
char bit = 2; // 第n位所在的数是几位数
long time10 = 10; // time10 = pow(10, bit-1);
// 1位数:0 ~ 9 共有count=10个数
// 2位数:10 ~ 99 共有count=90个数
// 3位数:100 ~ 999 共有count=900个数
// 4位数:1000 ~ 9999 共有count=9000个数
long count = 10;
// 循环过后,n是剩下的不足count个的位数。比如n是600中的6的位置。
// 则n = 600 - 10*2 - 90*3 = 410
// count = 900
// bit = 3, time10 = 100
for(;; ++bit, time10 *= 10){
n -= count;
count = 9 * time10 * bit;
if(n < count) break;
}
// n/bit = 410/3 = 136,即是100开始的第136个数,也就是236。
// n%bit = 410%3 = 2,即是表示在236的第2位的数,即是6。
return to_string(time10 + n/bit)[n % bit] - '0';
}
训练题:数字组合
题目:https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/
方法一:回溯算法(递归)
排列组合问题一定要想到回溯算法。
- 时间复杂度:O(logn)
- 空间复杂度:O(logn),栈空间,log10n
int translateNum(int num)
{
int count = 0, tmp = 0;
fuck(num, count, tmp);
return count;
}
// 从右往左遍历(低位到高位),例如 ......ab,ab是两个数字。
// 组合方法一:取b一位
// 组合方法二:如果ab>=10&&ab<=25,则又可以取ab两位,这又是一种组合方法。
// num=0,表示当前这一个组合方法组合完毕,组合方法数count + 1
void fuck(int num, int& count, int &tmp)
{
if(num == 0){
++count;
return;
}
fuck(num / 10, count, tmp);
tmp = num % 100;
if(tmp <= 25 && tmp >=10 ) fuck(num / 100, count, tmp);
}
方法二:动态规划
1、跳台阶(递归)
- 时间复杂度:O(logn)
- 空间复杂度:O(logn)
和跳台阶同理。
char tmp100 = -1;
int translateNum( int num )
{
if(num < 10) return 1;
tmp100 = num % 100;
if(tmp100 <= 25 && tmp100 >=10 ){
return translateNum(num/10) + translateNum(num/100);
}
else
return translateNum(num/10);
}
2、迭代
- 时间复杂度:O(logn)
- 空间复杂度:O(1)
int translateNum( int num )
{
if( num < 10 ) { return 1; }
// counti: dp[i-1]
// counti_1: dp[i-2]
int counti = 1, counti_1 = 1;
for(char tmp, tmp100; num; num /= 10) {
tmp100 = num % 100;
if( tmp100 >= 10 && tmp100 <= 25 )
{
tmp = counti_1;
counti_1 = counti; // dp[i-2] = dp[i-1]
counti = counti + tmp; // dp[i-1] = dp[i]
}
else counti_1 = counti; // dp[i-2] = dp[i-1]
}
return counti;
}
训练题:1+…+n求和
方法一:条件运算实现递归
- 时间复杂度:O(n)
- 空间复杂度:O(n) ```cpp
// n && fuck(n-1); 实现递归。 int sumNums(int n) { n && (n+= sumNums(n-1)); return n; }
<a name="pFDPD"></a>
## 方法二:位移+加法=乘法
```cpp
int sumNums(int n)
{
return multi(n, n + 1) >> 1;
}
// int multi(int A, int B) {
// int ans = 0;
// for ( ; B; B >>= 1) {
// if (B & 1) {
// ans += A;
// }
// A <<= 1;
// }
// return ans;
// }
int multi(int a, int b){
int result = 0;
loop(a, b, result);
return result;
}
bool loop(int &a, int &b, int& ans){
(b & 1) && (ans += a);
a <<= 1;
return b && loop(a, b >>= 1, ans);
}
技巧总结
位移+加法实现乘法:
int multi(int a, int b){
int result = 0;
for(; b; b >>= 1){
if(b & 1) result += a;
a <<= 1;
}
return result;
}
位移+乘法实现幂:
int pow(int a, int b){
int result = 1;
for(; b; b >>= 1){
if(b & 1) result *= a;
a *= a;
}
return result;
}
逻辑运算实现条件判断:
(b & 1) && (result *= a);
// 等价于
if( b & 1) result *= a;
递归代替循环:
// 不用条件运算符实现递归。
void fuck(int n){
n && fuck(n - 1); // 递归n次。
}
训练题:不用加减乘除做加法
int add(int a, int b) {
// carry = (unsigned int)(a & b) << 1; // carry是a + b的最后进位
// unsigned int是为考虑有符号的情况,右位移不知道怎么运算。
// num = a ^ b; // num是a + b不考虑最后进位的结果
// sum = carry + num; // a + b的最终结果,可以循环迭代,直到carry为0
for(int tmp; tmp;) {
tmp = (unsigned int)(a & b) << 1;
a = a ^ b;
b = tmp;
}
return a;
}
训练题:约瑟夫环
方法一:迭代
总结数学规律。
// 第一轮
// 0 1 2 3 4 0 1 2 3 4
// 最后剩余数字在本轮的索引idx,count为本轮的元素个数(未删除前)
// idx = 3, count = 5 最后剩余数字的索引:(0 + 3) % 5
// 0最后剩余数字(3)在上一轮的索引(下面一个)
// 3为从0开始数,第几个数开始删除,即m的值。
// 5为当前轮的元素个数(删除前)
//
// 综上:最后剩余数字在本轮的索引 = (lastIdx + m) % count
// lastIdx:最后剩余数字在上一轮的索引
//
// 3 4 0 1 3 4 0 1
// idx = 0, count = 4 (1 + 3) % 4
//
// 1 3 4 1 3 4
// idx = 1, count = 3 (1 + 3) % 3
//
// 1 3 1 3
// idx = 1, count = 2 (0 + 3) % 2
//
// 3
// idx = 0, count = 1
int lastRemaining(int n, int m) {
// 最后剩余数字的索引(索引和数值相同)
int targetIdx = 0; // 初始值数值0就是元素个数=1的轮的索引
// len:在本轮的元素个数(未删除前),同时也表示从元素个数=2的轮,往前回推到元素个数=5的轮。
for(int len = 2; len <= n; ++len){
// targetIdx的初始值刚好是len=2轮的lastIdx,即(targetIdx+m)中的targetIdx的值。
targetIdx = (targetIdx + m ) % len;
}
return targetIdx;
}
方法二:递归
// n表示当前轮的元素个数(未删除前),如n=1,表示最后剩下一个数字的那一轮;n = 5,就是第一轮。
// m表示从0开始的第几个数删除。
// 返回最后剩下的数字在当前轮的索引
int lastRemaining(int n, int m) {
// n=1就是一个元素,返回值当然是0了。
if(n == 1) return 0;
最后剩下的数字在当前轮的索引 = (该数字在下一轮的索引 + m) % n
return (lastRemaining(n-1, m) + m) % n;
}
训练题:判断顺子
- big - small < 5,big、small为非王牌
- 无重复牌 ```cpp
bool isStraight(vector
set<int> info;
int big = 0; // 最大的非0牌
int small = 20; // 最小的非0牌
for(char i = 0; i < 5; ++i){
if(nums[i]){
if(info.count(nums[i])) return false; // 有重复的非0牌肯定不是顺子
big = max(big, nums[i]);
small = min(small, nums[i]);
info.insert(nums[i]);
}
}
return big - small < 5; // 最大非0牌和最小牌之间不能超过4
}
<a name="LKmY0"></a>
# 训练题:和为s的连续整数序列
题目:[http://t.cn/A67EmKAI](http://t.cn/A67EmKAI)
<a name="CWEza"></a>
## 方法一:数学求极值
- target为目标值。
- k为连续数列的个数,k为正整数,k>=2。
- n为数列的起始数值。n为正整数,且n>=1。
![](https://cdn.nlark.com/yuque/__latex/e7984c427d7d40494c25e28654b6f320.svg#card=math&code=%5CLarge%20%20%5Cbegin%7Barray%7D%7Bl%7D%0A%5Cbegin%7Barray%7D%7Bl%7D%0Atarget%20%26%3D%20n%20%2B%20%28n%2B1%29%20%2B%20...%20%2B%20%28n%2Bk-1%29%20%5C%5C%0A%26%3D%20k%5Ctimes%20n%20%2B%20%5Cfrac%7Bk%5Ctimes%20%28k-1%29%7D%7B2%7D%20%5C%5C%0A%5Cend%7Barray%7D%20%5C%5C%0A%5CLongrightarrow%20k%5E%7B2%7D%2B%282n-1%29k-2target%3D0%20%5C%5C%0A%5CLongrightarrow%20k%20%3D%20%5Cfrac%7B1-2n%2B%5Csqrt%7B%282n-1%29%5E2%2B8target%7D%7D%7B2%7D%20%5C%5C%0A%0A%5CLongrightarrow%20%E4%BB%A4t%3D2n-1%5Cge1%EF%BC%8Cq%3D8target%5Cge24%EF%BC%8C%E6%9C%89k%20%3D%20%5Cfrac%7B%5Csqrt%7Bt%5E2%2Bq%7D-t%7D%7B2%7D%EF%BC%8Ct%E8%B6%8A%E5%A4%A7%EF%BC%8Ck%E8%B6%8A%E5%B0%8F%EF%BC%8C%5C%5C%E6%97%A0%E9%99%90%E6%8E%A5%E8%BF%910%EF%BC%8Ct%E5%8F%96%E6%9C%80%E5%B0%8F%E5%80%BC%E6%97%B6%EF%BC%8Ck%E6%9C%80%E5%A4%A7%E3%80%82%5C%5C%0A%5CLongrightarrow%20t%3D1%E6%97%B6%EF%BC%8Ck%3D%5Cfrac%7B%5Csqrt%7B8target%2B1%7D-1%7D%7B2%7D%E6%97%B6%EF%BC%8C%E6%95%B0%E5%88%97%E7%9A%84%E9%95%BF%E5%BA%A6%E6%9C%80%E9%95%BF%E4%B8%94%E9%A6%96%E4%BD%8D%E6%95%B0%E5%AD%97%E6%9C%80%E5%B0%8F%E3%80%82%5C%5C%0A%E5%BD%93%E7%84%B6k%E5%BF%85%E9%A1%BB%E8%A6%81%E6%98%AF%E6%95%B4%E6%95%B0%E3%80%82%0A%5Cend%7Barray%7D&height=347&id=T93Aw)
```cpp
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>> ret;
// k为数列的长度。
// 上面推导出了k的极值
for(int k = (sqrt(1 + 8*target) - 1)/2, tmp; k >= 2; --k)
{
// 目标整数序列:n n+1 ... n+k-1,其中n>=1,k>=2,target为数列和
// => k*n + k(k-1)/2 = target
// => k*n = target - k(k-1)/2,令tmp=target - k(k-1)/2
// => k*n = tmp;
// => n = tmp / k,因为n是整数,所以tmp能整数k时,则该k值为合适值
tmp = target - (k*(k-1)>>1);
if(tmp < k) break; // 边界条件,n = tmp / k >= 1 => tmp >=k
if(!(tmp % k)){ // 判断是否能整数
ret.push_back({});
tmp /= k;
for(int m = tmp; m < (tmp + k); ++m) ret.back().push_back(m);
}
}
return ret;
}
方法二:双指针
- left和right为某个序列的首尾索引。
- 初始,left=1,right=2。
- 当这个序列和小于目标值,就将right右移一位,使得序列值增大;反之,则将序列的left右移一位,使序列值减小。
- left必须小于right ```cpp
vector
vector<vector<int>> ret;
int left = 1, right = 2, sum;
for(int left = 1, right = 2, sum; left < right;) {
sum = (left + right)*(right - left + 1)/2;
if(sum == target){
ret.emplace_back();
for(int i = left; i <= right; ++i) ret.back().push_back(i);
}
if(sum < target) ++right;
else ++left;
}
return ret;
}
<a name="UQvRD"></a>
# 训练题:打印1到n(全排列)
题目:[http://t.cn/A6zxsZ55](http://t.cn/A6zxsZ55)
排列组合想到回溯算法。
<a name="a8yub"></a>
## 方法一
递归过程中记录了当前是第几位的数。
```cpp
vector<int> printNumbers( int n )
{
vector<int> ret;
string digitNum = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', };
string tmpNum( n, '\0' );
ret.reserve( pow( 10, n ) );
_printNumbers( ret, digitNum, n, tmpNum, 0 );
return ret;
}
// container:全排列应该就有10的n次方个。
// digitNum:10个阿拉伯数字字符。
// maxBits
void _printNumbers( vector<int> &container, const string &digitNum, const int &maxBits, string &tmpNum, int idx )
{
if( idx == maxBits )
{
int tmp = atoi( tmpNum.c_str() );
if( tmp ) { container.push_back( tmp ); }
return;
}
for( auto &c : digitNum )
{
tmpNum[idx] = c;
_printNumbers( container, digitNum, maxBits, tmpNum, idx + 1 );
}
}
方法二
递归过程不记录第几位的数,而是在string的结尾push_back/pop_back来调整。
vector<int> printNumbers( int n )
{
vector<int> ret;
string digitNum = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', };
string tmp;
bool flag = true;
_printNumbers( ret, digitNum, tmp, n, flag );
return ret;
}
void _printNumbers( vector<int> &container, const string &digitNum, string &tmp, const int &n, bool &flag )
{
if( tmp.size() == n )
{
if( flag ) { flag = false; }
else { container.push_back( atoi( tmp.c_str() ) ); }
return;
}
for( auto &c : digitNum )
{
tmp.push_back( c );
_printNumbers( container, digitNum, tmp, n, flag );
tmp.pop_back();
}
}
训练题:二进制中1的个数
方法一
int hammingWeight(uint32_t n)
{
int shit = 0;
for(; n; n >>= 1) {
if(n & 0x1) ++shit;
}
return shit;
}
方法二
int hammingWeight(uint32_t n) {
int shit = 0;
for(; n; ++shit) n &= (n-1); // 让n最右边的1变为0
return shit;
}
训练题:剪绳子
场景一
最先考虑剪成3,剩下的考虑剪成2。
int cuttingRope(int n) {
if( n <=3 ) { return n - 1; }
int a = n / 3, b = n % 3;
if(b == 0) return pow(3, a); // 刚好可以剪成长度为3的绳子若干
if(b == 1) return pow(3, a - 1) * 4; // 最后一段绳子长度是4,这时候要剪成2、2而不是1、3
return pow(3, a) * 2; // 最后一段绳子长度是5,剪成3、2
}
场景二
int cuttingRope( int n )
{
if( n <= 3 ) { return n - 1; }
int a = n / 3 - 1;
int b = n % 3;
int base = 1000000007;
long result = 1; // 必须声明long型
long contribute = 3; // 必须声明long型
// 快速求幂法
while( a )
{
if( a & 0x1 ) { result = ( result * contribute ) % base; }
contribute = ( contribute * contribute ) % base;
a >>= 1;
}
if( b == 0 ) { return ( 3 * result ) % base; }
if( b == 1 ) { return ( 4 * result ) % base; }
return ( 6 * result ) % base;
}
训练题:幂函数
double myPow( double x, int n )
{
// 为了处理负数次方,我们会考虑去相反数
n = n < 0 ? -n : n; // 值溢出的情况,因为有符号整形的整数与负数可能是不对称的。
}
方法一:递归
double myPow( double x, int n )
{
if( n == 0 || x == 1 || x == 0 ) { return 1; }
return n < 0 ? ( 1 / _myPow( x, n ) ) : _myPow( x, n );
}
double _myPow( double x, int n )
{
if( n == 0 ) { return 1; }
double result = _myPow( x, n / 2 );
return ( ( n & 0x1 == 1 ) ? x : 1 ) * result * result;
}
方法二:迭代
既然可以递归,当然就可以用迭代,时间复杂度一样,但是空间复杂度更少。难点在于对递归过程的逆解析。
double myPow(double x, int n) {
if( x == 0 || x == 1 || n == 0 ) { return 1; }
double ret = 1;
for( int shit = n; shit; shit /= 2) // 不能用位移,因为负数的右移是系统相关。
{
if( shit & 0x1 ) { ret *= x; }
x *= x;
}
return n > 0 ? ret : ( 1 / ret );
}
训练题:大数加法
情形一:非负数之和
方法一:栈
从尾部往前逐位相加,结果保存在栈中。
string solve(string s, string t) {
stack<char> stk;
int carry = 0;
for( int i = s.size() - 1, j = t.size() - 1, tmpNum = 0; i >= 0 || j >= 0; --i, --j )
{
if( j >= 0 && i < 0 )
{
tmpNum = t[j] - '0' + carry;
carry = tmpNum / 10;
stk.push( tmpNum % 10 );
}
else if( i >= 0 && j < 0 )
{
tmpNum = s[i] - '0' + carry;
carry = tmpNum / 10;
stk.push( tmpNum % 10 );
}
else
{
tmpNum = s[i] - '0' + t[j] - '0' + carry;
carry = tmpNum / 10;
stk.push( tmpNum % 10 );
}
}
if( carry ) { stk.push( carry ); }
if( stk.empty() ) { return "0"; }
string ret;
ret.reserve( stk.size() );
for( ; !stk.empty(); stk.pop() ) { ret.append( 1, stk.top() + '0' ); }
return ret;
}
方法二:不用栈
将结果直接保存在更长的那个字符串中,省去了栈空间。
string solve(string s, string t) {
int s_size = s.size();
int t_size = t.size();
int carry = 0;
string* bigStr = &t;
string* smalllStr = &t;
if( s_size > t_size ) { bigStr = &s; }
else { smalllStr = &s; }
for( int bigIdx = std::max( s_size - 1, t_size - 1 ), smallIdx = std::min( s_size - 1, t_size - 1 ), tmpNum = 0;
bigIdx >= 0 || smallIdx >= 0;
--bigIdx, --smallIdx )
{
if( smallIdx < 0 )
{
tmpNum = ( *bigStr )[bigIdx] - '0' + carry;
}
else
{
tmpNum = ( *smalllStr )[smallIdx] - '0' + ( *bigStr )[bigIdx] - '0' + carry;
}
carry = tmpNum / 10;
( *bigStr )[bigIdx] = tmpNum % 10 + '0';
}
if( carry ) { bigStr->insert( 0, 1, '1' ); }
return *bigStr;
}
训练题:整数反转
int reverse( int x )
{
int newNum = 0;
// const int int_max = 2147483647; // 最小的数
// const int int_min = -2147483648; // 最大的数
for( int mod = x % 10; x; x /= 10, mod = x % 10 )
{
if( ( newNum > 214748364 ) || ( newNum == 214748364 && mod > 7 ) ) { return 0; }
if( ( newNum < -214748364 ) || ( newNum <= -214748364 && mod < -8 ) ) { return 0; }
// 这里的*10和 + mod都可能出现溢出,因此在这之前需提前判断。
// 溢出情况1:*10溢出,触发条件:newNum比最数对应位置要大/小。
// 溢出情况2:+mod溢出,触发条件:newNum等于最数对应位置数,且加了mod超出最数个位值。
newNum = newNum * 10 + mod;
}
return newNum;
}
训练题:回文数字
方法一
- 时间复杂度:O(n)
- 空间复杂度:O(1)
// 先遍历出所有数字到一个数组中
// 然后从回文的中心位置向外扩展,依次判断是否为回文。
bool isPalindrome(int x) {
if( x < 0 || ( x != 0 && x % 10 == 0 ) ) { return false; }
if( !( x / 10 ) ) { return true; }
char num[10] = {0};
char flag[2] = { 0, 1 };
char maxIdx = -1;
while( x )
{
++maxIdx;
num[maxIdx] = x % 10;
x /= 10;
}
char left = ( maxIdx >> 1 )+1;
char right = ( maxIdx >> 1 ) + flag[maxIdx & 0x1] - 1;
while( --left >= 0 && ++right <= maxIdx ) if( num[left] != num[right] ) { return false; }
return true;
}
方法二
- 时间复杂度:O(n)
- 空间复杂度:O(1) ```cpp
// rNum为翻转后的数,x为剩余的数 // 当x < rNum时,翻转过半,这个时候我们判断rNum是否等于x即可判断是否会回文。
bool isPalindrome(int x) { if(x < 0) return false; // 负数不是回文数 if(x < 10) return true; // 一位数是回文数 if(!(x % 10)) return false; // 个位为0的数不是回文数
int tmp = 0;
while(tmp < x){
tmp = tmp * 10 + x % 10;
x /= 10;
}
// 情况一:翻转后的数 == 剩余的数,说明是回文,这种情况必须位数是偶数
// 情况二:位数为奇数时,翻转后的数要比剩余的数多1位,而这个位就是回文的中心,应跳过它。
return ( tmp == x ) || ( x == tmp / 10 );
}
```