LeetCode力扣周赛 270

周赛传送门


排名 203。最后一题属实陌生啦,只能想起知识点,完全想不起构造的方法啦。

5942. 找出 3 位偶数

思路:暴力枚举,哈希去重

时间复杂度 O ( n 3 ) \mathcal{O}(n^3) O(n3)

空间复杂度 O ( d 3 ) \mathcal{O}(d^3) O(d3) d d ddigits 的取值范围。

三层嵌套的 for 循环枚举组成数字的 d i g i t s i digits_i digitsi d i g i t s j digits_j digitsj d i g i t s k digits_k digitsk,枚举过程中需限制 i i i j j j k k k 互不相等,并可借助 unordered_map 对组合数字去重。

class Solution {
     
public:
    vector<int> findEvenNumbers(vector<int>& ds) {
     
        // 先对排序,保证构造出来的数字升序。
        sort(ds.begin(), ds.end());
        
        int n = ds.size();
        // mark,去重用的哈希表
        unordered_set<int> mark;
        // anw 保存答案
        vector<int> anw;
        
        // 开始枚举
        for (int i = 0; i < n; i++) {
     
            // 去除前导零
            if (ds[i] == 0) continue;
            for (int j = 0; j < n; j++) {
     
                if (i == j) continue;
                for (int k = 0; k < n; k++) {
     
                    // 判断偶数
                    if (k == i || k == j || ds[k]%2 == 1) continue;
                    int val = ds[i]*100 + ds[j]*10 + ds[k];
                    // 去重
                    if (mark.insert(val).second) {
     
                        anw.emplace_back(val);
                    }
                }
            }
        }
        return anw;
    }
};

5943. 删除链表的中间节点

思路:快慢指针,虚拟头节点

时间复杂度 O ( n ) O(n) O(n)

空间复杂度 O ( 1 ) O(1) O(1)

因为 head 也可能被删除,所以先定义一个 dummy 节点,其 next 指向 head

再定义两个指针:

  • ListNode *slow = &dummy;
  • ListNode *fast = head;

然后,slow 每次走一步,fast 每次走两步,这样当 fast 无法再走时,slow 恰巧指向待删除节点的前一个节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
     
public:
    ListNode* deleteMiddle(ListNode* head) {
     
        ListNode dummy(0, head);
        ListNode *fast = head, *slow = &dummy;
        while(fast != nullptr && fast->next != nullptr) {
     
            fast = fast->next->next;
            slow = slow->next;
        }
        // 删除中间节点
        slow->next = slow->next->next;
        return dummy.next;
    }
};

5944. 从二叉树一个节点到另一个节点每一步的方向

思路:深度优先遍历,删除公共前缀

时间复杂度 O ( n ) \mathcal{O}(n) O(n)

空间复杂度 O ( n ) \mathcal{O}(n) O(n)

首先,从根节点开始进行两次深度优先遍历,分别构造出:

  • rootstart 的路径,记为 r2s
  • rootdest 的路径,记为 r2d

由于 startdest 的最近公共祖先肯不是根节点,一次需找出 r2sr2d 的最长公共前缀并删除。

记删除后的路径分别为 r2s'r2d'。那么最短路径可描述为:从 start 出发,先走 r2s'.size()U,此时到达了 startdest 的最近公共祖先,然后再按 r2d' 行走即可到达 dest

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
     
public:
    // 找到 root -> goal 的路径,并存储在 path 中
    // 特别的,为了便于实现,将路径逆序存储在 path 中
    bool get(TreeNode *root, int goal, vector<char> &path) {
     
        if (root == nullptr) {
     
            return false;
        }
        if (root->val == goal) {
     
            return true;
        }
        if (get(root->left, goal, path)) {
     
            path.emplace_back('L');
            return true;
        } else if (get(root->right, goal, path)) {
     
            path.emplace_back('R');
            return true;
        }
        return false;
    }
    string getDirections(TreeNode* root, int startValue, int destValue) {
     
        vector<char> r2s;
        get(root, startValue, r2s);
        reverse(r2s.begin(), r2s.end());
        
        vector<char> r2d;
        get(root, destValue, r2d);
        reverse(r2d.begin(), r2d.end());
        
        // 找出公共前缀的长度
        int common = 0;
        for (; common < r2d.size() && common < r2s.size() && r2d[common] == r2s[common]; common++) {
     }
        
        // 构造答案
        return std::string(r2s.size()-common, 'U') + std::string(r2d.begin()+common, r2d.end());
    }
};

5932. 合法重新排列数对

思路:欧拉路

时间复杂度 O ( n ) \mathcal{O}(n) O(n)

空间复杂度 O ( n ) \mathcal{O}(n) O(n)

首先构图,将数字作为点,将数对作为边。那么问题转换为:找出一条路径,包含每条边一次且仅一次。这就是典型的欧拉路啦。不过太久没写了,比赛时死活没想起构造步骤,还是太弱了‍♂️

如果存在欧拉路,则所有点的出度和入度满足下列限制之一:

  • 所有点的出入度不为 0 且相等
  • 有且仅有两个点 u u u v v v 满足下述条件,其他点的出入度不为 0 且相等:
    • u u u 的入度和出度之差为 1
    • v v v 的出度和入度之差为 1

因为题目保证答案必然存在,所以可认为构造的图必然满足上述限制。

于是,可先找到点 v v v 作为起点,如果不存在 v v v 则说明存在欧拉回路,则可任选一点作为起点。不妨设起点为 s s s

设有一维数组 p a t h path path 用以记录欧拉路。从 s s s 出发开始深度优先遍历,每经过一条边就将其删除。在遍历过程中,如果点 t t t 没有出边了,则将其追加至 p a t h path path 中。在遍历结束后, p a t h path path 中保存的即为反向的欧拉路。

因此,在遍历结束后,可逆序遍历 p a t h path path,构造出答案。详细实现可见注释。

class Solution {
     
public:
    void dfs(int root, vector<int> &path, unordered_map<int, vector<int>> &edges) {
     
        // 遍历到了 root 点。
        auto &edge = edges[root];
        // 依次深度遍历 root 的出边指向的点
        while (!edge.empty()) {
     
            // 为了借助 vector::pop_back() 实现删除,这里倒着遍历
            // 先取出 edge.back()
            auto e = edge.back();
            // 删除这条边
            edge.pop_back();
            // 开始深度优先遍历 edge.back()
            dfs(e, path, edges);
        }
        // 执行到这时,root 的出边必然都删除了,因此将其追加至 path
        path.push_back(root);
    }
    vector<vector<int>> validArrangement(vector<vector<int>>& pairs) {
     
        // 边表
        unordered_map<int, vector<int>> edges;
        // 记录每个数字的出入度
        unordered_map<int, int> in, out;
        
        for (const auto &pair : pairs) {
     
            // 将 pair 作为有向边, pair[0] → pair[1]
            edges[pair[0]].push_back(pair[1]);
            // 更新出入度
            in[pair[1]]++;
            out[pair[0]]++;
        }
        
        // 选择起始点,先随机选择一个
        int start = in.begin()->first;
        
        for(const auto &p : out) {
     
            // 找到了出度 - 入度 = 1 的点,则此点必须为起点
            if (p.second - in[p.first] == 1) {
     
                start = p.first;
                break;
            }
        }
        
        // path 保存逆序的欧拉路
        std::vector<int> path;
        // 开始深度优先遍历
        dfs(start, path, edges);
        vector<vector<int>> anw;
        // 构造答案,path 中相邻的两个点,必然对应一个数对
        for (int i = path.size()-1; i >= 1; i--) {
     
            anw.emplace_back(vector<int>{
     path[i], path[i-1]});
        }
        return anw;
    }
};

你可能感兴趣的:(力扣周赛,leetcode,算法,职场和发展)