考场就坐
在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 。
当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上。如果有多个这样的座位,他会坐在编号最小的座位上。(另外,如果考场里没有人,那么学生就坐在 0 号座位上。)
返回 ExamRoom(int N) 类,它有两个公开的函数:其中,函数 ExamRoom.seat() 会返回一个 int (整型数据),代表学生坐的位置;函数 ExamRoom.leave(int p) 代表坐在座位 p 上的学生现在离开了考场。每次调用 ExamRoom.leave(p) 时都保证有学生坐在座位 p 上。
示例:
输入:["ExamRoom","seat","seat","seat","seat","leave","seat"], [[10],[],[],[],[],[4],[]]
输出:[null,0,9,4,2,null,5]
解释:
ExamRoom(10) -> null
seat() -> 0,没有人在考场里,那么学生坐在 0 号座位上。
seat() -> 9,学生最后坐在 9 号座位上。
seat() -> 4,学生最后坐在 4 号座位上。
seat() -> 2,学生最后坐在 2 号座位上。
leave(4) -> null
seat() -> 5,学生最后坐在 5 号座位上。
提示:
1 <= N <= 10^9
在所有的测试样例中 ExamRoom.seat() 和 ExamRoom.leave() 最多被调用 10^4 次。
保证在调用 ExamRoom.leave(p) 时有学生正坐在座位 p 上。
class ExamRoom {
public:
ExamRoom(int N) {
n = N;
}
int seat() {
if(s.empty()){
s.insert(0);
return 0;
}
int pre = -1, pos = 0;
int maxDist = *s.begin();
for(auto i: s){
if(pre != -1){
int dist = (i-pre)/2;
if(dist > maxDist){
maxDist = dist;
pos = pre + maxDist;
}
}
pre = i;
}
if(n-pre-1 > maxDist) pos = n-1;
s.insert(pos);
return pos;
}
void leave(int p) {
s.erase(p);
}
private:
set<int> s;
int n;
};
/**
* Your ExamRoom object will be instantiated and called as such:
* ExamRoom* obj = new ExamRoom(N);
* int param_1 = obj->seat();
* obj->leave(p);
*/
https://blog.csdn.net/yas12345678/article/details/52601454
数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int sum=0;
//得到异或结果,即为不相同两个数的异或结果sum
for(auto num:nums)
sum^=num;
//得到sum的二进制的1的最低位
int flag=(-sum)∑
vector<int> result(2);
//分成两个组进行异或,每组异或后的结果就是不相同两个数的其中之一
for(auto num:nums){
if((flag&num)==0)
result[0]^=num;
else{
result[1]^=num;
}
}
return result;
}
};
接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
// left[i]表示i左边的最大值,right[i]表示i右边的最大值
vector<int> left(n), right(n);
for (int i = 1; i < n; i++) {
left[i] = max(left[i - 1], height[i - 1]);
}
for (int i = n - 2; i >= 0; i--) {
right[i] = max(right[i + 1], height[i + 1]);
}
int water = 0;
for (int i = 0; i < n; i++) {
int level = min(left[i], right[i]);
water += max(0, level - height[i]);
}
return water;
}
};
环形数组循环
给定一个含有正整数和负整数的环形数组 nums。 如果某个索引中的数 k 为正数,则向前移动 k 个索引。相反,如果是负数 (-k),则向后移动 k 个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素。
确定 nums 中是否存在循环(或周期)。循环必须在相同的索引处开始和结束并且循环长度 > 1。此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。
示例 1:
输入:[2,-1,1,2,2]
输出:true
解释:存在循环,按索引 0 -> 2 -> 3 -> 0 。循环长度为 3 。
示例 2:输入:[-1,2]
输出:false
解释:按索引 1 -> 1 -> 1 ... 的运动无法构成循环,因为循环的长度为 1 。根据定义,循环的长度必须大于 1 。
示例 3:输入:[-2,1,-1,-2,-2]
输出:false
解释:按索引 1 -> 2 -> 1 -> ... 的运动无法构成循环,因为按索引 1 -> 2 的运动是向前的运动,而按索引 2 -> 1 的运动是向后的运动。一个循环中的所有运动都必须沿着同一方向进行。
链接:https://leetcode-cn.com/problems/circular-array-loop
/*双指针,python 据题:此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。*/
class Solution:
def circularArrayLoop(self, nums: List[int]) -> bool:
def getNext(i):
return (nums[i]+i)%N
N = len(nums)
for i in range(N):
if nums[i]==0: continue
slow = i # 慢指针代表当前位置
fast = getNext(i) # 快指针代表下一个位置
# 快慢指针同向且慢指针和快指针下一个也要同向
while(nums[slow]*nums[fast]>0 and nums[slow]*nums[getNext(fast)]>0):
if slow==fast: # 快慢指针相遇
if slow==getNext(slow):
break # 循环长度为1的情况
return True
slow = getNext(slow)
fast = getNext(getNext(fast))
return False
/*经过环状检查的数字需要用轮数来进行标注。比如第一轮检查的数字都标注上1,第二轮检查的数字都标注上2,直到找到一个可行的轮。如果遍历的时候碰触到别的轮,说明这个根本不行,直接下一轮。*/
class Solution {
public:
bool circularArrayLoop(vector<int>& nums) {
int N=nums.size();
vector<int> vis(N, 0);
int round=0;
bool dir=true; //true为正向,false为反向
for(int i=0;i<nums.size();i++)
{
if(vis[i]==0)
{
round++;
vis[i]=round;
int temp=i;
dir=nums[temp]>0?true:false;
int next=(temp+N+nums[temp]%N)%N;
if(next==temp||(dir!=(nums[next]>0?true:false)))
{
continue;
}
while(vis[next]==0)
{
//cout<<temp<<" "<<next<<endl;
if(next==temp||(dir!=(nums[next]>0?true:false)))
{
break;
}
temp=next;
vis[next]=round;
next=(temp+nums[temp]%N+N)%N;
}
if(next!=temp&&vis[next]==round)
{
return true;
}
}
}
return false;
}
};
摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
示例 1:
输入: [1,7,4,9,2,5]
输出: 6
解释: 整个序列均为摆动序列。
示例 2:输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。
示例 3:输入: [1,2,3,4,5,6,7,8,9]
输出: 2
链接:https://leetcode-cn.com/problems/wiggle-subsequence
/*利用摆动序列,波峰和波谷的差值最多为1的特点。一次遍历,常数空间。*/
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if (n < 2) {
return n;
}
int up = 1;
int down = 1;
for (int i = 1; i < n; i++) {
if (nums[i] > nums[i - 1]) {
up = down + 1;
}
if (nums[i] < nums[i - 1]) {
down = up + 1;
}
}
return max(up, down);
}
};
有序矩阵中第K小的元素
给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个元素。示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,返回 13。
链接:https://leetcode-cn.com/problems/kth-smallest-element-in-a-sorted-matrix
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
int l = matrix[0][0];
int r = matrix[matrix.size() - 1].back() + 1;
while(l < r) {
int m = l + (r - l) / 2;
int total = 0;
for(const auto& v : matrix)
total += upper_bound(v.begin(), v.end(), m) - v.begin();
if (total >= k) r = m;
else l = m + 1;
}
return l;
}
};