2021 05 13
The 5 leetcodes sorted out this week are 53-maximum subsequence sum, 78-subset, 77-combination, 46-full permutation and 200-island number respectively.
53 maximum suborder sum
Topic describes
Given an integer array nums, find a contiguous subarray with the maximum sum (the subarray contains at least one element) and return the maximum sum.
- Example 1:
Input: nums = [-- 2, 1, 3, 4, 1, 2, 1, 5, 4] output: 6: continuous subarray and maximum of [4, 1, 2, 1], is 6.Copy the code
- Example 2:
Input: nums = [1] Output: 1Copy the code
- Example 3:
Input: nums = [0] Output: 0Copy the code
- Example 4:
Input: nums = [-1] Output: -1Copy the code
- Example 5:
Input: nums = [-100000] Output: -100000Copy the code
- Tip:
1 <= nums.length <= 3 * 104
-105 <= nums[i] <= 105
Copy the code
Source: LeetCode link: leetcode-cn.com/problems/ma… Copyright belongs to the Collar buckle network. Commercial reprint please contact official authorization, non-commercial reprint please indicate the source.
Solution: the violent method
The brute force method is to take the sum of each successive subarray and take the maximum. Use two Pointers I and j to represent the beginning and end of a continuous subarray. Move j back one bit at a time, summing. If j reaches the end, move I back one bit, continuing to compute the sum of all continuous subarrays starting with the following character.
public int maxSubArray(int[] nums){
int result = Integer.MIN_VALUE;
int sum = 0;
for(int i = 0; i < nums.length; ++i){
// The summation is restarted each time the first element of the subarray changes
sum = 0;
for(int j = i; j < nums.length;++j){
sum += nums[j];
result = Math.max(result,sum);
}
}
return result;
}
Copy the code
Solution two dynamic programming
Assuming p[I] represents the sum of the largest continuous subsequence ending in nums[I], then for p[I +1], either take nums[I +1] itself or add to p[I], i.e., p[I +1] = Max (nums[I +1],nums[I +1] + p[I]).
Note that p[I] can only calculate the maximum sum of subsequences ending in nums[I]. There are a total of nums.length.
// Dynamic programming
public int maxSubArray(int[] nums) {
int result = nums[0];
int sum = nums[0];
for(int i = 1; i < nums.length; ++i){
sum = Math.max(sum + nums[i],nums[i]);
result = Math.max(result,sum);
}
return result;
}
Copy the code
Solution: divide and conquer
For a given array, if you split the array from mid into left and right, then there are three possible cases of the maximum suborder sum, and you only need to return the larger values of those three cases.
- Case 1: The largest suborder sum is in the left half
For example, the largest suborder sum of [1,4,-1,2,-2] is on the left, [1,4].
- Case 2: The largest suborder sum is in the right half
For example, the largest suborder sum of [2,-2,-1,4,1] is on the right, [4,1].
- Case 3: Maximum suborder sum spans left and right
For example, the maximum suborder sum of [-2,2,4,1,-1] across the left and right sides is [2,4,1].
Int maxSubArray(int[] nums,int left,int right); The termination condition for recursion is left == right.
In the first two cases, you can make direct recursive calls to get the maximum sum of suborders on the left and right sides. For the third case, which does not follow the rules of recursion, we need to do additional calculations, so let’s focus on the third case.
In the third case, the maximum continuous suborder and the sum of the following two parts:
- The left to
mid
The sum of the largest continuous suborder at the end - The right to
mid + 1
The sum of the largest continuous suborders at the beginning
For example, [-2,2,4,1,-3,4], the maximum sum of suborders ending in mid on the left is 6; The maximum sum of suborders on the right starting with mid+1 is 2. So the maximum sum across the left and right is 8.
Note that the third case is not the sum of the largest continuous suborders on the left and right sides. This is because the sum of the largest continuous suborder on one side is not necessarily contiguous to the sum of the largest continuous suborder on the other side. For example, in the figure above, the largest continuous suborder on the right is actually [4], but it is not adjacent to [2,4] on the left.
The code implementation is as follows:
/ / the main method
public int maxSubArray(int[] nums){
return maxSubArray(nums,0,nums.length - 1);
}
public int maxSubArray(int[] nums,int left,int right){
// Recursive termination condition
if(left == right){
return nums[left];
}
int mid = left + (right - left)/2;
int leftMax = maxSubArray(nums,left,mid);
int rightMax = maxSubArray(nums,mid + 1,right);
int midMax = midMax(nums,left,mid,right);
return max(leftMax,rightMax,midMax);
}
// Solve the third case
private int midMax(int[] nums,int left,int mid,int right){
int leftMax = nums[mid];
int rightMax = nums[mid + 1];
int sum = nums[mid];
int i = 0;
// start from mid-1 and walk forward
for(i = mid - 1; i >= left; --i){
sum += nums[i];
leftMax = leftMax > sum ? leftMax : sum;
}
sum = nums[mid+1];
// start with mid + 2
for(i = mid + 2; i <= right; ++i){
sum += nums[i];
rightMax = rightMax > sum ? rightMax : sum;
}
return leftMax + rightMax;
}
private int max(int a,int b,int c){
return a > b ? (a > c ? a : c) : (b > c ? b : c);
}
Copy the code
78 a subset
Topic describes
You are given an array of integers, nums, whose elements are different from each other. Returns all possible subsets (power sets) of this array.
The solution set cannot contain duplicate subsets. You can return the solution set in any order.
- Example 1:
Output: input: nums = [1, 2, 3], [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]Copy the code
- Example 2:
Nums = [0] Output: [[],[0]]Copy the code
- Tip:
1 <= nums.length <= 10-10 <= nums[I] <= 10 All elements in nums are differentCopy the code
Source: LeetCode link: leetcode-cn.com/problems/su… Copyright belongs to the Collar buckle network. Commercial reprint please contact official authorization, non-commercial reprint please indicate the source.
Method a DFS
DFS (depth-first search) and backtracking both make use of recursion ideas. We first draw a recursion tree using nums=[1,2,3] as an example.
Since repeating sets are not counted (for example, [1,2] and [2,1] are repeated), we can traverse each time from the next element of the current element, which is why the recursion tree is incomplete due to pruning.
DFS starts at the root of the recursion tree, follows a path to the bottom and back up. So DFS traversal of a subset of the order is [], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3].
DFS recursive function is as follows, where:
results
Represents the final result setstart
Indicates where in the array the current path starts traversalsubset
Represents a subset of the current path representation
public void dfs(int[] nums,List<List<Integer>> results,int start,List<Integer> subset){
// Copy a subset to avoid passing references
results.add(new ArrayList<>(subset));
// Recursion termination condition, pruning condition
if(start == nums.length){
return;
}
for(int i = start; i < nums.length; ++i){
// Continue along the current path until you reach a node and join the subset
subset.add(nums[i]);
dfs(nums,results,i+1,subset);
// After returning to the previous layer, the subset elements added to the current layer need to be removed
subset.remove(subset.size() - 1); }}Copy the code
Call a recursive function and return the result:
public List<List<Integer>> subsets(int[] nums){
List<List<Integer>> results = new ArrayList<>();
dfs(nums,results,0.new ArrayList<Integer>());
return results;
}
Copy the code
Solution two: backtracking
Backtracking traverses the length of the subset, which can be 1 to nums.length except for empty subsets. If, in traversal recursion, the length of the subset is found to be equal to the length of the current traversal, trace back up one level.
Backtracking traversal produce a subset of the order for the [], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3].
The design of the backtracking recursive function is as follows:
len
Represents the length of the current subsetstart
Indicates the element at which to start traversing and the next time backtracestart + 1
No duplicate sets are guaranteed
// backtrace recursive function
public void backtracking(int[] nums,List<List<Integer>> results,int len,int start,List<Integer> subset){
// Recursive termination condition
if(len == subset.size()){
results.add(new ArrayList<>(subset));
return ;
}
for(int i = start; i < nums.length; ++i){
subset.add(nums[i]);
backtracking(nums,results,len,i+1,subset);
subset.remove(subset.size() - 1); }}Copy the code
Recursive traceback calls to the possible length:
public List<List<Integer>> subsets(int[] nums){
List<List<Integer>> results = new ArrayList<>();
results.add(new ArrayList<>());
for(int i = 1; i <= nums.length; ++i){
backtracking(nums,results,i,0.new ArrayList<Integer>());
}
return results;
}
Copy the code
Solution three extension method
The idea of the extension method is to iterate over a set of numbers, adding the current element to each of the previous subsets to form a new subset, and then merging the previous subset and the new subset into the final subset set.
For example, nums=[1,2,3], the initial subset is [].
- To traverse the
1
, add the existing subset, get[1]
, and the previous subset, which is[], [1]
- To traverse the
2
, add the existing subset, get[2], [1, 2]
, and the previous subset, which is[], [1], [2], [1, 2]
- To traverse the
3
, add the existing subset, get[3], [1, 3], [2, 3], [1, 2, 3]
, and the previous combination, the subset is[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]
The extension method is implemented as follows:
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
results.add(new ArrayList<>());
for(int i = 0; i < nums.length; ++i){
// Copy the existing subset set
List<List<Integer>> copied = new ArrayList<>();
for(List<Integer> list : results){
ArrayList<Integer> temp = new ArrayList<>(list);
copied.add(temp);
}
// Add the current elements to results
for(List<Integer> list : copied){ list.add(nums[i]); results.add(list); }}return results;
}
Copy the code
77 combination
Topic describes
Given two integers n and k, return 1… All possible combinations of k numbers in n.
- Example:
Input: n = 4, k = 2 output: [[2, 4], [3, 4], [2, 3], [1, 2], [1, 3], [1, 4],]Copy the code
Source: LeetCode link: leetcode-cn.com/problems/co… Copyright belongs to the Collar buckle network. Commercial reprint please contact official authorization, non-commercial reprint please indicate the source.
Solution method – retrospective method
The idea and implementation logic of the backtracking method are similar. With the experience of question 78 above, we can also draw the recursion tree of this problem:
The green nodes are recursive trees with n=4 and k=2. The recursive function Combine is defined as follows, and the termination condition of recursion is that the size of the combination is equal to k.
// backtrace recursive function
void combine(List<List<Integer>> results,int n,int k,int start,List<Integer> comb){
if(comb.size() == k){
// results.add(comb) causes references to be passed and is not desirable
results.add(new ArrayList<>(comb));
return;
}
for(inti = start; i <= n; i++){ comb.add(i); combine(results,n,k,i+1,comb);
// Going back to the previous level, the last element of the combination needs to be removed
comb.remove(comb.size() - 1); }}/ / the main function
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> results = new ArrayList();
combine(results,n,k,1.new ArrayList<>());
return results;
}
Copy the code
When encountering recursion problems, in addition to recursion trees, I usually draw simple block diagrams like the following to deepen my understanding of the recursion process. The black box represents the invocation of recursive functions, and the statements are executed from top to bottom.
46 permutations
Topic describes
Given an array nums that does not contain duplicate digits, return all possible permutations. You can return the answers in any order.
- Example 1:
Output: input: nums = [1, 2, 3], [[1, 2, 3], [31] 1,,1,3 [2], [2,3,1], [3,1,2], [3, 2, 1]]Copy the code
- Example 2:
Input: nums = [0,1] output: [[0,1],[1,0]Copy the code
- Example 3:
Input: nums = [1] Output: [[1]Copy the code
Source: LeetCode link: leetcode-cn.com/problems/pe… Copyright belongs to the Collar buckle network. Commercial reprint please contact official authorization, non-commercial reprint please indicate the source.
Solution method – retrospective method
This problem can also be solved using backtracking. For nums=[1,2,3], draw the recursion tree:
Each path in a recursion tree forms a permutation, and each element appears in the permutation, but in a different order. So when you find a path with duplicate elements, you prune it.
The recursive function is simpler than the previous ones. The recursive function terminates when the size of the current array is equal to the length of the array, i.e. all elements are in the array.
// backtrace recursive function
public void permute(List<List<Integer>> results,int[] nums, List<Integer> ans){
if(ans.size() == nums.length){
results.add(new ArrayList<>(ans));
return;
}
for(int i = 0; i < nums.length; i++){
// Prune the path if it already exists in the permutation
if(! ans.contains(nums[i])){ ans.add(nums[i]); permute(results,nums,ans); ans.remove(ans.size() -1); }}}Copy the code
Call a recursive function to solve:
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
permute(results,nums,new ArrayList<>());
return results;
}
Copy the code
Number of 200 islands
Topic describes
Given a two-dimensional grid of ‘1’ (land) and ‘0’ (water), count the number of islands in the grid.
Islands are always surrounded by water, and each island can only be formed by connecting adjacent lands horizontally and/or vertically. Furthermore, you can assume that all four sides of the grid are surrounded by water.
- Example 1:
Input: the grid = [[" 1 ", "1", "1", "1", "0"], [" 1 ", "1", "0", "1", "0"], [" 1 ", "1", "0" and "0" and "0"], [" 0 "and" 0 ", "0" and "0", "0"]] output: 1Copy the code
- Example 2:
Input: the grid = [[" 1 ", "1", "0" and "0" and "0"], [" 1 ", "1", "0" and "0" and "0"], [" 0 "and" 0 ", "1", "0" and "0"], [" 0 "and" 0 ", "0", "1", "1"]] output: 3Copy the code
- Tip:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i] [j] with a value of '0' or '1'Copy the code
Source: LeetCode link: leetcode-cn.com/problems/nu… Copyright belongs to the Collar buckle network. Commercial reprint please contact official authorization, non-commercial reprint please indicate the source.
Method a BFS
An island is either surrounded by a boundary or a ‘0’. If you walk through the array and find land, it becomes ‘0’, and if there’s land around it, it becomes ‘0’, so when there’s no land around, you find an entire island, and since it’s all turned into water, it doesn’t conflict with the next island. Until the end of the iteration.
The process of turning the land around the land into an island until there is no land around it can use BFS breadth first search, which is usually used with queues.
If a ‘1’ is encountered while traversing a two-dimensional array, do the following:
- Number of islands plus 1
- Enqueue current element (element’s row and column number)
- Sets the current element to
'0'
That is, it becomes water - The queue is traversed until it is empty
- Take out the first element of the queue, if there is land around (up, down, left, right), add the surrounding elements to the queue and change to
'0'
.
- Take out the first element of the queue, if there is land around (up, down, left, right), add the surrounding elements to the queue and change to
The implementation code is as follows:
// Method 1 BFS idea
public int numIslands(char[][] grid) {
Queue<Pos> queue = new LinkedList<>();
int result = 0;
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if('1' == grid[i][j]){
result++;
queue.offer(new Pos(i,j));
grid[i][j] = '0';
while(! queue.isEmpty()){ Pos pos = queue.poll();if(pos.x - 1> =0 && grid[pos.x - 1][pos.y] == '1'){
queue.add(new Pos(pos.x - 1, pos.y));
grid[pos.x - 1][pos.y] = '0';
}
if(pos.x + 1 < grid.length && grid[pos.x + 1][pos.y] == '1'){
queue.add(new Pos(pos.x + 1, pos.y));
grid[pos.x + 1][pos.y] = '0';
}
if(pos.y - 1> =0 && grid[pos.x][pos.y - 1] = ='1'){
queue.add(new Pos(pos.x, pos.y - 1));
grid[pos.x][pos.y - 1] = '0';
}
if(pos.y + 1 < grid[0].length && grid[pos.x][pos.y + 1] = ='1'){
queue.add(new Pos(pos.x, pos.y + 1));
grid[pos.x][pos.y + 1] = '0';
}
}
}
}
}
return result;
}
Copy the code
When a land mass is encountered, it must be part of an island (so the number of islands is increased by 1), spreading out in all directions until it is surrounded by water.
Method two DFS
In the process of extension, you can also use the DFS idea, recursive calls. The recursive function DFS is to set the current element to ‘0’ and recursively expand the directions around it, terminating the recursion if it is surrounded by a boundary or water.
// DFS recursive function
void dfs(char[][] grid, int row, int col){
if(row < 0 || row > grid.length - 1 || col < 0 || col > grid[0].length - 1 || grid[row][col] == '0') {return;
}
grid[row][col] = '0';
dfs(grid,row - 1,col);
dfs(grid,row + 1,col);
dfs(grid,row,col - 1);
dfs(grid,row,col + 1);
}
Copy the code
Iterate over a two-dimensional array and call DFS when you encounter land.
// Method 2 DFS idea
public int numIslands(char[][] grid) {
int result = 0;
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if('1'==grid[i][j]){ result++; dfs(grid,i,j); }}}return result;
}
Copy the code
Solution of three union search set
This problem can also be solved by using and looking up UnionFind, which is more complicated, but it’s a good way to understand and look up the idea of UnionFind.
We can map the two-dimensional array of islands to a one-dimensional array root, whose length is Row *col (where row and col are the rows and rows of the two-dimensional array) for each element of the two-dimensional array.
Root [I] indicates the index of the ancestor of the ith element. If I ==root[I], then the ancestor of the ith element is root[I]. For example, root = [1,2,2], the ancestor of the 0th element is root[0] = 1, and the ancestor of the first element is root[1] = 2, and 2 == root[2], indicating that all three elements in the array have the same ancestor 2.
Returning to the question of islands, if some of the land elements share a common ancestor, then they can be considered an island.
Let’s define root in a look-up set class. The unionfind.class definition can be thought of as a template with a find(x) and union(x,y) method for query and merge operations, respectively.
// Look up the set template class
class UnionFind{
public UnionFind(char[][] grid){}
public int find(int x){}
public void union(int x, int y){}}Copy the code
In the island problem, and lookup sets need to define two members:
root[]
: array, maps a two-dimensional array, and records the ancestor of the elementcount
: integer. The default value is the number of elements in a two-dimensional arrayNumber of mergers
In the constructor, you initialize root, count, default root[I] = I, that is, you are your ancestor.
// Constructor of unionfind.class
public UnionFind(char[][] grid){
int eles = grid.length * grid[0].length;
count = eles;
root = new int[eles];
for(int i = 0; i < root.length; i++){ root[i] = i; }}Copy the code
Find (x) recursively finds the ancestor of the x-th element.
/** Recursively find the ancestor of X */
public int find(int x){
if(x == root[x]){
return x;
}
root[x] = find(root[x]);
return root[x];
}
Copy the code
The function of union(x,y) is to assimilate x and Y so that their ancestors are the same.
public void union(int x, int y){
int rootX = find(x);
int rootY = find(y);
if(rootX != rootY){
root[rootX] = rootY;
count--;
}
}
Copy the code
Unionfind.class should also provide a getCount() function to get the count. (Or you can make count public).
After looking at the code and looking up the set, the following is to use and looking up the set to solve the island number problem.
First define a variable waters to represent the number of 0, traversing the two-dimensional array:
- If it’s water,
waters++
- If it is land, the current element is carried out separately with the four elements up, down and left
union
operation - The final number of islands is
unionFind.count - waters
Why is the number of islands count-waters? We assume that the number of elements in the two-dimensional array is N, and there are waters elements. An island with n elements needs to merge n minus 1 times. That is, the number of final islands = n-waters-merge times, i.e., count-waters.
// select * from ()
public int numIslands(char[][] grid) {
int waters = 0;
UnionFind uf = new UnionFind(grid);
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(grid[i][j] == '0'){
waters++;
}else {
operateDir(uf,grid,i,j,-1.0);
operateDir(uf,grid,i,j,1.0);
operateDir(uf,grid,i,j,0, -1);
operateDir(uf,grid,i,j,0.1); }}}return uf.getCount() - waters;
}
// Perform assimilation merge on a direction
private void operateDir(UnionFind uf,char[][] grid,int i,int j,int xDir,int yDir){
int x = i + xDir;
int y = j + yDir;
if(x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == '1'){
uf.union(x*grid[0].length + y,i*grid[0].length+j); }}Copy the code