双指针法练习题(2024/5/27)

news2024/9/20 22:56:44

1 反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例 1:

输入:s = "abcdefg", k = 2
输出:"bacdfeg"

示例 2:

输入:s = "abcd", k = 2
输出:"bacd"

提示:

  • 1 <= s.length <= 104
  • s 仅由小写英文组成
  • 1 <= k <= 104

思路:

  1. 通过循环每隔 2k 个字符遍历字符串 s
  2. 在循环中,根据剩余字符的数量分三种情况处理:
    • 若剩余字符数量大于等于 k,则反转前 k 个字符。
    • 若剩余字符数量少于 k 但大于 0,则反转剩余所有字符。
    • 若剩余字符数量为 0,则说明已经处理完所有字符,跳出循环并返回结果

代码:

class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s.begin() + i, s.begin() + i + k );
            } else {
                // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

2反转字符串中的单词

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例 1:

输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

输入:s = "  hello world  "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

输入:s = "a good   example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

提示:

  • 1 <= s.length <= 104
  • s 包含英文大小写字母、数字和空格 ' '
  • s 中 至少存在一个 单词

思路:

解题思路分为以下三个步骤:

  1. 移除多余空格:
    • 使用两个指针,一个快指针用于遍历字符串,一个慢指针用于记录有效字符的位置。
    • 先去除字符串前面的空格,然后遍历字符串中间部分,去除冗余的空格,最后去除末尾的空格。
    • 根据空格的位置移动慢指针,将有效字符复制到合适的位置。
    • 最后重新设置字符串大小,确保末尾没有空格。
  2. 将整个字符串反转:
    • 使用 reverse 函数将整个字符串进行反转,即将字符串首尾对应位置的字符互换。
  3. 将每个单词反转:
    • 使用两个指针 start 和 end 来标识每个单词的起始和结束位置。
    • 遍历字符串,当遇到空格或字符串末尾时,将当前单词的区间 [start, end] 进行反转。
    • 更新 start 指针为下一个单词的起始位置。

代码:

class Solution {
public:
    // 函数功能:翻转字符串s中从start到end的字符
    void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 []
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }

    // 函数功能:去除字符串s中的多余空格
    void removeExtraSpaces(string& s) {
        int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针

        // 去掉字符串前面的空格
        while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
            fastIndex++;
        }

        for (; fastIndex < s.size(); fastIndex++) {
            // 去掉字符串中间部分的冗余空格
            if (fastIndex - 1 > 0
                    && s[fastIndex - 1] == s[fastIndex]
                    && s[fastIndex] == ' ') {
                continue;
            } else {
                s[slowIndex++] = s[fastIndex];
            }
        }

        if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
            s.resize(slowIndex - 1);
        } else {
            s.resize(slowIndex); // 重新设置字符串大小
        }
    }

    // 函数功能:翻转字符串s中的单词
    string reverseWords(string s) {
        removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。
        reverse(s, 0, s.size() - 1);
        int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。
        for (int i = 0; i <= s.size(); ++i) {
            if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。
                reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。
                start = i + 1; //更新下一个单词的开始下标start
            }
        }
        return s;
    }
};

3反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

思路:

只需要改变链表的next指针的指向,直接将链表反转 

  1. 定义三个指针 curpre 和 temp,分别代表当前节点、前一个节点和临时节点。
  2. 将 cur 指针初始化为头节点 headpre 初始化为 nullptr,表示反转后的链表的尾节点为 nullptr
  3. 使用 while 循环遍历链表,直到当前节点 cur 为 nullptr,即遍历到链表尾部。
  4. 在循环中,先将 cur 的下一个节点暂存到 temp 中,以免丢失后续节点。然后将 cur 的 next 指针指向 pre,实现反转操作。
  5. 接着更新 pre 和 cur 的位置,将 pre 移动到 cur 的位置,cur 移动到 temp 的位置,继续下一轮循环。
  6. 当循环结束时,链表已经完成了反转,最后返回 pre,即为反转后的头节点

重点部分:

首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。

为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。

代码:

class Solution {
public:
    // 函数功能:反转链表
    ListNode* reverseList(ListNode* head) {
        ListNode* temp;
        ListNode* cur = head; // 当前节点指针
        ListNode* pre = nullptr; // 前一个节点指针

        while (cur) {
            temp = cur->next; // 暂存当前节点的下一个节点
            cur->next = pre; // 当前节点的下一个节点指向前一个节点,实现反转
            pre = cur; // 前一个节点指针后移
            cur = temp; // 当前节点指针后移
        }

        return pre; // 返回反转后的头节点
    }
};

4删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

思路:

  • 首先,定义了两个指针 fast 和 slow,初始都指向虚拟头结点。
  • 然后,fast 指针先向前移动 n + 1 步。这里为什么是 n + 1 呢?因为要保证 slow 指向要删除节点的前一个节点,方便后续删除操作。
  • 接着,fast 和 slow 同时向前移动,直到 fast 指针指向链表的末尾。
  • 最后,删除 slow 指向的下一个节点,即为倒数第 n 个节点。
重点部分:

fast 首先走 n + 1 步的重点在于确保 slow 指针能够指向待删除节点的前一个节点,这样在删除操作时就能够方便地处理。举个例子,如果 fast 先行 n 步而不是 n + 1 步,那么 slow 就会指向待删除节点本身,而不是它的前一个节点,这会使得删除操作变得复杂。因此,通过让 fast 先行 n + 1 步,可以确保 slow 正好指向待删除节点的前一个节点,从而简化删除操作的实现。

代码部分:

// 快指针先走n步,使得快指针和慢指针相距n个节点
        while (n-- && fast != nullptr) {
            fast = fast->next;
        }

        fast = fast->next; // 快指针再提前走一步,因为需要让慢指针指向删除节点的上一个节点

完整代码:

class Solution {
public:
    // 函数功能:删除倒数第N个节点
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0); // 创建虚拟头节点,简化边界情况处理
        dummyHead->next = head; // 将虚拟头节点指向原始头节点
        ListNode* slow = dummyHead; // 慢指针,指向待删除节点的前一个节点
        ListNode* fast = dummyHead; // 快指针,用于遍历链表

        // 快指针先走n步,使得快指针和慢指针相距n个节点
        while (n-- && fast != nullptr) {
            fast = fast->next;
        }

        fast = fast->next; // 快指针再提前走一步,因为需要让慢指针指向删除节点的上一个节点

        // 快慢指针一起移动,直到快指针到达链表尾部
        while (fast != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }

        // 删除倒数第N个节点
        slow->next = slow->next->next; 

        return dummyHead->next; // 返回删除节点后的头节点
    }
};

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

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

相关文章

【管理咨询宝藏114】贝恩为某知名化妆品战略规划方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏114】贝恩为某知名化妆品战略…

导入 FDTD 仿真的 S 参数到 INTERCONNECT 的器件中

导入 FDTD 仿真的 S 参数到 INTERCONNECT 的器件中 正文正文 很多时候,仿真链路比较大时,我们可以将仿真的每个部分分隔开来,用 FDTD 计算出每一部分的 S 参数,然后将这些 S 参数导入 INTERCONNECT 中得到最终的仿真结果。这里我们来介绍一下这种方法。 首先,我们从右侧…

洗地机哪个品牌的质量比较好?家用洗地机品牌排行榜

随着科技的迅速发展和生活水平的不断提高&#xff0c;洗地机凭借其集吸尘、拖地和洗地于一体的技术优势&#xff0c;成为了家庭清洁的理想选择。洗地机不仅能够高效清理各种地面污渍&#xff0c;还能同时处理干湿垃圾&#xff0c;极大地提升了清洁效率。然而&#xff0c;市场上…

【SpeedAI科研小助手】2分钟极速解决知网维普重复率、AIGC率过高,一键全文降!文件格式不变,公式都保留的!

知网、维普极速降重、降AIGC率方法 非常简单&#xff0c;打开SpeedAI科研小助手&#xff0c;使用一键降重&#xff0c;上传自己的论文文件&#xff0c;等待即可。 等待弄完了之后&#xff0c;直接下载&#xff0c;可以发现word格式保持不变。直接交就完事了&#xff0c;全程2…

YOLOv10:全面的效率-准确性驱动模型设计

YOLOv10&#xff1a;全面的效率-准确性驱动模型设计 提出背景精细拆分解法双重标签分配一致的匹配度量以效率为导向的模型设计 YOLO v10 总结1. 双重标签分配策略2. 一致匹配度量策略 论文&#xff1a;https://arxiv.org/pdf/2405.14458 代码&#xff1a;https://github.com/T…

Mac 安装 PostgreSQL简易教程

Mac 安装 PostgreSQL简易教程 下载安装包 下载安装包 下载地址 我下载的文件&#xff1a;Postgres-2.7.3-16.dmg 双击 dmg 文件安装 拖拽图标到右边的文件&#xff0c;然后到应用程序中找到 Postgres.app 双击打开。 然后点击 Initialize 按钮 配置$PATH 到命令下工具&#…

智慧管网 | “数字大脑”加速“能源动脉”新升级

行业背景 我国作为全球最大的发展中国家&#xff0c;随着工业化、城镇化的发展&#xff0c;工业企业与居民对原油、天然气消费需求不断增长。而油气管网作为一组连接石油和天然气生产基地、储气库、终端市场等节点的管道网络系统&#xff0c;是油气上下游衔接协调发展的关键环…

148.栈与队列:前K个高频元素(力扣)

代码解决 class Solution { public:// 自定义比较类&#xff0c;用于优先队列&#xff08;小顶堆&#xff09;class mycomparison{public:// 重载操作符&#xff0c;用于比较两个pair&#xff0c;基于pair的第二个值&#xff08;频率&#xff09;bool operator()(const pair&l…

【机器学习】随机梯度下降算法以及优化

一、概述&#xff1a; 什么是梯度下降&#xff1f; 梯度下降法的基本思想可以类比为一个下山的过程。 假设这样一个场景:一个人被困在山上&#xff0c;需要从山上下来(i.e.找到山的最低点&#xff0c;也就是山谷)。但此时山上 的浓雾很大&#xff0c;导致可视度很低。因此&am…

全球伦敦金交易时间每天都一样吗?

伦敦金市场是一个全球化的市场&#xff0c;它全天的交易盘面由亚洲、欧洲和北美市场无缝地连接而成&#xff0c;无论来自世界上什么地方的投资者参与其中&#xff0c;都可以得到全天接近24个小时的交易行情&#xff0c;只要有足够的精力&#xff0c;根本不用担心没有交易获利的…

ResNet残差网络的学习【概念+翻译】

基于何明凯前辈论文的学习 1.主要内容&#xff08;背景&#xff09; 1、首先提了一个base&#xff1a;神经网络的深度越深&#xff0c;越难以训练。 2、原因&#xff1a;因为随着神经网络层数的增加&#xff0c;通常会遇到梯度消失或梯度爆炸等问题&#xff0c;这会导致训练变…

小预算大效果:揭秘品牌如何用创新方法实现低成本传播

说到品牌&#xff0c;我们都知道&#xff0c;没钱是真的难搞。 品牌建设就像跑马拉松&#xff0c;得慢慢来&#xff0c;持续投入&#xff0c;一点一滴积累声誉&#xff0c;这样才能培养出忠实的粉丝团。 但别急&#xff0c;就算资金紧张&#xff0c;我们也有办法让品牌慢慢站…

for循环绑定id,更新html页面的文字内容

需求&#xff1a;将方法中内容对齐 实现方式 给for循环中每个方法添加一个动态的id在DOM结果渲染完后&#xff0c;更新页面数据&#xff0c;否则会报错&#xff0c;找不到对应节点或对应节点为空 <view v-for"(item, index) in itemList" :key"index"…

Linux学习(十二)-- 用户管理与用户组管理、su与exit命令、sudo命令

目录 1. 用户管理 注&#xff1a; 以下命令需root用户执行 1.1 创建用户 1.2 删除用户 1.3 查看用户所属组 1.4 修改用户所属组 2.用户组管理 注&#xff1a; 以下命令需root用户执行 2.1 创建用户组 2.2 删除用户组 拓展&#xff1a; 3. su命令与exit命令 4. sudo…

计算机网路概述

目录 计算机网络的概念 计算机网络的定义&#xff1a; 计算机网络的组成&#xff1a; 终端系统/资源子网 通信子网 计算机网络的类型 按照拓扑分类​编辑 按照范国分类&#xff1a; 按传输方式进行分类 计算机网络体系结构 传输方式 按照传输方向区分 按照传输对象…

移动应用平台—WorkPlus企业级移动应用平台解决方案

在当今数字化和移动化的时代&#xff0c;企业需要一个强大的、灵活性高的企业级移动应用平台来连接员工、提高工作效率和创新能力。企业级移动应用平台是一种专门为企业构建和管理移动应用的解决方案&#xff0c;为企业提供了强大的功能和高度可定制的移动应用开发工具。 一、…

USB外设管理软件是什么?有哪些特别好用的USB管理软件

USB外设管理软件是什么&#xff1f;有哪些特别好用的USB管理软件 USB外设管理软件是一种专门用于监控和管理计算机上连接的USB设备的软件工具。这类软件通常提供多种功能&#xff0c;以便用户或管理员能够更好地控制和管理USB设备&#xff0c;从而提高计算机系统的安全性和工作…

HubSpot企业商机管理和销售自动化:提升业务效率的利器

在当今数字化时代&#xff0c;企业出海已成为拓展市场、增加营收的重要途径。然而&#xff0c;如何高效地管理商机和实现销售自动化&#xff0c;成为许多企业面临的挑战。HubSpot作为一款强大的营销、销售和服务自动化平台&#xff0c;为企业提供了全方位的解决方案。今天运营坛…

【数据结构】图解红黑树以及代码实现

目录 一、相关概念 性质 二、图解 1、插入操作 2、parent在左边情况1&#xff1a;cur为红色节点parent也是红色节点、uncle也为红色节点 3、parent在左边情况2&#xff1a;cur为红色节点parent也是红色节点、uncle为黑色或者是空&#xff0c;cur是parent的left 4、parent…

边缘计算网关的市场价格趋势-天拓四方

在数字化转型的大潮中&#xff0c;边缘计算网关作为连接云端与终端设备的核心节点&#xff0c;其重要性日益凸显。然而&#xff0c;面对市场上琳琅满目的边缘计算网关产品&#xff0c;对于许多企业和个人用户来说&#xff0c;边缘计算网关的价格成为选择产品时的重要考量因素。…