LeetCode Contest 70

最近在刷LeetCode,觉得很多题目都很有意思,特别想记下来,然后就回到了CSDN。看着以前写的博客真的很有意思,我打算在这里好好混,等以后有个人网站再转移阵地。那么就以记录一次比赛来开坑吧!

比赛回顾

前天的比赛是LeetCode的第70次比赛,是我参加的第二次比赛(好吧算上鸽了一次应该是第三次比赛)。这次比赛一共四道题,三道Medium,一道Hard。刚看到我就有点懵,竟然没有Easy的水题,不过实际做下来就还好,在比赛时间一个半小时内做了两道题,然后又过了差不多一个小时把剩下两道题做出来了。第一题比较简单,算是签到吧;第二题比较复杂,比赛的时候越想越乱,卡了好久,最后选择先放弃;第三题当时很快就过了,不过后来想想应该没考虑完全,可能是测例比较弱所以过了,我现在都能想到用一种测例卡掉;第四题是Hard,但看起来真的不难,只是最后不够时间了,比赛完了才写出来。下面再详细讲一下每道题的思路。

K-th Symbol in Grammar

问题

第一行是0,然后每一次将001取代,110取代得到下一行,规律如下:

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

问题

定义一种由RLX组成的字符串,其中RX可以替换成XRXL可以替换成LX,求初始字符串能够通过替换变成目标字符串。

思路

比赛时我的想法是R可以右移,L可以左移,因此只要保证目标字符串从左往右R的数量不少于初始字符串,L的数量不多于初始字符串,并且各种字符的数量相等即可。后来我发现这样并不完全正确,如果测例是RLLR,那么程序会得到true,但实际上应该是false,因此还要保证将所有LR的位置一致。

代码

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,但我觉得这道题的难度仅次于第一题。

扫描二维码关注公众号,回复: 1679351 查看本文章

代码

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,希望以后比赛能够做得更好

猜你喜欢

转载自blog.csdn.net/fast_g/article/details/79273784