常见笔试题

算法

冒泡排序:

private void BubbleSort(int[] arr)
{
    for (int i = arr.Length - 1; i >= 0; i--)
    {
        for (int k = 0; k < i; k++)
        {
            if (arr[k] > arr[k + 1])
            {
                swap(arr, k, k + 1);
            }
        }
    }
}

private void SelectSort(int[] arr)
{
    for (int i = 0; i < arr.Length - 1; i++)
    {
        int min = i;
        for (int j = i + 1; j < arr.Length; j++)
        {
            min = arr[j] < arr[min] ? j : min;
        }
        swap(arr, i, min);
    }
}

private void InisertSort(int[] arr)
{
    for (int i = 1; i < arr.Length; i++)
    {
        for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--)
        {
            swap(arr, j, j + 1);
        }
    }
}

归并排序:

public static void sort(int[] arr)
{
    printArray(arr);
    Console.WriteLine();
    int res =process(arr, 0, arr.Length - 1);
    printArray(arr);
    Console.WriteLine();
    Console.WriteLine(res);
}
private static int process(int[] arr,int L,int R)
{
    if (L == R) return 0;
    int mid = L + ((R - L) >> 1);
    return process(arr, L, mid)+
    process(arr, mid + 1, R)+
    merge(arr, L, mid, R);
}

private static int merge(int[] arr,int L,int M,int R)
{
    int[] help = new int[R - L + 1];
    int p1 = L;
    int p2 = M + 1;
    int i = 0, res = 0;
    while(p1<=M&& p2 <= R)
    {
        res += arr[p1] < arr[p2] ? arr[p1] * (R - p2 + 1) : 0;
        help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
    }
    while (p1 <= M)
        help[i++] = arr[p1++];
    while(p2<=R)
        help[i++] = arr[p2++];
    for (int j = 0; j < help.Length; j++)
    {
        arr[L + j] = help[j];
    }
    return res;
}

快排:

private void main(int[]arr)
{
    if (arr == null) return;
    proesss(arr, 0, arr.Length);
}

private void proesss(int[] arr,int L,int R)
{
    if (L >= R) return;
    Random rand = new Random();
    int temp = rand.Next(0, L - R + 1) + L;
    swap(arr, temp, R);
    int[] index = nethorland(arr, L, R);
    proesss(arr, L, index[0] - 1);
    proesss(arr, index[1] + 1, R);
}

private int[] nethorland(int[] arr,int L,int R)
{
    if (L > R) return new int[] { -1, -1 };
    if (L == R) return new int[] { L, R };
    int less = L - 1;
    int more = R;
    int index = L;
    while (index < more)
    {
        if (arr[index] == arr[R]) index++;
        else if (arr[index] < arr[R]) swap(arr, index++, ++less);
        else if (arr[index] > arr[R]) swap(arr, index, --more);

    }
    swap(arr, more, R);
    return new int[] { less + 1, more };
}

C#基础

1. const和readonly的区别

const是在声明时候赋值,readonly在声明或者构造函数中赋值

const在编译时常量,不占用运行时内存,readonly在运行时常量,占用内存

const之能修饰一些基本数据类型,readonly能修饰各种数据类型

const默认为静态,readonly不是

2. 拆装箱

装箱:值类型转换为object类型

拆箱:引用类型转换为值类型

3. 接口和抽象类的区别

  • 接口多继承;抽象类单继承
  • 接口不能有成员函数和构造函数,抽象类可以
  • 接口的子类必须重写所有声明的方法,抽象类的子类可以不重写普通方法
  • 接口没有private修饰符,全是默认的public;

4. class和struct的区别

class是引用类型,struct是值类型

class表现为行为,struct用于存储数据

class支持继承,可以继承自类和接口;struct没有继承性,不能从class继承,不能作为基类,但支持接口继承

4.1什么是CLR

公共语言运行库,是一个运行环境,负责资源管理(GC和内存分配),保证应用和底层分离;

C#会首先被编译为CLR,这个中间语言被封装在程序集(dll)中,这个过程是由C#编译器完成的;

4.1 什么是委托

可以理解为一个指向函数的引用

允许方法作为参数传递

可用于定义回调方法

4.2 构造器consturctor是否可以被重写

不能被重写,但是可以重载

4.3 using和new 两个关键字的意义

using:

  • 作为指令,用于导入命名空间
  • 作为语句,定义范围,在范围的末尾释放对象

new:新建实例或者隐藏父类方法

5. ref和out

ref传入引用,需要传入已经赋值的引用

out返回前赋值

6. new关键字用法

new实例化和隐藏父类的方法

7. string=null 和string str=""区别

null不分配内存

8. 序列化是什么

将一个类型转换为可以传输的数据,反之就是反序列化,可以用于数据的保存和传输

9. Async

异步

10. 重写和重载

11. 面对对象

封装:隐藏实现的细节

继承:提高代码的重用度

多态:同名方法的不同表现

12. GC产生原因,如何避免

为了避免内存泄漏

减少new的次数

使用对象池

频繁修改字符串的时候,使用stringbuilder

13. sealed关键字

修饰类,就是防止类被继承

修饰方法,就是防止子类重写

14.反射原理

审查元数据并收集关於它的类型信息的能力,元数据(编辑后的基本数据单元)就是一大堆表,编译器会创建一个类定义表,一个字段定义表,一个方法定义表等,System.Reflection命名空间包含的几个类,允许你反射(解析)这些元数据的代码。

15. Net和Mono的关系

net是语言平台

mono是运行环境,实现了CLR基础类库,是net在window等平台运行

16.构造函数使用static会报错嘛

如果添加了访问修饰符,就会报错

静态构造函数不允许有参数

17. string比stringbuilder的优势

18. func(int a,int b)用lambda

(a,b)=>{},属于闭包,如果调用了局部变量,会产生GC

19. C#常见的容器类

list

queue

stack

dictionary

20. 常规容器和泛型容器的区别

类型安全

21. 数组和list的区别

list动态扩容,有很多方法

22. 常见的数值类

int float bool

enum struct

23. 委托和接口

24. unsafe

非代码托管,用指针的时候用

25. for foreach enumerator的使用

for是按照索引循序遍历

foreach是迭代器遍历

26. JIT和AOT

Just in time 实时编译,执行满,占用空间小

Ahead of time,预编译,执行快,占用空间大

4. Attribte property makeup tag

Unity知识点

1. onenable和start启用时机

awake 实例化的时候启用,不能保证其他类也被实例化了

onenable 唤醒的时候启用,可以多次重复触发

start 更新的第一帧使用,一般用来与其他物体建立初始化的连接

2. 生命周期

3. 相机移动在哪个函数中

fixedupdate,因为物理动作都可以放在这里,是固定帧数执行,和网速无关

4. ?进程、线程、协程的区别

线程和协程都是进程的子集,一个进程有多个线程,一个线程有多个协程

进程:

协程

通过yield关键字,将代码分为多个片段,在update中执行,如果遇到yield了,就等待下一次的update

Ienumerator,本质是一个容器,里面有接口movenext,

它不是多线程,因为他会阻塞

5. onbecameVisible

在对象切换为可见或不可见的时候发生

6. unity中的寻路系统

nevmeshagent

7. NavMeshObstacle组件的作用

导航网格障碍物,动态的障碍,

8. RaycastHit代表

射线检测的碰撞信息

9. 通过什么区分射线碰到的对象

通过layermask,raycasthit返回碰撞标签

RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 1000, 1<<LayerMask.NameToLayer("TestLayer")))
{
       Log.Debug(" hit :" + hit.collider.gameobject );
}
else
{
       Log.Debug("ray cast null!");
}

layermask

LayerMask mask=1<<3; //开启layer3
LayerMask mask=1<<1 | 1<<9;  //开启1和9
LayerMask mask  = 1 << LayerMask.NameToLayer(“TestLayer”);  //表示开启层名“TestLayer” 的层 

10. 怎么判断两个平面是否相交?不能用碰撞体,说出计算方法。

在一个平面上分别取一个向量,看是否相交

11. MeshCollider和其他Collider的一个主要不同点?

meshcollider是网格碰撞器,,对复杂网格模型上的碰撞检测,比其他碰撞检测精确,但计算量也大。一般在面数高的模型上不会用,一般会做两个模型,一个用于显示,另外一个大概形状用于做碰撞检测

12. Unity3d中的碰撞器和触发器的区别?

tigger是触发器,可以穿透物体,没有物理属性

collider是碰撞器,不能穿透,有物理属性

13. 物体发生碰撞的条件

有一个有刚体,两个都有碰撞器,并且两个物体层级是可以碰撞的

Layer Coliision Matrix

14. 一个细小高速物体,撞向一个较大物体的时候,会发生什么,如何避免

会穿透

可以打开实时碰撞

数据结构与算法

1. 冒泡排序

private void sort(int[] arr){
	for(int i=arr.Length-1;i>0;i--){
	for(int j=0;j<i;j++)
	{
	if(arr[j]>arr[j+1]) swap(arr,j,j+1);
	}
	}
}

2. 说出你了解的数据结构

数组:长度固定,空间连续,读取速度快,插入慢

链表:长度不固定,空间不连续,读取速度慢,但插入快

栈:先进后出

队列:先进先出

字典:键值对,无序,一一对应,查找速度快

树:可以无限层次,父子节点

面向对象

1. 设计模式

单例:可用于对象池

工厂模式:通过不同的参数名,返回子类。

何时使用:当我们需要在不同条件下创建不同实例时使用

缺点:新增一个子类就要添加调用的逻辑,不符合开闭原则

public class SimpleFactory{
	public baseClass creat(string name){
        if(name=="a") return new sun1();
        else if(name == "b") return new sun2();
    }
}

public abstract class baseClass{
    public abstruct void print();
}
public class sun1:baseClass
{
	
}

public class sun2:baseClass
{
	
}

抽象工厂:通过一个接口创建相关或依赖对象的家族,适用于多个产品系列且需一起使用的场景

  • 先分为形状、颜色两种工厂;
  • 颜色工厂包括方法fill,形状工厂包括draw;
public class AbstractFactoryPatternDemo {
   public static void main(String[] args) {
 
      //获取形状工厂
      AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
 
      //获取形状为 Circle 的对象
      Shape shape1 = shapeFactory.getShape("CIRCLE");
 
      //调用 Circle 的 draw 方法
      shape1.draw();
 
      //获取形状为 Rectangle 的对象
      Shape shape2 = shapeFactory.getShape("RECTANGLE");
 
      //调用 Rectangle 的 draw 方法
      shape2.draw();
      
      //获取形状为 Square 的对象
      Shape shape3 = shapeFactory.getShape("SQUARE");
 
      //调用 Square 的 draw 方法
      shape3.draw();
 
      //获取颜色工厂
      AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
 
      //获取颜色为 Red 的对象
      Color color1 = colorFactory.getColor("RED");
 
      //调用 Red 的 fill 方法
      color1.fill();
 
      //获取颜色为 Green 的对象
      Color color2 = colorFactory.getColor("GREEN");
 
      //调用 Green 的 fill 方法
      color2.fill();
 
      //获取颜色为 Blue 的对象
      Color color3 = colorFactory.getColor("BLUE");
 
      //调用 Blue 的 fill 方法
      color3.fill();
   }
}

策略模式:将需要变化的方法提取出来,作为一个单独的接口

缺点

  • 每增加一个算法,都要增加一个策略类
  • 所有策略类都需要暴露,对外公开,以便被选择和使用

优点

  • 算法切换自由
//接口行为类
public interface FlyBehavior {
    public void fly();
}

//实现接口的不同行为
public class FlyWithWings : FlyBehavior{
    public void fly(){
        System.out.println("飞~~~");
    }
}

public class FlyWithRocket : FlyBehavior{

    public void fly(){
        System.out.println("带上火箭筒,飞~~~");
    }
}

//组合是实现接口的超类
public abstract class Duck {
    protected FlyBehavior flyBehavior;

    public void swim(){
        System.out.println("All duck can swim!");
    }

    public abstract void display();

    /**
     * 动态改变飞行行为
     */
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void performFly(){
        flyBehavior.fly();
    }
}   

//不同鸭子类
public class RedHeadDuck : Duck{

    public RedHeadDuck() {
        //可飞
        flyBehavior = new FlyWithWings();
        //flyBehavior = new FlyWithRocket();//带火箭飞
    }

    public override void display() {
        System.out.println("看着像红头鸭");
    }

}

2. 面向对象设计原则

里氏替换原则:子类能够代替父类

单一职责原则:一个类只负责一个事情,只有一个能引起它的变化

依赖倒置原则:依赖接口,而非实现

开放-封闭原则:对于扩展开放,对于修改封闭

接口隔离原则:多个小而精的接口,而不是一个大的

网络

1. TCP和UDP的区别

TCP是传输控制协议,面向连接的,可靠的,基于字节流的运输层通信协议;

UDP是用户数据报协议,不属于连接性协议,因为具有资源消耗小,处理速度快的优点,缺点是容易丢包;

TCP是基于连接的,UDP是基于无连接的

TCP对系统资源要求多,UDP少

TCP是面向流数据的,UDP是数据报

TCP保证数据正确性,UDP可能丢包

TCP保证数据的顺序,UDP不保证

2. 为什么连接是三次握手,关闭时四次挥手

在连接的时候,建立起请求后,把ACK和SYN放在一个报文中发送;

在关闭的时候,对方发来关闭的请求,只意味着对方没有东西发送了,不代表自己发送完了,所以需要ACK和FIN分开发;

3D数学

1. 四元数和欧拉角

四元数用于表示旋转,

2. 向量的点乘、叉乘、归一化的意义

点乘:计算向量夹角,判断敌人在前后,结果是标量

a b =|a||b|cosA

叉乘:得到法向量,判断敌人在左右,结果是矢量

aXb = |a||b||sinA|,>0 b在a的左侧;<0 b在a的右侧

归一化:只关心方向,不关心大小

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值