文章目录
- 多益笔试
- 中望龙腾面试
- 字节一面
- 乐易公司面试记录
- 小米
- vivo笔试
- 完美世界
-
- 一面
- 二面
- 智加
-
- 一面
- 米哈游
-
- 一面
- 二面
- 腾讯
-
- 一面
记录一下春招的面试笔试题,部分有答案,感触很深,面的还是很多的,到时候确定offer了会写一写春招经验
椭球公式
x2 / a2+y2 / b2+z2 / c2=1。
当x2/a2+y2/b2+z2/c2<1时 则点(x,y,z)在内部
反转链表
删除导数第N个数
总体很简单,有两个特殊的需要注意,第一是只有一个数或者零个数,第二个是需要删除的是第一个数
if(!head || !head -> next) return NULL;//判断是否只有一个数或者零个数ListNode* first = head;ListNode* second = head;for(int i = 0;i<n;i++) second = second->next;if(!second) return head->next; //判断是否要删的是第一个数,因为你要是走完发现走到了尽头就说明倒数第N个数是开头,那么久直接返回head->next就行,用源代码会有bug
高精度加法
一个很妙的不用判断进位并且前面0的处理方法
while(n1>=0 || n2>=0 || jw){int x = n1 >= 0 ? num1[n1] - '0' : 0;int y = n2 >= 0 ? num2[n2] - '0' : 0;
n变成1
n如果是奇数就变成n-1或者n+1,如果是偶数就变成n/2
用动态规划
vector<int> dp;
dp[0] = 0;
dp[1] = 0;
for(int i = 2;i<n;i++){if(i%2==0) dp[i] = dp[i/2] +1;else dp[i] = min(dp[i-1],dp[i+1]) + 1;
}
return dp[n];
static
https://zhuanlan.zhihu.com/p/37439983
为什么需要虚继承
1、为了解决多继承时的命名冲突和冗余数据问题
C++ 提出了虚继承,使得在派⽣类中只保留⼀份间接基类的成员。其中多继承(Multiple Inheritance)是指从多个直接基类中产⽣派⽣类的能⼒,多继承的派⽣类继承了所有⽗类的成员。
2、虚继承的⽬的是让某个类做出声明,承诺愿意共享它的基类
多益笔试
数据库保护四种
安全性控制,完整性控制,并发性控制和数据恢复
中望龙腾面试
stl底层实现
vector:数组
Dequeue(双端队列):二维数组
List:环状双向链表
set(集合):平衡的红黑树
multiset:红黑树
map:平衡二叉树
哈希表相关
哈希函数
避免冲突
一个是开放寻址法,一个是拉链法
指针常量和常量指针
指针常量就是指针本身是常量,换句话说,就是指针里面所存储的内容(内存地址)是常量,不能改变。但是,内存地址所对应的内容是可以通过指针改变的。
常量指针就是指向常量的指针,换句话说,就是指针指向的是常量,它指向的内容不能发生改变,不能通过指针来修改它指向的内容。但是,指针自身不是常量,它自身的值可以改变,从而指向另一个常量。
线程和进程
cpu分配时间片的最小单位是什么?
答:线程,进程是资源分配的最小单位,线程是运行调度的最小单位。
cpu分配时间片的算法
实现一个SolidWorks包括什么部分?技术栈是怎样?
c++与c的区别,面向对象体现在哪里
字节一面
-
指针和引用区别
在C和C++中,指针一般指的是某块内存的地址,通过这个地址,我们可以寻址到这块内存;而引用是一个变量的别名,例如我们给小明起了个外号:明明,那我们说明明的时候,就是说小明。
指针可以为空,引用不能为空
-
如何解释函数指针
让函数数据化的东西,比如说可以优化很多的if-else语句,
-
多态底层实现
-
编译过程,是否生成dll,替换库行不行
-
如何捕捉未定义的内存错误
-
有哪些内存泄露,如何解决
常见错误:https://blog.csdn.net/Joker_N/article/details/102688822
如何解决:https://blog.csdn.net/daaikuaichuan/article/details/80874436
申请和释放不一致
由于 C++ 兼容 C,而 C 与 C++ 的内存申请和释放函数是不同的,因此在 C++ 程序中,就有两套动态内存管理函数。一条不变的规则就是采用 C 方式申请的内存就用 C 方式释放;用 C++ 方式申请的内存,用 C++ 方式释放。也就是用 malloc/alloc/realloc 方式申请的内存,用 free 释放;用 new 方式申请的内存用 delete 释放。在上述程序中,用 malloc 方式申请了内存却用 delete 来释放,虽然这在很多情况下不会有问题,但这绝对是潜在的问题。
申请和释放不匹配
申请了多少内存,在使用完成后就要释放多少。如果没有释放,或者少释放了就是内存泄露;多释放了也会产生问题。上述程序中,指针p和pt指向的是同一块内存,却被先后释放两次。
释放后仍然读写
本质上说,系统会在堆上维护一个动态内存链表,如果被释放,就意味着该块内存可以继续被分配给其他部分,如果内存被释放后再访问,就可能覆盖其他部分的信息,这是一种严重的错误,上述程序第16行中就在释放后仍然写这块内存。
对策
1.避免在堆上分配内存
既然只有堆上会发生内存泄露,那第一原则肯定是避免在堆上面进行内存分配,尽可能的使用栈上的内存,由编译器进行分配和回收,这样当然就不会有内存泄露了。
- 生产者消费者模型,怎么优化
- 进程之间的通信
1、管道
2、消息队列
3、共享内存
4、信号量
5、Socket
-
进程内存划分
如上图,从低地址到高地址,一个程序由代码段、数据段、BSS段、堆、共享区、栈等组成。
-
**数据段:**存放程序中已初始化的全局变量和静态变量的一块内存区域。
-
**代码段:**存放程序执行代码的一块内存区域。只读,代码段的头部还会包含一些只读的常数变量。
-
BSS 段:存放程序中未初始化的全局变量和静态变量的一块内存区域。
-
可执行程序在运行时又会多出两个区域:堆区和栈区。
**堆区:**动态申请内存用。堆从低地址向高地址增长。
**栈区:**存储局部变量、函数参数值。栈从高地址向低地址增长。是一块连续的空间。
-
最后还有一个共享区,位于堆和栈之间。
-
-
三次握手四次握手过程,timewait作用
https://zhuanlan.zhihu.com/p/86426969
为什么握手要三次
弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。
第一次握手:客户端发送网络包,服务端收到了。
这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。
这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。
这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
因此,需要三次握手才能确认双方的接收与发送能力是否正常
为什么挥手要四次
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
-
一个用户上网的过程
1.在浏览器的地址栏中输入URL。
2.浏览器检查缓存中的DNS记录,以找到URL对应的IP地址。
-
- DNS:域名系统。DNS是互联网的一项服务,他作为将域名和IP地址相互映射的一个分布式数据库。互联网上的每个URL都有一个分配给它的唯一IP地址,DNS是URL及其IP地址的列表,就像是电话簿是名称及其对应电话号码的列表一样。为了查找DNS记录,浏览器会检查四个缓存。
- 第一,检查浏览器缓存。浏览器缓存会为您之前访问过的网站维护一个固定期限的DNS记录存储库,因此,它是运行DNS查询的第一个位置。
- 第二,浏览器检查操作系统缓存。如果它不在浏览器缓存中,浏览器将对您的底层计算机操作系统进行系统调用以获取记录,因为操作系统也会维护DNS记录的缓存。
- 第三,检查路由器缓存。如果它不在你的计算机上,浏览器将与维护其自己的DNS记录缓存的路由器进行通信。
- 第四,检查ISP缓存。如果所有步骤都找不到DNS记录,浏览器将转到ISP,ISP维护着自己的DNS服务器,其中包括一个DNS记录缓存,浏览器将检查这些缓存,以确保找到你请求的URL。
3.如果请求的URL不在缓存中,ISP的DNS服务器将发起DNS查询以查找托管URL的服务器的IP地址。
-
- DNS查询的目的是在Internet上搜索多个DNS服务器,直到找到网站正确的IP地址。这种类型的搜索称为递归搜索,因为搜索将重复从DNS服务器到DNS服务器继续进行,知道找到我们需要的IP地址会返回错误响应表示无法找到它。
- 我们将ISP的DNS服务器称为DNS递归器,其职责是通过向Internet上的其他DNS服务器咨询答案来找到预期域名的正确IP地址,其他DNS服务器被称为名称服务器,因为它们根据网站域名的域架构执行DNS搜索。
-
中间还有一段
-
第五步,回应报文经过封装,在网络中进行转发(利用各种路由协议如OSPF在网络中进行路由转发,利用ARP协议在二层设备上进行数据转发),最后发送到请求域名解析的主机。
-
主机分析数据包,知道IP地址后,写入DNS缓存表,浏览器接着给这个IP的服务器发送HTTP请求。
-
第六步,已经知道目标IP地址了,但是为了将消息传到服务器上要经过OSPF动态路由算法去进行路由转发,然后IP地址使用的是IP协议,整个发送过程中只涉及出发地和目的地2个IP地址,而ARP协议使用的是MAC地址,整个发送过程中涉及到每一个节点的MAC地址 。
4.浏览器发起与服务器的TCP连接。 -
- 一旦浏览器接收到正确的IP地址,他就会与匹配IP地址的服务器建立连接以传输信息,浏览器使用互联网协议来建立这样的连接。可以使用多种不同的Internet协议,但TCP是用于多种HTTP请求最常用协议。
- 在客户端和服务器之间传输数据报,建立TCP连接很重要,此连接时使用TCP/IP三次握手来完成,其中客户端和服务器交换SYN(同步)和ACK(确认)消息以建立连接。
- 三次握手
- 客户端通过Internet向服务器发送SYN数据包,询问它是否已打开新连接。
- 如果服务器有可以接收和启动新连接的开发端口,它将使用SYN/ACK数据包的ACKnowledgment进行响应。
- 客户端将收到来自服务器的SYN/ACK数据包,并通过发送ACK数据包进行确认。然后建立TCP连接进行数据传输!
-
5.浏览器向网络服务器发送HTTP请求。
-
- 一旦建立了TCP连接,就可以开始传输数据了,浏览器将发送一个GET请求,请求获取URL网页,如果您正在输入或提交表单,则可能是POST请求。
6.服务器处理请求并发回响应。
-
- 服务器包含一个web服务器(Apache、IIS),它接收来自浏览器请求并将其传递给请求处理程序以读取和生成响应。请求处理程序是一个程序,它读取请求、请求头和cookie,以检查请求的内容并在需要的时候更新服务器上的信息,然后它将以特定格式(JSON、XML、HTML)组装响应。
7.服务器发出HTTP响应。
-
-
服务器响应包含你请求的网页以及状态码、压缩类型(Content-Encoding)、如何缓存页面(Cache-Control)、要设置的任何cookie。隐私信息等。
- 状态码:
- 1xx仅表示信息性消息。
- 2xx表示某种成功。
- 3xx将客户端重定向到另一个URL。
- 4xx表示客户端错误。
- 5xx表示服务器端出错
-
8.浏览器显示HTML内容(对应HTML响应,这是最常见的)。
-
- 浏览器分阶段显示HTML内容,首先,它将呈现HTML骨架,然后它会检查HTML标签并发送GET请求来获取网页上的其他元素,例如图像、CSS样式表、JavaScript文件等。这些静态文件由浏览器缓存,因此不必获取它们在下次访问页面的时候
- 单链表如何判断有没有环,数学依据
快慢指针
- 两个栈实现一个队列
将一个栈当作输入栈,用于压入 \texttt{push}push 传入的数据;另一个栈当作输出栈,用于 }pop 和 peek 操作。
每次 pop 或 peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。
- 设计模式相关,读源码的时候有没有注意他的设计模式
https://zhuanlan.zhihu.com/p/431714886
乐易公司面试记录
-
vector容量上限,扩容策略
动态扩容,每次都增加到当前的2倍,然后如果超过max
-
map一一对应可以是类吗,类的话怎么实现一一对应
可以,因为map的底层实现是红黑树,只要保证你定义的类有比较符号即可
-
多态
-
双端队列
元素可以从队头出队和入队,也可以从队尾出队和入队
-
手写二分查找
int Binary(vector<int> ary, int target){int n = ary.size();if(n==0) return -1;int left = 0, right = n - 1;int mid;while(left<=right){mid = (left + right) / 2;int m = ary[mid];if(m==target) return mid;else if(m<target) left = mid + 1;else right = mid - 1;}return -1; }
-
手写快排
#include<iostream> using namespace std; int n,a[1000001]; void qsort(int l,int r)//应用二分思想 {int mid=a[(l+r)/2];//中间数int i=l,j=r;do{while(a[i]<mid) i++;//查找左半部分比中间数大的数while(a[j]>mid) j--;//查找右半部分比中间数小的数if(i<=j)//如果有一组不满足排序条件(左小右大)的数{swap(a[i],a[j]);//交换i++;j--;}}while(i<=j);//这里注意要有=if(l<j) qsort(l,j);//递归搜索左半部分if(i<r) qsort(i,r);//递归搜索右半部分 } int main() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];qsort(1,n);for(int i=1;i<=n;i++) cout<<a[i]<<" "; }
-
引用和指针(才知道引用直接传名字就可以,然后不用传指针)
-
进程间通信,(注意和进程中通信区别,并且问你有没有实践过)
-
网络编程
-
三次握手
-
数据库,非关系型和关系型,MongoDB和MySQL有啥区别,为啥选MongoDB
小米
一面
寻找第k大
vivo笔试
1.找钱的方案数变种,拿手机,每次可以拿1、2、3、4,问有几种拿法,爬楼梯变种
作者:拼子
链接:https://www.nowcoder.com/discuss/856152?type=all&order=recall&pos=&page=1&ncTraceId=&channel=-1&source_id=search_all_nctrack&gio_id=DCFD5C44A0863546D8B2F332A84B6BCB-1646712477134
来源:牛客网public static void main(String[] args) {Scanner in = new Scanner(System.in);while (in.hasNext()) {int n = in.nextInt();int[] dp = new int[20000];dp[0] = 1;dp[1] = 1;dp[2] = 2;dp[3] = 4;dp[4] = 8;if (n < 5) {System.out.println(dp[n]);} else {for (int i = 5; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] + dp[i - 4];}System.out.println(dp[n]);}}}
2.滑动窗口,LeetCode原题:https://leetcode-cn.com/problems/frequency-of-the-most-frequent-element/
3.多重背包+最小值,没写出来
作者:衣冠胜雪
链接:https://www.nowcoder.com/discuss/856152?type=all&order=recall&pos=&page=1&ncTraceId=&channel=-1&source_id=search_all_nctrack&gio_id=DCFD5C44A0863546D8B2F332A84B6BCB-1646712477134
来源:牛客网public static int minCost(int rooms, int staffNum, int[] count, int[] roomCapacity, int[] cost) {//rooms房间种类,staffNum总员工数,count 是每种房间的数量,roomCapacity是每种房间的承载人数,cost是每种房间的花费//dp[i][j]表示前i种房间住j个staff的最小花费int[][] dp = new int[rooms][staffNum+1];for (int i = 1; i <= staffNum; i++) { // 设置只住第一种旅馆的花费int roomNum = Math.min(i%roomCapacity[0]==0?i/roomCapacity[0]:i/roomCapacity[0]+1,count[0]);if(roomNum*roomCapacity[0]>=i) {dp[0][i] = roomNum * cost[0];}else{dp[0][i]=Integer.MAX_VALUE/2;//超出第1种房间承受范围的员工数设为最大值的一半,防止后面加法越界}}for (int i = 1; i < rooms; i++) {for (int j = 1; j <= staffNum; j++) {int prevMin = dp[i-1][j]; //求出0~i-1种房间中住j个人的最小花费int curMin = Integer.MAX_VALUE;//设置当前花费为最大for (int k = 1; k <= count[i]; k++) {//遍历当前房间承受最大范围,求出该范围内花费最小值if(j<k*roomCapacity[i]){break;}curMin = Math.min(curMin,dp[i-1][j-k*roomCapacity[i]]+k*cost[i]);}dp[i][j]=Math.min(prevMin,curMin);//和i-1种房间最小值比较,较小的则为i种房间j个员工的最小值}}return dp[rooms-1][staffNum];}
完美世界
一面
-
为什么选择客户端
-
对游戏引擎了解吗?
-
玩过什么游戏
-
说一下多态
-
说一下虚函数表保存在哪里
当生成类对象的时候,编译器会自动的将类对象的前四个字节设置为虚表的地址,而这四个字节就可以看作是一个指向虚函数表的指针。虚函数表可以看做一个函数指针数组。
-
构造函数初始化链表和直接在构造函数里面初始化有什么区别?
一句话,在进入派生类的构造函数内的时候,基类的部分已经构造完成了,你没有使用成员初始化列表初始化基类,因此基类采用的是基类自己默认的构造函数初始化的,然后进入构造函数后,你又使用基类的构造函数构造一次,说明基类部分构造了两次,肯定报错。
在构造函数初始化赋值效率低
常数据成员的初始化必须在初始化列表里完成
-
设计模式了解吗?平时用过吗?
1.单一职责。每个类只实现一个功能,而不要融合太多功能在一个类里
2.开放封闭原则。对增加开放,对修改关闭(增加功能可以)
3.依赖倒转原则。依赖于抽象(接口或父类),而不依赖于实现(子类)
4.迪米特法则(模块A只接触和自己有直接关系的模块B,如果模块B和模块C有直接关系, 而 模块A和模块C,没有,则A调用C ,要通过B,而不是直接调用)危险的事情:
1.复制代码是危险的。如果有两段相同的代码,几乎可以说一定是有问题的,因为每次改动,要维护两段代码
2.尽量减少IO操作,如操作数据库,网络发送,甚至printf ,这些操作比直接操作内存,慢很多倍、
3.修改Bug时,一定要从最简单的基本的地方开始检查,不要检查到最底层没问题,发现是传入的某个参数是错的。先不要怀疑系统的部分。
4.设计架构,同时了解细节
5.有些Bug,调起来可能费时费力,甚至花个二三天,其实当时写的时候,只要稍微注意,就可以轻松避免。避免Bug的代价与找出并修改Bug的代价,实在是差太多了。
6.把一段长代码,分成很多小函数,便于维护,连自己都不愿看,不愿改的代码,百分百有问题。
7.写程序时,先把流程搞清楚。把各个流程用的函数写清楚,函数可以留空,这样编程就变成了填空题。
做新功能时,把数据结构的设计,放在较重要的位置 -
数据结构知道什么?平时项目用过什么?(说了一个多叉树,然后说了一下和红黑树和哈希表的优劣对比)
-
构建堆的算法。时间复杂度是多少
-
如何不排序找中位数,时间复杂度是多少?
二面
主要根据你的简历问了很多,所以要细心准备简历上每一个东西,包括但不限于:
- 项目实现细节,如何改进?
- 项目用了什么数据结构,用了什么设计模式?
- 你看的书哪些印象最深刻(回去整理一下笔记,争取有话可讲)
- 你是机器人专业,为啥想做游戏?
- 你课外有学一些游戏相关的东西吗?(这个也要去准备一下)
智加
一面
-
进程间通信
-
signal kill
-
程序内存结构
-
static的作用
-
单例模式怎么实现?构造函数怎么写?
作者:Zopen 链接:https://zhuanlan.zhihu.com/p/62014096 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。#include <iostream> #include <thread> #include <mutex> using namespace std;std::once_flag flag;class Singleton { public:static Singleton& getInstance(){std::call_once(flag, []() {instance_.reset(new Singleton()); });return *instance_;}private:static std::unique_ptr<Singleton> instance_;private:Singleton() = default;Singleton(const Singleton& other) = delete;Singleton& operator=(const Singleton&) = delete; };std::unique_ptr<Singleton> Singleton::instance_;void do_onceflag() {Singleton& s = Singleton::getInstance();cout << &s << endl; }int main() {std::thread t1(do_onceflag);std::thread t2(do_onceflag);t1.join();t2.join();return 0; }
-
寻找共同最近祖先
-
多态了解吗?
-
知道右值吗?
可见左右值的概念很清晰,有地址的变量就是左值,没有地址的字面值、临时值就是右值。
右值就是拷贝了之后删了原来的,左值就是直接深拷贝?
大象的例子可以改成:大象本来放到A冰箱的, 后来想移动到B冰箱,移动语义的做法就是把冰箱上的A标志改成B标志, 这样就相当于放到了B冰箱,也不用重新去打造一个新的B冰箱了
-
最大连续子序列和
米哈游
一面
- 版本控制知道吗?git的原理?是分布式还是中心式?
- bash用过吗?C#用过吗
- 对象的地址,new了之后在哪里?堆区和栈区的区别?
- 指针和引用
- vector扩容机制
- map?
- 哈希函数
- 迭代器指导吗?
- struct和class的区别。C#里面struct可以继承吗?
- C#的内存回收机制?原理?
- bash管道怎么写?
- 手写一个排序算法,什么都可以,然后就默了一个快排。
二面
- 了解CICD吗?
- 了解自动化管线吗?
- 指针有什么用?为什么c++会要有这种东西啊
- malloc和new和区别?他们调用incline
- 自己写一个动态数组(然后接下来的问题
- 解释
腾讯
一面
-
问了项目,什么比较难,问了一下做的微信小程序,要怎么改进?
-
快排复杂度,比快排好的有什么
-
B树和B+树对比,应用场景?
-
平衡二叉树定义?插入了怎么维护性质
-
动态规划是什么?举个例子
-
深拷贝浅拷贝区别?有什么要注意的地方
-
虚函数有什么用
-
C++11特性知道吗?智能指针知道吗?
-
vector和list的扩容
-
任务调度算法?猜一下windows是用了什么
-
如何避免死锁?
-
如何实现同步?
-
HTTP和TCP的区别
-
HTTPS和HTTP的区别
-
get和pos区别
-
外件和组件
-
数据库相关:事物知道吗?索引知道吗
笔试题三道:
1.设定如下的对应关系( A=0,B=1,C=2,…,Z=25,AA=26,AB=27,…,AAA=xxx,… ),编写一个转换函数,根据上面的规则把一个字符串转换为数字
int StrToInt( const char * str ){
int length = str.size();
int sum = 0;
for(int i = 0;i<length);i++){
int temp = str[i] – ‘A’;
if (temp>=26 || temp<0){
return -1;
}
sum = 26 * sum + temp;
}
return sum;
}
2.从有序链表中去除重复的元素
(1, 1, 3, 3, 3, 5, 5, 5, 9, 9, 9, 9) -> (1, 3, 5, 9).
struct LinkNode {
int val;
struct LinkNode * next;
};
void remove( LinkNode * head ){
if (head == nullptr) return head;
LinkNode* L = head;
while(L->next){
if(L->val == l->next->val){
L->next = L->next->next;
}
}
return head;
}
\3. 费南德的金币游戏:费南德和你抢到20个银币和一个金币,你们的分赃规则:俩人轮流拿,每次至少拿一个最多不能超过四个(可以等于四个),金币和银币不能混合拿,金币最后拿!如果由你先拿,怎么才可以拿到那个金币?(写拿的方法,不是写代码)
拿四个稳赢,接下来他拿i个,你就拿5-i,这样一定会剩一个给他拿,这样我们就赢了
= str.size();
int sum = 0;
for(int i = 0;i<length);i++){
int temp = str[i] – ‘A’;
if (temp>=26 || temp<0){
return -1;
}
sum = 26 * sum + temp;
}
return sum;
}
2.从有序链表中去除重复的元素
(1, 1, 3, 3, 3, 5, 5, 5, 9, 9, 9, 9) -> (1, 3, 5, 9).
struct LinkNode {
int val;
struct LinkNode * next;
};
void remove( LinkNode * head ){
if (head == nullptr) return head;
LinkNode* L = head;
while(L->next){
if(L->val == l->next->val){
L->next = L->next->next;
}
}
return head;
}
\3. 费南德的金币游戏:费南德和你抢到20个银币和一个金币,你们的分赃规则:俩人轮流拿,每次至少拿一个最多不能超过四个(可以等于四个),金币和银币不能混合拿,金币最后拿!如果由你先拿,怎么才可以拿到那个金币?(写拿的方法,不是写代码)
拿四个稳赢,接下来他拿i个,你就拿5-i,这样一定会剩一个给他拿,这样我们就赢了