刷题篇--热题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 }

原文地址:https://www.cnblogs.com/qmillet/p/12161319.html

时间: 2024-10-10 22:56:40

刷题篇--热题HOT 61-70的相关文章

刷题篇--热题HOT 10-20

20.有效的括号 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效.有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合.注意空字符串可被认为是有效字符串.输入: "()",输出: true:输入: "()[]{}",输出: true:输入: "(]",输出: false:输入: "([)]",输出: false:输入: "{[]}"

01背包水题篇之 HDU2955——Robberies

原来是想dp[i],表示不被抓概率为i所能抢到的最大钱(概率1-100) 后来看了别人的博客是dp[i]表示抢了i钱最大的不被抓概率,嗯~,弱菜水题都刷不动. 那么状态转移方程就是 dp[i]=max(dp[i],dp[i-money]*p),初始化dp(0~maxn)为0,dp[0]=1(1毛钱都没抢你抓个毛线啊,哥是良民~) 又是贴代码环节~ <span style="font-size:18px;">#include<iostream> #include&

01背包水题篇之HDU3466——Proud Merchants

这是个好题,菜鸟刚学dp,这题把我以前的想法全都给完完全全的颠覆了.其实是自己没了解无后效性的概念. 然后我去开开心心滴跑去问队长:"队长,队长,怎么理解动归的无后效性啊???" 学长很深沉滴对我说:"做多了就会了" "噢噢"(好吧) 然后学长又补了句:"能构成有向无环图的都能用DP搞." 我心里想:"队长就知道搞妹~~~." 默默去翻小白书看看DAG去了. 为了搞清楚这题怎么写,操了度娘千百遍,还是没搞定

算法——一天一道算法题篇——找只出现一次的两个数

找只出现一次的两个数 题目: 一个整型数组里除了两个数字只出现一次之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 举例说明: 现在有一个数组:{1,3,4,2,4,3}; 假设数组元素的规模不是很大,想要找到只出现一次的元素,可以定义一个辅助数组,flag[100];里面存放的是数组元素出现的次数,flag数组的下标表示的是数组:{1,3,4,2,4,3}里的元素. 代码如下: package hello.ant; public class AlogArrayFind2 {

01背包水题篇之HDU1864——最大报销额

这个题目好果的01,只要把每个数乘以100,就能解决下标的问题了 继续贴代码环节(自己的代码好丑啊~~~) <span style="font-size:18px;">#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #define maxn 3100000 using namespace std; int dp[maxn]; i

HDU 1025 Constructing Roads In JGShining&#39;s Kingdom   LIS 简单题 好题 超级坑

Constructing Roads In JGShining's Kingdom Problem Description JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which are located in two parallel lines. Half of these cities are rich in resource (we call them rich cities) whi

纪中10日T1 2300. 【noip普及组第一题】模板题

2300. [noip普及组第一题]模板题 (File IO): input:template.in output:template.out 时间限制: 1000 ms  空间限制: 262144 KB  具体限制 题目描述 输入 输出 样例输入 样例输出 数据范围限制 朴素算法 考试开始的前一个小时我一直在折腾朴素算法 -> 对拍 1 #pragma GCC optimize(2) 2 #include<bits/stdc++.h> 3 #define IL inline 4 usin

LeetCode OJ Linked List: 138题、109题和191题

138题:Copy List with Random Pointer 题目分析: 本题思路1:第一步,你需要遍历一下链表,对于每个结点,你都new出一个连接在其后面.第二步,调整random指针.第三步,把复制的链表与原链表断开.时间复杂度O(N),空间复杂度O(1). 本题思路2:第一步,仍需要遍历一下链表,对于每个结点都new出一个节点,但不连接在其后面,把这种旧节点到新结点的映射关系,存储在map中.第二步,调整random指针.时间复杂度O(N),空间复杂度O(N). 本题思路3:第一步

sdut 2413:n a^o7 !(第三届山东省省赛原题,水题,字符串处理)

n a^o7 ! Time Limit: 1000MS Memory limit: 65536K 题目描述 All brave and intelligent fighters, next you will step into a distinctive battleground which is full of sweet and happiness. If you want to win the battle, you must do warm-up according to my inst