列表 arr 由在范围 [1, n] 中的所有整数组成,并按严格递增排序。请你对 arr 应用下述算法:
从左到右,删除第一个数字,然后每隔一个数字删除一个,直到到达列表末尾。
重复上面的步骤,但这次是从右到左。也就是,删除最右侧的数字,然后剩下的数字每隔一个删除一个。
不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
给你整数 n ,返回 arr 最后剩下的数字。
示例 1:
输入:n = 9
输出:6
解释:
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr = [2, 4, 6, 8]
arr = [2, 6]
arr = [6]
示例 2:
输入:n = 1
输出:1
提示:
1 <= n <= 10^9
2、分析
经过分析,我们可以发现
- 对于长度为n的数列,经过删除,其长度变为
?
x
/
2
?
\lfloor {x/2} \rfloor
?x/2?
- 每一轮删除之后,剩下的都是等差数列。而等差数列可以用
a
0
a_0
a0?和公差来表示,
a
n
a_n
an?可以通过
a
0
a_0
a0?和公差以及队列长度表示。所以我们只需要表示出每轮之后,
a
0
a_0
a0?和公差及数列长度的变化即可。
- 对于从左往右的删除,原来的
a
0
a_0
a0?一定会被删除,而新的
a
0
a_0
a0?就等于旧的
a
0
a_0
a0?+公差。对于从右向左的删除,如果长度为偶数个,那么
a
0
a_0
a0?不会被删除,否则会被删除,新的值为旧的
a
0
a_0
a0? + 公差。
3、题解
class Solution {
public:
int lastRemaining(int n) {
int begin = 1, step = 1,cnt = 1;
while(n != 1)
{
if(cnt % 2) begin += step;
else if(n % 2) begin += step;
step += step;
n >>= 1;
cnt ++;
}
return begin;
}
};
4、总结
- 对于等差数列可以用
a
0
a_0
a0?、公差、长度来进行表示,而无需处理整个队列的所有元素。一开始进行模拟的时候超时了。
|