• 2023-10-5一些笔试题(1)


    1、有一个web服务器,cpu是8核i7,磁盘数1,那么数据库连接池的连接数的合理配置应该为()左右。
    合理配置数据库连接池的连接数取决于多个因素,包括预计的并发请求量、数据库的负载情况、应用程序的需求等。虽然CPU核心数和磁盘数量可以提供一些指导,但它们并不是确定连接池连接数的唯一因素。

    一般来说,数据库连接池的连接数应该根据以下几个考虑因素进行配置:

    预计的并发请求数:根据应用程序的预计并发用户数、请求数量和处理时间,估计出并发请求的数量。连接池的连接数应该能够支持并发请求的处理需求。

    数据库负载情况:考虑数据库的性能和负载情况。如果数据库经常处于高负载状态,可以适当增加连接池的连接数以提高并发处理能力。然而,过多的连接数可能会导致数据库性能下降,因此需要在性能和资源消耗之间进行权衡。

    应用程序需求:考虑应用程序的特点和需求。某些应用程序可能需要更多的数据库连接,例如并发处理大量数据库操作的应用程序,而其他应用程序可能需要较少的连接。

    基于上述因素,给出一个具体的连接数是很困难的。通常建议开始时使用一个较小的连接池,然后在实际运行中进行监测和调整。可以观察数据库的性能指标、应用程序的响应时间等,逐步增加或减少连接数,以找到最优的配置。

    总的来说,根据您提供的信息,一个合理的起点可能是在连接池中使用大约 8 到 16 个连接。然而,这仅仅是一个初始估计,具体的连接数需要根据实际情况进行调整和优化。
    2、辗转相除法
    以下是使用 C++ 实现辗转相除法(欧几里得算法)的示例代码:

    #include 
    
    int gcd(int a, int b) {
        if (b == 0) {
            return a;
        }
        return gcd(b, a % b);
    }
    
    int main() {
        int a, b;
        std::cout << "Enter two numbers: ";
        std::cin >> a >> b;
        int result = gcd(a, b);
        std::cout << "GCD: " << result << std::endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这个示例中,gcd 函数使用递归的方式实现辗转相除法。函数接受两个整数参数 ab,并返回它们的最大公约数(GCD)。当 b 为 0 时,递归终止,返回 a。否则,递归调用 gcd 函数,将 ba 除以 b 的余数作为新的参数传递给函数。

    main 函数中,用户被要求输入两个整数,并调用 gcd 函数计算它们的最大公约数,最后将结果打印输出。

    请注意,在实际应用中,可能需要添加输入验证和错误处理的逻辑,以确保输入的是有效的整数值,并处理除数为 0 的情况。以上代码仅提供了基本的实现示例。

    以下是使用 while 循环实现辗转相除法的示例代码:

    #include 
    
    int gcd(int a, int b) {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
    
    int main() {
        int a, b;
        std::cout << "Enter two numbers: ";
        std::cin >> a >> b;
        int result = gcd(a, b);
        std::cout << "GCD: " << result << std::endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这个示例中,gcd 函数使用 while 循环实现辗转相除法。循环条件为 b != 0,当 b 不等于 0 时,继续执行循环体。

    在循环体内,首先将 b 的值保存在临时变量 temp 中,然后将 b 更新为 a 除以 b 的余数,将 a 更新为 temp(即原先的 b 值)。这样,每次循环都会将较大数更新为较小数,将较小数更新为两数之差的余数。

    b 等于 0 时,循环终止,此时 a 的值即为最大公约数(GCD)。

    main 函数中,用户被要求输入两个整数,并调用 gcd 函数计算它们的最大公约数,最后将结果打印输出。

    同样地,需要注意在实际应用中添加输入验证和错误处理的逻辑,以及处理除数为 0 的情况。以上代码仅提供了基本的使用 while 循环实现辗转相除法的示例。

    3、后缀表达式、前缀表达式、中缀表达式之间的转换。

    后缀表达式、前缀表达式和中缀表达式是表示数学表达式的不同方式。它们之间可以进行相互转换。

    1. 中缀表达式(Infix Expression)是我们常见的数学表达式形式,其中运算符位于操作数之间。例如:(2 + 3) * 4。

    2. 后缀表达式(Postfix Expression),也称为逆波兰表达式(Reverse Polish Notation,RPN),是一种将运算符放在操作数之后的表达式。例如:2 3 + 4 * 表示 (2 + 3) * 4。

    3. 前缀表达式(Prefix Expression),也称为波兰表达式(Polish Notation),是一种将运算符放在操作数之前的表达式。例如:* + 2 3 4 表示 (2 + 3) * 4。

    下面介绍如何在中缀、后缀和前缀表达式之间进行转换:

    1. 中缀转后缀(中缀表达式转后缀表达式):

      • 创建一个空栈和一个空结果列表。
      • 从左到右遍历中缀表达式的每个字符。
      • 如果遇到操作数(数字),将其添加到结果列表中。
      • 如果遇到运算符,将其与栈顶的运算符进行比较:
        • 如果栈为空或栈顶是左括号"(",则将当前运算符压入栈。
        • 如果当前运算符的优先级高于栈顶运算符的优先级,将当前运算符压入栈。
        • 如果当前运算符的优先级低于或等于栈顶运算符的优先级,则将栈顶运算符弹出,并将其添加到结果列表中。重复此操作直到栈顶运算符优先级低于当前运算符或栈为空。
      • 如果遇到左括号"(",将其压入栈。
      • 如果遇到右括号")",则从栈中依次弹出运算符并添加到结果列表中,直到遇到左括号为止。将左括号弹出栈,但不添加到结果列表。
      • 遍历完中缀表达式后,将栈中剩余的运算符依次弹出并添加到结果列表中。
      • 结果列表即为转换后的后缀表达式。
    2. 后缀转中缀(后缀表达式转中缀表达式):

      • 创建一个空栈。
      • 从左到右遍历后缀表达式的每个字符。
      • 如果遇到操作数(数字),将其压入栈。
      • 如果遇到运算符,从栈中弹出两个操作数,构造一个中缀表达式:操作数1 + 运算符 + 操作数2,并将中缀表达式作为一个操作数压入栈。
      • 遍历完后缀表达式后,栈中剩下的唯一元素即为转换后的中缀表达式。
    3. 后缀转前缀(后缀表达式转前缀表达式):

      • 创建一个空栈。
      • 从左到右遍历后缀表达式的每个字符。
      • 如果遇到操作数(数字),将其压入栈。
      • 如果遇到运算符,从栈中弹出两个操作数,构造一个前缀表达式:运算符 + 操作数2 + 操作数1,并将前缀表达式作为一个操作数压入栈。
      • 遍历完后缀表达式后,栈中剩下的唯一元素即为转换后的前缀表达式。

    以上是基本的转换算法。下面是使用C++代码实现中缀转后缀的示例:

    #include 
    #include 
    #include 
    #include 
    
    std::unordered_map<char, int> precedence = {
        {'+', 1},
        {'-', 1},
        {'*', 2},
        {'/', 2},
        {'^', 3}
    };
    
    bool isOperator(char c) {
        return precedence.find(c) != precedence.end();
    }
    
    bool isHigherPrecedence(char op1, char op2) {
        return precedence[op1] > precedence[op2];
    }
    
    std::string infixToPostfix(const std::string& infix) {
        std::stack<char> operatorStack;
        std::string postfix;
    
        for (char c : infix) {
            if (isOperator(c)) {
                while (!operatorStack.empty() && operatorStack.top() != '(' && isHigherPrecedence(operatorStack.top(), c)) {
                    postfix += operatorStack.top();
                    operatorStack.pop();
                }
                operatorStack.push(c);
            } else if (c == '(') {
                operatorStack.push(c);
            } else if (c == ')') {
                while (!operatorStack.empty() && operatorStack.top() != '(') {
                    postfix += operatorStack.top();
                    operatorStack.pop();
                }
                if (!operatorStack.empty() && operatorStack.top() == '(') {
                    operatorStack.pop();
                }
            } else {
                postfix += c;
            }
        }
    
        while (!operatorStack.empty()) {
            postfix += operatorStack.top();
            operatorStack.pop();
        }
    
        return postfix;
    }
    
    int main() {
        std::string infix;
        std::cout << "Enter an infix expression: ";
        std::getline(std::cin, infix);
    
        std::string postfix = infixToPostfix(infix);
        std::cout << "Postfix: " << postfix << std::endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    该示例使用了一个 std::stack 数据结构来模拟操作符栈,std::unordered_map 用于存储运算符的优先级。isOperator 函数用于判断字符是否为运算符,isHigherPrecedence 函数用于比较两个运算符的优先级。

    infixToPostfix 函数接受一个中缀表达式作为输入,并返回转换后的后缀表达式。在函数中,遍历中缀表达式的每个字符,根据字符的类型执行相应的操作。如果遇到操作数,则直接添加到后缀表达式中。如果遇到运算符,则与操作符栈中的运算符进行比较,将优先级较低的运算符弹出并添加到后缀表达式中,然后将当前运算符压入栈。如果遇到左括号,则直接压入栈。如果遇到右括号,则将栈中的运算符弹出并添加到后缀表达式中,直到遇到匹配的左括号为止。

    最后,将栈中剩余的运算符依次弹出并添加到后缀表达式中,即得到转换后的后缀表达式。

    请注意,在实际应用中,可能需要添加更多的输入验证和错误处理的逻辑,以及处理运算符的结合性(左结合或右结合)等情况。以上代码仅提供了基本的中缀转后缀的示例。

    4、有关reduceBykey 和groupBykey 说法错误的是
    A groupByKey默认没有聚合函数
    B、reduceBykey先在本地进行merge操作
    C、reduceBykey更适合用在大数据量上
    D. groupbykey 先进行分区间聚合,然后再进行网络传输
    A、groupByKey默认没有聚合函数。

    这个说法是错误的。在Spark中,groupByKey操作会将具有相同键的所有键值对分组到一起,并返回一个键值对集合,其中每个键与其对应的值列表相关联。这意味着groupByKey操作会对具有相同键的值进行分组,但并没有对值进行聚合操作。因此,groupByKey操作返回的结果是一个键值对集合,其中每个键与其对应的值列表相关联,而没有进行聚合计算。

    选项A说法正确的是A。选项B、C和D都是正确的说法。

    5、内存对齐:已知基本类型char,int,float在 64位分别占1,4,4字节。在指定#pragma pack(4)的情下。
    struct s1{
    float b;
    char c[10];
    long C;
    long long *d;
    };
    占用多少字节的空间?

    6、使用基数排存对十进制正整数据店列43,231,96,190,239进行升序排序、低位优先,堆数为10。排序过 程中的中间序列为
    A、190,231,43,96,239
    B、 190,43,231,96,239
    C、231、239、43,190,96
    D、239,231,43,190, 96
    解释:
    使用基数排序对十进制正整数数据43、231、96、190、239进行升序排序,低位优先,基数为10。

    基数排序的过程中的中间序列为:

    C、231、239、43,190,96

    在低位优先的基数排序中,首先按个位进行排序,得到序列:190,231,43,96,239。然后按十位排序,得到序列:231,239,43,190,96。然后按照百位排序,得到序列:43,96,190,231,239。

    所以,中间序列为C、231、239、43,190,96。
    基数排序学习

    7、在C++中,如果有 A *a,这是否会调用A的构造函数?
    在C++中,声明一个指向A类型对象的指针A *a,并不会调用A的构造函数。指针的声明只是为了存储一个指向A类型对象的地址,并没有创建对象实例。

    要创建A类型的对象并调用其构造函数,需要使用关键字new来动态分配内存,并显式调用A的构造函数。例如,可以使用以下方式创建A类型的对象并将其地址分配给指针a:

    A *a = new A();  // 调用A的默认构造函数
    
    • 1

    在这个例子中,new A()会调用A的构造函数来创建一个A类型的对象,并返回指向该对象的指针。然后,将该指针赋值给指针变量a。

    需要注意的是,使用new分配的内存需要手动释放,以避免内存泄漏。可以使用delete操作符来释放通过new分配的内存,例如:

    delete a;  // 释放a指向的内存空间
    
    • 1

    这将调用A的析构函数,并释放通过new分配的内存。

    8、对于下面的代码,下面选项描述正确的是:

    #include
    #include
    #include
    void modifyString(char* str){
        str =(char*) malloc (10*sizeof(char));
        strcpy(str, "Hello");
    }
    int main(){
        char * str = "World";
        modifyString(str);
        printf("%s\n", str);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    A.编译错误
    B,运行时错误
    C、运行输出:Hello
    D.、运行结果为输出: World

    解释:
    选项D描述正确,运行结果为输出: World。

    在给定的代码中,首先在main函数中定义了一个char*类型的指针变量str,并将其指向字符串常量"World"。

    然后,调用modifyString函数,并将str作为参数传递进去。在modifyString函数内部,通过malloc动态分配了一个长度为10的字符数组,并将其地址赋值给了str,然后使用strcpy函数将字符串"Hello"复制到了这个新分配的内存空间中。

    然而,需要注意的是,modifyString函数中的str是一个局部变量,对它的修改并不会影响到main函数中的str指针。所以,虽然在modifyString函数内部成功修改了str指向的内存空间,但这个修改对于main函数中的str是不可见的。

    因此,在main函数中打印str时,仍然会输出原始的字符串"World",而不是在modifyString函数中修改后的"Hello"。所以,选项D描述正确,运行结果为输出: World。

    9、根据表达式 double(((*fp)(int))[5])(char)关于fp类型描述正确的是:
    A、fp是指向函数的指针
    B、fp是一个数组
    C、fp是指向数组的指针
    D. 以上说法都不对

    解释:
    根据表达式double(*(*(*fp)(int))[5])(char),关于fp类型的描述正确的是:

    A、fp是指向函数的指针

    根据表达式的解读:

    • fpfp是一个标识符,表示一个变量名或指针名。
    • (*fp)(*fp)表示fp是一个指针,指向某种类型的对象。
    • (*(*fp)(int))(*(*fp)(int))表示fp是一个指针,指向一个函数。
    • (*(*(*fp)(int))[5])(*(*(*fp)(int))[5])表示fp是一个指针,指向一个返回类型为指向长度为5的数组的函数。
    • (*(*(*fp)(int))[5])(char)(*(*(*fp)(int))[5])(char)表示fp是一个指针,指向一个返回类型为double的函数,该函数接受一个int参数和一个char参数。

    综上所述,根据表达式的解读,选项A描述正确,fp是指向函数的指针。

  • 相关阅读:
    基于springboot的实习管理系统设计与实现-计算机毕业设计源码
    Linux中19个MySQL数据库管理命令
    Linux——常用命令
    【图像检测-裂缝识别】基于计算机实现断裂裂缝识别拼接附matlab代码
    关于MySQL日期函数你不知道的用法
    学习笔记1--自动驾驶定位技术概述
    C#版 iText7——画发票PDF(完整)
    关于docker的xuexi
    《Effective C++》条款14
    Mysql第二篇---InnoDB数据存储结构
  • 原文地址:https://blog.csdn.net/weixin_51187533/article/details/133580260