leetcode每日一题集合

303.区域和检索-数组不可变

给定一个整数数组  nums,处理以下类型的多个查询:

  1. 计算索引 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且次数为偶数]所需要的天数
  • f[i]=2+\sum_{k=j}^{i-1}f[k] 
  • (房间i访问2次+[j,i-1]内每个房间都得再次访问到)
  • 然后可以使用前缀式优化
  • s[0]=0,s[i+1]=\sum_{j=0}^{i}f[i]
  • f[i]=2+s[i]-s[j]; s[i+1]=s[i]+f[i];
  • 得到:
  • s[i+1]=s[i]*2-s[j]+2
  • 答案是f[0]+f[1]+f[2]+...+f[n-2]+1
  • 但从第0天就开始计数,所以s[n-1]+1-1
#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 组成。

  1. 一开始, curOrder 为 ["king"].
  2. 调用 Successor(king, curOrder) ,返回 Alice ,所以我们将 Alice 放入 curOrder 中,得到 ["king", "Alice"] 。
  3. 调用 Successor(Alice, curOrder) ,返回 Jack ,所以我们将 Jack 放入 curOrder 中,得到 ["king", "Alice", "Jack"] 。
  4. 调用 Successor(Jack, curOrder) ,返回 Bob ,所以我们将 Bob 放入 curOrder 中,得到 ["king", "Alice", "Jack", "Bob"] 。
  5. 调用 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
  • kingNameparentName, 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;
}

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值