zoukankan      html  css  js  c++  java
  • 刷题篇--热题HOT 61-70

    207.课程表

    现在你总共有 n 门课需要选,记为 0 到 n-1。在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?

    示例 1:输入: 2, [[1,0]],输出: true,解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的

    示例 2:输入: 2, [[1,0],[0,1]]输出: false,解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

    分析:这个问题相当于查找一个循环是否存在于有向图中,可使用BFS/DFS,本体先使用BFS,易于理解。如何判断一门课程能够学习--这门课程不用先修课程,或者先修课程已经完成,我们将已经修完的课程直接抛弃参与以后的讨论。一门课程的先修课程数即使‘’入度‘,当入度为0时,就说明这门课程可以完成,所以我们首先创建一个degree数组存储每门课程的入度。当一个有向图中不存在环,那么刚开始时肯定有一个入度为0,即没有先修课程的课程。我们以此课程进行广度遍历。没遍历到一门课程,那么说明此条路径可达,那么这门课程的入度减一,如果入度变为0,说明此课程不在环内,待完成课程数减一。最后查看待完成课程是否为0.如何实现遍历呢,使用队列,先进先出

    实例:degree[] = {0 ,0 ,1, 1, 2, 1}

    以课程1为出发点遍历有序图,将课程1放入队列。此时栈内:1

     

    出栈,遍历prerequisites[][],发现课程2的先修课程是1,那么就将degree[2] -1,此时degree[2]=0,那么说明2入度为0,可完成,入栈。此时栈内:2

    出栈,遍历prerequisites[][],发现课程3的先修课程是2,那么就将degree[3 ]-1,此时degree[3]=0,那么说明3入度为0,可完成,入栈。此时栈内:3

    (此处不用出栈,因为还在以课程2为入度的循环内)遍历prerequisites[][],发现课程4的先修课程是2,那么就将degree[4] -1,此时degree[4]=1,那么说明4入度为0,可完成,入栈。此时栈内:3,4

    出栈,遍历prerequisites[][],发现课程5的先修课程是3,那么就将degree[5 ]-1,此时degree[5]=1,那么说明5入度为1,不可完成。此时栈内:4

    出栈,遍历prerequisites[][],发现课程5的先修课程是4,那么就将degree[5 ]-1,此时degree[5]=0,那么说明5入度为0,可完成。入栈,此时栈内:5

    出栈,遍历prerequisites[][],发现没有以5为先修课程的,那么结束循环。此时队列也为空,那么结束循环。判断此时待修课程是否为0;

     1 class Solution {
     2     public boolean canFinish(int numCourses, int[][] prerequisites) {
     3         //计算入度数组
     4         int[] degree = new int[numCourses];
     5         for(int[] cp : prerequisites){
     6             degree[cp[0]]++;
     7         }
     8         //存放入度为0课程
     9         LinkedList<Integer> queue = new LinkedList();
    10         //找出入度为0的课程
    11         for(int i=0;i<numCourses;i++){
    12             if(degree[i]==0) queue.addLast(i);
    13         }
    14         //找到下一个入度为0的
    15         while(!queue.isEmpty()){
    16             //出栈,能够完成的课程
    17             Integer pre = queue.removeFirst();
    18             numCourses--;
    19             for(int[] req : prerequisites){
    20                 if(req[1] != pre) continue;
    21                 degree[req[0]]--;
    22                 if(degree[req[0]] == 0) queue.addLast(req[0]);
    23             }
    24         }
    25         return numCourses==0;
    26     }
    27 }

    208.实现Trie(前缀树)

    实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
    示例:
    Trie trie = new Trie();
    trie.insert("apple");
    trie.search("apple");   // 返回 true
    trie.search("app");     // 返回 false
    trie.startsWith("app"); // 返回 true
    trie.insert("app");   
    trie.search("app");     // 返回 true

    分析:Trie根节点不存入值

    1.Trie节点结构:一个int[26]数组来存储字符值,一个boolean类型is_end判断是否以该字符结尾。下图表示leet在树中的表示。

     2.插入:插入一个值,从根部开始搜索字符值,存在两种情况:

        链接存在:继续向下搜索

        链接不存在:创建新节点,直到达到插入值最后一个字符,将末尾字符is_end设置为true

    3.查找:

     4.前缀

     1 class TrieNode{
     2     TrieNode[] chiled;//记录孩子节点
     3     boolean is_end;
     4     TrieNode(){
     5         chiled = new TrieNode[26];//一个孩子节点有2266个分支,即一层树有26各分支
     6         is_end = false;
     7     }
     8 }
     9 class Trie {
    10     TrieNode root;//根节点,不存值
    11     /** Initialize your data structure here. */
    12     public Trie() {
    13         root = new TrieNode();
    14     }
    15     
    16     /** Inserts a word into the trie. */
    17     public void insert(String word) {
    18         TrieNode node = root;
    19         for(int i=0;i<word.length();i++){
    20             char c = word.charAt(i);
    21             if(node.chiled[c-'a']==null){//如果该层该字符位置为空,则新建一个
    22                 node.chiled[c-'a'] = new TrieNode();
    23             }
    24             node = node.chiled[c-'a'];//指向下一层
    25         }
    26         node.is_end = true;//字符串最后一个字符那层的位置is_end=true
    27     }
    28     
    29     /** Returns if the word is in the trie. */
    30     public boolean search(String word) {
    31         TrieNode node = root;
    32         for(int i=0;i<word.length();i++){
    33             char c= word.charAt(i);
    34             if(node.chiled[c-'a'] == null){//判断是否存在
    35                 return false;
    36             }
    37             node = node.chiled[c-'a'];
    38         }
    39         return node.is_end;//判断结尾
    40     }
    41     
    42     /** Returns if there is any word in the trie that starts with the given prefix. */
    43     /**
    44     *serach和startWith区别在于是否判断结尾is_end,startWith是只要字符存在就行
    45     */
    46     public boolean startsWith(String prefix) {
    47         TrieNode node = root;
    48         for(int i=0;i<prefix.length();i++){
    49             char c= prefix.charAt(i);
    50             if(node.chiled[c-'a'] == null){//判断是否存在
    51                 return false;
    52             }
    53             node = node.chiled[c-'a'];
    54         }
    55         return true;
    56     }
    57 }
    58 
    59 /**
    60  * Your Trie object will be instantiated and called as such:
    61  * Trie obj = new Trie();
    62  * obj.insert(word);
    63  * boolean param_2 = obj.search(word);
    64  * boolean param_3 = obj.startsWith(prefix);
    65  */

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

    示例 1:输入: [3,2,1,5,6,4] 和 k = 2,输出: 5
    示例 2:输入: [3,2,3,1,2,4,5,5,6] 和 k = 4,输出: 4
    分析:排序,输出nums[length-K],快排O(NlogN);复杂度不咋地,看了题解使用堆排序,维持一个大小为K的小顶堆,那么堆顶就是答案,如果大于堆顶且堆满就加入,反之不操作,复杂度为O(NlogK)。

     1 class Solution {
     2     public int findKthLargest(int[] nums, int k) {
     3         //利用最小堆,创建k+1的size,遍历数组进行添加,并poll一个
     4         PriorityQueue<Integer> heap = new PriorityQueue(k+1);
     5         for(int num : nums){
     6             heap.add(num);
     7             if(heap.size() > k){
     8                 heap.poll();
     9             }
    10         }
    11         return heap.poll();
    12     }
    13 }

    221.最大正方形

    在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。

    示例:

    输入: 
    
    1 0 1 0 0
    1 0 1 1 1
    1 1 1 1 1
    1 0 0 1 0
    
    输出: 4

    分析:动态规划,建立一个二维数组dp[i][j],在左上角矩阵中由matrix[i-1][j-1]参与构成的最大正方形边长。

    如果matrix[i-1][j-1]='1'  =>  dp[i][j] = min{dp[i-1][j], dp[i-1][j-1], dp[i][j-1] } +1。否则 dp[i][j]=0。

     1 class Solution {
     2     public int maximalSquare(char[][] matrix) {
     3         if(matrix.length == 0) return 0;
     4         int rl = matrix.length;
     5         int cl = matrix[0].length;
     6         int[][] dp = new int[rl+1][cl+1];
     7         int maxLen = 0;
     8         for(int i=1;i<=rl;i++){
     9             for(int j=1;j<=cl;j++){
    10                 if(matrix[i-1][j-1] == '1'){
    11                     dp[i][j] = Math.min(dp[i-1][j-1], Math.min(dp[i-1][j],dp[i][j-1]))+1;
    12                     maxLen = Math.max(maxLen, dp[i][j]);
    13                 }
    14             }
    15         }
    16         return maxLen*maxLen;
    17     }
    18 }

    226.翻转二叉树

    示例:

    输入:

         4
       /  
      2     7
     /       /
    1   3 6   9

    输出:

         4
       /  
      7     2
     /      /
    9   6 3   1

    分析:递归

     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 TreeNode invertTree(TreeNode root) {
    12         if(root == null){
    13             return root;
    14         }
    15         TreeNode leftNode = invertTree(root.left);
    16         TreeNode rightNode = invertTree(root.right);
    17         root.left = rightNode;
    18         root.right = leftNode;
    19         return root;
    20     }
    21 }

    迭代

    class Solution {
        public TreeNode invertTree(TreeNode root) {
            if(root == null) return null;
            Queue<TreeNode> queue = new LinkedList();
            queue.add(root);
            while(!queue.isEmpty()){
                TreeNode node = queue.poll();
                TreeNode temp = node.left;
                node.left = node.right;
                node.right = temp;
                if(node.left!=null) queue.add(node.left);
                if(node.right!=null) queue.add(node.right);
            }
            return root;
        }
    }

    234.回文链表

    请判断一个链表是否为回文链表。
    示例 1:输入: 1->2输出: false
    示例 2:输入: 1->2->2->1输出: true
    分析:1.快慢指针,指向中间。2.将前半段倒序。3.遍历比较

     1 /**
     2  * Definition for singly-linked list.
     3  * public class ListNode {
     4  *     int val;
     5  *     ListNode next;
     6  *     ListNode(int x) { val = x; }
     7  * }
     8  */
     9 class Solution {
    10     public boolean isPalindrome(ListNode head) {
    11         if(head == null || head.next == null) return true;
    12         ListNode cur = head;
    13         ListNode slow = head, fast = head.next, pre = null, prepre = null;
    14         while(fast!=null&&fast.next!=null){
    15             pre = slow;//先保存slow
    16             slow = slow.next;
    17             fast = fast.next.next;
    18             //先移动再反转
    19             pre.next = prepre;
    20             prepre = pre;
    21         }
    22         ListNode p2 = slow.next;//右边
    23         slow.next = pre;
    24         ListNode p1 = fast == null ? slow.next : slow;//如果是奇数个,则跳过中间节点
    25         while(p1 != null){
    26             if(p1.val != p2.val) 
    27                 return false;
    28             p1 = p1.next;
    29             p2 = p2.next;
    30         }
    31         return true;
    32     }
    33 }

    236.二叉树的最近公共祖先

    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
    例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]

    示例 1:输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1;输出: 3,解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
    示例 2:输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4;输出: 5,解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
    分析:递归,从根节点开始。如果存在公共祖先,所以要么是根节点,要么在左子树或右子树。如果根节点已经包含待求值中的某一值,则根节点一定是最早公共祖先(一个节点先遍历到,另一个节点是其祖先)。遍历过程中,存在三种情况:

    - 如果左子树中没有待求值,但右子树中返回了某一节点值,则可判断右子树中存在公共子节点,而递归操作就可以达到查找到最早公共子节点的作用

    - 如果左子树中返回每一节点,而右子树中为空,与上述情况相同

    - 如果左右子树均有返回节点,则可判断左右子树中应分别有一个待求节点,故此时的根节点一定是最早公共祖先。

     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 TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    12         if(root == null || root == p || root == q){
    13             return root;
    14         }
    15         //根据当前节点寻找左右子树
    16         TreeNode left = lowestCommonAncestor(root.left,p,q);
    17         TreeNode right = lowestCommonAncestor(root.right,p,q);
    18         //如果左子树有一个,右子树也找到一个,则返回当前节点
    19         if(left!=null&&right!=null){
    20             return root;
    21         }
    22         //左子树没有,那么去右子树找
    23         if(left == null) return right;
    24         //与上述相反
    25         if(right == null) return left;
    26         return null;
    27     }
    28 }

    238.除自身以外数组的乘积

    给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
    示例:输入: [1,2,3,4],输出: [24,12,8,6]
    说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。

    分析:暴力法不可取,O(N^2)。乘积 = 当前数左边乘积 * 当前数右边乘积。设置一数组,一次从头到尾遍历原数组存入左边乘值,一次从未到头遍历原数组存入右边乘值。例如:[1,2,3,4]

    从前往后:[1,1*1, 2*1, 2*3]

    从后往前:[1*2*3*4,1*1 * 3*4,2*1 * 4,2*3]

     1 class Solution {
     2     public int[] productExceptSelf(int[] nums) {
     3         int[] res = new int[nums.length];
     4         int k = 1;
     5         for(int i=0;i<nums.length;i++){
     6             res[i] = k;//[1,1,2,6]
     7             k *= nums[i];//[1,2,6,24]给下次乘值使用,这样就是i左边的乘值
     8         }
     9         k = 1;
    10         for(int i=nums.length-1;i>=0;i--){
    11             res[i] *= k;//[24,12,8,6]
    12             k *= nums[i];//[24,24,12,4] i右边的乘值 
    13         }
    14         return res;
    15     }
    16 }

    239.滑动窗口最大值

    给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
    示例:
    输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3,输出: [3,3,5,5,6,7]
    解释:
      滑动窗口的位置                最大值
    ---------------               -----
    [1  3  -1] -3  5  3  6  7       3
     1 [3  -1  -3] 5  3  6  7       3
     1  3 [-1  -3  5] 3  6  7       5
     1  3  -1 [-3  5  3] 6  7       5
     1  3  -1  -3 [5  3  6] 7       6
     1  3  -1  -3  5 [3  6  7]      7

    提示:你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
    分析:双向链表:剑指Offer

    Step1.使用一个双端队列存储滑窗内元素的下标。

    value 2 3 4 2 6 2 5
    index 0 1 2 3 4 5 6

    queue

         

     

    Step2.若queue为空,说明刚开始滑动,将num第一个元素的索引压入队列中。

    value 2 3 4 2 6 2 5
    index 0 1 2 3 4 5 6

     queue

     0    

     

    Step3.遍历num下一个元素num[1]=3,

      ①先判断当前索引位置减去队列最左边元素是否>=size,如果是,那么说明随着滑窗滑动,当前滑窗内的最大值滑出去了,已经不再这个滑窗内,这是需要将队列最左边元素弹出。(队列最左边为滑窗内最大值索引,判断是否有效)

      ②将当前元素与队列最右边的索引处的数组值对比,如果当前元素大于队列最右边对应的值,则将队列最右边的值弹出,直到找到对应值比当前元素值大的。(队列左边是最大的值,随着上述操作,会逐渐更新队列,滑出去或者新添加的值更大的话将会更新队列,保证队头对应值最大)

    value 2 3 4 2 6 2 5
    index 0 1 2 3 4 5 6

     

     1    

     

    value 2 3 4 2 6 2 5
    index 0 1 2 3 4 5 6

     

    2    

    ---输出4

     

    value 2 3 4 2 6 2 5
    index 0 1 2 3 4 5 6

     

     2 3  

    ---输出4

     

    value 2 3 4 2 6 2 5
    index 0 1 2 3 4 5 6

     

    4    

    ---输出6

     

    value 2 3 4 2 6 2 5
    index 0 1 2 3 4 5 6

     

    4 5  

    ---输出6

     

    value 2 3 4 2 6 2 5
    index 0 1 2 3 4 5 6

     

    4  5  

    ---输出6

     

    value 2 3 4 2 6 2 5 1
    index 0 1 2 3 4 5 6 7

     --当前索引处位置(7)-队列最左边(4)=size(3)--》滑出

     4 5 7

     --

    5 7  

     --输出5

     补充:队头是存储最大值的索引,队头被弹出的情况:新来的比队内所有数都大、队头不在当前子数组中。新来成员时,判断是否有老成员太老了和新成员是否比其他老成员大(循环比,大一个弹一个,直到队列为空或者比老成员小)。队内情况:索引由小到大,因为位于当前元素前面并且小的被淘汰了。

     1 class Solution {
     2     public int[] maxSlidingWindow(int[] nums, int k) {
     3         if(nums==null||nums.length==0) return nums;
     4         int len = nums.length;
     5         int[] res = new int[len-k+1];
     6         int index = 0;
     7         ArrayDeque<Integer> queue = new ArrayDeque<Integer>();//队列存储的是索引值
     8         for(int i=0;i<nums.length;i++){
     9             if(queue.isEmpty()){
    10                 queue.add(i);//刚开始时
    11             }
    12             if(i-queue.peekFirst()>=k){
    13                 queue.pollFirst();//将太老的老成员提出
    14             }
    15             while(!queue.isEmpty() && nums[i]>=nums[queue.peekLast()]){
    16                 queue.pollLast();
    17             }
    18             queue.addLast(i);
    19             if(i>=k-1){//k-1个元素开始,就要输出了
    20                 res[index++] = nums[queue.peekFirst()];
    21             }
    22         }
    23         return res;
    24     }
    25 }

    240.搜索二维矩阵Ⅱ

    编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:每行的元素从左到右升序排列。每列的元素从上到下升序排列。
    示例:
    现有矩阵 matrix 如下:
    [
      [1,   4,  7, 11, 15],
      [2,   5,  8, 12, 19],
      [3,   6,  9, 16, 22],
      [10, 13, 14, 17, 24],
      [18, 21, 23, 26, 30]
    ]
    给定 target = 5,返回 true。给定 target = 20,返回 false。
    分析:矩阵特点:左上角最小,右下角最大。本题从左下角遍历,如果大于当前元素,则向右寻找,反之,向上寻找。复杂度O(n+m)。

     1 class Solution {
     2     public boolean searchMatrix(int[][] matrix, int target) {
     3         if(matrix == null || matrix.length ==0 || matrix[0].length == 0){
     4             return false;
     5         }
     6         int m = matrix.length;
     7         int n = matrix[0].length;
     8         int row = m-1, col = 0;
     9         while(row >= 0 && col <= n-1){
    10             if(matrix[row][col] == target){
    11                 return true;
    12             }else if(matrix[row][col] < target){
    13                 col++;
    14             }else{
    15                 row--;
    16             }
    17         }
    18         return false;
    19     }
    20 }
  • 相关阅读:
    《敏捷软件需求》阅读笔记三
    《敏捷软件需求》阅读笔记二
    《敏捷软件需求》阅读笔记一
    《需求工程-软件建模与分析》阅读笔记三
    《需求工程-软件建模与分析》阅读笔记二
    《需求工程-软件建模与分析》阅读笔记一
    C#.NET程序设计实验三实验报告
    C#.NET程序设计实验二实验报告
    C#.NET程序设计实验一实验报告
    oracle——存储数据时的编码问题
  • 原文地址:https://www.cnblogs.com/qmillet/p/12161319.html
Copyright © 2011-2022 走看看