剑指offer专题——day02

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

(2)剑指 Offer 14- I. 剪绳子

(3)剑指 Offer 14- II. 剪绳子 II

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

(5)剑指 Offer 16. 数值的整数次方

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

(7)剑指 Offer 19. 正则表达式匹配

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

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

(10) 剑指 Offer 24. 反转链表

--------------------------------------------------------------------------

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

Difficulty: 中等

地上有一个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。请问该机器人能够到达多少个格子?

示例 1:

输入:m = 2, n = 3, k = 1
输出:3

示例 2:

输入:m = 3, n = 1, k = 0
输出:1

提示:

  • 1 <= n,m <= 100
  • 0 <= k <= 20

Solution:关键点:机器人要一步一步的走,被挡住的地方即使数位和小于k也是过不去的

//关键点:机器人要一步一步的走
//被挡住的地方即使数位和小于k也是过不去的

class Solution {
    public int movingCount(int m, int n, int k) {
        boolean[][] used = new boolean[m][n];
        return getCount(m, n, 0, 0, k, used);
    }

    public int getCount(int m, int n, int i, int j, int k, boolean[][] used){
        int count = 0;
        if(i<0 || i>=m || j<0 || j>=n || used[i][j] || getSum(i)+getSum(j)>k){
            return 0;
        }
        count++;
        used[i][j] = true;
        
        count += getCount(m, n, i-1, j, k, used);   //四个方向
        count += getCount(m, n, i, j-1, k, used);
        count += getCount(m, n, i+1, j, k, used);
        count += getCount(m, n, i, j+1, k, used);
        return count;
    }

    public int getSum(int i){
        int sum = 0;
        while(i != 0){
            sum += i%10;
            i = i/10;
        }
        return sum;
    }
}

剑指 Offer 14- I. 剪绳子

Difficulty: 中等

给你一根长度为 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。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

  • 2 <= n <= 58

注意:本题与主站 343 题相同:

Solution:解法1:暴力;解法2:找规律

// 方法一:暴力解法
//class Solution {
//     public int cuttingRope(int n) {
//         int max = 0;
//         for(int i=2; i<=n; i++){   //分成几段
//             int x = n/i, y = n%i;
//             int sum = 1;
//             for(int j=0; j
//                 if(y > 0){
//                     sum *= (x+1);
//                     y--;
//                 }
//                 else sum *= x;
//             }
//             max = Math.max(max, sum);
//         }
//         return max;
//     }
// }

//方法二:规律:当n大于3的时候,优先选3;
//当n=1的时候,挪出一个3 ,凑成4;
//当n=2的时候,直接乘以2

class Solution{
    public int cuttingRope(int n) {
        if(n == 2) return 1;
        if(n == 3) return 2;  //m>1,所以只能分成2和1
        if(n == 4) return 4;

        int sum = 1;
        while(n > 4){
            sum *= 3;
            n -= 3;
        }
        return sum*n;
    }
}

剑指 Offer 14- II. 剪绳子 II

Difficulty: 中等

给你一根长度为 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。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

  • 2 <= n <= 1000

注意:本题与主站 343 题相同:

Solution:使用上一题的规律,而且要将sum定义成long

//关键点:使用上一题的规律,而且要将sum定义成long
class Solution {
    public int cuttingRope(int n) {
        if(n == 2) return 1;
        if(n == 3) return 2;
        if(n == 4) return 4;

        long sum = 1;
        while(n > 4){
            sum = sum*3%1000000007;
            n = n-3;
        }
        return (int)(sum*n%1000000007);
    }
}

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

Difficulty: 简单

请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

注意:本题与主站 191 题相同:

Solution:位运算(三个注意的地方)

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int count  = 0;
        while(n != 0){  //(1)不能写n>0,因为负数的二进制也有1
            count = count + (n&1);  // (2)加号的优先级比与运算高,一定要加括号
            n = n>>>1;  //(3)有符号右移>>>,无符号右移>>
        }
        return count;
    }
}

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

Difficulty: 中等

实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

示例 1:

输入: 2.00000, 10
输出: 1024.00000

示例 2:

输入: 2.10000, 3
输出: 9.26100

示例 3:

输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25

说明:

  • -100.0 < x < 100.0
  • n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。

注意:本题与主站 50 题相同:

Solution:快速幂

/*
    快速幂解法:
        (1)当n为偶数, pow(x, n) = pow(x, n/2) * pow(x, n/2);
        (2)当n为奇数, pow(x, n) = pow(x, n/2) * pow(x, n/2) * x;
    
    初始化条件:
        (1)if(n == 0) return 1;
        (2)if(n == 1) return x;
        (3)if(n == -1) return 1/x;
*/
class Solution {
    public double myPow(double x, int n) {
        if(n == 0) return 1;
        if(n == 1) return x;
        if(n == -1) return 1/x;
        double half = myPow(x, n/2);
        double pow = myPow(x, n%2);
        return half*half*pow;
    }
}

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

Difficulty: 简单

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

返回删除后的链表的头节点。

**注意:**此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:

  • 题目保证链表中节点的值互不相同
  • 若使用 C 或 C++ 语言,你不需要 freedelete 被删除的节点

Solution:定义头结点,遍历即可

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode flag = new ListNode(-1), node;
        flag.next = head;
        node = flag;
        while(node!=null && node.next!=null && node.next.val!=val){
            node = node.next;
        }
        node.next = node.next.next;
        return flag.next;
    }
}

剑指 Offer 19. 正则表达式匹配

Difficulty: 困难

请实现一个函数用来匹配包含'. ''*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a""ab*ac*a"匹配,但与"aa.a""ab*a"均不匹配。

示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

示例 4:

输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

示例 5:

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母以及字符 .*,无连续的 '*'

注意:本题与主站 10 题相同:

Solution:递归,分情况讨论(1.当前元素相等;2.当前元素不相等)

class Solution {
    public boolean isMatch(String s, String p) {

        char[] str = s.toCharArray();
        char[] ptr = p.toCharArray();
        return help(str, ptr, 0, 0);
    }

    public boolean help(char[] str, char[] ptr, int i, int j){

        int len1 = str.length, len2 = ptr.length;

        if(j == len2) return i==len1;

        if(i == len1){
            if(j == len2-1) return false; //只剩下一个
            if(ptr[j+1] != '*') return false;
            return help(str, ptr, i, j+2);
        }

        if(str[i]==ptr[j] || ptr[j]=='.'){   //相等
            if(j+1<len2 && ptr[j+1]=='*'){
                return help(str, ptr, i, j+2) ||   //匹配0次
                        help(str, ptr, i+1, j+2) ||  //匹配1次
                        help(str, ptr, i+1, j);     //匹配多次
            }
            else{
                return help(str, ptr, i+1, j+1);
            }
        }
        else{  //不相等
            if(j+1<len2 && ptr[j+1]=='*'){  //匹配0次
                return help(str, ptr, i, j+2);
            }
            else return false;
        }
    }
}

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

Difficulty: 简单

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

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。

提示:

  1. 1 <= nums.length <= 50000
  2. 1 <= nums[i] <= 10000

Solution:双指针法

class Solution {
    public int[] exchange(int[] nums) {
        int len = nums.length;
        int l = 0, r = len-1;
        while(l < r){
            while(l<r && nums[l]%2 != 0) l++;
            while(l<r && nums[r]%2 == 0) r--;
            if(l < r){
                int temp = nums[l];
                nums[l] = nums[r];
                nums[r] = temp;
            }
        }
        return nums;
    }
}

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

Difficulty: 简单

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

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

Solution:快慢指针

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode slow = head, fast = head;
        for(int i=0; i<k; i++){
            if(fast == null) return null;
            fast = fast.next;
        }

        while(fast != null){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

剑指 Offer 24. 反转链表

Difficulty: 简单

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

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

限制:

0 <= 节点个数 <= 5000

注意:本题与主站 206 题相同:

Solution:1.递归;2.遍历(类似于头插法)

注意:递归写法,可以模拟一下过程,就知道要怎么写了

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null || head.next==null) return head;
        ListNode node = reverseList(head.next);
        head.next.next = head;  //不能写成 node.next = head, 因为node是头结点
        head.next = null;
        return node;
    }
}


你可能感兴趣的:(刷题第一轮)