1.内容介绍
本篇文章记录在leetcode中Math主题下面的题目和自己的思考以及优化过程,具体内容层次按照{题目,分析,初解,初解结果,优化解,优化解结果,反思}的格式来记录,供日后复习和反思[注:有些题目的解法比较单一,就没有优化过程]。题目的顺序按照leetcode给出的题目顺序,有些题目在并不是按照题目本身序号顺序排列的,也不是严格按照难易程度来排列的。
因此,这篇文章并不具有很强的归类总结性,归类总结性知识将会在其他文章记录,本篇重点在记录解题过程中的思路,希望能对自己有所启发。
2.题目和解题过程
2.1 Reverse Integer
- 题目:Given a 32-bit signed integer, reverse digits of an integer.
Example 1:
Input: 123 Output: 321
Example 2:
Input: -123 Output: -321
Example 3:
Input: 120 Output: 21
Note:
Assume we are dealing with an environment which could only hold integers within the 32-bit signed integer range. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.
- 分析:题目求解需要对给定的32bit整数进行逐位提取然后逆序生成,其中包含了对逆序溢出和前置0的边界检查。
- 初解:要提取最低位值需要取模运算,而提取每一位上的值则需要将该位移动到最低位上,例如:321,提取1执行321%10,提取2执行32%10,提取3执行3%10。因此每次提取到最低位的值之后需要对原有的值进行低位截断,也就是整除10操作。而原有的值与逆序之后的值的权重序列正好相反,也就是说3的权重由原来的100变为逆序后的1,2的权重由原来的10变为逆序后的10,1的权重由原来的1变为逆序后的100,。这可以用一个递归的过程来描述:对每一位上的值提取需要从后向前计算,而逆序数每一位权重的生成则需要从前向后计算。符合先进后出的逻辑。计算出新的逆序数之后判断是否溢出的原理是:32位整数如果溢出后,该32bit代表的值与原来的值不同,因此可以先用一个64位数来计算逆序数,然后转存到32位数上,再对两者进行对比检查是否相等即可。
class Solution { public: int reverse(int x) { if(x==0) return 0; bool flag = true; if(x<0) { flag = false; x = -x; } int times = 1; long re_val = re(x, times); int tmp = re_val; if(tmp != re_val) return 0; if(flag==false) tmp = -tmp; return tmp; } long re(int val, int& times) { if(val == 0) return 0; long extract_val = val % 10; val = val/10; long res = re(val, times); if(val != 0) times = times * 10; res += extract_val * times; return res; } };
- 初解结果:
- 反思:逆序的逻辑与栈后进先出的逻辑相似。
2.2 Palindrome Number
-
题目:Determine whether an integer is a palindrome. Do this without extra space.
- 分析:回文数的性质是数值按位对称,12321中3是对称中心,12和21分别按位对称。那么判断是否是回文数就可以从两端位值进行对比直到相遇。
- 初解:先计算给定的数有几位,然后分别从高位向下递减和从低位向上递增,提取高位和低位的值作对比,使用递归或迭代均可。
class Solution { public: bool isPalindrome(int x) { if(x < 10 && x >=0 ) return true; if(x < 0 || x == 10) return false; int front = 1, val = x, end = 1; while(1) { val = val / 10; if(val != 0) front = front * 10; else break; } return judge_bit(x, front, end); } bool judge_bit(int x, int front, int end) { if(front > end) { if(((x / front) % 10) == ((x / end) % 10)) { if(judge_bit(x, front / 10, end * 10) == true) return true; else return false; } else return false; } else return true; } };
- 初解结果:
- 反思:熟悉递归和迭代的性质。
2.3 Integer to Roman
- 题目:Given an integer, convert it to a roman numeral.Input is guaranteed to be within the range from 1 to 3999.
- 分析:罗马数的表示单位值是1000,500,100,50,10,5,1,分别对应于字母M,D,C,L,X,V,I。每个相同单位不能连续出现超过3次,比如:3是III,而4是VI。而且每个值只有一种表示形式,必须以大值单位优先表达。
- 初解:对于给定的整数值,先从大值单位开始计算该单位上对应的数,然后检查此数落在哪个范围:[0,3],[4,8],[9];然后予以不同的处理。最后将该数乘以对应的单位值保存起来向后累加,值得注意的是需要跳过500,50,5这三个单位,因为这样才能使得每个单位上的数落在上述范围内。
class Solution { public: string intToRoman(int num) { char unit[7] ={'M','D','C','L','X','V','I'}; int val[7] ={1000,500,100,50,10,5,1}; string res = ""; int unit_index = 0; while(num != 0) { int multiple = num / val[unit_index]; if( multiple > 0 ) { switch(multiple) { case 1:res+=unit[unit_index];break; case 2:res+=string(2,unit[unit_index]);break; case 3:res+=string(3,unit[unit_index]);break; case 4:res+=string(1,unit[unit_index])+string(1,unit[unit_index-1]);break; case 5:res+=unit[unit_index-1];break; case 6:res+=string(1,unit[unit_index-1])+string(1,unit[unit_index]);break; case 7:res+=string(1,unit[unit_index-1])+string(2,unit[unit_index]);break; case 8:res+=string(1,unit[unit_index-1])+string(3,unit[unit_index]);break; case 9:res+=string(1,unit[unit_index])+string(1,unit[unit_index-2]);break; } } num = num - multiple * val[unit_index]; unit_index += 2; } return res; } };
- 初解结果:
- 反思:严密分析题目逻辑最重要。
2.3 Roman to Integer
- 题目:Given a roman numeral, convert it to an integer.Input is guaranteed to be within the range from 1 to 3999.
- 分析:给定的罗马数字符串序列中,每个字符代表一个权重值,而权重大的在权重小的前面时是相加操作,相反则是相减操作。
- 初解:
class Solution { public: int romanToInt(string s) { int res = 0; unordered_map<char, int> flag_val; flag_val['I'] = 1; flag_val['X'] = 10; flag_val['C'] = 100; flag_val['V'] = 5; flag_val['L'] = 50; flag_val['D'] = 500; flag_val['M'] = 1000; for(int i = 0; i < s.size(); ++i) { if(i+1 < s.size()) { if(flag_val[s[i]] >= flag_val[s[i+1]]) res += flag_val[s[i]]; else res -= flag_val[s[i]]; } else res += flag_val[s[i]]; } return res; } };
- 初解结果:
- 反思:。。。
2.4 Integer to English Words
- 题目:
Convert a non-negative integer to its english words representation. Given input is guaranteed to be less than 231 - 1.For example,
123 -> "One Hundred Twenty Three" 12345 -> "Twelve Thousand Three Hundred Forty Five" 1234567 -> "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
- 分析:英文中是以1000为倍率单位向上增长的,低于1000的单位是0*1000,一百万到1000之间的单位是n×1000,高于一百万但低于10亿的单位是n×1000*1000*1000。其次提取每一个范围内各个位的数即可。
- 初解:从1000以内开始提取个位,十位,百位上面的数,然后截断低三位的数,重新提取低三位的值,更新此范围的大单位值。
class Solution { public: string numberToWords(int num) { if(num == 0) return string("Zero"); string unit[] = {"Zero"," One", " Two", " Three", " Four"," Five"," Six"," Seven"," Eight"," Nine"," Ten"," Eleven"," Twelve"," Thirteen"," Fourteen"," Fifteen"," Sixteen"," Seventeen"," Eighteen"," Nineteen"}; string decade[] = {"Zero"," Ten"," Twenty"," Thirty"," Forty"," Fifty"," Sixty"," Seventy"," Eighty"," Ninety"}; string radix[] = {""," Thousand"," Million"," Billion"}; int radix_index = 0; string res = ""; while(num != 0) { int a = num % 100, b = num % 10, c = (num / 10) % 10, d = (num / 100) % 10; string tmp = ""; if(a < 20 && a != 0) { tmp = unit[a]; } else { if(b != 0) tmp = unit[b]; if(c != 0) tmp = decade[c] + tmp; } if(d != 0) tmp = unit[d] + string(" Hundred") + tmp; if(a !=0 || b!=0 || c!=0 || d!=0) res = tmp + radix[radix_index] + res; num = num / 1000; ++radix_index; } return res.substr(1,res.size()); } };
- 初解结果:
- 反思:数学类型的题目要抓住数字规律即可。