【算法日记20】LeetCode 438. 找到所有字母异位词:定长滑动窗口的极致优雅

📍 题目背景

各位家人们,今天咱们来拆解一道算法面试中极其经典的字符串难题:438. 找到字符串中所有字母异位词。 这道题可以说是"滑动窗口(Sliding Window)"的教科书级经典应用,不仅考察了对窗口边界的掌控力,还考察了如何极致压缩时间复杂度。

【题目描述】 给定两个字符串 sp,找到 s 中所有 p异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。 字母异位词:指字母相同,但排列不同的字符串。

【示例】

输入: s = "cbaebabacd", p = "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。 起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

✨ 核心破题逻辑:定长滑动窗口 + 频率数组

直觉陷阱 :每次从 s 里截取一段跟 p 一样长的字符串,排个序再对比? 致命后果:每一次排序都需要额外时间,加上外层循环,如果字符串超级长,绝对会超时(TLE)!

【降维打击:定长滑动窗口】 我们要找的是异位词,说白了就是"字母种类和数量一致,不管顺序"。 既然题目说明了"只包含小写字母",我们就可以用两个大小为 26 的数组(scountpcount)来当做计数的"备忘录",这比真正的哈希表要快得多!

破题三步曲:

  1. 打造标尺 (pcount) :先把目标字符串 p 里的字母频率统计出来,这是我们的"核对标准"。

  2. 初始化第一个窗口 :在 s 的最左边,先吃进一个和 p 同样长度的窗口,统计出 scount,并和 pcount 进行第一次核对。

  3. 窗口开始滑动(灵魂操作)

    • 窗口整体向右平移一格。

    • 吃进新字符 :右边新进来的字母,在 scount 里对应数量 +1

    • 吐出旧字符 :左边被移出窗口的字母,在 scount 里对应数量 -1

    • 每次滑动,只发生这两次简单的加减法操作,完全不需要重新扫描整个窗口!然后立刻比对 scountpcount

💻 C++ 满分 AC 代码

掌握了上述逻辑,我们来看看优雅的 C++ 实现。注意代码中的几个防坑细节!

复制代码
#include <iostream>
#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        // 🚀 防坑 1:兜底防线!如果 s 比 p 还短,直接下班,防止越界报错
        if (s.size() < p.size()) {
            return {};
        }
        
        // 🚀 亮点:用数组代替哈希表,速度起飞!
        vector<int> scount(26, 0);
        vector<int> pcount(26, 0);
        vector<int> res; 
        
        // 1. 初始化第一个窗口和目标标尺
        for(int i = 0; i < p.size(); i++) {
            scount[s[i] - 'a']++;
            pcount[p[i] - 'a']++;
        }
        
        // 第一次核对
        if(scount == pcount) {
            res.push_back(0);
        }
        
        // 2. 窗口开始向右滑动
        for(int i = p.size(); i < s.size(); i++) {
            // 吃进右边界的新字符
            scount[s[i] - 'a']++;                 
            
            // 吐出左边界外的旧字符 (原来左边界的字符下标是 i - p.size())
            scount[s[i - p.size()] - 'a']--;      
            
            // 每次滑动后进行核对
            if(scount == pcount) {
                res.push_back(i - p.size() + 1);
            }
        }
        
        return res;
    }
};

🔍 硬核原理解析:大厂面试加分项

如果你能在面试中讲出以下两点,面试官绝对会对你刮目相看:

1. 神奇的 s[i] - 'a' 是什么意思?

这其实是利用了底层 ASCII 码的物理特性。 在计算机底层,小写字母 'a''z' 是按顺序排列的数字(97 到 122)。 当我们写出 s[i] - 'a' 时,就是用当前的字母减去 'a'

  • 如果是 'a'97 - 97 = 0,正好放进 0 号抽屉。

  • 如果是 'b'98 - 97 = 1,正好放进 1 号抽屉。

  • 以此类推...... 这个极其巧妙的数学减法,让我们省去了构建复杂哈希表的巨大开销!

2. C++ vector 的重载运算符 ==

在 C++ 中,如果我们比较两个类型相同、大小相等的 vector,可以直接使用 scount == pcount。底层会自动帮我们逐个元素进行对比,这不仅让代码极其简洁,而且由于是定长(26个元素)的连续内存块,对比速度极快!

📊 复杂度分析

  • 时间复杂度: O(N)。这里 N 是字符串 s 的长度。虽然每次循环都要对比两个大小为 26 的数组,但 26 是常数级操作(相当于 O(26 \\times N)),最终时间复杂度依然是极速的 O(N)。全程没有冗余的重复扫描!

  • 空间复杂度: O(1)。我们仅仅申请了两个大小固定为 26 的整数数组,无论输入字符串有多长,消耗的内存空间都不会增加。

💡 总结: "定长滑动窗口"的核心就在于那句口诀:吃进来一个,吐出去一个 。只要把控好这个边界,再结合 ASCII码相减 映射到数组的高阶技巧,任何同类型的字符串匹配难题都会在你面前迎刃而解!

附上动画演示

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>滑动窗口魔法:代码同步演示</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        .cell-transition { transition: all 0.3s ease-in-out; }
        
        /* 字符方块状态 */
        .char-box { border-color: #e5e7eb; background-color: #f9fafb; color: #9ca3af; }
        .char-in-window { box-shadow: 0 0 10px rgba(59, 130, 246, 0.4); border-color: #3b82f6; background-color: #eff6ff; color: #1e3a8a; transform: scale(1.05); z-index: 5; }
        .char-add { box-shadow: 0 0 15px rgba(34, 197, 94, 0.7); border-color: #22c55e; background-color: #dcfce7; color: #14532d; transform: scale(1.15); z-index: 10; }
        .char-remove { box-shadow: 0 0 15px rgba(239, 68, 68, 0.7); border-color: #ef4444; background-color: #fee2e2; color: #7f1d1d; transform: scale(1.15); z-index: 10; opacity: 0.8; }
        .char-match { box-shadow: 0 0 20px rgba(234, 179, 8, 0.8); border-color: #eab308; background-color: #fef9c3; color: #854d0e; transform: scale(1.1); z-index: 10; font-weight: 900; }
        
        /* 频率表动画 */
        .freq-match { background-color: #dcfce7 !important; color: #166534; font-weight: bold; }
        .freq-mismatch { background-color: #fee2e2 !important; color: #991b1b; font-weight: bold; }

        /* 代码高亮样式 */
        .code-line { transition: all 0.2s; border-left: 4px solid transparent; }
        .code-highlight { background-color: rgba(59, 130, 246, 0.25); border-left-color: #3b82f6; color: #ffffff !important; text-shadow: 0 0 5px rgba(255,255,255,0.3); }
        .code-keyword { color: #c678dd; } /* 紫色 */
        .code-type { color: #e5c07b; }    /* 黄色 */
        .code-var { color: #e06c75; }     /* 红色 */
        .code-num { color: #d19a66; }     /* 橙色 */
        .code-comment { color: #5c6370; font-style: italic; } /* 灰色 */
    </style>
</head>
<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-2 md:p-4 font-sans text-gray-800">

    <div class="max-w-7xl w-full bg-white rounded-2xl shadow-2xl overflow-hidden flex flex-col">
        <!-- 头部标题 -->
        <div class="bg-gradient-to-r from-blue-600 to-indigo-700 text-white p-4 md:p-6 text-center">
            <h1 class="text-2xl md:text-3xl font-bold mb-2">🔍 滑动窗口 x 核心代码 同步演示</h1>
            <p class="text-blue-200 text-sm md:text-base">透视底层逻辑:观察动画的同时,看看 C++ 代码走到哪一行了!</p>
        </div>

        <!-- 动画主界面 (分左右两栏) -->
        <div class="p-4 md:p-6 flex-1 flex flex-col lg:flex-row gap-6">
            
            <!-- 左侧:动画演示区 -->
            <div class="flex-1 flex flex-col border-r-0 lg:border-r-2 border-gray-100 lg:pr-6">
                <!-- 讲解信息框 -->
                <div class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-r-xl mb-6 min-h-[90px] flex items-center shadow-sm">
                    <p id="message-box" class="text-base md:text-lg font-medium text-blue-900 leading-relaxed">
                        准备开始!用长度为 3 的"滑动窗口"扫描字符串,并实时比对频率表。
                    </p>
                </div>

                <!-- 数据面板:频率表 & 结果 -->
                <div class="flex flex-col sm:flex-row gap-4 mb-8 items-stretch justify-center">
                    <div class="bg-gray-50 p-3 rounded-xl border-2 border-gray-100 shadow-inner flex-1 max-w-sm mx-auto w-full">
                        <h3 class="text-center font-bold text-gray-500 mb-2 text-xs tracking-widest uppercase">📊 字母频率表</h3>
                        <div class="grid grid-cols-6 gap-1 text-center">
                            <div class="font-bold text-gray-400 text-xs py-1 border-b">字母</div>
                            <div class="font-bold text-gray-400 text-xs py-1 border-b">a</div>
                            <div class="font-bold text-gray-400 text-xs py-1 border-b">b</div>
                            <div class="font-bold text-gray-400 text-xs py-1 border-b">c</div>
                            <div class="font-bold text-gray-400 text-xs py-1 border-b">d</div>
                            <div class="font-bold text-gray-400 text-xs py-1 border-b">e</div>
                            
                            <div class="font-bold text-blue-600 text-xs py-1.5 bg-blue-50 rounded-l">pCount</div>
                            <div class="font-mono text-gray-700 py-1.5 bg-blue-50">1</div>
                            <div class="font-mono text-gray-700 py-1.5 bg-blue-50">1</div>
                            <div class="font-mono text-gray-700 py-1.5 bg-blue-50">1</div>
                            <div class="font-mono text-gray-400 py-1.5 bg-blue-50">0</div>
                            <div class="font-mono text-gray-400 py-1.5 bg-blue-50 rounded-r">0</div>

                            <div class="font-bold text-indigo-600 text-xs py-1.5 bg-indigo-50 rounded-l mt-1">sCount</div>
                            <div id="count-a" class="font-mono text-gray-700 py-1.5 bg-indigo-50 mt-1 cell-transition">0</div>
                            <div id="count-b" class="font-mono text-gray-700 py-1.5 bg-indigo-50 mt-1 cell-transition">0</div>
                            <div id="count-c" class="font-mono text-gray-700 py-1.5 bg-indigo-50 mt-1 cell-transition">0</div>
                            <div id="count-d" class="font-mono text-gray-400 py-1.5 bg-indigo-50 mt-1 cell-transition">0</div>
                            <div id="count-e" class="font-mono text-gray-400 py-1.5 bg-indigo-50 mt-1 cell-transition rounded-r">0</div>
                        </div>
                    </div>

                    <div class="bg-white p-3 rounded-xl shadow border border-green-200 flex flex-col items-center justify-center min-w-[120px]">
                        <div class="text-xs text-green-600 font-bold mb-1">✅ 结果起始下标</div>
                        <div id="results-box" class="text-2xl font-black text-green-500">[]</div>
                    </div>
                </div>

                <!-- 字符串 S 展示区 -->
                <div class="mb-8 relative">
                    <h3 class="text-xs font-bold text-gray-400 mb-2 ml-2">主字符串 s</h3>
                    <div class="flex flex-nowrap justify-start md:justify-center gap-1 md:gap-2 overflow-x-auto pb-2 px-1" id="string-container">
                        <!-- 方块动态生成 -->
                    </div>
                </div>

                <!-- 控制按钮区 -->
                <div class="flex justify-center gap-3 mt-auto flex-wrap">
                    <button id="btn-reset" class="px-5 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 font-semibold rounded-full shadow transition-colors text-sm">
                        🔄 重置
                    </button>
                    <button id="btn-step" class="px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-bold rounded-full shadow transition-transform transform hover:scale-105 text-sm">
                        👣 下一步
                    </button>
                    <button id="btn-play" class="px-6 py-2 bg-emerald-500 hover:bg-emerald-600 text-white font-bold rounded-full shadow transition-transform transform hover:scale-105 text-sm">
                        ▶️ 自动播放
                    </button>
                </div>
            </div>

            <!-- 右侧:代码同步高亮区 -->
            <div class="w-full lg:w-5/12 bg-[#282c34] rounded-xl shadow-inner overflow-hidden flex flex-col">
                <div class="bg-[#21252b] px-4 py-2 flex items-center border-b border-gray-700">
                    <div class="flex gap-1.5 mr-4">
                        <div class="w-3 h-3 rounded-full bg-red-500"></div>
                        <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
                        <div class="w-3 h-3 rounded-full bg-green-500"></div>
                    </div>
                    <span class="text-gray-400 text-xs font-mono">Solution.cpp</span>
                </div>
                <div class="p-4 font-mono text-xs md:text-sm text-gray-300 overflow-y-auto leading-loose flex-1">
                    <div id="line-1" class="code-line px-2 py-0.5 code-comment">// 1. 初始化频率数组</div>
                    <div id="line-2" class="code-line px-2 py-0.5"><span class="code-type">vector</span>&lt;<span class="code-type">int</span>&gt; pCount(26, 0), sCount(26, 0);</div>
                    
                    <div id="line-3" class="code-line px-2 py-0.5 code-comment mt-2">// 2. 建立初始窗口</div>
                    <div id="line-4" class="code-line px-2 py-0.5"><span class="code-keyword">for</span> (<span class="code-type">int</span> i = 0; i &lt; pLen; i++) {</div>
                    <div id="line-5" class="code-line px-2 py-0.5 pl-6">pCount[p[i] - 'a']++;</div>
                    <div id="line-6" class="code-line px-2 py-0.5 pl-6">sCount[s[i] - 'a']++;</div>
                    <div id="line-7" class="code-line px-2 py-0.5">}</div>
                    
                    <div id="line-8" class="code-line px-2 py-0.5 code-comment mt-2">// 3. 核对第一个窗口</div>
                    <div id="line-9" class="code-line px-2 py-0.5"><span class="code-keyword">if</span> (sCount == pCount) {</div>
                    <div id="line-10" class="code-line px-2 py-0.5 pl-6">res.push_back(0);</div>
                    <div id="line-11" class="code-line px-2 py-0.5">}</div>
                    
                    <div id="line-12" class="code-line px-2 py-0.5 code-comment mt-2">// 4. 窗口开始向右滑动</div>
                    <div id="line-13" class="code-line px-2 py-0.5"><span class="code-keyword">for</span> (<span class="code-type">int</span> i = pLen; i &lt; sLen; i++) {</div>
                    <div id="line-14" class="code-line px-2 py-0.5 pl-6">sCount[s[i] - 'a']++; <span class="code-comment ml-2">// 吃进新字符</span></div>
                    <div id="line-15" class="code-line px-2 py-0.5 pl-6">sCount[s[i - pLen] - 'a']--; <span class="code-comment ml-2">// 吐出旧字符</span></div>
                    <div id="line-16" class="code-line px-2 py-0.5 pl-6 mt-1"><span class="code-keyword">if</span> (sCount == pCount) {</div>
                    <div id="line-17" class="code-line px-2 py-0.5 pl-10 text-[#56b6c2]">res.push_back(i - pLen + 1);</div>
                    <div id="line-18" class="code-line px-2 py-0.5 pl-6">}</div>
                    <div id="line-19" class="code-line px-2 py-0.5">}</div>
                </div>
            </div>
        </div>
    </div>

    <script>
        const s = "cbaebabacd";
        const p = "abc";
        const pLen = p.length;
        const sLen = s.length;
        
        let currentFrameIndex = 0;
        let isPlaying = false;
        let playInterval;
        let frames = [];
        const targetCounts = { a: 1, b: 1, c: 1, d: 0, e: 0 };

        function checkMatch(counts) {
            for (let char of ['a', 'b', 'c', 'd', 'e']) {
                if ((counts[char] || 0) !== targetCounts[char]) return false;
            }
            return true;
        }

        function generateFrames() {
            frames = [];
            let currentCounts = { a: 0, b: 0, c: 0, d: 0, e: 0 };
            let results = [];

            frames.push({
                sCount: {...currentCounts}, window: [-1, -1], results: [...results],
                msg: `首先初始化频率数组 <b>pCount</b> 和 <b>sCount</b>,大小都是 26,初始为 0。`,
                activeCode: [1, 2] // 对应 HTML 中的 line id
            });

            // 1. 初始化第一个窗口
            frames.push({
                sCount: {...currentCounts}, window: [0, 0], actionIdx: 0, actionType: 'add', results: [...results],
                msg: `【初始窗口】将左边前 ${pLen} 个字符吃进窗口。吃进 '${s[0]}'。`,
                activeCode: [3, 4, 5, 6, 7]
            });
            currentCounts[s[0]]++;
            
            frames.push({
                sCount: {...currentCounts}, window: [0, 1], actionIdx: 1, actionType: 'add', results: [...results],
                msg: `【初始窗口】吃进 '${s[1]}'。`,
                activeCode: [3, 4, 5, 6, 7]
            });
            currentCounts[s[1]]++;

            frames.push({
                sCount: {...currentCounts}, window: [0, 2], actionIdx: 2, actionType: 'add', results: [...results],
                msg: `【初始窗口】吃进 '${s[2]}'。初始窗口建立完毕,准备核对。`,
                activeCode: [3, 4, 5, 6, 7]
            });
            currentCounts[s[2]]++;

            let isMatch = checkMatch(currentCounts);
            if (isMatch) results.push(0);
            frames.push({
                sCount: {...currentCounts}, window: [0, 2], match: isMatch, results: [...results],
                msg: `检查 sCount 和 pCount 是否相等。${isMatch ? '🎉 匹配成功!记录下标 0。' : '不匹配。'}`,
                activeCode: isMatch ? [8, 9, 10, 11] : [8, 9, 11]
            });

            // 2. 窗口滑动
            for (let i = pLen; i < sLen; i++) {
                let leftOut = i - pLen;
                
                frames.push({
                    sCount: {...currentCounts}, window: [leftOut, i], actionIdx: i, actionType: 'add', results: [...results],
                    msg: `【滑动】指针向右,新吃进 <b>'${s[i]}'</b>。频率 +1。`,
                    activeCode: [12, 13, 14]
                });
                currentCounts[s[i]]++;

                frames.push({
                    sCount: {...currentCounts}, window: [leftOut + 1, i], actionIdx: leftOut, actionType: 'remove', results: [...results],
                    msg: `【滑动】为了保持长度为 ${pLen},必须吐出最左侧旧字符 <b>'${s[leftOut]}'</b>。频率 -1。`,
                    activeCode: [15]
                });
                currentCounts[s[leftOut]]--;

                isMatch = checkMatch(currentCounts);
                if (isMatch) results.push(leftOut + 1);
                
                frames.push({
                    sCount: {...currentCounts}, window: [leftOut + 1, i], match: isMatch, results: [...results],
                    msg: `比对更新后的窗口。${isMatch ? `🎉 匹配成功!记录起始下标 <b>${leftOut + 1}</b>。` : '不匹配。'}`,
                    activeCode: isMatch ? [16, 17, 18] : [16, 18]
                });
            }

            frames.push({
                sCount: {...currentCounts}, window: [-1, -1], results: [...results], end: true,
                msg: `🏁 循环结束,返回结果。核心操作全在循环内的常数级加减,完美 O(N)!`,
                activeCode: []
            });
        }

        function render() {
            const frame = frames[currentFrameIndex];
            
            // 1. UI 文本
            document.getElementById('message-box').innerHTML = frame.msg;
            document.getElementById('results-box').innerHTML = '[' + frame.results.join(', ') + ']';

            // 2. 代码高亮同步
            for (let i = 1; i <= 19; i++) {
                const lineEl = document.getElementById(`line-${i}`);
                if (!lineEl) continue;
                if (frame.activeCode && frame.activeCode.includes(i)) {
                    lineEl.classList.add('code-highlight');
                } else {
                    lineEl.classList.remove('code-highlight');
                }
            }

            // 3. 频率表
            for (let char of ['a', 'b', 'c', 'd', 'e']) {
                const el = document.getElementById(`count-${char}`);
                const currentVal = frame.sCount[char] || 0;
                el.innerText = currentVal;
                el.classList.remove('freq-match', 'freq-mismatch', 'text-gray-400');
                if (frame.match !== undefined) {
                    if (currentVal === targetCounts[char]) el.classList.add('freq-match');
                    else el.classList.add('freq-mismatch');
                } else {
                    if (currentVal === 0 && targetCounts[char] === 0) el.classList.add('text-gray-400');
                    else el.classList.add('text-gray-800');
                }
            }

            // 4. 字符串
            const container = document.getElementById('string-container');
            container.innerHTML = '';
            const [winStart, winEnd] = frame.window;

            for (let i = 0; i < sLen; i++) {
                const wrapper = document.createElement('div');
                wrapper.className = 'flex flex-col items-center flex-shrink-0';
                
                const idxLabel = document.createElement('div');
                idxLabel.className = 'text-[10px] md:text-xs font-bold text-gray-400 mb-1';
                idxLabel.innerText = i;

                const box = document.createElement('div');
                let boxClasses = 'w-9 h-9 md:w-12 md:h-12 flex items-center justify-center text-lg md:text-xl font-bold border-[3px] rounded-lg cell-transition ';
                
                if (frame.end) boxClasses += 'char-box opacity-50';
                else if (frame.match && i >= winStart && i <= winEnd) boxClasses += 'char-match';
                else if (i === frame.actionIdx) {
                    if (frame.actionType === 'add') boxClasses += 'char-add';
                    if (frame.actionType === 'remove') boxClasses += 'char-remove';
                } else if (i >= winStart && i <= winEnd) boxClasses += 'char-in-window';
                else boxClasses += 'char-box';
                
                box.className = boxClasses; 
                box.innerText = s[i];

                wrapper.appendChild(idxLabel);
                wrapper.appendChild(box);
                container.appendChild(wrapper);
            }
            
            const isEnd = currentFrameIndex >= frames.length - 1;
            document.getElementById('btn-step').disabled = isEnd;
            document.getElementById('btn-step').className = isEnd 
                ? "px-6 py-2 bg-gray-300 text-gray-500 font-bold rounded-full cursor-not-allowed shadow text-sm" 
                : "px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-bold rounded-full shadow transition-transform transform hover:scale-105 text-sm";
        }

        // --- 控制逻辑 ---
        document.getElementById('btn-step').addEventListener('click', () => { 
            if (currentFrameIndex < frames.length - 1) { currentFrameIndex++; render(); } 
        });
        document.getElementById('btn-reset').addEventListener('click', () => { 
            currentFrameIndex = 0; if(isPlaying) togglePlay(); render(); 
        });
        document.getElementById('btn-play').addEventListener('click', togglePlay);
        
        function togglePlay() {
            const playBtn = document.getElementById('btn-play');
            if (isPlaying) { 
                clearInterval(playInterval); 
                playBtn.innerHTML = '▶️ 自动播放'; 
                playBtn.classList.replace('bg-orange-500', 'bg-emerald-500'); 
                playBtn.classList.replace('hover:bg-orange-600', 'hover:bg-emerald-600'); 
                isPlaying = false; 
            } else {
                if (currentFrameIndex >= frames.length - 1) currentFrameIndex = 0;
                playBtn.innerHTML = '⏸️ 暂停播放'; 
                playBtn.classList.replace('bg-emerald-500', 'bg-orange-500'); 
                playBtn.classList.replace('hover:bg-emerald-600', 'hover:bg-orange-600'); 
                isPlaying = true;
                playInterval = setInterval(() => { 
                    if (currentFrameIndex < frames.length - 1) { 
                        currentFrameIndex++; render(); 
                    } else { togglePlay(); } 
                }, 1500); // 稍微放慢到1.5秒一帧,方便看清代码
            }
        }

        generateFrames(); 
        render();
    </script>
</body>
</html>
相关推荐
怪兽学LLM17 小时前
LeetCode 105. 从前序与中序遍历序列构造二叉树:分治递归思路详解
算法·leetcode·职场和发展
可编程芯片开发17 小时前
基于PI控制的三相整流器控制系统的simulink建模与仿真,包含超级电容充电和电机
算法
酿情师17 小时前
区块链共识算法深度拆解:PoW、PoS、PBFT、Raft 原理解析
算法·区块链·共识算法
hansang_IR18 小时前
【记录】「SCOI2016」三道模拟赛/26.7.12
c++·算法
QQ骞18 小时前
【ECU-HIL 嵌入式车载软件测试学习路线和面试描述方式(1)】
学习·面试·职场和发展
退休倒计时18 小时前
【每日一题】LeetCode 39. 组合总和 TypeScript
算法·leetcode·typescript
不是az18 小时前
力控相关知识点
算法·机器学习·最小二乘法
战血石LoveYY18 小时前
Integer类型超限,导致数据异常
java·算法
紫金修道19 小时前
PnP算法介绍(Perspective-n-Point)
算法
mm99542020 小时前
PMI三大证书:PMP、PgMP、PfMP证书对比
学习·职场和发展·项目管理·学习方法·pmp