zoukankan      html  css  js  c++  java
  • 刷题篇--热题HOT 31-40

    75.颜色分类

    给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
    注意:不能使用代码库中的排序函数来解决这道题。
    示例:输入: [2,0,2,1,1,0]输出: [0,0,1,1,2,2]
    分析:数组只有三个(常数个)元素,那么只要计算012的个数然后填入原数组。

     1 class Solution {
     2     public void sortColors(int[] nums) {
     3         int numOfZero = 0, numOfOne = 0, numOfTwo = 0;
     4         for(int i=0;i<nums.length;i++){
     5             if(nums[i]==0){
     6                 numOfZero++;
     7             }else if(nums[i]==1){
     8                 numOfOne++;
     9             }else{
    10                 numOfTwo++;
    11             }
    12         }
    13         int index = 0;
    14         while(index < numOfZero){
    15             nums[index] = 0;
    16             index++;
    17         }
    18         while(index < numOfOne+numOfZero){
    19             nums[index] = 1;
    20             index++;
    21         }
    22         while(index < numOfTwo+numOfOne+numOfZero){
    23             nums[index] = 2;
    24             index++;
    25         }
    26     }
    27 }

     法二:定义三个指针p0,p1,cur分别代表着p0索引左边全是0,p2索引右边全是2,p0~p2索引和之间全是1.所以要确定p0和p2的位置就可以了。

    cur初始定义为0,当nums[cur]=2时,就交换cur和p2的元素,p2-1。当nums[cur] = 0时,交换cur和p0的元素,cur+1,p0+1。如果nums[cur]=1,则将cur右移。直到cur>p2.

     1 class Solution {
     2     public void sortColors(int[] nums) {
     3         int p0 = 0, p2 = nums.length-1, cur = 0;
     4         while(cur <= p2){
     5             if(nums[cur]==0){
     6                 swap(nums, cur, p0);
     7                 cur++;
     8                 p0++;
     9             }else if(nums[cur]==1){
    10                 cur++;
    11             }else{
    12                 swap(nums, cur, p2);
    13                 p2--;
    14             }
    15         }
    16     }
    17     public void swap(int[] nums, int i, int j){
    18         int temp = nums[i];
    19         nums[i] = nums[j];
    20         nums[j] = temp;
    21     } 
    22 }

     

     76.最小覆盖字串

    给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

    示例:输入: S = "ADOBECODEBANC", T = "ABC",输出: "BANC"
    说明:如果 S 中不存这样的子串,则返回空字符串 ""。如果 S 中存在这样的子串,我们保证它是唯一的答案。
    分析:此问题有俩个点:

      1.如何确定包含字符串T:使用HashMap,key为字符串T的字符,value是字符出现次数。滑动窗口right++时,如果S[right]是T的字符,则将value-1,滑动窗口left++时,如果如果S[left]是T的字符,则将value+1。如果HashMap所有的value都是0,则说明left~right的字串包含T。

      2.如何保证最小字串:使用滑动窗口(一般用于最多、最少连续等关键词),定义left、right指针,right用于扩展,left用于收缩。确定最小窗口,则为结果。

     1 class Solution {
     2     public String minWindow(String s, String t) {
     3         int left = 0, right = 0;//滑动窗口左右两索引
     4         int minLeft = 0, minRight = -1;//保存最小子串的左右两边索引
     5         int minLen = s.length();//默认最小字串长度
     6         Map<Character, Integer> map = new HashMap();
     7         //初始map
     8         for(int i=0;i<t.length();i++){
     9             char ch = t.charAt(i);
    10             map.put(ch, map.getOrDefault(ch,0)+1);
    11         }
    12         //滑动窗口
    13         while(right < s.length()){
    14             char chOfRight = s.charAt(right);
    15             //移动右指针,并判断是否已经包含T
    16             if(map.containsKey(chOfRight)){
    17                 //当前相同字符次数减一
    18                 map.put(chOfRight,map.get(chOfRight)-1);
    19                 //判断是否全包含T
    20                 while(match(map)){
    21                     //移动左指针
    22                     char chOfLeft = s.charAt(left);
    23                     //当前相同字符次数加一
    24                     if(map.containsKey(chOfLeft)){
    25                         map.put(chOfLeft,map.get(chOfLeft)+1);
    26                     }
    27                     //修改最小字串索引、长度
    28                     if((right-left+1) <= minLen){
    29                         minLen = right-left+1;
    30                         minLeft = left;
    31                         minRight = right;
    32                     }
    33                     left++;
    34                 }
    35             }
    36             right++;
    37         }
    38         return s.substring(minLeft, minRight+1);
    39     }
    40     //判断map的value是否为空
    41     public boolean match(Map<Character, Integer> map){
    42         for(Integer value : map.values()){
    43             if(value > 0){
    44                 return false;
    45             }
    46         }
    47         return true;
    48     }
    49 }

    【优化】实践复杂度为O(mn),空间复杂度为O(m)。上面解法使用的是map存储T的字符次数。实际上可以使用一个int数组,索引是字符减去字符0之后的ascii值,数组值是出现的字符出现次数。那么如何判断T已经包含在left~right内呢?可以设置一个整数count = T.length(),每当right遍历到T中的字符,就减一,每当left遍历到就加一。代码此处省略

    78.子集

    给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。
    示例:输入: nums = [1,2,3];输出:[ [3],[1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
    分析:类似全排列,只不过将全排列最后存储结果变为每小一步都存储结果。

    结果输出为 [ [], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3] ]

     1 class Solution {
     2     List<List<Integer>> res = new ArrayList();
     3     public List<List<Integer>> subsets(int[] nums) {
     4         if(nums == null || nums.length == 0) return res;
     5         List<Integer> list = new ArrayList();
     6         helper(nums, 0, list);
     7         return res;
     8     }
     9     public void helper(int[] nums, int start, List<Integer> list){
    10         res.add(new ArrayList(list));
    11         for(int i = start ; i < nums.length ; i++){
    12             list.add(nums[i]);
    13             helper(nums, i+1, list);
    14             list.remove(list.size()-1);
    15         }
    16     }
    17 }

     

     79.单词搜索

    给定一个二维网格和一个单词,找出该单词是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
    board =
    [
      ['A','B','C','E'],
      ['S','F','C','S'],
      ['A','D','E','E']
    ]
    给定 word = "ABCCED", 返回 true.
    给定 word = "SEE", 返回 true.
    给定 word = "ABCB", 返回 false.
    分析:回溯+递归

     1 class Solution {
     2     public boolean exist(char[][] board, String word) {
     3         int index = 0;
     4         int m = board.length;
     5         int n = board[0].length;
     6         boolean[][] visited = new boolean[m][n];
     7         for(int i=0;i<m;i++){
     8             for( int j=0;j<n;j++){
     9                 if(board[i][j] == word.charAt(index)){
    10                     boolean res = isExist(board, index, word, i, j, visited);
    11                     if(res) return true;
    12                 }
    13             }
    14         }
    15         return false;
    16     }
    17     public boolean isExist(char[][] board, int index, String word, int i, int j, boolean[][] visited){
    18         if(index == word.length()) return true;
    19         if(i<0 || i >= board.length || j<0 || j>=board[0].length || visited[i][j] || board[i][j] != word.charAt(index)){
    20             return false;
    21         } 
    22         visited[i][j] = true;
    23         if(isExist(board, index+1, word, i-1, j, visited)||
    24            isExist(board, index+1, word, i+1, j, visited)||
    25            isExist(board, index+1, word, i, j-1, visited)||
    26            isExist(board, index+1, word, i, j+1, visited))
    27         {
    28             return true;        
    29         }
    30         visited[i][j] = false;//回溯
    31         return false;
    32     }
    33 }

     

     84.柱状图中最大的矩形

    给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

    以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3] 

     图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

     法一:分治,最大矩形面积取决于最低的柱子高度和两边之间的距离。先寻找出最低的柱子,以此柱子为界,再计算左右两边,取出最低柱子和左右边面积最大值。递归。

     1 class Solution {
     2     public int largestRectangleArea(int[] heights) {
     3         return calculateArea(heights, 0, heights.length-1);
     4     }
     5     public int calculateArea(int[] heights, int start, int end){
     6         if(start > end) return 0;
     7         //计算最小值
     8         int minHeightsIndex = start;
     9         for(int i = start; i <= end; i++){
    10             if(heights[i] < heights[minHeightsIndex]){
    11                 minHeightsIndex = i;
    12             }
    13         }
    14         //返回计算三个面积的最大值
    15         return Math.max(heights[minHeightsIndex]*(end-start+1),
    16                Math.max(calculateArea(heights,start,minHeightsIndex-1),calculateArea(heights,minHeightsIndex+1,end)));
    17     }
    18 }

     法二:包含当前柱子的最大矩形示意图如下:

     

     图解:

    ①先将2存入栈内,左边沿的左边是-1;

    ② 1小于2,那么2的左边沿和右边沿都能确定了,计算2的最大矩形面积,压入1;

    ③ 5大于1,则5的左边沿能够确定,右边沿未知,则压入5;

    ④ 6和5同样操作;

    ⑤ 2小于6,那么6的左边沿和右边沿能够确定,则计算面积并弹出,继续比较,2小于5,则5的左右边沿也能够确定,同6操作,继续比较,2大于1,压入2;

    ⑥ 3大于 2,直接压入;

    ⑦处理栈内剩下元素,这些元素当初是因为右边沿无法确定而一直存在栈内的,但是数组遍历完之后,这些栈内元素的右边沿都是数组的长度,左边沿是左边相邻的元素。【这里的压入弹出的数据时数组索引,而不是值】

      只需要计算上图中的六个面积的最大值即为答案。那面积如何求得呢?通过观察发现,矩形的左边沿是左边第一个小于本柱子的右边,右边沿是右边第一个小于本柱子的左边

      那么如何寻找呢?使用单调栈,栈内元素从小到大排列,这也就保证可以存储到左边沿。

      那么右边沿如何判断呢?如果遍历到的柱子高度低于栈顶元素,也就是说明以栈顶元素为核心的矩形找到了,遍历到的柱子就是右边沿右边一个位置。当然,这个遍历到的柱子可能是很多柱子的右边沿的右边,需要while循环。这样就可以得知左边沿()和右边沿了,只要在弹出过程中保存最大面积即可。

      遍历结束时如果最后栈内剩下元素,那么一定是递增的,右边沿是数组长度,例如上例就会剩下 1  4 5分别代表高度1 2 3,此时再使用一个while循环,不断计算面积保存最大值。

     1 class Solution {
     2     public int largestRectangleArea(int[] heights) {
     3         Stack<Integer> stack = new Stack();
     4         stack.push(-1);
     5         int maxArea = 0;
     6 
     7         //遍历
     8         for(int i=0; i < heights.length; i++){
     9             //左边沿位置 stack.pop().peek(),右边沿位置i-1
    10             while(stack.peek() != -1 && heights[i] <= heights[stack.peek()]){
    11                 maxArea = Math.max(maxArea, heights[stack.pop()]*(i-1-stack.peek()));
    12             }
    13             //不管有没有上述操作都要压入
    14             stack.push(i);
    15         }
    16 
    17         //处理剩下的数据,右边沿是heights.length-1,即最后一个元素
    18         while(stack.peek() != -1){
    19             maxArea = Math.max(maxArea, heights[stack.pop()]*(heights.length-stack.peek()-1));
    20         }
    21 
    22         return maxArea;
    23     }
    24 }

     85.最大矩形

    给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
    示例:输入:
    [
      ["1","0","1","0","0"],
      ["1","0","1","1","1"],
      ["1","1","1","1","1"],
      ["1","0","0","1","0"]
    ]       输出: 6

    分析:面积如何计算?矩形左上角位置和右下角位置索引计算。

    将本题转换为84题。

    ① 利用一维数组存储从上往下,以每行为底的每列高度;

    ②遍历matrix每一行,利用84题中栈方法更新最大面积;

     1 class Solution {
     2     public int maximalRectangle(char[][] matrix) {
     3         if(matrix.length == 0) return 0;
     4         int maxArea = 0;
     5         int[] heights = new int[matrix[0].length];
     6         //利用matrix数组存储从上往下,以每行为底的每列高度;
     7         for(int i=0;i<matrix.length;i++){
     8             for(int j=0;j<matrix[0].length;j++){
     9                 heights[j] = matrix[i][j] == '1' ? heights[j] + 1 : 0;
    10             }
    11             maxArea = Math.max(maxArea, largestRectangleArea84(heights));
    12         }
    13         return maxArea;
    14     }
    15     public int largestRectangleArea84(int[] heights) {
    16           Stack<Integer> stack = new Stack();
    17           stack.push(-1);
    18           int maxArea = 0;
    19           //遍历
    20           for(int i=0; i < heights.length; i++){
    21              //左边沿位置 stack.pop().peek(),右边沿位置i-1
    22             while(stack.peek() != -1 && heights[i] <= heights[stack.peek()]){
    23                  maxArea = Math.max(maxArea, heights[stack.pop()]*(i-1-stack.peek()));
    24              }
    25              //不管有没有上述操作都要压入
    26              stack.push(i);
    27          }
    28          //处理剩下的数据,右边沿是heights.length-1,即最后一个元素
    29          while(stack.peek() != -1){
    30              maxArea = Math.max(maxArea, heights[stack.pop()]*(heights.length-stack.peek()-1));
    31          }
    32          return maxArea;
    33      }
    34 }

    94. 二叉树的中序遍历

    递归

     1 /**
     2  * Definition for a binary tree node.
     3  * public class TreeNode {
     4  *     int val;
     5  *     TreeNode left;
     6  *     TreeNode right;
     7  *     TreeNode(int x) { val = x; }
     8  * }
     9  */
    10 class Solution {
    11     
    12     public List<Integer> inorderTraversal(TreeNode root) {
    13         ArrayList<Integer> res = new ArrayList();
    14         helper(root,res);
    15         return res;
    16     }
    17     public void helper(TreeNode root, List<Integer> res){
    18         if(root == null) return;
    19         helper(root.left, res);
    20         res.add(root.val);
    21         helper(root.right, res);
    22     }
    23 }

     非递归

     1 /**
     2  * Definition for a binary tree node.
     3  * public class TreeNode {
     4  *     int val;
     5  *     TreeNode left;
     6  *     TreeNode right;
     7  *     TreeNode(int x) { val = x; }
     8  * }
     9  */
    10 class Solution {
    11     public List<Integer> inorderTraversal(TreeNode root) {
    12         ArrayList<Integer> res = new ArrayList();
    13         Stack<TreeNode> stack = new Stack();
    14         TreeNode cur = root;
    15         while(cur != null || !stack.empty()){
    16             while(cur!=null){
    17                 stack.push(cur);
    18                 cur = cur.left;
    19             }
    20             cur = stack.pop();
    21             res.add(cur.val);
    22             cur = cur.right;
    23         }
    24         return res;
    25     }
    26 }

     97.不同的二叉搜索树

    给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

     示例:输入: 3输出: 5
    解释:给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

       1          3     3      2      1
                 /       /      /        
         3     2     1      1    3      2
        /      /                             
       2     1          2                     3

    分析:看了解析,发现数个数学归纳题。1…n数列,如果取中间i为根节点,则1…i-1,i+1…n分别为左子树和右子树。

     设置两个函数:

      G(n):长度为n的序列的不同二叉搜索树的个数。

         F(i,n):以i为根的不同二叉搜索树的个数。

    易知:G(n) = F(1,n)+F(2,n)+F(3,n)+…+F(n-1,n)+F(n,n),G(0) = 1 , G(1) = 1;

          F(i,n) = G(i-1)*G(n-i)

    综上:G(n) = ΣG(i-1)*G(n-i),i=1,2,...,n。例如G(4) = G(0)*G(4) + G(1)*G(3) + G(2)*G(2) + G(3)*G(1)

     1 class Solution {
     2     public int numTrees(int n) {
     3         int[] dp = new int[n+1];
     4         dp[0] = 1;
     5         dp[1] = 1;
     6         for(int i = 2 ; i <= n ; i++){
     7             for(int j=1;j<=i;j++){
     8                 dp[i] += dp[j-1]*dp[i-j]; 
     9             } 
    10         }
    11         return dp[n];
    12     }
    13 }

    实际上,G(n)函数是卡塔兰数 G(n+1) = (2(2n+1)/(n+2))*G(n)

    1 class Solution {
    2     public int numTrees(int n) {
    3         long res = 1;
    4         for(int i=0;i<n;i++){
    5             res = res*2*(2*i+1)/(i+2); 
    6         }
    7         return (int)res;
    8     }
    9 }

    98.验证二叉搜索树

    给定一个二叉树,判断其是否是一个有效的二叉搜索树。
    假设一个二叉搜索树具有如下特征:节点的左子树只包含小于当前节点的数;节点的右子树只包含大于当前节点的数; 所有左子树和右子树自身必须也是二叉搜索树。
    示例 1:
    输入:
        2
       /
      1   3
    输出: true
    示例 2:
    输入:
        5
       /
      1   4
         /
        3   6
    输出: false
    解释: 输入为: [5,1,4,null,null,3,6]。根节点的值为 5 ,但是其右子节点值为 4 。

    分析:一个合格的搜索二叉树的中序遍历一定是一个单调递增的数列,例如示例一:123√,示例二:15346×。

       5
       /
      1   6
           /
         4   7

    再例如上例:15467×。因此在中序遍历的时候,存储最后一个遍历的节点值temp,判断下一个遍历的节点值是否大于temp,如果大于,则继续遍历,直至遍历完。如果小于则返回false。

     1 /**
     2  * Definition for a binary tree node.
     3  * public class TreeNode {
     4  *     int val;
     5  *     TreeNode left;
     6  *     TreeNode right;
     7  *     TreeNode(int x) { val = x; }
     8  * }
     9  */
    10 class Solution {
    11     public boolean isValidBST(TreeNode root) {
    12         Stack<TreeNode> stack = new Stack();
    13         double temp = -Double.MAX_VALUE;
    14         TreeNode cur = root;
    15         while(cur != null || !stack.empty()){
    16             while(cur!=null){
    17                 stack.push(cur);
    18                 cur = cur.left;
    19             }
    20             cur = stack.pop();
    21             if(cur.val <= temp) return false;
    22             temp = cur.val;
    23             cur = cur.right;
    24         }
    25         return true;
    26     }
    27 }

    101.对称二叉树

    给定一个二叉树,检查它是否是镜像对称的。例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

         1
       /   
      2     2
     /     /
    3  4  4  3

    但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
        1
       /
      2   2
          
       3    3
    递归

     1 /**
     2  * Definition for a binary tree node.
     3  * public class TreeNode {
     4  *     int val;
     5  *     TreeNode left;
     6  *     TreeNode right;
     7  *     TreeNode(int x) { val = x; }
     8  * }
     9  */
    10 class Solution {
    11     public boolean isSymmetric(TreeNode root) {
    12         return helper(root, root);
    13     }
    14     public boolean helper(TreeNode left, TreeNode right){
    15         if(left==null&&right==null){
    16             return true;
    17         }else if(left == null || right == null || left.val != right.val){
    18             return false;
    19         }else{
    20             return helper(left.left,right.right) && helper(left.right,right.left);
    21         }
    22     }
    23 }

    //迭代-类似于层序遍历,使用队列

     1 class Solution {
     2     public boolean isSymmetric(TreeNode root) {
     3         Queue<TreeNode> queue = new LinkedList();
     4         queue.add(root);
     5         queue.add(root);
     6         while(!queue.isEmpty()){
     7             TreeNode node1 = queue.poll();
     8             TreeNode node2 = queue.poll();
     9             if(node1 == null && node2 == null) continue;
    10             if(node1 == null || node2 == null || node1.val != node2.val) return false;
    11             queue.add(node1.left);
    12             queue.add(node2.right);
    13             queue.add(node1.right);
    14             queue.add(node2.left);
    15         }
    16         return true;
    17     }
    18 }

     

     

     

     

     

     

     

  • 相关阅读:
    boost 1.49在vs 2005下编译的方法
    Mathematics for Computer Graphics
    字符串和字符数组长度
    四个月的学习心得
    话说stm32f10x-FSMC的配置与频率
    一些笔试题,大家都来围观呀~
    简单的生产者消费者-(windows下)
    STM32f10x下软件模拟IIc读写si5326问题
    usb枚举阶段(转载)
    STM32 GPIOB_PIN3复用功能小分析
  • 原文地址:https://www.cnblogs.com/qmillet/p/12144717.html
Copyright © 2011-2022 走看看