018数据结构之队列——算法备赛

移掉k位数字

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

注意:输出不能有任何前导零,给定的num不包含前导0。

​ 从原数字移除所有的数字,剩余为空就是 0 。

原题链接

思路分析

判断两个正整数的大小关系,先看位数,位数相同,从左往右看第一个不同的数字,该位数字小的则小,例123467<123589;

题目说移掉k位数字,可以看成选择 t =(s-k)(s为字符串长度)位数字组成的最小数值。

我们可以遍历字符串,每遍历到一位判断是否保留或移除 ,关键问题是如何决策是否保留

我们将保留的数字尾插在一个双端队列中,队列中的每一个数对应所求的每一位,每次遍历到一位数字,将它与队列尾部的数字比较,较小的话,队列尾删,直到该数字大于队列尾部,入队。这样做可以确保高位数字尽量小同时要保证队列长度符合t

注:选用双端队列作为解题的数据结构而不选用栈的原因:将容器中数据转换为目标字符串时更方便,(栈顶------>栈底)=(低位数字------>高位数字)。

代码

cpp 复制代码
string removeKdigits(string num, int k) {
    int s1=num.size();
    int s=s1-k;
    string tar;
    if(!s) return "0";
    deque<char>st;
    for(int i=0;i<s1;i++){
        //s-st.size()表示队列剩余空间,s1-i表示字符串剩余长度
        while(!st.empty()&&num[i]<st.back()&&s-st.size()<s1-i){
            st.pop_back();
        }
        if(st.size()<s)
        st.push_back(num[i]);
    }
    while(!st.empty()){
        if(!tar.size()&&st.front()=='0'){st.pop_front(); continue;}  //去除前导0
        tar.push_back(st.front());
        st.pop_front();
    }
    if(!tar.size()) return "0";  //为空置为"0"
    return tar;
}

删除字符串中所有的相邻重复项

问题描述

给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。

你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。

在执行完所有删除操作后,返回最终得到的字符串。

本题答案保证唯一。

原题链接

思路分析

使用栈模拟删除的操作,使用队列返回最终得到的字符串,所以选择双端队列。

代码

cpp 复制代码
string removeDuplicates(string s, int k) {
    deque<pair<char,int>>dq;
    for(char ch:s){  
        if(dq.empty()){
            dq.push_back({ch,1});
        }else{
            auto&[key,val] = dq.back();
            if(key==ch){
                val++;
                if(val==k) dq.pop_back();  //k个重复项删除
            }else{
                dq.push_back({ch,1});
            }
        }
    }
    string ans;
    while(!dq.empty()){  //从头到尾返回结果字符串
        auto [key,val] =dq.front();
        ans+=string(val,key);
        dq.pop_front();
    }
    return ans;
}

知道秘密的人

问题描述

在第 1 天,有一个人发现了一个秘密。

给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后,每天 给一个新的人 分享 秘密。同时给你一个整数 forget ,表示每个人在发现秘密 forget 天之后会 忘记 这个秘密。一个人 不能 在忘记秘密那一天及之后的日子里分享秘密。

给你一个整数 n ,请你返回在第 n 天结束时,知道秘密的人数。由于答案可能会很大,请你将结果对 109 + 7 取余 后返回。

  • 2 <= n <= 1000
  • 1 <= delay < forget <= n

原题链接

思路分析

初步设想:可以设计一个双端队列,每天将遗忘秘密的人从头部出队 ,新发现秘密的人从尾部入队,最后队列中剩余的总人数就是答案。

初步设想其实是维护了两个临界点:遗忘秘密的临界点front和发现秘密的临界点back。这是一个大致的想法,还有一个问题是每个人要发现秘密delay天后才能分享秘密,所以还需维护一个可以分享秘密的临界点valid。为了实现这三个临界点的特殊队列,我们可以用一个数组来自己实现:

最后[front,back]区间内的总人数就是答案。因为三个指针每次移动的步长是一样的,可以用anssum来动态地维护[front,back][front,valid)内的总人数。

代码

cpp 复制代码
int peopleAwareOfSecret(int n, int delay, int forget) {
    const int MOD = 1e9+7;
    int ans = 1,sum = 0;
    vector<int>q;  //数组模拟队列
    q.push_back(1);
    int front=0;  //首尾指针
    int valid = front;  //[front,valid)范围内的人可分享秘密
    for(int i=2;i<=n;i++){
        if(i>delay){
            sum = (sum+q[valid++])%MOD;  //可分享秘密的人数增加
        }
        if(i>forget){
            ans = (ans-q[front]+MOD)%MOD;  //知道秘密的总人数减少
            sum = (sum-q[front++]+MOD)%MOD;  //可分享秘密的人减少
        }
        q.push_back(sum);  //新增发现秘密的人
        ans = (ans+sum)%MOD;  //更新答案

    }
    return ans;
}
相关推荐
浩泽学编程4 小时前
【源码深度 第1篇】LinkedList:双向链表的设计与实现
java·数据结构·后端·链表·jdk
怎么没有名字注册了啊5 小时前
求一个矩阵中的鞍点
数据结构·算法
Greedy Alg5 小时前
LeetCode 74. 搜索二维矩阵
算法
小猪咪piggy6 小时前
【算法】day7 滑动窗口+二分查找
算法
仟千意6 小时前
数据结构:二叉树
数据结构·算法
一水鉴天6 小时前
整体设计 逻辑系统程序 之34七层网络的中台架构设计及链路对应讨论(含 CFR 规则与理 / 事代理界定)
人工智能·算法·公共逻辑
DuHz6 小时前
C程序中的数组与指针共生关系
linux·c语言·开发语言·嵌入式硬件·算法
而后笑面对6 小时前
力扣2025.10.19每日一题
算法·leetcode·职场和发展
·白小白6 小时前
力扣(LeetCode) ——11.盛水最多的容器(C++)
c++·算法·leetcode