面试题-C++

算法问题

1、什么是堆,什么场景下用堆?答:堆排序,小根堆;典型的应用是优先队列(出队顺序与入队顺序无关,只与队列中元素的优先级有关,优先级最高的元素最先出队。)操作系统:选择优先级最高的任务执行。特别注意:理解“动态执行”这个概念。上网:服务端依次回应客户端的请求:通常也是使用优先队列。

2STL?答:Vector变长数组,倍增的思想、string、栈和队列、priority_queue push()  插入一个元素,top()  返回堆顶元素,pop()  弹出堆顶元素,定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;
3.有实现过某种二叉平衡树吗,难点在哪里,红黑树呢?答:平衡树是为了解决二叉查找树退化为链表的情况,而红黑树是为了解决平衡树在插入、删除等操作需要频繁调整的情况。

特点:二分查找思想1、具有二叉查找树的全部特性。2、每个节点的左子树和右子树的高度差至多等于1

具体讲解:https://mp.weixin.qq.com/s/dYP5-fM22BgM3viWg4V44A,缺点平衡树要求每个节点的左子树和右子树的高度差至多等于1,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过左旋右旋来进行调整,使之再次成为一颗符合要求的平衡树。红黑树特点:1、具有二叉查找树的特点。2、根节点是黑色的;3、每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存数据。4、任何相邻的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的。5、每个节点,从该节点到达其可达的叶子节点是所有路径,都包含相同数目的黑色节点。

4.map为什么能o(1)时间复杂度完成set/get操作,hashMap扩容如何保证在新的maphash取到原来的值,并且这种hash是公平的?答:Map(字典)的每一个元素叫做键值对,所谓键值对其实就是组成的一对。通过查找key(键)的方式,来获取相应的 value(值),并且 key 的值不可以重复

5、知不知道快速排序和希尔排序,希尔排序和快速排序区别?知道哪些排序算法,说一下时间复杂度,快速排序什么情况下时间复杂度最高?答:排序算法:快速排序(x,划分交换排序,分区操作,O(nlogn,n^2))、归并排序(分治法:mid分界点,左右递归排序,序列合并,未排序放到temp, Onlogn))、希尔排序(将数组列在一个表中并对列分别进行插入排序,根据步长序列的不同而不同O(n1.2次幂))、冒泡排序(重复地走访要排序的数列,一次比较两个数据元素,如果顺序不对则进行交换,并一直重复这样的走访操作,直到没有要交换的数据元素为止.O(n^2)):example

快速排序最坏的情况:待排序的序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列如果递归树画出来,它就是一棵斜树。此时需要执行n1次递归调用,且第i次划分需要经过ni次关键字的比较才能找到第i个记录,也就是枢轴的位置,因此比较次数为http://images.51cto.com/files/uploadimg/20110826/222653304.jpg ,最终其时间复杂度为O(n2)

6、图中的最短路径问题怎么求、迪杰斯特拉算法和弗洛伊德算法的区别有哪些?答:最短路径求法,迪杰斯特拉算法:贪心思想,按路径长度递增的次序一步一步并入来求取,是贪心算法的一个应用,用来解决单源点到其余顶点的最短路径问题。弗洛伊德算法:动态规划思想,i到任意节点j的最短路径不外乎2种可能:1)直接从节点i到节点j2)从节点i经过若干个节点k到节点j

https://img-blog.csdnimg.cn/20200224103723143.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI3MjYyNzI3,size_16,color_FFFFFF,t_70

时间复杂度平均情况下O(m),最坏情况下 O(nm)n表示点数,m表示边数

7B树和B+树的区别有哪些?答:1B+树中只有叶子节点会带有指向记录的指针(ROWID),而B树则所有节点都带有,在内部节点出现的索引项不会再出现在叶子节点中。2B+树中所有叶子节点都是通过指针连接在一起,而B树不会。

https://images2015.cnblogs.com/blog/576154/201609/576154-20160907130956629-1833512478.png

B+树的优点:非叶子节点不会带上ROWID,这样,一个块中可以容纳更多的索引项,一是可以降低树的高度。二是一个内部节点可以定位更多的叶子节点。叶子节点之间通过指针来连接,范围扫描将十分简单,而对于B树来说,则需要在叶子节点和内部节点不停的往返移动。

 B树的优点:对于在内部节点的数据,可直接得到,不必根据叶子节点来定位。

8、数组和链表,数组内存分配相关的一些问题:扩展新内存时以几倍扩展?答:数组得预留空间,需要申请占用内存大小,优点:随机访问强,查找速度快,缺点浪费内存插入和删除效率低。链表在内存中可以存在任何地方,不要求连续。优点:插入删除速度快,内存利用率高,扩展灵活。

9、指针和引用?答:指针一般指的是某块内存的地址,通过这个地址,我们可以寻址到这块内存;而引用是一个变量的别名,例如我们给小明起了个外号:明明,那我们说明明的时候,就是说小明。

10、栈和队列,是否需要连续内存?答:栈是一块连续的内存存储(页面前进后退),而队列(线程池请求排队)可以不是,

11、迭代器失效问题,map是否会迭代器失效?答:对于maplist,只有删除一个元素时,对应删除位置的迭代器会失效。
12、静态全局变量和全局变量:联系和区别?答:局部用完内存撤回,全局静态存在。

13、面向对象设计原则:多态,封装,继承?封装:public,private,protected,继承:软件复用,自动获得基类的除了构造函数和析构函数以外的所有成员,可以在派生类中添加新的属性和方法扩展其功能。多态:在程序设计中存在同名不同方法的存在,主要通过子类对父类的覆盖来实现多态,设计原则之一就是要依赖于抽象,而不依赖于具体,增加灵活性。

虚函数主要用于实现多态,有纯虚函数和非纯虚函数。纯虚函数需要子类必须重写父类函数。虚函数的存在使得类不可以实例化。技术上实现是virtual 0.虚函数以函数指针的形式存放在虚函数表里。初始存放的是父类虚函数的地址。若子类重写了父类的函数,则对应地址被覆盖为相应子类函数的地址。

1、代码在内存中的分布都有哪些区,宏定义存在刚才你说的哪个区域?堆栈有什么区别啊,堆中的数据会回收吗,mallocnew有什么区别?

答:栈区stack由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。堆区heap一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。全局区(静态区)(static,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。文字常量区常量字符串就是放在这里的。 程序结束后由系统释放,程序代码区—存放函数体的二进制代码。

宏定义在全局区,堆栈区别:栈区(stack由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。堆区(heap一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收

malloc从堆上分配内存;new/delete会调用构造函数/析构函数对对象进行初始化与销毁;operator new/delete可以进行重载;然后强行分析了一下自由存储区与堆的区别。

2、说一下虚函数表是什么(C++后台开发必问),为什么析构函数都是虚函数,不这样会怎么样?

概念:C++中的虚函数实现了多态的机制,也就是用父类型指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数,这种技术可以让父类的指针有“多种形态”,这也是一种泛型技术,也就是使用不变的代码来实现可变的算法。C++通过继承和虚函数来实现多态性,虚函数是通过一张虚函数表实现的,虚函数表解决了继承、覆盖、

1)为什么父类的析构函数必须是虚函数?

         当我们动态申请一个子类对象时,使用基类指针指向该对象,如果不用虚函数,子类的析构函数不能得到调用,也就是为了在释放基类指针时可以释放掉子类的空间,防止内存泄漏.

2)为什么C++默认的析构函数不是虚函数?

         因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。

3、多态和重载都是什么?

知不知道现在C++现在发行到那个版本了?C++11有哪些新特性?autoLambda表达式

c++虚函数和纯虚函数有什么区别,分别应用在什么场合?

虚函数是类的一种特殊成员函数,主要是为实现C++的多态特性引入。虚函数之所以“虚”是因为调用的虚函数不是在静态编译(静态编联)时确定,而是在运行时通过动态编联确定的。多态核心理念即是通过基类访问派生的子类,通常情况是借助基类指针来访问派生类对象。纯虚函数是一种特殊的虚函数,通常定义在基类中。纯虚函数在基类中定义方法是在函数声明末尾加“=0”,如 virtual funcint int = 0纯虚函数的“纯”体现为基类不需要实现它,其主要作用是为派生类定义函数接口框架,由派生类完成纯虚函数的实现。

子类析构会调用父类的析构函数吗?执行顺序是什么?答:会,一般情况下构造函数调用父类-》子类;析构函数调用子类-》父类delete a时调用类型A的析构函数步骤如下:
1,
到虚函数映射表中查找A的实际析构函数;
2,发现被实例化为B的析构函数;
3,调用B的析构函数;
4,调用A的析构函数---如果A的析构不是纯虚函数的话。

析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了。定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数。

构造函数和虚函数的,我当时也是只说出了一些皮毛。问了我非递归求二叉树的深度搜索,归并排序,Top K海量数据查找

算法:

1、给定一个无序数组,查找中位数

答:1、先进行一趟快排,使得div左边的值都比arr[div]小,div右边的值都比arr[div]大,但是这个div的位置是不确定的,可能位于中间,也可能偏左或者偏右。2、计算出mid所在的下标,如果是奇数则是mid=(size+1)/2,如果是偶数则是mid=size/23、此时需要比较middiv所在的位置。如果middiv所在位置的左边,此时就要递归去左半区间查找;如果middiv的右边,此时就要递归去右半区间查找;如果恰好相等则说明div/mid所在的位置就是中位数。代码实现如下:

2、给n个人 再给n个人的朋友关系 1-2这种表示12是朋友 21也是朋友,再给一个int k,问能否把这n个人分成k组,每组的人彼此都不是朋友,如果能,输出这种结果

excel里的列数如下 A,B,C…Z…AA,AB,AC…AZ,BA…AAA…给你一个字符串 问它是excel里的多少列 AB就是28

一个正整数k 问有没有一个所有位数都是1的数字,正好整除这个k,如果有,返回所有合法数中最小的那个的的位数,没有返回-1,如给你3,那么111是合法数中最小的,返回3

1.给你一个正整数k,可能会很大但不超过integer_max,再给你一个正整数m0-9),问1-k中间有多少个数,m出现了多少次,如给你k=12,给m=1,那么11112这三个数,1一共出现了4
2.一个Nint矩阵,人从(00)开始走,只能往上或者往右走,矩阵内的数值是该坐标的分值,分值只能获取一次,问这个人从(00)走到(N-1N-1)再走回来,往返获得的分值最大是多少

100万数据找top k

分析:先拿10000个数建堆,然后一次添加剩余元素,如果大于堆顶的数(10000中最小的),将这个数替换堆顶,并调整结构使之仍然是一个最小堆,这样,遍历完后,堆中的10000个数就是所需的最大的10000个。建堆时间复杂度是Omlogm),算法的时间复杂度为Onmlogm)(n10亿,m10000)。

优化的方法:可以把所有10亿个数据分组存放,比如分别放在1000个文件中。这样处理就可以分别在每个文件的10^6个数据中找出最大的10000个数,合并到一起在再找出最终的结果。

解决方案:分治+Trie/hash+小顶堆(就是上面提到的最小堆),即先将数据集按照Hash方法分解成多个小数据集,然后使用Trie树活着Hash统计每个小数据集中的query词频,之后用小顶堆求出每个数据集中出现频率最高的前K个数,最后在所有top K中求出最终的top K

判断一个数是不是2的幂?

两个单链表有公共节点,找出第一个?蛮力法:在第一链表上顺序遍历每个结点,每遍历到一个结点的时候,在第二个链表上顺序遍历每个结点。如果在第二个链表上有一个结点和第一个链表上的结点一样,说明两个链表在这个结点上重合,于是就找到了它们的公共结点。如果第一个链表的长度为m,第二个链表的长度为n,显然该方法的时间复杂度是O(mn)借助外部空间法:经过分析我们发现两个有公共结点而部分重合的链表,拓扑形状看起来像一个Y,而不可能像X,如下图所示,两个链表在值为6的结点处交汇:

https://images2015.cnblogs.com/blog/381412/201509/381412-20150919233802023-2017571878.jpg

  如果两个链表有公共结点,那么公共结点出现在两个链表的尾部。如果我们从两个链表的尾部开始往前比较,最后一个相同的结点就是我们要找的结点。But,在单链表中只能从头结点开始按顺序遍历,最后才能到达尾结点。最后到达的尾结点却要最先被比较,这是“后进先出”的特性。于是,我们可以使用栈的特点来解决这个问题:分别把两个链表的结点放入两个栈里,这样两个链表的尾结点就位于两个栈的栈顶,接下来比较两个栈顶的结点是否相同。如果相同,则把栈顶弹出接着比较下一个栈顶,直到找到最后一个相同的结点

代码链接:https://www.cnblogs.com/edisonchou/p/4822675.html

top k用了4种方法寻找,同时计算了每种方法的时间复杂度。判断2的幂也用了四种(不断模2;列出了所有2的幂指数后二分搜索;减一与本身取&;统计二进制中1的个数)。两个链表公共节点用的计算长度后遍历对比,不详细说了,其实还想到另一种首尾相连找链表环头结点的方法,当时还没说面试官就打断我说这道题okay了。树、图、贪心、dp等都没问,有点扎心

二分查找和一个很简单的二维数组搜索?答:采用二分查找法,时间复杂度为O(max(m,n))。先将给定的值key与二维数组右上角的元素比较,若相等,则返回true,若key小于它,则最后一列的元素肯定都大于key,此时可以删除掉最后一列,而若key大于它,则第一行的元素肯定都小于key,此时可以删除掉第一行,依次向下比较,如果比较到了左下角的元素,还没有发现等于key的,则返回fasle

3、单链表逆序?答:第一步反转,P1P2 也就是使得P2->next=P1. 如图: P1<----p2--->P3第二步,采用同样的方式,反转P3P2,也就是使得;P1<---P2<---P3。于是想到利用指针的特性,重用第一步的反转。这个时候只要使得P1指向P2P2指向P3,再重用第一步反转P1P2,即P2->next=P1.。相当于从P1开始整体指针往右移动,这样P2P3之间的反转由于指针重新赋值了,变成了可以直接重用P1P2的反转了。

4、一颗二叉树,给定某个节点的值,输出根节点到该节点所经历的所有节点?答:dfs/bfs

5、快速排序(然后分析了一波快排的稳定性,时间复杂度,最坏时间复杂度)答:参考自己博客

6、构造回文?答:

算法基础-字符移位?

Q今天在上厕所时想到了这个问题:有n个数,两两组成二元组,相差最小的有多少对呢?相差最大呢?答:思路:先用快排,然后对有序数组分别求差值最大的对数和差值最小的对数。快排之后,如果数组为常数数组,即最大值=最小值,则结果已经出来了。否则,进行下面的操作,其中:差值最大的好求,看有序数组有几个最小值和几个最大值,相乘即可。差值最小的,由于是有序数组,必定是相邻的差值较小,故由快排后的有序数组生成差值数组(即相邻的两两相减)。如果差值数组中0,则查看差值数组中连续0的组数,可以由排列组合知识计算总的差值最小的对数;如果差值数组中没有0,则只需计算差值数组中有多少个最小值即可。注:差值数组必定都是非负数。空间复杂度较大,需要一些辅助数组。时间复杂度:快排O(NlogN),求差值最大O(N),求差值最小O(N),所以最终的时间复杂度为O(NlogN)

非递归求二叉树的深度搜索

字符串转整数

  1. 任意地方的空白字符‘ ’
  2. 跳过任意地方的非数字字符
  3. 数值范围限制(INT_MAX or INT_MIN

我们还需要做到:

  1. 标记出正负
  2. 剔除空白字符
  3. 判断数值大小是否超过范围
  4. 非法输入

发布了176 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_27262727/article/details/104905285
今日推荐