算法练习之 564.寻找最近的回文数

题目

给定一个整数 n ,你需要找到与它最近的回文数(不包括自身)。

“最近的”定义为两个整数差的绝对值最小。

示例 1:

输入: “123”
输出: “121”
注意:

n 是由字符串表示的正整数,其长度不超过18。

思路分析

一个数字附近的回文数有很多个,想要寻找最近的一个回文数,我们不可能通过遍历其所有回文数来达到目的。所以这时候就需要给这个数的相近回文数一个范围,查找这个范围内的回文数,然后进行比较获取最近的一个。
数字123,最近的回文数是121;100最近的回文数是99,999最近的回文数1001;通过举例不难发现,一个数的相近的回文数有可能比这个数少一位,有可能比这个数多一位,我们可以根据这个来圈出一个范围[99,1001]。比如123,比123小的回文数有121、111、101、99(99-123),比123大的回文数有131、141、151、161、171、181、191…1001,这么多个,可以比较这些回文数与123的差,和回文数的大小来确定最近的是121。
确定了两个边界后,怎么确定边界内的回文数呢。由于题目要求的是最近的回文数,我们知道当两个数高位相同时,低位越相近,则这两个数就越相近。
1.n不是回文数
比如123,最近的回文数是121。所以我们可以保留n的高位(比如123,保留12*)。但是回文数是一个数左右对称,所以低位就是高位的反序,所以低位可以保留赋值为**1(121)。上面这种情况是当n不是回文数的时候,如果当n是回文数时,上面的方法就不成立(题目要求不能是本身)
2.n是回文数
对于例子222,由于本身已经是一个回文数,所以我们就不需要再对第一位和第三位进行修改,只需要对第二位中间那位进行修改。题目要求最近并且最小的回文数,对于222有两个最近的回文数:232、212,最小的是212,所以其最近回文数为212。但是对于999来说,最近的回文数却是1001(989距离999更远)。
这就出现了两种情况了,一种是对n中间那个数-1,一种是+1。但是我们统一n不是回文数这种情况来看,123->121 就是12 +0 然后再将低位转成对应的高位1成121;222->212 就是22-1 再拼接原本的低位2;999->1001 就是99+1 然后再将低位转成对应的高位1成1001。
所以我们所需要做的操作就是对n的中间那位进行-1、+0、+1操作,然后将低位赋值为高位的逆序。这里举的例子都是n长度为奇数时的,当n为偶数时也是同样的情况,只不过高位取一半,而不是奇数时取了(len(n)+1)/2。

算法代码
/**
思路分析:求一个数的最近回文数,比如给了131,就要返回121.而且返回的回文数位数和n可能还不相同,比如n=100,则返回99。n=99,
返回101.那么就可以确定了最近回文数的一个范围,比如n为三位数123,则其回文数的范围在[99, 1001]之间。所以我们就可以就给出的边界值
和求出的其他回文数进行比较远近和大小。
	边界值是两种情况,身下的其他的回文数我们可以通过数字n本身求出来。我们知道回文数是一个数左右相互对称,比如12344321。为了将n改成
回文数,我们可以改变高位(低位不变),使得高位和低位相同;或者改变低位(高位不变),使得低位和高位相同。但是这里我们需要求最近的回文数,
所以我们只需要高位值不变改变低位(因为改变高位,会使得值变动的更大)。
	对于回文数 12534,为了求最近的回文数,我们可以先将数字切割成左右两块(左边的包含中心那个数字):left=124,right=34。
此时我们需要将n区分为是回文数,和不是回文数两种。如果n是回文数,我们需要改变n的中间那个值。这样我们只需要对中心那个数字+1 -1操作(left-1 = 125,left+1 = 123),
然后right反转left即可(这里要区分n长度是奇偶,奇数时不需要left最后一个,偶数时全部反转即可)。
	当n不是回文数,我们只需要使得right等于left的逆序即可(这里同样需要考虑奇偶)。
所以总结来说,我们只需要对left做-1 +0 +1 的操作(其中-1 +1是n为回文数时),就可以求出最近回文数的一个集合,然后再与边界值相比较即可
*/
func nearestPalindromic(n string) string {
	lenN := int64(len(n))                    //n的长度
	nNum, err := strconv.ParseInt(n, 10, 64) //将n转换成int64
	if err != nil {
		return "-1"
	}

	//直接判断1001 和99 类似的值谁距离n最近
	min := int64(0)                                                         //用于存储当前最小回文数与n的差值
	minPa := int64(0)                                                       //当前最小回文数
	num101 := int64(math.Abs(math.Pow10(int(lenN)) + 1.0 - float64(nNum)))  //求类似101 的回文数与n的差的绝对值
	num99 := int64(math.Abs(math.Pow10(int(lenN-1)) - 1.0 - float64(nNum))) //求类似99 的回文数与n的差的绝对值
	if int64(math.Pow10(int(lenN)+1)) < 0 {                                 //如果类型101的回文数超出int64上限,则直接将当前最近回文数设置为类似99
		min = int64(num99)
		minPa = int64(math.Pow10(int(lenN) - 1))
	} else {
		//判断类似101和99 的回文数哪个距离n最近
		if num101 > num99 {
			min = int64(num99)
			minPa = int64(math.Pow10(int(lenN-1)) - 1)
		} else {
			min = int64(num101)
			minPa = int64(math.Pow10(int(lenN)) + 1)
		}
	}
	leftNum, _ := strconv.ParseInt(n[:(lenN+1)/2], 10, 64) //获取左半部的值(1234 left=12,12345 left = 123)
	for i := -1; i <= 1; i++ {
		leftStr := strconv.FormatInt(leftNum+int64(i), 10)     //左半部字符串
		rightStr := ""                                         //右半部分字符串,如果lenN为奇数,则最中间那个值不需要
		for j := int64(len(leftStr)-1) - lenN%2; j >= 0; j-- { //反向遍历leftStr获取右半部
			rightStr += string(leftStr[j])
		}
		str := leftStr + rightStr //回文数字符串
		if n == str {             //移除是自己的情况
			continue
		}
		num, _ := strconv.ParseInt(str, 10, 64)
		//计算距离n最小的回文数
		mu := int64(math.Abs(float64(num - nNum)))
		if mu < min {
			min = mu
			minPa = num
		} else if mu == min && num < minPa {
			minPa = num
		}

	}
	return strconv.FormatInt(minPa, 10)
}

使用测试用例n=955166082267348992,进行go基准测试得到的结果为:执行了1000000次,每次消耗时间平均为:1300 ns/op。并且在领扣代码提交中耗时0ms,击败了100%的提交的go语言代码。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值