(一)剑指 Offer 14- I. 剪绳子
基本思路:
1.如果我们不考虑数学问题,我们可以采用动态规划的方法来解决这个问题。有点类似于青蛙跳台阶的题。
2.但是这道题还有数学解法:
(1)最优: 3 。把绳子尽可能切为多个长度为 3 的片段,留下的最后一段绳子的长度可能为 0,1,2三种情况。
(2)次优: 2 。若最后一段绳子长度为 22 ;则保留,不再拆为 1+1。
(3)最差: 1 。若最后一段绳子长度为 1 ;则应把一份 3 + 1替换为 2 + 2
推导见官方解答
动态规划代码:
class Solution {
//每一段都最大
void helpFun (int sum ,int[] save){
int maxNum = 1;
for(int i = 1;i<sum; i++){
int m = i*save[sum-i];
maxNum = Math.max(m,Math.max(maxNum,i*(sum-i)));
}
save[sum] = maxNum;
}
public int cuttingRope(int n) {
int[] save = new int[n+1];
// save[1] = 1;
save[2] = 1;
for(int i = 3;i <= n;i++){
helpFun(i,save);
// System.out.println(i+" "+save[i]);
}
return save[n];
}
}
数学解法如下:
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n - 1;
int a = n / 3, b = n % 3;
if(b == 0) return (int)Math.pow(3, a);
if(b == 1) return (int)Math.pow(3, a - 1) * 4;
return (int)Math.pow(3, a) * 2;
}
}
(二)剑指 Offer 20. 表示数值的字符串
基本思路:
这道题中涉及到状态转化,首先我们需要考虑,究竟有哪几种状态
(1)+/-
(2).
(3)E/e
(4)数字
首先,我们需要根据e来将数字分为指数和底数,因为指数和底数的规则是不同的;
接下来,对于底数,首先我们判断首位是否为±符号,如果是,则不会出现其他加减号;接下来,判断是否有小数点,小数点可以有0或1个。其他的都是数字。
对于指数,我们判断是否首位是否为-符号,如果是,则不会出现其他加减号;接下来,判断是否有小数点,小数点可以有0或1个。其他的都是数字。
class Solution {
private int index = 0;//全局索引
private boolean scanUnsignedInteger(String str) {
//是否包含无符号数
int before = index;
while(str.charAt(index) >= '0' && str.charAt(index) <= '9')
index++;
return index > before;
}
private boolean scanInteger(String str) {
//是否包含有符号数
if(str.charAt(index) == '+' || str.charAt(index) == '-')
index++;
return scanUnsignedInteger(str);
}
public boolean isNumber(String s) {
//空字符串
if(s == null || s.length() == 0)
return false;
//添加结束标志
s = s + '|';
//跳过首部的空格
while(s.charAt(index) == ' ')
index++;
boolean numeric = scanInteger(s); //是否包含整数部分
if(s.charAt(index) == '.') {
index++;
//有小数点,处理小数部分
//小数点两边只要有一边有数字就可以,所以用||,
//注意scanUnsignedInteger要在前面,否则不会进
numeric = scanUnsignedInteger(s) || numeric;
}
if((s.charAt(index) == 'E' || s.charAt(index) == 'e')) {
index++;
//指数部分
//e或E的两边都要有数字,所以用&&
numeric = numeric && scanInteger(s);
}
//跳过尾部空格
while(s.charAt(index) == ' ')
index++;
return numeric && s.charAt(index) == '|' ;
}
}
(三)剑指 Offer 43. 1~n 整数中 1 出现的次数
基本思路:
我们可以分别计算1出现在个位、十位、百位…上次数的和。
官方解答
class Solution {
public int countDigitOne(int n) {
//1在个位+1在十位+1在百位,,,,以此类推
int total = 0;
int weight = 0;//当前位的值
int round = 0;//由高位决定
int base = 1;//对应的位数,1,10,100等
int dig = 0;//保存低位数字
while(n/base > 0){//说明此位上有数值
weight = (n/base)%10;
if(weight == 0){
//如果这一位为0
round = n/(base*10);
total += base*round;
}else if (weight == 1){
dig = n%base;
if(n/base == 1){
total += dig+1;
}else{
round = n/(base*10)+1;
total += base*(round-1) + dig + 1;
}
}else{
round = n/(base*10)+1;
total += round * base;
}
if(base == 1000000000){//10^9次方是int中最大的10倍数,10次方就超出范围了
break;
}
base *= 10;
}
return total;
}
}
(三)剑指Offer44. 数字序列中某一位的数字
基本思路:
1.首先我们应当判断这个数字对应的是几位数的范围。规律:
(1)一位数从0到9共十个数,占10位
(2)两位数从10到99共100-10个数,占100x2
(3)三位数从100到999共1000-100-10个数,占1000x3位
以此类推
2.所求数位 在从数字 start开始的第 [(n - 1) / digit] 个 数字 中( startstart 为第 0 个数字)。
num = start + (n - 1) // digit
class Solution {
public int findNthDigit(int n) {
int digit = 1;//几位数
long start = 1;//10的几次方
long count = 9;//这一类位数对应的数字的数量
while (n > count) {
n -= count;
digit += 1;
start *= 10;
count = digit * start * 9;
}
long num = start + (n - 1) / digit; // 2.
return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.
}
}
(四)剑指 Offer 49. 丑数
基本思路:
每一个丑数必然是由之前的某个丑数与2,3或5的乘积得到的,这样下一个丑数就用之前的丑数分别乘以2,3,5,找出这三这种最小的并且大于当前最大丑数的值,即为下一个要求的丑数。
class Solution {
public int nthUglyNumber(int n) {
int p2=0,p3=0,p5=0;
int[] dp=new int[n];
dp[0]=1;
for(int i=1;i<n;i++){
dp[i]=Math.min(dp[p2]*2,Math.min(dp[p3]*3,dp[p5]*5));//下一个可能的丑数,在下面的数选择一个最小的
if(dp[i]==dp[p2]*2) p2++;//指向下一个丑数大的数
if(dp[i]==dp[p3]*3) p3++;//不确定是这个数乘以3大还是p2乘以2大,所以还要进行一次比较
if(dp[i]==dp[p5]*5) p5++;
}
return dp[n-1];
}
}
(五)剑指 Offer 67. 把字符串转换成整数
基本思路:
1.首先去除掉前面的空格
2.如果有-号,则保存为符号位
3.判断各位是否为数字,是数字就添加上,不是数字就终止
4.注意的是,这里需要进行是否溢出的判断
class Solution {
public int strToInt(String str) {
//检查是否超过范围:正数如果第二个数比第一个数还小,负数如果第二个数比第一个数还大
int res = 0, bndry = Integer.MAX_VALUE / 10;
int i = 0, sign = 1, length = str.length();
if(length == 0) return 0;
while(str.charAt(i) == ' ')
if(++i == length) return 0;
if(str.charAt(i) == '-') sign = -1;
if(str.charAt(i) == '-' || str.charAt(i) == '+') i++;
for(int j = i; j < length; j++) {
if(str.charAt(j) < '0' || str.charAt(j) > '9') break;
if(res > bndry || res == bndry && str.charAt(j) > '7')
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
res = res * 10 + (str.charAt(j) - '0');
}
return sign * res;
}
}
(六)剑指 Offer 42. 连续子数组的最大和
基本思路:
基于贪心算法,当叠加的和小于0时,就从下一个数重新开始,同时更新最大和的值(最大值可能为其中某个值)。故左往右计算,如果和是大于自己的,就取和,否则就取自己.在这个过程中记录最大值
class Solution {
public int maxSubArray(int[] nums) {
//
int tmp = nums[0];
int res = tmp;
for(int i = 1; i < nums.length;i++){
tmp = Math.max(nums[i],tmp+nums[i]);
res = Math.max(tmp,res);
}
return res;
}
}