Leetcode刷题——剑指offer_2

微风需要竹林溪流需要蜻蜓

剑指 Offer 12. 矩阵中的路径

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

思路:原理是采用深度优先遍历的思想,从二维数组的各个元素中进行分别的遍历,在遍历过程中,首先,如果遇到不符合情况,return false;反之 判断是否 二维字符网格符合的长度等于word的长度,如果符合条件 return ture,都不符合递归结束的条件,将二维数组的该元素设置为空,以防重复调用,继续迭代朝着该元素的四个方向, 在迭代结束后,恢复该数组的元素,return Boolean;

/**
 * @author shkstart
 * @create 2022-07-15 下午1:20
 * 矩阵中的路径问题
 * 采用dfs深度优先搜索的方法来解决
 * 递归结束的条件,然后在深层次进一步递归
 * 原理上,判断是否符合两个条件
 */
public class offer_12 {
    public boolean exist(char[][] board, String word) {
        char[] words=word.toCharArray();
        //考虑从若干个节点出发,从头开始的位置
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (dfs(board,words,i,j,0)) return true;
            }
        }
        return false;
    }

    public boolean dfs(char[][] board,char[] words,int i,int j,int k){
        //递归结束的条件:越界,然后与对应数组不对应
        if (i<0||i> board.length-1||j<0||j>board[0].length-1||board[i][j]!=words[k]){
            return false;
        }
        //递归结束成功的条件:不满足上述问题后+k匹配完成
        if (k==words.length-1) return true;

        //在使用完该矩阵的元素后为避免重复使用,设置为‘/0’
        board[i][j]='\0';

        //四个条件只要满足一个,就能成功
        boolean res= dfs(board,words,i+1,j,k+1) || dfs(board,words,i-1,j,k+1)||dfs(board,words,i,j-1,k+1)||dfs(board,words,i+1,j+1,k+1);
        board[i][j]=words[k];//递归dfs之后在恢复该元素的位置
        return res;
    }


面试题13. 机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0]的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格
[35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

思路:本题主要思想为判断机器人到达格子的数量,首先面对该类问题,依然采用递归的思路来进行处理,考虑到dfs 和bfs 这两类,在该问题中,考虑到一个位数和的问题,建议先写一个函数计算位数he,然后在主函数进行调用!然后设置一个visit数组来判断元素是否被访问过,dfs 递归过程中,设置一个循环出去的一个条件,比如越界,位数和不符合等!!接下来 将该元素设置为已访问,最后返回1+dfs(),进行递归处理!!
bfs的思想类似,设置一个队列用来装二维数组的下标,通过队列的进出,进行矩阵的遍历,在出队列的元素中进行处理,判断是否符合元素的要求!!设置一个全局变量 进行++;最后返回该变量!!

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
/**
 * @author shkstart
 * @create 2022-07-15 下午2:22
 * 机器人的运动范围:依旧采用DFS或者BFS的策略,将每一步拆分开来,判断增加的位数和,然后进行判断是否满足条件
 * 首先位数和小于给定的 K;其次要保证没有访问过,最后还不能越界
 *作为一个机器人,如果进行运动的
 */
public class offer_13 {
    int m,n,k;
    boolean[][] visit;

    //计算位数和!!!!
    public int bitSum(int m){
        int sum=0;
        while (m!=0){
            sum=sum+m%10;
            m=m/10;
        }
        return sum;}

    public int movingCount1(int m, int n, int k) {
        this.m = m;
        this.n = n;
        this.k = k;
        this.visit = new boolean[m][n];
        //从左上角开始进行搜索
        return dfs(0, 0);
    }

    public int dfs(int i,int j)
    {
        //不满足条件的话直接返回0,称为预剪枝
        if (i>m-1 ||i<0 ||j>n-1||j<0||bitSum(i)+bitSum(j)>k ||visit[i][j]) return 0;
        //满足上述条件,则可以访问 设置visit矩阵为TRUE,且进行+1;然后依次递归
        visit[i][j]=true;
        return 1+dfs(i+1,j)+dfs(i,j+1);
    }

    //采用BFS的策略;使用队列来进行依次访问
    public int movingCount2(int m,int n,int k){
        int res=0;//初始数量
        boolean[][] visited=new boolean[m][n];
        Queue<int[]> queue=new LinkedList<int[]>() ;//队列方法
        queue.add(new int[]{0,0});//使用数组来作为队列元素
        while (!queue.isEmpty()) {
            int[] x = queue.poll();//出栈
            int i = x[0], j = x[1]; //获取对应元素
            if (i > m - 1 || i < 0 || j > n - 1 || j < 0 || bitSum(i) + bitSum(j) > k || visit[i][j]) continue;
            visited[i][j] = true;
            res++;//f符合条件
            queue.add(new int[]{i + 1, j});//继续入栈
            queue.add(new int[]{i, j + 1});
        }
        return res;
        }
    }

剑指 Offer 14- I. 剪绳子

*给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

思路:主要分为两个方法,第一个是贪心法,首先可以证明出将绳子分为长度为三的段,所得乘积是最大的,因此尽量可能将绳子分为长度为3的段,当然 4的时候除外;;;第二个方法为动态规划,考虑到绳子的最大程度积是与前者存在关系的!!!因此建立动态规划数组,dp[] 在数组的生成过程中,两个for循环,第一个控制绳子的长度,第二控制绳子在改长度下取得范围,以便求得最大值;

/**
 * @author shkstart
 * @create 2022-07-15 下午4:15
 * 剪绳子问题
 * 最大长度问题
 * m,n
 */
public class offer_14_1 {
    public int cuttingRope1(int n) {
        if (n == 2) return 1;
        if (n == 3) return 2;
        if (n == 4) return 4;
        if (n == 5) return 6;
        if (n == 6) return 9;
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 1;
        dp[3] = 2;
        dp[4] = 4;
        dp[5] = 6;
        dp[6] = 9;
        for (int i = 7; i <= n; i++) {
            dp[i] = dp[i - 2] * 2 > dp[i - 3] * 3 ? dp[i - 2] * 2 : dp[i - 3] * 3;
        }
        return dp[n];
    }

    public int cuttingRope2(int n) {
        int[] dp = new int[n + 1];
        dp[2] = 1;
        for (int i = 3; i <= n; i++) {
            for (int j = 1; j < i - 1; j++) {
                //     dp[i]=dp[i-2]*2>dp[i-3]*3?dp[i-2]*2:dp[i-3]*3;
                //第一个为按照之前的最优解进行想×,第二个是将绳子分为两部分(i-j)与j
                dp[i] = Math.max(dp[i], Math.max(dp[i - j] * j, (i - j) * j));
            }

        }
        return dp[n];
    }
    
//贪心的思想:首先证明了绳子分为长度为三的段获得的总乘积最大
//然后在 绳子 剩余1时, 拆3 补1 可以使得 2*2>3*1,取余防止溢出

    public int cuttingRope3(int n) {
        long res=1;
        
        if (n<=3) return n-1;
        if (n%3==1) {res=res*4;n=n-4;}
        if (n%3==2)  {res=res*2;n=n-2;}
        while (n!=0){res=res*3%1000000007;n=n-3;}
        return (int)res;
    }
}

剑指 Offer 14- II. 剪绳子 II

*给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

思路:与上题相同,注意取模!!!

剑指 Offer 15. 二进制中1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。

思路:采用异或的方式,对数字上的每位进行统计

/**
 * @author shkstart
 * @create 2022-07-15 下午8:48
 * 二进制中1的个数
 * 注意异或运算的含金量
 */
public class offer_15 {
    
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            res += n & 1;
            n >>>= 1;
        }
        return res;
    }

剑指 Offer 16. 数值的整数次方

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

思路:采用快速幂的方式,考虑将n 与1进行异或,然后判断结果,并将n进行右移,同时 x=x^2;将x进行累乘;

/**
 * @author shkstart
 * @create 2022-07-15 下午8:55
 * 整数幂问题
 * 使用快速幂的方法来进行幂的求解,可提高速度
 */
public class offer_16 {
    public double myPow(double x, int n) {
        double sum = 1;
        //使用long型,防止溢出
        long b=n;
        if (x == 0) return 0;
        if (b == 0) return 1.0;
        if (b < 0) {
            x = 1 / x;
            b = -b;
        }
        while (b != 0) {
            if ((b & 1) == 1) sum *= x;//和1取交
            x = x * x;
            b >>= 1;
        }
        return sum;
    }
}

剑指 Offer 17. 打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

 public int[] printNumbers(int n) {
        int[] nums=new int[(int) Math.pow(10,n)-1];
        for (int i = 0; i < Math.pow(10, n)-1; i++) {
            nums[i]=i+1;
        }
        return nums;

剑指 Offer 18. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。

思路:一个简单的链表节点删除问题,当设置两节点,依次进行遍历,找到相应节点后,在进行前节点的next指向后节点的next;删除该节点

/**
 * @author shkstart
 * @create 2022-07-15 下午9:50
 * 删除链表节点问题
 */
public class offer_18 {

     public class ListNode {
         int val;
         ListNode next;
         ListNode(int x) { val = x; }
      }

    public ListNode deleteNode(ListNode head, int val) {
         if (head==null) return null;
         ListNode temp1=head;
         ListNode temp2=head.next;
         if (temp1.val==val){head=head.next;return head;}
         while(temp2!=null){
             if (temp2.val==val){temp1.next=temp2.next;}
             temp1=temp1.next;
             temp2=temp2.next;
         }
        return head;

剑指 Offer 20. 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
数值(按顺序)可以分成以下几个部分:
若干空格
一个 小数 或者 整数
(可选)一个 ‘e’ 或 ‘E’ ,后面跟着一个 整数若干空格
小数(按顺序)可以分成以下几个部分:
(可选)一个符号字符(‘+’ 或 ‘-’)
下述格式之一:
至少一位数字,后面跟着一个点 ‘.’
至少一位数字,后面跟着一个点 ‘.’ ,后面再跟着至少一位数字
一个点 ‘.’ ,后面跟着至少一位数字
整数(按顺序)可以分成以下几个部分:
(可选)一个符号字符(‘+’ 或 ‘-’)至少一位数字
部分数值列举如下:
[“+100”, “5e2”, “-123”, “3.1416”, “-1E-16”, “0123”]
部分非数值列举如下:
[“12e”, “1a3.14”, “1.2.3”, “±5”, “12e+5.4”]

思路: * 判断一个字符串是否可以表示为数值

  • 一个一个情况进行排查,首先要定义好布尔变量,然后依次遍历数组,进行相应的判断
  • 数组为数字,数组为小数点。数组为e/E,数组为加减号
  • 考虑到出现小数点后不能有小数点,e之后不能出现e,±只能出现在第一个或者e之后;
/**
 * @author shkstart
 * @create 2022-07-16 下午4:21
 * 判断一个字符串是否可以表示为数值
 *
 * 一个一个情况进行排查,首先要定义好布尔变量,然后依次遍历数组,进行相应的判断
 * 数组为数字,数组为小数点。数组为e/E,数组为加减号
 */
public class offer_20 {

    public boolean isNumber1(String s) {
        if (s.isEmpty()||s.length()==0) return false;
        char[] nums=s.trim().toCharArray();
       // if (nums.length==1&&nums[0]=='E'||nums[0]=='e'){return true;}
        boolean isnums=false,isEore=false,isdot=false;//设置变量
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] >= '0' && nums[i] <= '9') {
                //判断为数字
                isnums = true;
            } else if (nums[i] == '.') { //小数点的情况
                if (isdot || isEore) return false;//如果出现小数点或者前面不曾有过E或者e
                isdot = true;//标记出现过小数点
            } else if (nums[i] == 'e' || nums[i] == 'E') {//e 或者E的情况
                if (isEore == true || !isnums) return false;//曾经出现过e,或者前面不曾出现过数字
                isEore = true;
                isnums = false;  //对变量进行标记,表明下一次需要数字出现
            } else if (nums[i] == '+' || nums[i] == '-') {
                if (i != 0 && nums[i - 1] != 'E' && nums[i - 1] != 'e') {
                    return false;}
                //+-号不在数组第一个且前面的数组中的元素不为E
            } else return false;
        }
        return isnums;
        }

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。

*调节数组的顺序,使得奇数在前,偶数在后边

  • 快排思想:定义两个指针,从前往后,从后往前,进行遍历
/**
 * @author shkstart
 * @create 2022-07-17 上午10:06
 *调节数组的顺序,使得奇数在前,偶数在后边
 * 快排思想:定义两个指针,从前往后,从后往前,进行遍历
 */
public class offer_21 {
    public int[] exchange(int[] nums) {
        int i=0,j=nums.length-1,temp = 0;
        while (i<=j){
            if (nums[i]%2==0&&nums[j]%2!=0) {
                temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
                i++;
                j--;
            }
            if (nums[i]%2!=0) i++;
            if (nums[j]%2==0) j--;
        }
        return nums;
    }
}

剑指 Offer 22. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

第一次去判断链表节点的个数,然后根据节点个数来判断length-k的节点所在

   public ListNode getKthFromEnd(ListNode head, int k) {
         int length = 0;
        ListNode node1 = head;
        while (node1 != null) {
            node1 = node1.next;
            length++;
        }
        if (length < k) return null;
        while (length - k != 0) {
            head = head.next;
            k++;
        }
        return head;
    }

剑指 Offer 24. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

思路:有两种方法,一个是双指针,一个是递归的方法;
递归:考虑到在反转n-1链表的情况下,将head.next的下一个节点指向head,将head.next指向null; 递归既是在考虑到递归结果处理完成的情况下进行处理的过程
双指针,设计两个指针,分别对每个节点进行反转,并有另外一个变量表示下一节点,依次往下走;

 public ListNode reverseList1(ListNode head) {

          ListNode cur=head,pre=null;
          while (cur !=null){
              ListNode temp=cur.next;
              cur.next=pre;
              pre=cur;
              cur=temp;
          }
          return pre;
        }

    public ListNode reverseList2(ListNode head) {

          //递归思路:在完成条件一的基础上进行假设,进行下一步的操作
        if (head == null || head.next == null) return head;
        ListNode node = reverseList2(head.next);
        head.next.next = head;
        head.next = null;
        return node;
    }
}

剑指 Offer 25. 合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

思路:首先创建一个新节点,然后进行两个链表节点大小的遍历,遍历后,将小的节点连接到新节点后,之后next;当遍历结束后,看哪个链表节点不空,进行连接;

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {

        ListNode head=new ListNode(0),temp=head;//新创造一个头结点,然后使用指针来指向
        //伪节点的形式,依次进行比较合适的连入表中
        while (l2!=null &&l1!=null){
            if (l1.val>= l2.val){
                temp.next=l2;l2=l2.next;temp=temp.next;
            }else {
                temp.next=l1;l1=l1.next;temp=temp.next;
            }
        }
        //看是否有剩余,有剩余的话直接连上
        temp.next= l1!=null?l1:l2; //优化
        //while (l1!=null){ temp.next=l1;l1=l1.next;temp=temp.next;}
        //while (l2!=null){ temp.next=l2;l2=l2.next;temp=temp.next;}

       // head=head.next;
        return head.next;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值