您的位置:首页 > 编程语言 > C语言/C++

[LeetCode刷题笔记]Math数学类型题目(三)特殊的数字结构

2016-07-22 22:11 537 查看
原创文章

转载请注册来源http://blog.csdn.net/tostq

特殊的数字结构这类题一般都是折腾数字本身,特别要注意到观察数字的结构和特征。

常见的出题类型主要分为了回文数字、数字翻转、数字分割等几类。

一、回文数(对称数)

9. Palindrome Number

判断一个数是否是回文数(即数是对称的,如7654567)

这题的难点在于要求不能额外分配空间,也就是说我们不能先通过保存倒置数位,再来进行头尾数位的相互比较。换句话说,题目要求我们要同时比较头尾。

(1)一种可行的方法是同时保存首尾数字

class Solution {
public:
bool isPalindrome(int x) {
if(x<0)return false;
// wei
int w;
int xtemp=x;
for(w=0;xtemp!=0;w++)xtemp=xtemp/10;
//printf("%d",w);

int h=x,t=x;
int wh=w-1,wt=1;
while(h==t&&wh>=0){
t=x%10;
h=x/pow(10,wh);
x=(x-h*pow(10,wh))/10;
wh=wh-2;
//printf("|t=%d,h=%d,x=%d,wh=%d|",t,h,x,wh);
}
return (h==t)?true:false;
}
};


(2)另一种更为巧妙的方法,是先计算数字的倒转数,再比较两个数是否相等

bool isPalindrome(int x) {
if(x<0)return false;
int w;
int xtemp=x;
for(w=0;xtemp!=0;w++)xtemp=xtemp/10;

xtemp=x;
int cmp=0;
while(w--){
cmp=cmp*10+xtemp%10;
xtemp=xtemp/10;
}
return cmp==x;
}


二、数字翻转

7. Reverse Integer

输入:x = 123, 输出:321

输入:x = -123, 输出:-321

这道并不难,前面的回文介绍了数字倒转的方法,然而这道题的正确提交率并不高。

主要原因在于数据溢出,比如-2147483645,其数字倒转后肯定是溢出了,这个时候我们需要返回0,而实际上是不进行处理的话,返回肯定是非0的。

这里我们介绍一种非常简单判断是否溢出的方法,比如判断x*y=z是否溢出,只需要判断z/y==x就可以了。下面是具体的解法:

class Solution {
public:
int reverse(int x) {
int sign=(x>0)?1:-1;
x=(x>0)?x:-x;
int res=0;
while(x>0){
int newres=res*10+x%10;
if((newres-x%10)%10!=0||(newres-x%10)/10!=res)return 0; // good
x=x/10;
res=newres;
}
return sign*res;
}
};


三、高兴数

202. Happy Number

高兴数定义了这样一个过程:将数字的各位数平方相加得到一个新数,然后将这个新数的各位数平方继续相加,如果这个过程最终结果为1,则为高兴数,如果一直循环的话,则不是高兴数。

比如:19 就是一个高兴数

1^2 + 9^2 = 82

8^2 + 2^2 = 68

6^2 + 8^2 = 100

1^2 + 0^2 + 0^2 = 1

(1)通过Hash表记录

一种比较直观的方法,就是记录这个过程,来判断是否出现过循环,

这里我们可以引入前面介绍的Hash表unordered_map

class Solution {
public:
bool isHappy(int n) {
unordered_map<int,bool> m;
while(1){
if(m.find(n)==true)return false;
if(n==1)return true;
m.insert(make_pair(n,true));
int temp=0;
while(n!=0){
temp=temp+(n%10)*(n%10);
n=n/10;
}
n=temp;
}
}
};


(2)Floyd 循环定理

另一种更好的方法,是利用循环定理

Floyd 循环定理表明这样的一个现象:速度不同的两人,在一个环形跑道上同时起跑,那么未来他们两个肯定会在某一时刻相遇。

因此我们有如下的方法。

//Floyd Cycle detection algorithm
int powsum(int n){
int sum=0;
while(n!=0){
sum=sum+(n%10)*(n%10);
n=n/10;
}
return sum;
}
bool isHappy(int n) {
int slow=n,fast=n;
do{
slow=powsum(slow);
fast=powsum(fast);
fast=powsum(fast);
}while(slow!=fast);
if(slow==1)return true;
return false;
}


四、组合序列

60. Permutation Sequence

这道题是给定两个整数n(n=1~9),k,计算n!个组合数中的第k个的值,需要返回的是字符串形式。比如给n=3,k=6,有如下:

"123"

"132"

"213"

"231"

"312"

"321"

……

所以我们返回的是"321"

解题的思路:

1、因为我们已经知道n的大小,所以我们可以提前将一些存储起来。

2、注意序列是从1开始的,而不是从0开始的

3、这里是可以用递归的

具体解法:

class Solution {
private:
int fact[9] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};
public:
string getPermutation(int n, int k) {
if(n==1)return "1";
string s="";
int div=(k-1)/fact[n-1]+1;
int mod=(k-1)%fact[n-1]+1;
string s1=getPermutation(n-1,mod);
for(int i=0;i<s1.length();i++)
if(s1[i]-'0'>=div)s1[i]++;
s+=(char)(div+'0')+s1;
return s;
}
};


五、数字分割

343. Integer Break

这道题是给定一个正整数,将这个正整数分成多个正整数的和,使其分割的这些数相乘最大

比如n = 10, 返回 36 (10 = 3 + 3 + 4).

(1)动态归划来解决

这道题比较直观的方式是通过动态归划来解决,因为我们可以看到,n的结果同n-i的结果有明显的关系,假设n的结果为dp


则dp
=MAX(max(dp[n-i],(n-i))*max(i,dp[i]))

具体的解法:

#define max(x,y) (x>y?x:y)
int integerBreak(int n) {
if(n<4)return n-1;
int* dp=(int*)malloc((n+1)*sizeof(int));
dp[0]=0,dp[1]=0;
dp[2]=1; //n=2
dp[3]=2; //n=3

for(int i=4;i<=n;i++){ // from 4
int maxpro=0;
for(int j=i-1;j>1;j--){
maxpro=max(maxpro,max(j,dp[j])*max(i-j,dp[i-j]));
}
dp[i]=maxpro;
}
return dp
;
}


(2)另外,我们可以观察数的本身特征

我们可以直观地认为如果把一个数分解成越来越多的份,其相乘应该是越来越大的

考虑到n=a+b,a+b<ab,仅当a或b等于1,或a=b=2时,不成立。

所以当我们把数分成多个3相加的形式,最后乘应该是最大的。

但是还需要考虑最后的余数。

如果余数为1,最后应该写成3*4,而不是3*3*1。

而余数为2,还是可以写成3*3*2,其是大于3*5的。

具体解法:

int integerBreak(int n) {
if(n<4)return n-1;
int ret=n%3;
int dbn=n/3;
if(ret<2)return (3+ret)*(long long)pow(3,dbn-1);
else return ret*(long long)pow(3,dbn);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息