LeetGo 日更!

606 阅读20分钟

1220. 统计元音字母序列的数目

这可能是最简单的hard题目了吧!如果你秒解,那也可以对外面说我也是秒解hard的人了。

给你一个整数 n,请你帮忙统计一下我们可以按下述规则形成多少个长度为 n 的字符串:

字符串中的每个字符都应当是小写元音字母('a', 'e', 'i', 'o', 'u') 每个元音 'a' 后面都只能跟着 'e' 每个元音 'e' 后面只能跟着 'a' 或者是 'i' 每个元音 'i' 后面 不能 再跟着另一个 'i' 每个元音 'o' 后面只能跟着 'i' 或者是 'u' 每个元音 'u' 后面只能跟着 'a' 由于答案可能会很大,所以请你返回 模 10^9 + 7 之后的结果。

示例 1:

输入:n = 1
输出:5
解释:所有可能的字符串分别是:"a", "e", "i" , "o""u"

示例 2:

输入:n = 2
输出:10
解释:所有可能的字符串分别是:"ae", "ea", "ei", "ia", "ie", "io", "iu", "oi", "ou""ua"

示例 3:

输入:n = 5
输出:68

提示:

  • 1 <= n <= 2 * 10^4

思路:反向思考,a只能出现在某些特定元素的后面,同理思考其他的元音字母就可以得到列出状态装换方程。

class Solution {
    public int countVowelPermutation(int n) {
        int M = 1000000007;
        long  a=1,e=1,i=1,o=1,u=1;
        for(int k=2;k<=n;k++){
            long  aa=(e+i+u)%M;
            long  ee = (a+i)%M;
            long  ii = (e+o)%M;
            long  oo = i;
            long  uu = (o+i)%M;
            a = aa;
            e = ee;
            i = ii;
            o = oo;
            u = uu;
        }
        return (int)((a+e+i+o+u)%M);
    }
}

572. 另一个树的子树

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

示例 1: 给定的树 s:

     3
    / \
   4   5
  / \
 1   2

给定的树 t:

   4 
  / \
 1   2

返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。

示例 2: 给定的树 s:

     3
    / \
   4   5
  / \
 1   2
    /
   0

给定的树 t:

   4
  / \
 1   2

思路:

​ 一个树是另一个树的子树 则

  • 要么这两个树相等
  • 要么这个树是左树的子树
  • 要么这个树hi右树的子树
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        //这边有两个递归 主函数一个递归 子函数一个递归 两个递归相互嵌套 
        //主函数递归 是将每个节点作为根节点
        //子函数递归 是将相等的两个递归节点继续进行比较
        if(s==null){
            return false;
        }
        return isSubtree(s.left,t)||isSubtree(s.right,t)||isSubtree2(s,t);
    }
    private boolean isSubtree2(TreeNode s,TreeNode t){
        if(s==null&&t==null){
            return true;
        }
        if(s==null||t==null){
            return false;
        }
        return s.val==t.val&&isSubtree2(s.left,t.left)&&isSubtree2(s.right,t.right);
    }
    
}

445. 两数相加 II

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

进阶:

如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。

示例:

输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 8 -> 0 -> 7

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        //建立两个栈用来保存两个listnode里面中的元素 再利用一个add 用来保存进位元素
        //或者使用递归来做,但是两者的效果是一样的 
        Stack<Integer> ll1=new Stack<Integer>();
        Stack<Integer> ll2=new Stack<Integer>();
        while(l1!=null){
            ll1.add(l1.val);
            l1=l1.next;
        }
        while(l2!=null){
            ll2.add(l2.val);
            l2=l2.next;
        }
        int add=0;
        ListNode temp=null;
        while(!ll1.isEmpty()||!ll2.isEmpty()||add!=0){
             int sum=(ll1.isEmpty()?0:ll1.pop())+(ll2.isEmpty()?0:ll2.pop())+add;
             add=sum/10;
             ListNode tt = new ListNode(sum%10);
             tt.next=temp;
             temp=tt;
        }
        return temp;
    }
}

面试题29. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5] 示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] 输出:[1,2,3,4,8,12,11,10,9,5,6,7]

限制:

  • 0 <= matrix.length <= 100
  • 0 <= matrix[i].length <= 100

思路 按照顺序进行遍历 要记住什么时候退出while循环

class Solution {
      public int[] spiralOrder(int[][] matrix) {
        //顺时针打印矩阵
        //难点如何控制旋转的方向
        //旋转的顺序 左下右上
        if(matrix==null||matrix.length==0){
            return new int[]{};
        }
        int left=0;
        int down=matrix.length-1;
        int right=matrix[0].length-1;
        int up=0;
        //while结束的条件可以改为当前上下左右界限出现痛处就终止
        int []res=new int[(down+1)*(right+1)];
        int count=0;
        while(left<=right&&up<=down){
            //左边移动
            for(int i=left;i<=right;i++){
                res[count++]=matrix[up][i];
            }
            //下边移动
            for(int i=up+1;i<=down;i++){
                res[count++]=matrix[i][right];
            }
            if(up!=down){
                for(int i=right-1;i>=left;i--){
                    res[count++]=matrix[down][i];
                }
            }
           
            //上边移动
            if(right!=left){
                for (int i = down-1; i >up; i--) {
                    res[count++]=matrix[i][left];
                }
            }
            right--;
            up++;
            left++;
            down--;
        }
        return res;
    }
}
class Solution {
      public int[] spiralOrder(int[][] matrix) {
          //顺时针打印矩阵
          //偏向与设计题目
          int row=matrix.length;
          if(row==0){
              int a[]=new int[]{};
              return   a;
          }
          int col=matrix[0].length;
          int res[]=new int[row*col];
          int start=0;
          //定义系统的变量
          int left=0;
          int right=col-1;
          int top=0;
          int bottom=row-1;
          while(true){
              //右边移动 上面一层减少
              int temp=left;
              while(temp<=right){
                res[start++]=matrix[top][temp++];
              }
              if(++top>bottom){
                  break;
              }
              //下面移动 右边一层减少
              temp=top;
              while(temp<=bottom){
                  res[start++]=matrix[temp++][right];
              }
              if(--right<left){
                  break;
              }
              //左边移动 下边一层减少
              temp=right;
              while(temp>=left){
                  res[start++]=matrix[bottom][temp--];
              }
              if(--bottom<top){
                  break;
              }
              //上边移动 左边一层减少
              temp=bottom;
              while(temp>=top){
                  res[start++]=matrix[temp][left];
                  temp--;
              }
              if(++left>right){
                  break;
              }
          }
          return res;
    }
}

148. 排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3 输出: 1->2->3->4 示例 2:

输入: -1->5->3->4->0 输出: -1->0->3->4->5

思路:根据时间复杂度 可以得出使用归并的主体思想

class Solution {
    public ListNode sortList(ListNode head) {
        //链表排序 应该是归并排序吧
        //主要的几个步骤1. 找到链表中间的元素 进行拆分链表 2. 开始链表的合并
        if(head==null){
            return null;
        }       
        return mergeSort(head);
    }
    public ListNode mergeSort(ListNode head){
        if(head.next==null){
            return head;
        }
        //快慢指针找到中间元素
        ListNode tempHead=head;
        ListNode mid=head;
        //要记录中间元素的值 然后进行置null操作不然会出现重复mergeSort问题
        ListNode pre=null;
        while(head!=null&&head.next!=null){
            pre=mid;
            head=head.next.next;
            mid=mid.next;
        }
        pre.next=null;
        ListNode left = mergeSort(tempHead);
        ListNode right= mergeSort(mid);
        return merge(left,right);
    }
    //merge两个链表
    public ListNode merge(ListNode left,ListNode right){
        ListNode temp=new ListNode(0);
        ListNode res=temp;
        while(left!=null&&right!=null){
            if(left.val<right.val){
                temp.next=left;
                temp=temp.next;
                left=left.next;
            }else{
                temp.next=right;
                temp=temp.next;
                right=right.next;
            }
        }
        if(left!=null){
            temp.next=left;
        }
        if(right!=null){
            temp.next=right;
        }
        return res.next;
    }
}

76. 最小覆盖子串

image-20200510141655111

class Solution {
   public static String minWindow(String s, String t) {
        if (s == null || s == "" || t == null || t == "" || s.length() < t.length()) {
            return "";
        }
        //用来统计t中每个字符出现次数
        int[] needs = new int[128];
        //用来统计滑动窗口中每个字符出现次数
        int[] window = new int[128];

        for (int i = 0; i < t.length(); i++) {
            needs[t.charAt(i)]++;
        }

        int left = 0;
        int right = 0;

        String res = "";

        //目前有多少个字符
        int count = 0;

        //用来记录最短需要多少个字符。
        int minLength = s.length() + 1;

        while (right < s.length()) {
            char ch = s.charAt(right);
            window[ch]++;
            //如果加入窗口的值,是小于needs的值,就count++
            if (needs[ch] > 0 && needs[ch] >= window[ch]) {
                count++;
            }

            //移动到不满足条件为止
            while (count == t.length()) {
                ch = s.charAt(left);
                if (needs[ch] > 0 && needs[ch] >= window[ch]) {
                    count--;
                }
                if (right - left + 1 < minLength) {
                    minLength = right - left + 1;
                    res = s.substring(left, right + 1);

                }
                window[ch]--;
                left++;

            }
            right++;

        }
        return res;
    }
}

200. 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入: 11110 11010 11000 00000 输出: 1

示例 2:

输入: 11000 11000 00100 00011 输出: 3 解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。

思路

  1. 广度优先遍历直接上就可以
class Solution {
    public int numIslands(char[][] grid) {
        if(grid==null||grid.length==0){
            return 0;
        }
        //思路暴力+递归
        int res=0;
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j]=='1'){
                    //进入循环
                    dfs(i,j,grid);
                    res++;
                }
            }
        }
        return res;
    }
    //上左右进行置零操作
    private void dfs(int i,int j,char [][]grid){
        int row=grid.length;
        int col=grid[0].length;
        if(i<0||i>=row||j<0||j>=col||grid[i][j]=='0'){
            return;
        }else{
            grid[i][j]='0';
            dfs(i+1,j,grid);
            dfs(i-1,j,grid);
            dfs(i,j-1,grid);
            dfs(i,j+1,grid);
        }
    }
}

329. 矩阵中的最长递增路径

给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

示例 1:

输入: nums = [ [9,9,4], [6,6,8], [2,1,1] ] 输出: 4 解释: 最长递增路径为 [1, 2, 6, 9]。 示例 2:

输入: nums = [ [3,4,5], [3,2,6], [2,2,1] ] 输出: 4 解释: 最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。

class Solution {
    public int longestIncreasingPath(int[][] matrix) {
        //记忆化搜索目前理解  感觉有点像dp的感觉 bfs+记录
        if(matrix==null||matrix.length==0||matrix[0].length==0){
            return 0;
        }
        int max=0;
        int row=matrix.length;
        int col=matrix[0].length;
        int dp[][]=new int[row][col];
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                max=Math.max(max,loop(matrix,Integer.MIN_VALUE,dp,i,j));
            }
        }
        return max;
    }
    private int loop(int [][]mat,int pre,int [][]dp,int i ,int j){
        //这边给的一个条件是递增所以是<=就跳过元素
        if(i<0||j<0||i>=mat.length||j>=mat[0].length||mat[i][j]<=pre){
            return 0;
        }
        //使用记忆数据避免重复计算
        if(dp[i][j]!=0){
            return dp[i][j];
        }
        int max=0;
        max=Math.max(max,loop(mat,mat[i][j],dp,i-1,j));
        max=Math.max(max,loop(mat,mat[i][j],dp,i+1,j));
        max=Math.max(max,loop(mat,mat[i][j],dp,i,j+1));
        max=Math.max(max,loop(mat,mat[i][j],dp,i,j-1));
        dp[i][j]=1+max;
        return dp[i][j];
    }
}

179. 最大数

给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。

示例 1:

输入: [10,2]
输出: 210
示例 2:

输入: [3,30,34,5,9]
输出: 9534330
说明: 输出结果可能非常大,所以你需要返回一个字符串而不是整数。
class Solution {
    public String largestNumber(int[] nums) {
        //桶排序实现比较复杂
        //直接重新实现comparator 比较器
        if(nums==null||nums.length==0){
            return "";
        }
        //string到字符串的转换 然后重新定义比较函数
        String[]str=new String[nums.length];
        for(int i=0;i<str.length;i++){
            str[i]=String.valueOf(nums[i]);
        }
        //实现比较器
        Arrays.sort(str,new Comparator<String>(){
            @Override 
            public int compare(String o1,String o2){
                //compareTo 返回值为1 为升序 ,-1 降序
                //返回1 的时候进行交换 -1不交换D
                return (o2+o1).compareTo((o1+o2));
            }
        });
        StringBuilder sb=new StringBuilder();
        for(String s:str){
            sb.append(s);
        }
        String res=sb.toString();
        if(res.charAt(0)=='0'){
            return "0";
        }
        return res;
    }
}

215. 数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4 输出: 4 说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

class Solution {
    public int findKthLargest(int[] nums, int k) {
        //创建一个优先队列直接放入元素 创建一个最小堆 每次将最小的元素抛出保存下来的元素就是最大的元素
        //排序完 后选取元素
        //计数排序 就是太耗空间 
        //快排的切分思想
        //库函数直接排序
        int len=nums.length;
        int left=0;
        int right=len-1;
        //第k个元素
        int target=len-k;
        while(true){
            //快速排序 每次都能定位一个元素
            int index=partition(nums,left,right);
            if(index==target){
                return nums[index];
            }else if(index<target){
                left=index+1;
            }else{
                right=index-1;
            }
        }
    }
    public int partition(int []nums,int left,int right){
        int pivot=nums[left];
        int j=left;
        for(int i=left+1;i<=right;i++){
            if(nums[i]<pivot){
                //小于 pivot 元素交换到前面 j每次指向的是下一个比pivot小的元素
                j++;
                //因为这边有一个j++使得 所以left元素会被滞空
                swap(nums,j,i);
            }
        }
        swap(nums,j,left);
        return j;
    }
    private void swap(int []nums,int index1,int index2){
        int temp=nums[index1];
        nums[index1]=nums[index2];
        nums[index2]=temp;
    }
}

25. K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

示例:

给你这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5

说明:

你的算法只能使用常数的额外空间。 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

思路:

  1. 直接使用栈的每次保存节点然后重建一个链表 若k的值比较大,空间需要开比较大
  2. 使用递归的思路,先获取k个元素后 k+1元素反转链表的头节点然后将未反转链表的头节点链接到反转链表头节点,依次进行循环,进入递归
  3. 非递归,每次反转前k个元素 之后将反转后k链表的最后一个节点 当前新的开始节点进入第二轮遍历

递归解法

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        //递归写法
        ListNode cur=head;
        int count=0;
        //获取到k+1个链表节点(链表从1开始)
        while(cur!=null&&count!=k){
            cur=cur.next;
            count++;
        }
        if(count==k){
            //cur为K+1为头节点的反向节点
            cur=reverseKGroup(cur,k);
            //head未反转前的头节点
            //循环开始反转当前列表
            while(count-->0){
                ListNode temp=head.next;
                head.next=cur;
                cur=head;
                head=temp;
            }
            //若直接返回cur则count<k的时候cur为遍历链表的最后一个元素 而不是不足k的链表的头节点
            head=cur;
        }
        return head;
    }
}

非递归法

		int n=0;
        //计算链表长度
        for(ListNode i=head;i!=null;n++,i=i.next);
		//建立虚拟头节点
        ListNode dmy=new ListNode(0);
        dmy.next=head;
		//通过n>=k来避免最后不足k的链表的反转
        for(ListNode pre=dmy,tail=head;n>=k;n-=k){
            //反转节点
            for(int i=1;i<k;i++){
                ListNode next=tail.next.next;
                tail.next.next=pre.next;
                pre.next=tail.next;
                tail.next=next;
            }
            //pre指向反转链表的最后一个元素
            pre=tail;
            //tail指向将要开始反转链表的头节点
            //反转后的pre和tail 跟原先的dmy 以及head是相同的
            tail=tail.next;
        }
        return dmy.next;

212. 单词搜索 II

难度困难155

给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例:

输入: 
words = ["oath","pea","eat","rain"] and board =
[
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]

输出: ["eat","oath"]

说明: 你可以假设所有输入都由小写字母 a-z 组成。

提示:

  • 你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
  • 如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)
class Solution {
    public List<String> findWords(char[][] board, String[] words) {
        //构建字典树
        WordTrie myTrie = new WordTrie();
        TrieNode root = myTrie.root;
        //插入数据
        for (String word : words){
            myTrie.insert(word);
        }

        //构建结果集容器
        List<String> result = new LinkedList<>();
        //矩阵行数
        int m = board.length;
        //矩阵列数
        int n = board[0].length;
        //存储该节点是否访问
        boolean[][] visited = new boolean[n][m];
        //遍历整个二维数组
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                find(board, visited, i, j, m, n, result, root);
            }
        }
        return result;
    }

    private void find(char[][] board, boolean[][] visited, int i, int j, int m, int n, List<String> result, TrieNode cur) {
        //边界判断以及是否已经访问判断
        if (i < 0 || i >= m || j < 0 || j >= n || visited[j][i])
            return;
        //获取子节点状态,判断其是否有子节点
        cur = cur.child[board[i][j] - 'a'];
        if (cur == null) {
            return;
        }
        //修改节点状态,防止重复访问
        visited[j][i] = true;
        //找到单词加入
        if (cur.isEnd) {
            result.add(cur.val);
            //找到单词后,修改字典树内叶子节点状态为false,防止出现重复单词
            cur.isEnd = false;
        }
        find(board, visited, i + 1, j, m, n, result, cur);
        find(board, visited, i - 1, j, m, n, result, cur);
        find(board, visited, i, j + 1, m, n, result, cur);
        find(board, visited, i, j - 1, m, n, result, cur);
        //最后修改节点状态为未访问状态
        visited[j][i] = false;
    }


    /**
     * 字典树
     */
    class WordTrie {
        //创建根节点
        TrieNode root = new TrieNode();

        void insert(String s) {
            TrieNode cur = root;
            for (char c : s.toCharArray()) {
                //判断是否存在该字符的节点,不存在则创建
                if (cur.child[c - 'a'] == null) {
                    cur.child[c - 'a'] = new TrieNode();
                    cur = cur.child[c - 'a'];
                } else
                    cur = cur.child[c - 'a'];
            }
            //遍历结束后,修改叶子节点的状态,并存储字符串
            cur.isEnd = true;
            cur.val = s;
        }
    }

    /**
     * 字典树节点
     */
    class TrieNode {
        /**
         * 存储最后节点的字符串
         */
        String val;
        /**
         * 根据字符排序,[a,b,c,……,z]
         */
        TrieNode[] child = new TrieNode[26];
        /**
         * 是否是最后叶子节点
         */
        boolean isEnd = false;
    }
    class Solution {

  public List<String> findWords(char[][] board, String[] words) {

    //主要思路创建一棵字典树 配合回溯的算法减少多个单词都进行回溯


    private void find(char[][]board,boolean [][]visited,int i,int j,int m,int n,List<String> result,TireNode cur){

      if(i<0||i>=m||j<0||j>=n||visited[j][i])

      return;

      cur=cur.child[board[i][j]-'a'];

      if(cur==null){

        return;

      }

      visited[j][i]=true;

    }

    class WordTrie{

      TireNode root=new TireNode();

      void insert(Stirng s){

        TireNode cur=root;

        for(char c:s.toCharArray()){

          if(cur.child[c-'a']==null){

            cur.child[c-'a']=new TireNode();

            cur=cur.child[c-'a'];

          }else{

            cur=cur.child[c-'a'];

          }

        }

        cur.isEnd=true;

        cur.val=s;

      }

    }

    //字典树节点

    class TireNode{

      //存放最后的节点

      String val;

      TireNode[]child=new TireNode[26];

      boolean isEnd=false;

    }

  }

}
}

1371. 每个元音包含偶数次的最长子字符串

难度中等145

给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 'a','e','i','o','u' ,在子字符串中都恰好出现了偶数次。

示例 1:

输入:s = "eleetminicoworoep"
输出:13
解释:最长子字符串是 "leetminicowor" ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。

示例 2:

输入:s = "leetcodeisgreat"
输出:5
解释:最长子字符串是 "leetc" ,其中包含 2 个 e 。

示例 3:

输入:s = "bcbcbc"
输出:6
解释:这个示例中,字符串 "bcbcbc" 本身就是最长的,因为所有的元音 a,e,i,o,u 都出现了 0 次。

提示:

  • 1 <= s.length <= 5 x 10^5
  • s 只包含小写英文字母。

思路:

一开始想到用滑窗来做,发现窗户都划没了还是无法实现,看答案学会使用 map记录 数值,以及异或前缀和构建字典树的方式来实现

class Solution {
    private static final String VOWELS="aeiou";
    public int findTheLongestSubstring(String s) {
        Map<Integer,Integer> map=new HashMap<>();
        int size=s.length();
        int state=0;
        int maxSize=0;
        map.putIfAbsent(0,-1);
        for(int i=0;i<size;i++){
            for(int k=0;k<VOWELS.length();k++){
                if(s.charAt(i)==VOWELS.charAt(k)){
                    state^=(1<<(VOWELS.length()-k-1));
                    break;
                }
            }
            if(map.containsKey(state)){
                maxSize=Math.max(maxSize,i-map.get(state));
            }
            map.putIfAbsent(state,i);
        }
        return maxSize;
    }
}

227. 基本计算器 II

实现一个基本的计算器来计算一个简单的字符串表达式的值。

字符串表达式仅包含非负整数,+-*/ 四种运算符和空格 。 整数除法仅保留整数部分。

示例 1:

输入: "3+2*2"
输出: 7

示例 2:

输入: " 3/2 "
输出: 1

示例 3:

输入: " 3+5 / 2 "
输出: 5

说明:

  • 你可以假设所给定的表达式都是有效的。
  • 不要使用内置的库函数 eval
class Solution {
    public int calculate(String s) {
        //暴力方法 1先去空格 转stringbuffer
        //遍历的时候 找到前后计算结果后在不断继续遍历
        //使用栈来解决
        char sign='+';
        Stack<Integer> numsStack=new Stack<>();
        int num=0;
        int result=0;
        for(int i=0;i<s.length();i++){
            char cur=s.charAt(i);
            if(cur>='0'){
                num=num*10-'0'+cur;
            }
            //+-*/ asc2码都小于 数字
            if((cur<'0'&&cur!=' ')||i==s.length()-1){
                switch(sign){
                    case '+':numsStack.push(num);break;
                    case '-':numsStack.push(-num);break;
                    case '*':numsStack.push(numsStack.pop()*num);break;
                    case '/':numsStack.push(numsStack.pop()/num);break;
                }
                //保存下一次计算的上一个符号位标记
                sign=cur;
                num=0;
            }
        }
        while(!numsStack.isEmpty()){
            result+=numsStack.pop();
        }
        return result;
    }
}

146. LRU缓存机制

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果密钥已经存在,则变更其数据值;如果密钥不存在,则插入该组「密钥/数据值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

先放一个效率最低的做法 后期添加算法

class LRUCache {
    //如果不限制条件的话 可以使用list直接实现就可以了 arraylist和linkedlist时间复杂度都有点高
    //先用复杂度高的实现一下吧
    Queue<Integer> queue;
    Map<Integer,Integer> map;
    int capacity;
    public LRUCache(int capacity) {
        this.map=new HashMap<>();
        this.queue=new LinkedList<>();
        this.capacity=capacity;
    }
    
    public int get(int key) {
        if(queue.contains(key)){
            queue.remove(key);
            queue.add(key);
            return map.get(key);
        }else{
            return -1;
        }
    }
    
    public void put(int key, int value) {
        if(queue.contains(key)){
            queue.remove(key);
            queue.add(key);
            map.put(key,value);
        }
        else if(capacity==0){
            map.remove(queue.poll());
            queue.add(key);
            map.put(key,value);
        }else{
            queue.add(key);
            map.put(key,value);
            capacity--;
        }
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

102. 二叉树的层序遍历

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

示例: 二叉树:[3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]
/**
 * 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<>();
        if(root==null){
            return res;
        }
        LinkedList<TreeNode> list=new LinkedList<>();
        list.add(root);
        while(!list.isEmpty()){
            int size=list.size();
            LinkedList<Integer> temp=new LinkedList<>();
            for(int i=0;i<size;i++){
                TreeNode node=list.pop();
                temp.add(node.val);
                if(node.left!=null){
                    list.add(node.left);
                }
                if(node.right!=null){
                    list.add(node.right);
                }
            }
            res.add(temp);
        }
        return res;
    }
}