剑指 Offer第三天

1.剑指 Offer 13. 机器人的运动范围

思路:

设计到搜索题目,一般用广度优先(队列)或者深度优先(递归)来解决。

数位之和计算:

设一个数字x,将x对10取余可以求得其个位数,对x向下取整即向右移动一位,可以删除个位数得到十位数。
    因此可以通过循环求得数位之和s
    int sums(int x)
    	int s=0;
		while(x!=0){
            s+=x%10;
            x=x/10;
        }
	return s;

数位和增量公式:

由于机器人每次只能移动一格,因此每次只需计算x到x±1的数位和增量
设x的数位和为Sx,x+1的数位和就为Sx+1;
①当(x+1)10取余等于0时,Sx+1 = Sx - 8;
②当对10取余不等于0时,则Sx+1=Sx +1;
(x + 1) % 10 != 0 ? s_x + 1 : s_x - 8;
方法一:

深度优先遍历DFS:可以理解为暴力法模拟机器人在矩阵中的所有路径,DFS通过递归,先朝一个方向搜索到底,再回溯至上个节点,沿一个方向搜索,依次类推。

剪枝:在搜索中,遇到数位和超出目标值、此元素已访问。则应立即返回,称之为可行性剪枝。

算法解析

  • 递归参数:当前元素在矩阵中的行列索引i,j,两者的数位和si,sj
  • 终止条件:当行列索引越界;数位和超出目标值;当前元素已访问过,这三种情况时,返回0,不计入可达解
  • 递推工作:
    • 标记当前单元格:将索引(i,j)存入数组中
    • 搜索下一个单元格:计算当前元素下、右两个方向的数位和,并开启下层递归
  • 回溯返回值:返回右方可达解+下方可达解+1。

代码:

class Solution {
    int m,n,k;
    boolean[][] visited;
    public int movingCount(int m,int n,int k){
        this.m = m;this.n=n;this.k=k;
        this.visited = new boolean[m][n];
        return dfs(0,0,0,0);        
    }
    public int dfs(int i,int j,int si,int sj){
        if(i>=m||j>=n||si+sj>k||visited[i][j])
            return 0;
        visited[i][j]=true;
        return dfs(i+1,j,(i+1)%10 !=0 ?si+1:si-8,sj)+dfs(i,j+1,si,(j+1)%10 !=0 ?sj+1:sj-8)+1;
    }
}
方法二

广度优先遍历BFS:和DFS相比,二者都是遍历整个矩阵,不同点在于搜索顺序不同。DFS是朝一个方向走到底再回退,二BFS则是按照平推的方式向前搜索,通常使用队列实现广度优先遍历

算法解析

  • 初始化:将机器人初始点(0,0)加入到队列queue
  • 迭代终止条件:queue为空,代表已遍历完所有可达解
  • 迭代工作:
    1. 单元格出队:将队首单元格的索引、数位和弹出,作为当前搜索的单元格
    2. 判断是否跳过:若 索引越界、数位和超出目标值、当前元素已访问过,执行continue
    3. 标记当前单元格:将索引(i,j)存入集合visited
    4. 单元格入队:将当前元素的下方有方单元格的索引、数位和加入queue
  • 返回值:集合visited的长度len

代码:

class Solution {
    public int movingCount(int m,int n,int k){
        boolean[][] visited =new boolean[m][n];
        int res = 0;
        Queue<int[]> queue = new LinkedList<int[]>();
        queue.add(new int[]{0,0,0,0});
        while(queue.size() > 0){
            int[] x = queue.poll();
            int i = x[0],j=x[1],si=x[2],sj =x[3];
            if(i>=m||j>=n||k<si+sj||visited[i][j])
                continue;
            visited[i][j] = true;
            res ++;
            queue.add(new int[]{i+1,j,(i+1)%10 !=0?si+1:si-8,sj});
            queue.add(new int[]{i,j+1,si,(j+1)%10 !=0?sj+1:sj-8});
        }
        return res
    }
}
2.剑指 Offer 14- I. 剪绳子

思路:数学推导,找规律

代码:

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;
    }
}
3.剑指 Offer 14- II. 剪绳子 II

思路:和上题差不多,只是多了个大数取余,贪心算法。

  • 如果 n == 2,返回1,如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1

  • 如果 n == 4,返回4

  • 如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段;每次乘法操作后记得取余就行

  • 以上2和3可以合并

代码:

class Solution {
    public int cuttingRope(int n) {
        if(n < 4){
            return n - 1;
        }
        long res = 1;
        while(n > 4){
            res  = res * 3 % 1000000007;
            n -= 3;
        }
        return (int) (res * n % 1000000007);
    }
}
4.剑指 Offer 15. 二进制中1的个数

思路:

逐位判断:判断最后一位是否为1,计数,再将n右移一位,循环

代码:

public class Solution {
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            res += n & 1;
            n >>>= 1;
        }
        return res;
    }
}
巧用 n & (n - 1)n&(n−1)
  • (n−1) 解析: 二进制数字 nn 最右边的 11 变成 00 ,此 11 右边的 00 都变成 11
  • n & (n - 1)n&(n−1) 解析: 二进制数字 nn 最右边的 11 变成 00 ,其余不变。

代码:

public class Solution {
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            res ++;
            n &= n-1;
        }
        return res;
    }
}
5.剑指 Offer 16. 数值的整数次方

思路:

递归

  • 如果n == 0,返回1
  • 如果n < 0,最终结果为 1/x^{-n}
  • 如果n为奇数,最终结果为 x * x ^ {n - 1}
  • 如果n为偶数,最终结果为 x ^ {2*(n/2)}

代码

class Solution {
    public double myPow(double x, int n) {
        if(n==0){
            return 1;
        }else if(n<0){
            return 1/(x*myPow(x,-n-1));
        }else if(n%2==1){
            return x*myPow(x,n-1); 
        }else{
            return myPow(x*x,n/2);
        }
    }
}

迭代

class Solution {
    public double myPow(double x, int n) {
        long b = n;
        if(b < 0){
            x = 1 / x;
            b = - b;
        }
        double res = 1;
        while(b != 0){
            if(b % 2 != 0){
                res *= x;
            }
            b >>= 1;
            x *= x;
        }
        return res;
    }
}
快速幂解析(二分法角度)

思路:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oS8cZ7Ru-1621997918819)(C:\Users\tanglei\AppData\Roaming\Typora\typora-user-images\image-20210526105006600.png)]

  • 转化为位运算
    向下整除 n // 2等价于 右移一位 n >> 1 ;
    取余数n%2 等价于 判断二进制最右一位值n&1 ;

  • 算法
    在这里插入图片描述

代码

class Solution {
    public double myPow(double x, int n) {
        if(x == 0) return 0;
        long b = n;
        double res = 1.0;
        if(b < 0) {
            x = 1 / x;
            b = -b;
        }
        while(b > 0) {
            if((b & 1) == 1) res *= x;
            x *= x;
            b >>= 1;
        }
        return res;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值