既然巅峰留不住,那就重走来时路!


动机

跟室友丁神聊完之后,感到了深深的peer pressure。又拾起了想去找个实习的想法。不管是找个AI4S的科研岗还是找个大厂做算法的业务岗,都是对自己经历和能力的一大步提升。
计划出去半年,发点论文也好,积累些工业界经验也好,总比在学校无所事事的好。想起高师兄说的,你要么早点毕业,要么出去交换或者找个实习,在学校就是浪费时间。当时觉得高师兄在装逼,现在想想高师兄真的太真诚了,装逼的只有当时傲慢的自己。
我得努力一点了,至少能让自己在毕业时多些选择。

行动计划

每天刷点leetcode,把算法基本功捡起来。开发的八股还有点印象,再学点大模型的八股,拥抱一下时代潮流。计划用四五个月的时间,让自己能有去参加面试的信心和能力。

  1. 每天至少做每日一题,写题解。
  2. 有多余时间就看看经典算法题和面试高频题目,从数据结构开始,链表,数组,树,图。再看算法,dp和backtrack,图算法,以及一些小tricks,很多都不用重新理解原理,所以这遍再学习要比研二看的更深一些,一些acm用的高阶算法,线段树之类的也要涉及到。
  3. 大模型八股。transformer,bert,llm,RLHF。

刷题赎罪

2025.6.18

  • 每日一题

2966.划分数组并满足最大差限制

划分数组并满足最大差限制

第一直觉就是模拟一下。先排序,然后贪心。

class Solution {
public:
    vector<vector<int>> divideArray(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        int n = nums.size();
        for(int i = 0; i < n / 3; i++) {
            int buf = i * 3;
            if(nums[buf + 2] - nums[buf] > k) {
                return {};
            }
            res.push_back({nums[buf], nums[buf + 1], nums[buf + 2]});
        }
        return res;
    }
};
  • 链表

今天第一天给自己加餐。先看点链表的东西,做了反转链表相关的题目。

反转链表

反转链表

简单题,双指针。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur) {
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
};

没啥说的,熟练背诵……

反转链表2

反转指定区间内的链表

跟反转链表的思路一样,双指针。加点判断语句。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur) {
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;

        ListNode* pre = dummy;
        for(int i = 0; i < left - 1; i++) {
            pre = pre->next;
        }
        ListNode* r = pre;
        for(int i = 0; i < right - left + 1; i++) {
            r = r->next;
        }
        ListNode* l = pre->next;
        ListNode* cur = r->next;
        pre->next = nullptr;
        r->next = nullptr;
        reverseList(l);
        pre->next = r;
        l->next = cur;
        return dummy->next;
    }
};

2025.6.19

  • 每日一题

2967.划分数组使最大差为K

划分数组使最大差为K

跟昨天的每日一题长得好像啊,但是昨天的是K个一组,确定了必须划分的组数,今天是最少的组数,先写个暴力试一下。

class Solution {
public:
     int partitionArray(vector<int>& nums, int k) {
        sort(nums.negin(), nums.end());
        int n = nums.size();
        int res = 0;
        for(int i = 0; i < n; ) {
            int j = i + 1;
            while(j < n && nums[j] - nums[i] <= k) {
                j++;
            }
            res++;
            i = j;
        }
        return res;
    }
}

时间复杂度来自于排序算法的$O(n \log n)$,空间复杂度$O(1)$,感觉是没啥好优化的。

  • 链表

每日一题太简单了,做个难点的K个一组反转链表。

K个一组反转链表

K个一组反转链表

不管别的,先再默写一遍反转链表。应该是反转链表系列的基本模块。

ListNode* reserveList(ListNode* head) {
    ListNode* pre = nullptr;
    ListNode* cur = head;
    while(cur) {
        ListNode* next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

然后想k个一组反转链表是啥意思。好像双指针的思路可以用上。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur) {
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* pre = dummy;
        ListNode* cur = head;
        // 截取链表
        while(cur) {
            ListNode* right = pre;
            for(int i = 0; i < k; i++) {
                right = right->next;
                if(! right) {
                    return dummy->next;
                }
            }
            ListNode* next = right->next;
            right->next = nullptr;
            // 反转链表
            pre->next = reverse(cur);
            
        }
    }
};

一直到这里都很顺利,但是突然发现也需要反转后的子链表的尾部节点指向next接回去。于是稍作修改。

class Solution {
public:
    pair<ListNode*, ListNode*> reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur) {
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return {pre, head}; // 返回反转后的头和尾
    }

    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* pre = dummy;
        ListNode* cur = head;
        // 截取链表
        while(cur) {
            ListNode* right = pre;
            for(int i = 0; i < k; i++) {
                right = right->next;
                if(! right) {
                    return dummy->next;
                }
            }
            ListNode* next = right->next;
            right->next = nullptr;
            // 反转链表
            pair<ListNode*, ListNode*> res = reverseList(cur);
            pre->next = res.first;
            res.second->next = next; 
            pre = res.second; 
            cur = next; 
        }
        return dummy->next;
    }
};

时间复杂度就是遍历的$O(n)$,空间复杂度$O(1)$。
总的来说是一道不特别难的困难题。

八股

手撕

BPE算法

BPE(byte pair encoding)算法是一种编码方式。解决了在语言模型训练过程中的编码问题。


文章作者: 李垚
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 李垚 !
评论
  目录