剑指offer(中等)

news2024/11/18 12:18:52

目录

二维数组中的查找

重建二叉树

矩阵中的路径

剪绳子

 剪绳子②

数值的整数次方

表示数值的字符串

树的子结构

栈的压入、弹出序列

从上到下打印二叉树①

从上到下打印二叉树③

二叉搜索树的后序遍历序列

二叉树中和为某一值的路径

复杂链表的复制

二叉搜索树与双向链表

字符串的排列

数字序列中某一位的数字

把数字翻译成字符串

礼物的最大价值

最长不含重复字符的子字符串

丑数

数组中数字出现的次数

数组中数字出现的次数②

n个骰子的点数

股票的最大利润

求1+2+...+n

构建乘积数组

只出现一次的数字

单词长度的最大乘积

数组中和为0的三个数

和大于等于target的最短子数组

乘积小于K的子数组

和为k的子数组 

0和1个数相同的子数组 

二维子矩阵的和

字符串中的变位词

字符串中的所有变位词

不含重复字符的最长子字符串

回文子字符串的个数

删除链表的倒数第n个结点

链表中环的入口节点

链表中的两数相加

重排链表

 展平多级双向链表

排序的循环链表

插入、删除和随机访问都是O(1)的容器

最近最少使用缓存

变位词组

 最小时间差

后缀表达式

小行星碰撞

 每日温度

往完全二叉树添加节点

 二叉树最底层最左边的值

二叉树的右侧视图

二叉树剪枝

 从根节点到叶节点的路径数字之和

向下的路径节点之和

二叉搜索树中的中序后继 

所有大于等于节点的值之和

 二叉搜索树迭代器

 值和下标之差都在给定的范围内

日程表

出现频率最高的k个数字

 和最小的k个数对

先到这里吧,后面就好多是没学过的知识(比如前缀树),有时间精力再做吧


二维数组中的查找

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

  • 方法1:Z字形查找
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if(matrix.length==0 || matrix[0].length==0)return false;
        int n=0;
        int m=matrix[0].length-1;
        while(n<matrix.length && m>=0){
            if(target>matrix[n][m]){
                n++;
            }
            else if(target<matrix[n][m]){
                m--;
            }
            else{
                return true;
            }
        }
        return false;
    }
}

重建二叉树

剑指 Offer 07. 重建二叉树

  • 方法1:分治算法

大佬讲的很清楚

我的代码过程展示:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int[] preorder;
    HashMap<Integer,Integer> map=new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder=preorder;
        if(preorder.length==0)return null;
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        return buildTree2(0,0,inorder.length-1);
    }
    public TreeNode buildTree2(int root,int left,int right){
        if(left>right)return null;
        TreeNode node=new TreeNode(preorder[root]);
        int i=map.get(preorder[root]);
        node.left=buildTree2(root+1,left,i-1);
        node.right=buildTree2(i-left+root+1,i+1,right);
        return node;
    }
}

矩阵中的路径

剑指 Offer 12. 矩阵中的路径

  • 方法1:深度优先搜索(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;
    }
    public boolean dfs(char[][]board,char[] words,int i,int j,int k){
        //这个是终止条件(边界)
        if(i<0 || i>=board.length || j<0 || j>=board[0].length || board[i][j]!=words[k])return false;
        //如果k已经是单词的长度,那么说明单词存在于网格中
        if(k==words.length-1)return true;
        //给他做上标记,此坐标中的字母已经使用过了
        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,j+1,k+1);
        //到这一步就已经是递归完成了,因此我们需要把网格中的字母还原,以备后面查找使用
        board[i][j]=words[k];
        return res;

    } 
}

剪绳子

剑指 Offer 14- I. 剪绳子

方法1:动态规划

思路

class Solution {
    public int cuttingRope(int n) {
        int[] dp=new int[n+1];
        dp[2]=1;
        for(int i=3;i<n+1;i++){
            for(int j=2;j<i;j++){
                dp[i]=Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));
            }
        }
        return dp[n];
    }
}

方法2:贪心算法

class Solution {
    public int cuttingRope(int n) {
        if(n < 4){
            return n - 1;
        }
        int res = 1;
        while(n > 4){
            res *= 3;
            n -= 3;
        }
        return res * n;
    }
}

 剪绳子②

剑指 Offer 14- II. 剪绳子 II

  • 方法1:贪心算法

思路

class Solution {
    public int cuttingRope(int n) {
        if(n<4){
            return n-1;
        }
        long res=1;
        while(n>4){
            res=res*3%1000000007;
            n-=3;
        }
        return (int)(res*n%1000000007);
    }
}

数值的整数次方

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

  • 方法1:快速幂(使用二分法+二进制方法实现)

思路

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

表示数值的字符串

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

思路

class Solution {
    public boolean isNumber(String s) {
        //去掉首位空格
        s = s.trim();
        //是否出现数字
        boolean numFlag = false;
        //是否出现小数点
        boolean dotFlag = false;
        boolean eFlag = false;
        for (int i = 0; i < s.length(); i++) {
            //判定为数字,则标记numFlag
            if (s.charAt(i) >= '0' && s.charAt(i) <= '9') {
                numFlag = true;
                //小数点只可以出现再e之前,且只能出现一次.num  num.num num.都是被允许的
            } else if (s.charAt(i) == '.' && !dotFlag && !eFlag) {
                dotFlag = true;
                //判定为e,需要没出现过e,并且出过数字了
            } else if ((s.charAt(i) == 'e' || s.charAt(i) == 'E') && !eFlag && numFlag) {
                eFlag = true;
                //避免e以后没有出现数字
                numFlag = false;
                //判定为+-符号,只能出现在第一位或者紧接e后面
            } else if ((s.charAt(i) == '+' || s.charAt(i) == '-') && (i == 0 || s.charAt(i - 1) == 'e' || s.charAt(i - 1) == 'E')) {

                //其他情况,都是非法的
            } else {
                return false;
            }
        }
        //是否出现了数字 
        return numFlag;
    }
}

树的子结构

 剑指 Offer 26. 树的子结构

  • 方法1:先序遍历

思路

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        return (A != null && B != null) && (recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
    }
    boolean recur(TreeNode A, TreeNode B) {
        if(B == null) return true;
        if(A == null || A.val != B.val) return false;
        return recur(A.left, B.left) && recur(A.right, B.right);
    }
}

栈的压入、弹出序列

 剑指 Offer 31. 栈的压入、弹出序列

  • 方法1:模拟栈

思路

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> stack=new Stack<>();
        int i=0;
        for(int num:pushed){
            stack.push(num);
            while(!stack.isEmpty() && stack.peek()==popped[i]){
                stack.pop();
                i++;
            }
        }
        return stack.isEmpty();
    }
}

从上到下打印二叉树①

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

  • 方法1:借助队列实现

思路

/**
 * 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) {
        if(root==null)return new int[0];
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        List<Integer> res=new ArrayList<>();
        while(!queue.isEmpty()){
            TreeNode tmp=queue.poll();
            res.add(tmp.val);
            if(tmp.left!=null){
                queue.offer(tmp.left);
            }
            if(tmp.right!=null){
                queue.offer(tmp.right);
            }
        }
        int[] arr=new int[res.size()];
        for(int i=0;i<arr.length;i++){
            arr[i]=res.get(i);
        }
        return arr;
    }
}

从上到下打印二叉树③

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

  • 方法1:借助双端队列实现

思路

/**
 * 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) {
        List<List<Integer>> res=new ArrayList<>();
        Deque<TreeNode> de=new LinkedList<>();
        if(root!=null)de.offer(root);
        while(!de.isEmpty()){
            List<Integer> list=new ArrayList<>();
            for(int i=de.size();i>0;i--){
                TreeNode tmp=de.pollFirst();
                list.add(tmp.val);
                if(tmp.left!=null)de.offerLast(tmp.left);
                if(tmp.right!=null)de.offerLast(tmp.right);
            }
            res.add(list);
            if(de.isEmpty())break;
            list=new ArrayList<>();
            for(int i=de.size();i>0;i--){
                TreeNode tmp=de.pollLast();
                list.add(tmp.val);
                if(tmp.right!=null)de.offerFirst(tmp.right);
                if(tmp.left!=null)de.offerFirst(tmp.left);
            }
            res.add(list);
        }
        return res;
    }
}

二叉搜索树的后序遍历序列

剑指 Offer 33. 二叉搜索树的后序遍历序列

  • 方法1:递归分治
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        if(postorder.length==0)return true;
        return postOrder(postorder,0,postorder.length-1);
    }
    public boolean postOrder(int[] postorder,int start,int end){
        if(start>=end)return true;
        int i=start;
        for(;i<end;i++){
            if(postorder[i]>postorder[end]){
                break;
            }
        }
        for(int j=i;j<end;j++){
            if(postorder[j]<postorder[end]){
                return false;
            }
        }
        return postOrder(postorder,start,i-1) && postOrder(postorder,i,end-1);
    }
}

二叉树中和为某一值的路径

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

  • 方法1:深度优先搜索
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    List<List<Integer>> res=new ArrayList<>();
    List<Integer> list=new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        return dfs(root,target);
    }
    public void dfs(TreeNode root,int target){
        if(root==null)return;
        target-=root.val;
        list.add(root.val);
        if(target==0 && root.left==null && root.right==null){
            res.add(list);
        }
        dfs(root.left,target);
        dfs(root.right,target);
        list.remove(list.size()-1);

    }
}

复杂链表的复制

 剑指 Offer 35. 复杂链表的复制

  • 方法1:哈希表
/*
// 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;
        Map<Node,Node> map=new HashMap<>();
        Node cur=head;
        while(cur!=null){
            map.put(cur,new Node(cur.val));
            cur=cur.next;
        }
        cur=head;
        while(cur!=null){
            map.get(cur).next=map.get(cur.next);
            map.get(cur).random=map.get(cur.random);
            cur=cur.next;
        }
        return map.get(head);
    }
}

二叉搜索树与双向链表

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

  • 方法1:中序遍历

自己写出来的!!!

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val,Node _left,Node _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
    Node tmp=new Node(-1);
    Node pre=tmp;
    public Node treeToDoublyList(Node root) {
        if(root==null)return null;
        Node cur=root;
        inOrder(cur);
        tmp.right.left=pre;
        pre.right=tmp.right;
        return tmp.right;
        
    }
    public void inOrder(Node cur){
        if(cur==null)return;
        inOrder(cur.left);
        pre.right=cur;
        cur.left=pre;
        pre=cur;
        inOrder(cur.right);


    }
}

字符串的排列

剑指 Offer 38. 字符串的排列

  • 方法1:深度优先搜索(dfs)

思路

class Solution {
    List<String> res=new ArrayList<>();
    char[] c;
    public String[] permutation(String s) {
        c=s.toCharArray();
        dfs(0);
        return res.toArray(new String[res.size()]);
    }
    public void dfs(int x){
        if(x==c.length-1){
            res.add(String.valueOf(c));
        }
        Set<Character> set=new HashSet<>();
        for(int i=x;i<c.length;i++){
            if(set.contains(c[i]))continue;
            set.add(c[i]);
            swap(i,x);
            dfs(x+1);
            swap(i,x);
        }
    }
    public void swap(int i,int x){
        char tmp=c[i];
        c[i]=c[x];
        c[x]=tmp;
    }
}

数字序列中某一位的数字

剑指 Offer 44. 数字序列中某一位的数字

  • 方法1:思路

目前做过可能两遍了,但是靠自己想只能想出一半,还有不懂的地方,下次做的时候看看能不能想通:

1.题目说从下标0开始计数,这个条件在代码的哪里应用了?

2.关于算num和算最终返回值的思路

class Solution {
    public int findNthDigit(int n) {
        int weishu=1;
        long start=1;
        long count=9;
        while(n>count){
            n-=count;
            weishu+=1;
            start*=10;
            count=weishu*start*9;
        }
        long num=start+(n-1)/weishu;
        return Long.toString(num).charAt((n-1)%weishu)-'0';
    }
}

把数字翻译成字符串

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

  • 方法1:动归(通过字符串遍历的方式实现动归)
class Solution {
    public int translateNum(int num) {
        String s=String.valueOf(num);
        int a=1;
        int b=1;
        for(int i=2;i<=s.length();i++){
            String tmp=s.substring(i-2,i);
            int c=tmp.compareTo("10")>=0 && tmp.compareTo("25")<=0?a+b:a;
            b=a;
            a=c;
        }
        return a;
    }
}

礼物的最大价值

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

  • 方法1:动态规划

思路

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

最长不含重复字符的子字符串

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

  • 方法1:动态规划+哈希表

思路

这道题下次还得再做啊

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int tmp=0;
        int res=0;
        Map<Character,Integer> map=new HashMap<>();
        for(int j=0;j<s.length();j++){
            int i=map.getOrDefault(s.charAt(j),-1);
            map.put(s.charAt(j),j);
            tmp=tmp<j-i?tmp+1:j-i;
            res=Math.max(res,tmp);
        }
        return res;
    }
}

丑数

剑指 Offer 49. 丑数

  • 方法1:动态规划

思路

class Solution {
    public int nthUglyNumber(int n) {
        int[] dp=new int[n];
        dp[0]=1;
        int a=0;
        int b=0;
        int c=0;
        for(int i=1;i<dp.length;i++){
            dp[i]=Math.min(Math.min(dp[a]*2,dp[b]*3),dp[c]*5);
            if(dp[a]*2==dp[i])a++;
            if(dp[b]*3==dp[i])b++;
            if(dp[c]*5==dp[i])c++;
        }
        return dp[dp.length-1];
    }
}

数组中数字出现的次数

剑指 Offer 56 - I. 数组中数字出现的次数

一开始我想的是用HashMap,虽然能通过,但是题目要求时间复杂度是O(n),空间复杂度是O(1),如果使用HashMap空间复杂度就超了

所以看了大佬的思路,用下面这种方法做的

  • 方法1:位运算

思路

class Solution {
    public int[] singleNumbers(int[] nums) {
        int x=0;//x为其中一个只出现一次的数字
        int y=0;//y为另外一个只出现一次的数字
        int n=0;//n为遍历数组各数字异或运算的结果
        int m=1;//m为x^y二进制位首位出现1时的对应的数字
        for(int num:nums){
            n^=num;
        }
        while((n & m)==0){
            m<<=1;
        }
        for(int num:nums){
            if((num & m)==0){
                x^=num;
            }
        }
        return new int[]{x,x^n};
    }
}

数组中数字出现的次数②

剑指 Offer 56 - II. 数组中数字出现的次数 II

  • 方法1:位运算(利用遍历统计法实现)

 思路

class Solution {
    public int singleNumber(int[] nums) {
        int[] counts=new int[32];
        for(int num:nums){
            for(int j=0;j<counts.length;j++){
                counts[j]+=num&1;
                num>>=1;
            }
        }
        int res=0;   
        for(int i=0;i<counts.length;i++){
            res<<=1;
            res|=counts[31-i]%3;
        }
        return res;
    }
}

写的过程中这里大意写错了,报了这样的错误,就去了解下这个错误啥意思

required variable found value_qq_44842466的博客-CSDN博客

n个骰子的点数

剑指 Offer 60. n个骰子的点数

  • 方法1:动态规划

 思路

这道题我真的还是没看懂,也就是刚看完题解自己依旧做不出来的程度,所以这里不放代码提醒自己这道题相当于还没做,大家可以直接看上面的思路,思路里有大佬写的代码哦

股票的最大利润

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

  • 方法1:动态规划

思路

class Solution {
    public int maxProfit(int[] prices) {
        int cost=Integer.MAX_VALUE;//cost将来用来存放当前股票的最小价格
        int profit=0;//profit将来用来存放当前最大利润
        for(int price:prices){
            cost=Math.min(cost,price);
            profit=Math.max(profit,price-cost);
        }
        return profit;
    }
}

求1+2+...+n

剑指 Offer 64. 求1+2+…+n

  • 方法1:逻辑符短路

思路

class Solution {
    public int sumNums(int n) {
        boolean x=n>1 && (n+=sumNums(n-1))>0;
        return n;
    }
}

我一开始想的是二分法+位运算,但是失败了,然后去看题解看有没有和自己想法一样的,位运算解法之类的,官方题解里到时有位运算(快速乘)的,但是...emmmm,算了还是先放弃位运算的方法吧

构建乘积数组

剑指 Offer 66. 构建乘积数组

  • 方法1:动态规划

我先看的这个思路,但代码没有按这个大佬的写,而是看的下面评论区的另一位大佬用动态规划的思路写的

class Solution {
    public int[] constructArr(int[] a) {
        if(a==null || a.length==0)return new int[0];
        int len=a.length;
        int[] leftOfI=new int[len];
        int[] rightOfI=new int[len];
        leftOfI[0]=1;
        rightOfI[len-1]=1;
        for(int i=1;i<len;i++){
            leftOfI[i]=leftOfI[i-1]*a[i-1];
        }
        for(int i=len-2;i>=0;i--){
            rightOfI[i]=rightOfI[i+1]*a[i+1];
        }

        int[] res=new int[len];
        for(int i=0;i<len;i++){
            res[i]=leftOfI[i]*rightOfI[i];
        }
        return res;
    }
}

只出现一次的数字

剑指 Offer II 004. 只出现一次的数字 

这道题和之前做的这道题题目不一样,提示不一样,但做法代码一模一样,你会数组中数字出现的次数②这道题,那么此题你也就会,不过我把这道题还是贴在这儿了,大家可以找找不同,同时要建立一个警报系统,对于做过的题要有条件反射,拥有这种意识和能力,这对未来大家面试做没做过或相似的题的时候有帮助~大家想看此题代码,去数组中数字出现的次数②这道题中看代码就行(就在这篇博客里),我这里就不放了

单词长度的最大乘积

剑指 Offer II 005. 单词长度的最大乘积

  •  方法1:位运算模拟

这道题我最终搞懂是靠的这三位大佬

首先我先看的大佬1思路,然后对于或运算和左移那一行代码不是很明白,因此我就又看了大佬2的图以及大佬3的视频讲解 最终明白了或运算和左移那一行代码的意思,最终自己了一遍代码(思路还是大佬1的)

class Solution {
    public int maxProduct(String[] words) {
        int[] word=new int[words.length];
        int index=0;
        for(String w:words){
            int res=0;
            for(int i=0;i<w.length();i++){
                int tmp=w.charAt(i)-'a';
                res|=(1<<tmp);
            }
            word[index++]=res;
        }
        int max=0;
        for(int i=0;i<word.length;i++){
            for(int j=0;j<i;j++){
                if((word[i]&word[j])==0){
                    max=Math.max(max,words[i].length()*words[j].length());
                }
            }
        }
        return max;
    }
}

数组中和为0的三个数

剑指 Offer II 007. 数组中和为 0 的三个数

方法1:排序+双指针

 思路

使用HashSet进行去重

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        Set<List<Integer>> set=new HashSet<>();
        for(int i=0;i<nums.length-2;i++){
            if(i>0 && nums[i]==nums[i-1])continue;
            int target=-nums[i];
            int left=i+1;
            int right=nums.length-1;
            while(left<right){
                int sum=nums[left]+nums[right];
                if(sum==target){
                    set.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    left++;
                    right--;
                }
                else if(sum>target){
                    right--;
                }
                else{
                    left++;
                }
            }
        }
        return new ArrayList<>(set);
    }
}

优化:(是上面思路里面评论区赵四儿大佬的优化思路)

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res=new ArrayList<>();
        for(int i=0;i<nums.length-2;i++){
            if(nums[i]>0)break;
            if(i>0 && nums[i]==nums[i-1])continue;
            int target=-nums[i];
            int left=i+1;
            int right=nums.length-1;
            while(left<right){
                int sum=nums[left]+nums[right];
                if(sum==target){
                    res.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    left++;
                    right--;
                    while(left<right && nums[left]==nums[left-1]){
                        left++;
                    }
                    while(left<right && nums[right]==nums[right+1]){
                        right--;
                    }
                }
                else if(sum<target){
                    left++;
                }
                else{
                    right--;
                }
            }
        }
        return res;
    }
}

和大于等于target的最短子数组

剑指 Offer II 008. 和大于等于 target 的最短子数组

  • 方法1:滑动窗口

 思路

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int left=0;
        int sum=0;
        int res=Integer.MAX_VALUE;
        for(int right=0;right<nums.length;right++){
            sum+=nums[right];
            while(sum>=target){
                res=Math.min(res,right-left+1);
                sum-=nums[left];
                left++;
            }
        }
        return res>target?0:res;
    }
}

乘积小于K的子数组

剑指 Offer II 009. 乘积小于 K 的子数组

  • 方法1:滑动窗口

思路

 

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        int left=0;
        int sum=1;
        int count=0;
        for(int right=0;right<nums.length;right++){
            sum*=nums[right];
            while(left<=right && sum>=k){
                sum/=nums[left++];
            }
            if(left<=right){
                count+=right-left+1;
            }
        }
        return count;
    }
}

和为k的子数组 

剑指 Offer II 010. 和为 k 的子数组

这题还得再看再做!!!

  • 方法1:前缀和+哈希表

这道题如果nums[i]的取值范围不包括负数的话,那么就可以用滑动窗口

但是这样看来,这道题不能用滑动窗口

因此我们要找一个其他的方法——前缀和

这个大佬的思路 加上 这个大佬的图

看完相信你应该懂得差不多了

class Solution {
    public int subarraySum(int[] nums, int k) {
        int sum=0;//记录前缀和
        int count=0;//记录个数
        Map<Integer,Integer> map=new HashMap<>();
        map.put(0,1);
        for(int i:nums){
            sum+=i;
            count+=map.getOrDefault(sum-k,0);
            map.put(sum,map.getOrDefault(sum,0)+1);
        }
        return count;
    }
}

0和1个数相同的子数组 

剑指 Offer II 011. 0 和 1 个数相同的子数组

这题还得再看再做!!!

  • 方法1:前缀和+哈希表

思路

class Solution {
    public int findMaxLength(int[] nums) {
        int sum=0;//记录前缀和
        int count=0;//记录个数
        Map<Integer,Integer> map=new HashMap<>();
        map.put(0,-1);
        for(int i=0;i<nums.length;i++){
            sum+=nums[i]==0?-1:1;
            if(map.containsKey(sum)){
                count=Math.max(count,i-map.get(sum));
            }
            else{
                map.put(sum,i);
            }
        }
        return count;
    }
}

二维子矩阵的和

剑指 Offer II 013. 二维子矩阵的和

 

没懂没懂没懂!!!

  • 方法1:前缀和

思路

 

class NumMatrix {
    int[][] mat;

    public NumMatrix(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        mat = new int[m + 1][n + 1];

        for (int i = 1; i <= m; i++) {
            mat[i][1] = matrix[i-1][0];
        }

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                mat[i][j] = mat[i][j-1] + matrix[i-1][j-1];
            }
        }

        for (int i = 2; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                mat[i][j] += mat[i-1][j];
            }
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        return mat[row2 + 1][col2 + 1] - mat[row1][col2 + 1] - mat[row2 + 1][col1] + mat[row1][col1];
    }
}

字符串中的变位词

剑指 Offer II 014. 字符串中的变位词

  • 方法1:滑动窗口

思路

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        if(s1.length()>s2.length())return false;
        int[] arr1=new int[26];
        int[] arr2=new int[26];
        for(int i=0;i<s1.length();i++){
            arr1[s1.charAt(i)-'a']++;
            arr2[s2.charAt(i)-'a']++;
        }

        for(int i=s1.length();i<s2.length();i++){
            if(Arrays.equals(arr1,arr2)){
                return true;
            }
            arr2[s2.charAt(i-s1.length())-'a']--;
            arr2[s2.charAt(i)-'a']++;
        }
        return Arrays.equals(arr1,arr2);
    }
}

字符串中的所有变位词

剑指 Offer II 015. 字符串中的所有变位词

 

  • 方法1:滑动窗口

思路:有了这道题上面的题——字符串中的变位词的基础

这道题不是手到擒来嘛哇哈哈哈哈哈哈

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> list=new ArrayList<>();
        if(p.length()>s.length())return list;
        int[] arrs=new int[26];
        int[] arrp=new int[26];
        for(int i=0;i<p.length();i++){
            arrs[s.charAt(i)-'a']++;
            arrp[p.charAt(i)-'a']++;
        }
        for(int i=p.length();i<s.length();i++){
            if(Arrays.equals(arrs,arrp)){
                list.add(i-p.length());
            }
            arrs[s.charAt(i-p.length())-'a']--;
            arrs[s.charAt(i)-'a']++;
        }
        if(Arrays.equals(arrs,arrp)){
            list.add(s.length()-p.length());
        }
        return list;
    }
}

不含重复字符的最长子字符串

剑指 Offer II 016. 不含重复字符的最长子字符串

还得再做!!!

  • 方法1:滑动窗口

思路

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int left=0;
        int count=0;
        if(s.length()<=1)return s.length();
        Map<Character,Integer> map=new HashMap<>();
        int right=0;
        for(;right<s.length();right++){
            if(map.containsKey(s.charAt(right))){
                count=Math.max(count,right-left);
                left=Math.max(left,map.get(s.charAt(right))+1);
            }
            map.put(s.charAt(right),right);
        }
        count=Math.max(count,right-left);
        return count;
    }
}

回文子字符串的个数

剑指 Offer II 020. 回文子字符串的个数

 

 还得再做!!!

  • 方法1:中心扩展
class Solution {
    public int countSubstrings(String s) {
        int count = 0;
        //字符串的每个字符都作为回文中心进行判断,中心是一个字符或两个字符
        for (int i = 0; i < s.length(); ++i) {
            count += countPalindrome(s, i, i);
            count += countPalindrome(s, i, i+1);
        }
        return count;
    }

    //从字符串的第start位置向左,end位置向右,比较是否为回文并计数
    private int countPalindrome(String s, int start, int end) {
        int count = 0;
        while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
            count++;
            start--;
            end++;
        }
        return count;
    }
}


删除链表的倒数第n个结点

剑指 Offer II 021. 删除链表的倒数第 n 个结点

 

  • 方法1:双指针+傀儡节点
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode pre=new ListNode(-1);
        pre.next=head;
        ListNode fast=pre;
        ListNode slow=pre;
        while(n!=0){
            fast=fast.next;
            n--;
        }
        while(fast.next!=null){
            fast=fast.next;
            slow=slow.next;
        }
        slow.next=slow.next.next;
        return pre.next;

    }
   
}

链表中环的入口节点

剑指 Offer II 022. 链表中环的入口节点

  •  方法1:快慢指针
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow)break;
        }
        if(fast==null || fast.next==null)return null;
        slow=head;
        while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }
}

链表中的两数相加

剑指 Offer II 025. 链表中的两数相加

  •  方法1:反转链表
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        l1=reserveList(l1);
        l2=reserveList(l2);
        ListNode pre=new ListNode(-1);
        int sum=0;
        int jinwei=0;
        int num1=0,num2=0;
        while(l1!=null || l2!=null || jinwei!=0){
            if(l1!=null){
                num1=l1.val;
                l1=l1.next;
            }
            else{
                num1=0;
            }
            if(l2!=null){
                num2=l2.val;
                l2=l2.next;
            }
            else{
                num2=0;
            }
            sum=num1+num2+jinwei;
            jinwei=sum/10;
            ListNode node=new ListNode(sum%10);
            node.next=pre.next;
            pre.next=node;
        }
        return pre.next;
    }
    public ListNode reserveList(ListNode head){
        ListNode pre=null;
        while(head!=null){
            ListNode headN=head.next;
            head.next=pre;
            pre=head;
            head=headN;
        }
        return pre;
    }
}

重排链表

剑指 Offer II 026. 重排链表

  • 方法1:快慢指针+反转链表+链表合并
class Solution {
    public void reorderList(ListNode head) {
        // 找到中间节点
        ListNode mid = findMid(head);
        // 通过中间节点,将链表分为两部分,左边一部分不动,将右边一部分翻转
        ListNode curA = head;
        ListNode curB = reverse(mid.next);// 中间节点划分到左边部分
        mid.next = null;// 将链表一分为二
        // 将两个链表合并
        merge(curA, curB);
    }
    private ListNode findMid(ListNode head){
        // 创建一个辅助头节点
        ListNode tmp = new ListNode(0);
        tmp.next = head;
        // 从辅助头节点开始,快指针一次走2个单位,慢指针一次走1个单位
        // 当快指针.next == null 或者 快指针 == null 则表明,slow 指向了中间节点。
        ListNode slow = tmp, fast = tmp;
        while (fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
    private ListNode reverse(ListNode head){
        ListNode reversedList = null;
        while (head != null){
            ListNode next = head.next;
            head.next = reversedList;
            reversedList = head;
            head = next;
        }
        return reversedList;
    }
    private ListNode merge(ListNode l1, ListNode l2){
        int flag = 1;
        // 设定一个标记,辅助交叉遍历两个链表
        // flag 为奇数则插入 l1 的节点, flag 为偶数则插入 l2 的节点
        ListNode head = new ListNode(0);
        ListNode cur=head;
        while (l1 != null || l2 != null){
            if (flag % 2 == 0){
                cur.next = l2;
                l2 = l2.next;
            }else{
                cur.next = l1;
                l1 = l1.next;
            }
            flag++;
            cur = cur.next;
        }
        return head.next;
    }
}

 展平多级双向链表

剑指 Offer II 028. 展平多级双向链表

  • 方法1:深度优先搜索
/*
// Definition for a Node.
class Node {
    public int val;
    public Node prev;
    public Node next;
    public Node child;
};
*/

class Solution {
    public Node flatten(Node head) {
      Node last=dfs(head);
      return head;  
    }
    public Node dfs(Node node){
        Node cur=node;
        Node last=null;// 记录链表的最后一个节点
        while(cur!=null){
            Node next=cur.next;
            if(cur.child!=null){
                Node childLast=dfs(cur.child);
                cur.next=cur.child;
                cur.child.prev=cur;
                 //  如果 next 不为空,就将 last 与 next 相连
                if(next!=null){
                    childLast.next=next;
                    next.prev=childLast;
                }
                cur.child=null;// 将 child 置为空
                last=childLast;//这一步也很重要,想想为什么

            }
            else{
                last=cur;
            }
            cur=next;
        }
        return last;
    }
}

排序的循环链表

剑指 Offer II 029. 排序的循环链表

 

还得再写!!!

  • 方法1:链表模拟

思路

我是先看的上面这个思路然后自己写的代码,我感觉写的没问题,但是可能有很多代码的冗余,因此导致运行超时

这时回头去看思路当中的代码,发现大佬写的代码很妙~

class Solution {
    public Node insert(Node he, int x) {
        Node t = new Node(x);
        t.next = t;
        if (he == null) return t;
        Node ans = he;
        int min = he.val, max = he.val;
        while (he.next != ans) {
            he = he.next;
            min = Math.min(min, he.val);
            max = Math.max(max, he.val);
        }
        if (min == max) {
            t.next = ans.next;
            ans.next = t;
        } else {
            while (!(he.val == max && he.next.val == min)) he = he.next;
            while (!(x <= min || x >= max) && !(he.val <= x && x <= he.next.val)) he = he.next;
            t.next = he.next;
            he.next = t;
        }
        return ans;
    }
}

插入、删除和随机访问都是O(1)的容器

 剑指 Offer II 030. 插入、删除和随机访问都是 O(1) 的容器

还需要再练练!!!

  •  方法1:变长数组+哈希表

思路

class RandomizedSet {
    List<Integer> list;
    Map<Integer,Integer> map;
    Random random;

    /** Initialize your data structure here. */
    public RandomizedSet() {
        list=new ArrayList<>();
        map=new HashMap<>();
        random=new Random();
    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    public boolean insert(int val) {
        if(!map.containsKey(val)){
            int listIndex=list.size();
            list.add(val);
            map.put(val,listIndex);
            return true;
        }
        return false;
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    public boolean remove(int val) {
        if(map.containsKey(val)){
            int mapIndex=map.get(val);
            int last=list.get(list.size()-1);
            list.set(mapIndex,last);
            map.put(last,mapIndex);
            list.remove(list.size()-1);
            map.remove(val);
            return true;
        }
        return false;
    }
    
    /** Get a random element from the set. */
    public int getRandom() {
        int rr=random.nextInt(list.size());
        return list.get(rr);
    }
}

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet obj = new RandomizedSet();
 * boolean param_1 = obj.insert(val);
 * boolean param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

最近最少使用缓存

剑指 Offer II 031. 最近最少使用缓存

 

还要再做!!!

  • 方法1:哈希表+双向链表

思路

class LRUCache {
    class DLinkedNode {
        int key; //初始化key
        int value; //初始化value
        //初始化双向链表前后联系指针
        DLinkedNode prev;
        DLinkedNode next;

        //初始化双向链表
        public DLinkedNode() {
        }

        public DLinkedNode(int _key, int _value) {
            key = _key;
            value = _value;
        }
    }

    //创建哈希表
    HashMap<Integer, DLinkedNode> map = new HashMap<>();
    //链表当前大小
    int size;
    //链表当前容量
    int capacity;
    //链表头部尾部节点
    DLinkedNode head;
    DLinkedNode tail;

    public LRUCache(int capacity) {
        //大小初始化
        this.size = 0;
        //容量初始化
        this.capacity = capacity;
        //使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        //在哈希表中找到key所对应的节点
        DLinkedNode node = map.get(key);
        //如果这个节点不存在则返回-1
        if (node == null) {
            return -1;
        }
        //将这个节点移到头部,再返回这个节点所对应的值
        movetohead(node);
        return node.value;
    }

    public void put(int key, int value) {
        //在哈希表中找到key所对应的节点
        DLinkedNode node = map.get(key);
        //如果这个节点不存在,则创建一个新的节点进行添加
        if (node == null) {
            //创建新节点
            DLinkedNode newnode = new DLinkedNode(key, value);
            //将节点加入哈希表
            map.put(key, newnode);
            //将这个节点添加到双向链表头部
            addtohead(newnode);
            //双向链表容量+1
            size++;
            //如果容量超过最大容量,则需将双向链表尾部的节点移除
            if (size > capacity) {
                //在链表中删去尾部节点,同时哈希表中也移除掉这个节点,并且双向链表容量-1
                DLinkedNode tail = removetail();
                map.remove(tail.key);
                size--;
            }
        } else {
            //如果这个节点存在,则直接修改value
            node.value = value;
            //将这个节点在双向链表中移到头部
            movetohead(node);
        }
    }

    //在头部添加节点的方法
    public void addtohead(DLinkedNode node) {
        //head为虚拟头结点,由于是双向链表,添加一个节点需要建立四个连接关系
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    //移除节点方法
    public void removenode(DLinkedNode node) {
        //跳过这个节点重新建立双向连接关系
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    //将节点移到头部的方法
    public void movetohead(DLinkedNode node) {
        removenode(node);
        addtohead(node);
    }

    //将节点从尾部移除的方法
    public DLinkedNode removetail() {
        //尾部的待删除节点即为虚拟尾结点的前一个节点
        DLinkedNode res = tail.prev;
        //将这个节点删除
        removenode(res);
        return res;
    }
}

变位词组

剑指 Offer II 033. 变位词组

方法1:哈希表+排序

 思路,看的是方法1排序,方法2计数有时间可以看看

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String,List<String>> map=new HashMap<>();
        for(String str:strs){
            char[] cc=str.toCharArray();
            Arrays.sort(cc);
            String key=new String(cc);
            List<String> list=map.getOrDefault(key,new ArrayList<String>());//妙啊妙啊这思路
            list.add(str);
            map.put(key,list);
        }
        return new ArrayList<>(map.values());
    }
}

 最小时间差

剑指 Offer II 035. 最小时间差

这道题代码看懂了,但自己没有单独写一遍,有时间自己要写一遍!!!

  • 方法1:排序

 思路,看的方法1哦

解释下1440怎么来的 

class Solution {
    public int findMinDifference(List<String> timePoints) {
        Collections.sort(timePoints);
        int ans = Integer.MAX_VALUE;
        int t0Minutes = getMinutes(timePoints.get(0));
        int preMinutes = t0Minutes;
        for (int i = 1; i < timePoints.size(); ++i) {
            int minutes = getMinutes(timePoints.get(i));
            ans = Math.min(ans, minutes - preMinutes); // 相邻时间的时间差
            preMinutes = minutes;
        }
        ans = Math.min(ans, t0Minutes + 1440 - preMinutes); // 首尾时间的时间差
        return ans;
    }

    public int getMinutes(String t) {
        return ((t.charAt(0) - '0') * 10 + (t.charAt(1) - '0')) * 60 + (t.charAt(3) - '0') * 10 + (t.charAt(4) - '0');
    }
}

后缀表达式

剑指 Offer II 036. 后缀表达式

  • 方法1:栈
class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList<Integer>();
        int n = tokens.length;
        for (int i = 0; i < n; i++) {
            String token = tokens[i];
            if (isNumber(token)) {
                stack.push(Integer.parseInt(token));
            } else {
                int num2 = stack.pop();
                int num1 = stack.pop();
                switch (token) {
                    case "+":
                        stack.push(num1 + num2);
                        break;
                    case "-":
                        stack.push(num1 - num2);
                        break;
                    case "*":
                        stack.push(num1 * num2);
                        break;
                    case "/":
                        stack.push(num1 / num2);
                        break;
                    default:
                }
            }
        }
        return stack.pop();
    }

    public boolean isNumber(String token) {
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
    }
}

小行星碰撞

剑指 Offer II 037. 小行星碰撞

 

  • 方法1:栈

思路

class Solution {
    public int[] asteroidCollision(int[] asteroids) {
        Stack<Integer> s=new Stack<>();
        int p=0;
        while(p<asteroids.length){
            if(s.empty() || s.peek()<0 || asteroids[p]>0){
                s.push(asteroids[p]);
            }
            else if(s.peek()<=-asteroids[p]){
                if(s.pop()<-asteroids[p]){
                    continue;
                }
            }
            p++;
        }
        int[] arr=new int[s.size()];
        for(int i=arr.length-1;i>=0;i--){
            arr[i]=s.pop();
        }
        return arr;
    }
}

 每日温度

剑指 Offer II 038. 每日温度

  • 方法1:栈

思路

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        Stack<Integer> stack = new Stack<>();
        int[] ret = new int[temperatures.length];
        for (int i = 0; i < temperatures.length; i++) {
            while (!stack.empty() && temperatures[stack.peek()] < temperatures[i]) {
                int index = stack.pop();
                ret[index] = i - index;
            }
            stack.push(i);
        }
        return ret;
    }
}

往完全二叉树添加节点

剑指 Offer II 043. 往完全二叉树添加节点

  •  方法1:层序遍历+队列

思路

class CBTInserter {    
    private Queue<TreeNode> queue;
    private TreeNode root;

    public CBTInserter(TreeNode root) {
        this.root = root;
        
        queue = new LinkedList<>();
        queue.offer(root);
        //在初始化树时就层序遍历到第一个没有左或右子树的节点,即为待插入位置的父节点,在队列头部
        while (queue.peek().left != null && queue.peek().right != null) {
            TreeNode node = queue.poll();
            queue.offer(node.left);
            queue.offer(node.right);
        }
    }
    
    public int insert(int v) {
        //队列头部节点即为待插入位置的父节点
        TreeNode parent = queue.peek();
        TreeNode node = new TreeNode(v);
        //插入左子树,父节点仍无右子树,父节点不变
        //插入右子树,左右子树入列,并将该父节点出列,待插入位置更改为下一个
        if (parent.left == null) {
            parent.left = node;
        } else {
            parent.right = node;
            
            queue.poll();
            queue.offer(parent.left);
            queue.offer(parent.right);
        }
        
        return parent.val;
    }
    
    public TreeNode get_root() {
        return root;
    }
}

 二叉树每层的最大值

剑指 Offer II 044. 二叉树每层的最大值

这道题完完全全是自己写出来的!!呱唧呱唧!当然也是因为自己之前做题思路的积累,在这基础上结合题目进行匹配~

 

  • 方法1:队列

下面这个未通过的例子帮助我修改代码,最终AC

 

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> largestValues(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        if(root==null)return list;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        list.add(root.val);
        while(!queue.isEmpty()){
            int curSize=queue.size();
            int ss=curSize;
            int curCengMax=Integer.MIN_VALUE;
            int flag=0;
            while(curSize!=0){
                TreeNode tmp=queue.poll();
                if(tmp.left!=null && tmp.right!=null){
                    curCengMax=Math.max(curCengMax,Math.max(tmp.left.val,tmp.right.val));
                    queue.offer(tmp.left);
                    queue.offer(tmp.right);
                    flag+=0;
                }
                else{
                    if(tmp.left!=null){
                        curCengMax=Math.max(curCengMax,tmp.left.val);
                        queue.offer(tmp.left);
                        flag+=0;
                    }
                    else if(tmp.right!=null){
                        curCengMax=Math.max(curCengMax,tmp.right.val);
                        queue.offer(tmp.right);
                        flag+=0;
                    }
                    else{
                        flag+=1;
                    }
                }
                curSize--;
            }
            if(flag<ss){
                list.add(curCengMax);
            }
        }
        return list;
    }
}

 二叉树最底层最左边的值

剑指 Offer II 045. 二叉树最底层最左边的值

  • 方法1:层序遍历+队列

原来这道题也是用这样的思路啊,有点没想到哈哈哈还是经验优先啊举一反三能力还差点,再加油!

思路

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        int res=0;
        while(!queue.isEmpty()){
            int curSize=queue.size();
            for(int i=0;i<curSize;i++){
                TreeNode curRoot=queue.poll();
                if(i==0){
                    res=curRoot.val;
                }
                if(curRoot.left!=null){
                    queue.offer(curRoot.left);
                }
                if(curRoot.right!=null){
                    queue.offer(curRoot.right);
                }
            }
        }
        return res;
    }
}

二叉树的右侧视图

剑指 Offer II 046. 二叉树的右侧视图

  • 方法1:层序遍历+队列

这道题导致我没做出来我觉得还是没有把层序遍历+队列的思路融会贯通,导致遇到套用这个思路的题,他变了一些就还是会卡壳。

另外这道题也因为有侧视图如果右子树比左子树短,会不会能看到左子树那边在纠结这个问题。。。就比如下面这个

 

思路

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        Queue<TreeNode> queue=new LinkedList<>();
        List<Integer> list=new ArrayList<>();
        if(root!=null){
            queue.offer(root);
        }
        while(!queue.isEmpty()){
            int curSize=queue.size();
            for(int i=0;i<curSize;i++){
                TreeNode curRoot=queue.poll();
                if(i==curSize-1){
                    list.add(curRoot.val);
                }
                if(curRoot.left!=null){
                    queue.offer(curRoot.left);
                }
                if(curRoot.right!=null){
                    queue.offer(curRoot.right);
                }
            }
        }
        return list;
    }
}

二叉树剪枝

剑指 Offer II 047. 二叉树剪枝

有时间可以再做做,这个思路需要自己看到就能想到才算真的成功

  • 方法1:深度优先搜索

 思路

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode pruneTree(TreeNode root) {
        if(root==null)return null;
        root.left=pruneTree(root.left);
        root.right=pruneTree(root.right);
        if(root.val==0 && root.left==null && root.right==null){
            root=null;
        }
        return root;
    }
}

 从根节点到叶节点的路径数字之和

剑指 Offer II 049. 从根节点到叶节点的路径数字之和

这道题还得再写,思路还得自己想出来才行啊啊啊啊啊

  • 方法1:深度优先搜索

思路

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int sumNumbers(TreeNode root) {
        return dfs(root,0);
    }
    public int dfs(TreeNode node,int val){
        if(node==null)return 0;
        val=val*10+node.val;
        if(node.left==null && node.right==null){
            return val;
        }
        return dfs(node.left,val)+dfs(node.right,val);
    }
}

向下的路径节点之和

剑指 Offer II 050. 向下的路径节点之和

两个思路的代码都是直接拿过来的,自己没写呢,还得再看这道题!!这种题怎么这么难啊对我来说...

两个方法的思路

  • 方法1:深度优先搜索

 

class Solution {
    public int pathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return 0;
        }

        int ret = rootSum(root, targetSum);
        ret += pathSum(root.left, targetSum);
        ret += pathSum(root.right, targetSum);
        return ret;
    }

    public int rootSum(TreeNode root, int targetSum) {
        int ret = 0;

        if (root == null) {
            return 0;
        }
        int val = root.val;
        if (val == targetSum) {
            ret++;
        } 

        ret += rootSum(root.left, targetSum - val);
        ret += rootSum(root.right, targetSum - val);
        return ret;
    }
}

  • 方法2:前缀和

 

class Solution {
    public int pathSum(TreeNode root, int targetSum) {
        HashMap<Long, Integer> prefix = new HashMap<>();
        prefix.put(0L, 1);
        return dfs(root, prefix, 0, targetSum);
    }

    public int dfs(TreeNode root, Map<Long, Integer> prefix, long curr, int targetSum) {
        if (root == null) {
            return 0;
        }

        int ret = 0;
        curr += root.val;

        ret = prefix.getOrDefault(curr - targetSum, 0);
        prefix.put(curr, prefix.getOrDefault(curr, 0) + 1);
        ret += dfs(root.left, prefix, curr, targetSum);
        ret += dfs(root.right, prefix, curr, targetSum);
        prefix.put(curr, prefix.getOrDefault(curr, 0) - 1);

        return ret;
    }
}

二叉搜索树中的中序后继 

 剑指 Offer II 053. 二叉搜索树中的中序后继

还得再做!!思路得是自己想出来的才行!!

以下两种方法思路

  • 方法1:中序遍历迭代解法(使用栈)
class Solution {
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
        TreeNode prev = null, curr = root;
        while (!stack.isEmpty() || curr != null) {
            while (curr != null) {
                stack.push(curr);
                curr = curr.left;
            }
            curr = stack.pop();
            if (prev == p) {
                return curr;
            }
            prev = curr;
            curr = curr.right;
        }
        return null;
    }
}

  • 方法2:利用二叉搜索树特点
class Solution {
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        TreeNode successor = null;
        if (p.right != null) {
            successor = p.right;
            while (successor.left != null) {
                successor = successor.left;
            }
            return successor;
        }
        TreeNode node = root;
        while (node != null) {
            if (node.val > p.val) {
                successor = node;
                node = node.left;
            } else {
                node = node.right;
            }
        }
        return successor;
    }
}

所有大于等于节点的值之和

剑指 Offer II 054. 所有大于等于节点的值之和

 

还得再做

  • 方法1:反序中序遍历

思路

class Solution {
    int sum = 0;

    public TreeNode convertBST(TreeNode root) {
        if (root != null) {
            convertBST(root.right);
            sum += root.val;
            root.val = sum;
            convertBST(root.left);
        }
        return root;
    }
}

 二叉搜索树迭代器

剑指 Offer II 055. 二叉搜索树迭代器

 两个方法都看懂了,会做了,但是自己没有写,只是把代码贴上来了,下次自己写

以下两个方法的思路

  • 方法1:递归
class BSTIterator {
    private int idx;
    private List<Integer> arr;

    public BSTIterator(TreeNode root) {
        idx = 0;
        arr = new ArrayList<Integer>();
        inorderTraversal(root, arr);
    }

    public int next() {
        return arr.get(idx++);
    }

    public boolean hasNext() {
        return idx < arr.size();
    }

    private void inorderTraversal(TreeNode root, List<Integer> arr) {
        if (root == null) {
            return;
        }
        inorderTraversal(root.left, arr);
        arr.add(root.val);
        inorderTraversal(root.right, arr);
    }
}

  • 方法2:迭代
class BSTIterator {
    private TreeNode cur;
    private Deque<TreeNode> stack;

    public BSTIterator(TreeNode root) {
        cur = root;
        stack = new LinkedList<TreeNode>();
    }
    
    public int next() {
        while (cur != null) {
            stack.push(cur);
            cur = cur.left;
        }
        cur = stack.pop();
        int ret = cur.val;
        cur = cur.right;
        return ret;
    }
    
    public boolean hasNext() {
        return cur != null || !stack.isEmpty();
    }
}

 值和下标之差都在给定的范围内

剑指 Offer II 057. 值和下标之差都在给定的范围内

 没看懂,所以还没写

思路

日程表

剑指 Offer II 058. 日程表

  • 方法1:直接遍历

思路

class MyCalendar {
    List<int[]> booked;

    public MyCalendar() {
        booked = new ArrayList<int[]>();
    }

    public boolean book(int start, int end) {
        for (int[] arr : booked) {
            int l = arr[0], r = arr[1];
            if (l < end && start < r) {
                return false;
            }
        }
        booked.add(new int[]{start, end});
        return true;
    }
}

  • 方法2:平衡二叉树

思路

class MyCalendar {

    private TreeMap<Integer, Integer> map;

    public MyCalendar() {
        this.map = new TreeMap<>();
    }
    
    public boolean book(int start, int end) {

        Map.Entry<Integer, Integer> entry1 = map.floorEntry(start);
        Map.Entry<Integer, Integer> entry2 = map.ceilingEntry(start);

        if (entry1 != null && entry1.getValue() > start) {
            return false;
        }

        if (entry2 != null && entry2.getKey() < end) {
            return false;
        }

        map.put(start, end);
        return true;
    }
}

/**
 * Your MyCalendar object will be instantiated and called as such:
 * MyCalendar obj = new MyCalendar();
 * boolean param_1 = obj.book(start,end);
 */

出现频率最高的k个数字

剑指 Offer II 060. 出现频率最高的 k 个数字

  • 方法1:堆(优先级队列)

思路

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer,Integer> map=new HashMap<>();
        for(int n: nums){
            map.put(n,map.getOrDefault(n,0)+1);
        }
        PriorityQueue<int[]> pq=new PriorityQueue<>(new Comparator<int[]>(){
            public int compare(int[] m,int[] n){
                return m[1]-n[1];
            }
        });
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){
            int kk=entry.getKey();
            int vv=entry.getValue();
            if(pq.size()==k){
                if(pq.peek()[1]<vv){
                    pq.poll();
                    pq.offer(new int[]{kk,vv});
                }
            }
            else{
                pq.offer(new int[]{kk,vv});
            }
        }
        int[] res=new int[k];
        for(int i=0;i<k;i++){
            res[i]=pq.poll()[0];
        }
        return res;
    }
}

 和最小的k个数对

剑指 Offer II 061. 和最小的 k 个数对

代码自己没写,但差不多看懂了,还得自己写出来才行

  • 方法1:优先级队列

代码来自这里

思路来自这里

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> {
            return (nums1[o1[0]] + nums2[o1[1]]) - (nums1[o2[0]] + nums2[o2[1]]);
        });

        // Math.min(nums1.length, k)加快处理速度
        for(int i = 0; i < Math.min(nums1.length, k); i ++) {
            pq.offer(new int[] {i, 0});
        }

        List<List<Integer>> list = new ArrayList<>();
        while(k -- > 0 && !pq.isEmpty()) {
            int[] arr = pq.poll();
            list.add(Arrays.asList(nums1[arr[0]], nums2[arr[1]]));
            // 上一个循环已经考虑完所有nums1的数,这个循环考虑nums2中的数
            if(++arr[1] < nums2.length) {
                pq.offer(new int[] {arr[0], arr[1]});
            }
        }
        return list;
    }
}

先到这里吧,后面就好多是没学过的知识(比如前缀树),有时间精力再做吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/338580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++复习笔记8

泛型编程&#xff1a;编写的是与类型无关的通用代码&#xff0c;是代码复用的一种手段&#xff0c;模板是泛型编程的基础。 1.函数模板&#xff1a;类型参数化&#xff0c;增加代码复用性。例如对于swap函数&#xff0c;不同类型之间进行交换都需要进行重载&#xff0c;但是函数…

K_A12_003 基于STM32等单片机采集光敏二极管模块参数 串口与OLED0.96双显示

K_A12_003 基于STM32等单片机采集光敏二极管模块参数 串口与OLED0.96双显示一、资源说明二、基本参数参数引脚说明三、驱动说明IIC地址/采集通道选择/时序对应程序:四、部分代码说明1、接线引脚定义1.1、STC89C52RC光敏二极管模块1.2、STM32F103C8T6光敏二极管模块五、基础知识…

面向 3DoF+的虚拟视图合成算法研究(陈 莹)

面向 3DoF的虚拟视图合成算法研究&#xff08;陈 莹&#xff09;论文贡献多视点联合的虚拟视图合成算法视图合成中多视点伪影消除算法面向虚拟视图合成算法的 3DoF系统基于深度的虚拟视图合成算法视点映射&#xff08;3D-Warping&#xff09;三维空间映射变换&#xff08;3D-Wa…

TYPE-C 手机/电脑同时充电直播 视频采集卡方案

Type-C音视频采集卡有什么作用&#xff1f; ​能够帮助专业用户和游戏玩家迅速搭建简单、高性价比的音视频解决方案。可将新闻联播、体育竞赛、视频教学课程、网络视频等&#xff0c;通过HDMI高清视频信号分段或整体录制在本地计算机共享使用。支持多种带HDMI接口的游戏机设备…

生物素-琥珀酰亚胺酯Biotin-NHS;CAS号:35013-72-0;可对溶液中的抗体,蛋白质和任何其他含伯胺的大分子进行简单有效的生物素标记。

结构式&#xff1a; ​ 生物素-琥珀酰亚胺酯Biotin NHS CAS号&#xff1a;35013-72-0 英文名称&#xff1a;Biotin-NHS 中文名称&#xff1a;D-生物素 N-羟基琥珀酰亚胺酯&#xff1b;生物素&#xff0d;琥珀酰亚胺酯 CAS号&#xff1a;35013-72-0 密度&#xff1a;1.50.1 …

vue项目第二天

项目中使用element-ui库中文网https://element.eleme.cn/#/zh-CN安装命令npm install element-ui安装按需加载babel插件npm install babel-plugin-component -Dnpm i //可以通过npm i 的指令让配置刷新重新配置一下项目中使用element-ui组件抽离文件中按需使用element ui &…

sqoop 数据同步方案理解+问题解决

sqoop数据同步——问题与解决方案 1、sqoop导出oracle数据&#xff0c;数据源无法选择表空间&#xff0c;只能指定默认表空间的表。 方案&#xff1a;不指定数据源的表名&#xff0c;而是使用–query&#xff0c;利用sql语句把数据带出来。 例&#xff1a;--query "SELE…

【游戏逆向】内存构造发包结构体实现协议发送简单代码

捕捉到游戏的封包以后 我们可以对其进行发送来实现功能 基本有两种方式 第一种直接调用明文发包函数 第二种构造明文封包以后&#xff0c;再调用加密算法&#xff0c;然后通过send等发包函数对其进行发送 如果能够把加密算法分析透彻的话&#xff0c;第二种方式是最好的 …

element Ui树状图控件 spring boot Vue 实现角色授权功能

目录 前言&#xff1a; 二. element ui 2.1官网提供的核心代码 三.表结构 ​编辑 四.后端 4.1功能分析 4.2实体类 4.3 查询全部权限显示的结果 4.2修改角色权限的后台方法 五.vue 5.0代码总览 5.1树形图 5.2所需要的绑定数据 5.3所需方法 前言&#xff1a; 先上图…

微搭低代码从入门到精通04-创建自定义应用

微搭中的应用分为两类&#xff0c;模型应用和自定义应用。上一篇我们介绍了模型应用的创建方法&#xff0c;本篇我们介绍一下自定义应用的创建方法。 登录微搭的控制台&#xff0c;在左侧的菜单里点击应用&#xff0c;点击新建应用&#xff0c;选择新建自定义应用 输入应用的名…

logstash毫秒时间戳转日期以及使用业务日志时间戳替换原始@timestamp

文章目录问题解决方式参考问题 在使用Kibana观察日志排查问题时发现存在很多组的timestamp 数据一样&#xff0c;如下所示 详细观察内部数据发现其中日志数据有一个timestamp字段保存的是业务日志的毫秒级时间戳&#xff0c;经过和timestamp数据对比发现二者的时间不匹配。经…

1630_GNU的二进制分析工具nm简单使用探索

全部学习汇总&#xff1a; GreyZhang/toolbox: 常用的工具使用查询&#xff0c;非教程&#xff0c;仅作为自我参考&#xff01; (github.com) GNU有一套二进制的分析工具&#xff0c;之前是用过objdump的&#xff0c;但是也没有系统掌握。如果做底层软件的设计&#xff0c;这些…

OpenWrt路由器设置域名动态解析手把手教程

文章目录0、前言1、准备工作2、详细步骤2.1、OpenWrt路由器软件包安装2.2、防火墙放行入站数据&#xff08;修改为“接受”并保存应用&#xff09;2.3、域名解析服务商对域名的解析设置2.4、路由器中动态域名插件的设置0、前言 因为一直用着内网穿透&#xff08;zerotier或者是…

SQLyog Ultimate 13.2 Crack

SQLyog终极版 MySQL开发工具 让您的数据库开发人员在短时间内完成大量工作并不是确保质量结果的最佳方式。当涉及到您的 MySQL 数据库时&#xff0c;您不想偷工减料 — 尤其是当这样做会导致代价高昂的中断时。 使用 SQLyog 获得可靠的数据库备份和数据同步&#xff1a;一个强大…

HTTP协议路由转发小结

问题介绍&#xff1a;个人电脑 hosts 文件配置如下&#xff1a; xx.xx.xx.243 xxx.domain.com 其中 xx.xx.xx.243 是里约网关的服务器ip&#xff0c;而应用部署在 xx.xx.xx.93 上&#xff0c;但最终请求为何会转发到93这台服务器上呢&#xff1f; 原因是里约网关配置了转发规则…

【hello, world】计算机系统漫游

文章目录hello程序信息就是位 上下文程序被其他程序翻译成不同的格式预处理阶段编译阶段汇编阶段链接阶段了解编译系统如何工作是大有益处的优化程序性能理解链接时出现的错误避免安全漏洞处理器读并解释储存在内存中的指令系统的硬件组成总线I/O设备主存处理器运行hello程序高…

web3:区块链共识机制系列-POS(Proof of Stake)股权证明算法

web3相关学习一并收录至该博客&#xff1a;web3学习博客目录大全 前情衔接&#xff1a;web3:区块链常见的几大共识机制及优缺点 目录前言算法公式与原理算法公式运作原理以Peer Coin为例缺陷优点缺点特点分类发展历程casper协议1.什么是无成本利益关系问题2.引入casper协议解决…

力扣33.搜索旋转排序数组

文章目录力扣33.搜索旋转排序数组题目描述方法1&#xff1a;二分查找力扣33.搜索旋转排序数组 题目描述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&…

Android框架WiFi架构

同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 一、wpa_supplicant:wpa_supplicant本身开源项目源码,被谷歌收购之后加入Android移…

Python中的类和对象(4)

1. 构造函数 &#xff08;_ init _(self[,……])&#xff09; 在类中定义 _init _() 方法&#xff0c;可以实现在实例化对象的时候进行个性化定制&#xff1a; >>> class C: ... def __init__(self, x, y): ... self.x x ... self.y y ... …