当前位置: 首页 > news >正文

秋招突击——面经整理——有塔游戏提前批

文章目录

    • 引言
    • 正文
      • 一面
        • 说一下堆排序
      • 二面
        • 有了解过游戏后端应该是干什么的吗?
        • 博客是从什么时候开始写的?
        • 平常在哪里做题?做了多少题?
        • 给你二维矩阵,零代表可以走,一代表不可以走,从起点到终点,计算一下最短路径。
        • 你说用到了动态规划,这个题可以使用动态规划吗?
        • 如果你非要说能够使用动态规划,状态转移方程怎么写?
        • 这个题目可以使用广度优先搜索吗?
        • 广度时间复杂度是多少?
        • BFS求路径怎么做到?
        • DFS怎么保存路径?
        • 时间复杂度太高了怎么办?
        • 先讲一个背包问题,无限个不同面值的硬币,问你能不能凑出来一个特定的面额!
        • redis你怎么学的?redis的持久化有哪些?
        • redis持久化怎么修改配置?
        • 在项目中是怎么实现分布式锁的?
    • 补充
      • 迷宫问题
        • 深度优先搜索怎么做?如何保存路径!
        • 广度优先搜索实现?计算最短路径并保存具体的路径!
        • DP类型的迷宫问题实现
      • 背包问题的推导和优化
      • redis持久化具体怎么配置
        • RDB持久化配置
        • AOF快照配置
      • StringBuilder、StringBuffer和String
    • 总结

引言

  • 莫名其妙过了一面,而且一面并没有问什么东西。

正文

一面

说一下堆排序

二面

有了解过游戏后端应该是干什么的吗?
  • 没有细致了解过,简单说了一下自己玩过的游戏。
博客是从什么时候开始写的?
  • 退伍之后开始写的,写了差不多五六年!写的最多的还是数据结构(真不该这样说,下面就开始问我算法了,给我整的有点懵逼)
平常在哪里做题?做了多少题?
  • leetcode做了200道题,acwing做了三百多题,然后说了DP的相关内容,树形DP、区间DP、状态转换机 等(下面就开始问这个了,尴尬!)
给你二维矩阵,零代表可以走,一代表不可以走,从起点到终点,计算一下最短路径。
  • 迷宫问题常见方式是二维DP和回溯来实现(这里两种方法实现差异弄错了,能不能用DP只能说先定方向。
你说用到了动态规划,这个题可以使用动态规划吗?
  • (完全说错了,这里上下左右说明子状态还没有推导出来,所以不能用动态规划,我全程在胡扯!!)
  • 迷宫问题,固定方向可以使用动态规划,这里没有规定方向,不能使用动态规划。
  • 尴尬,这里沉默了大概五分钟,肯定会挂的,我还在这里强!无语!我又跟他争辩了十分钟!无语!
如果你非要说能够使用动态规划,状态转移方程怎么写?
  • 一下子顿悟了,不能使用动态规划!他说了方程才明白!
这个题目可以使用广度优先搜索吗?
  • 可以使用光度优先搜索,大概说了一下过程,但是广度优先搜索的怎么获取最短路径说的不够详细,然后怎么计算最短路径哪里没说好!
  • 又沉默了五分钟!完全没必要,肯定能够做的!
  • 这里又提到了使用交换队列,来计算路径,然后通过父节点的索引,来计算路径!
广度时间复杂度是多少?
  • 还是平方
BFS求路径怎么做到?
  • 有沉默!这个不应该沉默!
  • 然后扯到了之前参加的华为软件精英挑战赛,扯了一通,我忘记怎么计算最短路径了,怎么求出来的!过的有点久!尴尬!
DFS怎么保存路径?
时间复杂度太高了怎么办?
  • 加上一些A星启发式算法!
先讲一个背包问题,无限个不同面值的硬币,问你能不能凑出来一个特定的面额!
  • 写了一下DP的状态转换方程,但是这里直接写了公式优化的模式,而且他问的是能不能,不是最少的硬笔数,能不能应该还是使用或|,这里还是得好好过一下!
redis你怎么学的?redis的持久化有哪些?
  • 看黑马程序员学的,三周学完了
  • 这个八股说的很详细,这里就不写了!
redis持久化怎么修改配置?
在项目中是怎么实现分布式锁的?
  • 怎么用redis的,直接调用jedis相关指令,使用现成的东西,并没有深究,

补充

迷宫问题

深度优先搜索怎么做?如何保存路径!
  • DFS我还是会写的,这里是使用递归实现的,然后保存路径也是单独通过一个变量实现的。
import java.util.*;public class Main {private static final int[][] DIRECTIONS = {{0, 1}, {1, 0}}; // 右和下两个方向private static int minSum = Integer.MAX_VALUE;  // 用于存储最小路径和private static String minPath = "";  // 用于存储最小路径public static void main(String[] args) {Scanner in = new Scanner(System.in);int count = in.nextInt();while (count > 0) {count--;int x = in.nextInt();int y = in.nextInt();int[][] grid = new int[x][y];for (int i = 0; i < x; i++) {for (int j = 0; j < y; j++) {grid[i][j] = in.nextInt();}}// 开始DFS搜索dfs(grid, 0, 0, 0, new StringBuilder());// 输出最小路径和及其路径System.out.println("Minimum Path Sum: " + minSum);System.out.println("Path: " + minPath);}in.close();}private static void dfs(int[][] grid, int i, int j, int currentSum, StringBuilder path) {int x = grid.length;int y = grid[0].length;// 更新当前路径和路径字符串currentSum += grid[i][j];path.append("(").append(i).append(",").append(j).append(") ");// 如果到达右下角终点if (i == x - 1 && j == y - 1) {if (currentSum < minSum) {minSum = currentSum;minPath = path.toString();}} else {// 向下或向右继续探索for (int[] dir : DIRECTIONS) {int ni = i + dir[0];int nj = j + dir[1];if (ni >= 0 && ni < x && nj >= 0 && nj < y) {dfs(grid, ni, nj, currentSum, path);}}}// 回溯: 移除路径中当前节点path.setLength(path.length() - 6);  // 撤销最后一个坐标 "(i,j) "}
}

总结

  • DFS最好还是使用递归实现,然后记得恢复现场

    • 时间复杂度
      • 这个丢人丢大了,每次都是选择两个方向,然后每次操作都应该是2的 m + n次方,是一个指数级的操作,我还说是平方级,真的丢人。
    • 空间复杂度
      • m + n因为每一个节点都需要由递归调用栈
  • path.setLength(path.length() - 6); // 撤销最后一个坐标 "(i,j) “

  • setLength截取StringBuffer的特定长度

广度优先搜索实现?计算最短路径并保存具体的路径!
import java.util.Scanner;
import java.util.*;public class Main{private static final int[][] DIRECTIONS = {{0,1},{1,0}};public static void main(String args[]){Scanner in = new Scanner(System.in);int count = in.nextInt();while(count > 0){count --;int x = in.nextInt();int y = in.nextInt();int[][] grid = new int[x + 1][y +1];for(int i = 0;i < x;i ++){for(int j = 0;j < y;j ++){grid[i][j] = in.nextInt();}}// 使用dp进行遍历// 队列中的每一个元素是一个数组{i,j,currentSum,pathIndex}Queue<int[]> q = new LinkedList<>();// dp和path用于保存最小路径int[][] dp = new int[x][y];String[][] paths = new String[x][y];// 初始化dp和pathsfor (int i = 0;i < x;i ++)Arrays.fill(dp[i], Integer.MAX_VALUE);dp[0][0] = grid[0][0];paths[0][0] = "(0,0)";// 队列加入初始元素q.add(new int[]{0,0,grid[0][0]});while (!q.isEmpty()){int[] cur = q.poll();int i = cur[0],j = cur[1],curSum = cur[2];// 遍历所有方向for(int[] dir : DIRECTIONS){int ni =i + dir[0];int nj =j + dir[1];// 计算目标点的距离if(ni >= 0 && ni < x && nj >= 0 && nj < y){int newSum = curSum + grid[ni][nj];// 如果新的路径和更小,则更新if (newSum < dp[ni][nj]){dp[ni][nj] = newSum;paths[ni][nj] = paths[i][j] + String.format("(%d,%d)",ni,nj);q.add(new int[]{ni,nj,newSum});}}}}// 输出dp数组for (int i = 0;i < x;i ++){for (int j = 0;j < y;j ++){System.out.print(dp[i][j] + " ");}System.out.println();}// 输出最小路径System.out.println("Minimum Path sum " + dp[x - 1][y - 1]);System.out.println("Path " + paths[x - 1][y - 1]);}in.close();}
}

总结

  • 单纯DP,队列中的一个元素,是每一个点的坐标
  • 保存最短路径长度,需要创建一个路径长度矩阵,和原来的矩阵同样大小,然后逐个进行比较
  • 保存具体路径,需要维系一个具体路径矩阵,然后输出最后的坐标。
  • 上述三个步骤是递增的,如果要获取具体最短路径,就是需要创建一个具体的长度距离矩阵,然后进行计算比较。通过这个坐标点比较确定最短路径的。
  • 时间复杂度是n的平方
DP类型的迷宫问题实现
  • 最基本的摘花生,具体题目如下,这个就是经典的可以使用DP解决的迷宫问题,因为限定了方向,所以可以使用DP解决,因为子状态是固定的
    在这里插入图片描述

这里就有两种代码,一种是没有扩充边界的初始值情况,一种是有边界初始值的情况,具体如下

import java.util.Scanner;
import java.util.*;public class Main{public static void main(String args[]){Scanner in = new Scanner(System.in);int count = in.nextInt();while(count > 0){count --;int x = in.nextInt();int y = in.nextInt();int[][] grid = new int[x + 1][y +1];for(int i = 0;i < x;i ++){for(int j = 0;j < y;j ++){grid[i][j] = in.nextInt();}}// 使用dp进行遍历int[][] dp = new int[x + 1][y + 1];dp[0][0] = grid[0][0];for(int i = 0;i < x;i ++){for(int j = 0;j < y;j ++){if(i > 0) dp[i][j] = Math.max(dp[i - 1][j] ,dp[i][j])+ grid[i][j];if(j > 0) dp[i][j] = Math.max(dp[i][j] ,dp[i][j - 1]+ grid[i][j]);//}}for(int i = 0;i < x;i ++){for(int j = 0;j < y;j ++){System.out.print(dp[i][j] + " ");}System.out.println();}System.out.println(dp[x - 1][y - 1]);}}
}

注意,这里一定要初始化x = 0和y=0,也就是初始值的情况。否则会出现异常,因为没有计算到最初值的情况

import java.util.Scanner;
import java.util.*;public class Main{public static void main(String args[]){Scanner in = new Scanner(System.in);int count = in.nextInt();while(count > 0){count --;int x = in.nextInt();int y = in.nextInt();int[][] grid = new int[x + 1][y +1];for(int i = 1;i <= x;i ++){for(int j = 1;j <= y;j ++){grid[i][j] = in.nextInt();}}// 使用dp进行遍历int[][] dp = new int[x + 1][y + 1];for(int i = 1;i <= x;i ++){for(int j = 1;j <= y;j ++){dp[i][j] = Math.max(dp[i - 1][j] ,dp[i][j - 1])+ grid[i][j];}}System.out.println(dp[x ][y ]);}}
}

使用这种方式效果会更好,因为在地图矩阵周围又扩了一圈,不用担心会越界!

import java.util.Scanner;
import java.util.*;public class Main{public static void main(String args[]){Scanner in = new Scanner(System.in);int count = in.nextInt();while(count > 0){count --;int x = in.nextInt();int y = in.nextInt();int[][] grid = new int[x + 1][y +1];for(int i = 0;i < x;i ++){for(int j = 0;j < y;j ++){grid[i][j] = in.nextInt();}}// 使用dp进行遍历int[][] dp = new int[x + 1][y + 1];int[][] path = new int[x + 1][y + 1];dp[0][0] = grid[0][0];path[0][0] = -1;  // path仅仅是用来保存方向的// 当y等于零,初始化第零列for(int i = 1;i < x;i ++)   {dp[i][0] = dp[i - 1][0] + grid[i - 1][0];path[i][0] = 0;// 从上面来}// 当x等于零,没有办法减一for(int j = 1;j < y;j ++){dp[0][j] = dp[0][j - 1] + grid[0][j];path[0][j] = 1; // 从左边来}for(int i = 1;i < x;i ++){for(int j = 1;j < y;j ++){if(dp[i - 1][j] > dp[i][j - 1]){dp[i][j] = dp[i - 1][j] + grid[i][j];path[i][j] = 0;}else{dp[i][j] = dp[i - 1][j - 1] + grid[i][j];path[i][j] = 1;}}}// 回溯遍历最终的终点System.out.println("Path:");int i = x- 1,j = y - 1;StringBuilder pathStr = new StringBuilder();while (path[i][j ] != -1){if(path[i][j] == 1){// 来自于上面的格子pathStr.insert(0,"right  ");j --;}else{pathStr.insert(0,"down  ");i --;}}pathStr.insert(0,"(0,0)");System.out.println(pathStr.toString());}}
}

分析

  • 这里是创建了一个path,用来记录每一次做出决策的具体内容,而不是对应的点,因为这里每一个状态都是由子状态决定的,并并不是记录点就行了,或者说孤立的一个点!最后在从终点,逆序推导出最终的目标点的路径!
    在这里插入图片描述

背包问题的推导和优化

  • 这里放一道题,是一个典型的完全背包问题,以往做的链接,关于朴素做法我是知道的, 但是关于推导公式当时就没有写出来
  • 买书——第一次做
  • 买书——第二次做
  • 买书——第三次做

请添加图片描述

  • 这里使用集合的角度进行思考,n个物体和s个背包容量,从一个角度出发,物体的角度出发,就是按照物体的次序,对结果进行划分,每一个物体又可以放零个或者n个,这里就需要处理一下
    在这里插入图片描述
    根据这个公式可以知道,在逐次遍历的过程中,可以使用公式进行优化,具体实现如下

方案一:未经过优化,原始数组

import java.util.Scanner;
import java.util.*;class Main{public static void main(String args[]){Scanner in = new Scanner(System.in);int m = in.nextInt();int booksNum = 4;int[] books = {0,10,20,50,100};// 创建二维DP数组int[][] dp = new int[5][m + 1];dp[0][0] = 1;for(int i = 1;i <= booksNum;i ++){for(int j = 0;j <= m; j ++){for(int count = 0;count * books[i] <= j;count ++){dp[i][j] = dp[i][j] + dp[i - 1][j - count * books[i]];}}}System.out.println(dp[4][m]);}
}

上述版本就是完全没有使用任何优化,因为每一次递归的时候,都可能摆放n个物体,然后逐个进行递增,单纯按照集合推导的角度进行出发的!

方案二:经过优化,但是没有使用滚动数组优化

  • 根据之前推导的公式,可以使用f[i][j - vi]进行优化
import java.util.Scanner;
import java.util.*;class Main{public static void main(String args[]){Scanner in = new Scanner(System.in);int m = in.nextInt();int booksNum = 4;int[] books = {0,10,20,50,100};// 创建二维DP数组int[][] dp = new int[5][m + 1];dp[0][0] = 1;for(int i = 1;i <= booksNum;i ++){for(int j = 0;j <= m; j ++){if(j - books[i] < 0)dp[i][j] = dp[i - 1][j];else dp[i][j] = dp[i - 1][j] + dp[i][j - books[i]];// for(int count = 0;count * books[i] <= j;count ++){//     dp[i][j] = dp[i][j] + dp[i - 1][j - count * books[i]];// }}}System.out.println(dp[4][m]);}
}

在这里插入图片描述
不能直接写成如下形式,因为当前行的状态,是需要借鉴当前行容量以前的状态,如果不更新就是零了

在这里插入图片描述

方案三:最终的优化版本

import java.util.Scanner;
import java.util.*;class Main{public static void main(String args[]){Scanner in = new Scanner(System.in);int m = in.nextInt();int booksNum = 4;int[] books = {0,10,20,50,100};// 创建二维DP数组int[] dp = new int[m + 1];dp[0] = 1;for(int i = 1;i <= booksNum;i ++){for(int j = books[i];j <= m; j ++){dp[j] = dp[j] + dp[j - books[i]];}}System.out.println(dp[m]);}
}

滚动数组优化,上述代码使用了滚动数组优化,因为每一次都使用同一行,而且更新仅仅会调用之前的列,具体如下图!

在这里插入图片描述

redis持久化具体怎么配置

RDB持久化配置
  • 编辑redis.conf进行配置

    • save 900(时间范围) 1(数据发生修改的频次阈值)
  • 配置保存路径并指定rdb存储路径

    • 配置写入RDB持久化失败之后,阻止redis再进行写入操作
dir /path/to/dump-directory/
dbfilename dump.rdb
stop-writes-on-bgsave-error yes
AOF快照配置
  • redis.conf文件中配置,打开AOF快照
    appendonly yes
  • 配置AOF文件名
appendfilename "appendonly.aof"
  • 设置写入频率
# always: 每次有写操作时都同步(最安全,但性能最低)
# everysec: 每秒同步一次(推荐,性能和数据安全的平衡)
# no: 由操作系统决定何时同步(性能最好,但可能会丢数据)
appendfsync everysec
  • 设置重写AOF文件条件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

上述左右操作都需要重启redis进行配置

StringBuilder、StringBuffer和String

  • String

    • 不可变形,每次对其进行拼接或者修改,都是创建一个新的String对象
    • 频繁创建和修改消耗性能的。
    • 线程安全的
  • StringBuiler

    • 可变的,可以在原有对象上修改,不会生成新的对象
    • 内部使用一个可扩展的字符数组来存储字符串内容,拼接是在末尾追加字符,效率更高
    • 线程不安全的,适合单线程的操作
  • StringBufer

    • 可变的,内部使用可扩展的字符数组,拼接和删除都是修改对应的字符串
    • 线程安全的,内部每一个方法都是使用Synchronized修饰的

总结

  • 兄弟,千万不要说的太满了,这个友塔游戏纯纯翻车,什么都不会,被人家看穿了,我是做过,但是脑子记性不好,忘得差不多了!

  • 人家还是很想要你的,一直在给你机会,但是你说的确实不是很好,甚至说有点糟糕,无论是是吹了很多次的算法,还是八股的相关设置问题的。

    • 你说你的算法能力很强,但是我问了好几个,你还是不会!
    • 你说你做过项目,问了你一些基本的设置,你也说不知道!
  • 最后还在狡辩,说我没说错,是你说错了,真的印象差极了,真的没有必要这样的!还是不行,为人处事还是说话能力都不行!

8/12

  • 读了一些书,心境发生了变化,觉得这些都不算什么,尽力就行,成功或者失败都是一种体验,活着就是人生最大意义!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • AtCoder Beginner Contest 366 A~F
  • Mysql 约束(下)
  • 掌握这项技能,用Python爬虫定制你的私人电影推荐库
  • 【计算机网络】计算机网络第二章——信道复用技术
  • Ubuntu 22.04 安装 VirtualBox7
  • 怎么给图片加红色边框?图片加边框的超好用方法
  • 设计模式六大原则之:依赖倒置原则
  • [二次元]个人主页搭建
  • 虚拟化—XenServer安装教程详细(附客户端连接)
  • 注意力机制篇 | YOLOv8改进之引入NAMAttention注意力机制 | 基于标准化的注意力模块
  • 进阶!haproxy高级功能与配置
  • 机器学习(1)--数据可视化
  • 面试实战题-数据库及DAO层
  • 基于STM32设计的智能鱼缸_带鱼儿数量视觉识别(华为云IOT)(202)
  • LeetCode.20.有效的括号
  • create-react-app项目添加less配置
  • Leetcode 27 Remove Element
  • Linux后台研发超实用命令总结
  • ng6--错误信息小结(持续更新)
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • Spring框架之我见(三)——IOC、AOP
  • Theano - 导数
  • Vue小说阅读器(仿追书神器)
  • win10下安装mysql5.7
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 目录与文件属性:编写ls
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 用简单代码看卷积组块发展
  • puppet连载22:define用法
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​经​纬​恒​润​二​面​​三​七​互​娱​一​面​​元​象​二​面​
  • ​学习一下,什么是预包装食品?​
  • ​一、什么是射频识别?二、射频识别系统组成及工作原理三、射频识别系统分类四、RFID与物联网​
  • ######## golang各章节终篇索引 ########
  • (4)Elastix图像配准:3D图像
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (vue)页面文件上传获取:action地址
  • (笔试题)合法字符串
  • (七)Activiti-modeler中文支持
  • (贪心) LeetCode 45. 跳跃游戏 II
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转载)(官方)UE4--图像编程----着色器开发
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .net core 依赖注入的基本用发
  • .Net Core中的内存缓存实现——Redis及MemoryCache(2个可选)方案的实现
  • .net 流——流的类型体系简单介绍
  • .net 托管代码与非托管代码
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型