字符串与栈和队列-算法小结

news2025/5/16 6:42:47

字符串

双指针

反转字符串(双指针)

力扣题目链接

void reverseString(vector<char>& s) {
    for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {
        swap(s[i],s[j]);
    }
}

反转字符串II

力扣题目链接
遍历字符串的过程中,只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间

    string reverseStr(string s, int k) {
        for(int i=0;i<s.size();i+=(2*k)){
            if(i+k <= s.size())
               {reverse(s.begin()+i,s.begin()+k+i);}
            else{
              reverse(s.begin()+i,s.end());
              }
        }
        return s;
    }

翻转字符串里的单词

我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转
    力扣题目链接
class Solution {
public:
    string reverseWords(string s) {
        reverse(s.begin(),s.end());
        int y=0;
        for(int i=0;i<=s.size();i++){
            if(s[i]==' '){
                reverse(s.begin()+y,s.begin()+i);
                y=i+1;
            }     
        }         
        reverse(s.begin()+y,s.end());
	    removeExtraSpaces(s);
        return s;    
    }


//移除元素(双指针)
     void removeExtraSpaces(string& s) {
        int slow = 0; 
        for (int i = 0; i < s.size(); ++i) { 
            if (s[i] != ' ') { 
                if (slow != 0) s[slow++] = ' '; 
                while (i < s.size() && s[i] != ' ') { 
                    s[slow++] = s[i++];
                }
            }
        }
        s.resize(slow); 
    }
}; 

实现 strStr()

力扣题目链接

class Solution {
public:
    int strStr(string haystack, string needle) {
        int n = haystack.size(), m = needle.size();
        for (int i = 0; i + m <= n; i++) {
            bool flag = true;
            for (int j = 0; j < m; j++) {
                if (haystack[i + j] != needle[j]) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
               return i;
            }
        }
        return -1;
    }
};

KMP算法-CSDN博客

重复的子字符串

class Solution {
public:
    void getNext (int* next, const string& s){
        next[0] = -1;
        int j = -1;
        for(int i = 1;i < s.size(); i++){
            while(j >= 0 && s[i] != s[j + 1]) {
                j = next[j];
           }
            if(s[i] == s[j + 1]) {
                j++;
            }
            next[i] = j;
        }
    }
    bool repeatedSubstringPattern(string s) {
       if (s.size() == 0) {
            return false;
        }
        int next[s.size()];
        getNext(next, s);
        int n = s.size();
        if(next[n-1]!=-1 && n% (n- (next[n - 1] + 1))==0){
             return true;
        }
         return false;
    }
};

栈与队列

字符串去重

栈的目的,就是存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素

从栈里弹出的元素是倒序的,所以再对字符串进行反转一下

class Solution {
public:
    string removeDuplicates(string S) {
        stack<char> st;
        for (char s : S) {
            if (st.empty() || s != st.top()) {
                st.push(s);
            } else {
                st.pop(); 
            }
        }
        string result = "";
        while (!st.empty()) { 
            result += st.top();
            st.pop();
        }
        reverse (result.begin(), result.end());
        return result;

    }
};

可以拿字符串直接作为栈,这样省去了栈还要转为字符串的操作。

class Solution {
public:
    string removeDuplicates(string S) {
        string result;
        for(char s : S) {
            if(result.empty() || result.back() != s) {
                result.push_back(s);
            }
            else {
                result.pop_back();
            }
        }
        return result;
    }
};

逆波兰表达式求值

计算机可以利用栈来顺序处理,不需要考虑优先级了。也不用回退了, 所以后缀表达式对计算机来说是非常友好的

中缀:a * ( b + c ) - d   ->  ((a * ( b + c )) - d)

//将每个运算符左移到当前括号前 
前缀:- * a + b c d       <-  - (*(a +( b  c ))  d)        //波兰式 
//将每个运算符左移到当前括号后
后缀:a b c + * d -       <-   ((a ( b  c )+) *  d)-       //逆波兰式

2分钟秒杀-中缀表达式转前后缀表达式_哔哩哔哩_bilibili
【算法】数据结构中的栈有什么用?_哔哩哔哩_bilibili

在这里插入图片描述

优先级可以看为加上括号

bool isOperator(char c)
{
	return c == '+' || c == '-' || c == '*' || c == '/'||c=='^';

}

map <char, int> precedence = {
	{'+',1},
	{'-',1},
	{'*',2},
	{'/',2},
	{'^',3}
};

int applyop(int a, int b, char c)
{

	switch (c){
	case'+':return a + b; 
	case'-':return a - b; 
	case'*':return a * b; 
	case'/':return a / b;
	case'^':return pow(a, b); 

	}

}

void valute(stack<int>& val, stack<char>& ope)
{
	int val2 = val.top(); val.pop();
	int val1 = val.top(); val.pop();
	char ops = ope.top(); ope.pop();
	val.push(applyop(val1, val2, ops));
}


int compute(const string& input)
{
	stack<int>val;
	stack<char> ope;
	for (int i=0;i<input.length();i++)
	{
		char ch = input[i];
		if (isspace(ch))continue;
		else if (isdigit(ch)) {
			int num = 0;
			while (i < input.length() && isdigit(input[i])) {
				num = num * 10 + (input[i] - '0');
				i++;
			}
			val.push(num);
			i--;
		}
		else if (ch == '(') {
			ope.push(ch);
		}
		else if (ch == ')'){
			while (!ope.empty() && ope.top() != '(')
			{	
				valute(val, ope);
			}
			ope.pop();
		}
		else if (isOperator(ch)) {
			while (!ope.empty() && precedence[ope.top()] >= precedence[ch]) {

				valute(val, ope);
			}
			ope.push(ch);
		}
	}
	while (!ope.empty()) {
		valute(val, ope);
	}

	return val.top();
}
int main()
{
	string expression;
	cin >> expression;
	cout << compute(expression) << endl;
	return 0;
}

compute
遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
   
        stack<long long> st; 
        for (int i = 0; i < tokens.size(); i++) {
            if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
                long long num1 = st.top();
                st.pop();
                long long num2 = st.top();
                st.pop();
                if (tokens[i] == "+") st.push(num2 + num1);
                if (tokens[i] == "-") st.push(num2 - num1);
                if (tokens[i] == "*") st.push(num2 * num1);
                if (tokens[i] == "/") st.push(num2 / num1);
            } else {
                st.push(stoll(tokens[i]));
            }
        }

        long long result = st.top();
        st.pop(); // 把栈里最后一个元素弹出(其实不弹出也没事)
        return result;
    }
};

滑动窗口最大值

![[Pasted image 20250405163224.png]]
大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了。所以不能用大顶堆。

此时我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么


class Solution {
    class MyQueue { //单调队列(从大到小)
        public:
        deque<int> que;
        void pop(int value){
            if(!que.empty()&&que.front()==value){
                que.pop_front();
            }
        }
        void push(int value){
            while(!que.empty()&&value>que.back()){
                que.pop_back();
            }
            que.push_back(value);
        }
         int front() {
            return que.front();
        }
    };
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue  n;
        vector<int>max_num;
        for(int i=0;i<k;i++){
           n.push(nums[i]);
        }
         max_num.push_back(n.front());
       for(int i=k;i<nums.size();i++){
          n.pop(nums[i-k]);  
          n.push(nums[i]);
          max_num.push_back(n.front());
        }
        return max_num;
    }
};

前 K 个高频元素

  1. 要统计元素出现频率
  2. 对频率排序
  3. 找出前K个高频元素

使用优先级队列来对部分频率进行排序

为什么不用快排呢, 使用快排要将map转换为vector的结构,然后对整个数组进行排序, 而这种场景下,我们其实只需要维护k个有序的序列就可以了,所以使用优先级队列是最优的

而且使用大顶堆就要把所有元素都进行排序,那能不能只排序k个元素呢?

所以我们要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

class Solution {
class mycomparison {
    public:
    bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
       return lhs.second > rhs.second;
    }
};                
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int>map;
        for(int i=0;i<nums.size();i++){
                map[nums[i]]++;
        }
        priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
        for(auto m:map){
            pri_que.push(m);
            if(pri_que.size()>k){
                pri_que.pop();
            }
        }
      vector<int> result(k);
        for (int i = k - 1; i >= 0; i--) {
            result[i] = pri_que.top().first;
            pri_que.pop();
        }
        return result;
   }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2334665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

类似东郊到家的上门按摩预约服务系统小程序APP源码全开源

&#x1f525; 为什么上门按摩正在席卷全国&#xff1f; 万亿蓝海市场爆发 2024年中国按摩市场规模突破8000亿&#xff0c;上门服务增速达65% 90后成消费主力&#xff0c;**72%**白领每月至少使用1次上门按摩&#xff08;数据来源&#xff1a;艾媒咨询&#xff09; 传统痛点…

Python | 在Pandas中按照中值对箱形图排序

箱形图是可视化数据分布的强大工具&#xff0c;因为它们提供了对数据集内的散布、四分位数和离群值的洞察。然而&#xff0c;当处理多个组或类别时&#xff0c;通过特定的测量&#xff08;如中位数&#xff09;对箱形图进行排序可以提高清晰度并有助于揭示模式。在本文中&#…

游戏引擎学习第215天

总结并为今天做铺垫 今天的工作内容是解决调试系统中的一个小问题。昨天我们已经完成了大部分的调试系统工作&#xff0c;但还有一个小部分没有完全处理&#xff0c;那就是关于如何层次化组织数据的问题。我们遇到的一个问题是&#xff0c;演示代码中仍有一个尚未解决的部分&a…

【Redis】redis事物与管道

Redis 事务&#xff08;Transaction&#xff09; 事务概念 事务&#xff1a;是一组操作的集合&#xff0c;是不可分割的工作单元。Redis 事务特点&#xff1a; 一个事务可以一次执行多个命令。所有命令都被顺序化&#xff0c;形成一个队列。所有命令在执行 EXEC 时一次性、顺…

Django信号使用完全指南示例

推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 **引言:****先决条件:****目录:****1. 什么是Django信号?****2:设置你的Django项目****2.1. 安装Django**2.2. 创建一个Django项…

vulkanscenegraph显示倾斜模型(5.9)-vsg中vulkan资源的编译

前言 上一章深入剖析了GPU资源内存及其管理&#xff0c;vsg中为了提高设备内存的利用率&#xff0c;同时减少内存(GPU)碎片&#xff0c;采用GPU资源内存池机制(vsg::MemoryBufferPools)管理逻辑缓存(VkBuffer)与物理内存(VkDeviceMemory)。本章将深入vsg中vulkan资源的编译(包含…

今日行情明日机会——20250409

今日行情还需要考虑关税对抗~ 2025年4月8日涨停的主要行业方向分析 1. 军工&#xff08;19家涨停&#xff09; 细分领域&#xff1a;国防装备、航空航天、军民融合。催化因素&#xff1a;国家安全战略升级、国防预算增加、重大军工项目落地预期。 2. 免税&#xff08;15家涨…

系统分析师(六)-- 计算机网络

概述 TCP/IP 协议族 DNS DHCP 网络规划与设计 逻辑网络设计 物理网络设计 题目 层次化网络设计 网络冗余设计 综合布线系统 IP地址 网络接入技术 其他网络技术应用 物联网

Qwen2.5-VL Technical Report 论文翻译和理解

一、TL&#xff1b;DR Qwen2.5-VL是QwenVL的最新模型&#xff0c;在视觉识别、精准目标定位、稳健文档解析以及长视频理解等方面实现了重大突破引入了动态分辨率处理和绝对时间编码&#xff0c;使其能够处理不同尺寸的图像以及长达数小时的视频&#xff0c;并实现秒级事件定位…

Foxmail邮件客户端跨站脚本攻击漏洞(CNVD-2025-06036)技术分析

Foxmail邮件客户端跨站脚本攻击漏洞&#xff08;CNVD-2025-06036&#xff09;技术分析 漏洞背景 ‌漏洞编号‌&#xff1a;CNVD-2025-06036 ‌CVE编号‌&#xff1a;待分配 ‌厂商‌&#xff1a;腾讯Foxmail ‌影响版本‌&#xff1a;Foxmail < 7.2.25 ‌漏洞类型‌&#x…

高并发内存池(三):PageCache(页缓存)的实现

前言&#xff1a; 在前两期内容中&#xff0c;我们深入探讨了内存管理机制中在 ThreadCache 和 CentralCache两个层级进行内存申请的具体实现。这两层缓存作为高效的内存分配策略&#xff0c;能够快速响应线程的内存需求&#xff0c;减少锁竞争&#xff0c;提升程序性能。 本期…

Linux驱动开发-网络设备驱动

Linux驱动开发-网络设备驱动 一&#xff0c;网络设备总体结构1.1 总体架构1.2 NAPI数据处理机制 二&#xff0c;RMII和MDIO2.1 RMII接口2.2 MDIO接口 三&#xff0c;MAC和PHY模块3.1 MAC模块3.2 PHY模块 四&#xff0c;网络模型4.1 网络的OSI和TCP/IP分层模型4.1.1 传输层&…

DataEase同比环比

DataEase同比环比 前言术语实现表结构设计DataEase设计创建数据集创建仪表盘最后前言 某大数据项目,需要比较展示今年跟去年的数据,如下图: 说明:比较24,25的产品销量,相同月份做一组,并排放一块 还有更进一步: 说明:比较24,25相同月份,相同产品的销量 直接用DataE…

RAG 工程基础

RAG 概念 RAG&#xff08;Retrieval - Augmented Generation&#xff09;技术是一种将检索与生成相结合的人工智能技术&#xff0c;旨在利用外部知识源来增强语言模型的生成能力&#xff0c;提高生成内容的质量、准确性和相关性。 具体来说&#xff0c;RAG 技术在处理用户输入的…

基础算法:滑动窗口_python版本

能使用滑动窗口的题&#xff0c;基本都需要数字为正整数&#xff0c;这样才能保证滑入一个数字总和是增加的(单调性) 一、209. 长度最小的子数组 思路&#xff1a; 已每个位置为右端点&#xff0c;依次加大左端点&#xff0c;最短不满足 sum(num[left,right]) < target的。…

Qt 之opengl shader language

着色器示例代码 实际运行效果

PyRoboPlan 库,给 panda 机械臂微分 IK 上大分,关节限位、碰撞全不怕

视频讲解&#xff1a; PyRoboPlan 库&#xff0c;给 panda 机械臂微分 IK 上大分&#xff0c;关节限位、碰撞全不怕 代码仓库&#xff1a;https://github.com/LitchiCheng/mujoco-learning 今天分享PyRoboPlan库&#xff0c;比之前的方式优点在于&#xff0c;这个库考虑了机械…

GPT - TransformerDecoderBlock

本节代码定义了一个 TransformerDecoderBlock 类&#xff0c;它是 Transformer 架构中解码器的一个基本模块。这个模块包含了多头自注意力&#xff08;Multi-Head Attention&#xff09;、前馈网络&#xff08;Feed-Forward Network, FFN&#xff09;和层归一化&#xff08;Lay…

LabVIEW 控制电机需注意的关键问题

在自动化控制系统中&#xff0c;LabVIEW 作为图形化编程平台&#xff0c;因其高度可视化、易于集成硬件等优势&#xff0c;被广泛应用于电机控制场景。然而&#xff0c;要实现稳定、精确、高效的电机控制&#xff0c;仅有软件并不足够&#xff0c;还需结合硬件选型、控制逻辑设…

Jenkins 发送钉钉消息

这里不介绍 Jenkins 的安装&#xff0c;可以网上找到很多安装教程&#xff0c;重点介绍如何集成钉钉消息。 需要提前准备钉钉机器人的 webhook 地址。&#xff08;网上找下&#xff0c;很多教程&#xff09; 下面开始配置钉钉机器人&#xff0c;登录 Jenkins&#xff0c;下载 …