C/C++面试,数组相关的题目十分重要。笔者根据上海三思、心动网络等公司笔试面试题来分析,
本文罗列了一些常见的面试题,仅供参考,如果您有更好的题目或者想法,欢迎留言讨论。目前有以下18道题目,如有好的题目,随时更新。
给定一个含有n个元素的整型数组a,求a中所有元素的和。可能您会觉得很简单,是的,的确简单,但是为什么还要说呢,原因:这道题要求用递归,只用一行代码。
分析
简单说一下,两种情况:
1. 如果数组元素个数为0,那么和为0;
2. 如果数组元素个数为n,那么先求出前n-1个元素之和,再加上a[n - 1]即可;
代码
求数组的最大值和最小值
给定一个含有n个元素的整型数组a,求a中的最大值和最小值。
分析
常规的做法是遍历一次,分别求出最大值和最小值,事实上,至多3|n/2|(||代表向下取整)就足以找出最小值和最大值。做法是:记录比较过程中遇到的最小值和最大值。我们并不是将每一个输入元素与当前的最大值和最小值进行比较(这样做的代价是每个元素需要两次比较),而是成对的处理元素。先将一对输入元素进行比较,然后把较小者与当前最小值进行比较,把较大者与当前最大值比较,因此每两个元素比较3次。
如何假定当前最小值和最大值的初始值依赖于n是奇数还是偶数。如果n是奇数,就将最小值和最大值都设为第一个元素的值,然后成对的处理余下的元素。如果n是偶数,就对前两个元素做一次比较,以决定最小值和最大值的初值,然后如同n是奇数的情形一样,成对的处理余下的元素。
但是这里要说的是分治法,将数组分成左右两部分,先求出左半部分的最大值和最小值,再求出右半部分的最大值和最小值,然后综合起来求总体的最大值和最小值。这是个递归的过程,对于划分后的左右两部分,同样重复这个过程,直到划分区间内只剩下一个元素或者两个元素。
代码
2求数组的最大值和次小值
给定一个含有n个元素的整型数组a,求a中的最大值和次小值。
分析
思路和上一题类似,同样是用分治法,先求出左边的最大值leftmax和次大值leftsecond,再求出右边的最大值rightmax和次大值rightsecond,然后合并,如何合并呢?分情况考虑。
1. 如果leftmax > rightmax,那么可以肯定leftmax是最大值,但次大值不一定是rightmax,但肯定不是rightsecond,只需将leftsecond和rightmax做一次比较即可;
2. 如果rightmax > leftmax,那么可以肯定的rightmax是最大值,但次大值不一样是leftmax,但肯定不是leftsecond,所以只需将leftmax与rightsecond做一次比较即可;
注意
这种方法无法处理最大元素有多个的情况,比如3,5,7,7,将返回7,7而不是7,5。感谢网友,从无到有靠他人指出。
代码
3求数组中出现次数超过一半的元素
上海三思软件工程师面试题。给定一个含有n个元素的整型数组a,其中有个元素x出现次数超过n/2,求出这个元素。据说是百度的一道题。
分析
暂且先不说当前作者的思路,我看到这个问题的时候,想到了如下方法:
1. 先进行排序,常规的方法就是快速排序,平均时间复杂度为O(nlogn)),然后取第n/2个元素即可,当然,这种方法的前提是存在这个所谓的“主元素”;若不清楚,还是需要验证的;
2. 由于肯定该数组中含有这么个元素,该题就转换为找中位数了。找中位数的方法线性时间内有两种:
- 数组求和
- 求数组中的最大值和最小值
- 求数组中的最大值和次大值
- 求数组中出现次数超过一半的元素
- 求数组中元素的最短距离
- 求两个有序数组的共同元素
- 求三个数组的共同元素
- 找出数组中唯一重复的元素
- 找出出现奇数次的元素
- 求数组中满足给定和的数对
- 最大子段和
- 最大子段积
- 数组循环移位
- 字符串逆转
- 组合问题
- 合并两个数组
- 重排问题
- 找出绝对值最小的元素
给定一个含有n个元素的整型数组a,求a中所有元素的和。可能您会觉得很简单,是的,的确简单,但是为什么还要说呢,原因:这道题要求用递归,只用一行代码。
分析
简单说一下,两种情况:
1. 如果数组元素个数为0,那么和为0;
2. 如果数组元素个数为n,那么先求出前n-1个元素之和,再加上a[n - 1]即可;
代码
- // 数组求和
- int sum(int*a, int n)
- {
- return n ==0?0 : sum(a, n -1) + a[n -1];
- }
求数组的最大值和最小值
给定一个含有n个元素的整型数组a,求a中的最大值和最小值。
分析
常规的做法是遍历一次,分别求出最大值和最小值,事实上,至多3|n/2|(||代表向下取整)就足以找出最小值和最大值。做法是:记录比较过程中遇到的最小值和最大值。我们并不是将每一个输入元素与当前的最大值和最小值进行比较(这样做的代价是每个元素需要两次比较),而是成对的处理元素。先将一对输入元素进行比较,然后把较小者与当前最小值进行比较,把较大者与当前最大值比较,因此每两个元素比较3次。
如何假定当前最小值和最大值的初始值依赖于n是奇数还是偶数。如果n是奇数,就将最小值和最大值都设为第一个元素的值,然后成对的处理余下的元素。如果n是偶数,就对前两个元素做一次比较,以决定最小值和最大值的初值,然后如同n是奇数的情形一样,成对的处理余下的元素。
但是这里要说的是分治法,将数组分成左右两部分,先求出左半部分的最大值和最小值,再求出右半部分的最大值和最小值,然后综合起来求总体的最大值和最小值。这是个递归的过程,对于划分后的左右两部分,同样重复这个过程,直到划分区间内只剩下一个元素或者两个元素。
代码
- // 求数组的最大值和最小值,返回值在maxValue和minValue
- void MaxandMin(int *a, int l, int r, int& maxValue, int& minValue)
- {
- if(l == r) // l与r之间只有一个元素
- {
- maxValue = a[l] ;
- minValue = a[l] ;
- return ;
- }
-
- if(l + 1 == r) // l与r之间只有两个元素
- {
- if(a[l] >= a[r])
- {
- maxValue = a[l] ;
- minValue = a[r] ;
- }
- else
- {
- maxValue = a[r] ;
- minValue = a[l] ;
- }
- return ;
- }
-
- int m = (l + r) / 2 ; // 求中点
-
- int lmax ; // 左半部份最大值
- int lmin ; // 左半部份最小值
- MaxandMin(a, l, m, lmax, lmin) ; // 递归计算左半部份
-
- int rmax ; // 右半部份最大值
- int rmin ; // 右半部份最小值
- MaxandMin(a, m + 1, r, rmax, rmin) ; // 递归计算右半部份
-
- maxValue = max(lmax, rmax) ; // 总的最大值
- minValue = min(lmin, rmin) ; // 总的最小值
- }
2求数组的最大值和次小值
给定一个含有n个元素的整型数组a,求a中的最大值和次小值。
分析
思路和上一题类似,同样是用分治法,先求出左边的最大值leftmax和次大值leftsecond,再求出右边的最大值rightmax和次大值rightsecond,然后合并,如何合并呢?分情况考虑。
1. 如果leftmax > rightmax,那么可以肯定leftmax是最大值,但次大值不一定是rightmax,但肯定不是rightsecond,只需将leftsecond和rightmax做一次比较即可;
2. 如果rightmax > leftmax,那么可以肯定的rightmax是最大值,但次大值不一样是leftmax,但肯定不是leftsecond,所以只需将leftmax与rightsecond做一次比较即可;
注意
这种方法无法处理最大元素有多个的情况,比如3,5,7,7,将返回7,7而不是7,5。感谢网友,从无到有靠他人指出。
代码
- // 找出数组的最大值和次大值,a是待查找的数组,left和right是查找区间,max和second存放结果
- void MaxandMin(int a[], int left, int right, int&max, int&second)
- {
- if(left == right)
- {
- max = a[left] ;
- second = INT_MIN;
- }
- elseif(left +1== right)
- {
- max = a[left] > a[right] ? a[left] : a[right] ;
- second = a[left] < a[right] ? a[left] : a[right] ;
- }
- else
- {
- int mid = left + (right - left) /2 ;
-
- int leftmax ;
- int leftsecond ;
- MaxandMin(a, left, mid, leftmax, leftsecond) ;
-
- int rightmax ;
- int rightsecond ;
- MaxandMin(a, mid +1, right, rightmax, rightsecond) ;
-
- if (leftmax > rightmax)
- {
- max = leftmax ;
- second = leftsecond > rightmax ? leftsecond : rightmax ;
- }
- else
- {
- max = rightmax ;
- second = leftmax < rightsecond ? rightsecond : leftmax ;
- }
- }
- }
3求数组中出现次数超过一半的元素
上海三思软件工程师面试题。给定一个含有n个元素的整型数组a,其中有个元素x出现次数超过n/2,求出这个元素。据说是百度的一道题。
分析
暂且先不说当前作者的思路,我看到这个问题的时候,想到了如下方法:
1. 先进行排序,常规的方法就是快速排序,平均时间复杂度为O(nlogn)),然后取第n/2个元素即可,当然,这种方法的前提是存在这个所谓的“主元素”;若不清楚,还是需要验证的;
2. 由于肯定该数组中含有这么个元素,该题就转换为找中位数了。找中位数的方法线性时间内有两种:
第一、基于分治法的线性期望时间求中位数,该方法是线性期望时间,类似于快速排序;
第二、最坏情况下也是线性时间复杂度的方法——“五分中项的中项”划分方法;
3、分治的思想 若a中存在主元素,则将a分为两部分后,a的主元素也必为两部分中至少一部分主元素,因此可用分治法。
将元素划分两部分,递归地检查两部分有无主元素。算法如下:
a. 若a中只含有一个元素,则此元素就是主元素,返回此数;
b. 将a分为两部分a1和a2(二者元素相等或只差一个),分别递归调用此方法求其主元素m1和m2;
c. 若m1和m2都存在且相等,则这个是就是a的主元素,返回次数;
d. 若m1和m2都存在且不相等,则分别检查这两个数是否为a的主元素,若有则返回此数,若无则返回空值;
e. 若m1和m2都不存在,则a无主元素,返回空值;
f. 若m1和m2只有一个存在,则检查这个数是否为a的主元素,若是则返回此数,若否就返回空值;
对于作者给出的这种算法,我在《编程之美》上看到过,即“寻找发帖水王”的题目。
设置一个当前值和当前值的计数器,初始化当前值为数组首元素,计数器值为1,然后从第二个元素开始遍历整个数组,对于每个被遍历到的值a[i]
1. 如果a[i] == currentvalue,则计数器加1;
2. 如果a[i]!= currentvalue,则计数器减一,如果计数器等于0,则更新当前值为a[i],并将计数器值重置为1;
代码
3、分治的思想 若a中存在主元素,则将a分为两部分后,a的主元素也必为两部分中至少一部分主元素,因此可用分治法。
将元素划分两部分,递归地检查两部分有无主元素。算法如下:
a. 若a中只含有一个元素,则此元素就是主元素,返回此数;
b. 将a分为两部分a1和a2(二者元素相等或只差一个),分别递归调用此方法求其主元素m1和m2;
c. 若m1和m2都存在且相等,则这个是就是a的主元素,返回次数;
d. 若m1和m2都存在且不相等,则分别检查这两个数是否为a的主元素,若有则返回此数,若无则返回空值;
e. 若m1和m2都不存在,则a无主元素,返回空值;
f. 若m1和m2只有一个存在,则检查这个数是否为a的主元素,若是则返回此数,若否就返回空值;
对于作者给出的这种算法,我在《编程之美》上看到过,即“寻找发帖水王”的题目。
设置一个当前值和当前值的计数器,初始化当前值为数组首元素,计数器值为1,然后从第二个元素开始遍历整个数组,对于每个被遍历到的值a[i]
1. 如果a[i] == currentvalue,则计数器加1;
2. 如果a[i]!= currentvalue,则计数器减一,如果计数器等于0,则更新当前值为a[i],并将计数器值重置为1;
代码
- void Find(int a[], int length)
- {
- int candidate;
- int i, ntimes;
- i = 0;
- ntimes = 0;
-
- for(i = 0; i < length; i++)
- {
- if(ntimes == 0)//计数为0时,读入新的元素,计数加1
- {
- candidate = a[i];
- ntimes = 1;
- }
- else
- {
- if(candidate == a