summary2

164 阅读14分钟

新开一个summary
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

动态规划:之前想到是动态规划了,但是没想清楚状态转移方程。 dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1

image.png

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

这道题的重点是找重合的长度,可以先计算两条线右端点的最小值 - 左端点的最大值,如果小于零,说明不重合 想出来了 但是想的稍微有点慢

class Solution {
    public int getOverlappingLength(int a1, int a2, int b1, int b2) {
        int right = Math.min(a2, b2);
        int left = Math.max(a1, b1);
        if(left >= right) {
            return 0;
        } else {
            return right - left;
        }
    }
    public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
        int row = getOverlappingLength(ax1, ax2, bx1, bx2);
        int col = getOverlappingLength(ay1, ay2, by1, by2);
        
        return (ax2 - ax1) * (ay2 - ay1) + (bx2 - bx1) * (by2 - by1) - row * col;
    }
}

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]

【笔记】摩尔投票法。该算法用于1/2情况,它说:“在任何数组中,出现次数大于该数组长度一半的值只能有一个。”

那么,改进一下用于1/3。可以着么说:“在任何数组中,出现次数大于该数组长度1/3的值最多只有两个。

1/2的场景的使用方法就是每次消除两个不相同的数,剩下的数就是【数量大于一半的数】 1/3的场景的使用方法相同,就是每次消除三个不同的数,剩下的数就是我们想要的

class Solution {
public List<Integer> majorityElement(int\[] nums) {
int element1 = 0;
int vote1 = 0;
int element2 = 0;
int vote2 = 0;

        for(int num: nums) {
            if(vote1 > 0 && element1 == num) {
                vote1++;
            } else if(vote2 > 0 && element2 == num) {
                vote2++;
            } else if(vote1 == 0) {
                element1 = num;
                vote1++;
            } else if(vote2 == 0) {
                element2 = num;
                vote2++;
            } else {
                vote1--;
                vote2--;
            }
        }

        int cnt1 = 0;
        int cnt2 = 0;
        for(int num: nums) {
            if(vote1 > 0 && element1 == num) {
                cnt1++;
            }

            if(vote2 > 0 && element2 == num) {
                cnt2++;
            }
        }

        List<Integer> res = new ArrayList<>();
        if(vote1 > 0 && cnt1 > nums.length/3) {
            res.add(element1);
        }
        if(vote2 > 0 && cnt2 > nums.length/3) {
            res.add(element2);
        }
        return res;
    }

}

237. Delete Node in a Linked List

There is a singly-linked list head and we want to delete a node node in it.

You are given the node to be deleted node. You will not be given access to the first node of head.

All the values of the linked list are unique, and it is guaranteed that the given node node is not the last node in the linked list.

Delete the given node. Note that by deleting the node, we do not mean removing it from memory. We mean:

  • The value of the given node should not exist in the linked list.
  • The number of nodes in the linked list should decrease by one.
  • All the values before node should be in the same order.
  • All the values after node should be in the same order.

Custom testing:

  • For the input, you should provide the entire linked list head and the node to be given nodenode should not be the last node of the list and should be an actual node in the list.
  • We will build the linked list and pass the node to your function.
  • The output will be the entire list after calling your function.

 

Example 1:

Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
Explanation: You are given the second node with value 5, the linked list should become 4 -> 1 -> 9 after calling your function.

Example 2:

Input: head = [4,5,1,9], node = 1
Output: [4,5,9]
Explanation: You are given the third node with value 1, the linked list should become 4 -> 5 -> 9 after calling your function.

最开始还在想没head怎么做,后来想可以一个一个往前移。但是还有更简单的方法: 将自己完全变成另一个人,再杀了那个人就行了。

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

241. Different Ways to Add Parentheses

Given a string expression of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. You may return the answer in any order.

The test cases are generated such that the output values fit in a 32-bit integer and the number of different results does not exceed 104.

 

Example 1:

Input: expression = "2-1-1"
Output: [0,2]
Explanation:
((2-1)-1) = 0 
(2-(1-1)) = 2

Example 2:

Input: expression = "2*3-4*5"
Output: [-34,-14,-10,-10,10]
Explanation:
(2*(3-(4*5))) = -34 
((2*3)-(4*5)) = -14 
((2*(3-4))*5) = -10 
(2*((3-4)*5)) = -10 
(((2*3)-4)*5) = 10

重要的是要找到分隔符 比如说 /-/

我们可以递归运算符的左边 dfs(l,i-1) 拿到左边所有的结果,递归运算符右边 dfs(i+1,r) 拿到右边的所有结果,结合「乘法原理」即可知道以当前运算符 s[i] 为分割点的表达式的所有方案。

class Solution {
    char[] ch;
    public List<Integer> dfs(int l, int r) {
        List<Integer> res = new ArrayList<>();
        for(int i = l; i <= r; i++) {
            if(ch[i] >= '0' && ch[i] <= '9') {
                continue;
            }
            List<Integer> left = dfs(l, i-1);
            List<Integer> right = dfs(i+1, r);
            for(int a: left) {
                for(int b: right) {
                    if(ch[i] == '+') {
                        res.add(a + b);
                    } else if(ch[i] == '-') {
                        res.add(a - b);
                    } else {
                        res.add(a * b);
                    }
                }
            }
        }

        if(res.size() == 0) {
            int curr = 0;
            for(int i = l; i <= r; i++) {
                curr = curr * 10 + ch[i] - '0';
            }
            res.add(curr);
        }
        return res;
    }
    public List<Integer> diffWaysToCompute(String expression) {
        ch = expression.toCharArray();
        return dfs(0, ch.length - 1);
    }
}

todo: 区间dp

287. Find the Duplicate Number

Given an array of integers nums containing n + 1 integers where each integer is in the range [1, n] inclusive.

There is only one repeated number in nums, return this repeated number.

You must solve the problem without modifying the array nums and uses only constant extra space.

 

Example 1:

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

Example 2:

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

很难想的题,Floyd 判圈算法,需要把数组判断成链表来做 把数组中每个元素看成个链表节点, val是指向下一节点(数组下标)的指针。 这样代码看起来就是快慢指针遍历链表了。

class Solution {
    public int findDuplicate(int[] nums) {
        int slow = nums[0];
        int fast = nums[nums[0]];

        while (slow != fast) {
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        slow = 0;
        while(fast != slow) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
}

289. Game of Life

According to Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970."

The board is made up of an m x n grid of cells, where each cell has an initial state: live (represented by a 1) or dead (represented by a 0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):

  1. Any live cell with fewer than two live neighbors dies as if caused by under-population.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by over-population.
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

The next state is created by applying the above rules simultaneously to every cell in the current state, where births and deaths occur simultaneously. Given the current state of the m x n grid board, return the next state.

 

Example 1:

Input: board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
Output: [[0,0,0],[1,0,1],[0,1,1],[0,1,0]]

Example 2:

Input: board = [[1,1],[1,0]]
Output: [[1,1],[1,1]]

这道题直接做就行,可以通过新建一个memory去存。但是空间复杂度比较高。更好的做法是通过新建状态来判断

class Solution {
    int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}};
    public void nextState(int[][] board, int i, int j) {
        int row = board.length;
        int col = board[0].length;
        int count = 0;
        for(int[] dir: dirs) {
            int r = i + dir[0];
            int c = j + dir[1];
            if(r < 0 || r >= row || c < 0 || c >= col) {
                continue;
            }
            if(board[r][c] == 1 || board[r][c] == 2) {
                count++;
            }
        }
        if(board[i][j] == 1) {
            if(count < 2) { // was live now dead
                board[i][j] = 2;
            }
            if(count > 3) {
                board[i][j] = 2;
            }
        } else {
            if(count == 3) { // was dead now live
                board[i][j] = -1;
            }
        }

    }
    public void gameOfLife(int[][] board) {
        int row = board.length;
        int col = board[0].length;
        

        for(int i = 0; i < row; i++) {
            for(int j = 0; j < col; j++) {
                nextState(board, i, j);
            }
        }

        for(int i = 0; i < row; i++) {
            for(int j = 0; j < col; j++) {
                if(board[i][j] == 2) {
                    board[i][j] = 0;
                }
                if(board[i][j] == -1) {
                    board[i][j] = 1;
                }

            }
        }
    }
}

284. Peeking Iterator

Design an iterator that supports the peek operation on an existing iterator in addition to the hasNext and the next operations.

Implement the PeekingIterator class:

  • PeekingIterator(Iterator<int> nums) Initializes the object with the given integer iterator iterator.
  • int next() Returns the next element in the array and moves the pointer to the next element.
  • boolean hasNext() Returns true if there are still elements in the array.
  • int peek() Returns the next element in the array without moving the pointer.

Note:  Each language may have a different implementation of the constructor and Iterator, but they all support the int next() and boolean hasNext() functions.

 

Example 1:

Input
["PeekingIterator", "next", "peek", "next", "next", "hasNext"]
[[[1, 2, 3]], [], [], [], [], []]
Output
[null, 1, 2, 2, 3, false]

Explanation
PeekingIterator peekingIterator = new PeekingIterator([1, 2, 3]); // [1,2,3]
peekingIterator.next();    // return 1, the pointer moves to the next element [1,2,3].
peekingIterator.peek();    // return 2, the pointer does not move [1,2,3].
peekingIterator.next();    // return 2, the pointer moves to the next element [1,2,3]
peekingIterator.next();    // return 3, the pointer moves to the next element [1,2,3]
peekingIterator.hasNext(); // return False

可以让操作提前一步进行 image.png

class PeekingIterator implements Iterator<Integer> {
  Iterator<Integer> iterator;
	Integer nextElement;
	public PeekingIterator(Iterator<Integer> iterator) {
	    // initialize any member here.
	    this.iterator = iterator;
			nextElement = iterator.next();
	}
	
    // Returns the next element in the iteration without advancing the iterator.
	public Integer peek() {
      return nextElement;
	}
	
	// hasNext() and next() should behave the same as in the Iterator interface.
	// Override them if needed.
	@Override
	public Integer next() {
			Integer ret = nextElement;
			nextElement = iterator.hasNext() ? iterator.next() : null;
	    return ret;
	}
	
	@Override
	public boolean hasNext() {
	    return nextElement != null;
	}
}

310. Minimum Height Trees

A tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.

Given a tree of n nodes labelled from 0 to n - 1, and an array of n - 1 edges where edges[i] = [ai, bi] indicates that there is an undirected edge between the two nodes ai and bi in the tree, you can choose any node of the tree as the root. When you select a node x as the root, the result tree has height h. Among all possible rooted trees, those with minimum height (i.e. min(h))  are called minimum height trees (MHTs).

Return a list of all MHTs'  root labels. You can return the answer in any order.

The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.

 

Example 1:

Input: n = 4, edges = [[1,0],[1,2],[1,3]]
Output: [1]
Explanation: As shown, the height of the tree is 1 when the root is the node with label 1 which is the only MHT.

Example 2:

Input: n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
Output: [3,4]

这道题使用的方法就是去掉所有入度为1的点,然后最后剩下的一个或两个点就是答案

根节点一定处在内部,目标根节点一定是最内部的点。

举一个简单的例子:外部点1---内部点---外部点2。很显然,只有取内部点作为根节点,才会出现弯折,高度才会最小。

因此我们需要像园丁一样不断修剪外部的点(即度数为1的点),并且更新相应节点的度(度数减1),把新的外部点暴露出来,作为下一轮要修剪的点,直到我们剩余的节点数量不超过两个为止

为什么是不超过两个呢?因为当前是不成环的。当剩余节点数量为1时,该点就是最中间的点;当剩余节点数量为2时,再修剪下去就没有内部点了,这两个点都是最中间的点;当剩余节点数量大于2时,必然可以继续修剪下去,暴露出更内部的点

class Solution {
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        if(n == 1) {
            List<Integer> res = new ArrayList<>();
            res.add(0);
            return res;
        }
        Map<Integer, List<Integer>> map = new HashMap<>();
        int[] indegree = new int[n];
        boolean[] memo = new boolean[n];
        Arrays.fill(memo, true);
        for(int i = 0; i < n; i++) {
            map.put(i, new ArrayList<>());
        }
        for(int[] edge: edges) {
            int a = edge[0];
            int b = edge[1];
            map.get(a).add(b);
            map.get(b).add(a);
            indegree[a]++;
            indegree[b]++;
        }
        int count = 0;
        ArrayDeque<Integer> queue = new ArrayDeque<Integer>();
        for(int i = 0; i < n; i++) {
            if (indegree[i] == 1) {
                queue.addLast(i);
            }
        }
        while(count < n-2) {
            int size = queue.size();
            for(int i = 0; i < size; i++) {
                int num = queue.poll();
                indegree[num] = 0;
                count++;
                for(Integer neighbour: map.get(num)) {
                    indegree[neighbour]--;
                    if(indegree[neighbour] == 1){
                        queue.addLast(neighbour);
                    }
                }
            }   
        }
        
        List<Integer> res = new ArrayList<>();
        while(!queue.isEmpty()) {
            res.add(queue.poll());
        }
        return res;
    }
}

324. Wiggle Sort II

Given an integer array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3]....

You may assume the input array always has a valid answer.

 

Example 1:

Input: nums = [1,5,1,1,6,4]
Output: [1,6,1,5,1,4]
Explanation: [1,4,1,5,1,6] is also accepted.

Example 2:

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

首先,我们可以很容易想到一种简单的解法:将数组进行排序,然后从中间位置进行等分(如果数组长度为奇数,则将中间的元素分到前面),然后将两个数组进行穿插。 之前就是这么想的,结果错了

例如,对于数组[1,1,2,2,3,3],分割为[1,1,2]和[2,3,3],虽然A和B都出现了2,但穿插后为[1,2,1,3,2,3],满足要求。 而如果2的个数再多一些,即[1,1,2,2,2,3],分割为[1,1,2]和[2,2,3],最终结果为[1,2,1,2,2,3],来自A的2和来自B的2出现在了相邻位置。

出现这一问题是因为重复数在A和B中的位置决定的,因为r在A尾部,B头部,所以如果r个数太多(大于等于(length(nums) + 1)/2),就可能在穿插后相邻。要解决这一问题,我们需要使A的r和B的r在穿插后尽可能分开。一种可行的办法是将A和B反序:

例如,对于数组[1,1,2,2,2,3],分割为[1,1,2]和[2,2,3],分别反序后得到[2, 1, 1]和[3, 2, 2],此时2在A头部,B尾部,穿插后就不会发生相邻了。

class Solution {
    public void wiggleSort(int[] nums) {
        int len = nums.length;
        int[] res = Arrays.copyOfRange(nums, 0, len);
        Arrays.sort(res);

        int l = len % 2 == 0 ? len / 2 - 1 : (len + 1) / 2 - 1;
        int r = len - 1;
        int i = 0;
        int flag = 0;
        while(i < len) {
            if(flag == 0) {
                nums[i] = res[l];
                l--;
            } else {
                nums[i] = res[r];
                r--;
            }
            flag = flag == 0 ? 1 : 0;
            i++;
        }
    }
}

378. Kth Smallest Element in a Sorted Matrix

Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest element in the matrix.

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

You must find a solution with a memory complexity better than O(n2).

 

Example 1:

Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
Output: 13
Explanation: The elements in the matrix are [1,5,9,10,11,12,13,13,15], and the 8th smallest number is 13

Example 2:

Input: matrix = [[-5]], k = 1
Output: -5

方法一是用priorityQueue,但是这个方法的问题是没有使用矩阵的性质,时间复杂度是n^2 * logk 方法二是用归并排序,把矩阵的每一行想象成是一个有序数组,然后这个问题就变成从n个有序数组中找第k大的数

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
            public int compare(int[] a, int[] b) {
                return a[0] - b[0];
            }
        });
        int n = matrix.length;
        for (int i = 0; i < n; i++) {
            pq.offer(new int[]{matrix[i][0], i, 0});
        }
        for (int i = 0; i < k - 1; i++) {
            int[] now = pq.poll();
            if (now[2] != n - 1) {
                pq.offer(new int[]{matrix[now[1]][now[2] + 1], now[1], now[2] + 1});
            }
        }
        return pq.poll()[0];
    }
}

方法三 二分法 通过二分查找去找,check方法的返回值是 所有小于mid的数是否小于k
问题是如何确定mid + 1一定在矩阵中: 之前考虑怎么保证算出值一定在矩阵里呢,我是这么想的,假设答案应该是m为第k个元素,s为第(k+1)个元素,那么使得矩阵中有k个比mid小于等于的元素,mid一定会在m与s之间,而算法中找到的是left,仔细想,其实就是第一个满足有k个比自身小于等于的元素,无疑m是第一个,所以找到的left一定是在矩阵中的

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length;
        int left = matrix[0][0];
        int right = matrix[n-1][n-1];
        while(left < right) {
            int mid = left + ((right - left) >> 1);
            if(check(matrix, mid, k, n)) {
                right = mid;
            } else {
                left = mid+1;
            }
        }
        return left;
    }

    public boolean check(int[][] matrix, int mid, int k, int n) {
        int i = n-1;
        int j = 0;
        int num = 0; // 拿到所有小于mid的数
        while(i >= 0 && j < n) {
            if(matrix[i][j] <= mid) {
                num += i+1;
                j++;
            } else {
                i--;
            }
        }

        return num >= k;
    }
}

386. Lexicographical Numbers

Given an integer n, return all the numbers in the range [1, n] sorted in lexicographical order.

You must write an algorithm that runs in O(n) time and uses O(1) extra space. 

 

Example 1:

Input: n = 13
Output: [1,10,11,12,13,2,3,4,5,6,7,8,9]

Example 2:

Input: n = 2
Output: [1,2]

很简单的一道题,硬是没想出来,思路想到了但是没实现也没用

class Solution {
    public List<Integer> lexicalOrder(int n) {
        List<Integer> res = new ArrayList<>();
        int number = 1;
        for(int i = 0; i < n; i++) {
            res.add(number);
            if(number * 10 <= n){
                number = number*10;
            } else {
                while(number % 10 == 9 || number + 1 > n) {
                    number = number/10;
                }
                number++;
            }
        }
        return res;
    }
}

390. Elimination Game

You have a list arr of all integers in the range [1, n] sorted in a strictly increasing order. Apply the following algorithm on arr:

  • Starting from left to right, remove the first number and every other number afterward until you reach the end of the list.
  • Repeat the previous step again, but this time from right to left, remove the rightmost number and every other number from the remaining numbers.
  • Keep repeating the steps again, alternating left to right and right to left, until a single number remains.

Given the integer n, return the last number that remains in arr.

 

Example 1:

Input: n = 9
Output: 6
Explanation:
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr = [2, 4, 6, 8]
arr = [2, 6]
arr = [6]

Example 2:

Input: n = 1
Output: 1

最开始没有思路 觉得应该硬算,但是其实是数学题

class Solution {
    public int lastRemaining(int n) {
        int remain = n;
        boolean flag = true;
        int res = 1;
        int step = 1;
        while(remain > 1) {
            // 当从左到右遍历 或者 从右到左遍历但是总数是奇数的时候+step
            if(flag || remain % 2 == 1) {
                res += step;
            }
            // flag指的是从左到右遍历 or 从右到左遍历
            flag = !flag;
            // remain指剩下多少数
            remain = remain / 2;
            // step指的是每次跨越多少,因为之前删除了元素,所以step每次都是*2
            step = step * 2;
        }
        return res;
    }
}

542. 01 Matrix

Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell.

The distance between two adjacent cells is 1.

 

Example 1:

Input: mat = [[0,0,0],[0,1,0],[0,0,0]]
Output: [[0,0,0],[0,1,0],[0,0,0]]

Example 2:

Input: mat = [[0,0,0],[0,1,0],[1,1,1]]
Output: [[0,0,0],[0,1,0],[1,2,1]]

最短距离bfs 离谱的是queue的操作竟然写错了 写成stack了,然后debug竟然de出来了。原因就是没理解bfs的原理和底层逻辑,就是我们先把所有的0加到queue里,然后把之后把所有相邻的cell也都加入到queue里,这样就能保证每一个cell到0的距离是最小的

class Solution {
    int[][] dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    public int[][] updateMatrix(int[][] mat) {
        int m = mat.length;
        int n = mat[0].length;
        Deque<int[]> queue = new ArrayDeque<>();
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(mat[i][j] == 0) {
                    queue.offer(new int[]{i, j});
                } else {
                    mat[i][j] = -1;
                }
            }
        }
        while(!queue.isEmpty()) {
            int[] curr = queue.poll();
            int i = curr[0];
            int j = curr[1];
            for(int[] dir: dirs) {
                int r = i + dir[0];
                int c = j + dir[1];
                if(r < 0 || r >= m || c < 0 || c >= n) {
                    continue;
                }
                if(mat[r][c] == -1) {
                    mat[r][c] = mat[i][j] + 1;
                    queue.offer(new int[]{r, c}); 
                } 
            }
        }
        return mat;
       
    }
}

419. Battleships in a Board

Given an m x n matrix board where each cell is a battleship 'X' or empty '.', return the number of the battleships on board.

Battleships can only be placed horizontally or vertically on board. In other words, they can only be made of the shape 1 x k (1 row, k columns) or k x 1 (k rows, 1 column), where k can be of any size. At least one horizontal or vertical cell separates between two battleships (i.e., there are no adjacent battleships).

Example 1:

Input: board = [["X",".",".","X"],[".",".",".","X"],[".",".",".","X"]]
Output: 2

Example 2:

Input: board = [["."]]
Output: 0

最开始没读完题就用了unionfind来做 但是效率比较低。之后改成用dfs然后做出来了,但是没有用到这道题的特性 只能是1k 或者 k1。所以直接m*n即可

class Solution {
    public int countBattleships(char[][] board) {
        int m = board.length;
        int n = board[0].length;
        int count = 0;
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(board[i][j] == 'X') {
                    if(i-1 >= 0 && board[i-1][j] == 'X') {
                        continue;
                    }
                    if(j-1 >= 0 && board[i][j-1] == 'X') {
                        continue;
                    }
                    count++;
                }
            }
        }
        return count;
    }
}

1209. Remove All Adjacent Duplicates in String II

You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters from s and removing them, causing the left and the right side of the deleted substring to concatenate together.

We repeatedly make k duplicate removals on s until we no longer can.

Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique.

 

Example 1:

Input: s = "abcd", k = 2
Output: "abcd"
Explanation: There's nothing to delete.

Example 2:

Input: s = "deeedbbcccbdaa", k = 3
Output: "aa"
Explanation: First delete "eee" and "ccc", get "ddbbbdaa"
Then delete "bbb", get "dddaa"
Finally delete "ddd", get "aa"

Example 3:

Input: s = "pbbcggttciiippooaais", k = 2
Output: "ps"

之前是用了一个稍微有一点复杂的方式去做了这道题,定义了一个map的数据结构,然后每一个key就是一个character,相应的value就是一个list,每个list里就是从前到后分别存了几个数

但是其实不用这么麻烦 直接定义两个stack就可以,一个stack表示所有的character,另一个stack代表count

class Solution {
    public String removeDuplicates(String s, int k) {
        Deque<Character> stack = new LinkedList<>();
        Deque<Integer> count = new LinkedList<>();
        for(Character ch: s.toCharArray()) {
            if(!count.isEmpty() && count.getFirst() == k-1 && stack.getFirst() == ch) {
                for(int i = 0; i < k-1; i++) {
                    stack.poll();
                }
                count.poll();
                continue;
            }

            else {
                if(!stack.isEmpty() && stack.getFirst() == ch) {
                    int num = count.poll();
                    count.push(num+1);
                } else {
                    count.push(1);
                }
            }
            stack.push(ch);
        }
        StringBuilder sb = new StringBuilder();
        while(!stack.isEmpty()) {
            sb.append(stack.poll());
        }
        sb.reverse();
        return sb.toString();
    }
}

2684. Maximum Number of Moves in a Grid

You are given a 0-indexed m x n matrix grid consisting of positive integers.

You can start at any cell in the first column of the matrix, and traverse the grid in the following way:

  • From a cell (row, col), you can move to any of the cells: (row - 1, col + 1)(row, col + 1) and (row + 1, col + 1) such that the value of the cell you move to, should be strictly bigger than the value of the current cell.

Return the maximum number of moves that you can perform.

 

Example 1:

Input: grid = [[2,4,3,5],[5,4,9,3],[3,4,2,11],[10,9,13,15]]
Output: 3
Explanation: We can start at the cell (0, 0) and make the following moves:
- (0, 0) -> (0, 1).
- (0, 1) -> (1, 2).
- (1, 2) -> (2, 3).
It can be shown that it is the maximum number of moves that can be made.

Example 2:


Input: grid = [[3,2,4],[2,1,9],[1,1,7]]
Output: 0
Explanation: Starting from any cell in the first column we cannot perform any moves.
  1. 本题可以用dfs + 记忆化存储来解决,时间复杂度是O(mn), 跟之前的一道hard题是一样的。同理bfs也是可以解决这道问题的
class Solution {
    int[][] dirs = {{-1, 1}, {0, 1}, {1, 1}};
    public int dfs(int[][] grid, int i, int j, int[][] cache) {
        int m = grid.length;
        int n = grid[0].length;

        int num = 0;
        for(int[] dir: dirs) {
            int r = i + dir[0];
            int c = j + dir[1];
            if(r < 0 || r >= m || c < 0 || c >= n) {
                continue;
            }
            if(cache[r][c] != -1) {
                num = Math.max(num, cache[r][c] + 1);
            } else if(grid[r][c] > grid[i][j]) {
                num = Math.max(num, dfs(grid, r, c, cache) + 1);
            }
        }
        cache[i][j] = num;
        return num;
    }
    public int maxMoves(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] cache = new int[m][n];
        
        for(int[] row: cache) {
            Arrays.fill(row, -1);
        }

        int res = 0;
        for(int i = 0; i < m; i++) {
            res = Math.max(res, dfs(grid, i, 0, cache));
        }
        return res;
    }
}
  1. 看评论说可以直接用dp做, 但是要先遍历column 然后再遍历row 因为题目中让从{{-1, 1}, {0, 1}, {1, 1}}这三个方向去选 可以确定的是当前列一定是依赖下一列的值的 所以要先遍历列,等下一列全遍历完再计算前一列
class Solution {
    int[][] dirs = {{-1, 1}, {0, 1}, {1, 1}};
    public int maxMoves(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        int res = 0;

        for(int j = n-2; j >= 0; j--) {
            for(int i = 0; i < m; i++) {
                for(int[] dir: dirs) {
                    int r = dir[0] + i;
                    int c = dir[1] + j;
                    if(r < 0 || r >= m || c < 0 || c >= n) {
                        continue;
                    }
                    if(grid[i][j] < grid[r][c]) {
                        dp[i][j] = Math.max(dp[i][j], dp[r][c] + 1);
                    }
                }
                if(j == 0) {
                    res = Math.max(res, dp[i][j]);
                }
            }
        }

        return res;
    }
}

1232. Check If It Is a Straight Line

You are given an array coordinatescoordinates[i] = [x, y], where [x, y] represents the coordinate of a point. Check if these points make a straight line in the XY plane.

 

 

Example 1:

Input: coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]
Output: true

Example 2:

Input: coordinates = [[1,1],[2,2],[3,4],[4,5],[5,6],[7,7]]
Output: false

这道题是求slope 但是最好不要用除法,因为除法的time complexity太大了。可以用乘法 AX + BY = 0

class Solution {
    public boolean checkStraightLine(int[][] coordinates) {
        int deltaX = coordinates[0][0];
        int deltaY = coordinates[0][1];

        for(int i = 1; i < coordinates.length; i++) {
            coordinates[i][1] -= deltaY;
            coordinates[i][0] -= deltaX;
        }

        int A = coordinates[1][1]; 
        int B = -coordinates[1][0];

        for(int i = 2; i < coordinates.length; i++) {
            int x = coordinates[i][0];
            int y = coordinates[i][1];
            if(A * x + B * y != 0) {
                return false;
            }
        } 
        return true;
    }
}

2405. Optimal Partition of String

Given a string s, partition the string into one or more substrings such that the characters in each substring are unique. That is, no letter appears in a single substring more than once.

Return the minimum number of substrings in such a partition.

Note that each character should belong to exactly one substring in a partition.

 

Example 1:

Input: s = "abacaba"
Output: 4
Explanation:
Two possible partitions are ("a","ba","cab","a") and ("ab","a","ca","ba").
It can be shown that 4 is the minimum number of substrings needed.

Example 2:

Input: s = "ssssss"
Output: 6
Explanation: The only valid partition is ("s","s","s","s","s","s").

贪心法,遇到重复的就重启一个map

class Solution {
    public int partitionString(String s) {
        int[] map = new int[26];
        int res = 0;
        for(int i = 0; i < s.length(); i++) {
            if(map[s.charAt(i) - 'a'] == 1) {
                res++;
                map = new int[26];
            }
            map[s.charAt(i) - 'a']++;
        }
        return res + 1;
    }
}

2735. Collecting Chocolates

You are given a 0-indexed integer array nums of size n representing the cost of collecting different chocolates. The cost of collecting the chocolate at the index i is nums[i]. Each chocolate is of a different type, and initially, the chocolate at the index i is of ith type.

In one operation, you can do the following with an incurred cost of x:

  • Simultaneously change the chocolate of ith type to ((i + 1) mod n)th type for all chocolates.

Return the minimum cost to collect chocolates of all types, given that you can perform as many operations as you would like.

 

Example 1:

Input: nums = [20,1,15], x = 5
Output: 13
Explanation: Initially, the chocolate types are [0,1,2]. We will buy the 1st type of chocolate at a cost of 1.
Now, we will perform the operation at a cost of 5, and the types of chocolates will become [1,2,0]. We will buy the 2nd type of chocolate at a cost of 1.
Now, we will again perform the operation at a cost of 5, and the chocolate types will become [2,0,1]. We will buy the 0th type of chocolate at a cost of 1. 
Thus, the total cost will become (1 + 5 + 1 + 5 + 1) = 13. We can prove that this is optimal.

Example 2:

Input: nums = [1,2,3], x = 4
Output: 6
Explanation: We will collect all three types of chocolates at their own price without performing any operations. Therefore, the total cost is 1 + 2 + 3 = 6.

恶心的题 看了半天才看懂
方法只有一种:暴力枚举
没做出来 没遇到过这类的题 好懵。
就是定义一个array用来update转了之后的index
多做几遍熟悉一下

class Solution {
    public long minCost(int[] nums, int x) {
        int n = nums.length;
        long res = 0;
        // 用来update index的
        int[] cho = new int[n];
        for(int i = 0; i < n; i++) { 
            res += nums[i];
            cho[i] = i;
        }

        long current = res;
        for(int i = 1; i < n; i++) { // i代表转了的几次
            for(int j = 0; j < n; j++) { // j代表从0开始遍历
                if(nums[cho[j]] > nums[(j + i) % n]) { 
                    // 如果当前的值大于转了之后的值,那么current就减少多少
                    current -= nums[cho[j]] - nums[(j + i) % n];
                    // 然后更新当前index,因为这样才能保证我们拿到的都是最小值
                    cho[j] = (j + i) % n;
                }
            }
            current += x; // 再加上offset
            res = Math.min(res, current);
        }
        return res;
    }
}