美团2016招聘笔试:奇数位丢弃
对于一个由0…n的所有数按升序组成的序列,我们要进行一些筛选,每次我们取当前所有数字中从小到大的第奇数位个的数,并将其丢弃。重复这一过程直到最后剩下一个数。请求出最后剩下的数字。
输入描述:
每组数据一行一个数字,为题目中的n(n小于等于1000)。
输出描述:
一行输出最后剩下的数字。
输入例子:
500
输出例子:
255
方法一:常规解法,将数组不断更新,每一次去除奇位数的元素,直至数组的长度为1,输出即可。
算法复杂度:
T(n) = n + n/2 + n/4 +…1 = 2^n-1
故为O(2^n)
时间复杂度:
O(n)
代码:(偷个懒写个python版)
n = int(input())
lis = [i for i in range(0,n+1)]
while len(lis)!=1:
lis = [i for i in lis if (lis.index(i)+1)%2==0]
print(lis)
方法二:利用二进制巧妙解法
思路:
我们发现,每一次删除的元素都是对应下标位置的二进制最右端为0的元素,列如0(二进制为0),2(二进制为10),4(二进制为100)。剩余的元素例如1(01),3(11),5(101)在下一次的变换中对应的二进制下标为原二进制下标左移一位之后的1(1),3(10),5(10)之后继续删除对应下标的二进制数的最右端为0的元素。所以最后剩下的元素,一定是从右往左数二进制数中含1最多的元素,因为每一次删除移位后最右端都为1,会被一直保留下来
算法复杂度:
O(n)
空间复杂度:
O(1)
#include<iostream>
using namespace std;
int main(){
int n;
cin>>n;
int x = 1;
while( x <= n )
x = x*2 +1;
cout << (x>>1) << endl;
}
方法三:数学公式法
分析可知,每一次减少一半的数据,经过long(n)(向下取整)次减少到只有一个数。那么只有2^floor(log(n)) 或者 2^floor(log(n))-1 能够经过long(n)次的向下取整之后得到1.因为在第一次删除时,已经去除所有的偶数,因此排除2log(n))。所以这个数为2log(n))-1
算法复杂度:
时间复杂度:O(1)
空间复杂度:O(1)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
cout<<(int)pow(2,floor(log2(n)))-1<<endl;
return 0;
}