【腾讯】Java后端面经,范围极广|0306

  1. 括号匹配

    import java.util.Stack;
    
    public class BracketMatching {
        public static boolean isBracketMatching(String str) {
            Stack<Character> stack = new Stack<>(); // 创建一个栈用于存储左括号
    
            for (int i = 0; i < str.length(); i++) {
                char ch = str.charAt(i);
    
                if (ch == '(' || ch == '[' || ch == '{') { // 如果是左括号,则入栈
                    stack.push(ch);
                } else if (ch == ')' || ch == ']' || ch == '}') { // 如果是右括号
                    if (stack.isEmpty()) { // 如果栈为空,说明没有与之匹配的左括号,返回false
                        return false;
                    }
    
                    char top = stack.pop(); // 弹出栈顶元素
    
                    // 判断右括号与栈顶元素是否匹配
                    if ((ch == ')' && top != '(') || (ch == ']' && top != '[') || (ch == '}' && top != '{')) {
                        return false; // 不匹配,返回false
                    }
                }
            }
    
            return stack.isEmpty(); // 如果栈为空,说明所有括号都匹配成功,返回true;否则返回false
        }
    
        public static void main(String[] args) {
            String str1 = "((()))";
            String str2 = "([{}])";
            String str3 = "({[}])";
    
            System.out.println(isBracketMatching(str1));  // true
            System.out.println(isBracketMatching(str2));  // true
            System.out.println(isBracketMatching(str3));  // false
        }
    }
    

面经专栏直通车

面经专栏下载

  1. 遍历二叉搜索树

    // 定义二叉树节点类
    class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
    
        public TreeNode(int val) {
            this.val = val;
        }
    }
    
    public class BSTTraversal {
        // 中序遍历二叉搜索树
        public static void inorderTraversal(TreeNode root) {
            if (root == null) {
                return;
            }
    
            inorderTraversal(root.left); // 递归遍历左子树
            System.out.print(root.val + " "); // 输出当前节点的值
            inorderTraversal(root.right); // 递归遍历右子树
        }
    
        // 前序遍历二叉搜索树
        public static void preorderTraversal(TreeNode root) {
            if (root == null) {
                return;
            }
    
            System.out.print(root.val + " "); // 输出当前节点的值
            preorderTraversal(root.left); // 递归遍历左子树
            preorderTraversal(root.right); // 递归遍历右子树
        }
    
        // 后序遍历二叉搜索树
        public static void postorderTraversal(TreeNode root) {
            if (root == null) {
                return;
            }
    
            postorderTraversal(root.left); // 递归遍历左子树
            postorderTraversal(root.right); // 递归遍历右子树
            System.out.print(root.val + " "); // 输出当前节点的值
        }
    
        public static void main(String[] args) {
            // 构建一个二叉搜索树
            TreeNode root = new TreeNode(4);
            root.left = new TreeNode(2);
            root.right = new TreeNode(6);
            root.left.left = new TreeNode(1);
            root.left.right = new TreeNode(3);
            root.right.left = new TreeNode(5);
            root.right.right = new TreeNode(7);
    
            System.out.print("Inorder traversal: ");
            inorderTraversal(root); // 中序遍历
            System.out.println();
    
            System.out.print("Preorder traversal: ");
            preorderTraversal(root); // 前序遍历
            System.out.println();
    
            System.out.print("Postorder traversal: ");
            postorderTraversal(root); // 后序遍历
            System.out.println();
        }
    }
    
    
  2. 给定一组非负整数,求能拼接成的最大整数。

    import java.util.Arrays;
    import java.util.Comparator;
    
    public class MaxNumber {
        public static String largestNumber(int[] nums) {
            // 将整数数组转换为字符串数组
            String[] strNums = new String[nums.length];
            for (int i = 0; i < nums.length; i++) {
                strNums[i] = String.valueOf(nums[i]);
            }
    
            // 自定义比较器,用于比较两个字符串的拼接结果
            Comparator<String> comparator = new Comparator<String>() {
                @Override
                public int compare(String s1, String s2) {
                    String order1 = s1 + s2;
                    String order2 = s2 + s1;
                    return order2.compareTo(order1); // 降序排列
                }
            };
    
            // 使用自定义比较器对字符串数组进行排序
            Arrays.sort(strNums, comparator);
    
            // 如果排序后的数组第一个元素是0,则直接返回"0"
            if (strNums[0].equals("0")) {
                return "0";
            }
    
            // 拼接排序后的字符串数组
            StringBuilder sb = new StringBuilder();
            for (String str : strNums) {
                sb.append(str);
            }
    
            return sb.toString();
        }
    
        public static void main(String[] args) {
            int[] nums = {10, 2, 5, 9, 23};
            String result = largestNumber(nums);
            System.out.println("最大整数:" + result);
        }
    }
    
    
  3. Java语言和C语言有哪些区别?

    • 编程范式:C语言是一种过程式编程语言,而Java语言是一种面向对象编程语言。Java语言基于类和对象的概念,支持封装、继承和多态等面向对象的特性。
    • 内存管理:C语言需要手动管理内存,包括分配和释放内存。而Java语言使用垃圾回收机制,自动管理内存,开发者不需要显式地进行内存管理。
    • 平台依赖性:C语言是一种编译型语言,编写的程序需要针对特定的操作系统和硬件平台进行编译。而Java语言是一种跨平台的语言,通过Java虚拟机(JVM)实现了平台无关性,Java程序可以在不同的操作系统上运行。
    • 异常处理:C语言使用错误码来处理异常情况,开发者需要手动检查错误码并进行相应的处理。Java语言引入了异常处理机制,通过try-catch-finally语句块来捕获和处理异常,使得代码更加清晰和可读。
    • 标准库和生态系统:C语言的标准库相对较小,提供了基本的输入输出、字符串处理等功能。Java语言拥有丰富的标准库和第三方库,提供了大量的API和工具,涵盖了各种领域,方便开发者进行开发。
    • 编译和执行方式:C语言通过编译器将源代码编译成机器码,然后直接执行。而Java语言通过编译器将源代码编译成字节码,然后在JVM上执行字节码。
  4. RPC了解吗?

    RPC(Remote Procedure Call,远程过程调用)。

    RPC是一种通信协议,用于不同计算机之间的远程通信。它允许一个计算机程序调用另一个计算机上的函数或方法,就像调用本地函数一样,隐藏了底层网络通信的细节。

    在RPC中,通信的两端分别是客户端和服务器。客户端发起一个远程调用请求,服务器接收请求并执行相应的操作,然后将结果返回给客户端。RPC框架负责处理底层的网络通信、序列化和反序列化等细节,使得远程调用过程对开发者透明。

    常见的RPC框架包括Dubbo、gRPC、Thrift等。

    RPC的优点包括:

    • 简化分布式系统开发:RPC隐藏了底层通信细节,使得开发者可以像调用本地函数一样调用远程函数,简化了分布式系统的开发。
    • 提高系统性能:RPC可以将计算任务分布到不同的服务器上,充分利用资源,提高系统的并发性和性能。
    • 提高系统可扩展性:通过RPC,可以将系统拆分成多个服务,每个服务可以独立部署和扩展,提高系统的可扩展性。
  5. 介绍常用的Linux命令?

    1. ls:列出目录内容。
    2. cd:切换当前工作目录。
    3. pwd:显示当前工作目录的路径。
    4. mkdir:创建新目录。
    5. rm:删除文件或目录。
    6. cp:复制文件或目录。
    7. mv:移动文件或目录,也可用于重命名文件或目录。
    8. cat:显示文件内容。
    9. grep:在文件中搜索指定的模式。
    10. chmod:修改文件或目录的权限。
  6. Linux中如果出现了CPU满载,如何排查问题?

    当Linux系统出现CPU满载的情况时,可以按照以下步骤进行问题排查:

    1. 使用top命令查看系统当前的进程和CPU占用情况。按下Shift + P按CPU使用率排序进程,观察哪些进程占用了较高的CPU资源。
    2. 使用ps命令结合top命令的结果,获取更详细的进程信息。例如,ps auxf可以显示所有进程的详细信息,包括进程ID、CPU使用率等。
    3. 使用htop命令代替top命令,它提供了更友好的交互式界面,可以更方便地查看和管理进程。
    4. 使用pidstat命令监测特定进程的CPU使用情况。例如,pidstat -p <进程ID> -u 1可以每秒钟显示指定进程的CPU使用情况。
    5. 使用iotop命令查看磁盘I/O情况,因为高磁盘I/O可能导致CPU负载升高。
    6. 使用sar命令查看系统的历史性能数据,例如,sar -u可以显示CPU使用率的历史数据。
    7. 使用strace命令追踪进程的系统调用,以确定是否有异常的系统调用导致CPU满载。
    8. 检查系统日志文件,如/var/log/messages/var/log/syslog等,查找异常或错误信息。
    9. 如果有可疑的进程或服务,可以尝试重启它们,或者使用kill命令终止它们。
    10. 如果以上方法无法解决问题,可以考虑使用性能分析工具,如perfstracegdb等,对进程进行更深入的分析和调试。
  7. 介绍一下 堆 这个数据结构?

    堆(Heap)是一种特殊的树状数据结构,它满足以下两个主要性质:

    1. 堆是一个完全二叉树(Complete Binary Tree):除了最底层外,其他层的节点都是满的,且最底层的节点都靠左排列。
    2. 堆中每个节点的值都必须满足堆的性质:对于最大堆(Max Heap),父节点的值大于或等于其子节点的值;对于最小堆(Min Heap),父节点的值小于或等于其子节点的值。

    堆通常用于实现优先队列(Priority Queue)和堆排序(Heap Sort)等算法。

    堆可以分为最大堆和最小堆两种类型:

    • 最大堆:父节点的值大于或等于其子节点的值。根节点是堆中的最大值。
    • 最小堆:父节点的值小于或等于其子节点的值。根节点是堆中的最小值。

    堆的主要操作包括插入和删除操作:

    • 插入操作:将一个新元素插入到堆中,保持堆的性质不变。
    • 删除操作:删除堆中的根节点,并保持堆的性质不变。

    堆的插入和删除操作的时间复杂度都是O(log n),其中n是堆中元素的个数。

    堆的应用场景包括:

    • 优先队列:堆可以用于实现高效的优先队列,根据优先级获取最大或最小元素。
    • 堆排序:堆排序是一种基于堆的排序算法,具有稳定的时间复杂度O(n log n)。
    • 求Top K问题:通过维护一个大小为K的最小堆或最大堆,可以高效地求解Top K大或Top K小的元素。
    • 图算法:堆可以用于实现Dijkstra算法、Prim算法等图算法中的优先级队列。
  8. 动态链接库和静态链接库的区别?

    动态链接库(Dynamic Link Library,DLL)和静态链接库(Static Link Library)是两种常见的库文件形式,它们在链接和加载方式上有以下区别:

    1. 链接方式:
      • 静态链接库:在编译时将库的代码和应用程序的代码合并成一个可执行文件。链接器将库的代码复制到应用程序中,生成一个独立的可执行文件。应用程序与库的代码是静态链接的关系。
      • 动态链接库:在编译时只将库的引用信息记录在可执行文件中,而不将库的代码复制到应用程序中。在运行时,操作系统动态加载并链接库的代码,使得多个应用程序可以共享同一个库文件。
    2. 文件大小:
      • 静态链接库:库的代码被完整地复制到每个应用程序中,因此每个应用程序都包含了库的完整代码,导致应用程序的文件大小较大。
      • 动态链接库:多个应用程序可以共享同一个库文件,因此每个应用程序只需要记录库的引用信息,导致应用程序的文件大小较小。
    3. 内存占用:
      • 静态链接库:每个应用程序都包含了库的完整代码,因此在内存中会有多份库的代码副本,导致内存占用较高。
      • 动态链接库:多个应用程序共享同一个库文件,库的代码只需要加载一次,多个应用程序共享同一份库的代码,因此在内存中的占用较少。
    4. 更新和维护:
      • 静态链接库:如果库的代码发生更新,需要重新编译和链接应用程序,将新的库代码合并到应用程序中。
      • 动态链接库:如果库的代码发生更新,只需要替换库文件即可,不需要重新编译和链接应用程序。
  9. 介绍一下动态规划的思想?上楼梯问题了解吗?用了动态规划和暴力时间复杂度分别是多少?

    动态规划(Dynamic Programming)是一种解决多阶段决策问题的优化方法,它将问题分解为多个子问题,并通过保存子问题的解来避免重复计算,从而提高算法的效率。

    动态规划的基本思想是:将原问题分解为若干个子问题,先求解子问题的解,然后利用子问题的解构建原问题的解。通过保存子问题的解,避免重复计算,从而减少时间复杂度。

    上楼梯问题是动态规划中的一个经典问题。假设有n个台阶,每次可以走1步或2步,问有多少种不同的方式可以爬到楼顶。

    使用动态规划解决上楼梯问题的思路如下:

    1. 定义状态:设dp[i]表示爬到第i个台阶的不同方式数。
    2. 状态转移方程:由于每次可以走1步或2步,所以到达第i个台阶的方式数等于到达第i-1个台阶的方式数加上到达第i-2个台阶的方式数,即dp[i] = dp[i-1] + dp[i-2]。
    3. 初始条件:dp[0] = 1,dp[1] = 1,表示到达第0个台阶和第1个台阶的方式数都为1。
    4. 计算顺序:从小到大计算dp[i],直到计算到dp[n],即到达第n个台阶的方式数。

    使用暴力方法解决上楼梯问题的思路如下:

    1. 枚举所有可能的方式,对于每一种方式,计算到达楼顶的路径数。
    2. 递归地计算每一种方式的路径数,直到到达楼顶。
    3. 统计所有方式的路径数之和。

    动态规划解决上楼梯问题的时间复杂度为O(n),因为需要计算从第2个台阶到第n个台阶的方式数,每个台阶只需要计算一次。 暴力方法解决上楼梯问题的时间复杂度为O(2^n),因为需要枚举所有可能的方式,每个台阶都有两种选择。

  10. 什么是稳定排序?

    稳定排序(Stable Sorting)是指在排序算法中,如果两个元素的比较结果相等,那么它们在排序后的结果中的相对位置保持不变。换句话说,如果在排序前,元素A在元素B的前面,且A与B的值相等,那么在排序后,A仍然在B的前面。

    稳定排序的特点是能够保持相等元素的相对顺序,这在某些应用场景中非常重要。例如,当需要按照多个条件进行排序时,稳定排序可以确保先按照第一个条件排序,再按照第二个条件排序,而不会打乱第一个条件的顺序。

    常见的稳定排序算法有冒泡排序、插入排序、归并排序和基数排序等。这些算法在排序过程中会考虑元素的相对位置,以保持排序的稳定性。

    相对应的,非稳定排序(Unstable Sorting)是指在排序算法中,如果两个元素的比较结果相等,它们在排序后的结果中的相对位置可能会发生变化。非稳定排序算法在排序过程中可能会打乱相等元素的相对顺序。

    在选择排序算法时,如果需要保持相等元素的相对顺序,就应选择稳定排序算法。否则,如果相等元素的相对顺序不重要,可以选择非稳定排序算法,它们通常具有更高的性能。

  11. 图片分片上传的逻辑是什么?

    图片分片上传是一种将大文件分割成多个小片段进行上传的策略,以提高上传效率和稳定性。其基本逻辑如下:

    1. 客户端将待上传的图片文件进行分片切割,将文件分割成多个固定大小的片段(chunk)。
    2. 客户端按照一定的顺序将这些分片依次上传到服务器端。可以使用HTTP协议的POST请求或其他上传协议进行分片上传。
    3. 服务器端接收到每个分片后,将其暂存到临时存储区,通常是磁盘或内存。
    4. 当所有分片都上传完成后,服务器端根据上传的顺序将这些分片进行合并,还原成完整的图片文件。
    5. 完整的图片文件可以进行进一步的处理,如存储到数据库或文件系统中,或进行其他业务逻辑操作。

    在图片分片上传的过程中,还需要考虑以下几个方面的逻辑:

    • 分片大小:需要根据实际情况确定每个分片的大小,通常根据网络环境和服务器性能进行调整,以保证上传效率和稳定性。
    • 分片顺序:客户端需要按照一定的顺序上传分片,通常是从第一个分片开始,依次上传到最后一个分片。
    • 分片校验:客户端可以对每个分片进行校验,例如计算分片的哈希值,以确保分片的完整性和准确性。
    • 断点续传:如果上传过程中出现网络中断或其他异常情况,客户端可以记录已上传的分片信息,下次继续上传时可以从断点处继续上传,以实现断点续传的功能。

    通过图片分片上传的方式,可以有效地处理大文件的上传,提高上传效率和稳定性,并且可以灵活控制上传过程,适应不同的网络环境和服务器条件。

  12. 如何用消息队列做削峰填谷的?

    使用消息队列可以有效地实现削峰填谷的目标,具体的实现方式如下:

    1. 创建消息队列:选择合适的消息队列系统,如RabbitMQ、Kafka等,并创建相应的消息队列。
    2. 发送消息:将需要处理的任务或请求转化为消息,并发送到消息队列中。这些消息可以包含任务的相关信息,如任务类型、参数等。
    3. 消费消息:编写消费者程序,从消息队列中获取消息,并进行相应的处理。消费者可以根据业务需求进行扩展,可以是单个消费者或多个消费者。
    4. 控制消费速率:通过控制消费者的数量和处理速度,来控制任务的处理速度。可以动态调整消费者的数量,根据实际情况增加或减少消费者的数量。
    5. 峰值处理:当任务量激增时,消息队列可以暂时存储大量的消息,而不会导致系统负载过高。消费者按照自身的处理能力逐步消费消息,避免了系统的峰值压力。
    6. 谷值填充:当任务量减少时,消息队列中的消息可以被逐步消费,保证系统的资源得到充分利用,避免了资源的浪费。
    7. 异步处理:通过消息队列的异步特性,可以将任务的处理与请求的接收解耦。请求可以快速响应,而任务的处理可以在后台进行,提高系统的响应速度和吞吐量。

    通过使用消息队列进行削峰填谷,可以平滑处理系统的高峰期和低谷期,提高系统的稳定性和可伸缩性。同时,消息队列还可以提供消息持久化、消息重试等功能,增加系统的可靠性和容错性。

出处:Zyccccccc

24面经大全 文章被收录于专栏

收录各个网友分享的各个公司的面经,并给出答案。

全部评论
为什么java还要问动态和静态链接库
4 回复
分享
发布于 03-10 10:52 广东
佬什么bg
点赞 回复
分享
发布于 03-08 17:41 广东
滴滴
校招火热招聘中
官网直投

相关推荐

41 179 评论
分享
牛客网
牛客企业服务