[7].整数反转

题目

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例 1:

输入: 123
输出: 321

示例 2:

输入: -123
输出: -321

示例 3:

输入: 120
输出: 21

注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [ − 2 31 , 2 31 − 1 ] [−2^{31}, 2^{31} − 1] [231,2311], 请根据这个假设,如果反转后整数溢出那么就返回 0 0 0
 


函数原型

C的函数原型:

int reverse( int x ){}

分析:

  • 输入参数是整型数字,如 123
  • 返回值也是整型数字, 如 321
     

边界条件

int reverse( int x ){
	if( x < -2147483648 || x > 2147483647 )
	    return 0;
	// 边界判断,-2147483648是 2的31次方,或者写为 INT_MIN、INT_MAX
}

不过,输入参数x并不需要判断溢出,因为 x 是一个的范围始终在 [ − 2 31 , 2 31 − 1 ] [−2^{31}, 2^{31} − 1] [231,2311]

所以,这个边界判断应该是用在整数反转之后的。

比如 x = 2147483648 时,刚好没溢出,但翻反后 8463847412 显然是溢出了。
 


算法设计:模拟法

我们可以分成三步实现:

  • 整数转字符

  • 使用栈或数组或递归来翻转字符

  • 字符转整数

整数转字符、字符转整数,在 C 语言里很方便。

  • sscanf() 函数,可以把字符转其他任何类型, 如整型;

  • sprintf() 函数,可以其他类型(如整型)转字符;

具体用法:

// 字符转整数
char str[32+1] = "123";
int num = 0;
sscanf(str, "%d", &num);

// 整数转字符
int num = 123;
char str[32+1] = {'\0'};
sprintf(str, "%d", num);  

第一、三步都知道怎么实现了,再用数组翻转字符,那大概就完成了。

实现数组翻转:

// 方案一:用俩个数组翻转的方法,但多了一个空间复杂度n
int i, j;
char res_str[33] = { '\0' };

for (i = len - 1, j = 0; i < -1, j < len; --i, ++j)
	res_str[i] = str[j];

// 方案二:原地翻转,就只需要一个变量而已
int tmp;
for (i = 0; i < len / 2; i++)
{
	tmp = str[i];
	str[i] = str[len - 1 - i];
	str[len - 1 - i] = tmp;
}

思路就是这样,但魔鬼在细节,还有许多东西没考虑进来。

int reverse(int x)
{
	// 第一步,整数转字符
	char str[33] = { '\0' };
	sprintf(str, "%d", x);

	
	// 第二步,用数组翻转字符 -> 先得到字符长度(整数多少位) -> 用数组翻转字符 
	int len = strlen(str);

	int tmp;
	for (int i = 0; i < len / 2; i++)
	{
		tmp = str[i];
		str[i] = str[len - 1 - i];
		str[len - 1 - i] = tmp;
	}

	// 第三步,字符转整数
	int n;
	sscanf(str, "%ld", &n);

	return n;
}

还有许多东西没考虑进来。

比如,负数的问题。

为了处理负数,我们加一个标志位 flag。

  // 如果 x 是负数,那flag = -1
	int flag=0;
	if ( x < 0 )
	    flag = -1;
	    
	x = -x;

又比如,翻转后可能会溢出。

if( n < -2147483648 || n > 2147483647 )
    return 0;

结合在一起:

int reverse(int x)
{
	// 负数处理
	int flag = 0;
	if (x < 0)
		flag = -1;

	// 第一步,整数转字符
	char str[33] = { '\0' };
	sprintf(str, "%d", x);

	// 第二步,数组翻转字符
	int len = strlen(str);

	// 用数组翻转字符
	int tmp;
	for (int i = 0; i < len / 2; i++)
	{
		tmp = str[i];
		str[i] = str[len - 1 - i];
		str[len - 1 - i] = tmp;
	}

	// 第三步,字符转整数
	int n;
	sscanf(str, "%ld", &n);

	if (flag == -1)
		n = -n;

	// 边界判断
	if (n < -2147483648 || n > 2147483647)
		return 0;

	return n;
}

好,自己测试一遍:

int reverse(int x)
{
	// 负数处理
	int flag = 0;
	if (x < 0)
		flag = -1;

	// 第一步,整数转字符
	char str[33] = { '\0' };
	sprintf(str, "%d", x);

	// 第二步,数组翻转字符
	int len = strlen(str);
	// 先得到字符长度(整数多少位)

	// 用数组翻转字符
	int tmp;
	for (int i = 0; i < len / 2; i++)
	{
		tmp = str[i];
		str[i] = str[len - 1 - i];
		str[len - 1 - i] = tmp;
	}

	// 第三步,字符转整数
	int n;
	sscanf(str, "%d", &n);

	if (flag == -1)
		n = -n;

	// 边界判断
	if (n < -2147483648 || n > 2147483647)
		return 0;

	return n;
}

int main()
{
	int n;
	scanf("%d", &n);
	printf("\n%d\n", reverse(n));
} 

上面的测试用例:123、-123、620 输出结果和预期结果一样。

可提交到Leetcode平台上, 1534236469 1534236469 1534236469 这个数字测试通不过。

1534236469 < 2147483647 ( 2 31 − 1 ) 1534236469 < 2147483647(2^{31}-1) 1534236469<2147483647(2311),但翻转后的数 9646324351 > 2147483647 9646324351 > 2147483647 9646324351>2147483647,按理说,我们应该 return 0 的呀。

这里怎么输出的是 1056389759 1056389759 1056389759 呢?

找呀找,问题在 sscanf() 函数这里。

    // 第三步,字符转整数
    int  n;
	sscanf(str, "%d", &n);

第二步是翻转, 1534236469 1534236469 1534236469 变成了 9646324351 9646324351 9646324351, 而 int n 的最大限度是 2147483647 2147483647 2147483647

嗯嗯,找到问题点了。

于是,怎么办呢?

改成长整型即可~

    // 第三步,字符转整数
    long long n;
	sscanf(str, "%ld", &n);

完整代码:

int reverse(int x)
{
	// 负数处理
	int flag = 0;
	if (x < 0)
		flag = -1;

	// 第一步,整数转字符
	char str[33] = { '\0' };
	sprintf(str, "%d", x);

	// 第二步,数组翻转字符
	int len = strlen(str);
	// 先得到字符长度(整数多少位)

	// 用数组翻转字符
	int tmp;
	for (int i = 0; i < len / 2; i++)
	{
		tmp = str[i];
		str[i] = str[len - 1 - i];
		str[len - 1 - i] = tmp;
	}

	// 第三步,字符转整数
	long long n;
	sscanf(str, "%ld", &n);

	if (flag == -1)
		n = -n;

	// 边界判断
	if (n < -2147483648 || n > 2147483647)
		return 0;

	return n;
}

过程模拟的复杂度:

  • 时间复杂度: Θ ( n ) \Theta (n) Θ(n)
  • 空间复杂度: Θ ( n ) \Theta (n) Θ(n)
     

算法设计:求余法

对于有规律的式子,我们就可以用模拟计算机模拟人的推理。

核心在于模拟上面等式右边的过程。

那我们必须找到规律的不变式、结束条件。

要取某数第 i 位,使用取余数和整除即可分离出每一位。

比如 123

  • 1123/100
  • 2123/10%10
  • 3123%10

翻转 123321

  • 3(123%10)*100
  • 2((123/10)%10)*10
  • 1123/100

模拟下来:

要取某数第 i 位,使用取余数和整除即可分离出每一位

而后把第 i 位再不断乘左移(是10进制,所以乘10

并加上第 i+1 位的余数。

int num = 0;
while(x != 0){
   num = num * 10 + x%10;
   x /= 10
}

测试一下吧。

int reverse( int x ){
	int num = 0;
	while(x != 0){
	   num = num * 10 + x%10;
	   x /= 10;
	}
	
	return num;
}

AC(通过),那提交了。

哎呦,忘记了还有溢出的。

if( num < -2147483648 || num > 2147483647 )
      return 0;

完整版:

int reverse( int x ){
	int num = 0;
	while(x != 0){
	   if( num < -2147483648/10 || num > 2147483647/10 )
        return 0;
	   num = num * 10 + x%10;
	   x /= 10;
	}
	
	return num;
}

数学模拟的复杂度:

  • 时间复杂度: Θ ( n ) \Theta (n) Θ(n)
  • 空间复杂度: Θ ( 1 ) \Theta (1) Θ(1)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值