303.区域和检索-数组不可变
给定一个整数数组 nums
,处理以下类型的多个查询:
- 计算索引
left
和right
(包含left
和right
)之间的nums
元素的 和 ,其中left <= right
实现 NumArray
类:
NumArray(int[] nums)
使用数组nums
初始化对象int sumRange(int i, int j)
返回数组nums
中索引left
和right
之间的元素的 总和 ,包含left
和right
两点(也就是nums[left] + nums[left + 1] + ... + nums[right]
)
示例 1:
输入: ["NumArray", "sumRange", "sumRange", "sumRange"] [[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]] 输出: [null, 1, -1, -3] 解释: NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]); numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3) numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1)) numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))
提示:
1 <= nums.length <= 104
-105 <= nums[i] <= 105
0 <= i <= j < nums.length
- 最多调用
104
次sumRange
方法
这是一道前缀和的题目
#include<iostream>
#include<vector>
using namespace std;
class NumArray {
vector<int> s;
public:
NumArray(vector<int>& nums) {
s.resize(nums.size()+1);
for(int i=0;i<nums.size();i++){
s[i+1]=s[i]+nums[i];
}
}
int sumRange(int left, int right) {
return s[right+1]-s[left];
}
};
/**
* Your NumArray object will be instantiated and called as such:
* NumArray* obj = new NumArray(nums);
* int param_1 = obj->sumRange(left,right);
*/
int main() {
vector<int> nums = {-2, 0, 3, -5, 2, -1};
NumArray* obj = new NumArray(nums);
int result1 = obj->sumRange(0, 2); // 返回 1,因为 sum(nums[0..2]) = sum(-2, 0, 3) = 1
int result2 = obj->sumRange(2, 5); // 返回 -1,因为 sum(nums[2..5]) = sum(3, -5, 2, -1) = -1
cout << "Result 1: " << result1 << endl;
cout << "Result 2: " << result2 << endl;
delete obj;
return 0;
}
1997.访问完所有房间的第一天
你需要访问 n
个房间,房间从 0
到 n - 1
编号。同时,每一天都有一个日期编号,从 0
开始,依天数递增。你每天都会访问一个房间。
最开始的第 0
天,你访问 0
号房间。给你一个长度为 n
且 下标从 0 开始 的数组 nextVisit
。在接下来的几天中,你访问房间的 次序 将根据下面的 规则 决定:
- 假设某一天,你访问
i
号房间。 - 如果算上本次访问,访问
i
号房间的次数为 奇数 ,那么 第二天 需要访问nextVisit[i]
所指定的房间,其中0 <= nextVisit[i] <= i
。 - 如果算上本次访问,访问
i
号房间的次数为 偶数 ,那么 第二天 需要访问(i + 1) mod n
号房间。
请返回你访问完所有房间的第一天的日期编号。题目数据保证总是存在这样的一天。由于答案可能很大,返回对 109 + 7
取余后的结果。
示例 1:
输入:nextVisit = [0,0] 输出:2 解释: - 第 0 天,你访问房间 0 。访问 0 号房间的总次数为 1 ,次数为奇数。 下一天你需要访问房间的编号是 nextVisit[0] = 0 - 第 1 天,你访问房间 0 。访问 0 号房间的总次数为 2 ,次数为偶数。 下一天你需要访问房间的编号是 (0 + 1) mod 2 = 1 - 第 2 天,你访问房间 1 。这是你第一次完成访问所有房间的那天。
示例 2:
输入:nextVisit = [0,0,2] 输出:6 解释: 你每天访问房间的次序是 [0,0,1,0,0,1,2,...] 。 第 6 天是你访问完所有房间的第一天。
示例 3:
输入:nextVisit = [0,1,2,0] 输出:6 解释: 你每天访问房间的次序是 [0,0,1,1,2,2,3,...] 。 第 6 天是你访问完所有房间的第一天。
提示:
n == nextVisit.length
2 <= n <= 105
0 <= nextVisit[i] <= i
这个题感觉好难啊
- 首次访问这个房间i时,第一次访问为奇数,所以下一天一定是访问j=nextVisit[i]房间;
- 而且访问偶数次之后才能访问右边下一个房间,所以i左边的房间,一定已经访问偶数次;
- 然后由于i-1都是偶数,到i房间为奇数次了,则这个时候会回到j=nextVisit[i]房间了
- 如果想要再次回到i房间,那么[j,i-1]范围内的每个房间,我们都会再次访问到
- 因此,设f[i]为[访问到房间i且次数为奇数]到[访问到房间i且次数为偶数]所需要的天数
- (房间i访问2次+[j,i-1]内每个房间都得再次访问到)
- 然后可以使用前缀式优化
- 设
- 得到:
- 答案是
- 但从第0天就开始计数,所以
#include<iostream>
#include<vector>
using namespace std;
int firstDayBeenInAllRooms(vector<int>& nextVisit) {
const int MOD=1'000'000'007;
int n=nextVisit.size();
vector<long>s(n);
for(int i=0;i<n-1;i++){
int j=nextVisit[i];
s[i+1]=(s[i]*2-s[j]+2+MOD)%MOD;
}
return s[n-1];
}
int main(){
vector<int>nextVisit={0,0};
int day=firstDayBeenInAllRooms(nextVisit);
cout<<day;
return 0;
}
虽然看起来式子很简短,但是其中需要自己推导的过程还是好复杂感觉。。。
2810.故障键盘
你的笔记本键盘存在故障,每当你在上面输入字符 'i'
时,它会反转你所写的字符串。而输入其他字符则可以正常工作。
给你一个下标从 0 开始的字符串 s
,请你用故障键盘依次输入每个字符。
返回最终笔记本屏幕上输出的字符串。
示例 1:
输入:s = "string" 输出:"rtsng" 解释: 输入第 1 个字符后,屏幕上的文本是:"s" 。 输入第 2 个字符后,屏幕上的文本是:"st" 。 输入第 3 个字符后,屏幕上的文本是:"str" 。 因为第 4 个字符是 'i' ,屏幕上的文本被反转,变成 "rts" 。 输入第 5 个字符后,屏幕上的文本是:"rtsn" 。 输入第 6 个字符后,屏幕上的文本是: "rtsng" 。 因此,返回 "rtsng" 。
示例 2:
输入:s = "poiinter" 输出:"ponter" 解释: 输入第 1 个字符后,屏幕上的文本是:"p" 。 输入第 2 个字符后,屏幕上的文本是:"po" 。 因为第 3 个字符是 'i' ,屏幕上的文本被反转,变成 "op" 。 因为第 4 个字符是 'i' ,屏幕上的文本被反转,变成 "po" 。 输入第 5 个字符后,屏幕上的文本是:"pon" 。 输入第 6 个字符后,屏幕上的文本是:"pont" 。 输入第 7 个字符后,屏幕上的文本是:"ponte" 。 输入第 8 个字符后,屏幕上的文本是:"ponter" 。 因此,返回 "ponter" 。
提示:
1 <= s.length <= 100
s
由小写英文字母组成s[0] != 'i'
使用deque更容易。deque用法深度解析,一篇文章弄懂deque容器各种操作-CSDN博客
#include<iostream>
#include<cstring>
#include<deque>
using namespace std;
string finalString(string s) {
deque<char>q;
bool head=false;
for(char ch:s) {
if(ch!='i'){
if(head){
q.push_front(ch);
}
else{
q.push_back(ch);
}
}
else{
head=!head;
}
}
string ans=(head?string{q.rbegin(),q.rend()} :string{q.begin(),q.end()});
return ans;
}
int main(){
string a="string";
cout<<finalString(a)<<endl;
return 0;
}
1600.王位继承顺序
一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点,这个家庭里有人出生也有人死亡。
这个王国有一个明确规定的王位继承顺序,第一继承人总是国王自己。我们定义递归函数 Successor(x, curOrder)
,给定一个人 x
和当前的继承顺序,该函数返回 x
的下一继承人。
Successor(x, curOrder): 如果 x 没有孩子或者所有 x 的孩子都在 curOrder 中: 如果 x 是国王,那么返回 null 否则,返回 Successor(x 的父亲, curOrder) 否则,返回 x 不在 curOrder 中最年长的孩子
比方说,假设王国由国王,他的孩子 Alice 和 Bob (Alice 比 Bob 年长)和 Alice 的孩子 Jack 组成。
- 一开始,
curOrder
为["king"]
. - 调用
Successor(king, curOrder)
,返回 Alice ,所以我们将 Alice 放入curOrder
中,得到["king", "Alice"]
。 - 调用
Successor(Alice, curOrder)
,返回 Jack ,所以我们将 Jack 放入curOrder
中,得到["king", "Alice", "Jack"]
。 - 调用
Successor(Jack, curOrder)
,返回 Bob ,所以我们将 Bob 放入curOrder
中,得到["king", "Alice", "Jack", "Bob"]
。 - 调用
Successor(Bob, curOrder)
,返回null
。最终得到继承顺序为["king", "Alice", "Jack", "Bob"]
。
通过以上的函数,我们总是能得到一个唯一的继承顺序。
请你实现 ThroneInheritance
类:
ThroneInheritance(string kingName)
初始化一个ThroneInheritance
类的对象。国王的名字作为构造函数的参数传入。void birth(string parentName, string childName)
表示parentName
新拥有了一个名为childName
的孩子。void death(string name)
表示名为name
的人死亡。一个人的死亡不会影响Successor
函数,也不会影响当前的继承顺序。你可以只将这个人标记为死亡状态。string[] getInheritanceOrder()
返回 除去 死亡人员的当前继承顺序列表。
示例:
输入: ["ThroneInheritance", "birth", "birth", "birth", "birth", "birth", "birth", "getInheritanceOrder", "death", "getInheritanceOrder"] [["king"], ["king", "andy"], ["king", "bob"], ["king", "catherine"], ["andy", "matthew"], ["bob", "alex"], ["bob", "asha"], [null], ["bob"], [null]] 输出: [null, null, null, null, null, null, null, ["king", "andy", "matthew", "bob", "alex", "asha", "catherine"], null, ["king", "andy", "matthew", "alex", "asha", "catherine"]] 解释: ThroneInheritance t= new ThroneInheritance("king"); // 继承顺序:king t.birth("king", "andy"); // 继承顺序:king > andy t.birth("king", "bob"); // 继承顺序:king > andy > bob t.birth("king", "catherine"); // 继承顺序:king > andy > bob > catherine t.birth("andy", "matthew"); // 继承顺序:king > andy > matthew > bob > catherine t.birth("bob", "alex"); // 继承顺序:king > andy > matthew > bob > alex > catherine t.birth("bob", "asha"); // 继承顺序:king > andy > matthew > bob > alex > asha > catherine t.getInheritanceOrder(); // 返回 ["king", "andy", "matthew", "bob", "alex", "asha", "catherine"] t.death("bob"); // 继承顺序:king > andy > matthew > bob(已经去世)> alex > asha > catherine t.getInheritanceOrder(); // 返回 ["king", "andy", "matthew", "alex", "asha", "catherine"]
提示:
1 <= kingName.length, parentName.length, childName.length, name.length <= 15
kingName
,parentName
,childName
和name
仅包含小写英文字母。- 所有的参数
childName
和kingName
互不相同。 - 所有
death
函数中的死亡名字name
要么是国王,要么是已经出生了的人员名字。 - 每次调用
birth(parentName, childName)
时,测试用例都保证parentName
对应的人员是活着的。 - 最多调用
105
次birth
和death
。 - 最多调用
10
次getInheritanceOrder
。
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
using namespace std;
class ThroneInheritance {
private:
string king;
unordered_map<string, vector<string>> familyTree;
unordered_set<string> dead;
void preorderTraversal(vector<string>& result, string person) {
if (!dead.count(person)) {
result.push_back(person);
}
for (const string& child : familyTree[person]) {
preorderTraversal(result, child);
}
}
public:
ThroneInheritance(string kingName) {
king = kingName;
familyTree[king];
}
void birth(string parentName, string childName) {
familyTree[parentName].push_back(childName);
}
void death(string name) {
dead.insert(name);
}
vector<string> getInheritanceOrder() {
vector<string> inheritanceOrder;
preorderTraversal(inheritanceOrder, king);
return inheritanceOrder;
}
};
int main() {
ThroneInheritance* obj = new ThroneInheritance("king");
obj->birth("king", "Alice");
obj->birth("Alice", "Jack");
obj->birth("Alice", "Bob");
vector<string> inheritanceOrder = obj->getInheritanceOrder();
cout << "Inheritance Order: ";
for (const string& person : inheritanceOrder) {
cout << person << " ";
}
cout << endl;
obj->death("Jack");
inheritanceOrder = obj->getInheritanceOrder();
cout << "Inheritance Order after Jack's death: ";
for (const string& person : inheritanceOrder) {
cout << person << " ";
}
cout << endl;
return 0;
}
这个题目经观察可知,应该使用多叉树的前序遍历。
主要代码是:
void preorderTraversal(vector<string>& result, string person) {
if (!dead.count(person)) {
result.push_back(person);
}
for (const string& child : familyTree[person]) {
preorderTraversal(result, child);
}
}
2009.使数组连续的最小操作数
给你一个整数数组 nums
。每一次操作中,你可以将 nums
中 任意 一个元素替换成 任意 整数。
如果 nums
满足以下条件,那么它是 连续的 :
nums
中所有元素都是 互不相同 的。nums
中 最大 元素与 最小 元素的差等于nums.length - 1
。
比方说,nums = [4, 2, 5, 3]
是 连续的 ,但是 nums = [1, 2, 3, 5, 6]
不是连续的 。
请你返回使 nums
连续 的 最少 操作次数。
示例 1:
输入:nums = [4,2,5,3] 输出:0 解释:nums 已经是连续的了。
示例 2:
输入:nums = [1,2,3,5,6] 输出:1 解释:一个可能的解是将最后一个元素变为 4 。 结果数组为 [1,2,3,5,4] ,是连续数组。
示例 3:
输入:nums = [1,10,100,1000] 输出:3 解释:一个可能的解是: - 将第二个元素变为 2 。 - 将第三个元素变为 3 。 - 将第四个元素变为 4 。 结果数组为 [1,2,3,4] ,是连续数组。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
这个题需要注意数组去重问题
如果只需要数组去重长度,那么int m=unique(nums.begin(), nums.end()) - nums.begin();;
如果需要数组,那么
// unordered_set<int> cnt(nums.begin(),nums.end());
// vector<int> sortedUniqueNums(cnt.begin(),cnt.end());
#include<iostream>
#include<vector>
#include<algorithm>
#include<unordered_set>
// #include<ranges>
using namespace std;
int minOperations(vector<int>& nums) {
int n=nums.size();
// unordered_set<int> cnt(nums.begin(),nums.end());
// vector<int> sortedUniqueNums(cnt.begin(),cnt.end());
// sort(sortedUniqueNums.begin(),sortedUniqueNums.end());
// ranges::sort(nums);
sort(nums.begin(),nums.end());
int m = unique(nums.begin(), nums.end()) - nums.begin(); // 原地去重
// int res=n,left=0;
// for(int i=0;i<sortedUniqueNums.size();i++) {
// //right-sortedUniqueNums[i]=n-1;
// int right=sortedUniqueNums[i]+n-1;
// while(left<sortedUniqueNums.size()&&sortedUniqueNums[left]<=right) {
// res=min(res,n-(left-i+1));
// left++;
// }
// }
// return res;
int ans = 0, left = 0;
for (int i = 0; i < m; i++) {
while (nums[left] < nums[i] - n + 1) { // nums[left] 不在窗口内
left++;
}
ans = max(ans, i - left + 1);
}
return n - ans;
}
int main(){
vector<int>nums={4,2,5,3};
cout<<minOperations(nums);
return 0;
}
1052.爱生气的书店老板
有一个书店老板,他的书店开了 n
分钟。每分钟都有一些顾客进入这家商店。给定一个长度为 n
的整数数组 customers
,其中 customers[i]
是在第 i
分钟开始时进入商店的顾客数量,所有这些顾客在第 i
分钟结束后离开。
在某些时候,书店老板会生气。 如果书店老板在第 i
分钟生气,那么 grumpy[i] = 1
,否则 grumpy[i] = 0
。
当书店老板生气时,那一分钟的顾客就会不满意,若老板不生气则顾客是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 minutes
分钟不生气,但却只能使用一次。
请你返回 这一天营业下来,最多有多少客户能够感到满意 。
示例 1:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], minutes = 3 输出:16 解释:书店老板在最后 3 分钟保持冷静。 感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.
示例 2:
输入:customers = [1], grumpy = [0], minutes = 1 输出:1
提示:
n == customers.length == grumpy.length
1 <= minutes <= n <= 2 * 104
0 <= customers[i] <= 1000
grumpy[i] == 0 or 1
当我看见这道题的时候,就感觉是滑动窗口题,我们只需要遍历一遍就可以得出这个最大结果
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
public:
int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int minutes) {
//使用滑动窗口
//初始化该窗口
int ans=0;
int i,j=0;
int n=customers.size();
vector<int>t(n,0);
for(int i=0;i<n;i++) {
if(grumpy[i]==0) ans+=customers[i];
}
// cout<<ans<<endl;
//初始化
for(int i=0;i<minutes;i++) {
if(grumpy[i]==1) ans+=customers[i];
}
// cout<<ans<<endl;
t[0]=ans;
for(i=minutes,j=0;i<n;i++,j++){
ans=ans+(grumpy[i]==0?0:customers[i])-(grumpy[j]==1?customers[j]:0);
// cout<<ans<<endl;
t[j+1]=ans;
}
return *max_element(t.begin(),t.end());
}
};
int main(){
// customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], minutes = 3
vector<int> customers={1,0,1,2,1,1,7,5};
vector<int> grumpy= {0,1,0,1,0,1,0,1};
int minutes=3;
Solution s;
cout<<s.maxSatisfied(customers,grumpy,minutes);
return 0;
}