Leetcode(5)

Leetcode 5

  • [201. Bitwise AND of Numbers Range](#201. Bitwise AND of Numbers Range)
  • [202. Happy Number](#202. Happy Number)
  • [203. Remove Linked List Elements](#203. Remove Linked List Elements)
  • [204. Count Primes](#204. Count Primes)
  • [205. Isomorphic Strings](#205. Isomorphic Strings)
  • [206. Reverse Linked List](#206. Reverse Linked List)
  • [207. Course Schedule](#207. Course Schedule)
  • [208. Implement Trie (Prefix Tree)](#208. Implement Trie (Prefix Tree))
  • [209. Minimum Size Subarray Sum](#209. Minimum Size Subarray Sum)
  • [210. Course Schedule II](#210. Course Schedule II)
  • [211. Design Add and Search Words Data Structure](#211. Design Add and Search Words Data Structure)
  • [213. House Robber II](#213. House Robber II)
  • [215. Kth Largest Element in an Array](#215. Kth Largest Element in an Array)
  • [**TO BE FINISHED NOT CHECK**](#TO BE FINISHED NOT CHECK)
  • [216. Combination Sum III](#216. Combination Sum III)
  • [217. Contains Duplicate](#217. Contains Duplicate)
  • [219. Contains Duplicate II](#219. Contains Duplicate II)
  • [221. Maximal Square](#221. Maximal Square)
  • [222. Count Complete Tree Nodes](#222. Count Complete Tree Nodes)
  • [223. Rectangle Area](#223. Rectangle Area)
  • [225. Implement Stack using Queues](#225. Implement Stack using Queues)
  • [226. Invert Binary Tree](#226. Invert Binary Tree)
  • [227. Basic Calculator II](#227. Basic Calculator II)
  • [228. Summary Ranges](#228. Summary Ranges)
  • [229. Majority Element II](#229. Majority Element II)
  • [230. Kth Smallest Element in a BST](#230. Kth Smallest Element in a BST)
  • [231. Power of Two](#231. Power of Two)
  • [232. Implement Queue using Stacks](#232. Implement Queue using Stacks)
  • [234. Palindrome Linked List](#234. Palindrome Linked List)
  • [235. Lowest Common Ancestor of a Binary Search Tree](#235. Lowest Common Ancestor of a Binary Search Tree)
  • [239. Sliding Window Maximum](#239. Sliding Window Maximum)
  • [354. Russian Doll Envelopes](#354. Russian Doll Envelopes)
  • [355. Design Twitter](#355. Design Twitter)
  • [399. Evaluate Division](#399. Evaluate Division)
  • [1091. Shortest Path in Binary Matrix](#1091. Shortest Path in Binary Matrix)
  • [438. Find All Anagrams in a String](#438. Find All Anagrams in a String)
  • 474
  • [567. Permutation in String](#567. Permutation in String)
  • [647. Palindromic Substrings](#647. Palindromic Substrings)
  • [692. Top K Frequent Words](#692. Top K Frequent Words)
  • [752. Open the Lock](#752. Open the Lock)
  • [875. Koko Eating Bananas](#875. Koko Eating Bananas)
  • [886. Possible Bipartition](#886. Possible Bipartition)
  • [1091. Shortest Path in Binary Matrix](#1091. Shortest Path in Binary Matrix)
  • [1143. Longest Common Subsequence](#1143. Longest Common Subsequence)

201. Bitwise AND of Numbers Range

201. Bitwise AND of Numbers Range

Given two integers left and right that represent the range [left, right], return the bitwise AND of all numbers in this range, inclusive.
Example 1:

Input: left = 5, right = 7

Output: 4

Example 2:

Input: left = 0, right = 0

Output: 0

Example 3:

Input: left = 1, right = 2147483647

Output: 0
Constraints:

0 <= left <= right <= 231 - 1

Brute force

java 复制代码
    public int rangeBitwiseAnd1(int left, int right) {
        int ans = left;
        if(left==2147483646){
            return left;
        }else if(left==2147483647){
            return right;
        }
        for(int i = left + 1; i <= right; i++){
            if(ans == 0){
                return 0;
            }
            ans &= i;
        }
        return ans;
    }

Bitwise-AND of any two numbers will always produce a number less than or equal to the smaller number.

From the right (bigger) to the left(less), decreasing the [left , right] boundary iteratively.

Because after the & right boundary, it will generate a smaller answer.

Just update the right value and because we know that during '&' operations the value either remain the same or get decreases so we can skip many iterations!

java 复制代码
    public int rangeBitwiseAnd(int left, int right) {
        for(int i=right-1;i>=left;i--) {
            
            right=right&i;
            i=right;
        }
        return right;
    }

202. Happy Number

202. Happy Number

Write an algorithm to determine if a number n is happy.

A happy number is a number defined by the following process:

Starting with any positive integer, replace the number by the sum of the squares of its digits.

Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.

Those numbers for which this process ends in 1 are happy.

Return true if n is a happy number, and false if not.
Example 1:

Input: n = 19

Output: true

Explanation:

1^2 + 9^2 = 82

8^2 + 2^2 = 68

6^2 + 8^2 = 100

1^2 + 0^2 + 0^2 = 1

Example 2:

Input: n = 2

Output: false
Constraints:

1 <= n <= 2^31 - 1

Check when to end the loop

If the result is 1 end the loop and return, if it will be false and can't end the loop automatically, have to check when to end the loop.

Use the set to examine the duplicate result and end the loop. If the result is duplicate, it proofs it is can't quit the loop automatically.

java 复制代码
class Solution {
    public boolean isHappy(int n) {
        int ans = 0;
        int temp;
        //use set to examine the loop
        //HashSet is unordered
        HashSet<Integer> set = new HashSet<>();
        set.add(n);
        while(n>0){
            if(n==1){
                return true;
            }
            while(n > 0){
                temp = n%10;
                ans += (temp*temp);
                n /= 10;
            }
            if(set.contains(ans)){
                return false;
            }
            n = ans;
            set.add(n);
            ans = 0;
        }
        return false;
    }
}

203. Remove Linked List Elements

203. Remove Linked List Elements

Given the head of a linked list and an integer val, remove all the nodes of the linked list that has Node.val == val, and return the new head.
Example 1:

Input: head = [1,2,6,3,4,5,6], val = 6

Output: [1,2,3,4,5]

Example 2:

Input: head = [], val = 1

Output: []

Example 3:

Input: head = [7,7,7,7], val = 7

Output: []
Constraints:

The number of nodes in the list is in the range [0, 104].

1 <= Node.val <= 50

0 <= val <= 50

Use a dummyHead and test the next node.

java 复制代码
/**
 * 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 removeElements(ListNode head, int val) {
        ListNode pre = new ListNode();
        pre.next =head;
        ListNode s = pre;
        while(pre.next!=null){
            if(pre.next.val==val){
                pre.next = pre.next.next;
            }
            else{
                pre = pre.next;
            }
        }
        return s.next;
    }
}

204. Count Primes

204. Count Primes

Given an integer n, return the number of prime numbers that are strictly less than n.
Example 1:

Input: n = 10

Output: 4

Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7.

Example 2:

Input: n = 0

Output: 0

Example 3:

Input: n = 1

Output: 0
Constraints:

0 <= n <= 5 * 106

Prime Sieve
A prime number (or a prime) is a natural number greater than 1 that is not a product of two smaller natural numbers.

Prime Number can't be the multiple of any other natural number besides 1 and itself.

For the sieve of Eratosthenes, we start by creating a boolean array (seen) of size n to represent each of the numbers less than n.

We start at 2 and for each number processed (num), we iterate through and mark each multiple (mult) of num, starting at num ^ 2, as seen. We start at num ^ 2 because every multiple up to the num'th multiple will have been guaranteed to have been seen before, since they're also a multiple of a smaller number. For example, when processing 5s, we can skip to 25 because 10 will have been seen when we processed 2s, 15 when we processed 3s, and 20 when we processed 2s.

Then we move num forward, skipping any numbers that have already been seen. By doing this, we will only stop on prime numbers, because they haven't been seen as a multiple of a previous iteration. We just have to update our count (ans) each time we stop and then return ans once we reach n.

Method 1

Use array to note whether a number is a composite number (not prime, has a factorization).

Mark multiples starting from num*num because the numbers before num*num are not prime numbers that have been marked

java 复制代码
    public int countPrimes(int n) {
        boolean[] seen = new boolean[n];
        int ans = 0;
        for (int num = 2; num < n; num++) {
            //if seen[]==true not a prime continue
            if (seen[num]) continue;
            //not skip, is prime, count++, add all multiples of the number to the prime sieve
            //Mark multiples starting from num*num
            ans += 1;
            for (long mult = (long)num * num; mult < n; mult += num)
                seen[(int)mult] = true;
        }
        return ans;
    }

123 ms 89.02% 47 MB 74.20%
Method 2
mark multiple from 2 to sqrt(n)****, get the number of prime by subtract the number of composite number

java 复制代码
    public int countPrimes(int n) {
        if (n <= 2) return 0;
        
        int count = n-2; // Initially we have n-2 primes as 1 and n are excluded
        double rootN = Math.floor(Math.sqrt(n));
        boolean[] isPrime = new boolean[n];
        Arrays.fill(isPrime,true);
    
        for (int i=2; i<=rootN; i++)
            if (isPrime[i])
                for (int j=i*i; j<n; j+=i)
                    if (isPrime[j]) {
                        isPrime[j] = false;
                        count--;
                    }
    
        return count;
    }

104 ms 95.57% 46.8 MB 79.83 %

205. Isomorphic Strings

205. Isomorphic Strings

Given two strings s and t, determine if they are isomorphic.

Two strings s and t are isomorphic if the characters in s can be replaced to get t.

All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character, but a character may map to itself.
Example 1:

Input: s = "egg", t = "add"

Output: true

Example 2:

Input: s = "foo", t = "bar"

Output: false

Example 3:

Input: s = "paper", t = "title"

Output: true
Constraints:

1 <= s.length <= 5 * 104

t.length == s.length

s and t consist of any valid ascii character.

Use string replace to replace the character in t and check whether is equals to s.

Wrong

"egcd"

"adfd"

Output true

Expected false
Your input

"foo"

"bar"

Output true

Expected false

java 复制代码
class Solution {
    public boolean isIsomorphic(String s, String t) {
        if(s.length()!=t.length()){
            return false;
        }
        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i)!=t.charAt(i)){
                t = t.replace(t.charAt(i),s.charAt(i));
            }
        }
        int[] ch = new int[256];
        for(int i = 0; i < s.length(); i++){
            ch[s.charAt(i)]++;
        }
        for(int i = 0; i < s.length(); i++){
            ch[t.charAt(i)]--;
        }
        for(int i:ch){
            if(i != 0){
                return false;
            }
        }
        return true;
    }
}

AC

There is one-one relationship between the chars.
Use HashMap to store the one-one relationship.

key: character in s

value: corresponding character in t

java 复制代码
    public boolean isIsomorphic(String s, String t) {
        if(s == null || s.length() <= 1) return true;
        HashMap<Character, Character> map = new HashMap<>();
        for(int i = 0 ; i< s.length(); i++){
            char a = s.charAt(i);
            char b = t.charAt(i);
            if(map.containsKey(a)){
                 if(map.get(a).equals(b))
                    continue;
                else
                    return false;
            }else{
                if(!map.containsValue(b))
                    map.put(a,b);
                else return false;
                
            }
        }
        return true; 
    }

206. Reverse Linked List

Given the head of a singly linked list, reverse the list, and return the reversed list.
Example 1:

Input: head = [1,2,3,4,5]

Output: [5,4,3,2,1]

Example 2:

Input: head = [1,2]

Output: [2,1]

Example 3:

Input: head = []

Output: []
Constraints:

The number of nodes in the list is the range [0, 5000].

-5000 <= Node.val <= 5000

Method 1 Insert the node at head of the list

java 复制代码
    public ListNode reverseList(ListNode head) {
        if(head==null) return head;
        ListNode pre = new ListNode();
        pre.next = head;
        ListNode p = head.next;
        head.next = null;   
        ListNode s;
        while(p!=null){
            s = p.next;
            p.next = pre.next;
            pre.next = p;
            p = s;
        }
        return pre.next;
    }

0ms 100% 43.8MB 5.97%

Method 2 Reverse the pointer

p s r p->s p<-s

java 复制代码
    public ListNode reverseList(ListNode head) {
        if(head==null) return head;
        ListNode p = head;
        ListNode s = p.next;
        p.next = null;
        ListNode r;
        while(s!=null){
            r = s.next;
            s.next = p;
            p = s;
            s = r;
        }
        return p;
    }

0ms 100% 44MB

207. Course Schedule

207. Course Schedule

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai.

For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1.

Return true if you can finish all courses. Otherwise, return false.
Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]

Output: true

Explanation: There are a total of 2 courses to take.

To take course 1 you should have finished course 0. So it is possible.

Example 2:

Input: numCourses = 2, prerequisites = [[1,0],[0,1]]

Output: false

Explanation: There are a total of 2 courses to take.

To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.
Constraints:

1 <= numCourses <= 2000

0 <= prerequisites.length <= 5000

prerequisites[i].length == 2

0 <= ai, bi < numCourses

All the pairs prerequisites[i] are unique.

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. Return true if you can finish all courses.

0 , 1 \] : To Take course 0 you have to first take course 1. **deadlock** Check whether there is a deadlock. **Graph Problem** Use **DFS or BFS** to check whether there is a loop. ![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d4f79bbc6f95fc3229cd7d83664c3798.png) **Use the HashMap to store the dependency relationship.** **Key: Each course Value: All their prerequisite courses** ![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/143fae4f09cec76cd34c081b4024e854.png) Do DFS from 0 to n-1 node. ```java class Solution { HashSet visiting = new HashSet<>(); ArrayList> arr = new ArrayList<>(); HashSet canVisit = new HashSet<>(); public boolean canFinish(int numCourses, int[][] prerequisites) { //initialize the preCourse list //use arr.get(courseNum) to get course's preCourse for(int i = 0; i < numCourses; i++){ ArrayList pre = new ArrayList<>(); arr.add(pre); } for(int i = 0; i < prerequisites.length; i++){ arr.get(prerequisites[i][0]).add(prerequisites[i][1]); } for(int i = 0; i < numCourses; i++){ if(dfs(i)==false){ return false; } } return true; } public boolean dfs(int course){ //visiting means this dfs() is searching for course's pre. Itself is in the loop. if(visiting.contains(course)){ return false; } //shortcut remember the the possible answer, avoiding repeated calculation if(canVisit.contains(course)){ return true; } //no preCourse for this course, can be taken if(arr.get(course).size()==0){ canVisit.add(course); return true; } //Add to current visiting before check the preCourse visiting.add(course); for(Integer i:arr.get(course)){ if(dfs(i)==false){ return false; } } canVisit.add(course); //Remove from current visiting. Because this course can be reach and not in a loop visiting.remove(course); return true; } } ``` *3 ms 95.77% 43.2 MB 70.85%* Another solution of same idea ```java class Solution { //DFS enum Status { NOT_VISITED, VISITED, VISITING; } public boolean canFinish(int numCourses, int[][] prerequisites) { if(prerequisites == null || prerequisites.length == 0 || prerequisites[0].length == 0) return true; // building graph List> list = new ArrayList<>(); // System.out.println(list.size()); for(int i = 0; i < numCourses; i++) { list.add(new ArrayList()); } for(int[] p: prerequisites) { int prerequisite = p[1]; int course = p[0]; list.get(course).add(prerequisite); } Status[] visited = new Status[numCourses]; for(int i = 0; i < numCourses; i++) { // if there is a cycle, return false if(dfs(list, visited, i)) return false; } return true; } private boolean dfs(List> list, Status[] visited, int cur) { if(visited[cur] == Status.VISITING) return true; if(visited[cur] == Status.VISITED) return false; visited[cur] = Status.VISITING; for(int next: list.get(cur)) { if(dfs(list, visited, next)) return true; } visited[cur] = Status.VISITED; return false; } } ``` ## 208. Implement Trie (Prefix Tree) [208. Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/) > A trie (pronounced as "try") or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker. > Implement the Trie class: > > Trie() Initializes the trie object. > > void insert(String word) Inserts the string word into the trie. > > boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. > > boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise. > Example 1: > > Input > > \["Trie", "insert", "search", "search", "startsWith", "insert", "search"

\[\], \["apple"\], \["apple"\], \["app"\], \["app"\], \["app"\], \["app"\]

Output

null, null, true, false, true, null, true

Explanation

Trie trie = new Trie();

trie.insert("apple");

trie.search("apple"); // return True

trie.search("app"); // return False

trie.startsWith("app"); // return True

trie.insert("app");

trie.search("app"); // return True
Constraints:

1 <= word.length, prefix.length <= 2000

word and prefix consist only of lowercase English letters.

At most 3 * 104 calls in total will be made to insert, search, and startsWith.

java 复制代码
class TrieNode{
     
    public TrieNode[] list;
    public boolean isEnd;
    public TrieNode(){
        list = new TrieNode[26];
        
        isEnd = false;
    }
}
     
class Trie {
    
    private TrieNode root;

    public Trie() {
        root = new TrieNode();
    }
    
    public void insert(String word) {
        TrieNode p = root;
        for(int i = 0; i < word.length(); i++){
            if(p.list[word.charAt(i)-'a']==null){
                // if null insert node
                p.list[word.charAt(i)-'a'] = new TrieNode();
            }
            p = p.list[word.charAt(i)-'a'];
        }
        p.isEnd = true;
    }
    
    public boolean search(String word) {
        TrieNode p = root;
        for(int i = 0; i < word.length(); i++){
            if(p.list[word.charAt(i)-'a']==null){
                // character not exist
                return false;
            }
            p = p.list[word.charAt(i)-'a'];
        }
        if(p.isEnd == true){
            return true;
        }
        return false;
    }
    
    public boolean startsWith(String prefix) {
        TrieNode p = root;
        for(int i = 0; i < prefix.length(); i++){
            if(p.list[prefix.charAt(i)-'a']==null){
                // character not exist
                return false;
            }
            p = p.list[prefix.charAt(i)-'a'];
        }
        return true;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

36 ms 90.36% 51.1 MB 90.80%

209. Minimum Size Subarray Sum

209. Minimum Size Subarray Sum

Given an array of positive integers nums and a positive integer target, return the minimal length of a subarray whose sum is greater than or equal to target. If there is no such subarray, return 0 instead.
Example 1:

Input: target = 7, nums = [2,3,1,2,4,3]

Output: 2

Explanation: The subarray [4,3] has the minimal length under the problem constraint.

Example 2:

Input: target = 4, nums = [1,4,4]

Output: 1

Example 3:

Input: target = 11, nums = [1,1,1,1,1,1,1,1]

Output: 0
Constraints:

1 <= target <= 109

1 <= nums.length <= 105

1 <= nums[i] <= 104
Follow up: If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log(n)).

Sliding window

left , right

Move the right boundary until it reach the condition, then move the left to narrow the edge and update the optimal solution. When it get out of the condition, keep move the right boundary.

java 复制代码
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int left = 0;
        int right = 0;
        int min = Integer.MAX_VALUE;
        int sum = 0;
        while(right<nums.length){
            while(sum < target && right<nums.length){
                sum += nums[right];
                right++;
            }
            while(sum>=target && left<=right){
                int len = right - left;                
                if(min>len){
                    min = len;
                }
                sum -= nums[left];
                left++;
            }
        }
        return min==Integer.MAX_VALUE?0:min;
    }
}

1 ms 100.00% 49.7 MB 91.47%

210. Course Schedule II

210. Course Schedule II

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai.

For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1.

Return the ordering of courses you should take to finish all courses. If there are many valid answers, return any of them. If it is impossible to finish all courses, return an empty array.
Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]

Output: [0,1]

Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1].

Example 2:

Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]

Output: [0,2,1,3]

Explanation: There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0.

So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3].

Example 3:

Input: numCourses = 1, prerequisites = []

Output: [0]
Constraints:

1 <= numCourses <= 2000

0 <= prerequisites.length <= numCourses * (numCourses - 1)

prerequisites[i].length == 2

0 <= ai, bi < numCourses

ai != bi

All the pairs [ai, bi] are distinct.

Return an empty array :

复制代码
return new int[0];
java 复制代码
class Solution {
    HashSet<Integer> visiting = new HashSet<>();
    ArrayList<ArrayList<Integer>> arr = new ArrayList<>();
    HashSet<Integer> canVisit = new HashSet<>();
    int[] result;
    int index = 0;
        
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        result = new int[numCourses];
        //initialize the preCourse list 
        //use arr.get(courseNum) to get course's preCourse
        for(int i = 0; i < numCourses; i++){
            ArrayList<Integer> pre = new ArrayList<>();
            arr.add(pre);
        }
        for(int i = 0; i < prerequisites.length; i++){
            arr.get(prerequisites[i][0]).add(prerequisites[i][1]);
        }

        for(int i = 0; i < numCourses; i++){
            if(canVisit.contains(i)){
                continue;
            }
            if(dfs(i)==false){
                return new int[0];
            }
        }
        return result;
        
    }
    public boolean dfs(int course){
        //visiting means this dfs() is searching for course's pre. Itself is in the loop.
        if(visiting.contains(course)){
            return false;
        }
        //shortcut remember the the possible answer, avoiding repeated calculation
        if(canVisit.contains(course)){
            return true;
        }
        //no preCourse for this course, can be taken
        if(arr.get(course).size()==0){
            canVisit.add(course);
            result[index] = course;
            index++;
            return true;
        }
        //Add to current visiting before check the preCourse
        visiting.add(course);
        for(Integer i:arr.get(course)){
            if(dfs(i)==false){
                return false;
            }
        }
        canVisit.add(course);
        //Remove from current visiting. Because this course can be reach and not in a loop
        visiting.remove(course);
        result[index] = course;
        index++;
        return true;
    }
}

3 ms 99.03% 43.3 MB 86.90%

Topological sort

Used in Directed Acyclic Graph (DAG) 有向无环图

a linear ordering of its vertices such that for every directed edge u->v from vertex u to vertex v, u comes before v in the ordering.

Steps:

  1. Initialization:
  • Create a visited array to keep track of visited nodes.
  • Use a stack to store the nodes in the topological order.
  1. DFS for Topological Sort:
  • For each unvisited node, perform DFS.
  • During DFS, mark the current node as visited.
  • Recur for all adjacent nodes (dependencies).
  • After all the adjacent nodes are processed, push the current node onto the stack.
  1. Result:
  • After the DFS completes for all nodes, the stack contains the topological order, which is obtained by popping nodes from the stack.

211. Design Add and Search Words Data Structure

Design a data structure that supports adding new words and finding if a string matches any previously added string.
Implement the WordDictionary class:

WordDictionary() Initializes the object.

void addWord(word) Adds word to the data structure, it can be matched later.

bool search(word) Returns true if there is any string in the data structure that matches word or false otherwise. word may contain dots '.' where dots can be matched with any letter.
Example:

Input

"WordDictionary","addWord","addWord","addWord","search","search","search","search"

\[\],\["bad"\],\["dad"\],\["mad"\],\["pad"\],\["bad"\],\[".ad"\],\["b..."\]

Output

null,null,null,null,false,true,true,true

Explanation

WordDictionary wordDictionary = new WordDictionary();

wordDictionary.addWord("bad");

wordDictionary.addWord("dad");

wordDictionary.addWord("mad");

wordDictionary.search("pad"); // return False

wordDictionary.search("bad"); // return True

wordDictionary.search(".ad"); // return True

wordDictionary.search("b..."); // return True
Constraints:

1 <= word.length <= 25

word in addWord consists of lowercase English letters.

word in search consist of '.' or lowercase English letters.

There will be at most 3 dots in word for search queries.

At most 104 calls will be made to addWord and search.

Prefix Tree / TrieTree

Use dfs and backtrack to track all the exist node when meeting ' . '

java 复制代码
class TrieNode{
    public TrieNode[] dic;
    boolean isEnd;
    
    public TrieNode(){
        dic = new TrieNode[26];
        isEnd = false;
    }
}
class WordDictionary {
    
    TrieNode root;

    public WordDictionary() {
        root = new TrieNode();
    }
    
    public void addWord(String word) {
        TrieNode p = root;
        for(int i = 0; i < word.length(); i++){
            if(p.dic[word.charAt(i)-'a']==null){
                p.dic[word.charAt(i)-'a'] = new TrieNode();
            }
            p = p.dic[word.charAt(i)-'a'];
        }
        p.isEnd = true;
    } 
    
    public boolean search(TrieNode p,String word,int index){
        if(index > word.length() || p==null){
            return false;
        }
        if(index == word.length()){
            return p.isEnd;
        }
        if(word.charAt(index) == '.'){
            for(TrieNode n:p.dic){
                if(search(n,word,index+1)){
                    return true;
                }
            }
            return false;
        }
        if (p.dic[word.charAt(index)-'a']==null){
            return false;
        }
        return search(p.dic[word.charAt(index)-'a'],word,index+1);
    }
    
    public boolean search(String word) {
        return search(root,word,0);
    }

}

533 ms 80.82% 104.4 MB 64.85%

213. House Robber II

213. House Robber II

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system connected, and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: nums = [2,3,2]

Output: 3

Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2), because they are adjacent houses.

Example 2:

Input: nums = [1,2,3,1]

Output: 4

Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).

Total amount you can rob = 1 + 3 = 4.

Example 3:

Input: nums = [1,2,3]

Output: 3
Constraints:

1 <= nums.length <= 100

0 <= nums[i] <= 1000

Houses are linked as a circular linked list .

The first house is related to the last house.
Have two conditions: 1. Rob the first one and leave the last one. 2. Leave first and rob the last.
Test this two condition by using two dp array.

java 复制代码
    //rob[]  store the last house
    //notrob[] not rob the last
    //dp[i] = Max(dp[i-2]+nums[i], dp[i-1])
    public int rob(int[] nums) {
        if(nums.length==1){
            return nums[0];
        }
        int[] rob = new int[nums.length+1];
        int[] notrob = new int[nums.length+1];
        notrob[1] = nums[0];
        rob[1] = 0;
        for(int i = 2; i < nums.length; i++){
            notrob[i] = Math.max(notrob[i-2]+nums[i-1], notrob[i-1]);
            rob[i] = Math.max(rob[i-2]+nums[i-1], rob[i-1]);
        }
        notrob[nums.length] = notrob[nums.length-1];
        rob[nums.length] = Math.max(rob[nums.length-2]+nums[nums.length-1], rob[nums.length-1]);
        return Math.max(notrob[nums.length],rob[nums.length]);
    }

0 ms 100% 41.5 MB 44.04%

215. Kth Largest Element in an Array

215. Kth Largest Element in an Array

Given an integer array nums and an integer k, return the kth largest element in the array.

Note that it is the kth largest element in the sorted order, not the kth distinct element.

You must solve it in O(n) time complexity.
Example 1:

Input: nums = [3,2,1,5,6,4], k = 2

Output: 5

Example 2:

Input: nums = [3,2,3,1,2,4,5,5,6], k = 4

Output: 4
Constraints:

1 <= k <= nums.length <= 105

-104 <= nums[i] <= 104

Must solve it in O(n) time complexity

k largest(or smallest) elements in an array

Method 1 Heap Sort

Use Max Heap the pop k times.
Use Min Heap to store the smallest k number.
The elements are stored based on the priority order which is ascending by default.

java 复制代码
    public int findKthLargest(int[] nums, int k) {
        Queue<Integer> minHeap = new PriorityQueue<>();
        for (int n : nums) {
            minHeap.add(n);
            if (minHeap.size() > k) {
                minHeap.remove();
            }
        }
        return minHeap.peek();
    }

PriorityQueue

java 复制代码
Queue<Integer> minHeap = new PriorityQueue<>();

The elements are stored based on the priority order which is ascending by default.

  • boolean add(E element)
  • public peek() : This method retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.
  • public poll() : This method retrieves and removes the head of this queue, or returns null if this queue is empty.

Constructors:

  1. PriorityQueue(): Creates a PriorityQueue with the default initial capacity (11) that orders its elements according to their natural ordering.

    复制代码
     PriorityQueue<E> pq = new PriorityQueue<E>();
  2. PriorityQueue(Collection c): Creates a PriorityQueue containing the elements in the specified collection.

    复制代码
     PriorityQueue<E> pq = new PriorityQueue<E>(Collection<E> c);
  3. PriorityQueue(int initialCapacity): Creates a PriorityQueue with the specified initial capacity that orders its elements according to their natural ordering.

    复制代码
     PriorityQueue<E> pq = new PriorityQueue<E>(int initialCapacity);
  4. PriorityQueue(int initialCapacity, Comparator comparator): Creates a PriorityQueue with the specified initial capacity that orders its elements according to the specified comparator.

    复制代码
     PriorityQueue<E> pq = new PriorityQueue(int initialCapacity, Comparator<E> comparator);

Implement PriorityQueue through Comparator in Java

Heap Sort

Array = {1, 3, 5, 4, 6, 13, 10, 9, 8, 15, 17}

Corresponding Complete Binary Tree is:

复制代码
             1
          /     \
       3         5
    /    \     /  \
  4      6   13  10
 / \    / \
 9   8  15 17

Total Nodes = 11.

Total non-leaf nodes= (11/2)-1=5

last non-leaf node = 6.

Therefore, Last Non-leaf node index = 4.

To build the heap, heapify only the nodes: [1, 3, 5, 4, 6] in reverse order.

Heapify 6: Swap 6 and 17.

复制代码
             1
          /     \
       3         5
    /    \      /  \
 4      17   13  10
/ \    /  \
9   8  15   6

Heapify 4: Swap 4 and 9.

复制代码
             1
          /     \
       3         5
    /    \      /  \
 9      17   13  10
/ \    /  \
4   8  15   6

Heapify 5: Swap 13 and 5.

复制代码
             1
          /     \
       3         13
    /    \      /  \
 9      17   5   10
/ \    /  \
4   8  15   6

Heapify 3: First Swap 3 and 17, again swap 3 and 15.

复制代码
             1
         /     \
    17         13
   /    \      /  \
9      15   5   10
/ \    /  \
4   8  3   6

Heapify 1: First Swap 1 and 17, again swap 1 and 15, finally swap 1 and 6.

复制代码
             17
          /      \
      15         13
     /    \      /  \
   9      6    5   10
  / \    /  \
4   8  3    1

Method 2 Quick Sort

java 复制代码
    //quick sort
    public int findKthLargest(int[] nums, int k) {
        int start = 0, end = nums.length - 1, index = nums.length - k;
        while (start < end) {
            int pivot = partion(nums, start, end);
            if (pivot < index) start = pivot + 1; 
            else if (pivot > index) end = pivot - 1;
            else return nums[pivot];
        }
        return nums[start];
    }
    
    private int partion(int[] nums, int start, int end) {
        int pivot = start, temp;
        while (start <= end) {
            while (start <= end && nums[start] <= nums[pivot]) start++;
            while (start <= end && nums[end] > nums[pivot]) end--;
            if (start > end) break;
            temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
        }
        temp = nums[end];
        nums[end] = nums[pivot];
        nums[pivot] = temp;
        return end;
    }

TO BE FINISHED NOT CHECK

216. Combination Sum III

216. Combination Sum III

Find all valid combinations of k numbers that sum up to n such that the following conditions are true:

Only numbers 1 through 9 are used.

Each number is used at most once.

Return a list of all possible valid combinations. The list must not contain the same combination twice, and the combinations may be returned in any order.
Example 1:

Input: k = 3, n = 7

Output: [[1,2,4]]

Explanation:

1 + 2 + 4 = 7

There are no other valid combinations.

Example 2:

Input: k = 3, n = 9

Output: [[1,2,6],[1,3,5],[2,3,4]]

Explanation:

1 + 2 + 6 = 9

1 + 3 + 5 = 9

2 + 3 + 4 = 9

There are no other valid combinations.

Example 3:

Input: k = 4, n = 1

Output: []

Explanation: There are no valid combinations.

Using 4 different numbers in the range [1,9], the smallest sum we can get is 1+2+3+4 = 10 and since 10 > 1, there are no valid combination.
Constraints:

2 <= k <= 9

1 <= n <= 60

backtrack

When adding into List<List< Integer >>, must give its a new instance(memory address), or the ans(List< Integer >) will remain changeable in the later traverse.

复制代码
Wrong
ansList.add(ans);
ansList = [{}]

Accepted
ansList.add(new ArrayList(ans));
java 复制代码
class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> ansList = new ArrayList<>();
        helper(k,1,n,ansList,new ArrayList<Integer>());
        return ansList;
    }
    public void helper(int k,int i,int n,List<List<Integer>> ansList,List<Integer> ans){
        if(k == 0 && n == 0){
            ansList.add(new ArrayList(ans));
            return;
        }
        for(;i <= 9; i++){
            if(i > n){
                return;
            }
            ans.add(i);
            helper(k-1,i+1,n-i,ansList,ans);
            ans.remove(ans.size()-1);
        }
    }
}

0 ms 100.00% 40.2 MB 65.34%

217. Contains Duplicate

217. Contains Duplicate

Given an integer array nums, return true if any value appears at least twice in the array, and return false if every element is distinct.
Example 1:

Input: nums = [1,2,3,1]

Output: true

Example 2:

Input: nums = [1,2,3,4]

Output: false

Example 3:

Input: nums = [1,1,1,3,3,4,3,2,4,2]

Output: true
Constraints:

1 <= nums.length <= 105

-109 <= nums[i] <= 109

Method 1 Use Set

java 复制代码
    public boolean containsDuplicate(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for(int i = 0; i < nums.length; i++){
            if(set.contains(nums[i])){
                return true;
            }
            set.add(nums[i]);
        }
        return false;
    }

7 ms 88.51% 54.5 MB 83.40%

Method 2 Sort and check

java 复制代码
class Solution {
    public boolean containsDuplicate(int[] nums) {
        Arrays.sort(nums);
        for(int i = 0; i < nums.length-1; i++){
            if(nums[i]==nums[i+1]){
                return true;
            }
        }
        return false;
    }
}

23 ms 45.55% 71.1 MB 20.57%

219. Contains Duplicate II

219. Contains Duplicate II

Given an integer array nums and an integer k, return true if there are two distinct indices i and j in the array such that nums[i] == nums[j] and abs(i - j) <= k.
Example 1:

Input: nums = [1,2,3,1], k = 3

Output: true

Example 2:

Input: nums = [1,0,1,1], k = 1

Output: true

Example 3:

Input: nums = [1,2,3,1,2,3], k = 2

Output: false
Constraints:

1 <= nums.length <= 105

-109 <= nums[i] <= 109

0 <= k <= 105

Use a hashmap to store the number and its indexs.

Calculate the dictance when traversing.

java 复制代码
    //Use HashMap to record the value and the corresponding index. Duplicate when containsKey()==true
    //can also use hashset
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(nums[i])) {
                if (i - map.get(nums[i]) <= k) return true;
            }
            map.put(nums[i], i);
        }
        return false;
    }

221. Maximal Square

221. Maximal Square

Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.
Example 1:

Input: matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]

Output: 4

Example 2:

Input: matrix = [["0","1"],["1","0"]]

Output: 1

Example 3:

Input: matrix = [["0"]]

Output: 0
Constraints:

m == matrix.length

n == matrix[i].length

1 <= m, n <= 300

matrix[i][j] is '0' or '1'.

dp

if(matrix[ x ][ y ]==0){

dp[ x ][ y ] = 0;

}

if(matrix[ x ][ y ]==1){
dp[ x ][ y ] = min(dp[ x - 1 ][ y ],dp[ x ][ y - 1 ],dp[ x - 1][ y - 1 ]) + 1;

}

Record the maximum square boundary length according to the top,left,top-left grid.

\["1","0","1","0"

"1","0","1","1"

"1","0","1","1"

"1","1","1","1"\]

Expected 4

java 复制代码
class Solution {
    //https://leetcode.com/problems/maximal-square/discuss/600149/Python-Thinking-Process-Diagrams-DP-Approach
    /*
        matrix[i][j]=='1'
        dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
        According to the top,left,top-left, record the maximum square boundary length
        0 1     1 1     1 1 0  1 1 0 
        1 X     1 X     1 1 0  1 2 0
          1       1     0 1 1  0 1 1
    */
    public int maximalSquare(char[][] matrix) {
        int max = 0;
        int[][] dp = new int[matrix.length][matrix[0].length];
        for(int i = 0; i < matrix.length; i++){
            for(int j = 0; j < matrix[0].length; j++){
                if(i == 0 || j == 0){
                    if(matrix[i][j]=='1'){
                        dp[i][j] = 1;
                    }
					//dp[i][j] init to be 0
                }
                else if(matrix[i][j]=='1'){
                    dp[i][j] = Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                }
				//dp[i][j] init to be 0
                if(dp[i][j]>max){
                    max = dp[i][j];
                }
           }
        }
        return max*max;
    }
}

6 ms 85.29% 53.9 MB 89.48%

java 复制代码
    public int maximalSquare(char[][] matrix) {
        int max = 0;
        int[][] dp = new int[matrix.length+1][matrix[0].length+1];
        
        for(int i = 1; i <= matrix.length; i++){
            for(int j = 1; j <= matrix[0].length; j++){
                if(matrix[i-1][j-1]=='1'){
                    dp[i][j] = Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                }
                if(dp[i][j]>max){
                    max = dp[i][j];
                }
           }
        }
        return max*max;
    }

8 ms 72.00% 58.4 MB 66.15%

222. Count Complete Tree Nodes

222. Count Complete Tree Nodes

Given the root of a complete binary tree, return the number of the nodes in the tree.

According to Wikipedia, every level, except possibly the last, is completely filled in a complete binary tree, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.

Design an algorithm that runs in less than O(n) time complexity.

Example 1:

Input: root = [1,2,3,4,5,6]

Output: 6

Example 2:

Input: root = []

Output: 0

Example 3:

Input: root = [1]

Output: 1
Constraints:

The number of nodes in the tree is in the range [0, 5 * 104].

0 <= Node.val <= 5 * 104

The tree is guaranteed to be complete.

In Complete Tree, only the last level can have the missing nodes. All previous levels are filled.

The number of nodes in each leve is 2^(n-1) (root in the 1 level).

The number of nodes n in a full binary tree is and at most (2^n) - 1

java 复制代码
class Solution {
    public int countNodes(TreeNode root) {
        if(root==null){
            return 0;
        }
        TreeNode left = root, right = root;
        int countL = 0,countR = 0;
        while(left!=null){
            countL++;
            left = left.left;
        }
        while(right!=null){
            countR++;
            right = right.right;
        }
        if(countL==countR){
            return (1<<countL)- 1;
        }
        return 1 + countNodes(root.left) + countNodes(root.right);
    }
}

0 ms 100.00% 45.2 MB 84.59%

Use BFS to count the last level and depth.

java 复制代码
class Solution {
    public int countNodes(TreeNode root) {
        //BFS level traverse
        if(root==null)  return 0;
        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode p = root;
        int count = 0;
        queue.offer(p);
        while(!queue.isEmpty()){
            p = queue.poll();
            count++;
            if(p.left!=null)    queue.offer(p.left);
            if(p.right!=null)    queue.offer(p.right);
        }
        return count;
    }
}

5 ms 7.98% 51.9 MB

223. Rectangle Area

223. Rectangle Area

Given the coordinates of two rectilinear rectangles in a 2D plane, return the total area covered by the two rectangles.

The first rectangle is defined by its bottom-left corner (ax1, ay1) and its top-right corner (ax2, ay2).

The second rectangle is defined by its bottom-left corner (bx1, by1) and its top-right corner (bx2, by2).

Example 1:

Rectangle Area

Input: ax1 = -3, ay1 = 0, ax2 = 3, ay2 = 4, bx1 = 0, by1 = -1, bx2 = 9, by2 = 2

Output: 45

Example 2:

Input: ax1 = -2, ay1 = -2, ax2 = 2, ay2 = 2, bx1 = -2, by1 = -2, bx2 = 2, by2 = 2

Output: 16
Constraints:

-104 <= ax1 <= ax2 <= 104

-104 <= ay1 <= ay2 <= 104

-104 <= bx1 <= bx2 <= 104

-104 <= by1 <= by2 <= 104

复制代码
There are primarily 3 scenarios.

scenario 1 (partly overlapping):
ax1-----------ax2
   	bx1-----------bx2

scenario 2 (fully overlapping):
ax1-----------ax2
 	bx1-bx2

scenario 3 (no overlapping):
ax1-----------ax2
	                bx1-----------bx2

Hence, intersection can be discovered by:

  • determining the greater of the two starting locations

  • calculating the smaller of the two endpoints

    复制代码
      ax1-----------ax2
     		bx1-----------bx2
     :	ax2 - bx1
     		ax1-----------ax2
     bx1-----------bx2
     :	bx2 - ax1
     
     overlapX = min(ax2,bx2) - max(ax1,bx1)
     
     overlapY = min(ay2,by2) - max(ay1,by1)

No overlapping: 0 0 0 0 / -1 -1 1 1

Expected: 4

java 复制代码
class Solution {
    public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
        
        int areaOf1stRectangle = (ax2 - ax1) * (ay2 - ay1);
        int areaOf2ndRectangle = (bx2 - bx1) * (by2 - by1);

        int xOverlap = Math.min(ax2, bx2) - Math.max(ax1, bx1);
        int yOverlap = Math.min(ay2, by2) - Math.max(ay1, by1);
        int areaOverlap = (xOverlap > 0 && yOverlap > 0) ? xOverlap * yOverlap : 0;

        return (areaOf1stRectangle + areaOf2ndRectangle - areaOverlap); 
    }
}

225. Implement Stack using Queues

225. Implement Stack using Queues

Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (push, top, pop, and empty).
Implement the MyStack class:

void push(int x) Pushes element x to the top of the stack.

int pop() Removes the element on the top of the stack and returns it.

int top() Returns the element on the top of the stack.

boolean empty() Returns true if the stack is empty, false otherwise.

Notes:

You must use only standard operations of a queue, which means that only push to back, peek/pop from front, size and is empty operations are valid.

Depending on your language, the queue may not be supported natively. You may simulate a queue using a list or deque (double-ended queue) as long as you use only a queue's standard operations.
Example 1:

Input

"MyStack", "push", "push", "top", "pop", "empty"

\[\], \[1\], \[2\], \[\], \[\], \[\]

Output

null, null, null, 2, 2, false

Explanation

MyStack myStack = new MyStack();

myStack.push(1);

myStack.push(2);

myStack.top(); // return 2

myStack.pop(); // return 2

myStack.empty(); // return False
Constraints:

1 <= x <= 9

At most 100 calls will be made to push, pop, top, and empty.

All the calls to pop and top are valid.

Approach #1 (Two Queues, push - O(1)O(1), pop O(n)O(n) )

java 复制代码
class MyStack {
    //LinkedList inplements Queue interface
    //Queue<String> queue = new LinkedList<String>();

    Queue<Integer> queue1;
    Queue<Integer> queue2;
    
    public MyStack() {
        queue1 = new LinkedList<> ();
        queue2 = new LinkedList<> ();
    }
    
    public void push(int x) {
        queue1.offer(x);
    }
    
    public int pop() {
        int temp,ans = -1;
        while(queue1.size()!=1){
            temp = queue1.poll();
            queue2.offer(temp);
        }
        ans = queue1.poll(); 
        while(!queue2.isEmpty()){
            temp = queue2.poll();
            queue1.offer(temp);
        }
        return ans;
    }
    
    public int top() {
        int temp = -1,ans = -1;
        while(!queue1.isEmpty()){
            temp = queue1.poll();
            queue2.offer(temp);
        }
        ans = temp;
        while(!queue2.isEmpty()){
            temp = queue2.poll();
            queue1.offer(temp);
        }
        return ans;
    }
    
    public boolean empty() {
        return queue1.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

Approach #2 (One Queue, push - O(n)O(n), pop O(1)O(1) )

Push

When we push an element into a queue, it will be stored at back of the queue due to queue's properties. But we need to implement a stack, where last inserted element should be in the front of the queue, not at the back. To achieve this we can invert the order of queue elements when pushing a new element.

java 复制代码
private LinkedList<Integer> q1 = new LinkedList<>();

// Push element x onto stack.
public void push(int x) {
    q1.add(x);
    int sz = q1.size();
    while (sz > 1) {
        q1.add(q1.remove());
        sz--;
    }
}

Time complexity : O(n)O(n). The algorithm removes n elements and inserts n + 1n+1 elements to q1 , where n is the stack size. This gives 2n + 12n+1 operations. The operations add and remove in linked lists has O(1)O(1) complexity.

Space complexity : O(1)O(1).

Pop

The last inserted element is always stored at the front of q1 and we can pop it for constant time.

java 复制代码
// Removes the element on top of the stack.
public void pop() {
    q1.remove();
}

Complexity Analysis

  • Time complexity : O(1)O(1).
  • Space complexity : O(1)O(1).

Empty

Queue q1 contains all stack elements, so the algorithm checks if q1 is empty.

java 复制代码
// Return whether the stack is empty.
public boolean empty() {
    return q1.isEmpty();
}

Time complexity : O(1)O(1).

Space complexity : O(1)O(1).

Top

The top element is always positioned at the front of q1. Algorithm return it.

java 复制代码
// Get the top element.
public int top() {
    return q1.peek();
}

Time complexity : O(1)O(1).

Space complexity : O(1)O(1).

java 复制代码
class MyStack {
    
    Queue<Integer> q;

    public MyStack() {
        q = new LinkedList<>(); 
    }
    
    public void push(int x) {
        int size = q.size();
        q.offer(x);
        while(size > 0){
            q.offer(q.poll());
            size--;
        }
    }
    
    public int pop() {
        return q.poll();
    }
    
    public int top() {
        return q.peek();
    }
    
    public boolean empty() {
        return q.isEmpty();
    }
}

0 ms 100.00% 40.1 MB 76.71%

Add1 1

Add2 1 2-> 21

Add3 21 3-> 321

java 复制代码
    public void push(int x) {
        int size = q.size();
        int[] temp = new int[size];
        int i = 0;
        while(i < size){
            temp[i++] = q.peek();
            q.poll();
            
        }
        q.offer(x);
        for(int t:temp){
            q.offer(t);
        }
    }

offer,add 区别:

一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,多出的项就会被拒绝。

这时新的 offer 方法就可以起作用了。它不是对调用 add() 方法抛出一个 unchecked 异常,而只是得到由 offer() 返回的 false。

poll,remove 区别:

remove() 和 poll() 方法都是从队列中删除第一个元素。remove() 的行为与 Collection 接口的版本相似, 但是新的 poll() 方法在用空集合调用时不是抛出异常,只是返回 null。因此新的方法更适合容易出现异常条件的情况。

peek,element区别:

element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element() 抛出一个异常,而 peek() 返回 null。

226. Invert Binary Tree

226. Invert Binary Tree

Given the root of a binary tree, invert the tree, and return its root.

Example 1:

Input: root = [4,2,7,1,3,6,9]

Output: [4,7,2,9,6,3,1]

Example 2:

Input: root = [2,1,3]

Output: [2,3,1]

Example 3:

Input: root = []

Output: []
Constraints:

The number of nodes in the tree is in the range [0, 100].

-100 <= Node.val <= 100

Recursive

Swift the left subtree and the right subtree.
Inverting from the subtree to root is equals to inverting from root to subtree

Invert the root and go to the subtree.

java 复制代码
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null)  return root;
        TreeNode temp;
        temp = root.left;
        root.left = root.right;
        root.right = temp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

227. Basic Calculator II

227. Basic Calculator II

228. Summary Ranges

228. Summary Ranges

You are given a sorted unique integer array nums.

A range [a,b] is the set of all integers from a to b (inclusive).

Return the smallest sorted list of ranges that cover all the numbers in the array exactly. That is, each element of nums is covered by exactly one of the ranges, and there is no integer x such that x is in one of the ranges but not in nums.

Each range [a,b] in the list should be output as:

"a->b" if a != b

"a" if a == b
Example 1:

Input: nums = [0,1,2,4,5,7]

Output: ["0->2","4->5","7"]

Explanation: The ranges are:

0,2\] --\> "0-\>2" \[4,5\] --\> "4-\>5" \[7,7\] --\> "7" Example 2: Input: nums = \[0,2,3,4,6,8,9

Output: ["0","2->4","6","8->9"]

Explanation: The ranges are:

0,0\] --\> "0" \[2,4\] --\> "2-\>4" \[6,6\] --\> "6" \[8,9\] --\> "8-\>9" Constraints: 0 \<= nums.length \<= 20 -231 \<= nums\[i\] \<= 231 - 1 All the values of nums are unique. nums is sorted in ascending order.

java 复制代码
class Solution {
    public List<String> summaryRanges(int[] nums) {
        List<String> ans = new ArrayList<>();
        if(nums.length == 0)    return ans;
        int begin = -1;
        for(int i = 0; i < nums.length; i++){
            begin = i;
            //find continuous sequence
            while(i+1<nums.length && nums[i]+1==nums[i+1]){
                i++;
            }
            if(begin!=i){
                //String s = (char)(nums[begin]+'0')+"->"+(char)(nums[i]+'0');
                String s = ""+nums[begin]+"->"+nums[i];
                ans.add(s);
            }
            else{
                String s = Integer.toString(nums[i]);
                ans.add(s);
            }
        }
        return ans;
    }
}

229. Majority Element II

229. Majority Element II

Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times.
Example 1:

Input: nums = [3,2,3]

Output: [3]

Example 2:

Input: nums = [1]

Output: [1]

Example 3:

Input: nums = [1,2]

Output: [1,2]
Constraints:

1 <= nums.length <= 5 * 104

-109 <= nums[i] <= 109
Follow up: Could you solve the problem in linear time and in O(1) space?

Method 1

Use HashMap to count the number of appearances.

Time complexity :- O(NlogN)

Space Complexity :- O(N) [Hash Map] + O(N) [List]

java 复制代码
public List<Integer> majorityElement(int[] nums) {

   Map<Integer, Integer> map = new HashMap<>();

   for (int n : nums) {
   		if (!map.containsKey(n)) 
   			map.put(n, 1);
   		else {
   			map.put(n, map.get(n) + 1);
   		}
   }

   List<Integer> majority = new ArrayList<>();

   for (Map.Entry<Integer, Integer> entries : map.entrySet()) {
   		if (entries.getValue() > Math.floor(nums.length / 3)) {
   			majority.add(entries.getKey());
   		}
   }

   return majority;
}

Method 2

Sort the array and count every distinct element.

java 复制代码
class Solution {
    public List<Integer> majorityElement(int[] nums) {
        Arrays.sort(nums);
        List<Integer> ans = new ArrayList<Integer>();
        int count = 1;
        int len = nums.length/3;
        for(int i = 0; i < nums.length; i++){
            count = 1;
            while(i+1 < nums.length && nums[i+1]==nums[i]){
                count++;
                i++;
            }
            if(count>len){
                ans.add(nums[i]);
            }
        }
        return ans;
    }
}

Method 3 Boyer-Moore vote

Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times.
Only 1 - 2 possible number.

Time Compexity :- O(N)

Space Complexity :- O(1)

java 复制代码
public List<Integer> majorityElement(int[] nums) {

	int num1 = Integer.MAX_VALUE, num2 = Integer.MAX_VALUE, count1 = 0, count2 = 0, len = nums.length;

	// collecting votes
	for (int n : nums) {
		if (n == num1) count1++;
		else if (n == num2) count2++;
		else if (count1 == 0) {
			num1 = n;
			count1 = 1;
		} else if (count2 == 0) {
			num2 = n;
			count2 = 1;
		} else {
			count1--;
			count2--;
		}
	}

	// now checking if num1 and num2 occurs more than n/3 times i.e validating vote count
	count1 = 0;
	count2 = 0;
	for (int n : nums) {
		if (n == num1) count1++;
		if (n == num2) count2++;
	}

	List<Integer> result = new ArrayList<>();

	if (count1 > len / 3)
		result.add(num1);
	if (count2 > len / 3)
		result.add(num2);

	return result;
}

There can be at most k - 1 major element in an array if the major element appears more than ⌊n / k⌋ times.

In the begining, we assume there are k - 1 candidates:

These candidates can take any value;
The vote of these candidates must be 0

复制代码
	int num1 = any integer, num2 = Iany integer, count1 = 0, count2 = 0

Then we traverse the array:

If current element equals to the value of any candidate, the candidate get a vote; (one voter can only vote for one candidate)

复制代码
	if (n == num1) count1++;

If the vote of any candidate is 0, then current element is set as a new candidate and he can get a vote immediately; (A voter can also be elected)

复制代码
	if (count1 == 0) {
		num1 = n;
		count1 = 1;
	}

Otherwise, current element vote against all candidates and all candidates lose a vote.

复制代码
	else {
		count1--;
		count2--;
	}

Assume you're voting for the president. If you want to select Trump or Biden. Ok, just vote for them (case 1). If Trump is impeached or Biden is dead, now you can run for the president (case 2). If you want to vote for Lebron James, of course both Biden or Trump won't get your vote (case 3).

After election, we need to count the vote of each candidate to see whether they are qualified for the position, i.e., the vote is larger than ⌊n / k⌋.

230. Kth Smallest Element in a BST

230. Kth Smallest Element in a BST

Given the root of a binary search tree, and an integer k, return the kth smallest value (1-indexed) of all the values of the nodes in the tree.
Example 1:

Input: root = [3,1,4,null,2], k = 1

Output: 1

Example 2:

Input: root = [5,3,6,2,4,null,null,1], k = 3

Output: 3
Constraints:

The number of nodes in the tree is n.

1 <= k <= n <= 104

0 <= Node.val <= 104
Follow up: If the BST is modified often (i.e., we can do insert and delete operations) and you need to find the kth smallest frequently, how would you optimize?

Use Inorder traversal to visit the BST and get the ascending sequence.

Recursive dfs

java 复制代码
class Solution {
    private static int ans;
    private static int count;
    public int kthSmallest(TreeNode root, int k) {
        count = k;
        trackBST(root);
        return ans;
    }
    private void trackBST(TreeNode root){
        if(root.left!=null) trackBST(root.left);
        count--;
        if(count==0){
            ans = root.val;
            return ; 
        }
        if(root.right!=null) trackBST(root.right);
    }
}

0 ms 100.00% 42.5 MB 71.17%

k不作为全局变量 在递归时不共享

JAVA 对象传递(数组、类、接口)是引用传递,原始类型数据(整形、浮点型、字符型、布尔型)传递是值传递。

Stack

java 复制代码
    public int kthSmallest(TreeNode root, int k) {
        //DFS 中序遍历
        Deque<TreeNode> st = new ArrayDeque<>();
        TreeNode p = root;
        //把左子树左结点都进栈
        while(p!=null){
            st.addFirst(p);
            p = p.left;
        }
        while(!st.isEmpty()){
            p = st.removeFirst();
            k--;
            if(k==0){
                return p.val;
            }
            p = p.right;
            //右子树也从左结点开始入栈
            while(p!=null){
                st.addFirst(p);
                p = p.left;
            }       
        }
        return -1;
    }

0ms 100.00% 44.8 MB 7.91%

231. Power of Two

231. Power of Two

Given an integer n, return true if it is a power of two. Otherwise, return false.

An integer n is a power of two, if there exists an integer x such that n == 2x.
Example 1:

Input: n = 1

Output: true

Explanation: 20 = 1

Example 2:

Input: n = 16

Output: true

Explanation: 24 = 16

Example 3:

Input: n = 3

Output: false
Constraints:

-231 <= n <= 231 - 1

Consider edge cases: n == 1, 0, negative

java 复制代码
class Solution {
    public boolean isPowerOfTwo(int n) {
        if(n == 1){
            return true;
        }
        if(n <= 0){
            return false;
        }
        while(n > 2){
            if(n % 2 == 1){
                return false;
            }
            n >>= 1;
        }
        return true;
    }
}

1 ms100.00% 39.5 MB 84.15%

java 复制代码
class Solution {
    public boolean isPowerOfTwo(int n) {
        if(n == 1) return true;
        if(n <= 0) return false;
        while(n > 1){
            if(n % 2 == 1){
                return false;
            }
            n /= 2;
        }
        return true;
    }
}
java 复制代码
    public boolean isPowerOfTwo(int n) {
        return n>0 && Integer.bitCount(n) == 1;
    }

The bitCount() method of Integer class of java.lang package returns the count of the number of one-bits in the two's complement binary representation of an int value. This function is sometimes referred to as the population count.

232. Implement Queue using Stacks

232. Implement Queue using Stacks

Implement a first in first out (FIFO) queue using only two stacks. The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty).

Implement the MyQueue class:

void push(int x) Pushes element x to the back of the queue.

int pop() Removes the element from the front of the queue and returns it.

int peek() Returns the element at the front of the queue.

boolean empty() Returns true if the queue is empty, false otherwise.

Notes:

You must use only standard operations of a stack, which means only push to top, peek/pop from top, size, and is empty operations are valid.

Depending on your language, the stack may not be supported natively. You may simulate a stack using a list or deque (double-ended queue) as long as you use only a stack's standard operations.
Example 1:

Input

"MyQueue", "push", "push", "peek", "pop", "empty"

\[\], \[1\], \[2\], \[\], \[\], \[\]

Output

null, null, null, 1, 1, false

Explanation

MyQueue myQueue = new MyQueue();

myQueue.push(1); // queue is: [1]

myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)

myQueue.peek(); // return 1

myQueue.pop(); // return 1, queue is [2]

myQueue.empty(); // return false
Constraints:

1 <= x <= 9

At most 100 calls will be made to push, pop, peek, and empty.

All the calls to pop and peek are valid.
Follow-up: Can you implement the queue such that each operation is amortized O(1) time complexity? In other words, performing n operations will take overall O(n) time even if one of those operations may take longer.

Two stack

java 复制代码
class MyQueue {

    Stack<Integer> st1;
    Stack<Integer> st2;
    
    public MyQueue() {
        st1 = new Stack<Integer>();
        st2 = new Stack<Integer>();
    }
    
    public void push(int x) {
        st1.push(x);
    }
    
    public int pop() {
        int temp;
        int ans;
        while(!st1.isEmpty()){
            temp = st1.pop();
            st2.push(temp);
        }
        ans = st2.pop();
        while(!st2.isEmpty()){
            temp = st2.pop();
            st1.push(temp);
        }
        return ans;
    }
    
    public int peek() {
        int temp;
        int ans;
        while(!st1.isEmpty()){
            temp = st1.pop();
            st2.push(temp);
        }
        ans = st2.peek();
        while(!st2.isEmpty()){
            temp = st2.pop();
            st1.push(temp);
        }
        return ans;
    }
    
    public boolean empty() {
        return st1.isEmpty();
    }
}

One Stack

java 复制代码
class MyQueue {
    Stack<Integer> s;

    public MyQueue() {
        s = new Stack<>();
    }
    
    public void push(int x) {
        int[] temp = new int[s.size()];
        int i = 0;
        while(!s.isEmpty()){
            temp[i++] = s.peek();
            s.pop();
        }
        s.add(x);
        while(i > 0){
            s.add(temp[--i]);
        }
    }
    
    public int pop() {
        return s.pop();
    }
    
    public int peek() {
        return s.peek();
    }
    
    public boolean empty() {
        return s.empty();
    }
}

empty() isEmpty()

234. Palindrome Linked List

234. Palindrome Linked List

Given the head of a singly linked list, return true if it is a palindrome or false otherwise.
Example 1:

Input: head = [1,2,2,1]

Output: true

Example 2:

Input: head = [1,2]

Output: false
Constraints:

The number of nodes in the list is in the range [1, 105].

0 <= Node.val <= 9
Follow up: Could you do it in O(n) time and O(1) space?

Use array to copy half of the list: O(n) time and O(n) space

Reverse the first half part of the list

Reverse the second half of the list

java 复制代码
    public boolean isPalindrome(ListNode head) {
        ListNode slow = head, fast = head, prev, temp;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        prev = slow;
        slow = slow.next;
        prev.next = null;
        while (slow != null) {
            temp = slow.next;
            slow.next = prev;
            prev = slow;
            slow = temp;
        }
        fast = head;
        slow = prev;
        while (slow != null) {
            if (fast.val != slow.val) return false;
            fast = fast.next;
            slow = slow.next;
        }
        return true;
    }

235. Lowest Common Ancestor of a Binary Search Tree

Given a binary search tree (BST), find the lowest common ancestor (LCA) node of two given nodes in the BST.

According to the definition of LCA on Wikipedia: "The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself)."
Example 1:

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8

Output: 6

Explanation: The LCA of nodes 2 and 8 is 6.

Example 2:

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4

Output: 2

Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

Example 3:

Input: root = [2,1], p = 2, q = 1

Output: 2
Constraints:

The number of nodes in the tree is in the range [2, 105].

-109 <= Node.val <= 109

All Node.val are unique.

p != q

p and q will exist in the BST.

  • if both p and q exist in Tree rooted at root, then return their LCA
  • if neither p and q exist in Tree rooted at root, then return null
  • if only one of p or q (NOT both of them), exists in Tree rooted at root, return it
java 复制代码
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if( root == p || root == q || root == null)
            return root;
        TreeNode left = lowestCommonAncestor( root.left,  p,  q);
        TreeNode right = lowestCommonAncestor( root.right,  p,  q);
        if(left == null)
            return right;
        else if (right == null)
            return left;
        else
            return root;
    }
}

parent pointer and path

父节点指针表:

使用哈希表 parentMap 记录每个节点的父节点。我们通过深度优先搜索(DFS)遍历树,记录每个节点的父节点。

使用一个栈来进行遍历,每次从栈中取出节点,并将其子节点及其父节点关系记录到 parentMap 中。

查找 p 的祖先:

一旦建立了 parentMap,我们可以从节点 p 开始,一直追溯到根节点,并将所有的祖先节点存储到集合 ancestors 中。

查找 q 的第一个公共祖先:

从节点 q 开始追溯,直到找到第一个出现在 p 的祖先集合中的节点。这就是 p 和 q 的最近公共祖先。

java 复制代码
import java.util.HashMap;
import java.util.HashSet;

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // Map to store parent pointers
        HashMap<TreeNode, TreeNode> parentMap = new HashMap<>();
        // Stack for tree traversal
        Stack<TreeNode> stack = new Stack<>();
        
        // Initialize with the root (root has no parent)
        parentMap.put(root, null);
        stack.push(root);
        
        // Traverse the tree until we find both p and q
        while (!parentMap.containsKey(p) || !parentMap.containsKey(q)) {
            TreeNode node = stack.pop();
            
            // Add the children to the stack and record their parent
            if (node.left != null) {
                parentMap.put(node.left, node);
                stack.push(node.left);
            }
            if (node.right != null) {
                parentMap.put(node.right, node);
                stack.push(node.right);
            }
        }
        
        // Create a set to store the ancestors of p
        HashSet<TreeNode> ancestors = new HashSet<>();
        
        // Traverse from p to the root, adding all ancestors of p to the set
        while (p != null) {
            ancestors.add(p);
            p = parentMap.get(p);
        }
        
        // Now traverse from q to the root. The first ancestor of q that is in p's ancestor set is the LCA
        while (!ancestors.contains(q)) {
            q = parentMap.get(q);
        }
        
        return q;  // q is now the lowest common ancestor
    }
}

Use dfs to find the ancester stack of the two Node. Compare the ancester.

java 复制代码
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
	    Deque<TreeNode> pStack = new ArrayDeque<TreeNode>();
	    Deque<TreeNode> qStack = new ArrayDeque<TreeNode>();
	    TreeNode target = null;
	    if (findPath(root, p, pStack) && findPath(root, q, qStack)) {
 		    while (!pStack.isEmpty()) {
 			    TreeNode pNode = pStack.removeFirst();
			    if (qStack.contains(pNode))
				    target = pNode;
		    }
	    } 
	    return target;
    }

    private boolean findPath(TreeNode root, TreeNode node, Deque<TreeNode> stack) {
	    if (root == null)
		    return false;
	    if (root == node) {
		    stack.addFirst(root);
		    return true;
	    } else {
		    if (findPath(root.left, node, stack) ||  findPath(root.right, node, stack)) {
		        stack.addFirst(root);
			    return true;
		    }
	    }
	    return false;
    }
}

5 ms 98.48% 43.7 MB 96.27%

Two stack

java 复制代码
import java.util.Stack;

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // Stacks to hold the paths from root to p and from root to q
        Stack<TreeNode> pathToP = new Stack<>();
        Stack<TreeNode> pathToQ = new Stack<>();
        
        // Find path to p and path to q
        findPath(root, p, pathToP);
        findPath(root, q, pathToQ);
        
        // Compare the paths
        TreeNode lca = null;
        while (!pathToP.isEmpty() && !pathToQ.isEmpty() && pathToP.peek() == pathToQ.peek()) {
            lca = pathToP.pop();
            pathToQ.pop();
        }
        
        return lca;
    }
    
    // Helper function to find the path from root to target
    private boolean findPath(TreeNode root, TreeNode target, Stack<TreeNode> path) {
        if (root == null) {
            return false;
        }
        
        // Add current node to path
        path.push(root);
        
        // Check if current node is the target
        if (root == target) {
            return true;
        }
        
        // Check if target is in the left or right subtree
        if (findPath(root.left, target, path) || findPath(root.right, target, path)) {
            return true;
        }
        
        // If target is not found, backtrack
        path.pop();
        return false;
    }
}

239. Sliding Window Maximum

239. Sliding Window Maximum

You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.

Return the max sliding window.
Example 1:

Input: nums = [1,3,-1,-3,5,3,6,7], k = 3

Output: [3,3,5,5,6,7]

Explanation:

Window position Max


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 Example 2: Input: nums = \[1\], k = 1 Output: \[1

Constraints:

1 <= nums.length <= 105

-104 <= nums[i] <= 104

1 <= k <= nums.length

java 复制代码
import java.util.*;

class MonotonicQueue {
    private Deque<Integer> deque = new LinkedList<>();

    // Push a new element into the queue
    public void push(int n) {
        while (!deque.isEmpty() && deque.peekLast() < n) {
            deque.pollLast();
        }
        deque.offerLast(n);
    }

    // Get the maximum element from the queue
    public int max() {
        return deque.peekFirst();
    }

    // Pop an element from the queue
    public void pop(int n) {
        if (!deque.isEmpty() && deque.peekFirst() == n) {
            deque.pollFirst();
        }
    }
}

public class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return new int[0];
        }
        
        MonotonicQueue window = new MonotonicQueue();
        List<Integer> res = new ArrayList<>();

        for (int i = 0; i < nums.length; i++) {
            if (i < k - 1) {
                window.push(nums[i]);
            } else {
                window.push(nums[i]);
                res.add(window.max());
                window.pop(nums[i - k + 1]);
            }
        }

        return res.stream().mapToInt(i -> i).toArray();
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = {1,3,-1,-3,5,3,6,7};
        int k = 3;
        int[] result = solution.maxSlidingWindow(nums, k);
        System.ou
        t.println(Arrays.toString(result));
    }
}

354. Russian Doll Envelopes

You are given a 2D array of integers envelopes where envelopes[i] = [wi, hi] represents the width and the height of an envelope.

One envelope can fit into another if and only if both the width and height of one envelope are greater than the other envelope's width and height.

Return the maximum number of envelopes you can Russian doll (i.e., put one inside the other).

Note: You cannot rotate an envelope.
Example 1:

Input: envelopes = [[5,4],[6,4],[6,7],[2,3]]

Output: 3

Explanation: The maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]).

Example 2:

Input: envelopes = [[1,1],[1,1],[1,1]]

Output: 1
Constraints:

1 <= envelopes.length <= 105

envelopes[i].length == 2

1 <= wi, hi <= 105

这道题⽬其实是**最⻓递增⼦序列(Longes Increasing Subsequence,简写为LIS)**的⼀个变种,因为很显然,每次合法的嵌套是⼤的套⼩的,相当于找⼀个最⻓递增的⼦序列,其⻓度就是最多能嵌套的信封个数。

Each legal nesting is a large nesting of small ones, which is equivalent to finding the longest increasing subsequence, the length of which is the maximum number of envelopes that can be nested.

但是难点在于,标准的 LIS 算法只能在数组中寻找最⻓⼦序列,⽽我们的信封是由 (w, h) 这样的⼆维数对形式表⽰的,如何把 LIS 算法运⽤过来呢?

先对宽度 w 进⾏升序排序,如果遇到 w 相同的情况,则按照⾼度 h 降序排序。之后把所有的 h 作为⼀个数组,在这个数组上计算 LIS 的⻓度就是答案。

First sort the width w in ascending order. If w is the same, sort by height h in descending order.

对于宽度 w 相同的数对,要对其⾼度 h 进⾏降序排序。
因为两个宽度相同的信封不能相互包含的,逆序排序保证在 w 相同的数对中最多只选取⼀个。

java 复制代码
// envelopes = [[w, h], [w, h]...]
public int maxEnvelopes(int[][] envelopes) {
	int n = envelopes.length;
	// 按宽度升序排列,如果宽度⼀样,则按⾼度降序排列
	Arrays.sort(envelopes, new Comparator<int[]>()
	{
		public int compare(int[] a, int[] b) {
		return a[0] == b[0] ?
		b[1] - a[1] : a[0] - b[0];
		}
	});
	// 对⾼度数组寻找 LIS
	int[] height = new int[n];
	for (int i = 0; i < n; i++)
		height[i] = envelopes[i][1];
	return lengthOfLIS(height);
}

/* 返回 nums 中 LIS 的⻓度 */
public int lengthOfLIS(int[] nums) {
	int piles = 0, n = nums.length;
	int[] top = new int[n];
	for (int i = 0; i < n; i++) {
		// 要处理的扑克牌
		int poker = nums[i];
		int left = 0, right = piles;
		// ⼆分查找插⼊位置
		while (left < right) {
			int mid = (left + right) / 2;
			if (top[mid] >= poker)
				right = mid;
			else
				left = mid + 1;
			}
			if (left == piles) piles++;
			// 把这张牌放到牌堆顶
			top[left] = poker;
		}
	// 牌堆数就是 LIS ⻓度
	return piles;
}

355. Design Twitter

355. Design Twitter

Design a simplified version of Twitter where users can post tweets, follow/unfollow another user, and is able to see the 10 most recent tweets in the user's news feed.
Implement the Twitter class:

Twitter() Initializes your twitter object.

void postTweet(int userId, int tweetId) Composes a new tweet with ID tweetId by the user userId. Each call to this function will be made with a unique tweetId.

List getNewsFeed(int userId) Retrieves the 10 most recent tweet IDs in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user themself. Tweets must be ordered from most recent to least recent.

void follow(int followerId, int followeeId) The user with ID followerId started following the user with ID followeeId.

void unfollow(int followerId, int followeeId) The user with ID followerId started unfollowing the user with ID followeeId.
Example 1:

Input

"Twitter", "postTweet", "getNewsFeed", "follow", "postTweet", "getNewsFeed", "unfollow", "getNewsFeed"

\[\], \[1, 5\], \[1\], \[1, 2\], \[2, 6\], \[1\], \[1, 2\], \[1\]

Output

null, null, \[5\], null, null, \[6, 5\], null, \[5\]

Explanation

Twitter twitter = new Twitter();

twitter.postTweet(1, 5); // User 1 posts a new tweet (id = 5).

twitter.getNewsFeed(1); // User 1's news feed should return a list with 1 tweet id -> [5]. return [5]

twitter.follow(1, 2); // User 1 follows user 2.

twitter.postTweet(2, 6); // User 2 posts a new tweet (id = 6).

twitter.getNewsFeed(1); // User 1's news feed should return a list with 2 tweet ids -> [6, 5]. Tweet id 6 should precede tweet id 5 because it is posted after tweet id 5.

twitter.unfollow(1, 2); // User 1 unfollows user 2.

twitter.getNewsFeed(1); // User 1's news feed should return a list with 1 tweet id -> [5], since user 1 is no longer following user 2.
Constraints:

1 <= userId, followerId, followeeId <= 500

0 <= tweetId <= 104

All the tweets have unique IDs.

At most 3 * 104 calls will be made to postTweet, getNewsFeed, follow, and unfollow.

twitter list

Stack< Pair<userId,twitterId>> order(newest to oldest): Use ArrayDeque for stack
follow list

HashMap<followerId,HashSet> HashMap HashSet for O(1) get set check existed

java 复制代码
class Twitter {


    Deque<Pair<Integer,Integer>> twitterList;
    HashMap<Integer,HashSet<Integer>> followList;
    public Twitter() {
        twitterList = new ArrayDeque<>();
        followList = new HashMap<>();
    }
    
    public void postTweet(int userId, int tweetId) {
        Pair<Integer,Integer> newPost = new Pair<>(userId,tweetId);
        twitterList.push(newPost);
    }
    
    public List<Integer> getNewsFeed(int userId) {
        List<Integer> newsFeed = new ArrayList<>();// 10 
        int count = 0;
        for(Pair<Integer,Integer> twitter:twitterList){
            if(count==10){
                break;
            }
            if(twitter.getKey()==userId || (followList.containsKey(userId) &&followList.get(userId).contains(twitter.getKey()))){
                newsFeed.add(twitter.getValue());
                count++;
            }
        }
        return newsFeed;

    }
    
    public void follow(int followerId, int followeeId) {
        if(followList.containsKey(followerId)){
            followList.get(followerId).add(followeeId);
        }else{
            HashSet<Integer> set = new HashSet<>();
            set.add(followeeId);
            followList.put(followerId,set);
        }
    }
    
    public void unfollow(int followerId, int followeeId) {
        if(followList.containsKey(followerId)){
            followList.get(followerId).remove(followeeId);
        }
    }
}

/**
 * Your Twitter object will be instantiated and called as such:
 * Twitter obj = new Twitter();
 * obj.postTweet(userId,tweetId);
 * List<Integer> param_2 = obj.getNewsFeed(userId);
 * obj.follow(followerId,followeeId);
 * obj.unfollow(followerId,followeeId);
 */

7ms 100.00% 41.97MB 13.92%

O(N) O(N)

Object Design

Add a time

create object Tweet, User

Maintain a tweet list

User maintain a follow set

Use Priority queue for time order

java 复制代码
class Twitter {
private static int timestamp = 0;
private static class Tweet {}
private static class User {}
	...
}
java 复制代码
class Tweet {
	private int id;
	private int time;
	private Tweet next;
	
	// 需要传⼊推⽂内容(id)和发⽂时间
	public Tweet(int id, int time) {
		this.id = id;
		this.time = time;
		this.next = null;
	}
}

// static int timestamp = 0
class User {
	private int id;
	public Set<Integer> followed;
	// ⽤户发表的推⽂链表头结点
	public Tweet head;
	public User(int userId) {
		followed = new HashSet<>();
		this.id = userId;
		this.head = null;
		// 关注⼀下⾃⼰
		follow(id);
	}
	public void follow(int userId) {
		followed.add(userId);
	}
	public void unfollow(int userId) {
	// 不可以取关⾃⼰
		if (userId != this.id)
			followed.remove(userId);
		}
	public void post(int tweetId) {
		Tweet twt = new Tweet(tweetId, timestamp);
		timestamp++;
		// 将新建的推⽂插⼊链表头
		// 越靠前的推⽂ time 值越⼤
		twt.next = head;
		head = twt;
	}
}
java 复制代码
public List<Integer> getNewsFeed(int userId) {
	List<Integer> res = new ArrayList<>();
	if (!userMap.containsKey(userId)) return res;
	// 关注列表的⽤户 Id
	Set<Integer> users = userMap.get(userId).followed;
	// ⾃动通过 time 属性从⼤到⼩排序,容量为 users 的⼤⼩
	PriorityQueue<Tweet> pq = new PriorityQueue<>(users.size(), (a, b)->(b.time - a.time));
	// 先将所有链表头节点插⼊优先级队列
	for (int id : users) {
		Tweet twt = userMap.get(id).head;
		if (twt == null) continue;
			pq.add(twt);
	}
	while (!pq.isEmpty()) {
		// 最多返回 10 条就够了
		if (res.size() == 10) break;
		// 弹出 time 值最⼤的(最近发表的)
		Tweet twt = pq.poll();
		res.add(twt.id);
		// 将下⼀篇 Tweet 插⼊进⾏排序
		if (twt.next != null)
			pq.add(twt.next);
	}
	return res;
}

399. Evaluate Division

https://leetcode.com/problems/evaluate-division/description/

You are given an array of variable pairs equations and an array of real numbers values, where equations[i] = [Ai, Bi] and values[i] represent the equation Ai / Bi = values[i]. Each Ai or Bi is a string that represents a single variable.

You are also given some queries, where queries[j] = [Cj, Dj] represents the jth query where you must find the answer for Cj / Dj = ?.

Return the answers to all queries. If a single answer cannot be determined, return -1.0.

Note: The input is always valid. You may assume that evaluating the queries will not result in division by zero and that there is no contradiction.

Note: The variables that do not occur in the list of equations are undefined, so the answer cannot be determined for them.
Example 1:

Input: equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]

Output: [6.00000,0.50000,-1.00000,1.00000,-1.00000]

Explanation:

Given: a / b = 2.0, b / c = 3.0

queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ?

return: [6.0, 0.5, -1.0, 1.0, -1.0 ]

note: x is undefined => -1.0

Example 2:

Input: equations = [["a","b"],["b","c"],["bc","cd"]], values = [1.5,2.5,5.0], queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]]

Output: [3.75000,0.40000,5.00000,0.20000]

Example 3:

Input: equations = [["a","b"]], values = [0.5], queries = [["a","b"],["b","a"],["a","c"],["x","y"]]

Output: [0.50000,2.00000,-1.00000,-1.00000]
Constraints:

1 <= equations.length <= 20

equations[i].length == 2

1 <= Ai.length, Bi.length <= 5

values.length == equations.length

0.0 < values[i] <= 20.0

1 <= queries.length <= 20

queries[i].length == 2

1 <= Cj.length, Dj.length <= 5

Ai, Bi, Cj, Dj consist of lower case English letters and digits.

java 复制代码
class Solution {
    //java.lang.StackOverflowError
    private double dfs(Map<String,Map<String,Double>> graph, String from,String to,HashSet<String> visited){
        if(visited.contains(from)){
            return -1;
        }
        if(!graph.containsKey(from) || !graph.containsKey(to)){
            return -1;
        }
        visited.add(from);
        if(graph.get(from).containsKey(to)){
            return graph.get(from).get(to);
        }
        for(Map.Entry<String,Double> entry:graph.get(from).entrySet()){
            String neighbor = entry.getKey();
            if(!visited.contains(neighbor)){//if neighbor is not visited
                double result = dfs(graph,neighbor,to,visited);
                if(result!=-1){
                    return entry.getValue()*result;
                }
            }

        }
        visited.remove(from);
        return -1;
    }
    
    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        //node is string use Map<> for node,weighted edge use Map<>
        Map<String,Map<String,Double>> graph = new HashMap<>();

        int edgeNum = values.length;
        for(int i = 0; i < edgeNum; i++){
            String from = equations.get(i).get(0);
            String to = equations.get(i).get(1);
            graph.putIfAbsent(from,new HashMap<>());
            graph.putIfAbsent(to,new HashMap<>());
            graph.get(from).put(to,values[i]);
            graph.get(to).put(from,1/values[i]);
        }
        int len = queries.size();
        double[] result = new double[len];
        for(int i = 0; i < len; i++){
            // 每次搜索时都初始化一个新的访问节点集合
            HashSet<String> visited = new HashSet<>();
            result[i] = dfs(graph,queries.get(i).get(0),queries.get(i).get(1),visited);
        }
        return result;

    }
}

错误

java 复制代码
class Solution {
    private double dfs(Map<String,Map<String,Double>> graph, String from,String to){
        if(!graph.containsKey(from) || !graph.containsKey(to)){
            return -1;
        }
        if(graph.get(from).containsKey(to)){
            return graph.get(from).get(to);
        }else{
            for(Map.Entry<String,Double> entry:graph.get(from).entrySet()){
                if(dfs(graph,from,entry.getKey())!=-1){
                    return entry.getValue()*dfs(graph,entry.getKey(),to);
                }
            }
        }
        return -1;
    }
    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        //node is string use Map<> for node,weighted edge use Map<>
        Map<String,Map<String,Double>> graph = new HashMap<>();

        int edgeNum = values.length;
        for(int i = 0; i < edgeNum; i++){
            String from = equations.get(i).get(0);
            String to = equations.get(i).get(1);
            graph.putIfAbsent(from,new HashMap<>());
            graph.putIfAbsent(to,new HashMap<>());
            graph.get(from).put(to,values[i]);
            graph.get(to).put(from,1/values[i]);
        }
        int len = queries.size();
        double[] result = new double[len];
        for(int i = 0; i < len; i++){
            result[i] = dfs(graph,queries.get(i).get(0),queries.get(i).get(1));
        }
        return result;

    }
}

你的代码出现 java.lang.StackOverflowError 主要是由于递归调用 dfs 函数时,没有适当地处理已经访问过的节点,导致了无限递归。特别是在图的遍历中,如果没有标记已经访问的节点,很容易陷入循环,导致栈溢出。

如何修复 StackOverflowError:

为了避免重复访问相同的节点,可以使用一个 Set 来跟踪当前递归路径中已经访问过的节点

DFS 访问邻接表,HashSet visited,回溯,在dfs最后将当前访问节点移除,确保在访问每一条线路时,不会重复循环

java 复制代码
if(dfs(graph,from,entry.getKey(),visited)!=-1){
    return entry.getValue() * dfs(graph,entry.getKey(),to,visited);
}

这里你在 if 条件中递归调用了一次 dfs,然后在 return 中又递归调用了一次 dfs。这意味着你会进行不必要的多次递归调用,可能导致递归深度过深,并且会对性能产生负面影响。

1091. Shortest Path in Binary Matrix

https://leetcode.com/problems/shortest-path-in-binary-matrix/

Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear path, return -1.

A clear path in a binary matrix is a path from the top-left cell (i.e., (0, 0)) to the bottom-right cell (i.e., (n - 1, n - 1)) such that:
All the visited cells of the path are 0.

All the adjacent cells of the path are 8-directionally connected (i.e., they are different and they share an edge or a corner).

The length of a clear path is the number of visited cells of this path.
Example 1:

Input: grid = [[0,1],[1,0]]

Output: 2

Example 2:

Input: grid = [[0,0,0],[1,1,0],[1,1,0]]

Output: 4

Example 3:

Input: grid = [[1,0,0],[1,1,0],[1,1,0]]

Output: -1
Constraints:

n == grid.length

n == grid[i].length

1 <= n <= 100

grid[i][j] is 0 or 1

In the problem, you're given a binary matrix (grid) where 0 represents a passable cell (you can walk through it), and 1 represents an obstacle (you cannot walk through it).

BFS implement Queue interface with LinkedList

visited current node, then push all its neighborhood into queue.

matrix grid, int[][] grid we store the coordinate into the queue

Use the grid[][] array itself to store the distance, 0 represent path can be visited, when visited a new node add one based on the previous store in grid.

If the grid[][] is not 0, means it already visited

edge condition:

error: if the start or end cell is not 0 , no available path, return -1

success: meet [length-1][length-1]

java 复制代码
class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        int n = grid.length;
        if (grid[0][0] != 0 || grid[n - 1][n - 1] != 0) {
            return -1; // If start or end is blocked
        }
        Queue<int[]> queue = new LinkedList<>(); // store the coordinate [x,y]
        //define the direction in two-dimensional array {{,},{,},{,},{,}}
        int[][] direction = new int[][]{{0,-1},{0,1},{-1,0},{1,0},{-1,-1},{1,1},{-1,1},{1,-1}};
        queue.offer(new int[]{0, 0});
        grid[0][0] = 1;
        while(queue.size()>0){
            int[] cell = queue.poll();
            int x = cell[0];
            int y = cell[1];
            if(x == n-1 && y == n-1){
                return grid[x][y];
            }
            for(int i = 0; i < 8; i++){
                int nextX = x + direction[i][0];
                int nextY = y + direction[i][1];
                //validate next step
                if(nextX>=0 && nextY>=0 && nextX < n && nextY < n && grid[nextX][nextY] == 0){
                    // store the path length in grid, if grid[x][y] == 0 : unvisited, update when visit new cell
                    queue.offer(new int[]{nextX, nextY});
                    grid[nextX][nextY] = grid[x][y]+1;
                }
            }

        }
           return -1; 
    }
}

438. Find All Anagrams in a String

https://leetcode.com/problems/find-all-anagrams-in-a-string/description/

Given two strings s and p, return an array of all the start indices of p's anagrams in s. You may return the answer in any order.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.
Example 1:

Input: s = "cbaebabacd", p = "abc"

Output: [0,6]

Explanation:

The substring with start index = 0 is "cba", which is an anagram of "abc".

The substring with start index = 6 is "bac", which is an anagram of "abc".

Example 2:

Input: s = "abab", p = "ab"

Output: [0,1,2]

Explanation:

The substring with start index = 0 is "ab", which is an anagram of "ab".

The substring with start index = 1 is "ba", which is an anagram of "ab".

The substring with start index = 2 is "ab", which is an anagram of "ab".
Constraints:

1 <= s.length, p.length <= 3 * 104

s and p consist of lowercase English letters.

edge p.length()>s.length()

java 复制代码
class Solution {
    public boolean check(int[] dict){
        for(int i:dict){
            if(i!=0){
                return false;
            }
        }
        return true;
    }
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> result = new ArrayList<>();
        int[] dict = new int[26];
        for(char c:p.toCharArray()){
            dict[c-'a']++;
        } 
        int pLen = p.length();
        int sLen = s.length();
        if(pLen > sLen){
            return result;
        }
        for(int i = 0; i < pLen; i++){
            char c = s.charAt(i);
            dict[c-'a']--;
        }
        if(check(dict)){
            result.add(0);
        }
        for(int i = 1; i <= sLen - pLen; i++){
            char right = s.charAt(i + pLen-1);
            char left = s.charAt(i-1);
            dict[right-'a']--;
            dict[left-'a']++;
            if(check(dict)){
                result.add(i);
            }
        }
        return result;
    
    }
}

474

https://leetcode.com/problems/ones-and-zeroes/

567. Permutation in String

567. Permutation in String

Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise.

In other words, return true if one of s1's permutations is the substring of s2.
Example 1:

Input: s1 = "ab", s2 = "eidbaooo"

Output: true

Explanation: s2 contains one permutation of s1 ("ba").

Example 2:

Input: s1 = "ab", s2 = "eidboaoo"

Output: false
Constraints:

1 <= s1.length, s2.length <= 104

s1 and s2 consist of lowercase English letters.

不是backtracking 因为不需要全部可行permutation,只需要一个

permutation of s1 -> 说明s2的子串为s1的排列,即和s1有相同的字符频率

Sliding Window
fixed length sliding window

length = s1.length()

java 复制代码
class Solution {
    public boolean check(int[] dict){
        for(int i:dict){
            if(i!=0){
                return false;
            }
        }
        return true;
    }
    public boolean checkInclusion(String s1, String s2) {
        int[] dict = new int[26];
        for(char c:s1.toCharArray()){
            dict[c-'a']++;
        } 
        int len1 = s1.length();
        int len2 = s2.length();
        if(len2 < len1){
            return false;
        }
        for(int i = 0; i < len1; i++){
            char c = s2.charAt(i);
            dict[c-'a']--;
        }
        if(check(dict)){
            return true;
        }
        for(int i = len1; i < len2; i++){
            char right = s2.charAt(i);
            char left = s2.charAt(i-len1);
            dict[right-'a']--;
            dict[left-'a']++;
            if(check(dict)){
                return true;
            }
        }
        return false;
    
    }
}

整个算法的时间复杂度为 O(26 * n),其中 n 是 s2 的长度。因为字母表的大小是常数(26),所以实际复杂度接近 O(n)。

647. Palindromic Substrings

https://leetcode.com/problems/palindromic-substrings/description/

Given a string s, return the number of palindromic substrings in it.

A string is a palindrome when it reads the same backward as forward.

A substring is a contiguous sequence of characters within the string.
Example 1:

Input: s = "abc"

Output: 3

Explanation: Three palindromic strings: "a", "b", "c".

Example 2:

Input: s = "aaa"

Output: 6

Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".
Constraints:

1 <= s.length <= 1000

s consists of lowercase English letters.

expand from center

java 复制代码
class Solution {
    int count = 0;
    public void helper(String s,int left, int right){
        while(left >= 0 && right < s.length() && s.charAt(left)==s.charAt(right)){
            count++;
            left--;
            right++;
        }
    }
    public int countSubstrings(String s) {
        
        for(int i = 0; i < s.length(); i++){
            helper(s,i,i);
            helper(s,i,i+1);
        }
        
        return count;
    }
}

DP

DP遍历是回文的不同情况

一个字符:是

两个字符:相等是

三个及以上字符:两端是,看中间

遍历 0...n 把每一个字符当两端起始进行检测

java 复制代码
class Solution {
    public int countSubstrings(String s) {
        int len = s.length();
        boolean[][] dp = new boolean[len][len];

        int count = 0;

        for(int i = 0; i < len; i++){
            dp[i][i] = true;
        }

        for(int i = 0; i < len - 1; i++){
            if(s.charAt(i)==s.charAt(i + 1)){
                dp[i][i + 1] = true;
            }
        }

        for(int length = 2; length < len; length++ ){
            for(int i = 0; i + length < len; i++){
                if(s.charAt(i)==s.charAt(i+length)){
                    dp[i][i+length] = dp[i+1][i+length-1];
                }
            }
        }
        for(int i = 0; i < len; i++){
            for(int j = 0; j < len; j++)
                if(dp[i][j] == true)
                    count++;
        }
        return count;
    }
}

692. Top K Frequent Words

692. Top K Frequent Words

Given an array of strings words and an integer k, return the k most frequent strings.

Return the answer sorted by the frequency from highest to lowest. Sort the words with the same frequency by their lexicographical order.
Example 1:

Input: words = ["i","love","leetcode","i","love","coding"], k = 2

Output: ["i","love"]

Explanation: "i" and "love" are the two most frequent words.

Note that "i" comes before "love" due to a lower alphabetical order.

Example 2:

Input: words = ["the","day","is","sunny","the","the","the","sunny","is","is"], k = 4

Output: ["the","is","sunny","day"]

Explanation: "the", "is", "sunny" and "day" are the four most frequent words, with the number of occurrence being 4, 3, 2 and 1 respectively.
Constraints:

1 <= words.length <= 500

1 <= words[i].length <= 10

words[i] consists of lowercase English letters.

k is in the range [1, The number of unique words[i]]
Follow-up: Could you solve it in O(n log(k)) time and O(n) extra space?

traverse the given string array to count the frequency of the words

Use Map<String,Integer> to store

Get the top K Frequent Words, use Max Heap/PriorityQueue

java 复制代码
class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        // Step 1: Count the frequency of each word
        Map<String, Integer> frequencyMap = new HashMap<>();
        for (String word : words) {
            frequencyMap.put(word, frequencyMap.getOrDefault(word, 0) + 1);
        }

        // Step 2: Use a PriorityQueue to keep the top k frequent words
        //priority queue min heap by default, customize comparator
        //lambda ()->{}
        PriorityQueue<Map.Entry<String, Integer>> maxHeap = new PriorityQueue<>(
            (a, b) -> {
                if (a.getValue().equals(b.getValue())) {
                    return a.getKey().compareTo(b.getKey()); // lexicographically order
                }
                return b.getValue() - a.getValue(); // frequency order
            }
        );

        // Step 3: Add elements to the heap
        for (Map.Entry<String, Integer> entry : frequencyMap.entrySet()) {
            maxHeap.offer(entry);
        }

        // Step 4: Extract the top k frequent words from the heap
        List<String> result = new ArrayList<>();
        for (int i = 0; i < k; i++) {
            result.add(maxHeap.poll().getKey());
        }

        return result;
    }
}
  • map.getOrDefault(key,default value)
  • map vs map.entry()

A Map<String, Integer> represents the entire map , which is a collection of key-value pairs.
A Map.Entry<String, Integer> represents a single key-value pair from the map.

Since the priority queue is supposed to order individual key-value pairs (entries), it is defined to store Map.Entry<String, Integer> objects.

priority queue in java is min heap by default

Customize comparator

java 复制代码
(a,b)->{
	//return a-b; // increasing
	//return b-a; //decreasing
	if(a.getValue()!=b.getValue()){
		return a.getValue()- b.getValue();
	}
	return a.getKey()-b.getKey();
}

== Operator

Usage: Compares references (memory addresses) of two objects.

equals() Method

Usage: Compares the actual content (characters) of two strings.

compareTo() Method

Usage: Compares two strings lexicographically. Returns:

A negative integer if the first string is lexicographically less than the second.

Zero if the strings are equal.

A positive integer if the first string is lexicographically greater than the second.

PriorityQueue, TreeSet, or when sorting collections using Collections.sort(). This is done by implementing the Comparator interface or overriding the compareTo method when implementing the Comparable interface.

Custom Ordering with Comparator

The Comparator interface provides a way to define custom ordering for objects without modifying the objects themselves. You can create a Comparator by implementing its compare method, which compares two objects and returns an integer based on their relative order.

Basic Example

Let's say you have a Person class and you want to sort a list of Person objects by age.

java 复制代码
import java.util.*;

class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

public class CustomOrderingExample {
    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        // Custom ordering by age using Comparator
        Comparator<Person> ageComparator = new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return p1.age - p2.age;  // Ascending order by age
            }
        };

        Collections.sort(people, ageComparator);

        // Print the sorted list
        System.out.println(people);
    }
}

Using Lambda Expressions for Simplicity

Java 8 introduced lambda expressions, which allow you to write more concise code. The above comparator can be written using a lambda expression:

java 复制代码
Comparator<Person> ageComparator = (p1, p2) -> p1.age - p2.age;

Custom Ordering with Multiple Criteria

Often, you might need to sort by multiple criteria. For example, if two Person objects have the same age, you might want to further sort them by name in alphabetical order:

java 复制代码
Comparator<Person> ageThenNameComparator = (p1, p2) -> {
    if (p1.age == p2.age) {
        return p1.name.compareTo(p2.name);  // Sort by name if ages are the same
    }
    return p1.age - p2.age;  // Otherwise, sort by age
};

Custom Ordering in a PriorityQueue

You can also use custom ordering in a PriorityQueue to determine how elements are prioritized:

java 复制代码
PriorityQueue<Person> queue = new PriorityQueue<>(ageThenNameComparator);
queue.add(new Person("Alice", 30));
queue.add(new Person("Bob", 25));
queue.add(new Person("Charlie", 25));

// The person with the lowest age and then by name will be at the front
while (!queue.isEmpty()) {
    System.out.println(queue.poll());  // Removes and prints the head of the queue
}

Explanation:
Comparator Interface:
The Comparator interface requires implementing the compare method, which takes two objects and returns an integer:
Negative if the first object should come before the second.
Zero if they are equal in order.
Positive if the first object should come after the second.

752. Open the Lock

752. Open the Lock

You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. The wheels can rotate freely and wrap around: for example we can turn '9' to be '0', or '0' to be '9'. Each move consists of turning one wheel one slot.

The lock initially starts at '0000', a string representing the state of the 4 wheels.

You are given a list of deadends dead ends, meaning if the lock displays any of these codes, the wheels of the lock will stop turning and you will be unable to open it.

Given a target representing the value of the wheels that will unlock the lock, return the minimum total number of turns required to open the lock, or -1 if it is impossible.
Example 1:

Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202"

Output: 6

Explanation:

A sequence of valid moves would be "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202".

Note that a sequence like "0000" -> "0001" -> "0002" -> "0102" -> "0202" would be invalid,

because the wheels of the lock become stuck after the display becomes the dead end "0102".

Example 2:

Input: deadends = ["8888"], target = "0009"

Output: 1

Explanation: We can turn the last wheel in reverse to move from "0000" -> "0009".

Example 3:

Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"

Output: -1

Explanation: We cannot reach the target without getting stuck.
Constraints:

1 <= deadends.length <= 500

deadends[i].length == 4

target.length == 4

target will not be in the list deadends.

target and deadends[i] consist of digits only.

deadends 相当于对应的路径是堵死的,不能到达,等于图/树中的障碍

四位密码锁,从0000到指定数字,中间不能经过给定的数字组合,最短的路径
是一个无权图最短路径问题 ,使用BFS

分析从0000开始有多少可能的转移方式

1000 0100 0010 0001 9000 0900 0090 0009

及每一位都可以进行+1或者-1,总共有四个可以变换的位置,即从一个状态出发,有八个可能的下一步状态可供选择

Key Point:

  • BFS (Queue)
  • Use Visited Set to skip duplicate. When add item into queue, add into set
  • Four digits, each has two action add or subtract
  • Check deadend
  • If find target, return
java 复制代码
class Solution {
    private String add(String str,int index){
        char[] ch = str.toCharArray();
        if(ch[index]=='9'){
            ch[index]='0';
        }else{
            ch[index] += 1;
        }
        return new String(ch);
    }
    private String minus(String str,int index){
        char[] ch = str.toCharArray();
        if(ch[index]=='0'){
            ch[index]='9';
        }else{
            ch[index] -= 1;
        }
        return new String(ch);
        
    }
    public int openLock(String[] deadends, String target) {
        Queue<String> queue = new LinkedList<>();
        Set<String> visited = new HashSet<>();
        Set<String> dead = new HashSet<>();
        for(String deadend:deadends){
            dead.add(deadend);
        }
        queue.offer("0000");
        visited.add("0000");
        int step = 0;
        while(!queue.isEmpty()){
            int size = queue.size();
            for(int i = 0; i < size; i++){
                String str = queue.poll();
                if(dead.contains(str)){
                    continue;
                }
                if(str.equals(target)){
                    return step;
                }
                for(int index = 0; index < 4; index++){
                    String addStr = add(str,index);
                    String minusStr = minus(str,index);
                    if(!visited.contains(addStr)){
                        queue.offer(addStr);
                        visited.add(addStr);
                    } 
                    if(!visited.contains(minusStr)){
                        queue.offer(minusStr);
                        visited.add(minusStr);
                    } 
                }
            }
            step++;
        }
        return -1;
    }
}
java 复制代码
 char[] ch = str.toCharArray();
return new String(ch);

双向BFS 优化

同时从start和end开始搜索,看start和end是否相交

双向 BFS 还是遵循 BFS 算法框架的,只是不再使⽤队列,⽽是使⽤HashSet ⽅便快速判断两个集合是否有交集。

另外的⼀个技巧点就是 while 循环的最后交换 q1 和 q2 的内容,所以只要默认扩散 q1 就相当于轮流扩散 q1 和 q2 。

java 复制代码
int openLock(String[] deadends, String target) {
	Set<String> deads = new HashSet<>();
	for (String s : deadends) deads.add(s);
	// ⽤集合不⽤队列,可以快速判断元素是否存在
	Set<String> q1 = new HashSet<>();
	Set<String> q2 = new HashSet<>();
	Set<String> visited = new HashSet<>();
	int step = 0;
	q1.add("0000");
	q2.add(target);
	while (!q1.isEmpty() && !q2.isEmpty()) {
	// 哈希集合在遍历的过程中不能修改,⽤ temp 存储扩散结果
		Set<String> temp = new HashSet<>();
		/* 将 q1 中的所有节点向周围扩散 */
		for (String cur : q1) {
		/* 判断是否到达终点 */
		if (deads.contains(cur))
			continue;
		if (q2.contains(cur))
			return step;
		visited.add(cur);
		/* 将⼀个节点的未遍历相邻节点加⼊集合 */
		for (int j = 0; j < 4; j++) {
		String up = plusOne(cur, j);
		if (!visited.contains(up))
			temp.add(up);
		String down = minusOne(cur, j);
		if (!visited.contains(down))
			temp.add(down);
		}
	}
	/* 在这⾥增加步数 */
	step++;
	// temp 相当于 q1
	// 这⾥交换 q1 q2,下⼀轮 while 就是扩散 q2
	q1 = q2;
	q2 = temp;
	}
	return -1;
}

⽆论传统 BFS 还是双向 BFS,⽆论做不做优化,从 Big O衡量标准来看,时间复杂度都是⼀样的

875. Koko Eating Bananas

875. Koko Eating Bananas

Koko loves to eat bananas. There are n piles of bananas, the ith pile has piles[i] bananas. The guards have gone and will come back in h hours.

Koko can decide her bananas-per-hour eating speed of k. Each hour, she chooses some pile of bananas and eats k bananas from that pile. If the pile has less than k bananas, she eats all of them instead and will not eat any more bananas during this hour.

Koko likes to eat slowly but still wants to finish eating all the bananas before the guards return.

Return the minimum integer k such that she can eat all the bananas within h hours.
Example 1:

Input: piles = [3,6,7,11], h = 8

Output: 4

Example 2:

Input: piles = [30,11,23,4,20], h = 5

Output: 30

Example 3:

Input: piles = [30,11,23,4,20], h = 6

Output: 23
Constraints:

1 <= piles.length <= 104

piles.length <= h <= 109

1 <= piles[i] <= 109

Time exceeded

java 复制代码
class Solution {
    public int minEatingSpeed(int[] piles, int h) {
        int sum = 0;
        int min = 0;
        int max = -1;
        for(int pile:piles){
            sum += pile;
            if(pile>max){
                max = pile;
            }
        }
        if(sum%h!=0)
            min =  sum/h + 1;
        else
            min =  sum/h; 
        for(int i = min; i <= max; i++){
            int count = 0;
            for(int pile:piles){
                while(pile>0){
                    if(pile<i)
                        pile = 0;
                    }else
                        pile -= i;
                    count++;
                }
            }
            if(count<=h){
                return i;
            }
        }
        return max;
    }
}

Optimize with binary search

二叉搜索查找边界

java 复制代码
class Solution {
    private boolean valid(int[] piles,int speed,int h){
        int count = 0;
        for(int pile:piles){
            count += (pile / speed) + ((pile % speed > 0) ? 1 : 0);
        }
        if(count<=h){
            return true;
        }
        return false;
    }

    public int minEatingSpeed(int[] piles, int h) {
        int max = -1;
        for(int pile:piles){
            if(pile>max){
                max = pile;
            }
        }
        int low = 1;
        int high = max + 1;
        while(low<high){
            int mid = (high-low)/2 + low;
            if(valid(piles,mid,h)){
                high = mid;
            }else{
                low = mid + 1;
            }
        }
        return low;
    }
}

12ms 80.86% 45.14 MB 35.65%

886. Possible Bipartition

https://leetcode.com/problems/possible-bipartition/description/

We want to split a group of n people (labeled from 1 to n) into two groups of any size. Each person may dislike some other people, and they should not go into the same group.

Given the integer n and the array dislikes where dislikes[i] = [ai, bi] indicates that the person labeled ai does not like the person labeled bi, return true if it is possible to split everyone into two groups in this way.
Example 1:

Input: n = 4, dislikes = [[1,2],[1,3],[2,4]]

Output: true

Explanation: The first group has [1,4], and the second group has [2,3].

Example 2:

Input: n = 3, dislikes = [[1,2],[1,3],[2,3]]

Output: false

Explanation: We need at least 3 groups to divide them. We cannot put them in two groups.
Constraints:

1 <= n <= 2000

0 <= dislikes.length <= 104

dislikes[i].length == 2

1 <= ai < bi <= n

All the pairs of dislikes are unique.

二分图 Graph Bipartite

相连的结点不属于一组

给定一组讨厌关系(无向图中的边),判断是否可以将这些人分为两组,使得每组内部没有讨厌关系。

Union Find
二分图

A和B dislike,说明AB不能再一起,如果在一起了false

又因为只能分成两组,所以,A只能和B的敌人在一起(A不和B,A不和A敌,B不和B敌,所以A和B敌,B和A敌相连)

使用HashMap存储节点的敌人,存储一个就够了,剩下的敌人直接和已经存储的相连

java 复制代码
class Solution {
    class UnionFind {
        private int[] parent;

        public UnionFind(int n){
            parent = new int[n+1];
            for(int i = 0; i <= n; i++){
                parent[i] = i;
            }
        }

        public int find(int i){
            if(parent[i]!=i){
                parent[i] = find(parent[i]);
            }
            return parent[i];
        }

        public void union(int x,int y){
            int px = find(x);
            int py = find(y);
            // parent[py] = px;
            if(px != py) {
                parent[py] = px;
            }
        }
    }
    public boolean possibleBipartition(int n, int[][] dislikes) {
        UnionFind uf = new UnionFind(n);
        Map<Integer, Integer> enemyMap = new HashMap<>();

        for (int[] dislike : dislikes) {
            int a = dislike[0];
            int b = dislike[1];

            if (!enemyMap.containsKey(a)) {
                enemyMap.put(a, b);
            } else {
                uf.union(b, enemyMap.get(a));
            }

            if (!enemyMap.containsKey(b)) {
                enemyMap.put(b, a);
            } else {
                uf.union(a, enemyMap.get(b));
            }

            // Check if a and b are in the same set, which would be an error
            if (uf.find(a) == uf.find(b)) {
                return false;
            }
        }

        return true;
    }

}

DFS/BFS

将结点相连,连成图
使用DFS或者BFS遍历,染色/0 1,当前节点与相邻节点的颜色必须是相反的,满足二分图,否则不成立

**在对结点进行DFS遍历时,只遍历未访问过的

在添加结点到邻接链表时,因为是无向图,所以要双向添加

在对邻接结点进行dfs访问时,

错误

java 复制代码
for(int neighbor : adjList.get(i)) {
    if(color[neighbor] == 0) {
        dfs(adjList, neighbor, color, -flag);
    } else if(color[neighbor] == flag) {
        return false;
    }
}

冲突检测不充分:颜色标记的逻辑依赖于每个节点的颜色都是唯一且不变的,如果没有正确处理节点是否已经访问过,当遇到一个已经访问过的节点时,程序可能不会及时检测到冲突(即两个相邻节点被标记为同一颜色),这会导致错误的结果。

8

\[1,2\],\[1,3\],\[4,5\],\[4,6\],\[3,5\],\[3,7\],\[5,7\]

Expected: false

java 复制代码
class Solution {
    private boolean dfs(List<List<Integer>> adjList,int i,int[] color,int flag){
        color[i] = flag;
        for(int neighbor:adjList.get(i)){
            if(color[neighbor]==0){
                if(!dfs(adjList,neighbor,color,-flag)){
                    return false;
                } //dfs(adjList,neighbor,color,-flag) wrong
            }else if(color[neighbor]==flag){
                return false;
            }
        }
        return true;
    }
    public boolean possibleBipartition(int n, int[][] dislikes) {
        List<List<Integer>> adjList = new ArrayList<>(n+1);
        int[] color = new int[n+1]; //node [1-n] 1 -1 for two group 0 for unvisited
        adjList.add(new ArrayList<Integer>());//for index = 0
        for(int i = 1; i <= n; i++){
            adjList.add(new ArrayList<Integer>());
        }
        for(int[] dislike:dislikes){
            adjList.get(dislike[0]).add(dislike[1]);//add [1] to [0]'s adjList as well as add [0] to [1]'s
            adjList.get(dislike[1]).add(dislike[0]);
        }
        for(int i = 1; i <= n; i++){
            if(color[i]==0 && !dfs(adjList,i,color,1)){ //check visited
                return false;
            }
        }
        return true;
    }
}

1091. Shortest Path in Binary Matrix

https://leetcode.com/problems/shortest-path-in-binary-matrix/

Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear path, return -1.

A clear path in a binary matrix is a path from the top-left cell (i.e., (0, 0)) to the bottom-right cell (i.e., (n - 1, n - 1)) such that:
All the visited cells of the path are 0.

All the adjacent cells of the path are 8-directionally connected (i.e., they are different and they share an edge or a corner).

The length of a clear path is the number of visited cells of this path.

In the problem, you're given a binary matrix (grid) where 0 represents a passable cell (you can walk through it), and 1 represents an obstacle (you cannot walk through it).

BFS implement Queue interface with LinkedList

visited current node, then push all its neighborhood into queue.

matrix grid, int[][] grid we store the coordinate into the queue

Use the grid[][] array itself to store the distance, 0 represent path can be visited, when visited a new node add one based on the previous store in grid.

If the grid[][] is not 0, means it already visited

edge condition:

error: if the start or end cell is not 0 , no available path, return -1

success: meet [length-1][length-1]

java 复制代码
class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        int n = grid.length;
        if (grid[0][0] != 0 || grid[n - 1][n - 1] != 0) {
            return -1; // If start or end is blocked
        }
        Queue<int[]> queue = new LinkedList<>(); // store the coordinate [x,y]
        //define the direction in two-dimensional array {{,},{,},{,},{,}}
        int[][] direction = new int[][]{{0,-1},{0,1},{-1,0},{1,0},{-1,-1},{1,1},{-1,1},{1,-1}};
        queue.offer(new int[]{0, 0});
        grid[0][0] = 1;
        while(queue.size()>0){
            int[] cell = queue.poll();
            int x = cell[0];
            int y = cell[1];
            if(x == n-1 && y == n-1){
                return grid[x][y];
            }
            for(int i = 0; i < 8; i++){
                int nextX = x + direction[i][0];
                int nextY = y + direction[i][1];
                //validate next step
                if(nextX>=0 && nextY>=0 && nextX < n && nextY < n && grid[nextX][nextY] == 0){
                    // store the path length in grid, if grid[x][y] == 0 : unvisited, update when visit new cell
                    queue.offer(new int[]{nextX, nextY});
                    grid[nextX][nextY] = grid[x][y]+1;
                }
            }

        }
           return -1; 
    }
}

1143. Longest Common Subsequence

https://leetcode.com/problems/longest-common-subsequence/description/

Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.

A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.

For example, "ace" is a subsequence of "abcde".

A common subsequence of two strings is a subsequence that is common to both strings.
Example 1:

Input: text1 = "abcde", text2 = "ace"

Output: 3

Explanation: The longest common subsequence is "ace" and its length is 3.

Example 2:

Input: text1 = "abc", text2 = "abc"

Output: 3

Explanation: The longest common subsequence is "abc" and its length is 3.

Example 3:

Input: text1 = "abc", text2 = "def"

Output: 0

Explanation: There is no such common subsequence, so the result is 0.
Constraints:

1 <= text1.length, text2.length <= 1000

text1 and text2 consist of only lowercase English characters.

DP on string

java 复制代码
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        //keep same order can delete char
        //dp[i][j] for  (0,i) (0,j) longest common subsequence
        //t1[i] == t2[j] : dp[i][j] = dp[i-1][j-1] + 1
        //t1[i] != t2[j] : dp[i][j] = 1. remove 1 dp[i-1][j] 2.remove 2 dp[i][j-1]

        //base case dp[0][0] = 0 = dp[0][i] = dp[i][0]

        int len1 = text1.length();
        int len2 = text2.length();
        int[][] dp = new int[len1+1][len2+1];

        for(int i = 0; i <= len1; i++){
            dp[i][0] = 0;
        }
        for(int i = 0; i <= len2; i++){
            dp[0][i] = 0;
        }

        for(int i = 1; i <= len1; i++){
            for(int j = 1; j <= len2; j++){
                if(text1.charAt(i-1)==text2.charAt(j-1)){
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
                else{
                    dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return dp[len1][len2];

    }
}
相关推荐
今天也好累2 小时前
贪心算法之分数背包问题
c++·笔记·学习·算法·贪心算法
剪一朵云爱着3 小时前
力扣1878. 矩阵中最大的三个菱形和
算法·leetcode
笙歌已沫3 小时前
蓝桥杯2024年第15届B组试题D
算法·职场和发展·蓝桥杯
一碗白开水一4 小时前
【第30话:路径规划】自动驾驶中Hybrid A星(A*)搜索算法的详细推导及代码示例
人工智能·算法·机器学习·计算机视觉·数学建模·自动驾驶
未来之窗软件服务4 小时前
自己写算法(五)CryptoJS 前端学习——东方仙盟练气期
学习·算法·仙盟创梦ide·东方仙盟·东方仙盟加密
徐小夕5 小时前
pxcharts-vue-max 多维表格编辑器:支持大数据渲染 + 二次开发
前端·vue.js·算法
stjiejieto5 小时前
手机中的轻量化 AI 算法:智能生活的幕后英雄
人工智能·算法·智能手机
纪元A梦6 小时前
贪心算法应用:欧拉路径(Fleury算法)详解
算法·贪心算法
大任视点6 小时前
科技赋能噪声防控,守护职业安全健康
大数据·人工智能·算法