[11] Container With Most Water [Medium]
O(n^2)的暴力解法直接TLE。
正确的解法是Two Pointers。 O(n)的复杂度。保持两个指针i,j;分别指向长度数组的首尾。如果ai 小于aj,则移动i向后(i++)。反之,移动j向前(j--)。如果当前的area大于了所记录的area,替换之。这个想法的基础是,如果i的长度小于j,无论如何移动j,短板在i,不可能找到比当前记录的area更大的值了,只能通过移动i来找到新的可能的更大面积。

1 class Solution { 2 public: 3 int maxArea(vector<int>& height) { 4 int i = 0; 5 int j = height.size() - 1; 6 int ans = 0; 7 while(i < j) { 8 int area = (j - i) * min(height[i], height[j]); 9 ans = max(ans, area); 10 if (height[i] <= height[j]) { 11 ++i; 12 } 13 else { 14 --j; 15 } 16 } 17 return ans; 18 } 19 };
[15] 3Sum [Medium]
它要求返回结果不重复,所以去重。不能用unique,会TLE,直接在循环里面跳过。
先排序,两根指针,夹逼定理

1 class Solution { 2 public: 3 vector<vector<int>> threeSum(vector<int>& nums) { 4 vector<vector<int>> result; 5 if (nums.size() < 3) { 6 return result; 7 } 8 sort(nums.begin(), nums.end()); 9 for (int i = 0; i < nums.size(); ) { 10 int j = i + 1; 11 int k = nums.size() - 1; 12 while (j < k) { 13 if (nums[i] + nums[j] + nums[k] == 0) { 14 result.push_back(vector<int>{nums[i], nums[j], nums[k]}); 15 ++j, --k; 16 while (j < k && nums[j-1] == nums[j]) { ++j; } 17 while (j < k && nums[k+1] == nums[k]) { --k; } 18 } 19 else if (nums[i] + nums[j] + nums[k] < 0) { 20 ++j; 21 while (j < k && nums[j-1] == nums[j]) { ++j; } 22 } 23 else { 24 --k; 25 while (j < k && nums[k+1] == nums[k]) { --k; } 26 } 27 } 28 ++i; 29 while (nums[i-1] == nums[i]) { ++i; } 30 } 31 return result; 32 } 33 };
[16] 3Sum Closest [Medium]
三个数求和,返回离target最近的和。
Two Pointers, 跟15题一样, 先排序,两根指针左右夹逼。

1 class Solution { 2 public: 3 int threeSumClosest(vector<int>& nums, int target) { 4 int result = 0; 5 int min_gap = INT_MAX; 6 sort(nums.begin(), nums.end()); 7 for (int i = 0; i < nums.size() - 2; ++i) { 8 int start = i + 1, end = nums.size() - 1; 9 while (start < end) { 10 int temp = nums[i] + nums[start] + nums[end]; 11 int gap = abs(temp - target); 12 if (gap < min_gap) { 13 min_gap = gap; 14 result = temp; 15 } 16 if (temp == target) break; 17 if (temp > target) { 18 --end; 19 } 20 else if (temp < target) { 21 ++start; 22 } 23 } 24 } 25 return result; 26 } 27 };
[17] 4Sum [Medium]
四个数求和,求出和target相等的元组, 思路和3sum一个样,包括和去重的方式都一样。
还是先排序,然后两根指针。

1 class Solution { 2 public: 3 vector<vector<int>> fourSum(vector<int>& nums, int target) { 4 vector<vector<int>> result; 5 if (nums.size() < 4) { 6 return result; 7 } 8 sort(nums.begin(), nums.end()); 9 for (int i = 0; i < nums.size()-3; ) { 10 for(int j = i+1; j < nums.size()-2; ) { 11 int start = j + 1, end = nums.size() - 1; 12 while (start < end) { 13 int sum = nums[i] + nums[j] + nums[start] + nums[end]; 14 if (sum == target) { 15 result.push_back(vector<int>{nums[i], nums[j], nums[start], nums[end]}); 16 ++start, --end; 17 while (start < end && nums[start-1] == nums[start]) { 18 ++start; 19 } 20 while (start < end && nums[end+1] == nums[end]) { 21 --end; 22 } 23 } 24 else if (sum < target) { 25 ++start; 26 while (start < end && nums[start-1] == nums[start]) { 27 ++start; 28 } 29 } 30 else { 31 --end; 32 while (start < end && nums[end+1] == nums[end]) { 33 --end; 34 } 35 } 36 } 37 ++j; 38 while (nums[j-1] == nums[j]) { ++j; } 39 } 40 ++i; 41 while(nums[i-1] == nums[i]) { ++i; } 42 } 43 return result; 44 } 45 };
[45] Jump Game II [Hard]
这个题需要记忆。再考肯定不会。。。要记忆。。。
给你一个数组,数组元素的值是从当前index最多能往前面移动的步数,求到数组末尾最少移动的步数。
我是参考了这个:https://www.tianmaying.com/tutorial/LC45

1 /* 2 将每个位置都看作一个点,并从第i个点向它之后的nums[i]个点都连一条长度为1的有向边,而现在的问题就是从0号点到达size-1号点需要的最短距离,这就是一个很简单的最短路问题,实际上由于边的长度均为1,而且不存在环,我们可以用宽度优先搜索(时间复杂度为O(n^2),即边数)来进行相关的计算。 3 4 不难发现,这道题目转换出的最短路问题存在三个条件: 5 6 边的长度均为1 7 不存在环 8 连出的边是连续的 9 我们是不是可以用这三个“很强”的条件来做一些优化呢,答案自然是肯定的! 10 11 ——如果令f[i]表示从0号点到达i号点的最短路径,那么对于任意i<j,有f[i]<=f[j],即f是非递减的,这个结论的证明是显然的,在此不作过多赘述。 12 13 在有了这样的结论之后,我们就会发现,其实对于f数组来说,它会是一段段的存在,先是一个0,然后是一段1,然后是一段2,依此类推,那么现在问题来了,每一段的长度是多少呢? 14 15 这个问题很好回答,如果我们令l[k]表示f数组中值为k的一段的左边界,r[k]表示f数组中值为k的一段的有边界,那么有 16 17 l[k] = r[k - 1] + 1,这是显然的 18 r[k] = max{i + nums[i] | l[k - 1] <= i <= r[k - 1]},由于f值为k的位置一定是从f值为k-1的位置走来的,所以只需要看从所有f值为k-1的位置里最远可以到达的地方即可。 19 也就是说,我们可以在对nums的一遍扫描中,依次求出所有的l[k]和r[k],而f数组也就自然求解出来了——答案也就得到了。 20 */

1 class Solution { 2 public: 3 int jump(vector<int>& nums) { 4 int k = 0, l = 0, r = 0; 5 while (r < nums.size()-1) { 6 int next_r = r; 7 for (int i = l; i <= r; ++i) next_r = max(next_r, nums[i]+i); 8 ++k; l = r + 1; r = next_r; 9 } 10 return k; 11 } 12 };
[48] Rotate Image [Medium]
题目要求一个方阵顺时针旋转90°。
经过研究发现顺时针旋转90°,相当于先转置,再每行逆序。要学会矩阵转置的写法。

1 class Solution { 2 public: 3 void rotate(vector<vector<int>>& matrix) { 4 const int N = matrix.size(); 5 //trans 6 for (int i = 0; i < N; ++i) { 7 for (int j = i+1; j < N; ++j) { 8 swap(matrix[i][j], matrix[j][i]); 9 } 10 } 11 12 //reverse 13 for (int i = 0; i < N; ++i) { 14 int start = 0, end = N - 1; 15 while (start < end) { 16 swap(matrix[i][start++], matrix[i][end--]); 17 } 18 } 19 } 20 };
[54] Spiral Matrix [Medium]
打印旋转矩阵。 设置有用的四个变量 beginx, beginy, endx, endy。然后用一个i去遍历。

1 class Solution { 2 public: 3 vector<int> spiralOrder(vector<vector<int>>& matrix) { 4 vector<int> ans; 5 if (matrix.size() == 0) { 6 return ans; 7 } 8 const int m = matrix.size(); 9 const int n = matrix[0].size(); 10 int beginx = 0, beginy = 0; 11 int endx = n-1, endy = m - 1; 12 while (true) { 13 for (int i = beginx; i <= endx; ++i) { 14 ans.push_back(matrix[beginy][i]); 15 } 16 ++beginy; 17 if (beginy > endy) break; 18 19 for (int i = beginy; i <= endy; ++i) { 20 ans.push_back(matrix[i][endx]); 21 } 22 --endx; 23 if (beginx > endx) break; 24 25 for (int i = endx; i >= beginx; --i) { 26 ans.push_back(matrix[endy][i]); 27 } 28 --endy; 29 if (beginy > endy) break; 30 31 for (int i = endy; i >= beginy; --i) { 32 ans.push_back(matrix[i][beginx]); 33 } 34 ++beginx; 35 if (beginx > endx) break; 36 } 37 return ans; 38 } 39 };
[56] Merge Intervals [Hard]
合并线段(数轴) 参考57题。 在 Insert Interval的基础上,一个新的interval集合,然后每次从旧的里面取一个interval出来,然后插入到新的集合中。

1 /** 2 * Definition for an interval. 3 * struct Interval { 4 * int start; 5 * int end; 6 * Interval() : start(0), end(0) {} 7 * Interval(int s, int e) : start(s), end(e) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<Interval> merge(vector<Interval>& intervals) { 13 vector<Interval> ans; 14 if (intervals.size() == 0) { 15 return ans; 16 } 17 ans.push_back(intervals[0]); 18 for (int i = 1; i < intervals.size(); ++i) { 19 insert_interval(ans, intervals[i]); 20 } 21 return ans; 22 } 23 vector<Interval> insert_interval(vector<Interval>& intervals, Interval newInterval) { 24 vector<Interval>::iterator it = intervals.begin(); 25 while (it != intervals.end()) { 26 if (it->start > newInterval.end) { 27 break; 28 } 29 else if (it->end < newInterval.start) { 30 it++; 31 continue; 32 } 33 else { 34 newInterval.start = min(newInterval.start, it->start); 35 newInterval.end = max(newInterval.end, it->end); 36 intervals.erase(it); 37 } 38 } 39 intervals.insert(it, newInterval); 40 return intervals; 41 } 42 };
[57] Insert Interval [Hard]
先判断是否能放在最前面,然后在现有的intervals中一个一个遍历。有覆盖就更新newInterval,然后把原本的那段删除。

1 /** 2 * Definition for an interval. 3 * struct Interval { 4 * int start; 5 * int end; 6 * Interval() : start(0), end(0) {} 7 * Interval(int s, int e) : start(s), end(e) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<Interval> insert(vector<Interval>& intervals, Interval newInterval) { 13 vector<Interval>::iterator it = intervals.begin(); 14 while (it != intervals.end()) { 15 if (it->start > newInterval.end) { 16 break; 17 } 18 else if (it->end < newInterval.start) { 19 ++it; 20 continue; 21 } 22 else { 23 newInterval.start = min(it->start, newInterval.start); 24 newInterval.end = max(it->end, newInterval.end); 25 intervals.erase(it); 26 } 27 } 28 intervals.insert(it, newInterval); 29 return intervals; 30 } 31 };
[59] Spiral Matrix II [Medium]
跟前一题Spiral Matrix的区别是前一题是遍历, 这个题是造一个矩阵出来。 思路写法都一样。

1 class Solution { 2 public: 3 vector<vector<int>> generateMatrix(int n) { 4 vector<vector<int>> matrix(n, vector<int>(n, 0)); 5 if (n == 0) { 6 return matrix; 7 } 8 int number = 1; 9 int beginx = 0, beginy = 0, endx = n - 1, endy = n - 1; 10 while (true) { 11 for (int i = beginx; i <= endx; ++i) { 12 matrix[beginy][i] = number++; 13 } 14 ++beginy; 15 if (beginy > endy) { break; } 16 17 for (int i = beginy; i <= endy; ++i) { 18 matrix[i][endx] = number++; 19 } 20 --endx; 21 if (beginx > endx) { break; } 22 23 for (int i = endx; i >= beginx; --i) { 24 matrix[endy][i] = number++; 25 } 26 --endy; 27 if (beginy > endy) { break; } 28 29 for (int i = endy; i >= beginy; --i) { 30 matrix[i][beginx] = number++; 31 } 32 ++beginx; 33 if (beginx > endx) { break; } 34 } 35 return matrix; 36 } 37 };
[73] Set Matrix Zeroes [Medium]
如果矩阵中一个元素为0, 那么把该元素所在的行和列都置零。O(1)的空间复杂度
思路:有个简单的O(m+n)的空间复杂度的,就是设两个布尔数组,来控制行和列是否为0.
但是如果要降低到O(1)的话,就可以牺牲第0行和第0列来存这两个数组。提前用两个变量保存好第0行,第0列是否置0.

1 class Solution { 2 public: 3 void setZeroes(vector<vector<int>>& matrix) { 4 const int m = matrix.size(); 5 if (m == 0) return; 6 const int n = matrix[0].size(); 7 bool row_has_zero = false; 8 bool col_has_zero = false; 9 for (size_t i = 0; i < m; ++i) { 10 if (matrix[i][0] == 0) { 11 col_has_zero = true; 12 break; 13 } 14 } 15 for (size_t j = 0; j < n; ++j) { 16 if (matrix[0][j] == 0) { 17 row_has_zero = true; 18 break; 19 } 20 } 21 22 for (size_t i = 1; i < m; ++i) { 23 for (size_t j = 1; j < n; ++j) { 24 if (matrix[i][j] == 0) { 25 matrix[i][0] = 0; 26 matrix[0][j] = 0; 27 } 28 } 29 } 30 31 for (size_t i = 1; i < m; ++i) { 32 for (size_t j = 1; j < n; ++j) { 33 if (matrix[i][0] == 0 || matrix[0][j] == 0) { 34 matrix[i][j] = 0; 35 } 36 } 37 } 38 39 if (col_has_zero == true) { 40 for (size_t i = 0; i < m; ++i) { 41 matrix[i][0] = 0; 42 } 43 } 44 45 if (row_has_zero == true) { 46 for (size_t j = 0; j < n; ++j) { 47 matrix[0][j] = 0; 48 } 49 } 50 return; 51 } 52 };
[118] Pascal's Triangle [Easy]

1 class Solution { 2 public: 3 vector<vector<int>> generate(int numRows) { 4 vector<vector<int>> ans; 5 if (numRows == 0) return ans; 6 for (size_t i = 0; i < numRows; ++i) { 7 vector<int> ans_row(i+1, 1); 8 if (i == 0 || i == 1) { 9 ans.push_back(ans_row); 10 continue; 11 } 12 for (size_t j = 1; j < i ; ++j) { 13 ans_row[j] = ans[i-1][j-1] + ans[i-1][j]; 14 } 15 ans.push_back(ans_row); 16 } 17 return ans; 18 } 19 };
[119] Pascal's Triangle II [Easy]
只要求O(k)的额外空间。我自己写的解法用了两个数组,其实一个就够了。

1 class Solution { 2 public: 3 vector<int> getRow(int rowIndex) { 4 vector<int> ans(rowIndex+1, 1); 5 vector<int> help(rowIndex+1, 0); 6 if (rowIndex == 0 || rowIndex == 1) { 7 return ans; 8 } 9 for (size_t i = 2; i < rowIndex+1; ++i) { 10 help = ans; 11 for (size_t j = 1; j < i; ++j) { 12 ans[j] = help[j-1] + help[j]; 13 } 14 ans[i] = 1; 15 } 16 return ans; 17 } 18 };
一个数组的还没看懂(好困,睡了==)

1 class Solution { 2 public: 3 vector<int> getRow(int rowIndex) { 4 vector<int> A(rowIndex+1, 0); 5 A[0] = 1; 6 for(int i=1; i<rowIndex+1; i++) 7 for(int j=i; j>=1; j--) 8 A[j] += A[j-1]; 9 return A; 10 } 11 };