1.谷歌面试题:给定能随机生成整数 1 到 5 的函数,写出能随机生成整数 1 到 7 的函数。
回答:此题的关键是让生成的 1 到 7 的数出现概率相同。 只要我们可以从 n 个数中随机选出 1 到 n 个数,反复进行这种运算,直到剩下最后一个数 即可。 我们可以调用 n 次给定函数,生成 n 个 1 到 5 之间的随机数,选取最大数所在位置即 可满足以上要求。 例如 初始的 7 个数[1,2,3,4,5,6,7]. 7 个 1 到 5 的随机数[5,3,1,4,2,5,5] 那么我们保留下[1,6,7], 3 个 1 到 5 的随机数[2,4,1] 那么我们保留下[6] 6 就是我们这次生成的随机数。
2. 谷歌面试题:判断一个自然数是否是某个数的平方。当然不能使用开方运算。
回答: 假设待判断的数字是 N。
方法 1: 遍历从 1 到 N 的数字,求取平方并和 N 进行比较。 如果平方小于 N,则继续遍历;如果等于 N,则成功退出;如果大于 N,则失败退出。 复杂度为 O(n^0.5)。
方法 2: 使用二分查找法,对 1 到 N 之间的数字进行判断。 复杂度为 O(logn)。
方法 3: 由于 (n+1)^2 =n^2+2n+1, =... =1+(2*1+1)+(2*2+1)+...+(2*n+1) 注意到这些项构成了等差数列(每项之间相差 2) 。 所以我们可以比较 N-1,N-1-3,N-1-3-5...和 0 的关系。 如果大于 0,则继续减;如果等于 0,则成功退出;如果小于 0,则失败退出。 复杂度为 O(n^0.5)。不过方法 3 中利用加减法替换掉了方法 1 中的乘法,所以速度会更 快些。复杂度为 O(n^0.5)。
#define times 100
int calSqrt3(int N)
{
int k = 1;
int x = 0;
while(N > 0)
{
N -= k;
k += 2;
x++;
}
if(N == 0) return x;
else return -1;
}
int calSqrt2(int N)
{
int l = 1;
int h = N;
int mid;
while(l <= h){
mid = l + (h-l)/2;
int tmp = mid*mid;
if(tmp == N) return mid;
else if(tmp > N) h = mid-1;
else l = mid+1;
}
return -1;
}
int main()
{
//method3 O(N^0.5)
clock_t start,end;
double d;
start = clock();
for(int i = 1;i<=times;i++)
{
int tmp = calSqrt3(i);
// if(tmp > 0) cout<<i<<":"<<tmp<<endl;
}
end = clock();
d = (double)(end - start) / CLOCKS_PER_SEC;
cout<<d<<endl<<endl;
//method2 O(logn)
start = clock();
for(int i = 1;i<=times;i++)
{
int tmp = calSqrt2(i);
// if(tmp > 0) cout<<i<<":"<<tmp<<endl;
}
end = clock();
d = (double)(end - start) / CLOCKS_PER_SEC;
cout<<d<<endl<<endl;
return 0;
}
方法2的复杂度最小,实测也最快。
5. 谷歌面试题:在半径为 1 的圆中随机选取一点。
回答: 假设圆心所在位置为坐标元点(0,0)。
方法 1. 在 x 轴[-1,1],y 轴[-1,1]的正方形内随机选取一点。然后判断此点是否在圆内(通过计算 此点到圆心的距离) 。如果在圆内,则此点即为所求;如果不在,则重新选取直到找到为止。 正方形的面积为 4,圆的面积为 pi,所以正方形内的随机点在圆内的概率是 pi/4。
方法 2. 从[0,2*pi)中随机选一个角度,对应于圆中的一条半径,然后在此半径上选一个点。但 半径上的点不能均匀选取, 选取的概率应该和距圆心的长度成正比, 这样才能保证随机点在 圆内是均匀分布的。
7.谷歌面试题:设计一个数据结构,其中包含两个函数,1.插入一个数字,2.获得中数。并估计时间复杂度。
回答: 1).使用数组存储。 插入数字时,在 O(1)时间内将该数字插入到数组最后。 获取中数时,在 O(n)时间内找到中数。 (选数组的第一个数和其它数比较,并根据比较 结果的大小分成两组, 那么我们可以确定中数在哪组中。 然后对那一组按照同样的方法进一 步细分,直到找到中数。 )
2).使用排序数组存储。 插入数字时,在 O(logn)时间内找到要插入的位置,在 O(n)时间里移动元素并将新数字 插入到合适的位置。获得中数时,在 O(1)复杂度内找到中数。
3).使用大根堆和小根堆存储。 使用大根堆存储较小的一半数字,使用小根堆存储较大的一半数字。 插入数字时,在 O(logn)时间内将该数字插入到对应的堆当中,并适当移动根节点以保 持两个堆数字相等(或相差 1) 。 获取中数时,在 O(1)时间内找到中数。
方法3代码如下:
#include<iostream>
#include<vector>
#include <ctime>
#include <cstdlib>
using namespace std;
template <class T>
class MidVec{
vector<T> smallerHeap;
vector<T> largerHeap;
int mid;
void minHeapify(vector<T> & vec,int i)
{
int left = i*2 + 1;
int right = i*2 + 2;
int smallest = i;
if(left < vec.size()){
smallest = (vec[left] < vec[smallest])?left:smallest;
}
if(right < vec.size())
{
smallest = (vec[right] < vec[smallest])?right:smallest;
}
if(smallest != i){
swap(vec[i],vec[smallest]);
minHeapify(vec,smallest);
}
}
void maxHeapify(vector<T> & vec,int i)
{
int left = i*2 + 1;
int right = i*2 + 2;
int largest = i;
if(left < vec.size()){
largest = (vec[left] > vec[largest])?left:largest;
}
if(right < vec.size())
{
largest = (vec[right] > vec[largest])?right:largest;
}
if(largest != i){
swap(vec[i],vec[largest]);
maxHeapify(vec,largest);
}
}
public:
void push(int i)
{
if(i > mid){
if(largerHeap.size() > smallerHeap.size()){
largerHeap[0] = i;
smallerHeap.push_back(mid);
swap(smallerHeap[0],smallerHeap[smallerHeap.size()-1]);
for(int j = smallerHeap.size()/2;j>=0;j--)
maxHeapify(smallerHeap,j);
}else{
largerHeap.push_back(i);
swap(largerHeap[0],largerHeap[largerHeap.size()-1]);
}
for(int j = largerHeap.size()/2;j>=0;j--)
minHeapify(largerHeap,j);
}else{
if(smallerHeap.size() > largerHeap.size()){
smallerHeap[0] = i;
largerHeap.push_back(mid);
swap(largerHeap[0],largerHeap[largerHeap.size()-1]);
for(int j = largerHeap.size()/2;j>=0;j--)
minHeapify(largerHeap,j);
}else{
smallerHeap.push_back(i);
swap(smallerHeap[0],smallerHeap[smallerHeap.size()-1]);
}
for(int j = smallerHeap.size()/2;j>=0;j--)
maxHeapify(smallerHeap,j);
}
if(smallerHeap.size() >= largerHeap.size()) mid = smallerHeap[0];
else mid = largerHeap[0];
}
int getMid()
{
return mid;
}
};
int main()
{
MidVec<int> vec;
int a[] = {1,7,8,4,2,5,3,6,9,0};
for(int i = 0;i<10;i++)
{
srand(a[i]); //根据系统时间设置随机数种子
int tmp = rand() % 100;
cout<<tmp<<" ";
vec.push(tmp);
cout<<vec.getMid()<<endl;
}
return 0;
}