1. 主元素。一个数组长度为N,其中有超过一半的元素相同,用O(n)时间找到这个元素
主元素问题。一个集合中删除两个不同元素,主元素不变。
template<typename T>
T mainElement(T * array, int n)
{
T seed = array[0];
int count = 1;
for(int i = 1;i<n;++i)
{
if(seed == array[i])
{
count++;
}
else{
if(count > 0)count--;
else{
seed= array[i];
}
}
}
count=0;
for(int i = 0; i < n;++i)
{
if(array[i]==seed)++count;
}
if(count >=n/2)return seed;
return -1;
}
2. 有一栋楼100层,现在有两个鸡蛋。曾在一层楼,该层楼以以下楼层扔鸡蛋不会破裂,该层楼以上扔则会破裂,问尝试几次可以找到该层楼。
f(x,y)表示有x个鸡蛋可以扔y次,这种情况下可以测试的最高楼层,如f(1,1)=1,f(1,2)=2,...f(1,n)=n,f(2,1)=1
f(2,n)=1+f(1,n-1)+f(2,n-1) 分两种情况:
1 . f(1,n-1),即第一次尝试不成功,鸡蛋破碎,则此时最多可以测试f(1,n-1)层楼。
2. f(2, n- 1),第一个尝试成功,鸡蛋没破碎,则此时最多可以测试f(2,n-1)层楼
则一共可以测得1+f(1,n-1)+f(2,n-1) 层楼。因为我们可以现在f(1,n-1)+1层楼扔一次鸡蛋,如果成功,则还可以测得f(2,n-1),如果不成功,剩下的一个鸡蛋依旧可以测得f(1,n-1)层楼。所以一共可测1+f(1,n-1)+f(2,n-1) 层楼。
代码:
int egg(int eggs,int times){
if(eggs<1)return 0;
if(eggs == 1 || times == 1)return times;
return 1+egg(eggs-1,times-1)+egg(eggs,times-1);
}
3. 在一个二维空间中有n个点,问如何将n个点平均分割成3部分。
#include<iostream>
using namespace std;
struct pixel
{
int x;int y;
};
#define N 50
pixel ps[N];
void swap(pixel & p1,pixel & p2)
{
pixel tmp = p1;
p1 = p2;
p2 = tmp;
}
int partition(int l, int r)
{
int x = ps[l].x;
int y = ps[l].y;
int i = l,j=i+1;
for(;j<=r;++j)
{
if(ps[j].x>x/*&&ps[j].y>y*/){
i+=1;
swap(ps[i],ps[j]);
}
}
swap(ps[i],ps[l]);
return i;
}
int select(int l,int r,int cnt)
{
int i = partition(l,r);
int k = i-l+1;
if(k == cnt)return i;
if(k>cnt)return select(l,i-1,cnt);
return select(i+1,r,cnt-k);
}
int split(int part)
int l = 0,r=N-1;
int cnt = (r-l+1)/part;
int i = select(l,r,cnt);
int j = select(i+1,r,cnt);
cout<<"i = "<<i<<endl;
for(int k = 0; k<=i;++k){
cout<<"x="<<ps[k].x<<",y="<<ps[k].y<<endl;
}
cout<<"j = "<<j<<endl;
for(int k = i+1; k<=j;++k){
cout<<"x="<<ps[k].x<<",y="<<ps[k].y<<endl;
}
cout<<"r="<<r<<endl;
for(int k = j+1; k<=r;++k){
cout<<"x="<<ps[k].x<<",y="<<ps[k].y<<endl;
}
}
将一个二维空间的点分成3份,类似于select算法,先找到三分之一小的那个数,将所有点分层两份,一份是占所有点的三分之一,另一份占三分之二。然后在三分之二的数中再用select找到三分之一的点,这样所有点被分层3份了。
4. 有一个球,它可以爬楼梯,它每次可以爬一层,而可以一次爬二层,问它爬到n层楼梯有几种方法。
f(n)表示到第n层楼的方法个数f(n)=f(n-1)+f(n-2)因为到n层楼梯的有两种方法。一种是先到第n-1层,再爬一层即到n层。另一种是先到n-2层,再爬2层到n层。所以这是个fibonacci数列。
代码:
int fib(int i)
{
if(i == 1 || i == 2)return 1;
return fib(i-1)+fib(i-2);
}
int fib_dp(int i)
{
int dp[3];
dp[1]=dp[2]=1;
int tmp = 0;
for(int j = 3;j<=i;++j)
{
tmp = dp[2];
dp[2]+=dp[1];
dp[1]=tmp;
}
return dp[2];
}
分两种,一种是递归的,一种是动归的。递归的复杂度为2^n,动归的是O(n),差的还是挺多的。
5. 变态的递归。写一个函数func(i,n)打印i->n->i. 该函数只能有一个分号,不能用while,for,if,else等,只能用printf。
int func(int i,int N)
{
return ((i==N&&printf("%d\n",i))||(printf("%d\n",i)&&func(i+1,N)&&printf("%d\n",i)));
}
6. 二维空间中以一组线段[x,y],如 x1 > y2 则两个线段可以连接,写一个算法找到最多可连接的线段数量。
struct Interval
{
int left;
int right;
};
#define IN 5
Interval intervals[IN]={{1,8},{3,3},{5,5},{7,8},{9,9}};
bool compare(const Interval & it1,const Interval & it2)
{
return it1.left < it2.left;
}
void sortInterval()
{
sort(intervals,intervals+IN,compare);
}
int connect()
{
sortInterval();
int maxCnt = 1;
int maxRight = intervals[0].right;
int cnt=1;
for(int i = 1; i< IN;++i){
if(intervals[i].left <=maxRight){
cnt+=1;
if(intervals[i].right>maxRight){
maxRight = intervals[i].right;
}
}
else{
maxRight = intervals[i].right;
if(cnt>maxCnt)maxCnt = cnt;
cnt=1;
}
}
if(cnt>maxCnt)maxCnt=cnt;
return maxCnt;
}
区间树的题目,以区间的左边端点升序排序。记录当前连接区间所能到达的最右边端点大小,如果后续区间的左端点小于当前连接区间的最右端点则该区间可以加入当前连接区间,否则作为新的一个连接线段。