记录一下网易外包的面试问题,问的都很简单,可惜还是没有把握住(ᇂ_ᇂ|||)
1.自我介绍
就是普通的自我介绍
2.有关项目的问题
- 问了项目中的一个功能是如何实现的,因为用到了物理关节,所以问了Unity的物理关节都有哪些,知不知道这些关节是如何实现的。
- 项目中自己觉得做的好的部分
3.C#的相关问题
因为和面试官说了工作中都是用C#在开发,没有用其他语言,所以只问我C#的问题,真的很善良了。
什么是协程函数、线程,协程堵塞会堵塞主线程吗
1,什么是协程
协程,从字面意义上理解就是协助程序的意思,我们在主任务进行的同时,需要一些分支任务配合工作来达到最终的效果
稍微形象的解释一下,想象一下,在进行主任务的过程中我们需要一个对资源消耗极大的操作时候,如果在一帧中实现这样的操作,游戏就会变得十分卡顿,这个时候,我们就可以通过协程,在一定帧内完成该工作的处理,同时不影响主任务的进行
2,协程的原理
首先需要了解协程不是线程,协程依旧是在主线程中进行
进程:是指⼀个内存中运⾏的应⽤程序,每个进程都有⼀个独⽴的内存空间,⼀个应⽤程序可以同时运⾏多个进程;进程也是程序的⼀次执⾏过程,是系统运⾏程序的基本单位;系统运⾏⼀个程序即是 ⼀个进程从创建、运⾏到消亡的过程。
线程:线程是进程中的⼀个执⾏单元,负责当前进程中程序的执⾏,⼀个进程中⾄少有⼀个线程。⼀个进程中是可以有多个线程的,这个应⽤程序也可以称之为多线程程序。
结构体和类的区别
- 结构体是值类型,类是引用类型
- 结构体存在栈中,类存在堆中
- 结构体成员不能使用protected访问修饰符,而类可以
- 结构体成员变量申明不能指定初始值,而类可以
- 结构体不能申明无参的构造函数,而类可以
- 结构体申明有参构造函数后,无参构造不会被顶掉
- 结构体不能申明析构函数,而类可以
- 结构体不能被继承,而类可以
- 结构体需要在构造函数中初始化所有成员变量,而类随意
- 结构体不能被静态static修饰(不存在静态结构体),而类可以
Delegate(委托)
C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。
委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。
装箱拆箱
1、什么是装箱和拆箱?
简单来说:
装箱是将值类型转换为引用类型 ;拆箱是将引用类型转换为值类型。(网上广为流传)
C#中值类型和引用类型的最终基类都是Object类型(它本身是一个引用类型)。也就是说,值类型也可以当做引用类型来处理。而这种机制的底层处理就是通过装箱和拆箱的方式来进行,利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类型的值相互转换,将值类型与引用类型链接起来 。
2、装箱和拆箱的内部操作是什么样的?
.NET中,数据类型划分为 值类型 和 引用 (不等同于C++的指针) 类型 ,与此对应,内存分配被分成了两种方式,一为栈,二为堆,注意:是托管堆。
值类型只会在栈中分配。 引用类型分配内存与托管堆。(托管堆对应于垃圾回收。)
装箱操作:
对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。按三步进行。
1:首先从托管堆中为新生成的引用对象分配内存(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。
2:然后将值类型的数据拷贝到刚刚分配的内存中。
3:返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用了。
可以看出,进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。
拆箱操作:
1、首先获取托管堆中属于值类型那部分字段的地址,这一步是严格意义上的拆箱。
2、将引用对象中的值拷贝到位于线程堆栈上的值类型实例中。
经过这2步,可以认为是同boxing是互反操作。严格意义上的拆箱,并不影响性能,但伴随这之后的拷贝数据的操作就会同boxing操作中一样影响性能。
UDP和TCP,现在这个视频会议用的是什么协议
- 连接TCP 是面向连接的传输层协议,传输数据前先要建立连接。UDP 是不需要连接,即刻传输数据。
- 服务对象TCP 是一对一的两点服务,即一条连接只有两个端点。UDP 支持一对一、一对多、多对多的交互通信
- 可靠性TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。UDP 是尽最大努力交付,不保证可靠交付数据。
- 拥塞控制、流量控制TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
- 首部开销TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
- 传输方式TCP 是流式传输,没有边界,但保证顺序和可靠。UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
- 分片不同TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。
TCP 和 UDP 应用场景:
由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:
- FTP 文件传输;
- HTTP / HTTPS;
由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:
- 包总量较少的通信,如 DNS 、SNMP 等;
- 视频、音频等多媒体通信;广播通信;
数组与链表的差别
数组
一、数组的特点
1.在内存中,数组是一块连续的区域
2.数组需要预留空间
在使用前需要提前申请所占内存的大小,这样不知道需要多大的空间,就预先申请可能会浪费内存空间,即数组空间利用率低
ps:数组的空间在编译阶段就需要进行确定,所以需要提前给出数组空间的大小(在运行阶段是不允许改变的)
3.在数组起始位置处,插入数据和删除数据效率低。
插入数据时,待插入位置的的元素和它后面的所有元素都需要向后搬移
删除数据时,待删除位置后面的所有元素都需要向前搬移
4.随机访问效率很高,时间复杂度可以达到O(1)
因为数组的内存是连续的,想要访问那个元素,直接从数组的首地址处向后偏移就可以访问到了
5.数组开辟的空间,在不够使用的时候需要扩容,扩容的话,就会涉及到需要把旧数组中的所有元素向新数组中搬移
6.数组的空间是从栈分配的
二、数组的优点
随机访问性强,查找速度快,时间复杂度为O(1)
三、数组的缺点
1.头插和头删的效率低,时间复杂度为O(N)
2.空间利用率不高
3.内存空间要求高,必须有足够的连续的内存空间
4.数组空间的大小固定,不能动态拓展
链表
一、链表的特点
1.在内存中,元素的空间可以在任意地方,空间是分散的,不需要连续
2.链表中的元素都会两个属性,一个是元素的值,另一个是指针,此指针标记了下一个元素的地址
每一个数据都会保存下一个数据的内存的地址,通过此地址可以找到下一个数据
3.查找数据时效率低,时间复杂度为O(N)
因为链表的空间是分散的,所以不具有随机访问性,如要需要访问某个位置的数据,需要从第一个数据开始找起,依次往后遍历,直到找到待查询的位置,故可能在查找某个元素时,时间复杂度达到O(N)
4.空间不需要提前指定大小,是动态申请的,根据需求动态的申请和删除内存空间,扩展方便,故空间的利用率较高
5.任意位置插入元素和删除元素效率较高,时间复杂度为O(1)
6.链表的空间是从堆中分配的
二、链表的优点
1.任意位置插入元素和删除元素的速度快,时间复杂度为O(1)
2.内存利用率高,不会浪费内存
3.链表的空间大小不固定,可以动态拓展
三、链表的缺点
随机访问效率低,时间复杂度为0(N)
lambda表达式,是什么,和函数的差别
定义:"Lambda表达式"是一个匿名函数,是一种高效的类似于函数式编程的表达式
好处:Lambda简化了匿名委托的使用,减少开发中需要编写的代码量。
具体内容:它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式。
写法:所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to"。Lambda运算符的左边是输入参数(如果有),右边是表达式或语句块。Lambda表达式x => x * x读作"x goes to x times x"。
3.算法部分
1.判断镜像二叉树
- /**
- * Definition for a binary tree node.
- * public class TreeNode {
- * public int val;
- * public TreeNode left;
- * public TreeNode right;
- * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
- * this.val = val;
- * this.left = left;
- * this.right = right;
- * }
- * }
- */
- public class Solution {
- public bool CheckSymmetricTree(TreeNode root) {
- if(root == null)
- return true;
- return ChildSymmetric(root.left, root.right);
- }
- public bool ChildSymmetric(TreeNode left, TreeNode right){
- if(left == null && right == null)
- return true;
- if(left == null || right == null)
- return false;
- return left.val == right.val && ChildSymmetric(left.right,right.left) && ChildSymmetric(left.left, right.right);
- }
- }
2.原地合并有序数组
- public class Solution {
- public void Merge(int[] nums1, int m, int[] nums2, int n) {
- for(int i = m, j = 0; j < n;i++,j++){
- nums1[i] = nums2[j];
- }
- Array.Sort(nums1);
- }
- }
3.链表倒数第n个
- /**
- * Definition for singly-linked list.
- * public class ListNode {
- * public int val;
- * public ListNode next;
- * public ListNode(int val=0, ListNode next=null) {
- * this.val = val;
- * this.next = next;
- * }
- * }
- */
- public class Solution {
- public ListNode RemoveNthFromEnd(ListNode head, int n) {
- ListNode dummHead = new ListNode(0, head);
- ListNode temp1 = dummHead, temp2 = dummHead;
- for(int i = 0; i < n; i++){
- temp2 = temp2.next;
- }
- while(temp2.next != null){
- temp1 = temp1.next;
- temp2 = temp2.next;
- }
- temp1.next = temp1.next.next;
- return dummHead.next;
- }
- }