最近在刷LeetCode,觉得很多题目都很有意思,特别想记下来,然后就回到了CSDN。看着以前写的博客真的很有意思,我打算在这里好好混,等以后有个人网站再转移阵地。那么就以记录一次比赛来开坑吧!
比赛回顾
前天的比赛是LeetCode的第70次比赛,是我参加的第二次比赛(好吧算上鸽了一次应该是第三次比赛)。这次比赛一共四道题,三道Medium,一道Hard。刚看到我就有点懵,竟然没有Easy的水题,不过实际做下来就还好,在比赛时间一个半小时内做了两道题,然后又过了差不多一个小时把剩下两道题做出来了。第一题比较简单,算是签到吧;第二题比较复杂,比赛的时候越想越乱,卡了好久,最后选择先放弃;第三题当时很快就过了,不过后来想想应该没考虑完全,可能是测例比较弱所以过了,我现在都能想到用一种测例卡掉;第四题是Hard,但看起来真的不难,只是最后不够时间了,比赛完了才写出来。下面再详细讲一下每道题的思路。
K-th Symbol in Grammar
问题
第一行是0
,然后每一次将0
用01
取代,1
用10
取代得到下一行,规律如下:
row 1: 0
row 2: 01
row 3: 0110
row 4: 01101001
然后求第N
行第K
个数字
思路
观察发现每一行同一个位置的数字是一样的,而K
是有效位置,所以可以看作是求数列01101001……
的第K项。假设第i
项已知,则可以求得第2*i
项和第2*i+1
项,那么我们可以用下面的方法求第j
项,如果j = 1
,那么第j
项为0
;如果j
为偶数,那么第j
项与第j/2
项相反,即两者之和为1
;如果j
为奇数,那么第j
项等于第(j+1)/2
项。由于整数除法结果向下整取,无论j
是奇数还是偶数,都可以通过第(j+1)/2
项求得第j
项。
代码
class Solution {
public:
int kthGrammar(int N, int K) {
int ans = 0; //结果初始化为0
while (K > 1) {
if (K % 2 == 0) ans = 1-ans; //每一次若K为偶数,则将结果取反
K = (K+1) / 2; //更新K
}
return ans;
}
};
Split BST
问题
将给出的二叉搜索树分成两个子树,一个子树的节点的值均小于或等于给定的值,另一个子树的节点的值均大于给定的值,并且符合以下要求:若任意子节点C
和它的父节点P
在同一个子树中,那么节点C
的父节点仍为P
。
思路
刚看到这题我就感觉可以做,只要将连接大于给定值节点和小于等于给定值节点的路全部去掉,然后再重新构建二叉树就可以了。不过我在处理细节的时候思路就乱了,所以没能做出来。首先设当前节点,上界节点和下界节点,其中当前节点进行搜索给定值的迭代,上界节点的值为已搜索的值中大于给定值的最小值,下界节点的值为已搜索的值中小于等于给定值的最大值。上界节点的左子节点必然为NULL
,下界节点的右子节点必然为NULL
,若当前节点的值小于等于给定值,则将当前节点作为下界节点的右子节点并将当前节点作为新的下界节点,若当前节点的值大于给定值,则将当前节点作为上界节点的左子节点并将当前节点作为新的上界节点。然后是确定两个子树的根节点,两个子树的根节点分别为第一个上界节点和第一个下界节点,其中必然有一个节点是二叉搜索树的根节点。
代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<TreeNode*> splitBST(TreeNode* root, int V) {
if (root == NULL) return {NULL, NULL};
TreeNode* current = root; //当前节点
TreeNode* parent = NULL; //当前节点的父节点
TreeNode* upper = NULL; //上界节点
TreeNode* lower = NULL; //下界节点
TreeNode* subTree = NULL; //除了二叉搜索树的根节点以外的另一个子树节点
while (current != NULL) {
if (subTree == NULL && upper != NULL && lower != NULL) subTree = parent; //记录子树节点
if (current->val > V) {
if (upper != NULL) upper->left = current; //上界节点的左子节点为当前节点
parent = upper = current; //记录父节点并更新上界节点
current = current->left; //搜索迭代
} else {
if (lower != NULL) lower->right = current; //下界节点的右子节点为当前节点
parent = lower = current; //记录父节点并更新下界节点
current = current->right; //搜索迭代
}
}
if (subTree == NULL && upper != NULL && lower != NULL) subTree = parent; //记录子树节点
if (lower != NULL) lower->right = NULL; //下界节点的右子节点为NULL
if (upper != NULL) upper->left = NULL; //上界节点的左子节点为NULL
return {root, subTree};
}
};
Swap Adjacent in LR String
问题
定义一种由R
、L
和X
组成的字符串,其中RX
可以替换成XR
,XL
可以替换成LX
,求初始字符串能够通过替换变成目标字符串。
思路
比赛时我的想法是R
可以右移,L
可以左移,因此只要保证目标字符串从左往右R
的数量不少于初始字符串,L
的数量不多于初始字符串,并且各种字符的数量相等即可。后来我发现这样并不完全正确,如果测例是RL
和LR
,那么程序会得到true
,但实际上应该是false
,因此还要保证将所有L
和R
的位置一致。
代码
class Solution {
public:
bool canTransform(string start, string end) {
int s = start.size();
if (s != end.size()) return false;
if (s == 0) return true;
int crs = 0; //count R in start
int cre = 0; //count R in end
int cls = 0; //count L in start
int cle = 0; //count L in end
vector<int> cs;
vector<int> ce;
for (int i = 0; i < s; ++i) {
if (start[i] == 'R') ++crs; //total R in start from 0 to i
if (end[i] == 'R') ++cre; //total R in end from 0 to i
if (crs < cre) return false;
if (start[i] == 'L') ++cls; //total L in start from 0 to i
if (end[i] == 'L') ++cle; //total L in end from 0 to i
if (cls > cle) return false;
if (start[i] != 'X') cs.push_back(start[i]);
if (end[i] != 'X') ce.push_back(end[i]);
}
if (crs != cre) return false;
if (cls != cle) return false;
for (int i = 0; i < cs.size(); ++i) {
if (cs[i] != ce[i]) return false;
}
return true;
}
};
Swim in Rising Water
问题
在一个N*N
的矩阵中,寻找一条路径从grid[0][0]
到grid[N-1][N-1]
使得路径中的最大值最小。
思路
看到这道题我就想到了SPFA算法,同样是找一条路径,最短路是路径长度最小,这道题是路径中最大值最小。首先假设起点到其它点的距离为无穷大,将起点加入队列待处理,然后每次取出队列中第一个点,考虑经过该点到达相邻的点,那么路径中的最大值为到达该点的路径中的最大值和将要到达的相邻点的值两者之中的较大值,若这个较大值比先前确定将要到达的点的路径中的最大值小,那么将其更新为这个较大值,并将那个点加入队列待处理,最后直到队列为空,得到从起点到达每个点的路径中的最大值的最小值。虽然这道题是Hard,但我觉得这道题的难度仅次于第一题。
代码
class Solution {
public:
int swimInWater(vector<vector<int>>& grid) {
int rows = grid.size();
int cols = grid[0].size();
vector<vector<int>> m(rows, vector<int>(cols, INT_MAX));
queue<pair<int, int> > q;
m[0][0] = grid[0][0];
q.push({0, 0});
while (!q.empty()) {
int i = q.front().first;
int j = q.front().second;
int v = m[i][j];
q.pop();
//up point
if (i > 0 && max(grid[i-1][j], v) < m[i-1][j]) {
m[i-1][j] = max(grid[i-1][j], v);
q.push({i-1, j});
}
//left point
if (j > 0 && max(grid[i][j-1], v) < m[i][j-1]) {
m[i][j-1] = max(grid[i][j-1], v);
q.push({i, j-1});
}
//down point
if (i < rows-1 && max(grid[i+1][j], v) < m[i+1][j]) {
m[i+1][j] = max(grid[i+1][j], v);
q.push({i+1, j});
}
//right point
if (j < cols-1 && max(grid[i][j+1], v) < m[i][j+1]) {
m[i][j+1] = max(grid[i][j+1], v);
q.push({i, j+1});
}
}
return m[rows-1][cols-1];
}
};
总结
这次比赛的难度不大,但因为时间不够还是没能全部AC,希望以后比赛能够做得更好