42. Trapping Rain Water

42. Trapping Rain Water

Question

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.

Solution

双指针,设置当前左侧的最大高度left和右侧的最大高度right。

分别从两侧遍历height[]数组,当出现更高的height时更新left和right。
否则记录left和right与height[i]的差值,并记录在数组waterLeft[]和waterRight[]中。

遍历两个数组,添加两者中的最小值到volume。

*由于单个参数只记录了一侧的最大值,因此最大值另一侧的水的体积会被多计算,因此分别从两侧遍历来获得最小值。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int trap(int[] height) {
int n = height.length, left = 0, right = 0, volume = 0;
int[] waterLeft = new int[n], waterRight = new int[n];

for(int i = 0; i < n; i++){
if(left <= height[i]) left = height[i];
else waterLeft[i] = left - height[i];
}

for(int i = n - 1; i >= 0; i--){
if(right <= height[i]) right = height[i];
else waterRight[i] = right - height[i];
}

for(int i = 0; i < n; i++){
volume += Math.min(waterLeft[i], waterRight[i]);
}

return volume;
}
}

1423. Maximum Points You Can Obtain from Cards

Question

There are several cards arranged in a row, and each card has an associated number of points. The points are given in the integer array cardPoints.

In one step, you can take one card from the beginning or from the end of the row. You have to take exactly k cards.

Your score is the sum of the points of the cards you have taken.

Given the integer array cardPoints and the integer k, return the maximum score you can obtain.

Solution 1

这种取两端的问题可以转化为取中间连续k个元素之和最小。

计算所有数字的和sum。
然后用维护一个宽度为k的窗口内的加和temp,并记录其最小值min。

返回sum - min,结果即是取两侧时可以取的最大值。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int maxScore(int[] cardPoints, int k) {
int n = cardPoints.length, sum = 0;
for(int point : cardPoints) sum += point; //求所有数值的和
if(k == n) return sum;
int left = 0, right = n-k, temp = 0, min = 0;

for(int i = 0; i < n-k; i++){
temp += cardPoints[i];
min = temp;
}
while(right < n){ //维护窗口内的temp,并更新min
temp += cardPoints[right] - cardPoints[left];;
min = Math.min(min, temp);
right++;
left++;
}
return sum - min;
}
}

Solution 2

分别计算从两侧开始的加和并记录在两个数组left[]和right[]上。

然后分别取两个指针,左侧指针l从0开始遍历到k,右侧指针对应的位置为l+n-k。计算并更新最大值max。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int maxScore(int[] cardPoints, int k) {
int n = cardPoints.length, max = 0;
int[] left = new int[n+1], right = new int[n+1];

for(int i = 0; i < n; i++){
left[i+1] = left[i] + cardPoints[i];
right[n-i-1] = right[n-i] + cardPoints[n-i-1];
}

for(int l = 0; l <= k; l++){
max = Math.max(max, left[l] + right[n-k+l]);
}

return max;
}
}
1679. Max Number of K-Sum Pairs

1679. Max Number of K-Sum Pairs

Question

You are given an integer array nums and an integer k.

In one operation, you can pick two numbers from the array whose sum equals k and remove them from the array.

Return the maximum number of operations you can perform on the array.

Solution

两种思路,第一种,哈希表统计总数,然后逐个排除。
每次从数组中取一个数,先查询寻找的target是否在哈希表内。
如果表内存在target,则将target的计数减一,对数加一。

如果target不在表内,则将当前的数字加入表内。
最后返回结果。时间复杂度为O(n)。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int maxOperations(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
int count = 0;
for(int num : nums){
int target = k - num;
if(map.containsKey(target) && map.get(target) > 0){
map.put(target, map.get(target)-1);
count++;
}
else{
map.put(num, map.getOrDefault(num, 0)+1);
}
}
return count;
}
}

Solution 2

第二种思路,先排序。
然后双指针放在首尾。
当两者的和等于目标值时增加计数器,并移动两个指针。
当两者的和大于目标值时,右侧指针左移。
当两者的和小于目标值时,左侧指针右移。
最后返回结果。由于有排序存在,因此时间复杂度为O(nlogn) + O(n)。

Code

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 maxOperations(int[] nums, int k) {
Arrays.sort(nums);
int left = 0, right = nums.length-1, count = 0;

while(left < right){
if(nums[left] + nums[right] == k){
count++;
left++;
right--;
}
else if(nums[left] + nums[right] > k){
right--;
}
else{
left++;
}
}
return count;
}
}
581. Shortest Unsorted Continuous Subarray

581. Shortest Unsorted Continuous Subarray

Question

Given an integer array nums, you need to find one continuous subarray that if you only sort this subarray in ascending order, then the whole array will be sorted in ascending order.

Return the shortest such subarray and output its length.

Solution

未排序子数组的左侧和右侧均为单调递增的区间。
我们的目标就是找到单调递增的边界。

中段由于是乱序的,因此其最大值与最小值必定不在中段的两端。
因此我们可以从左至右的遍历数组,不断地维护一个当前的最大值max。
由于只有中段是无序的,因此只有此时后面的数值才可能小于前面的最大值max
当这种情况发生时,则记录当前节点位置。
同理,我们也可以从右至左遍历,维护一个当前的最小值min。
只有在中段时,前面的数值才可能大于后面的最大值min。
当这种情况发生时,则记录当前节点位置。

最后如果两个指针的位置相同,则返回0。(此时数组完全有序,指针未移动。)
如果两个指针的位置不同,则返回两者的差+1。(即两者的宽度。)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int findUnsortedSubarray(int[] nums) {
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
int start = 0;
int end = 0;

for(int i = 0; i < nums.length; i++){
if(nums[i] < max) end = i;
else max = nums[i];

int j = nums.length - i - 1;
if(nums[j] > min) start = j;
else min = nums[j];
}
if(start == end) return 0;
return end - start + 1;
}
}

905. Sort Array By Parity

Given an integer array nums, move all the even integers at the beginning of the array followed by all the odd integers.

Return any array that satisfies this condition.

双指针,右侧指针之后存放奇数项。
当左侧指针的元素为奇数时,交换left和right上的元素。然后将右侧指针左移。
当左侧指针的元素为偶数时,左侧指针左移。

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
class Solution {
int[] arr;
public int[] sortArrayByParity(int[] nums) {
arr = nums;
int left = 0;
int right = nums.length-1;

while(left < right){
if(nums[left] % 2 == 1){
swap(left, right);
right--;
}
else{
left++;
}
}
return arr;
}

private void swap(int left, int right){
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
}

844. Backspace String Compare

Given two strings s and t, return true if they are equal when both are typed into empty text editors. '#' means a backspace character.

Note that after backspacing an empty text, the text will continue empty.

辅助方法getNextValid,返回下一个有效值。
将两个指针分别设置在两个字符串的尾部。
当两指针有一个大于0时,进行循环。
每次都搜索两个指针的下一个有效值。
如果两个指针上的字符不同则返回false。

最后返回两个指针的停留位置是否相同。

注意:
如果有一个字符串指针先更新到0以下,另一个指针仍有可能更新到一个“#”字符位置。
此时最后的结应该是两个空字符串。
因此需要继续循环一次,得出其是否会归到零以下。如果此时归零则两者的指针位置仍然相等。

因此即使getNextValid返回的下一个值为负数也应该保留其数值。

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 {
public boolean backspaceCompare(String s, String t) {
int i = s.length()-1;
int j = t.length()-1;

while(i >= 0 || j >= 0){
i = getNextValid(s, i);
j = getNextValid(t, j);
if( i >= 0 && j >= 0 && s.charAt(i) != t.charAt(j)){
return false;
}
i--;
j--;
}
return (i == j);
}

private int getNextValid(String s, int start){
int i = start;
int count = 0;
while(i >= 0 && (s.charAt(i) == '#' || count != 0)){
if(s.charAt(i) == '#'){
count++;
}
else{
count--;
}
i--;
}
return i;
}
}

15. 3Sum

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

Notice that the solution set must not contain duplicate triplets.

三数之和,重点是如何遍历后去除重复的数组。
首先对数组进行排序。
遍历数组,三个数中最小的数字为nums[i]。
此时需要去除重复的nums[i]。如重复则继续下一次循环。
此时设置双指针left和right,分别在nums[i]右侧的子数组的首尾。

  • 当nums[i] + nums[left] + nums[right] > 0时,后两个数的和需要减小,right指针向左移动。
  • 当nums[i] + nums[left] + nums[right] < 0时,后两个数的和需要增大,left指针向右移动。
  • 当nums[i] + nums[left] + nums[right] = 0时,找到一个组合。
    此时需要去除重复的nums[left]和nums[right]。如重复则更新left或right的指针。
  • 将组合添加到返回列表。

最后返回列表。

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
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList();
Arrays.sort(nums);

for(int i = 0; i < nums.length; i++){
int left = i+1;
int right = nums.length-1;
if(i>0 && nums[i] == nums[i-1]){
continue;
}

while(left < right){
if(nums[i]+nums[left]+nums[right]==0){
while(left+1 < nums.length && nums[left] == nums[left+1]){
left++;
}
while(right-1 > i && nums[right] == nums[right-1]){
right--;
}
List<Integer> list = new ArrayList();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
ans.add(list);

left++;
right--;
}
else if(nums[i]+nums[left]+nums[right]>0){
right--;
}
else{
left++;
}
}
}
return ans;
}
}

141. Linked List Cycle

问题
Given head, the head of a linked list, determine if the linked list has a cycle in it.

There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the node that tail’s next pointer is connected to. Note that pos is not passed as a parameter.

Return true if there is a cycle in the linked list. Otherwise, return false.

遍历并移动快慢指针。
如两个指针最终相遇,则链表中有循环。
如快指针移动到链表尾部,则链表无循环。

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 singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
if ( head == null){
return false;
}
else if ( fast.next == null){
return false;
}

while( fast != null && fast.next != null ){
slow = slow.next;
fast = fast.next.next;
if (slow == fast){
return true;
}
}
return false;
}
}

680. Valid Palindrome II

问题
Given a string s, return true if the s can be palindrome after deleting at most one character from it.

双指针,字符串两边对比。
如果两边字符不相等,则更新两边指针,并分别传入辅助方法再次对比。
两个结果有一个是true则返回true。

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
class Solution {
public boolean validPalindrome(String s) {
int left = 0;
int right = s.length() - 1;

while (left < right){
if (s.charAt(left) == s.charAt(right) ){
left++;
right--;
}
else{
return ( checkPalindrome(s, left+1, right) || checkPalindrome(s, left, right-1));
}
}
return true;
}
private boolean checkPalindrome(String s, int left, int right){
while (left < right){
if (s.charAt(left)==s.charAt(right)){
left++;
right--;
}
else{
return false;
}
}
return true;
}
}

19. Remove Nth Node From End of List

问题
Given the head of a linked list, remove the nth node from the end of the list and return its head.


双指针,同时记录前n个节点和当前节点。
当前指针到链表尾部时,删除前面的指针,注意处理edge cases。

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
/**
* 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 removeNthFromEnd(ListNode head, int n) {
ListNode preNode = null;
ListNode removedNode = head;
ListNode fastNode = head;
for ( int i = 0; i < n; i++ ){
fastNode = fastNode.next;
}

while ( fastNode != null ){
fastNode = fastNode.next;
preNode = removedNode;
removedNode = removedNode.next;
}

if ( removedNode == head ){
head = head.next;
}
else if ( removedNode.next == null){
preNode.next = null;
}
else{
preNode.next = removedNode.next;
}

return head;
}
}

876. Middle of the Linked List

问题
Given the head of a singly linked list, return the middle node of the linked list.

If there are two middle nodes, return the second middle node.

快慢指针,两个指针不同速度遍历链表。当快指针达到链表尾部时候,慢指针正好在中间。

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
/**
* 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 middleNode(ListNode head) {

ListNode slowNode = head;
ListNode fastNode = head;

while( fastNode != null ){
fastNode = fastNode.next;
if (fastNode == null){
return slowNode;
}
slowNode = slowNode.next;
fastNode = fastNode.next;
}
return slowNode;
}
}

74. Search a 2D Matrix

问题
Write an efficient algorithm that searches for a value target in an m x n integer matrix matrix. This matrix has the following properties:

  • Integers in each row are sorted from left to right.
  • The first integer of each row is greater than the last integer of the previous row.

二分搜索,以整个数组尺寸作为搜索范围。
每次搜索中间值,如等于target则返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int left = 0;
int right = (matrix.length * matrix[0].length) - 1;
while(left <= right){
int mid = left + (right - left)/2;
int row = mid / matrix[0].length;
int col = mid % matrix[0].length;
if(matrix[row][col] == target){
return true;
}
else if(matrix[row][col] > target){
right = mid - 1;
}
else{
left = mid + 1;
}
}
return false;
}
}

双指针,先搜索到合适的行。
再搜索到合适的列。

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
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int i = 0;
int j = 0;

while(i < matrix.length){
if(matrix[i][0] == target){
return true;
}
else if(matrix[i][0] < target){
i++;
}
else{
break;
}
}
if( i == 0 ){
return false;
}
i--;
while(j < matrix[0].length){
if(matrix[i][j] == target){
return true;
}
j++;
}
return false;
}
}

11. Container With Most Water

问题
You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]).
Find two lines that together with the x-axis form a container, such that the container contains the most water.
Return the maximum amount of water a container can store.
Notice that you may not slant the container.

双指针在首尾,二者容量取决于两者中较小的一个。
贪心算法,保留两个指针上较大的元素,移动较小一边的指针。
由于指针移动时距离只会减小,因此当新的元素比上一个更大时才有可能比之前的容量更大。
遍历一次找到最大容量。
时间复杂度:O(n)

感觉这个移动有点博弈论的味了,每次都移动自己最差的一边,虽然可能变得更差,但是总比不动(或者减小)强,动最差的部分可能找到更好的结果,但是动另一边总会更差或者不变,兄弟们,这不是题,这是人生,逃离舒适圈!!(这解释我觉得无敌了,哈哈哈)

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
class Solution {
public int maxArea(int[] height) {
int best = 0;

int i = 0;
int j = height.length - 1;

while ( i < j ){
int product = 0;
if (height[i] < height[j]){
product = height[i] * ( j -i );
i++;
}
else{
product = height[j] * ( j -i );
j--;
}
if ( product > best){
best = product;
}
}
return best;
}
}

344. Reverse String

问题简述
Write a function that reverses a string. The input string is given as an array of characters s.

You must do this by modifying the input array in-place with O(1) extra memory.

双指针,同时更新并交换两个数值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public void reverseString(char[] s) {
int i = 0;
int j = s.length - 1;

while( i < j ) {
char temp = s[i];
s[i] = s[j];
s[j] = temp;

i++;
j--;
}
}
}

167. Two Sum II - Input Array Is Sorted

问题描述
Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number. Let these two numbers be numbers[index1] and numbers[index2] where 1 <= index1 < index2 <= numbers.length.

Return the indices of the two numbers, index1 and index2, added by one as an integer array [index1, index2] of length 2.

The tests are generated such that there is exactly one solution. You may not use the same element twice.

Your solution must use only constant extra space.

由于是有序数列,因此可以采用双指针。
左右两侧和不等于目标时,根据大小结果移动左右指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public int[] twoSum(int[] numbers, int target) {
int i = 0;
int j = numbers.length - 1;
int[] ans = new int[2];

while( i < j ){
int diff = target - numbers[j];
if ( diff == numbers[i]){
ans[0] = i+1;
ans[1] = j+1;
return ans;
}
else if ( diff < numbers[i] ) {
j--;
}
else{
i++;
}
}
return ans;
}
}