我是靠谱客的博主 冷静鸵鸟,最近开发中收集的这篇文章主要介绍剑指offer简单(java)上第1天 栈与队列(简单)第 2 天 链表(简单)第 3 天 字符串(简单)第 4 天 查找算法(简单)第 5 天 查找算法(中等)第 6 天 搜索与回溯算法(简单)(广搜)第 7 天 搜索与回溯算法(简单)(深搜)第 8 天 动态规划(简单)第 9 天 动态规划(中等)第 10 天 动态规划(中等)第 11 天 双指针(简单)第 12 天 双指针(简单)第 13 天 双指针(简单)第 14 天 搜索与回溯算法(中等)第 15 天 搜索与回溯算法(中等),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

剑指offer-leetcode(Java)

  • 第1天 栈与队列(简单)
    • 剑指 Offer 09. 用两个栈实现队列
    • 剑指 Offer 30. 包含min函数的栈(没思路)
  • 第 2 天 链表(简单)
    • 剑指 Offer 06. 从尾到头打印链表
    • 剑指 Offer 24. 反转链表
    • [M] 剑指 Offer 35. 复杂链表的复制
  • 第 3 天 字符串(简单)
    • 剑指 Offer 05. 替换空格
    • 剑指 Offer 58 - II. 左旋转字符串
  • 第 4 天 查找算法(简单)
    • 剑指 Offer 03. 数组中重复的数字
    • 剑指 Offer 53 - I. 在排序数组中查找数字 I
    • 剑指 Offer 53 - II. 0~n-1中缺失的数字
  • 第 5 天 查找算法(中等)
    • [M] 剑指 Offer 04. 二维数组中的查找
    • [不会] 剑指 Offer 11. 旋转数组的最小数字
    • 剑指 Offer 50. 第一个只出现一次的字符
  • 第 6 天 搜索与回溯算法(简单)(广搜)
    • 剑指 Offer 32 - I. 从上到下打印二叉树
    • [注意泛型使用] 剑指 Offer 32 - II. 从上到下打印二叉树 II
    • [M] 剑指 Offer 32 - III. 从上到下打印二叉树 III
  • 第 7 天 搜索与回溯算法(简单)(深搜)
    • [M] 剑指 Offer 26. 树的子结构
    • 剑指 Offer 27. 二叉树的镜像
    • [没想到] 剑指 Offer 28. 对称的二叉树
  • 第 8 天 动态规划(简单)
    • 剑指 Offer 10- I. 斐波那契数列
    • 剑指 Offer 10- II. 青蛙跳台阶问题
    • [M] 剑指 Offer 63. 股票的最大利润
      • [E] 121. 买卖股票的最佳时机(同上)
      • [M] 122. 买卖股票的最佳时机 II
      • [H] 123. 买卖股票的最佳时机 III
      • [H] 188. 买卖股票的最佳时机 IV
      • [M] 309. 最佳买卖股票时机含冷冻期
      • [M] 714. 买卖股票的最佳时机含手续费
  • 第 9 天 动态规划(中等)
    • 剑指 Offer 42. 连续子数组的最大和
    • 剑指 Offer 47. 礼物的最大价值
  • 第 10 天 动态规划(中等)
    • [M] 剑指 Offer 46. 把数字翻译成字符串
    • [M] 剑指 Offer 48. 最长不含重复字符的子字符串
  • 第 11 天 双指针(简单)
    • 剑指 Offer 18. 删除链表的节点
    • 剑指 Offer 22. 链表中倒数第k个节点
  • 第 12 天 双指针(简单)
    • [不会] 剑指 Offer 25. 合并两个排序的链表
    • 剑指 Offer 52. 两个链表的第一个公共节点
  • 第 13 天 双指针(简单)
    • 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
    • 剑指 Offer 57. 和为s的两个数字
    • 剑指 Offer 58 - I. 翻转单词顺序
  • 第 14 天 搜索与回溯算法(中等)
    • [M] 剑指 Offer 12. 矩阵中的路径
    • [M]剑指 Offer 13. 机器人的运动范围
  • 第 15 天 搜索与回溯算法(中等)
    • [M] 剑指 Offer 34. 二叉树中和为某一值的路径
    • [不会][M] 剑指 Offer 36. 二叉搜索树与双向链表
    • 剑指 Offer 54. 二叉搜索树的第k大节点

第1天 栈与队列(简单)

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

剑指 Offer 09. 用两个栈实现队列
方法一:一个栈从底部是头,另一个栈底部是尾,要添加时把所有信息放到第一个然后加,要删除时把所有信息移到第二个里再删。

class CQueue {

    //bottom is head
    private Stack<Integer> s1;
    //bottom is tail
    private Stack<Integer> s2;
    // current stack
    private Stack<Integer> cs;

    public CQueue() {
        s1 = new Stack<Integer>();
        s2 = new Stack<Integer>();
        cs = s2;
    }
    
    public void appendTail(int value) {
        if (cs != s1) {
            change(s2,s1);
            cs = s1;
        }
        cs.push(value);
    }
    
    public int deleteHead() {
        if (cs != s2) {
            change(s1,s2);
            cs = s2;
        } else {
            // 之前cs是s2表明刚添加过,则队列不可能是空
            if (cs.isEmpty()) {
                return -1;
            }
        }
        return cs.pop();
        
    }

    private void change(Stack<Integer> from, Stack<Integer> to) {
        while(!from.isEmpty()){
            Integer a = from.pop();
            to.push(a);
        }
    }
}

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

方法二:方法一移动操作占用大量时间,题目里只需要添加和删除,所以没有必要把所有信息存一个栈。第一个栈存添加的,第二个栈存删除的。栈是先进先出,而队列是先进后出。当添加时直接加第一个里,删除时先看第二个里有没有,若没有则把所有元素移到第二个再删,如果有则说明上次移动过去的还没删完,直接删。

class CQueue {

    //add stack
    private Stack<Integer> s1;
    //delete stack
    private Stack<Integer> s2;

    public CQueue() {
        s1 = new Stack<Integer>();
        s2 = new Stack<Integer>();
    }
    
    public void appendTail(int value) {
        s1.push(value);
    }
    
    public int deleteHead() {
        if (s2.isEmpty()) {
            if (s1.isEmpty()) {
                return -1;
            } else {
                while(!s1.isEmpty()){
                    s2.push(s1.pop());
                }
                return s2.pop();
            }
        } else {
            return s2.pop();
        }
    }
}

剑指 Offer 30. 包含min函数的栈(没思路)

剑指 Offer 30. 包含min函数的栈
重点是辅助栈使min方法O(1)。非严格降序是大于等于。
在这里插入图片描述

class MinStack {

    //main stack
    Stack<Integer> ms;
    //assist stack
    Stack<Integer> as;

    /** initialize your data structure here. */
    public MinStack() {
        ms = new Stack<Integer>();
        as = new Stack<Integer>();
    }
    
    public void push(int x) {
        ms.push(x);
        if (as.isEmpty() || as.peek()>=x) {
            as.push(x);
        }
    }
    
    public void pop() {
        if(!ms.isEmpty()){
            int a = ms.pop();
            if (a == as.peek()) {
                as.pop();
            }
        }
    }
    
    public int top() {
        return ms.peek();
    }
    
    public int min() {
        return as.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

第 2 天 链表(简单)

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

剑指 Offer 06. 从尾到头打印链表
选择反转链表后直接输出,也可以用栈来辅助。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        int count = 0;
        ListNode a = head;
        while (a != null) {
            count += 1;
            a = a.next;
        }

        head = reverse(head);
        int[] ans = new int[count];
        for (int i=0; i<count; i++) {
            ans[i] = head.val;
            head = head.next;
        }
        return ans;
    }

    private ListNode reverse(ListNode root) {
        if (root == null) return null;
        ListNode a=root,b,c;
        b = a.next;
        if (b == null) return a;
        c = b.next;
        a.next = null;

        while (c != null) {
            b.next = a;
            a = b; b = c; c = b.next;
        }
        b.next = a;
        return b;
    }
}

剑指 Offer 24. 反转链表

剑指 Offer 24. 反转链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode root) {
        if (root == null) return null;
        ListNode a=root,b,c;
        b = a.next;
        if (b == null) return a;
        c = b.next;
        a.next = null;

        while (c != null) {
            b.next = a;
            a = b; b = c; c = b.next;
        }
        b.next = a;
        return b;
    }
}

[M] 剑指 Offer 35. 复杂链表的复制

剑指 Offer 35. 复杂链表的复制
方法一:遍历两边,第一次是正常的顺序,同时用哈希表记录两次对应的地址,第二遍赋值random指针。时间O(n),空间O(n)。

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) return null;

        HashMap<Node,Node> cor = new HashMap<Node,Node>();
        Node newHead = new Node(0);
        Node newCur = newHead, cur = head;
        while (cur != null) {
            newCur.val = cur.val;
            cor.put(cur, newCur);
            cur = cur.next;
            if(cur == null) {
                break;
            }
            newCur.next = new Node(0);
            newCur = newCur.next;
        }

        cur = head;
        Node ran,from,to;
        while (cur != null) {
            ran = cur.random;
            from = cor.get(cur);
            to = cor.get(ran);
            from.random = to;
            cur = cur.next;
        }
        return newHead;
    }
}

方法二:简化空间复杂度,第一步复制各节点然后重组,第二步更新random指针,第三步拆分两个链表。时间O(n),空间O(1)。

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) return null;
        Node cur = head;
        Node temp;
        // 1.create new Linked List
        while (cur!=null) {
            temp = cur.next;
            cur.next = new Node(cur.val);
            cur.next.next = temp;
            cur = temp;
        }
        // 2.change the random point of new list
        // random point :cur -> cur.random   &   cur.next -> cur.random.next
        cur = head;
        while(cur!=null){
            if(cur.random!=null) {
                cur.next.random = cur.random.next;
            } else {
                cur.next.random = null;
            }
            cur = cur.next.next;
        }
        // 3.separate two list
        cur = head;
        Node ans = cur.next;
        while(cur.next!=null){
            temp = cur.next; // the next Node
            cur.next = temp.next;
            cur = temp;
        }
        return ans;
    }
}

第 3 天 字符串(简单)

剑指 Offer 05. 替换空格

剑指 Offer 05. 替换空格
先设置一个三倍的数组,之后遇到空格连续赋值为%20。

剑指 Offer 58 - II. 左旋转字符串

剑指 Offer 58 - II. 左旋转字符串
java中string为不可变对象,但是在c++中可以使用原地替换使空间复杂度降到O(1)。

// abcdefg k=2
reverse_string(s, 0, length-1); // gfedcba
reverse_string(s, 0, length-n-1); // cdefgba
reverse_string(s, length-n, length-1); // cdefgab

第 4 天 查找算法(简单)

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

剑指 Offer 03. 数组中重复的数字
方法一:由于说了数字范围不会超过length,因此可以把对应的数字交换到相同索引上,第二次遇到直接说明是重复的。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int len = nums.length;
        for(int i=0;i<len;i++) {
            int here = nums[i];
            if(here!=i){
                if(nums[here]==here){
                    //the second time a num occurs
                    return here;
                }
                // swap nums[i] and nums[here]
                int temp = nums[here];
                nums[here] = here;
                nums[i] = temp;
                i -= 1;
            }
        }
        //impossible
        return 0;
    }
}

剑指 Offer 53 - I. 在排序数组中查找数字 I

剑指 Offer 53 - I. 在排序数组中查找数字 I
方法一:二分找到后,从中间出发向两边一个一个找,最差情况下又成O(n)。

class Solution {
    public int search(int[] nums, int target) {
        int len = nums.length;
        int left=0, right=len-1, mid=(left+right)/2;
        int index=-1;

        while(left<=right){
            mid=(left+right)/2;
            if(nums[mid]==target){
                index=mid;
                break;
            }else if(nums[mid]<target){
                left=mid+1;
            }else{
                right=mid-1;
            }
        }
        if(index==-1){
            return 0;
        }else{
            int count = 1;
            //the worst condition is all nums equal target 
            for(int i=index-1;i>=0&&nums[i]==target;i--){
                count += 1;
            }
            for(int i=index+1;i<len&&nums[i]==target;i++){
                count += 1;
            }
            return count;
        }
    }
}

方法二:利用二分找左右边界,然后相减得到答案。要注意左边界是right-1。

class Solution {
    public int search(int[] nums, int target) {
        int left = binarySearch(nums,target,true);
        int right = binarySearch(nums,target,false);
        return right-left-1;
    }

    // special binary search
    // lower is true if want to find left bound
    private int binarySearch(int[] nums, int target, boolean lower){
        int len = nums.length;
        int left=0, right=len-1;
        while(left<=right){
            int mid = (left+right)/2;
            //nums[mid]<=target if right bound
            //有点像电子导论里的逻辑电路A!B+!AB二选一,正常二分如果等于就直接返回
            if((nums[mid]<target && lower) || (nums[mid]<=target && !lower)){
                left = mid+1;
            }else{
                right = mid-1;
            }
        }
        //左边界一直会找到right=left-1
        if(lower) return right;
        return left;
    }
}

剑指 Offer 53 - II. 0~n-1中缺失的数字

剑指 Offer 53 - II. 0~n-1中缺失的数字
简单二分。

class Solution {
    public int missingNumber(int[] nums) {
        int len = nums.length;
        int left=0, right=len-1;
        while(left<=right){
            int mid=(left+right)/2;
            if(nums[mid]>mid){
                right=mid-1;
            }else{
                left=mid+1;
            }
        }
        return left;
    }
}

第 5 天 查找算法(中等)

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

剑指 Offer 04. 二维数组中的查找
通过20那个例子来看就找到规律了,两次二分即可。

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        //先横着找第一个比他小的,再竖着找第一个比他大的,再横着找第一个比他小的,一直找到最后一行,一直锁定左下角
        if(matrix.length==0) return false;
        int width=matrix[0].length, height=matrix.length;
        int left=0,up=0,right=width-1,down=height-1; // right一直减小和up一直增大,因此结束条件是最后一行还找不到,此时up会加1大于down,这是列出界,还有第一个数就大于target,此时right就会为-1,这是行出界,就返回false
        while(up<=down && left<=right){
            while(left<=right){
                int mid=(left+right)/2;
                if(matrix[up][mid]==target) return true;
                else if(matrix[up][mid]<target) left = mid+1;
                else right = mid-1;
            }
            left=0;
            while(up<=down && left<=right){ // 对应行出界
                int mid=(up+down)/2;
                if(matrix[mid][right]==target) return true;
                else if(matrix[mid][right]<target) up = mid+1;
                else down = mid-1;
            }
            down=height-1;
        }
        return false;
    }
}

[不会] 剑指 Offer 11. 旋转数组的最小数字

剑指 Offer 11. 旋转数组的最小数字
如果直接和left相比,left不确定在左还是右,而right确定是在右排序数组的最右。
因此一值和right相比,如果小于right说明mid在右边,如果大于right说明mid在左边,如果等于right则不确定在哪。而且由于重复元素的存在[10,10,10,1,10],[10,1,10,10,10],因此只能right-1来线性更新right。
官方题解有图解释

class Solution {
    public int minArray(int[] numbers) {
        int len=numbers.length;
        int left=0,right=len-1;
        // 不加等于最后结束在else if,加了结束在else
        while(left<right){
            int mid = (left+right)/2;
            // 小于的话不能减一,[3,1,2],若mid为1,则mid-1到了左边
            if(numbers[mid]<numbers[right]) right=mid;
            else if(numbers[mid]>numbers[right]) left=mid+1;
            else {
                right -= 1;
            }
        }
        return numbers[left];
    }
}

剑指 Offer 50. 第一个只出现一次的字符

剑指 Offer 50. 第一个只出现一次的字符
两个数组,一个记录次数,一个记录顺序。用hash有更好的?没看。

class Solution {
    public char firstUniqChar(String ss) {
        int len = ss.length();
        char[] s = ss.toCharArray();
        // 记录出现顺序
        char[] order = new char[26];
        int size = 0;
        // 记录出现次数
        int[] count = new int[26];

        for(int i=0;i<len;i++){
            int index = c2i(s[i]);
            if(count[index]==0){
                order[size] = s[i];
                size += 1;
            }
            count[index] += 1;
        }

        for(int i=0;i<size;i++){
            char ans = order[i];
            if(count[c2i(ans)]==1) return ans;
        }
        return ' ';
    }

    private int c2i(char c){
        return c-'a';
    }
}

第 6 天 搜索与回溯算法(简单)(广搜)

剑指 Offer 32 - I. 从上到下打印二叉树

剑指 Offer 32 - I. 从上到下打印二叉树
层次遍历bfs简单用queue。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] levelOrder(TreeNode root) {
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        ArrayList<Integer> ans = new ArrayList<Integer>();
        if(root!=null) q.offer(root);
        while(!q.isEmpty()){
            TreeNode node = q.poll();
            ans.add(node.val);
            if(node.left!=null) q.offer(node.left);
            if(node.right!=null) q.offer(node.right);
        }

        int[] res = new int[ans.size()];
        for(int i = 0; i < ans.size(); i++)
            res[i] = ans.get(i);
        return res;
    }
}

[注意泛型使用] 剑指 Offer 32 - II. 从上到下打印二叉树 II

剑指 Offer 32 - II. 从上到下打印二叉树 II

incompatible types: ArrayList<ArrayList> cannot be converted to List<List>
这个错误出现在我试图用一个 ArrayList<ArrayList>() new 一个 List<List> 对象的时候,把第二个 ArrayList 改成 List ,错误就没有了,那么原理是什么呢,是因为他俩是并列的,类似double和int相对与number是同级的。

来自一个详细解释(泛型)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        ArrayList<List<Integer>> ans = new ArrayList<List<Integer>>();
        ArrayList<Integer> part = new ArrayList<Integer>();
        // 由于最后循环外加了一次part,所以成了二维[[]],所以在这返回
        if(root==null) return ans;
        TreeNode flag = new TreeNode(0);
        flag.left = flag;
        q.offer(root);
        q.offer(flag);
        // 判断应该是大小为1,即只有分隔符
        while(q.size()>1){
            TreeNode node = q.poll();
            if(node.left == node){
                q.offer(flag);
                ans.add(part);
                part = new ArrayList<Integer>();
                continue;
            }
            part.add(node.val);
            if(node.left!=null) q.offer(node.left);
            if(node.right!=null) q.offer(node.right);
        }
        // 最后一层在循环里没加进去
        ans.add(part);
        return ans;
    }
}

[M] 剑指 Offer 32 - III. 从上到下打印二叉树 III

剑指 Offer 32 - III. 从上到下打印二叉树 III
简单加了个reverse函数?

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        ArrayList<List<Integer>> ans = new ArrayList<List<Integer>>();
        ArrayList<Integer> part = new ArrayList<Integer>();
        if(root==null) return ans;
        TreeNode flag = new TreeNode(0);
        flag.left = flag;
        q.add(root);
        q.add(flag);
        int count = 0;
        while(q.size()>1){
            TreeNode node = q.poll();
            if(node.left == node){
                q.add(flag);
                if(count==1) part = reverse(part);
                count = (count+1)%2;
                ans.add(part);
                part = new ArrayList<Integer>();
                continue;
            }
            part.add(node.val);
            if(node.left!=null) q.add(node.left);
            if(node.right!=null) q.add(node.right);
        }
        if(count==1) part = reverse(part);
        ans.add(part);
        return ans;
    }

    private ArrayList<Integer> reverse(ArrayList<Integer> list){
        int len = list.size();
        int left=0,right=len-1;
        while(left<right){
            Integer temp = list.get(right);
            list.set(right, list.get(left));
            list.set(left, temp);
            left += 1;
            right -= 1;
        }
        return list;
    }
}

第 7 天 搜索与回溯算法(简单)(深搜)

[M] 剑指 Offer 26. 树的子结构

剑指 Offer 26. 树的子结构
主体遍历,下部分递归,注意A或B为空时返回true还是false。许多if可以简化到一句。
未简化版

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        // 直接用前中后遍历的字符串也可以判断? 不行,A的东西比B多,可能穿插
        // 首先遍历A这个树
        if(A==null || B==null) return false;
        if(A!=null && B!=null && A.val==B.val){
            boolean a = isSameStructure(A,B);
            if(a) return true;
        }
        boolean b = isSubStructure(A.left,B);
        boolean c = isSubStructure(A.right,B);
        if(b||c) return true;
        else return false;
    }

    // root is the same
    private boolean isSameStructure(TreeNode A, TreeNode B) {
        // 跟着B走
        if(A==null && B!=null) return false;
        if(B==null) return true;
        if(A.val!=B.val) return false;
        else return isSameStructure(A.left, B.left) && isSameStructure(A.right, B.right);
    }
}

简化版

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        return (A!=null && B!=null) && (isSameStructure(A,B) || 
            isSubStructure(A.left,B) || isSubStructure(A.right,B));
    }

    private boolean isSameStructure(TreeNode A, TreeNode B) {
        if(B==null) return true;
        if(A==null || A.val!=B.val) return false;
        else return isSameStructure(A.left, B.left) && isSameStructure(A.right, B.right);
    }
}

剑指 Offer 27. 二叉树的镜像

剑指 Offer 27. 二叉树的镜像
简单swap。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        // 递归swap?
        if(root==null) return null;
        TreeNode node;
        node=root.right;
        root.right=root.left;
        root.left=node;
        mirrorTree(root.left);
        mirrorTree(root.right);
        return root;
    }
}

[没想到] 剑指 Offer 28. 对称的二叉树

剑指 Offer 28. 对称的二叉树
方法一:暴力复制反转再对比,空间复杂度太大。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        // 先做一个镜像再判断?
        TreeNode mirrorTree = mirrorTree(copy(root));
        return isSameTree(root,mirrorTree);
    }

    private TreeNode copy(TreeNode root) {
        if(root==null) return null;
        TreeNode newRoot = new TreeNode(root.val);
        newRoot.left = copy(root.left);
        newRoot.right = copy(root.right);
        return newRoot;
    }

    private TreeNode mirrorTree(TreeNode root) {
        if(root==null) return null;
        TreeNode node;
        node=root.right;
        root.right=root.left;
        root.left=node;
        mirrorTree(root.left);
        mirrorTree(root.right);
        return root;
    }

    private boolean isSameTree(TreeNode A, TreeNode B) {
        if(A==null && B==null) return true;
        return A!=null && B!=null && A.val==B.val &&
            isSameTree(A.left,B.left) && isSameTree(A.right,B.right);
    }
}

方法二:从顶至下一对对看。下面看false和true的情况。

False:
1. only one of them is null
2. the values are not the same
3. only one point cross the leaf node
True:
1. cross the leaf node
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root==null) return true;
        return recur(root.left,root.right);
    }
    
    private boolean recur(TreeNode L, TreeNode R) {
        if(L==null && R==null) return true;
        if(L==null || R==null || L.val != R.val) return false;
        return recur(L.left,R.right) && recur(L.right,R.left);
    }
}

第 8 天 动态规划(简单)

剑指 Offer 10- I. 斐波那契数列

剑指 Offer 10- I. 斐波那契数列
简单dp。

class Solution {
    public final int MOD = 1000000007;
    public int fib(int n) {
        int[] F = new int[101];
        F[0] = 0;
        F[1] = 1;
        for(int i=2;i<=n;i++){
            F[i] = (F[i-1] + F[i-2])%MOD;
        }
        return F[n];
    }
}

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

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

dp[n] = dp[n-1]+dp[n-2];
dp[0] = 1, dp[1] = 1;

class Solution {
    public int MOD = 1000000007;
    public int numWays(int n) {
        int[] dp = new int[101];
        dp[0] = 1;
        dp[1] = 1;
        for(int i=2;i<=n;i++){
            dp[i] = (dp[i-1] + dp[i-2])%MOD;
        }
        return dp[n];
    }
}

[M] 剑指 Offer 63. 股票的最大利润

剑指 Offer 63. 股票的最大利润
方法一:左扫最小值,右扫最大值,相减得答案。遍历了三遍。

class Solution {
    public int maxProfit(int[] prices) {
        int num = prices.length;
        if(num==0) return 0;
        // start from left
        int[] min = new int[num];
        min[0] = prices[0];
        // start from right
        int[] max = new int[num];
        max[num-1] = prices[num-1];
        for(int i=1;i<num;i++){
            min[i] = min[i-1]<prices[i] ? min[i-1] : prices[i];
        }
        for(int i=num-2;i>=0;i--){
            max[i] = max[i+1]>prices[i] ? max[i+1] : prices[i];
        }
        int ans = 0;
        for(int i=0;i<num;i++){
            int temp = max[i]-min[i];
            ans = temp>ans ? temp : ans;  
        }
        return ans;
    }
}

方法二:动态规划。dp[i]代表前i天最高利润。

dp[i] = max( dp[i-1], prices[i]-min(prices[0:i-1]) )
dp[0] = 0

优化:由于dp[i]只和dp[i-1]有关,因此可以用变量profit大题dp[i-1],又min(prices[0:i-1])也可以通过一个数cost来代替。

profit = max( profit, prices[i]-cost)
cost = min(cost, prices[i-1])
这里cost没有和prices[i]比是因为即使prices[i]-cost减下来是负数,也有一开始的profit=0代替掉。

class Solution {
    public int maxProfit(int[] prices) {
        int num = prices.length;
        if(num==0) return 0;
        int profit = 0;
        int cost = prices[0];
        for(int i=1;i<num;i++){
            profit = Math.max(profit, prices[i]-cost);
            cost = Math.min(cost, prices[i]);
        }
        return profit;
    }
}

[E] 121. 买卖股票的最佳时机(同上)

[121. 买卖股票的最佳时机(同上)]https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

[M] 122. 买卖股票的最佳时机 II

122. 买卖股票的最佳时机 II
多次买卖一支股票问题。
方法一:动态规划。buy代表有股票。

dp[day, buy] = max( dp[day-1,buy], dp[day-1, sell] - prices[day] )
dp[day, sell] = max( dp[day-1, sell], dp[day-1, buy] + prices[day] )

但发现day只与day-1有关,因此可以简化。

buy = max(oldbuy, oldsell - prices[day])
sell = max(oldsell, oldbuy + prices[day])

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        int buy = -prices[0], sell = 0;
        for(int day=0; day<len; day++){
            int oldbuy = buy, oldsell = sell;
            buy = Math.max(oldbuy, oldsell - prices[day]);
            sell = Math.max(oldsell, oldbuy + prices[day]);
        }
        return sell;
    }
}

方法二:画图就发现和哪天卖没关系,就是把所有上升段加起来,和同一天买卖不冲突。
上升段

class Solution {
    public int buy=0, sell=1;
    public int maxProfit(int[] prices) {
        int len = prices.length;
        int profit = 0;
        for(int i=1;i<len;i++){
            int minus = prices[i]-prices[i-1];
            if(minus>0) profit += minus;
        }
        return profit;
    }
}

[H] 123. 买卖股票的最佳时机 III

123. 买卖股票的最佳时机 III
方法一:和第二题基本一样,不过要注意设置第一次和第二次同时开始,这样第二次遇到第一次卖完时就会自动换到收益更高的方法。

dp[day][flag]代表前day天最多赚多少,flag可以为0,1,2,3,代表第一次买入、卖出,第二次买入和卖出
dp[day][0] = max(-prices[day], dp[day-1][0]);
dp[day][1] = max(dp[day-1][0] + prices[day], dp[day-1][1]);
dp[day][2] = max(dp[day-1][1] - prices[day], dp[day-1][2]);
dp[day][3] = max(dp[day-1][2] + prices[day], dp[day-1][3]);
dp[0][0] = -prices[0];
dp[2][0] = -prices[0];

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        int[][] dp = new int[len][4];
        dp[0][0] = -prices[0];
        // 防止第二轮比第一轮先开始,最多也是同时开始
        dp[0][2] = -prices[0]; 
        for(int day=1;day<len;day++){
            dp[day][0] = Math.max(-prices[day], dp[day-1][0]);
            dp[day][1] = Math.max(dp[day-1][0] + prices[day], dp[day-1][1]);
            dp[day][2] = Math.max(dp[day-1][1] - prices[day], dp[day-1][2]);
            dp[day][3] = Math.max(dp[day-1][2] + prices[day], dp[day-1][3]);
        }
        return dp[len-1][3];
    }
}

同理简化空间复杂度

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        int dp0=-prices[0],dp1=0,dp2=-prices[0],dp3=0;
        int odp0=dp0,odp1=dp1,odp2=dp2,odp3=dp3;
        for(int day=1;day<len;day++){
            odp0=dp0;odp1=dp1;odp2=dp2;odp3=dp3;
            dp0 = Math.max(-prices[day], odp0);
            dp1 = Math.max(odp0 + prices[day], odp1);
            dp2 = Math.max(odp1 - prices[day], odp2);
            dp3 = Math.max(odp2 + prices[day], odp3);
        }
        return dp3;
    }
}

[H] 188. 买卖股票的最佳时机 IV

188. 买卖股票的最佳时机 IV
一模一样的题

class Solution {
    public int maxProfit(int k, int[] prices) {
        int len = prices.length;
        if(len==0 || k==0) return 0;
        int[] dp = new int[2*k];
        int[] odp = new int[2*k];
        for(int i=0;i<k;i++){
            dp[i*2] = -prices[0];
        }
        for(int day=1;day<len;day++){
            for(int i=0;i<2*k;i++){
                odp[i] = dp[i];
            }
            dp[0] = Math.max(-prices[day], odp[0]);
            dp[1] = Math.max(odp[0] + prices[day], odp[1]);
            for(int i=1;i<k;i++){
                dp[2*i] = Math.max(odp[2*i-1] - prices[day], odp[2*i]);
                dp[2*i+1] = Math.max(odp[2*i] + prices[day], odp[2*i+1]);
            }
        }
        return dp[2*k-1];
    }
}

[M] 309. 最佳买卖股票时机含冷冻期

309. 最佳买卖股票时机含冷冻期
加冷冻期的状态即可。

buy->sell -->> buy->sell->cool
dp[day][0] = Math.max(dp[day-1][2] - prices[day], dp[day-1][0]);
dp[day][1] = Math.max(dp[day-1][0] + prices[day], dp[day-1][1]);
dp[day][2] = Math.max(dp[day-1][1], dp[day-1][2]);

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        int[][] dp = new int[len][3];
        dp[0][0] = -prices[0];
        for(int day=1;day<len;day++){
            dp[day][0] = Math.max(dp[day-1][2] - prices[day], dp[day-1][0]);
            dp[day][1] = Math.max(dp[day-1][0] + prices[day], dp[day-1][1]);
            dp[day][2] = Math.max(dp[day-1][1], dp[day-1][2]);
        }
        return dp[len-1][1];
    }
}

简化空间。

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        int dp0=0,dp1=0,dp2=0;
        int odp0=0,odp1=0,odp2=0;
        dp0 = -prices[0];
        for(int day=1;day<len;day++){
            odp0=dp0;odp1=dp1;odp2=dp2;
            dp0 = Math.max(odp2 - prices[day], odp0);
            dp1 = Math.max(odp0 + prices[day], odp1);
            dp2 = Math.max(odp1, odp2);
        }
        return dp1;
    }
}

[M] 714. 买卖股票的最佳时机含手续费

714. 买卖股票的最佳时机含手续费
和第二题一样,只是买入的时候price还要加上fee。

dp[day][0] = max( dp[day-1][1] - prices[i] - fee, dp[day-1][0] )
dp[day][1] = max( dp[day-1][0] + prices[0], dp[day-1][1] )

直接简化空间复杂度

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int len = prices.length;
        int dp0=-prices[0]-fee, dp1=0, odp0=dp0, odp1=dp1;
        for(int day=1;day<len;day++){
            odp0=dp0;
            odp1=dp1;
            dp0 = Math.max(odp0, odp1-prices[day]-fee);
            dp1 = Math.max(odp1, odp0+prices[day]);
        }
        return dp1;
    }
}

第 9 天 动态规划(中等)

剑指 Offer 42. 连续子数组的最大和

剑指 Offer 42. 连续子数组的最大和
方法一:二维dp,O(n),0代表这个不选

dp[i][1] = max (dp[i-1][1] + nums[i], nums[i])
dp[i][0] = max (dp[i-1][0], dp[i-1][1])

class Solution {
    public int maxSubArray(int[] nums) {
        int len = nums.length;
        int[][] dp = new int[len][2];
        dp[0][0] = nums[0];
        dp[0][1] = nums[0];
        for(int i=1;i<len;i++){
            dp[i][1] = Math.max(dp[i-1][1]+nums[i], nums[i]);
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]);
        }
        return Math.max(dp[len-1][0], dp[len-1][1]); 
    }
}

简化一下,时间就减少很多,应该是二维存取太慢。

class Solution {
    public int maxSubArray(int[] nums) {
        int len = nums.length;
        int dp0 = nums[0], dp1 = nums[0];
        for(int i=1;i<len;i++){
            int odp0 = dp0, odp1 = dp1;
            dp1 = Math.max(odp1+nums[i], nums[i]);
            dp0 = Math.max(odp0, odp1);
        }
        return Math.max(dp0,dp1); 
    }
}

方法二:定义可以改为dp[i]代表包含i的最大连续和,用一个变量来保存i之前的最大连续和,就不用dp[i][0]来保存了。

dp = max( dp+nums[i], nums[i)]
res = max(dp, res)

class Solution {
    public int maxSubArray(int[] nums) {
        int len = nums.length;
        int dp = nums[0], res = nums[0];
        for(int i=1;i<len;i++){
            dp = Math.max(dp+nums[i], nums[i]);
            res = Math.max(dp, res);
        }
        return res; 
    }
}

剑指 Offer 47. 礼物的最大价值

剑指 Offer 47. 礼物的最大价值
简单二维dp。

class Solution {
    public int maxValue(int[][] grid) {
        // dp[i][j] = max(dp[i-1][j], dp[i][j-1])+grid[i][j]
        int height = grid.length, width = grid[0].length;
        if(height+width==0) return 0;
        int[][] dp = new int[height][width];
        dp[0][0] = grid[0][0];
        for(int i=1;i<height;i++){
            dp[i][0] = dp[i-1][0]+grid[i][0];
        }
        for(int i=1;i<width;i++){
            dp[0][i] = dp[0][i-1]+grid[0][i];
        }
        for(int i=1;i<height;i++){
            for(int j=1;j<width;j++){
                dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1])+grid[i][j];
            }
        }
        return dp[height-1][width-1];
    }
}

第 10 天 动态规划(中等)

[M] 剑指 Offer 46. 把数字翻译成字符串

剑指 Offer 46. 把数字翻译成字符串
简单的dp,注意是种类,9和99和999都只能转为1种,06不等于6,写个示例找规律也能找到转移方程。最后i只和i-1和i-2有关,可以滚动数组减空间复杂度。

dp[i] = dp[i-1] 直接加一个数字而已,不会有改变
dp[i] = dp[i-1] + dp[i-2]新加的数字能和上一个数字结合,则可以多一种数字组合,和dp[i-2]结合,06这种不能算
1 1
11 2
111 3
1111 5

class Solution {
    public int translateNum(int num) {
        String s = String.valueOf(num);
        int len = s.length();
        if(len==2&&num<26) return 2;
        if(len<=2) return 1;
        int[] dp = new int[len];
        dp[0] = 1;
        Integer temp = Integer.valueOf(s.substring(0,2));
        if(temp<26 && temp>9) dp[1] = 2;
        else dp[1] = 1;
        for(int i=2;i<len;i++){
            dp[i] = dp[i-1];
            temp = Integer.valueOf(s.substring(i-1,i+1));
            if(temp<26 && temp>9) dp[i] += dp[i-2];
        }
        return dp[len-1];
    }
}

[M] 剑指 Offer 48. 最长不含重复字符的子字符串

剑指 Offer 48. 最长不含重复字符的子字符串
方法一:自己想的dp。

dp[i]为包含第i个的最长,设置res进行比较以保留之前的最大值
if s[i] is not in s[i-dp[i-1]:i+1] -> O(n^2) in time
store in an array to check, using a flag to avoid to clear the array -> O(n)
如果未重复 dp[i] = dp[i-1] + 1;
awpqw -> pqw
如果重复了及上次出现的index大于等于此次字符串开始的index
if(last index of s[i] > i - dp[i-1])
dp[i] = i - last index of s[i];
(last index of s[i]) stores in check array
如果这样判断check[s[i]-‘a’]!=-1,则在每次更新扫的时候要遍历这个节点以前的字符更新为-1
注意,不仅包括小写字母,所以要用hashmap

这时候dp[i]只和dp[i-1]有关,可以简化,时间复杂度成O(n),空间复杂度为O(1)。

class Solution {
    public int lengthOfLongestSubstring(String string) {
        int len = string.length();
        if(len<1) return 0;
        HashMap<Character, Integer> check = new HashMap<Character, Integer>();
        int[] dp = new int[len];
        char[] s = string.toCharArray();
        dp[0] = 1;
        check.put(s[0],0);
        int res = 1;
        
        for(int i=1;i<len;i++){
            if(check.containsKey(s[i]) && check.get(s[i])>=i-dp[i-1]){
                //顺序不能反
                res = Math.max(res, dp[i-1]);
                dp[i] = i - check.get(s[i]);
            } else {
                dp[i] = dp[i-1] + 1;
            }
            check.put(s[i],i);
        }
        return Math.max(res, dp[len-1]);
    }
}

方法二:双指针,check.get(s[i])>=i-dp可以用一个指针代替左边界。直接用max(left, check.get(s[i]))来更新左指针。比如[pwwkew]中,左指针分别为-1,-1,1,1,1,2。注意左指针应该在字符串左边,这样ww就不会造成相减等于0。

class Solution {
    public int lengthOfLongestSubstring(String string) {
        int len = string.length();
        HashMap<Character, Integer> check = new HashMap<Character, Integer>();
        int left = -1, res = 0;
        char[] s = string.toCharArray();

        for(int i=0;i<len;i++){
            if(check.containsKey(s[i])){
                left = Math.max(check.get(s[i]),left);
            }
            res = Math.max(i-left, res);
            check.put(s[i],i);
        }
        return res;
    }
}

第 11 天 双指针(简单)

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

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

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode node = head;
        if(node.val == val) return head.next;
        ListNode pre = node;
        while(node.next != null){
            node = node.next;
            if(node.val==val){
                pre.next = node.next;
                return head;
            }
            pre = node;
        }
        return head;
    }
}

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

剑指 Offer 22. 链表中倒数第k个节点
双指针保存移动左边的点。

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode res = head, node = head;
        int count = 1;
        while(node.next != null){
            node = node.next;
            count += 1;
            if(count>k){
                res = res.next;
            }
        }
        return res;
    }
}

第 12 天 双指针(简单)

[不会] 剑指 Offer 25. 合并两个排序的链表

剑指 Offer 25. 合并两个排序的链表
加一个头结点,一个一个加进去,当时没想到头结点,想复杂了。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode head = new ListNode(0), cur = head;
        while(l1!=null && l2!=null){
            if(l1.val<l2.val){
                cur.next = l1;
                l1 = l1.next;
                cur = cur.next;
            }else{
                cur.next = l2;
                l2 = l2.next;
                cur = cur.next;
            }
        }
        if(l1!=null){
            cur.next = l1;
        }else{
            cur.next = l2;
        }
        return head.next;
    }
}

剑指 Offer 52. 两个链表的第一个公共节点

剑指 Offer 52. 两个链表的第一个公共节点
方法一:要求时间是n,空间是1,因此一开始看下两者长度,只有长度相同的后半部分才可能出现相等。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int l1=getLen(headA), l2=getLen(headB);
        int minus = l1-l2;
        ListNode n1=headA, n2=headB;
        while(minus<0){
            n2 = n2.next;
            minus += 1;
        }
        while(minus>0){
            n1 = n1.next;
            minus -= 1;
        }
        while(n1!=null){
            if(n1==n2) return n1;
            n1 = n1.next;
            n2 = n2.next;
        }
        return null;
    }
    
    private int getLen(ListNode l){
        int count = 0;
        while(l!=null){
            count += 1;
            l = l.next;
        }
        return count;
    }
}

方法二:一个指针先走完A再走B,另一个先走B再走A,设A为a+c,c为公共长度,则B为b+c。
若c不是0,这两个指针相遇前走的长度分别为a+c+b,a+b+c。若c等于0,则最后都指向null。
复杂度差不了多少,方法一getlen走的步数为a+c+a+b,第二次找走了a+b次。

第 13 天 双指针(简单)

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

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

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

剑指 Offer 57. 和为s的两个数字

剑指 Offer 57. 和为s的两个数字
要求了递增,右指针先确定边界(nums[len]+nums[0]<=target)。
之后两指针相加,若大于,则right–,小于则left++。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int len=nums.length, right=len-1, left=0;
        while(nums[right]>target || nums[right]+nums[0]>target){
            right -= 1;
        }
        while(left<right){
            int l=nums[left],r=nums[right],s=l+r;
            if(s<target) left+=1;
            else if(s>target) right-=1;
            else return new int[]{l,r};
        }
        return new int[0];
    }
}

剑指 Offer 58 - I. 翻转单词顺序

剑指 Offer 58 - I. 翻转单词顺序
方法一:存都list再反向输出。

class Solution {
    public String reverseWords(String s) {
        ArrayList<String> list = new ArrayList<String>();
        int len = s.length(), count=0, flag=2, left=0;
        int right=len;//防止全是空格
        for(int i=0;i<len;i++){
            if(s.charAt(i)!=' ' && flag==2){
                left = i;
                flag -= 1;
            }else if(s.charAt(i)==' ' && flag==1){
                right = i;
                list.add(s.substring(left,right));
                count += 1;
                flag = 2;
            }
        }
        if(flag==1){
            list.add(s.substring(left,len));
            count += 1;
        }
        // 否则说明最后全是空格
        StringBuilder sb = new StringBuilder(0);
        for(int i=count-1;i>0;i--){
            sb.append(list.get(i)+' ');
        }
        if(list.size()>0) sb.append(list.get(0));
        return sb.toString();
    }
}

方法二:从后往前,只走一轮。

class Solution {
    public String reverseWords(String s) {
        s = s.trim();
        ArrayList<String> list = new ArrayList<String>();
        StringBuilder sb = new StringBuilder(0);
        int len = s.length(), right=len-1, left=right;
        while(right>=0){
            while(left>=0 && s.charAt(left)!=' ') left-=1;
            sb.append(s.substring(left+1,right+1) + " ");
            while(left>=0 && s.charAt(left)==' ') left-=1;
            right = left;
        }
        return sb.toString().trim();
    }
}

第 14 天 搜索与回溯算法(中等)

[M] 剑指 Offer 12. 矩阵中的路径

剑指 Offer 12. 矩阵中的路径
方法一:正常dfs,复杂度高。

class Solution {
    int[][] flag = new int[200][200];
    char[][] board = new char[200][200];
    int height, width;
    int[] dx = new int[]{-1,0,1,0};
    int[] dy = new int[]{0,1,0,-1};
    String word = null;
    int length = 0;

    public boolean exist(char[][] board, String word) {
        this.board = board;
        this.word = word;
        this.length = word.length();
        this.height = board.length;
        this.width = board[0].length;
        if(length==0) return false;
        for(int i=0;i<height;i++){
            for(int j=0;j<width;j++){
                if(word.charAt(0)==board[i][j] && dfs(i,j,0)){
                    return true;
                }
            }
        }
        return false;
    }

    private boolean dfs(int x, int y, int index){
        //不能是index==length
        if(index==length-1) return true;
        flag[x][y] = 1;
        for(int i=0;i<4;i++){
            int nx = x+dx[i];
            int ny = y+dy[i];
            // word.charAt(0)==board[i][j]放到这统一判断,保证到最后一个字符时,index==length-1成立且字符相等
            if(nx>=0&&nx<height&&ny>=0&&ny<width
            && flag[nx][ny]==0 
            && word.charAt(index+1)==board[nx][ny]
            && dfs(nx,ny,index+1)){
                return true;
            }
        }
        flag[x][y] = 0;
        return false;
    }
}

方法二WRONG:通过保存此点出发为false来剪枝。比如

AAA
ABA
AAA

若目标为ABC,则可提前记住B为index==1时,从B出发无法返回true。
!这样是不对的,因为第二次到同样的地方时,可能方向是反的。第一次是1->2->3不对设置2位置index为2时不能达到,但4->2->1是对的,第一次知识因为1已经走过了,导致2到不了。
但时间用时较长,不知为什么,可能是因为初始化变量,或是if中有太多and而不是or。

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

剑指 Offer 13. 机器人的运动范围
方法一:简单bfs,时间和空间都很差。

class Solution {
    int[] dx = new int[]{-1,0,1,0};
    int[] dy = new int[]{0,1,0,-1};

    public int movingCount(int m, int n, int k) {
        Queue<Integer> q = new LinkedList<Integer>();
        int x=0,y=0;
        int count=0;
        int[][] flag = new int[m][n];
        flag[0][0] = 1;
        q.add(x);
        q.add(y);
        while(!q.isEmpty()){
            x=q.poll();
            y=q.poll();
            if(judge(x,y,k)){
                count += 1;
                for(int i=0;i<4;i++){
                    if(bound(x+dx[i],y+dy[i],m,n)&&flag[x+dx[i]][y+dy[i]]==0){
                        q.add(x+dx[i]);
                        q.add(y+dy[i]);
                        flag[x+dx[i]][y+dy[i]] = 1;
                    }
                }
            }
        }
        return count;
    }
    
    private boolean judge(int x, int y, int k){
        int count = 0;
        while(x>0){
            count += x%10;
            x = x/10;
        }
        while(y>0){
            count += y%10;
            y = y/10;
        }
        return count<=k;
    }

    private boolean bound(int x, int y, int m, int n){
        return x>=0 && x<m && y>=0 && y<n;
    }
}

第 15 天 搜索与回溯算法(中等)

[M] 剑指 Offer 34. 二叉树中和为某一值的路径

剑指 Offer 34. 二叉树中和为某一值的路径
回溯,注意判断root为叶节点,而非root是null,否则会加两次。

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        List<Integer> temp = new ArrayList<Integer>();
        dfs(root,target,ans,temp,0);
        return ans;
    }

    private void dfs(TreeNode root, int target, List<List<Integer>> ans, List<Integer> temp, int index){
        if(root==null) return;
        temp.add(root.val);
        if(root.left==root.right && root.left==null && target==root.val){
            List<Integer> ntemp = new ArrayList<Integer>();
            Collections.addAll(ntemp,new Integer[temp.size()]);
            Collections.copy(ntemp,temp);
            ans.add(ntemp);
        }
        dfs(root.left, target-root.val, ans, temp, index+1);
        dfs(root.right, target-root.val, ans, temp, index+1);
        temp.remove(index);
    }
}

[不会][M] 剑指 Offer 36. 二叉搜索树与双向链表

剑指 Offer 36. 二叉搜索树与双向链表

二叉搜索数BST,左小右大。中序遍历就是从小到大。
左子树应该返回最右节点,右子树应该返回最左节点。若对应的为空,则返回root。
这样不对,遇到[4,1,null,null,2,null,3]这种123在4左边时,应该返回的是右节点,但123这种由于是右子树,返回的是左节点。
应该把右节点放到根节点,然后根节点拉d

最终用中序遍历加几行代码来解决。中序遍历结果中,pre.right=cur, cur.left=pre。

class Solution {
    Node head, pre;
    public Node treeToDoublyList(Node root) {
        if(root==null) return null;
        midTrip(root);
        // pre==last
        head.left = pre;
        pre.right = head;
        return head;
    }

    public void midTrip(Node cur) {
        if(cur==null) return;
        midTrip(cur.left);
        if(head==null) head = cur; 
        cur.left = pre;
        if(pre!=null) pre.right = cur;
        pre = cur;
        midTrip(cur.right);
    }
}

剑指 Offer 54. 二叉搜索树的第k大节点

剑指 Offer 54. 二叉搜索树的第k大节点
递归中序反着来。

class Solution {
    int count = 0, ans = 0, k;
    public int kthLargest(TreeNode root, int k) {
        this.k = k;
        midTrip(root);
        return ans;
    }

    private void midTrip(TreeNode root) {
        if(root==null || count==k) return;
        midTrip(root.right);
        count += 1;
        if(count==k){
            ans = root.val;
            return;
        }
        midTrip(root.left);
    }
}

最后

以上就是冷静鸵鸟为你收集整理的剑指offer简单(java)上第1天 栈与队列(简单)第 2 天 链表(简单)第 3 天 字符串(简单)第 4 天 查找算法(简单)第 5 天 查找算法(中等)第 6 天 搜索与回溯算法(简单)(广搜)第 7 天 搜索与回溯算法(简单)(深搜)第 8 天 动态规划(简单)第 9 天 动态规划(中等)第 10 天 动态规划(中等)第 11 天 双指针(简单)第 12 天 双指针(简单)第 13 天 双指针(简单)第 14 天 搜索与回溯算法(中等)第 15 天 搜索与回溯算法(中等)的全部内容,希望文章能够帮你解决剑指offer简单(java)上第1天 栈与队列(简单)第 2 天 链表(简单)第 3 天 字符串(简单)第 4 天 查找算法(简单)第 5 天 查找算法(中等)第 6 天 搜索与回溯算法(简单)(广搜)第 7 天 搜索与回溯算法(简单)(深搜)第 8 天 动态规划(简单)第 9 天 动态规划(中等)第 10 天 动态规划(中等)第 11 天 双指针(简单)第 12 天 双指针(简单)第 13 天 双指针(简单)第 14 天 搜索与回溯算法(中等)第 15 天 搜索与回溯算法(中等)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

相关文章

评论列表共有 0 条评论

立即
投稿
返回
顶部