本周和好友“女声男”同时参加weekly contest。有同学共同竞争还是挺有压力的。因为我练习算法题已经有半年时间了,他还是新手,如若最后还败北了,岂不丢人。不过结果还好,没有丢自己的人。我在离比赛结束还有10min时全部AC,而且所有题目都是一遍过,略胜一筹。不得不说,这次的题比往届简单不少,之前我的水平一直维持在只做出2道题目,ranking 800左右,而本次ranking 为 356 / 3870。从排名上看有所进步。
下面我分享一下四道题目的思路。
977. Squares of a Sorted Array
给定一个数组,返回各个元素的平方数组,该数组是排好序的。直接brute force即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Solution { public: vector<int> sortedSquares(vector<int>& A) { vector<int> square; for (auto a: A) { square.push_back(a * a); } sort(square.begin(), square.end()); return square; } };
|
时间复杂度:O(n log n),因为有个排序。
空间复杂度:O(n),因为要返回一个新的数组,除此之外,不实用额外空间。当然,你也可以使用传进来的数组所占的空间,更trick,但没有多大意义。
看过Solution后,惊奇地发现竟然还有种O(n)的解法。仔细一看,原来是自己没有注意到数组A本身是排好序的。问题可以转化为合并2个排好序的数组。
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
| class Solution { public: vector<int> sortedSquares(vector<int>& A) { vector<int> square; int j = 0; for (; j < A.size() && A[j] < 0; j++) ; int i = j - 1; while (i >= 0 && j < A.size()) { if (A[i]*A[i] < A[j]*A[j]) { square.push_back(A[i]*A[i]); i--; } else { square.push_back(A[j]*A[j]); j++; } }
while (i >= 0) { square.push_back(A[i]*A[i]); i--; } while (j < A.size()) { square.push_back(A[j]*A[j]); j++; } return square; } };
|
978. Longest Turbulent Subarray
给定一个数组,返回“升降子数组”的最大长度。所谓“升降子数组”,即 相邻2个元素对大小变化的符号相反,比如[1, 2, 1, 2, 1]。画在坐标图上就是波浪状。
思路:遍历一遍数组,如果新的元素对变化符号与前一个元素对变化符号相反,则 当前升降子数组长度 += 1
; 否则,重置当前升降子数组长度
为 2。遍历的同时根据 当前升降子数组长度
更新 最长升降子数组长度
。
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
| class Solution { public: int maxTurbulenceSize(vector<int>& A) { int max_length = 1; int last_state = 0; int current_length = 2; for (int i = 0; i < A.size() - 1; i++) { int state = 0; if (A[i] > A[i + 1]) { state = 1; } else if (A[i] < A[i + 1]) { state = -1; } else { state = 0; } if (state == -last_state && state != 0) { current_length++; } else { current_length = 2; } max_length = max(max_length, current_length); last_state = state; } return max_length; } };
|
时间复杂度: O(n),只需要遍历数组一遍;
空间复杂度: O(1), 只使用常数的额外空间。
979. Distribute Coins in Binary Tree
Intution: 遇到树的题目,最先先到的就是递归,dfs。无脑写dfs最难的地方在于确定参数和返回值,也就是明确子树与root之间的关系。在本题中,子树与root之间的关系就是,存在硬币的转移,从子树到root,或是从root到子树。所以只需要一个返回值,表示这一转移即可。
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
|
class Solution { private: int move = 0; int recurse(TreeNode* root) { if (!root) return 0; int surplus = 0; int left = recurse(root->left); int right = recurse(root->right); surplus = left + right + root->val - 1; move += abs(surplus); return surplus; } public: int distributeCoins(TreeNode* root) { recurse(root); return move; } };
|
时间复杂度: O(n), n为node的数目。因为每个node都要没调用一次。
空间复杂度: 平均 O(log n), 即 树的深度。
980. Unique Paths III
Intution: 寻找路径数目,最直接的brute force是直接dfs进行backtrack。由于题目规模比较小,1 <= grid.length * grid[0].length <= 20
,所以还是可以AC的。激动人心的是在比赛结束前10min,AC掉这最后一道题目。
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| class Solution { private: int path = 0; void dfs(vector<vector<int>>& grid, vector<vector<bool>> &visit, int i, int j) { if (grid[i][j] == 2) { int true_all = true; for (int xi = 0; xi < grid.size(); xi++) { for (int xj = 0; xj < grid[xi].size(); xj++) { if (grid[xi][xj] == 0 && visit[xi][xj] == false) { true_all = false; goto end_of_outer_loop; } } } end_of_outer_loop: if (true_all) { path++; } return; } visit[i][j] = true; if (i > 0) { int new_i = i - 1; int new_j = j; if (visit[new_i][new_j] == false && (grid[new_i][new_j] == 0 || grid[new_i][new_j] == 2)) dfs(grid, visit, new_i, new_j); } if (j > 0) { int new_i = i; int new_j = j - 1; if (visit[new_i][new_j] == false && (grid[new_i][new_j] == 0 || grid[new_i][new_j] == 2)) dfs(grid, visit, new_i, new_j); } if (i < grid.size() - 1) { int new_i = i + 1; int new_j = j; if (visit[new_i][new_j] == false && (grid[new_i][new_j] == 0 || grid[new_i][new_j] == 2)) dfs(grid, visit, new_i, new_j); } if (j < grid[0].size() - 1) { int new_i = i; int new_j = j + 1; if (visit[new_i][new_j] == false && (grid[new_i][new_j] == 0 || grid[new_i][new_j] == 2)) dfs(grid, visit, new_i, new_j); } visit[i][j] = false; } public: int uniquePathsIII(vector<vector<int>>& grid) { vector<vector<bool>> visit(grid.size(), vector<bool> (grid[0].size(), false)); int i, j; for (i = 0; i < grid.size(); i++) { for (j = 0; j < grid[0].size(); j++) { if (grid[i][j] == 1) { goto find_begin_square; } } } find_begin_square: dfs(grid, visit, i, j); return path; } };
|
时间复杂度: O(4^n),n为网格数,因为每次要向4个方向走,而且回溯必然是指数级的。
空间复杂度: O(n),dfs的最深递归调用层数。