未完待续…
一.简单计算
7. 反转整数
反转类的经常出现,链表,数组,字符串、整数、栈等等,
while(x)
{
int tmp = res;//1.保存前一个值用于下面的溢出判断
res=res*10+x%10;//2.累计
x/=10;//3.更新x
if(res/10!=tmp)//4.溢出判断
return 0;
}
return res;
9. 回文数判断
整数逆序,然后看是否相等就可以了
//三种特殊情况
if(x<0) return false;//1.
if(x == 0) return true;//2.
if(x % 10 == 0) return false;//3.
while(x) {//4.
num = num * 10 + x % 10;//累计
x /= 10;//更新
}
29. 两数相除
if (divisor == 0 || (dividend == INT_MIN && divisor == -1)) return INT_MAX;//1.溢出判断
long long m=abs((long long) dividend), n=abs((long long) divisor),res=0;//2.定义为long long类型是为了左移位
int sign=((dividend<0)^(divisor<0))?-1:1;//3.确定符号
while(m>=n)//4.只要被除数大于等于除数就继续循环
{
long long t=n,mul=1;
while(m>=(t<<1))//只要被除数大于等于除数就继续循环
{
t=t<<1;
mul=mul<<1;//累计mul
}
res+=mul;//累计res
m-=t;//减去已经被除过的部分(每次最少减去1*t,这时商的累积量为1,其余情况累积量为2^k)
}
return sign==1?res:-res;
43. 字符串相乘
1.申请一个大小为len1 + len2的数组tmp,用来保存中间值:vector<int> temp(len1 + len2, 0);
2.对两个字符串嵌套for循环,假设str1的每一个字符->数字分别 * str2的第0,1,…,n-1个字符->数字,得到的结果保存在i+j+1的位置:temp[i + j + 1] += d1 * d2
3.tmp数组中的各个位置保存的是d1 * d2直接相乘的值,有可能是大于9的,进一步处理进位:
for (int i=len1 + len2-1;i>0;--i) {//3.用数组保存中间值2
temp[i-1] += temp[i] / 10;
temp[i] %= 10;
}
4.找到tmp中第一个不为0的位置:while(temp[i]==0) ++i;
5.sring res,然后从i位置开始转化为字符串:for(int j=i;j<len1 + len2;++j) res+=temp[j] + '0';
66.加一
1.先把最后一位+1,digits[n-1]=sum%10;int sign=sum/10;
如果sign==0,无进位则直接返回digits;
2.有进位则继续计算数组中前面的各个值,直到sign==0(则直接返回digits)
for(int i=n-2;i>=0 && sign != 0;--i){
sum=digits[i]+sign;
digits[i]=sum%10;
sign=sum/10;
}
3.或者数组遍历完成,但是此时sign!=0,说明数组遍历结束此时sign==1,要在数组前面插入一个1,此时需要把数组放到新的内存位置了。
67.二进制求和
1.开一个string,大小为a,b长度中较大的
2.遍历两个string,从后往前相加,别忘了进位标志位
while(i >= 0 || j >= 0) //遍历两个string
{
int sum = (i >= 0 ? a[i--]-'0' : 0) + (j >= 0 ? b[j--]-'0' : 0 ) + sign;
res[max(i,j)+1] = (sum % 2) + '0';
sign = sum/2;
}
3.如果最后标志位不为0,要在前面加个1
if (sign) res = "1" + res;
求和、积(还是求和)问题,注意进位标志位的更新,和数值的统计
1.和:
int sum = (i >= 0 ? a[i--]-'0' : 0) + (j >= 0 ? b[j--]-'0' : 0 ) + sign;//1.求当前和
res[max(i,j)+1] = (sum % 2) + '0';//2.max(i,j)+1位置处放新的值
sign = sum/2;//3.更新标志位
2.积:temp[i + j + 1] += d1 * d2;
//i + j + 1位置处累积
50. Pow(x, n)
1024=(2^5)^2 = ((2^2)^2*2)^2 =((2*2) * (2*2) 2) ((2*2) * (2*2) *2)
1.采用递归的方法,double myPow(double x, int n)
截止条件如下:
if( n==0 )
return 1;
if( n==1 )
return x;
2.递归过程中,指数没次减半int t = n/2;
3.if(n<0) { t = -t;x = 1/x; }
4.递归:result = myPow(x,t);
5.最后,如果指数为偶数,就result * result,否则result * result * x:return (n%2==0)?result*result:result*result*x;
69.x 的平方根
367. 有效的完全平方数
1.牛顿迭代法:
p=x^2;
求x
令f(x)=x^2-p;
找到f(x)的根xn(xn>0),
(1)求f(x)的切线:坐标点:(xn,f(xn))
y=f’(xn)(x-xn)+f(xn)=0;
(2)更新:令x=xn+1整理得:
xn+1=xn-f(xn)/f’(xn);=> xn+1=xn-(xn^2-p)/2xn;
=>xn+1=xn/2 + P/2xn; =>r = (r + x/r) / 2
(3)这样跌待下去就找到了根值xn
int mySqrt(int x) {
long r = x;//r即为xn
while (r*r > x)
r = (r + x/r) / 2;
return r;//有效完全平方数则返回即可:return r*r == x;
}
2.二分法:
int mySqrt(int x) {
if(x<=1)
return x;
int l=1,r=x;
while(l<=r)//1.二分
{
int mid=l+(r-l)/2;//2.找到中间位置,防止溢出
if (mid == x/mid)/3.1/恰好找到,返回
return mid;
if (mid < x/mid)//3.2 l=mid+1,防止溢出
l=mid+1;
else if (mid > x/mid)//3.3 r=mid-1,防止溢出
r=mid-1;
}
return r;//因为l为需要返回的值时,它是小于根号x的,会右移,而r恰好大于根号x时,会左移,正好得到要返回的值*/
}
二.性质数,公式找规律
231. 判断2的幂
保证n>0 && n&(n-1)各个位置恰好为0
return n > 0 && !(n&(n-1));//n&(n-1)==0说明恰好为2的幂
326. 判断3的幂
1.//判断一个浮点数恰好为整数,fmod(f,1)==0,即没有小数部分
2.log10(n)/log10(3)=log3(n) <=> 指数 k=log3(n) <=> n=3^k,
3.log是e为底的对数;log10是10为底的对数;但是为啥用log就不行呢??????
//log3(n)没有小数部分,log10(n)/log10(3)返回的是浮点数
return fmod(log10(n)/log10(3) , 1) ==0;
258.给一个非负整数 num,反复添加所有的数字,直到结果只有一个数字
对num一直对9取余就可以了,但是要注意值得范围是0 ~ 9,因此 num=9时,返回9
1.(x + y) % z = (x % z + y % z) % z;
2.x % z % z = x % z;
3.对n % 9 做一些变形,注意,这里已经不是等价变形了,而是:=>
n % 9 = [(n - 1) + 1] % 9 =[ (n - 1) % 9 + 1 % 9 ] % 9 变形=> 1+(n - 1) % 9
return 1 + (num - 1) % 9;//对num % 9做变形
172.阶乘后的零
这里我们要求n!末尾有多少个0,因为我们知道0是2和5相乘得到的,而在1到n这个范围内,2的个数要远多于5的个数,所以这里只需计算从1到n这个范围内有多少个5就可以了。
除了计算n/5, 还要计算n/5/5, n/5/5/5, n/5/5/5/5, …, n/5/5/5,,,/5直到商为0,然后就和,就是最后的结果。
int trailingZeroes(int n) {
int res=0;
while(n)//1到n这个范围内有多少个5^k(k=0,1,...只要5^k<=n)
{
res+=n/5;//累积5的个数
n/=5;//更新n值
}
return res;
}
202. 快乐数
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。如果可以变为 1,那么这个数就是快乐数。
在一个while(1)循环中:
1.n==1时,返回true;
2.申请一个数组vector<int> ans
,ans中不包含n,则在ans中加入n
3.ans中包含n,返回false,(此时说明计算的平方和又回到n值了,说明它不是快乐数)
4.得到每一轮的新的n值
bool isHappy(int n) {
//unordered_map<int,int> ans;//也可以用hash_table
vector<int> ans;
while(1)
{
if(n == 1)//n==1时,返回true
return true;
if(find(ans.begin(), ans.end(), n) == ans.end())//ans中不包含n,则在ans中加入n
ans.push_back(n);
else//ans中包含n,返回false
return false;
//每一轮得到一个新的n值
int sum = 0;
while(n)
{
sum += (n % 10) * (n % 10);
n = n /10;
}
n = sum;
}
}
223.矩形面积
在二维平面上计算出两个由直线构成的矩形叠加覆盖后的面积。
1.先求两个矩形的总面积
2.把四个横坐标和四个纵坐标分别放在数组x和y中,并且在分别x,y中做排序
3.计算中间两个坐标值的差的乘积就是重叠的面积
total = total - (x[2] - x[1]) * (y[2] - y[1]);
268. 缺失数字
相比于[0…n]数组,该数组缺少了一个数,
那么先计算出[0…n]的数组的和,再减去待计算数组的和,那么缺少的数就出来了。
int ans=0;
int n=nums.size();
int sum=(1+n)*n/2;//1.0~n的和sum
for(int i=0;i<n;++i)//2.待计算数组的和ans
ans+=nums[i];
return sum-ans;//3.sum-ans 即为所求
263.丑数
丑数就是只包含质因子 2, 3, 5 的正整数。1 也可以被当做丑数。
1.从2开始(2,3,4,5):
2.如果恰好可以整除num则继续整除,否则下一个(2,3,4,5)
3.最后num恰好为1,返回true,否则返回false
bool isUgly(int num) {
for (int i=2; i<6 && num; i++)//从2开始:2,3,4,5,//4可以分解成2*2,不影响,实际在这里4是不会有机会整除的,因为2都不行了
while (num % i == 0)//如果恰好整除,则继续除
num /= i;
return num == 1;//最后num恰好为1,返回true:说明确实是只包含质因子 2, 3, 5;否则就是false
}
264.丑数 II
编写程序找第 n 个丑数。例如, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 就是前10个丑数。1 一般也被当做丑数。
如果我们把所有的丑数依次按升序存储到一个列表里面,那么这个列表后面的某个元素,一定是前面的某个元素乘2、乘3、或乘5得到的;
所以,假设现在我们已知这样一个列表的一部分,想要往列表里面继续添加新元素的话,因为这个列表已经是排好序的,所以我们可以设定三个指针index2, index3, index5,他们所指向的元素,乘2,乘3,乘5分别为此时的M2, M3, M5;
每次遍历结束之后,只需要从这三个指针的位置(含)开始向后扫描即可。DP
int nthUglyNumber(int n) {
if(n <= 0)
return false;
if(n == 1)
return true;
int t2 = 0, t3 = 0, t5 = 0; //pointers for 2, 3, 5
vector<int> v(n,0);
v[0] = 1;
for(int i = 1; i < n ; i ++)
{
v[i] = min(v[t2]*2,min(v[t3]*3,v[t5]*5));//找到2,3,5上一位置处 *2,3,5 后最小的数
if(v[i] == v[t2]*2) t2++;
if(v[i] == v[t3]*3) t3++;
if(v[i] == v[t5]*5) t5++;
}
return v[n-1];
}