剑指offer刷题笔记(java版)(7月)

剑指 Offer 03. 数组中重复的数字

哈希表

/*
//java 2020.7.25
在哈希表中添加数据,如果表中已经有该项,直接返回即可
*/
class Solution {
    public int findRepeatNumber(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();       //数据采用的哈希表结构
        int res = 0;

        //在哈希表中添加数据
        // for(int i = 0; i < nums.length; i++){
        //     if(map.containsKey(nums[i])){
        //         res = nums[i];
        //     }else{
        //         map.put(nums[i], 1);
        //     }
        // }

        //可以直接使用for each循环
        for(int num: nums){
            if(map.containsKey(num)){
                res = num;
            }else{
                map.put(num, 1);
            }
        }

        return res;     
    }
}

(官方)集合

class Solution {
    public int findRepeatNumber(int[] nums) {
        Set<Integer> set = new HashSet<Integer>();
        int repeat = -1;
        for (int num : nums) {
            if (!set.add(num)) {
                repeat = num;
                break;
            }
        }
        return repeat;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/mian-shi-ti-03-shu-zu-zhong-zhong-fu-de-shu-zi-b-4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 04. 二维数组中的查找

//java 安安 2020.7.25 还可按照题解改进
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        boolean flag = false;
    
        for(int i = matrix.length-1; i >= 0 ; i--){       
            for(int j = 0; j < matrix[0].length; j++){
                if(matrix[i][j] > target){
                    break;
                }else if(matrix[i][j] == target){
                    flag = true;
                }
                //System.out.print(matrix[i][j] + " ");
            }
            //System.out.println();
        }

        return flag;
    }
}
//java 2020.7.25 官解
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {   //先判断特殊情况
            return false;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int row = 0, column = columns - 1;
        while (row < rows && column >= 0) {
            int num = matrix[row][column];
            if (num == target) {
                return true;
            } else if (num > target) {
                column--;
            } else {
                row++;
            }
        }
        return false;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solution/mian-shi-ti-04-er-wei-shu-zu-zhong-de-cha-zhao-b-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 05. 替换空格

1.字符串替换函数

//anan 直接用字符串替换函数
class Solution {
    public String replaceSpace(String s) {
        String res = s.replace(" ", "%20");
        return res;
    }
}

2.StringBuilder

//anan 用StringBulider
class Solution {
    public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < s.length(); i++){
            //if(s.charAt(i) == " "){  //不能用双引号
            if(s.charAt(i) == ' '){
                sb.append("%20");
            }else{
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }
}

2代码改进

class Solution {
    public String replaceSpace(String s) {
        StringBuilder res = new StringBuilder();
        for(Character c : s.toCharArray())
        {
            if(c == ' ') res.append("%20");
            else res.append(c);
        }
        return res.toString();
    }
}

作者:jyd
链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/solution/mian-shi-ti-05-ti-huan-kong-ge-ji-jian-qing-xi-tu-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(官方)字符数组

class Solution {
    public String replaceSpace(String s) {
        int length = s.length();
        char[] array = new char[length * 3];
        int size = 0;
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            if (c == ' ') {
                array[size++] = '%';
                array[size++] = '2';
                array[size++] = '0';
            } else {
                array[size++] = c;
            }
        }
        String newStr = new String(array, 0, size);
        return newStr;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/solution/mian-shi-ti-05-ti-huan-kong-ge-by-leetcode-solutio/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 06. 从尾到头打印链表

两次遍历,第一次数长度,第二次填充数值

//先判断链表个数,按照这个长度申请数组,从头到尾第二次遍历链表的时候从后往前填充数组
class Solution {
    public int[] reversePrint(ListNode head) {
        ListNode p = head;
        int length = 0;
        while(p != null){
            length++;
            p = p.next;
        }
        //System.out.println(length);

        int[] res = new int[length];
        p = head;
        for(int i = 0; p != null; p = p.next, i++){
            //System.out.println(p.val);
            res[length-1-i] = p.val;
        }

        return res;
    }
}

(官方)辅助栈

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        Stack<ListNode> stack = new Stack<ListNode>();
        ListNode temp = head;
        while (temp != null) {
            stack.push(temp);
            temp = temp.next;
        }
        int size = stack.size();
        int[] print = new int[size];
        for (int i = 0; i < size; i++) {
            print[i] = stack.pop().val;
        }
        return print;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/solution/mian-shi-ti-06-cong-wei-dao-tou-da-yin-lian-biao-b/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(别人)递归法

利用递归: 先走至链表末端,回溯时依次将节点值加入列表 ,这样就可以实现链表值的倒序输出

class Solution {
    ArrayList<Integer> tmp = new ArrayList<Integer>();
    public int[] reversePrint(ListNode head) {
        recur(head);
        int[] res = new int[tmp.size()];
        for(int i = 0; i < res.length; i++)
            res[i] = tmp.get(i);
        return res;
    }
    void recur(ListNode head) {
        if(head == null) return;
        recur(head.next);
        tmp.add(head.val);
    }
}

作者:jyd
链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/solution/mian-shi-ti-06-cong-wei-dao-tou-da-yin-lian-biao-d/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 07. 重建二叉树

复制数组 太费空间了

//anan java 2020.7.28
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    public TreeNode buildTree(int[] preorder, int[] inorder) {        
        return recur(preorder, inorder);
    }

    public TreeNode recur(int[] preorder, int[] inorder){
        //System.out.println("preorder:" + Arrays.toString(preorder));
        //System.out.println("inorder:" + Arrays.toString(inorder));
        if(preorder.length == 0) return null;

        TreeNode root = new TreeNode();
        root.val = preorder[0];
        int rootIndex = -1;
        for(int i = 0; i < inorder.length; i++){
            if(inorder[i] == preorder[0]){
                rootIndex = i;
                break;
            }
        }
        //System.out.println("left");
        root.left = recur(Arrays.copyOfRange(preorder,1, rootIndex+1), java.util.Arrays.copyOfRange(inorder,0, rootIndex));
        //System.out.println("right");
        root.right = recur(Arrays.copyOfRange(preorder, rootIndex+1, preorder.length), java.util.Arrays.copyOfRange(inorder,rootIndex+1, inorder.length));

        return root;
    }
}

直接在原数组操作,灵活应用下标

//anan 
class Solution {
    
    public TreeNode buildTree(int[] preorder, int[] inorder) {        
        return recur(preorder, 0, preorder.length-1, inorder, 0, inorder.length-1);
    }

    public TreeNode recur(int[] preorder, int pre, int preEnd, int[] inorder, int in, int inEnd){
        //System.out.println("preorder:" + Arrays.toString(preorder));
        //System.out.println("inorder:" + Arrays.toString(inorder));
        if(pre > preEnd) return null;

        TreeNode root = new TreeNode();
        root.val = preorder[pre];
        if(pre == preEnd ) return root;
        
        int rootIndex = -1;
        int length = 0;
        for(int i = in; i < inEnd+1; i++){
            if(inorder[i] == preorder[pre]){
                rootIndex = i;
                break;
            }
        }
        length = rootIndex-in;
        
        System.out.println("left");
        root.left = recur(preorder, pre+1, pre+length, inorder, in, rootIndex-1);
        System.out.println("right");
        root.right = recur(preorder, pre+length+1, preEnd, inorder, rootIndex+1, inEnd);
        //root.right = recur(preorder, pre+length+1, preorder.length-1, inorder, rootIndex+1, inorder.length-1);  //错误 原因:右子树的终点不一定都在数组末尾

        return root;
    }
}

(官方)hashmap提高了效率

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder == null || preorder.length == 0) {
            return null;
        }
        Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
        int length = preorder.length;
        for (int i = 0; i < length; i++) {
            indexMap.put(inorder[i], i);
        }
        TreeNode root = buildTree(preorder, 0, length - 1, inorder, 0, length - 1, indexMap);
        return root;
    }

    public TreeNode buildTree(int[] preorder, int preorderStart, int preorderEnd, int[] inorder, int inorderStart, int inorderEnd, Map<Integer, Integer> indexMap) {
        if (preorderStart > preorderEnd) {
            return null;
        }
        int rootVal = preorder[preorderStart];
        TreeNode root = new TreeNode(rootVal);
        if (preorderStart == preorderEnd) {
            return root;
        } else {
            int rootIndex = indexMap.get(rootVal);
            int leftNodes = rootIndex - inorderStart, rightNodes = inorderEnd - rootIndex;
            TreeNode leftSubtree = buildTree(preorder, preorderStart + 1, preorderStart + leftNodes, inorder, inorderStart, rootIndex - 1, indexMap);
            TreeNode rightSubtree = buildTree(preorder, preorderEnd - rightNodes + 1, preorderEnd, inorder, rootIndex + 1, inorderEnd, indexMap);
            root.left = leftSubtree;
            root.right = rightSubtree;
            return root;
        }
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-by-leetcode-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 09. 用两个栈实现队列

自己写的用的stack

//java anan 2020.7.29
class CQueue {
    Stack<Integer> stack1 = new Stack<>();  //存放数据
    Stack<Integer> stack2 = new Stack<>();  //输出时的辅助栈

    public CQueue() {

    }
    
    public void appendTail(int value) {
        stack1.push(value);
    }
    
    //这种删除方式有点繁琐
    public int deleteHead() {
        if(stack1.empty()){  //stack1为空,说明队列为空
            return -1;
        }

        while(!stack1.empty()){
            stack2.push(stack1.pop());
        }
        int res = stack2.pop();
        while(!stack2.empty()){
            stack1.push(stack2.pop());
        }

        return res;
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

(结合官解和别人的)用的deque和linkedlist

//stack1 支持插入操作,stack2 支持删除操作
class CQueue {
    Deque<Integer> stack1;  
    Deque<Integer> stack2;  

    public CQueue() {
        stack1 = new LinkedList<>();
        stack2 = new LinkedList<>(); 
    }
    
    public void appendTail(int value) {
        stack1.push(value);
    }
    
    public int deleteHead() {
        
        if(!stack2.isEmpty()){
            return stack2.pop();
        }
        if(stack1.isEmpty()){
            return -1;
        }

        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        
        return stack2.pop();
    }
}

在这里插入图片描述
在这里插入图片描述

剑指 Offer 10- I. 斐波那契数列(dp模板)

在这里插入图片描述

安安:动态规划 double 数据类型不兼容,故用了强制类型转化

//java anan 2020.7.29
class Solution {
    public int fib(int n) {
        if(n == 0) return 0;

        double[] res = new double[n+1];
        res[0] = 0;
        res[1] = 1;

        for(int i = 2; i < n+1; i++){
            res[i] = (res[i-1] + res[i-2]) % (1e9+7);
        }
        //System.out.println(Arrays.toString(res));

        return (int)res[n];
    }
}

安安改进:类型改进dp

//java anan改进 把1e9+7改成了1000000007
class Solution {
    public int fib(int n) {
        if(n == 0) return 0;

        int[] res = new int[n+1];
        res[0] = 0;
        res[1] = 1;

        for(int i = 2; i < n+1; i++){
            //res[i] = (res[i-1] + res[i-2]) % (1e9+7);
            //Line 10: error: incompatible types: possible lossy conversion from double to int
            //1e9+7是浮点数的表示方式,所以会出现这个错误,改成整数形式的就好了
            res[i] = (res[i-1] + res[i-2]) % (1000000007);
        }
        //System.out.println(Arrays.toString(res));

        return res[n];
    }
}

在这里插入图片描述

官解和别人:空间优化

class Solution {
    public int fib(int n) {
        if(n <= 1) return n;

        int a = 0, b = 1, sum=0;
        for(int i = 2; i < n+1; i++){
            sum = (a+b)%1000000007;
            a = b;
            b = sum;
        }
        return sum;
    }
}

在这里插入图片描述

递归:会超时

class Solution {
    public int fib(int n) {
        if(n == 0 || n == 1) return n;
        return fib(n-1) + fib(n-2);
    }
}

在这里插入图片描述

记忆化递归

class Solution {
    private int[] memo;

    public int fib(int n) {
        memo = new int[n+1];  //创建记忆数组
        Arrays.fill(memo, -1);
        if (n <= 1) return n;

        memo[0] = 0;
        memo[1] = 1;
        return memoize(n);
    }

    public int memoize(int n) {
      if (memo[n] != -1) {   //如果数组里有值,说明之前算过,直接返回即可
          return memo[n];
      }
      memo[n] = (memoize(n-1) + memoize(n-2))%(1000000007);
      return memo[n];
    }
}

在这里插入图片描述

剑指 Offer 10- II. 青蛙跳台阶问题

//java anan 2020.7.29
class Solution {
    public int numWays(int n) {
        if(n == 0 || n == 1) return 1;

        int[] dp = new int[n+1];
        dp[1] = 1;
        dp[2] = 2;

        for(int i = 3; i < n+1; i++){
            dp[i] = (dp[i-1]+dp[i-2]) % (1000000007);
        }

        return dp[n];
    }
}

153. 寻找旋转排序数组中的最小值

二分法

我们希望找到旋转排序数组的最小值,如果数组没有被旋转呢?如何检验这一点呢?

如果数组没有被旋转,是升序排列,就满足 last element > first element。
在这里插入图片描述

//二分法查找
class Solution {
    public int findMin(int[] nums) {
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int middle = left + (right-left) / 2;
            //int middle = (left + right) / 2; 这样写可能会溢出
            if (nums[middle] < nums[right]) {  //[5 6 7 1 2 3 4]
                // middle可能是最小值
                right = middle;
            } else {                 //[5 6 7 1 2 3]
                // middle肯定不是最小值
                left = middle + 1;
            }
        }
        return nums[left];
    }
}

剑指 Offer 11. 旋转数组的最小数字

在这里插入图片描述

//二分法查找
class Solution {
    public int findMin(int[] nums) {
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int middle = left + (right-left) / 2;
            //int middle = (left + right) / 2; 这样写可能会溢出
            if (nums[middle] < nums[right]) {  
                // middle可能是最小值
                right = middle;
            } else if(nums[middle] > nums[right]){                 
                // middle肯定不是最小值
                left = middle + 1;
            } else{
                right--;
            }
        }
        return nums[left];
        //return Math.min(nums[left],nums[right]);//最后答案定位在了两个数字,小的那个即为答案  
        //这个是看到别人的题解里的,如果最后很难判别,就这样写,也算是一个小技巧吧
    }
}

33. 搜索旋转排序数组

在这里插入图片描述

class Solution {
    public int search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start <= end) {
            mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            //前半部分有序,注意此处用小于等于
            if (nums[start] <= nums[mid]) {
                //target在前半部分
                if (target >= nums[start] && target < nums[mid]) {
                    end = mid - 1;
                } else {
                    start = mid + 1;
                }
            } else {
                if (target <= nums[end] && target > nums[mid]) {
                    start = mid + 1;
                } else {
                    end = mid - 1;
                }
            }

        }
        return -1;

    }

// 作者:reedfan
// 链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/ji-bai-liao-9983de-javayong-hu-by-reedfan/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
}

81. 搜索旋转排序数组 II

图解
在这里插入图片描述

public boolean search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start <= end) {
            mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                return true;
            }
            if (nums[start] == nums[mid]) {
                start++;
                continue;
            }
            //前半部分有序
            if (nums[start] < nums[mid]) {
                //target在前半部分
                if (nums[mid] > target && nums[start] <= target) {
                    end = mid - 1;
                } else {  //否则,去后半部分找
                    start = mid + 1;
                }
            } else {
                //后半部分有序
                //target在后半部分
                if (nums[mid] < target && nums[end] >= target) {
                    start = mid + 1;
                } else {  //否则,去后半部分找
                    end = mid - 1;

                }
            }
        }
        //一直没找到,返回false
        return false;

    }

作者:reedfan
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii/solution/zai-javazhong-ji-bai-liao-100de-yong-hu-by-reedfan/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 12. 矩阵中的路径

anan dfs

//anan
/*
遍历矩阵,找到和字符串第一个字母相同的位置
从该位置开始,上下左右开始找和字符串第二个字母相同的位置
*/

class Solution {
    
    public boolean exist(char[][] board, String word) {
        int i0;
        int j0;
        int index = 0;

        boolean[][] flag = new boolean[board.length][board[0].length];

        for(int i = 0; i < board.length; i++){
            for(int j = 0; j < board[0].length; j++){
                //System.out.print(board[i][j] + " ");
                if(board[i][j] == word.charAt(0)){
                    i0 = i;
                    j0 = j;
                    //System.out.println("主函数:" + i0 + " " + j0);
                    if(recur(board, i0, j0, word, 0, flag) == true){
                        return true;
                    }
                }
            }
            //System.out.println();
        }

        return false;

    }

    public boolean recur(char[][] board, int i, int j, String word, int index, boolean[][] flag){
        boolean res;

        if(index >= word.length()){
            return true;
        }
        
        if(i >= 0 && i < board.length && j >= 0 && j < board[0].length && flag[i][j] == false && board[i][j] == word.charAt(index)){
            //System.out.println("位置:" + i + " " +  j + " " + board[i][j]);
            flag[i][j] = true;
            //showFlag(flag);
            res = recur(board, i-1, j, word, index+1, flag) || recur(board, i, j-1, word, index+1, flag) || recur(board, i+1, j, word, index+1, flag) || recur(board, i, j+1, word, index+1, flag); //上左下右
            flag[i][j] = false;   //递归完一定要把flag变回去,因为每次递归用的都是同一个flag
            return res;

        }else{
            return false;
        }
    }

    public void showFlag(boolean[][] flag){
        for(int i = 0; i < flag.length; i++){
            for(int j = 0; j < flag[0].length; j++){
                System.out.print((flag[i][j] == true ? 1 :0) + " ");                
            }
            System.out.println();
        }
    }
}

dfs代码改进 加了全局变量

/*
本问题是典型的矩阵搜索问题,可使用 深度优先搜索(DFS)+ 剪枝 解决。
*/

class Solution {
    private char[][] board;
    private String word;
    private int rows;
    private int cols;
    private boolean[][] marked;
    
    public boolean exist(char[][] board, String word) {
        this.board = board;  //两个变量名字一样,所以要加this
        this.word = word;
        rows = board.length;  //只有一个,不用加this
        cols = board[0].length;
        marked = new boolean[rows][cols];

        if(rows == 0) return false;
        
        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                if(dfs(i, j, 0) == true){  //第一个位置也可以在递归里进行判定
                    return true;
                }               
            }
        }
        return false;
    }

    public boolean dfs(int i, int j, int index){  //index表示String的下标
        if(index >= word.length())  return true;    
        if(i < 0 || i >= rows || j < 0 || j >= cols || marked[i][j] == true || board[i][j] != word.charAt(index)) return false;
             
        marked[i][j] = true;
        boolean res = dfs(i-1, j, index+1) || dfs(i, j-1, index+1) || dfs(i+1, j, index+1) || dfs(i, j+1, index+1); //上左下右
        marked[i][j] = false;   //递归完一定要把flag变回去,因为每次递归用的都是同一个flag
        return res;       
    }
}

dfs 没有用标记数组

class Solution {
    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;
    }
    boolean dfs(char[][] board, char[] word, int i, int j, int k) {
        if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
        if(k == word.length - 1) return true;
        char tmp = board[i][j];
        board[i][j] = '/';
        boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || 
                      dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
        board[i][j] = tmp;
        return res;
    }
}

// 作者:jyd
// 链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/solution/mian-shi-ti-12-ju-zhen-zhong-de-lu-jing-shen-du-yo/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

dfs 用了偏移量数组

class Solution {
    private char[][] board;
    private String word;
    private int rows;
    private int cols;
    private boolean[][] marked;
    private int[][] direction = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}}; //上左下右
    //        x-1,y
    // x,y-1  x,y    x,y+1
    //        x+1,y
    
    public boolean exist(char[][] board, String word) {
        this.board = board;  //两个变量名字一样,所以要加this
        this.word = word;
        rows = board.length;  //只有一个,不用加this
        cols = board[0].length;
        marked = new boolean[rows][cols];

        if(rows == 0) return false;
        
        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                if(dfs(i, j, 0) == true){  //第一个位置也可以在递归里进行判定
                    return true;
                }               
            }
        }
        return false;
    }

    public boolean dfs(int i, int j, int index){  //index表示String的下标
        if(index >= word.length())  return true;    
        if(i < 0 || i >= rows || j < 0 || j >= cols || marked[i][j] == true || board[i][j] != word.charAt(index)) return false;
             
        marked[i][j] = true;
        for(int k = 0; k < 4; k++){    
            if(dfs(i + direction[k][0], j + direction[k][1], index+1) == true){
                return true;
            }
        }  
        //boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || 
                      dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);   
                      //之前是用或的方式递归的,现在用偏移量数组     
        marked[i][j] = false;   //递归完一定要把flag变回去,因为每次递归用的都是同一个flag
        return false;       
    }
}

200. 岛屿数量

anan dfs

/*
安安理解:如何判定这是一个岛屿?
这片区域都是1,这片区域的外围都是0
1的话就一直递归,0的话就停止
*/

class Solution {
    private char[][] grid;
    private int rows;
    private int cols;
    private boolean[][] marked;
    private int count;
    private boolean flag;

    public int numIslands(char[][] grid) {
        this.grid = grid;
        rows = grid.length;
        if(rows == 0) return 0;   //这个判断必须要在此处,如果放到后面就会出错
        cols = grid[0].length;
        marked = new boolean[rows][cols];
        count = 0;   //统计岛屿的个数
        flag = false;

        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                dfs(i, j);
                if(flag == true){
                    count++;
                    //System.out.println("count:" + count);
                    flag = false;
                }
            }
        }
        return count;
    }

    public boolean dfs(int i, int j){
        if(i<0 || i>=rows || j<0 || j>=cols || marked[i][j]==true || grid[i][j]=='0') return false;
        //System.out.println(i + " " + j + " " + grid[i][j]);

        marked[i][j] = true;  //标记过后,不再擦掉。因此标记了就说明该位置已经和其他地方连成一片了
        boolean res = dfs(i-1,j) || dfs(i,j-1) || dfs(i+1,j) || dfs(i,j+1);
        //当最后4个人方向全部为false的时候,说明已经形成一个岛屿了
        //为什么不直接在这count++?因为这样回溯的时候会使count累加次数过多
        if(res == false){   
            flag = true;           
        } 
        return res;
    }
}

dfs 改进

/*
我们可以将二维网格看成一个无向图,竖直或水平相邻的 11 之间有边相连。

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 11,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 11 都会被重新标记为 00。

最终岛屿的数量就是我们进行深度优先搜索的次数。

作者:LeetCode
链接:https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/

class Solution {
    private char[][] grid;
    private int rows;
    private int cols;

    public int numIslands(char[][] grid) {
        this.grid = grid;
        rows = grid.length;
        if(rows == 0 || grid == null) return 0;
        cols = grid[0].length;
        int count; = 0;   //统计岛屿的个数

        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                if(grid[i][j] == '1'){
                    dfs(i, j);
                    count++;
                }
            }
        }
        return count;
    }

    public void dfs(int i, int j){  //我们看到,最后判断岛屿数量并不需要dfs的回溯结果,所以设置为void
        if(i<0 || i>=rows || j<0 || j>=cols || grid[i][j]=='0') return;
        
        grid[i][j] = '0';  //此处不必再加marked数组

        dfs(i-1,j);
        dfs(i,j-1);
        dfs(i+1,j);
        dfs(i,j+1);        
    }
}

DFS和BFS

岛屿类问题的通用解法、DFS 遍历框架

DFS

class Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        for(int i = 0; i < grid.length; i++) {
            for(int j = 0; j < grid[0].length; j++) {
                if(grid[i][j] == '1'){
                    dfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }
    private void dfs(char[][] grid, int i, int j){
        if(i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') return;
        grid[i][j] = '0';  //这个地方最好不要用0
        //在一些题解中,可能会把「已遍历过的陆地格子」标记为和海洋格子一样的 0,美其名曰「陆地沉没方法」,即遍历完一个陆地格子就让陆地「沉没」为海洋。这种方法看似很巧妙,但实际上有很大隐患,因为这样我们就无法区分「海洋格子」和「已遍历过的陆地格子」了。如果题目更复杂一点,这很容易出 bug。

        dfs(grid, i + 1, j);
        dfs(grid, i, j + 1);
        dfs(grid, i - 1, j);
        dfs(grid, i, j - 1);
    }
}

作者:jyd
链接:https://leetcode-cn.com/problems/number-of-islands/solution/number-of-islands-shen-du-you-xian-bian-li-dfs-or-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

BFS

lass Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        for(int i = 0; i < grid.length; i++) {
            for(int j = 0; j < grid[0].length; j++) {
                if(grid[i][j] == '1'){
                    bfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }
    private void bfs(char[][] grid, int i, int j){
        Queue<int[]> list = new LinkedList<>();
        list.add(new int[] { i, j });
        while(!list.isEmpty()){
            int[] cur = list.remove();
            i = cur[0]; j = cur[1];
            if(0 <= i && i < grid.length && 0 <= j && j < grid[0].length && grid[i][j] == '1') {
                grid[i][j] = '0';
                list.add(new int[] { i + 1, j });
                list.add(new int[] { i - 1, j });
                list.add(new int[] { i, j + 1 });
                list.add(new int[] { i, j - 1 });
            }
        }
    }
}

作者:jyd
链接:https://leetcode-cn.com/problems/number-of-islands/solution/number-of-islands-shen-du-you-xian-bian-li-dfs-or-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

695. 岛屿的最大面积

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int res = 0;

        for(int i = 0; i < grid.length; i++){
            for(int j = 0; j< grid[0].length; j++){
                if(grid[i][j] == 1){  //开始遍历
                    res = Math.max(res, dfs(grid, i, j));
                }               
            }
        }
        return res;
    }

    public int dfs(int[][] grid, int i, int j){
        if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] != 1)   return 0;

        grid[i][j] = 2;
        return 1 + dfs(grid, i-1, j) + dfs(grid, i+1, j) + dfs(grid, i, j-1) + dfs(grid, i, j+1);
    }
}

463. 岛屿的周长

//题目说明 只有一个岛屿

class Solution {
    public int islandPerimeter(int[][] grid) {
        int res = 0;

        for(int i = 0; i < grid.length; i++){
            for(int j = 0; j< grid[0].length; j++){
                if(grid[i][j] == 1){  //开始遍历
                    res = dfs(grid, i, j);
                }               
            }
        }
        return res;
    }

    public int dfs(int[][] grid, int i, int j){
        if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == 0)   return 1;
        if(grid[i][j] == 2)  return 0;
        grid[i][j] = 2;
        return dfs(grid, i-1, j) + dfs(grid, i+1, j) + dfs(grid, i, j-1) + dfs(grid, i, j+1);
    }
}

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

anan dfs

class Solution {
    boolean[][] marked;
    public int movingCount(int m, int n, int k) {
        if(m == 0 || n == 0) return 0;
        marked = new boolean[m][n];
        return dfs(m,n,0,0,k);
        
    }

    public int dfs(int m, int n, int i, int j, int k){
        if(i < 0 || i >= m || j < 0 || j >= n || (sum(i)+sum(j)) > k || marked[i][j] == true) return 0;
        marked[i][j] = true;
        return 1 + dfs(m,n,i-1,j,k) + dfs(m,n,i,j-1,k) + dfs(m,n,i+1,j,k) + dfs(m,n,i,j+1,k);
    }

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

dfs改进:考虑两个方向就可以了 向下和向右

class Solution {
    boolean[][] marked;  //marked可以换为visited,更形象
    public int movingCount(int m, int n, int k) {
        if(m == 0 || n == 0) return 0;
        marked = new boolean[m][n];
        return dfs(m,n,0,0,k);
        
    }

    public int dfs(int m, int n, int i, int j, int k){
        if(i < 0 || i >= m || j < 0 || j >= n || (sum(i)+sum(j)) > k || marked[i][j] == true) return 0;
        marked[i][j] = true;
        return 1 + dfs(m,n,i+1,j,k) + dfs(m,n,i,j+1,k);
    }

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

bfs

class Solution {
    private boolean[][] marked;
    private int res;
    
    public int movingCount(int m, int n, int k) {
        if(m == 0 || n == 0) return 0;
        marked = new boolean[m][n];
        res = 0;

        bfs(m,n,0,0,k);
        return res;        
    }

    public void bfs(int m, int n, int i, int j, int k){
        Queue<int[]> list = new LinkedList<>();

        list.add(new int[] {0, 0});
        while(!list.isEmpty()){
            int[] cur = list.remove();
            i = cur[0];
            j = cur[1];

            if(i < 0 || i >= m || j < 0 || j >= n || (sum(i)+sum(j)) > k || marked[i][j] == true) continue;
            marked[i][j] = true;
            res++;
            list.add(new int[] {i+1, j});
            list.add(new int[] {i, j+1});
        }              
    }

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

剑指 Offer 14- I. 剪绳子

anan 递归 超时 超过了41就通过不了了

class Solution {
    private int max;
    public int cuttingRope(int n) {
        max = 0;
        if(n < 2) return n;

        for(int i = 2; i <= n; i++){ //长度为n的绳子,可以剪为2、3、...、n段
            dfs(n, i, 1);
        }
        return max;
    }

    //功能:把长度为n的绳子剪为m段
    public void dfs(int n, int m, int mul){
        //System.out.println("mul:" + mul + " max:" + max);
        if(m == 1){
            max = Math.max(max, mul*n);
            return;
        } 
        
        for(int i = 1; i <= n/m; i++){                     
            dfs(n-i, m-1, mul*i);
            max = Math.max(max, mul*i);
        }
    }
}

动态规划

class Solution {
    public int cuttingRope(int n) {
        int[] dp = new int[n+1];
        if(n < 2) return n;
        dp[1] = 1;

        for(int i = 2; i <= n; i++){
            int curMax = 0;
            for(int j = 1; j < i; j++){
                curMax = Math.max(curMax, Math.max(j*dp[i-j], j*(i-j)));   //因为拆的时候不能拆成1段,但是后面算的时候是可以把前面的看成一个整体的
            }
            dp[i] = curMax;
        }
        return dp[n];
    }


}

剑指 Offer 14- II. 剪绳子 II

贪心思想 + 快速幂求余

传送门

class Solution {
    public int cuttingRope(int n) {
        if(n <= 3) return n - 1;
        int b = n % 3, p = 1000000007;
        long rem = 1, x = 3;
        for(int a = n / 3 - 1; a > 0; a /= 2) {
            if(a % 2 == 1) rem = (rem * x) % p;
            x = (x * x) % p;
        }
        if(b == 0) return (int)(rem * 3 % p);
        if(b == 1) return (int)(rem * 4 % p);
        return (int)(rem * 6 % p);
    }
}

作者:jyd
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/mian-shi-ti-14-ii-jian-sheng-zi-iitan-xin-er-fen-f/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

BigInteger

传送门

import java.math.BigInteger;

class Solution {
    public int cuttingRope(int n) {
        if(n<2)
            return 0;
        if(n==2)
            return 1;
        if(n==3)
            return 2;
        /*
        d[i]表示长度为i的绳子剪完后各段乘积的最大值, 最终目标是dp[n]
        dp[i]可以看成是长度为i-k的绳子的最大值和长度为k的绳子的最大值的乘积, 子问题最优, 所以dp[i]也是最优
        状态转移方程: dp[i] = max(dp[i], dp[i-k]*dp[k])
        */
        //下面的初始值不同于上面的特殊情况, 上面是必须剪一刀, 下面的三个初始值不用再减了
        BigInteger[] dp = new BigInteger[n+1];
        dp[1] = new BigInteger("1");//内循环中会用到这个值
        dp[2] = new BigInteger("2");
        dp[3] = new BigInteger("3");
        for(int i=4; i<=n; i++){
            //初始化dp[i]
            dp[i] = new BigInteger("0");
            //长度为i的绳子有i-1个剪切位置; 不论i是奇数还是偶数, 只考虑前i/2个剪切位置即可, 后面的剪切位置是重复的
            for(int j=1; j<=i/2; j++){
                //因为j和i-j都小于i, 所以这是自底向上的计算方式
                dp[i] = dp[i].max(dp[j].multiply(dp[i-j]));
            }
        }
        return dp[n].mod(new BigInteger("1000000007")).intValue();
    }
}

作者:littlehaes
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/java-dong-tai-gui-hua-ji-bai-100-he-jian-sheng-zi-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安安csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值