盘点面试常见的设计类算法问题

news2024/9/23 15:29:22

设计问题也是一种很重要的考察类型,特征是整体难度不大,但是要求结构合理、复用性好、考虑周全,代码规范等等。有些面试官不喜欢烧脑的动态规划等问题,就喜欢设计题,笔者曾经在面试微博和猿辅导都遇到过类似的问题。这些题目中最重要的是LRU设计,我们前面应重点讲解。LeetCode里还有大量的设计类型的问题,这些题目整体来说难度都不大,但是要完整写完要花费很多篇幅,感兴趣的同学可以看这里力扣 加强练习。本文我们盘点几道典型的设计问题。

1 设计推特

LeetCode355.设计一个简化版的推特(Twitter),可以让用户实现发送推文,关注/取消关注其他用户,能够看见关注人(包括自己)的最近 10 条推文。

实现 Twitter 类:

Twitter() 初始化简易版推特对象
void postTweet(int userId, int tweetId) 根据给定的 tweetId 和 userId 创建一条新推文。每次调用此函数都会使用一个不同的 tweetId 。
List<Integer> getNewsFeed(int userId) 检索当前用户新闻推送中最近  10 条推文的 ID 。新闻推送中的每一项都必须是由用户关注的人或者是用户自己发布的推文。推文必须 按照时间顺序由最近到最远排序 。
void follow(int followerId, int followeeId) ID 为 followerId 的用户开始关注 ID 为 followeeId 的用户。
void unfollow(int followerId, int followeeId) ID 为 followerId 的用户不再关注 ID 为 followeeId 的用户。

这种题目是很多面试官特别喜欢出的问题,因此务必认真分析,代码尽量写得规范一些。本题的一种方式是采用哈希表+链表的方式来实现。

根据题意我们知道,对于每个推特用户,我们需要存储他关注的用户 Id,以及自己发的推文 Id 的集合,为了使每个操作的复杂度尽可能的低,我们需要根据操作来决定存储这些信息的数据结构。注意,由于题目中没有说明用户的 Id 是否连续,所以我们需要用一个以用户 Id 为索引的哈希表来存储用户的信息。

对于操作 3 和操作 4,我们只需要用一个哈希表存储,即可实现插入和删除的时间复杂度都为 O(1)O(1)。

对于操作 1 和操作 2,由于操作 2 要知道此用户关注的人和用户自己发出的最近十条推文,因此我们可以考虑对每个用户用链表存储发送的推文。每次创建推文的时候我们在链表头插入,这样能保证链表里存储的推文的时间是从最近到最久的。那么对于操作 2,问题其实就等价于有若干个有序的链表,我们需要找到它们合起来最近的十条推文。由于链表里存储的数据都是有序的,所以我们将这些链表进行线性归并即可得到最近的十条推文。这个操作与 23. 合并K个排序链表 基本等同。

 如果我们直接照搬「合并K个排序链表」的解法来进行合并,那么无疑会造成空间的部分浪费,因为这个题目不要求你展示用户的所有推文,所以我们只要动态维护用户的链表,存储最近的 recentMax 个推文 Id 即可(题目中的 recentMax 为 10)。那么对于操作 1,当发现链表的节点数等于 recentMax 时,我们按题意删除链表末尾的元素,再插入最新的推文 Id。对于操作 2,在两个链表进行线性归并的时候,只要已合并的数量等于 recentMax,代表已经找到这两个链表合起来后最近的 recentMax 条推文,直接结束合并即可。

class Twitter {
    private class Node {
        // 哈希表存储关注人的 Id
        Set<Integer> followee;
        // 用链表存储 tweetId
        LinkedList<Integer> tweet;
​
        Node() {
            followee = new HashSet<Integer>();
            tweet = new LinkedList<Integer>();
        }
    }
​
    // getNewsFeed 检索的推文的上限以及 tweetId 的时间戳
    private int recentMax, time;
    // tweetId 对应发送的时间
    private Map<Integer, Integer> tweetTime;
    // 每个用户存储的信息
    private Map<Integer, Node> user;
​
    public Twitter() {
        time = 0;
        recentMax = 10;
        tweetTime = new HashMap<Integer, Integer>();
        user = new HashMap<Integer, Node>();
    }
​
    // 初始化
    public void init(int userId) {
        user.put(userId, new Node());
    }
​
    public void postTweet(int userId, int tweetId) {
        if (!user.containsKey(userId)) {
            init(userId);
        }
        // 达到限制,剔除链表末尾元素
        if (user.get(userId).tweet.size() == recentMax) {
            user.get(userId).tweet.remove(recentMax - 1);
        }
        user.get(userId).tweet.addFirst(tweetId);
        tweetTime.put(tweetId, ++time);
    }
    
    public List<Integer> getNewsFeed(int userId) {
        LinkedList<Integer> ans = new LinkedList<Integer>();
        for (int it : user.getOrDefault(userId, new Node()).tweet) {
            ans.addLast(it);
        }
        for (int followeeId : user.getOrDefault(userId, new Node()).followee) {
            if (followeeId == userId) { // 可能出现自己关注自己的情况
                continue;
            }
            LinkedList<Integer> res = new LinkedList<Integer>();
            int tweetSize = user.get(followeeId).tweet.size();
            Iterator<Integer> it = user.get(followeeId).tweet.iterator();
            int i = 0;
            int j = 0;
            int curr = -1;
            // 线性归并
            if (j < tweetSize) {
                curr = it.next();
                while (i < ans.size() && j < tweetSize) {
                    if (tweetTime.get(curr) > tweetTime.get(ans.get(i))) {
                        res.addLast(curr);
                        ++j;
                        if (it.hasNext()) {
                            curr = it.next();
                        }
                    } else {
                        res.addLast(ans.get(i));
                        ++i;
                    }
                    // 已经找到这两个链表合起来后最近的 recentMax 条推文
                    if (res.size() == recentMax) {
                        break;
                    }
                }
            }
            for (; i < ans.size() && res.size() < recentMax; ++i) {
                res.addLast(ans.get(i));
            }
            if (j < tweetSize && res.size() < recentMax) {
                res.addLast(curr);
                for (; it.hasNext() && res.size() < recentMax;) {
                    res.addLast(it.next());
                }
            }
            ans = new LinkedList<Integer>(res);
        }
        return ans;
    }
    
    public void follow(int followerId, int followeeId) {
        if (!user.containsKey(followerId)) {
            init(followerId);
        }
        if (!user.containsKey(followeeId)) {
            init(followeeId);
        }
        user.get(followerId).followee.add(followeeId);
    }
    
    public void unfollow(int followerId, int followeeId) {
        user.getOrDefault(followerId, new Node()).followee.remove(followeeId);
    }
}

2 设计循环双端队列

LeetCode641.设计实现双端队列。

实现 MyCircularDeque 类:

MyCircularDeque(int k) :构造函数,双端队列最大为 k 。
boolean insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true ,否则返回 false 。
boolean insertLast() :将一个元素添加到双端队列尾部。如果操作成功返回 true ,否则返回 false 。
boolean deleteFront() :从双端队列头部删除一个元素。 如果操作成功返回 true ,否则返回 false 。
boolean deleteLast() :从双端队列尾部删除一个元素。如果操作成功返回 true ,否则返回 false 。
int getFront() ):从双端队列头部获得一个元素。如果双端队列为空,返回 -1 。
int getRear() :获得双端队列的最后一个元素。 如果双端队列为空,返回 -1 。
boolean isEmpty() :若双端队列为空,则返回 true ,否则返回 false  。
boolean isFull() :若双端队列满了,则返回 true ,否则返回 false 。

题目要求简洁明了,也不算很难。不过在实现的时候,我们要注意代码的规范和是否严谨。基于数组和链表都可以实现双端队列,但是数组实现的话,处理起来要麻烦很多,还要解决内存泄露的问题,具体我们在《队栈Hash》一章介绍过。我们同样可以使用双向链表来模拟双端队列,实现双端队列队首与队尾元素的添加、删除。双向链表实现比较简单,双向链表支持 O(1)O(1) 时间复杂度内在指定节点的前后插入新的节点或者删除新的节点。

循环双端队列的属性如下:

head:队列的头节点;
tail:队列的尾节点
capacity:队列的容量大小。
size:队列当前的元素数量。

实现循环双端队列的接口方法,要点如下:

  • MyCircularDeque(int k):初始化队列,同时初始化队列元素数量size 为 0。head,tail 初始化为空。

  • insertFront(int value):队列未满时,在队首头结点head 之前插入一个新的节点,并更新head,并更新 size。

  • insertLast(int value):队列未满时,在队w尾节点tail 之后插入一个新的节点,并更新tail,并更新size。

  • deleteFront():队列不为空时,删除头结点head,并更新head 为head 的后一个节点,并更新size。

  • deleteLast():队列不为空时,删除尾结点tail,并更新tail 为tail 的前一个节点,并更新size。

  • getFront():返回队首节点指向的值,需要检测队列是否为空。

  • getRear():返回队尾节点指向的值,需要检测队列是否为空。

  • isEmpty():检测当前 size 是否为 0。

  • isFull():检测当前size 是否为capacity。

代码实现如下:

class MyCircularDeque {
    private class DLinkListNode {
        int val;
        DLinkListNode prev, next;
​
        DLinkListNode(int val) {
            this.val = val;
        }
    }
​
    private DLinkListNode head, tail;
    private int capacity;
    private int size;
​
    public MyCircularDeque(int k) {
        capacity = k;
        size = 0;
    }
​
    public boolean insertFront(int value) {
        if (size == capacity) {
            return false;
        }
        DLinkListNode node = new DLinkListNode(value);
        if (size == 0) {
            head = tail = node;
        } else {
            node.next = head;
            head.prev = node;
            head = node;
        }
        size++;
        return true;
    }
​
    public boolean insertLast(int value) {
        if (size == capacity) {
            return false;
        }
        DLinkListNode node = new DLinkListNode(value);
        if (size == 0) {
            head = tail = node;
        } else {
            tail.next = node;
            node.prev = tail;
            tail = node;
        }
        size++;
        return true;
    }
​
    public boolean deleteFront() {
        if (size == 0) {
            return false;
        }
        head = head.next;
        if (head != null) {
            head.prev = null;
        }
        size--;
        return true;
    }
​
    public boolean deleteLast() {
        if (size == 0) {
            return false;
        }
        tail = tail.prev;
        if (tail != null) {
            tail.next = null;
        }
        size--;
        return true;
    }
​
    public int getFront() {
        if (size == 0) {
            return -1;
        }
        return head.val;
    }
​
    public int getRear() {
        if (size == 0) {
            return -1;
        }
        return tail.val;
    }
​
    public boolean isEmpty() {
        return size == 0;
    }
​
    public boolean isFull() {
        return size == capacity;
    }
}

同样的题目可以参考LeetCode707题。

3 设计浏览器记录

LeetCode1472.你有一个只支持单个标签页的 浏览器 ,最开始你浏览的网页是 homepage ,你可以访问其他的网站 url ,也可以在浏览历史中后退 steps 步或前进 steps 步。

请你实现 BrowserHistory 类:

  • BrowserHistory(string homepage) ,用 homepage 初始化浏览器类。

  • void visit(string url) 从当前页跳转访问 url 对应的页面 。执行此操作会把浏览历史前进的记录全部删除。

  • string back(int steps) 在浏览历史中后退 steps 步。如果你只能在浏览历史中后退至多 x 步且 steps > x ,那么你只后退 x 步。请返回后退 至多 steps 步以后的 url 。

  • string forward(int steps) 在浏览历史中前进 steps 步。如果你只能在浏览历史中前进至多 x 步且 steps > x ,那么你只前进 x 步。请返回前进至多steps步以后的url 。

class BrowserHistory 
{
    List<String> a = new ArrayList<>();
    int i;
​
    public BrowserHistory(String homepage) 
    {
        a.add(homepage);
        i = 0;
    }
    
    public void visit(String url) 
    {
        a.subList(i + 1, a.size()).clear();
        a.add(url);
        i ++;
    }
    
    public String back(int steps) 
    {
        i = Math.max(0, i - steps);
        return a.get(i);
    }
    
    public String forward(int steps) 
    {
        i = Math.min(i + steps, a.size() - 1);
        return a.get(i);
    }
}

4 表达式求值

表达式计算是编译原理、自然语言处理、文本分析等领域非常重要的问题,我们这里看一个相对中等的问题,逆波兰表达式。LeetCode150.根据 逆波兰表示法,求表达式的值。说明:

  1. 有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

  2. 注意 两个整数之间的除法只保留整数部分。

  3. 可以保证给定的逆波兰表达式总是有效的。也即表达式总会得出有效数值且不存在除数为 0 的情况。

示例1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
​
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

本题看起来很复杂,但其实很简单,我们先理解一下什么是表达式,表达式就是小学里学的类似((2 + 1) * 3)这样的式子,根据不同的记法,有前缀、中缀和后缀三种方式,其区别在于运算符相对于操作数的位置,前缀表达式的运算符位于操作数之前,中缀和后缀同理,如下图,其实这就对应了树的前中后三种遍历方式。

 对应的三种表达式就是:

中缀表达式:1 + (2 + 3) × 4 - 5
前缀表达式:- + 1 × + 2 3 4 5
后缀表达式:1 2 3 + 4 × + 5 -

从上面的例子我们也可以看到 中缀表达式是最像人话的,它是一种通用的算术或逻辑公式表示方法,操作符以中缀形式处于操作数的中间。 虽然人的大脑很容易理解与分析中缀表达式,但对计算机来说中缀表达式却是很复杂的,因此计算表达式的值时,通常需要先将中缀表达式转换为前缀或后缀表达式再进行求值。 前缀表达式的运算符位于两个相应操作数之前,前缀表达式又被称为前缀记法或波兰式。而后缀式就是逆波兰式,知道这些就行了。

观察后缀表达式可以发现,其特点就是数字先保存下来,然后遇到符号就计算,例如”1 2 3 +“,遇到 +号就将2+3加起来变成5再继续其他操作,直到最后完成。

如果用栈来解释就是遇见数字即进栈,遇见运算符,则取出栈中最上面的两个元素进行计算,最后将运算结果入栈。实现代码其实很容易:

public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for(String token : tokens){
            if(!Character.isDigit(token.charAt(0)) && token.length() == 1){
                /**
                 * 运算符,从栈中取出两个数进行运算!
                 */
                int b = stack.pop();
                int a = stack.pop();
                switch (token){
                    /**
                     * 根据运算符的种类进行计算
                     * 将结果直接入栈!
                     */
                    case "+":stack.push(a + b);break;
                    case "-":stack.push(a - b);break;
                    case "*":stack.push(a * b);break;
                    case "/":stack.push(a / b);break;
                }
            }else {
                /**
                 * 整数直接入栈!
                 */
                stack.push(Integer.parseInt(token));
            }
        }
        return stack.pop();
    }

5 设计计算器

计算器也是非常常见的问题,我们看一个中等问题。LeetCode227.给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。整数除法仅保留整数部分。

你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

示例:

输入:s = "3+2*2"
输出:7

解决运算器问题,最好的工具就是栈。由于乘除优先于加减计算,因此不妨考虑先进行所有乘除运算,并将这些乘除运算后的整数值放回原表达式的相应位置,则随后整个表达式的值,就等于一系列整数加减后的值。

基于此,我们可以用一个栈,保存这些(进行乘除运算后的)整数的值。对于加减号后的数字,将其直接压入栈中;对于乘除号后的数字,可以直接与栈顶元素计算,并替换栈顶元素为计算后的结果。

具体来说,遍历字符串 ss,并用变量preSign 记录每个数字之前的运算符,对于第一个数字,其之前的运算符视为加号。每次遍历到数字末尾时,根据 preSign 来决定计算方式:

加号:将数字压入栈; 减号:将数字的相反数压入栈; 乘除号:计算数字与栈顶元素,并将栈顶元素替换为计算结果。 代码实现中,若读到一个运算符,或者遍历到字符串末尾,即认为是遍历到了数字末尾。处理完该数字后,更新 preSign 为当前遍历的字符。

遍历完字符串 s 后,将栈中元素累加,即为该字符串表达式的值。

class Solution {
    public int calculate(String s) {
        Deque<Integer> stack = new ArrayDeque<Integer>();
        char preSign = '+';
        int num = 0;
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            if (Character.isDigit(s.charAt(i))) {
                num = num * 10 + s.charAt(i) - '0';
            }
            if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n - 1) {
                switch (preSign) {
                case '+':
                    stack.push(num);
                    break;
                case '-':
                    stack.push(-num);
                    break;
                case '*':
                    stack.push(stack.pop() * num);
                    break;
                default:
                    stack.push(stack.pop() / num);
                }
                preSign = s.charAt(i);
                num = 0;
            }
        }
        int ans = 0;
        while (!stack.isEmpty()) {
            ans += stack.pop();
        }
        return ans;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/32565.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【操作系统】2.1 进程与线程总结

2.1.1 操作系统之进程的定义、特征、组成、组织 2.1.1 操作系统之进程的定义、特征、组成、组织_StudyWinter的博客-CSDN博客 进程由程序段、数据段、进程控制块&#xff08;PCB&#xff09;三部分组成。 进程是进程实体的运行过程&#xff0c;是系统进行资源分配和资源调度的…

隆化的大米排之首 国稻种芯·中国水稻节:河北承德十大特产

隆化的大米排之首 国稻种芯中国水稻节&#xff1a;河北承德十大特产 中新网河北新闻10月8日电 (张桂芹 王思明) 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农业大健康大会报道&#xf…

python基础项目实战-俄罗斯方块

一、俄罗斯方块游戏设计分析: 俄罗斯方块是一款风靡全球,从一开始到现在都一直经久不衰的电脑、手机、掌上游戏机产品,是一款游戏规则简单,但又不缺乏乐趣的简单经典小游戏,上手容易,适用范围广泛,人所共知。俄罗斯方块游戏基本规则是油4个小方块组成的7种不同的规则图形…

DevOps持续部署的关键要素

当有版本通过持续集成流水线进行构建之后&#xff0c;就可以将其部署至某个具体的环境&#xff0c;这就需要自动化部署技术&#xff0c;将这个自动化部署和持续集成流水线连接起来&#xff0c;就可实现持续部署。如图1所示&#xff0c;实现持续部署的前提是至少拥有一条完整的自…

mipi phy理解

MIPI 接口广泛用于摄像头,和显示部分;PHY 属于 MIPI 接口的最底层部分,也就是物理层,直接涉及到物理连线,信号传输等等,要搞清楚 MIPI 的数据传送,那么从 PHY 入手了解,是一个不错的选择;在 PHY 上,只负责定义数据物理层的收发标准; MIPI 的 PHY 分为了 3 种: D-P…

BartForConditionalGeneration的使用细节

输入到 BartForConditionalGeneration 类的各个参数是什么意思&#xff1f; decoder_input_ids 是必须要以 <s> 开头的。这个参数可以自己生成然后传入到模型中&#xff0c;也可以交由代码自己生成&#xff08;一般会根据label右移一位再补0&#xff09; case 1&#x…

ImmunoChemistry艾美捷细胞内GSH测定试剂盒方案

用ImmunoChemistry艾美捷细胞内GSH测定评估细胞内谷胱甘肽水平的变化。这种全细胞谷胱甘肽测定采用专有的硫醇敏感染料ThioBright™ 绿色&#xff0c;以监测游离形式谷胱甘肽&#xff08;即GSH&#xff09;浓度的相对变化。通过流式细胞术分析荧光信号。 谷-胱-甘肽&#xff0c…

​DPDK 高效原因初探

Linux处理Packets主逻辑 系统接受数据包的过程 当网卡收到第一个包时候,通过DMA把这个包发送给接受队列(rx)系统通过中断的方式通知新数据包的到来,同时也需要把数据包传递给内核的buffer(每个包一个buffer,sk_buff struct).一个数据包到来会触发多次的中断&#xff0c;内核处…

.net-----语言集成查询LINQ

语言集成查询LINQ前言相关语言要素初始值设定项匿名类型相关语言要素Lambda表达式扩展方法LINQ的概念和基本操作集成语言查询LINQLINQ查询操作标准查询运算符数据排序数据筛选数据投影数据分组联接运算数据分区限定运算聚合运算集合运算生成运算元素操作串联运算相等运算数据类…

【新知实验室 基于WEB的实时音视频(TRTC)案例搭建】

文章目录1. 实时音视频&#xff08;TRTC&#xff09;1.1 实时音视频(TRTC)概述1.2 实时音视频(TRTC)功能集成1.2.1 含 UI 组件集成方案1.2.2 无 UI 组件集成方案1.3 实时音视频(TRTC)架构2. 使用体验2.1 注册腾讯云2.2 申请实时音视频(TRTC)应用2.3 下载SDK的Demo源码2.4 获取 …

前后端结合解决Excel海量公式计算的性能问题

背景 在数据密集的业务领域&#xff0c;尤其是金融&#xff0c;保险&#xff0c;税务等行业中&#xff0c;经常需要利用Excel模型&#xff0c;来对业务进行分析和处理。例如&#xff1a; 1.金融投资&#xff1a; 根据模型进行估值计算&#xff0c;并对投资风险进行评估&#x…

降本增效这九个月,爱奇艺从“穿越火线”,到“冷静增长”

在互联网行业一致宣称降本、提质、增效的小周期里&#xff0c;爱奇艺已经把这个趋势彻底吃透&#xff0c;展现出成熟的一面。 11月22日美股盘前&#xff0c;爱奇艺发布了2022年第三季度业绩&#xff0c;连续三个季度运营盈利&#xff0c;而且当季净增会员数超千万&#xff0c;…

iwebsec靶场 数据库漏洞通关2-Redis数据库漏洞

iwebsec靶场的redis漏洞为未授权漏洞&#xff0c;如下所示。 一、Redis未授权漏洞原因 那么这个未授权漏洞的原理是什么呢&#xff1f;Redis 默认情况下&#xff0c;会绑定在 0.0.0.0:6379&#xff0c;如果没有进行采用相关的策略&#xff0c;比如添加防火墙规则避免其他非信任…

PreScan快速入门到精通第三十九讲基于车道线识别传感器的车道保持辅助算法Demo讲解

车道保持辅助系统介绍: 什么是车道保持辅助系统? 疲劳和分心是无意中偏离车辆行驶车道线的最常见原因。车道保持辅助系统主动帮助驾驶者将其车辆保持在车道内,避免或者降低事故的发生。 车道保持辅助系统使用一个前置的摄像头,一般安装在车内后视镜附近,用来检测车辆前方…

项目风险管理十大黄金法则!高质量项目管理必杀技!

随着新冠疫情的发生&#xff0c;在这个VUCA时代越来越多企业开始重视风险管理。其实项目风险管理的好处是巨大的&#xff0c;如果您以积极的方式处理不确定的项目事件&#xff0c;则可能赚很多钱或提前拿到现金流。结果是您可以最大程度地减少项目威胁的影响并抓住发生的机会。…

野火FPGA强化(1):串口

文章目录第31讲&#xff1a;串口RS232串口数据接收模块&#xff1a;Uart_rx串口数据发送模块&#xff1a;Uart_tx底层模块&#xff1a;rs232第32讲&#xff1a;使用SignalTap II嵌入式逻辑分析仪在线调试第33讲&#xff1a;串口RS485key_filterwater_ledbreath_ledled_ctrluart…

Linux 内存之vmstat

文章目录前言一、vmstat简介1.1 processes1.2 memory1.3 block IO1.4 System1.5 cpu activity二、使用步骤2.1 vmstat -a2.2 vmstat -f2.3 vmstat -m2.4 vmstat -s2.5 vmstat -d/D三、vmstat 数据来源参考资料前言 NAMEvmstat - Report virtual memory statisticsvmstat 报告有…

Gang Scheduling Performance Benefits for Fine-Grain Synchronization

Gang Scheduling Performance Benefits for Fine-Grain Synchronization 题目 什么是 gang secheduling &#xff1f; 组调度&#xff0c;要么一组全部执行&#xff0c;要么都不执行什么是 fine-grain 同步 &#xff1f;细粒度同步 CSDN 博客 摘要 gang scheduling, where …

SSM框架的基本整合

整合SSM框架要做哪些事情&#xff1a; SpringMVC: pom web.xml a. 前端调度器servlet b. 编码过滤器filter c. 支持rest的过滤器springmvc.xml a. 扫描controller包 b. 添加 c. 视图解析器 d. 静态资源解析添加控制器类… Spring:web.xml a. 监听器&#xff08;在启动web容器时…

Qt QHeaderView 添加复选框

有两种方法&#xff1a; 1. 重载paintSection 主要是重载paintSection和mousePressEvent这两个函数 headview.h #ifndef HEADERVIEW_H #define HEADERVIEW_H#include <QObject> #include <QHeaderView> #include <QPainter> #include <QCheckBox> …