knowledge_map/出题.md
2021-05-18 16:46:32 +08:00

11 KiB
Raw Blame History

# 面试题

1

题目

H2O生成

现在有两种线程,氧 oxygen 和氢 hydrogen你的目标是组织这两种线程来产生水分子。

存在一个屏障barrier使得每个线程必须等候直到一个完整水分子能够被产生出来。

氢和氧线程会被分别给予 releaseHydrogen 和 releaseOxygen 方法来允许它们突破屏障。

这些线程应该三三成组突破屏障并能立即组合产生一个水分子。

你必须保证产生一个水分子所需线程的结合必须发生在下一个水分子产生之前。

换句话说:

如果一个氧线程到达屏障时没有氢线程到达,它必须等候直到两个氢线程到达。 如果一个氢线程到达屏障时没有其它线程到达,它必须等候直到一个氧线程和另一个氢线程到达。 书写满足这些限制条件的氢、氧线程同步代码。

答案

class H2O {
public:
    int cntO;// 氧气的计数
    int cntH;// 氢气的计数
    mutex m;
    condition_variable cv;

    H2O() {
        cntO = 0;
        cntH = 0;
    }

    void hydrogen(function<void()> releaseHydrogen) {
        unique_lock<mutex> l(m);
        cv.wait(l, [this]()
        {
            // 氢气最大是2
            return this->cntH < 2;
        });
        // releaseHydrogen() outputs "H". Do not change or remove this line.
        releaseHydrogen();
        ++cntH;
        // 已经构成 H2O ,重置计数器
        if (cntH + cntO == 3)
        {
            cntH = 0;
            cntO = 0;
        }
        cv.notify_one();
    }

    void oxygen(function<void()> releaseOxygen) {
        unique_lock<mutex> l(m);
        cv.wait(l, [this]()
        {
            // 氧气最大是1
            return this->cntO < 1;
        });
        // releaseOxygen() outputs "O". Do not change or remove this line.
        releaseOxygen();
        ++cntO;
        // 已经构成 H2O ,重置计数器
        if (cntH + cntO == 3)
        {
            cntH = 0;
            cntO = 0;
        }
        cv.notify_one();
    }
};

2

题目

用图画出无人机姿态控制的串级反馈控制系统

答案

image-20210513201926139

进程间通信消息队列

3

题目

请你说一下多线程,线程同步的几种方式

答案

进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;

线程是进程的子任务是CPU调度和分派的基本单位用于保证程序的实时性实现进程内部的并发线程是操作系统可识别的最小执行和调度单位。每个线程都独自占用一个虚拟处理器独自的寄存器组指令计数器和处理器状态。每个线程完成不同的任务但是共享同一地址空间也就是同样的动态内存映射文件目标代码等等打开的文件队列和其他内核资源。

线程间通信的方式:

1、临界区

通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;

2、互斥量 Synchronized/Lock

采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问

3、信号量 Semphare

为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。

4、事件(信号)Wait/Notify

通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作

4

题目

编写一个函数作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2移位后应该是“hiabcdefg” 函数头是这样的: //pStr是指向以'\0'结尾的字符串的指针 //steps是要求移动的n

void LoopMove ( char * pStr, int steps )
{
 //请填充...
}

答案

void LoopMove ( char *pStr, int steps )
{
 int n = strlen( pStr ) - steps;
 char tmp[MAX_LEN]; 
 strcpy ( tmp, pStr + n ); 
 strcpy ( tmp + steps, pStr); 
 *( tmp + strlen ( pStr ) ) = '\0';
 strcpy( pStr, tmp );
}

5

题目

把M个同样的苹果放在N个同样的盘子里允许有的盘子空着不放问共有多少种不同的分法用K表示511和151 是同一种分法。

输入描述

第一行是测试数据

输出描述

对输入的每组数据M和N用一行输出相应的K。

输入例子

1
7 3

输出例子

8

答案

#include<stdio.h>
 
int fun(int m,int n)  //m个苹果放在n个盘子***有几种方法
{
    if(m==0||n==1)  //因为我们总是让m>=n来求解的所以m-n>=0,所以让m=0时候结束如果改为m=1
        return 1;    //则可能出现m-n=0的情况从而不能得到正确解   
    if(n>m)
        return fun(m,m);
    else
        return fun(m,n-1)+fun(m-n,n);
}
 
int main()
{
    int T,m,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&m,&n);
        printf("%d\n",fun(m,n));
    }
}

6

题目

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1

输入:

s = "3[a]2[bc]"

输出:

aaabcbc

输入:

s = "3[a2[c]]"

输出:

accaccacc

初始代码

class Solution {
public:
    string decodeString(string s) {

    }
};

答案

class Solution {
public:
    string getDigits(string &s, size_t &ptr) {
        string ret = "";
        while (isdigit(s[ptr])) {
            ret.push_back(s[ptr++]);
        }
        return ret;
    }

    string getString(vector <string> &v) {
        string ret;
        for (const auto &s: v) {
            ret += s;
        }
        return ret;
    }

    string decodeString(string s) {
        vector <string> stk;
        size_t ptr = 0;

        while (ptr < s.size()) {
            char cur = s[ptr];
            if (isdigit(cur)) {
                // 获取一个数字并进栈
                string digits = getDigits(s, ptr);
                stk.push_back(digits);
            } else if (isalpha(cur) || cur == '[') {
                // 获取一个字母并进栈
                stk.push_back(string(1, s[ptr++])); 
            } else {
                ++ptr;
                vector <string> sub;
                while (stk.back() != "[") {
                    sub.push_back(stk.back());
                    stk.pop_back();
                }
                reverse(sub.begin(), sub.end());
                // 左括号出栈
                stk.pop_back();
                // 此时栈顶为当前 sub 对应的字符串应该出现的次数
                int repTime = stoi(stk.back()); 
                stk.pop_back();
                string t, o = getString(sub);
                // 构造字符串
                while (repTime--) t += o; 
                // 将构造好的字符串入栈
                stk.push_back(t);
            }
        }

        return getString(stk);
    }
};

7

题目

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

输入:

matrix = [[1,2,3],[4,5,6],[7,8,9]]

输出:

[1,2,3,6,9,8,7,4,5]

输入:

matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]

输出:

[1,2,3,4,8,12,11,10,9,5,6,7]

初始代码

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {

    }
};

答案

class Solution {
private:
    static constexpr int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.size() == 0 || matrix[0].size() == 0) {
            return {};
        }
        
        int rows = matrix.size(), columns = matrix[0].size();
        vector<vector<bool>> visited(rows, vector<bool>(columns));
        int total = rows * columns;
        vector<int> order(total);

        int row = 0, column = 0;
        int directionIndex = 0;
        for (int i = 0; i < total; i++) {
            order[i] = matrix[row][column];
            visited[row][column] = true;
            int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
            if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) {
                directionIndex = (directionIndex + 1) % 4;
            }
            row += directions[directionIndex][0];
            column += directions[directionIndex][1];
        }
        return order;
    }
};

8

题目

static的用法定义和用途

答案

1用static修饰局部变量使其变为静态存储方式(静态数据区),那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中。

2用static修饰全局变量使其只在本文件内部有效而其他文件不可连接或引用该变量。

3用static修饰函数对函数的连接方式产生影响使得函数只在本文件内部有效对其他文件是不可见的这一点在大工程中很重要很重要避免很多麻烦很常见。这样的函数又叫作静态函数。使用静态函数的好处是不用担心与其他文件的同名函数产生干扰另外也是对函数本身的一种保护机制。

9

题目

const的用法定义和用途

答案

const主要用来修饰变量、函数形参和类成员函数

1用const修饰常量定义时就初始化以后不能更改。

2用const修饰形参func(const int a){};该形参在函数里不能改变。

3用const修饰类成员函数该函数对成员变量只能进行只读操作就是const类成员函数是不能修改成员变量的数值的。

被const修饰的东西都受到强制保护可以预防意外的变动能提高程序的健壮性。

10

题目

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

img

输入m = 3, n = 7
输出28

初始代码

class Solution {
public:
    int uniquePaths(int m, int n) {

    }
};

答案

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> f(m, vector<int>(n));
        for (int i = 0; i < m; ++i) {
            f[i][0] = 1;
        }
        for (int j = 0; j < n; ++j) {
            f[0][j] = 1;
        }
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                f[i][j] = f[i - 1][j] + f[i][j - 1];
            }
        }
        return f[m - 1][n - 1];
    }
};