49. Group Anagrams

Given an array of strings strs, group the anagrams together. You can 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.

利用JAVA中字符串会固定内存地址。(因此在进行字符串操作时是生成了新的对象。)
遍历每个单词,记录26个字母出现的次数,并映射到字符数组上。
将字符数组转换成字符串,生成一个新的字符串。

将字符串作为key放入map中,value储存原有单词。(字符串的内存地址固定,因此同样的字符串可以被搜索到。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> ret = new ArrayList<>();
HashMap<String, List<String>> map = new HashMap<>();

for(String word : strs){
char[] alphabet = new char[26];
for(int j = 0; j < word.length(); j++){
alphabet[word.charAt(j)-'a']++;
}
String s = new String(alphabet);
List<String> str = map.getOrDefault(s, new ArrayList<String>());
str.add(word);
map.put(s, str);
}
for(String key : map.keySet()){
ret.add(map.get(key));
}
return ret;
}
}

797. All Paths From Source to Target

Given a directed acyclic graph (DAG) of n nodes labeled from 0 to n - 1, find all possible paths from node 0 to node n - 1 and return them in any order.

The graph is given as follows: graph[i] is a list of all nodes you can visit from node i (i.e., there is a directed edge from node i to node graph[i][j]).

回溯。DFS搜索所有的路径。
当搜索到最后一个位置时,保存动态数组并返回。(此处需要深拷贝数组。)
回到上一层,取走动态数组的最后一个节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
List<List<Integer>> ret = new LinkedList<>();
dfs(ret, new ArrayList<>(), graph, 0);
return ret;
}

private void dfs(List<List<Integer>> ret, List<Integer> arr, int[][] graph, int i){
if( i == graph.length - 1){
arr.add(i);
ret.add(new ArrayList(arr));
return;
}
arr.add(i);
for( int j : graph[i] ){
dfs(ret, arr, graph, j);
arr.remove(arr.size()-1);
}
}
}

535. Encode and Decode TinyURL

Note: This is a companion problem to the System Design problem: Design TinyURL.
TinyURL is a URL shortening service where you enter a URL such as https://leetcode.com/problems/design-tinyurl and it returns a short URL such as http://tinyurl.com/4e9iAk. Design a class to encode a URL and decode a tiny URL.

There is no restriction on how your encode/decode algorithm should work. You just need to ensure that a URL can be encoded to a tiny URL and the tiny URL can be decoded to the original URL.

Implement the Solution class:

  • Solution() Initializes the object of the system.
  • String encode(String longUrl) Returns a tiny URL for the given longUrl.
  • String decode(String shortUrl) Returns the original long URL for the given shortUrl. It is guaranteed that the given shortUrl was encoded by the same object.

计算传入连接的哈希值。将其作为key放入map中。
解码时将url转换为key,取出map中的value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Codec {
HashMap<Integer, String> map = new HashMap<>();

// Encodes a URL to a shortened URL.
public String encode(String longUrl) {
int key = longUrl.hashCode();
map.put(key, longUrl);
return Integer.toString(key);
}

// Decodes a shortened URL to its original URL.
public String decode(String shortUrl) {
return map.get(Integer.parseInt(shortUrl));
}
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.decode(codec.encode(url));

1091. 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.

由于不能重复搜索,且需要在搜索时将搜索过的位置改变,因此需要采用BFS逐一层级的搜索。
逐层级搜索并记录层级。最后返回层级+1即可。
(还有更快的优化算法A*)

整体没有改变太多,基本还是方法二那种。启发式函数改为切比雪夫距离距离。
距离计算方法有很多,常用启发式函数有这几种。选择合适的启发式函数有利于速度的提升。这题可以用好几种启发式函数,初学都可以试着都写一下。

曼哈顿距离(Manhattan Distance)
一般只能在四个方向上移动时用(右、左、上、下)

对角线距离(Diagonal Distance):
当我们只允许向八个方向移动时用(国际象棋中的王移动方式那种)

欧几里得距离(Euclidean Distance):
不受限制,允许向任何方向移动时。

切比雪夫距离(Chebyshev Distance):
可参考:[https://leetcode-cn.com/problems/minimum-time-visiting-all-points/solution/fang-wen-suo-you-dian-de-zui-xiao-shi-jian-by-le-2/](LeetCode 1266)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public int shortestPathBinaryMatrix(int[][] grid) {
int n = grid[0].length;
int m = grid.length;

Queue<Integer> q = new LinkedList<>();
int count = 0;
q.add(0);
int size = q.size();

while(!q.isEmpty()){
for(int k = 0; k < size; k++){
int curr = q.poll();
int i = curr / n;
int j = curr % n;

if(grid[i][j] ==1) continue;
if(i == m-1 && j == n-1 && grid[i][j] == 0) return count+1;

if(grid[i][j] == 0){
grid[i][j] = 1;
if(i+1<n && j+1<n) q.add((i+1) * n + (j+1));
if(i+1<n && j-1>=0) q.add((i+1) * n + (j-1));
if(i+1<n) q.add((i+1) * n + j);
if(i-1>=0 && j-1>=0) q.add((i-1) * n + (j-1));
if(i-1>=0 && j+1<n) q.add((i-1) * n + (j+1));
if(j+1<n) q.add(i * n + (j+1));
if(j-1>=0) q.add(i * n + (j-1));
if(i-1>=0) q.add((i-1) * n + j);
}
}
size = q.size();
count++;
}
return -1;
}
}

572. Subtree of Another Tree

Given the roots of two binary trees root and subRoot, return true if there is a subtree of root with the same structure and node values of subRoot and false otherwise.

A subtree of a binary tree tree is a tree that consists of a node in tree and all of this node’s descendants. The tree tree could also be considered as a subtree of itself.

帮助方法isEqual,DFS搜索判断两个节点的子节点是否完全相同。
DFS搜索,如果两个根节点的值相等则返回,且调用isEqual方法,如果子节点都相同则返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
return dfs(root, subRoot) != null;
}

private TreeNode dfs(TreeNode root, TreeNode subRoot){
if(root == null) return null;
if(root.val == subRoot.val && isEqual(root, subRoot)) return root;
if(dfs(root.left, subRoot) != null ) return dfs(root.left, subRoot);
else return dfs(root.right, subRoot);
}

private boolean isEqual(TreeNode root, TreeNode subRoot){
if(root == null || subRoot == null) return root == subRoot;
if(root.val != subRoot.val) return false;
return isEqual(root.left, subRoot.left) && isEqual(root.right, subRoot.right);
}
}

117. Populating Next Pointers in Each Node II

Question

Given a binary tree

1
2
3
4
5
6
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}

Populate each next pointer to point to its next right node. If > there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

Solution

BFS搜索,从右至左将每层节点放入队列。
用一个size记录该层级放入节点的数量,每次循环将size-1,当size归零时放入新一层级的节点。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;

public Node() {}

public Node(int _val) {
val = _val;
}

public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/

class Solution {
public Node connect(Node root) {
if(root == null) return root;

Queue<Node> q = new LinkedList();
q.add(root);

while(!q.isEmpty()){
int size = q.size();
Node next = null;
while(size > 0){
size--;
Node curr = q.poll();
curr.next = next;
next = curr;
if(curr.right != null) q.add(curr.right);
if(curr.left != null) q.add(curr.left);
}
}
return root;
}
}

763. Partition Labels

You are given a string s. We want to partition the string into as many parts as possible so that each letter appears in at most one part.

Note that the partition is done so that after concatenating all the parts in order, the resultant string should be s.

Return a list of integers representing the size of these parts.

将英文字母出现的首尾作为intervals看待。
根据字符创建数组并填入左右的index。
根据左侧index排序数组。

从左至右,如果intervals有交集,则合并。
否则在答案中添加当前interval的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public List<Integer> partitionLabels(String s) {
int[][] alphabet = new int[26][2];
int head = s.charAt(0) - 'a';
for(int i = 0; i < s.length(); i++){
int index = s.charAt(i) - 'a';
if(head != index && alphabet[index][0] == 0) alphabet[index][0] = i;
alphabet[index][1] = i;
}

Arrays.sort(alphabet, (a,b) -> a[0] - b[0]);

List<Integer> ans = new ArrayList();
int[] hold = alphabet[0];
for(int i = 1; i < alphabet.length; i++){
if(alphabet[i][0] <= hold[1]){
hold[1] = Math.max(hold[1], alphabet[i][1]);
}
else{
ans.add(hold[1]-hold[0]+1);
hold = alphabet[i];
}
}
ans.add(hold[1]-hold[0]+1);
return ans;

}
}

290. Word Pattern

Given a pattern and a string s, find if s follows the same pattern.

Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in s.

先将s分割成数组。
如果words的长度和pattern的长度不同则返回false。
用长度为26的数组储存pattern的字符。
同时遍历words和pattern,将pattern的字符换算成数字作为index,将words[i]填入数组。
如果数组中已经有word存在,则检查新的word和数组内的word是否相等。
如果不相等,则返回false。

最后遍历数组,比对各个字符中对应的word是否相等,如果有相等的,则返回false。由于数组长度固定,因此这个操作的时间复杂度是O(1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution {
public boolean wordPattern(String pattern, String s) {
String[] words = s.split(" ");
String[] alphabet = new String[26];

if(words.length != pattern.length()){
return false;
}

for(int i = 0; i < pattern.length(); i++){

if( alphabet[pattern.charAt(i) - 'a'] != null){
if(!alphabet[pattern.charAt(i) - 'a'].equals(words[i])){
return false;
}
}
else{
alphabet[pattern.charAt(i) - 'a'] = words[i];
}
}

for(int i = 0; i < alphabet.length; i++){
for(int j = i+1; j < alphabet.length; j++){
if(alphabet[i] == null || alphabet[j] == null){
continue;
}
if(alphabet[i].equals(alphabet[j])){
return false;
}
}
}

return true;
}
}

415. Add Strings

Given two non-negative integers, num1 and num2 represented as string, return the sum of num1 and num2 as a string.

You must solve the problem without using any built-in library for handling large integers (such as BigInteger). You must also not convert the inputs to integers directly.

按照正常加法计算方法即可。
先将指针设置在两个String的末尾。
每次计算位置上加和的结果。
最后需要将字符串翻转过来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public String addStrings(String num1, String num2) {
int i = num1.length() - 1;
int j = num2.length() - 1;
int carry = 0;

StringBuffer ans = new StringBuffer();

while( i >= 0 || j >= 0 || carry > 0){
int x = i >= 0 ? num1.charAt(i) - '0' : 0;
int y = j >= 0 ? num2.charAt(j) - '0' : 0;
int result = x + y + carry;

ans.append(result % 10);
carry = result / 10;

i--;
j--;
}
ans.reverse();
return ans.toString();
}
}

409. Longest Palindrome

Given a string s which consists of lowercase or uppercase letters, return the length of the longest palindrome that can be built with those letters.

Letters are case sensitive, for example, “Aa” is not considered a palindrome here.

回文字符串除了中心一个元素,其他的字符必须成对出现。因此可以直接统计字符串里有几组成对的字符。

用数组记录字符出现的次数。
如果数组中某个字符出现了两次,则可以将其归零。然后可以组成的最长回文数字加2。
如果字符串还剩下其他字符未选择,则最长回文数可以加一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int longestPalindrome(String s) {
int[] alphabet = new int['z' - 'A' + 1];
int count = 0;
int total = s.length();

for(int i = 0; i < s.length(); i++){
int index = s.charAt(i) - 'A';
alphabet[index]+=1;
if( alphabet[index] == 2 ){
total -= 2;
count += 2;
alphabet[index] = 0;
}
}

if(total != 0){
count++;
}

return count;
}
}

547. Number of Provinces

There are n cities. Some of them are connected, while some are not. If city a is connected directly with city b, and city b is connected directly with city c, then city a is connected indirectly with city c.

A province is a group of directly or indirectly connected cities and no other cities outside of the group.

You are given an n x n matrix isConnected where isConnected[i][j] = 1 if the ith city and the jth city are directly connected, and isConnected[i][j] = 0 otherwise.

Return the total number of provinces.

DFS搜索,当map[i][i] = 1时加入搜索。
搜索时将map[i][i]设为0。遍历并递归其数列。
当map[i][i]等于0时,返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
int[][] map;
public int findCircleNum(int[][] isConnected) {
map = isConnected;
int count = 0;

for(int i = 0; i < isConnected.length; i++){
if(map[i][i] == 1){
count++;
dfs(i);
}
}
return count;
}

private void dfs(int i){
if(map[i][i] == 0){
return;
}
map[i][i] = 0;
for(int j = 0; j < map[0].length; j++){
if(map[i][j] == 1){
dfs(j);
}
}
}
}

705. Design HashSet

Design a HashSet without using any built-in hash table libraries.

Implement MyHashSet class:

  • void add(key) Inserts the value key into the HashSet.
  • bool contains(key) Returns whether the value key exists in the HashSet or not.
  • void remove(key) Removes the value key in the HashSet. If key does not exist in the HashSet, do nothing.

计算哈希值,使用数组实现Hash Set。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MyHashSet {
int PRIME = 1009;
List<Integer>[] data;

public MyHashSet() {
data = new List[PRIME];
for(int i = 0; i < PRIME; i++){
data[i] = new LinkedList<Integer>();
}
}

public void add(int key) {
if(!contains(key)){
int h = getHash(key);
data[h].add(key);
}
}

public void remove(int key) {
int h = getHash(key);
data[h].remove(new Integer(key));
}

public boolean contains(int key) {
int h = getHash(key);
for( int num : data[h] ){
if( num == key ){
return true;
}
}
return false;
}

private int getHash(int o){
return o % PRIME;
}
}

/**
* Your MyHashSet object will be instantiated and called as such:
* MyHashSet obj = new MyHashSet();
* obj.add(key);
* obj.remove(key);
* boolean param_3 = obj.contains(key);
*/

713. Subarray Product Less Than K

Given an array of integers nums and an integer k, return the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than k.

滑动窗口,维护一个窗口内的乘积。
当乘积小于目标值时,窗口右侧向右移动。每加入一个新数值,可以增加(j-i+1)个组合。
当乘积大于目标时,窗口左侧向右移动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int i = 0;
int j = 0;
int product = nums[0];
int count = 0;

while(i < nums.length && j < nums.length){
if(product < k){
count += (j - i) + 1;
j++;
if(j < nums.length ) product *= nums[j];
}
else{
product /= nums[i];
i++;
}
}
return count;
}
}

209. Minimum Size Subarray Sum

Given an array of positive integers nums and a positive integer target, return the minimal length of a contiguous subarray [numsl, numsl+1, …, numsr-1, numsr] of which the sum is greater than or equal to target. If there is no such subarray, return 0 instead.

滑动窗口,先取最左侧数字。记录窗口的最小值。
如果小于目标值,则右侧窗口向右移动,扩大窗口。更新窗口内的值。
如果大于等于目标值,则左侧窗口向右移动,缩小窗口。更新窗口内的值。
同时如果窗口大小小于最小值则更新窗口最小值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int min = Integer.MAX_VALUE;
int left = 0;
int right = 0;
int sum = nums[0];
while(right < nums.length){
if(sum >= target){
min = Math.min(min, right - left + 1);
sum -= nums[left];
left++;
}
else{
right++;
if(right < nums.length) sum += nums[right];
}
}
return min == Integer.MAX_VALUE ? 0 : min;
}
}

计算前缀和。
前缀和[j]与前缀和的[i]的差就是i+1到j的和。
因此需要找到sum[j] - sum[i] >= k。
暴力枚举的话需要O(n^2^)的时间复杂度。

由于前缀和是有序的,因此我们可以采用二分搜索。
寻找sum[j] - k >= sum[i]。
Arrays.binarySearch方法可以返回查找到的目录。
如果没有该值,方法会返回一个负数,其取反(x取反相当于[-(x+1)])的值就是应该插入的目录。
将ans设置为无限大,如果(index - i)小于最小值则更新到ans。

时间复杂度O(nlogn)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int[] sum = new int[nums.length+1];
for(int i = 1; i <= nums.length; i++){
sum[i] = sum[i-1] + nums[i-1];
}

int ans = Integer.MAX_VALUE;
for(int i = 0; i < sum.length; i++){
int search = sum[i] + target;
int index = Arrays.binarySearch(sum, search);
if(index < 0){
index = ~index;
}
if(index < sum.length){
ans = Math.min(ans, index - i);
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}
200. Number of Islands

200. Number of Islands

Question

Given an m x n 2D binary grid grid which represents a map of ‘1’s (land) and ‘0’s (water), return the number of islands.

An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Solution

遍历所有点,如果等于1则对其进行DFS搜索。
在搜索的同时将1设为0。
如果等于0则递归返回。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
char[][] area;
public int numIslands(char[][] grid) {
area = grid;
int count = 0;

for(int i = 0; i < area.length; i++){
for(int j = 0; j < area[0].length; j++){
if(area[i][j] == '1'){
dfs(i, j);
count++;
}
}
}
return count;
}

private void dfs(int i, int j){
if(area[i][j] == '0'){
return;
}
area[i][j] = '0';

if( i+1 < area.length ){
dfs(i+1, j);
}
if( i-1 >= 0 ){
dfs(i-1, j);
}
if( j+1 < area[0].length ){
dfs(i, j+1);
}
if( j-1 >= 0 ){
dfs(i, j-1);
}
}
}

Solution 2

同样是dfs搜索遍历每个位置,然而这个方法使用visited[][]数组来记录点是否被访问过。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
int[][] visited;
char[][] map;
int[][] operations;
public int numIslands(char[][] grid) {
visited = new int[grid.length][grid[0].length];
map = grid;
int count = 0;
operations = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(visited[i][j] == 1 || map[i][j] == '0') continue;
dfs(i, j);
count++;
}
}
return count;
}

private void dfs(int x, int y){
if(x < 0 || x >= map.length || y < 0 || y >= map[0].length ) return;
if(visited[x][y] == 1 || map[x][y] == '0') return;
visited[x][y] = 1;
for(int[] operation : operations){
int m = x + operation[0];
int n = y + operation[1];
dfs(m, n);
}

}
}