校招&考研复试刷题题解

校招&考研复试刷题题解

基础题只有代码,有点思维量的有文字解析

力扣

力扣: 两数之和

题解

点击查看代码
class Solution {
public:
    std::unordered_map<int, int> mp; 
    std::vector<int> twoSum(std::vector<int>& nums, int target) {
        for(int i = 0; i < nums.size(); i++){
            if(mp.count(target - nums[i])) return std::vector<int>{i, mp[target - nums[i]]};
            else mp.emplace(nums[i], i);
        }
        return std::vector<int>();
    }
};

力扣128: 最长连续序列

题解

\(O(NlogN)\)的解决方法很好想

  1. 开个std::umorderedmap计数, 二分答案 \(O(N\log max)\) .
  2. 先排序,然后扫一遍,\(O(N\log N)\)
  3. 直接用std::set,用个指针扫一遍 \(O(N\log N)\) 本质和离散化比较像.

\(O(N \alpha(N))\)的解法

\(\alpha()\) 为阿克曼函数(Ackermann function)的反函数,实际应用中几乎可以视为常数。

点击查看代码
struct DSU{
    std::vector<int> f, sz;
    DSU(int n) : f(n + 1), sz(n + 1, 1) { 
		iota(f.begin(), f.end(), 0);
	}
    int find(int x){   // 路径压缩
        return f[x] == x ? x : f[x] = find(f[x]);
    }
    bool merge(int a, int b){   // 按秩合并
        int fa = find(a), fb = find(b);
        if(fa == fb) return false;
        if(sz[fb] > sz[fa]) std::swap(fa, fb);
        f[fb] = fa;
        sz[fa] += sz[fb];
        return true;
    }
    bool same(int a, int b) { return find(a) == find(b); }
};

class Solution {
public:
    std::unordered_map<int, int> mp;
    int longestConsecutive(std::vector<int>& nums) {
        DSU d(nums.size() + 1);
        for(int i = 0; i < nums.size(); i++)
            mp.emplace(nums[i], i);
        
        for(auto x : nums)
            if(mp.count(x + 1)) d.merge(mp[x], mp[x + 1]);
        
        int ans = 0;
        for(int i = 0; i < nums.size(); i++)
            ans = std::max(ans, d.sz[i]);
        return ans;
    }
};

\(O(N)\) 解法

先用hash去重,再巧妙枚举。

点击查看代码
class Solution {   // 法2
public:
    std::unordered_set<int> se;
    int longestConsecutive(std::vector<int>& nums) {
        for(int i = 0; i < nums.size(); i++)
            se.insert(nums[i]);
        
        int ans = 0;
        for(const int &x : se){   
            // 必须去重, 否则整多个最小值就g了
            if(se.count(x - 1)) continue;
            int tmp = 1, now = x;
            while(se.count(++now)) tmp++;
            ans = std::max(ans, tmp);
        }
        return ans;
    }
};

力扣283移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

题解

点击查看代码
class Solution {
public:
    void moveZeroes(std::vector<int>& nums) {
        int i = 0, j = 0, n = nums.size();
        while(i < n && j < n){
            while(i < n && (nums[i] != 0)) i++;
            while(j < n && (j < i || nums[j] == 0)) j++;
            if(j == n || i == n) break;
            std::swap(nums[i], nums[j]);
        }
    }
};

力扣11盛最多水的容器 Median

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。

说明:你不能倾斜容器。

image-20251228140819543

题解

考虑最大的宽度 \(l,r\) 考虑如下两种情况:

  1. 缩小宽度,移动高的一边
    • 由于宽度减小且容器高由短板决定,所以一定不存在更优解。
    • 这种情况可以全部排除。
  2. 缩小宽度,移动低的一边
    • 宽度缩小的同时,短板可能更新,从而得到更优解。

所以一开始让宽度最大,每次缩减宽度只改变低的下标,即可找到最优解,时间复杂度为 \(O(N)\)

点击查看代码
class Solution {
public:
    int maxArea(std::vector<int>& height) {
        int l = 0, r = height.size() - 1;
        int ans = 0;
        while(l < r){
            ans = std::max(ans, std::min(height[l], height[r]) * (r - l));
            if(height[l] < height[r]) l++;
            else r--;
        }
        return ans;
    }
};

力扣200岛屿数量 Median

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

题解

很基础的DFS染色

点击查看代码
const int N = 305;

class Solution {
public:
    bool col[N][N];
    int n, m;
    vector<int> nx{1,0,-1,0};
    vector<int> ny{0,1,0,-1};
    
    inline bool inmap(int x, int y){
        return (x >= 0 && x < n) && (y >= 0 && y < m);
    }

    void dfs(vector<vector<char>>& grid, int x,int y, int tot){
        col[x][y] = tot;
        for(int i = 0; i < 4; i++){
            int xx = x + nx[i], yy = y + ny[i];
            if(inmap(xx, yy) && !col[xx][yy] && grid[xx][yy] == '1') 
                dfs(grid, xx, yy, tot);
        }
    }

    int numIslands(vector<vector<char>>& grid) {
        memset(col, 0, sizeof(col));
        int tot = 0;
        n = grid.size(); m = grid[0].size();
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                if(!col[i][j] && grid[i][j] == '1') dfs(grid, i, j, ++tot);
            }
        }
        return tot;
    }
};

力扣207课程表

题解

点击查看代码
const int N = 2005, M = 5005;

class Solution {
public:
    int head[N], ver[M], nxt[M], deg[N];
    int n, m, tot = 1;

    inline void add(int x, int y){
        ver[++tot] = y; nxt[tot] = head[x]; head[x] = tot;
    }

    bool canFinish(int num, vector<vector<int>>& E) {
        n = num; m = E.size();
        memset(head, 0, (n + 1) * sizeof(int));
        memset(deg, 0, (n + 1) * sizeof(int));
        for(int i = 0; i < E.size(); i++){
            add(E[i][1] + 1, E[i][0] + 1);  deg[E[i][0] + 1]++;
        }

        queue<int> q;
        int ans = n;
        for(int i = 1; i <= n; i++) if(!deg[i]) q.emplace(i);
        while(!q.empty()){
            int x = q.front(); q.pop();
            ans--;
            for(int i = head[x]; i; i = nxt[i]){
                if(--deg[ver[i]] == 0) q.push(ver[i]);
            }
        }
        return ans == 0;
    }
};

力扣-46全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序返回答案。

题解

点击查看代码
class Solution {
public:
    void dfs(vector<vector<int>>& ans, vector<int>& nums, int now, int n){
        if(now == n){
            ans.emplace_back(nums);
            return ;
        }
        for(int i = now; i < n; i++){
            swap(nums[now], nums[i]);
            dfs(ans, nums, now + 1, n);
            swap(nums[now], nums[i]);
        }

    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> ans;
        dfs(ans, nums, 0, nums.size());
        return ans;
    }
};

力扣53-最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组是数组中的一个连续部分。

题解

点击查看代码
class Solution {
public:
    int maxSubArray(std::vector<int>& nums) {
        int l = 0, tmp = 0;
        int ans = -1e9;
        for(int r = 0; r < nums.size(); r++){
            if(tmp < 0){
                tmp = nums[r]; l = r;
                ans = std::max(tmp, ans);
                continue;
            }
            tmp += nums[r];
            ans = std::max(ans, tmp);
        }
        return ans;
    }
};

力扣56-合并区间

题解
如果是只要边界挨着就合并的话可以考虑前缀和差分,复杂度为区间范围

点击查看拓展思路代码
class Solution {
public:
    std::vector<std::vector<int>> merge(std::vector<std::vector<int>>& ls) {
        std::vector<int> su(10005, 0);
        for(int i = 0; i < ls.size(); i++){
            su[ls[i][0]]++; su[ls[i][1] + 1]--;
        }
        for(int i = 1; i < 10004; i++) su[i] += su[i - 1];
        std::vector<std::vector<int>> ans;
        int l = -1, r = -1;
        for(int i = 0; i < 10002; i++){
            if(l == -1 && su[i] != 0) {
                l = r =i; continue;
            }
            if(l != -1 && su[i] != 0){
                r++; continue;
            }
            if(!su[i] && l != -1 && r != -1){
                ans.emplace_back(std::vector<int> {l ,r});
                l = r = -1;
            }
        }
        return ans;
    }
};

本题只合并重叠的区间,考虑先按左端点排序再枚举。

点击查看代码
class Solution {   //  本题AC解法
public:
    static bool cmp(std::vector<int>& x, std::vector<int>& y){
        if(x[0] == y[0]) return x[1] < y[1];
        return x[0] < y[0];
    }

    std::vector<std::vector<int>> merge(std::vector<std::vector<int>>& ls) {
        std::vector<std::vector<int>> ans;
        sort(ls.begin(), ls.end(), cmp);
        ls.emplace_back(std::vector<int> {10005, 10005});
        int l = ls[0][0], r = ls[0][1];
        for(int i = 1; i < ls.size(); i++){
            if(ls[i][0] <= r) { r = std::max(r, ls[i][1]); continue; }
            ans.emplace_back(std::vector<int> {l, r});
            l = ls[i][0], r = ls[i][1];
        }
        return ans;
    }
};

力扣35-搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。

题解

二分模板题

点击查看代码
class Solution {
public:

    int searchInsert(vector<int>& nums, int& x) {
        int l = 0, r = nums.size();
        while(l < r){
            int mid = (l + r) >> 1;
            if(nums[mid] >= x) r = mid;
            else l = mid + 1;
        }
        return r;
    }
};

力扣74-搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵:

  • 每行中的整数从左到右按非严格递增顺序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。

题解

点击查看代码
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int l1 = 0, r1 = matrix.size() - 1;
        int n = matrix[0].size();
        while(l1 < r1){
            int mid = (l1 + r1) >> 1;
            if(matrix[mid][n - 1] >= target) r1 = mid;
            else l1 = mid + 1;
        }
        int l = 0, r = n - 1;
        while(l < r){
            int mid = (l + r) >> 1;
            if(matrix[r1][mid] >= target) r = mid;
            else l = mid + 1;
        }
        return matrix[r1][r] == target;
    }
};

力扣-4寻找两个正序数组的中位数-Hard

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 \(O(log (m+n))\)

题解

这是一个从 \(O(m+n)\),到 \(O(\log m \log n)\) 再到 \(\log(\max\{m,n\})\) 的优化思路。

  • 首先,很容易想到一个 \(O(m+n)\) 的解法:对两个有序序列做一个归并,然后进行二分即可。
  • 难的是怎么做到 \(O(\log (m+n))\) 的复杂度,考虑优化。
  • 这时脑子里忽然蹦出一个 \(O(\log m \log n)\) 的解法:
    • 对其中一个序列二分其边界 \(x\)\(x\) 表示在nums1中有 \(x\) 个元素在中位数左边,然后写一个 chack()
    • check()里面对nums2做二分,找到 \(nums1[x]\)nums2中的位置,看是否满足中位数的条件(看在边界之前的元素个数是大于还是小于 \(\frac{m+n}{2}\),注意一下判断总元素的奇偶这个细节)
    • 这种解正确性的本质是一个序列的边界是具有01单调性的,确认后,另一个序列的边界也有01单调性。
  • 这个复杂度已经很优了,至少在算法竞赛里是卡不掉的,因为输入输出的时间远比这个算法运行长的多。
  • 现在考虑如何优化到 \(\log(m+n)\) 的复杂度。
  • 此时,忽然想到,中位数一定要满足的条件(上一种解法中说的)。所以考虑二分一个数组,在另一个数组中直接按中位数的条件算出下标,具体逻辑见代码。
  • 即划分数组的方法,时间复杂度为 \(O(\log \max (m,n))\)
点击查看代码
class Solution {
public:
    inline bool check(vector<int>& nums1, vector<int>& nums2, int i, int j) {
        // nums_i1, nums_i, nums_j1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j]
        int nums_i1 = (i == 0 ? INT_MIN : nums1[i - 1]);
        int nums_i = (i == nums1.size() ? INT_MAX : nums1[i]);
        int nums_j1 = (j == 0 ? INT_MIN : nums2[j - 1]);
        int nums_j = (j == nums2.size() ? INT_MAX : nums2[j]);
        return nums_i1 <= nums_j;
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if (nums1.size() > nums2.size()) {
            return findMedianSortedArrays(nums2, nums1);
        }
        
        int m = nums1.size();   // m > n
        int n = nums2.size();
        int l = 0, r = m;
        // med1:前一部分的最大值
        // med2:后一部分的最小值
        int med1 = 0, med2 = 0;

        while (l < r) {
            // 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
            // 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
            int i = (l + r + 1) >> 1;
            int j = ((m + n + 1) >> 1) - i;
            if (check(nums1, nums2, i, j)) l  = i;
            else r = i - 1; 
        }
        int i = l;
        int j = ((m + n + 1) >> 1) - i;
        int nums_i1 = (i == 0 ? INT_MIN : nums1[i - 1]);
        int nums_i = (i == m ? INT_MAX : nums1[i]);
        int nums_j1 = (j == 0 ? INT_MIN : nums2[j - 1]);
        int nums_j = (j == n ? INT_MAX : nums2[j]);
        med1 = max(nums_i1, nums_j1);
        med2 = min(nums_i, nums_j);
        return ((m + n) & 1) ? med1 : (med1 + med2) / 2.0;
    }
};
posted @ 2025-12-27 17:14  CH-Yu  阅读(45)  评论(0)    收藏  举报