IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【每日多题之双指针】 -> 正文阅读

[数据结构与算法]【每日多题之双指针】

道阻且长,行则将至。

1、仅仅反转字母

1.1、题目描述

917、仅仅反转字母

给你一个字符串 s ,根据下述规则反转字符串:
????所有非英文字母保留在原有位置。
????所有英文字母(小写或大写)位置反转。
返回反转后的 s
示例:
输入:s = “ab-cd”
输出:“dc-ba”

1.2、题目分析

????(1)双指针的题目,维护两个指针 lr,初始化分别指向字符串首和尾;
????(2)使用 l 指针从左边开始扫描,r 指针从右边开始扫描找到 英文字母 ,也就是待反转的字符;
????(3)反转字符串之后,不要忘了左右指针也要继续扫描下去,直到扫描结束。

1.3、代码实现

class Solution {
public:
    string reverseOnlyLetters(string s) {
        int l = 0, r = s.size()-1;
        while(true){
            while(l < r && !isalpha(s[l])) ++l;
            while(l < r && !isalpha(s[r])) --r;
            if(l >= r){
                break;
            }
            std::swap(s[l], s[r]);
            ++l;
            --r;
        }
        
        return s;
    }
};

2、两数之和 II - 输入有序数组

2.1、题目描述

167、两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1]numbers[index2]
1 <= index1 < index2 <= numbers.length
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1index2
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

2.2、题目分析

????(1)双指针的经典题目了,也是比较简单的题目;
????(2)维护左右两个指针 leftright ,分别指向数组的首尾,思路很清晰,将当前左右指针指向的数之和与 target 比较;
????(3)大于目标值,右指针 -- ,小于目标值左指针 ++ ,否则目标值则返回。

2.3、代码实现

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int m = numbers.size();
		int left = 0, right = m - 1;
		while (left < right){
			int sum = numbers[left] + numbers[right];
			if(sum > target){
                --right;
            }
            else if(sum < target){
                ++left;
            }
            else{
                return {left+1, right+1};
            }
		}
		
		return { -1, -1 };
    }
};

3、区间子数组个数

3.1、题目描述

795、 区间子数组个数

给你一个整数数组 nums 和两个整数:leftright。找出 nums中连续、非空且其中最大元素在范围 [left, right] 内的子数组,并返回满足条件的子数组的个数。
生成的测试用例保证结果符合 32-bit 整数范围。
示例:
输入:nums = [2,1,4,3], left = 2, right = 3
输出:3
解释:满足条件的三个子数组:[2], [2, 1], [3]

3.2、题目分析

????(1)参考的是Leetcode中的题解,晚霞作棠
????(2)双指针维护可行区间,遍历每一个下标,贡献为当前双指针区间 [i, j]j 是区间中的大于等于 L 小于等于 R 的元素下标。

3.3、代码实现

class Solution {
public:
    int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
        int cnt = 0, l = 0;
        int j = -1;
        int i;
        for(i = 0; i < nums.size(); ++i){
            if(nums[i] >= left && nums[i] <= right){
                j = i;
            }
            if(nums[i] > right){
                l = i + 1;
                j = -1;
            }
            if(j != -1){
                cnt += (j - l + 1);
            }
        }

        return cnt;
    }
};

4、比较版本号

4.1、题目描述

165、比较版本号

给你两个版本号 version1version2,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.330.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 010 < 1
返回规则如下:
????如果 version1 > version2 返回 1
????如果 version1 < version2 返回 -1
????除此之外返回 0
示例:
输入:version1 = “1.01”, version2 = “1.001”
输出:0
解释:忽略前导零,“01” 和 “001” 都表示相同的整数 “1”

4.2、题目分析

????(1)比较版本号的大小,一个想法是两个版本的字符串按照 '.' 对应解析出来比较大小;
????(2)具体就是维护双指针分别指向 version1version2 ,遍历它们求出对应位置的版本号数字大小进行比较;
????(3)(Ⅰ)处的初始化为 0 是为了计算当前位置版本数字大小,也是当前位置无版本号时的数字。

4.3、代码实现

class Solution {
public:
    int compareVersion(string version1, string version2) {
        int n = version1.size(), m = version2.size();
        int i = 0, j = 0;
        while(i < n || j < m){
            long x = 0, y = 0;			// (Ⅰ)
            for(; i < n && version1[i] != '.'; ++i){
                x = x * 10 + version1[i] - '0';
            }
            ++i;
            for(; j < m && version2[j] != '.'; ++j){
                y = y * 10 + version2[j] - '0';
            }
            ++j;
            if(x != y){
                return x > y ? 1 : -1;
            }
        }

        return 0;
    }
};

5、压缩字符串

5.1、题目描述

443、压缩字符串

给你一个字符数组 chars,请使用下述算法压缩:
从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符
????如果这一组长度为 1 ,则将字符追加到 s 中。
????否则,需要向 s 追加字符,后跟这一组的长度。
压缩后得到的字符串 s 不应该直接返回 ,需要转储到字符数组 chars 中。需要注意的是,如果组长度为 1010 以上,则在 chars 数组中会被拆分为多个字符。
请在 修改完输入数组后 ,返回该数组的新长度。
你必须设计并实现一个只使用常量额外空间的算法来解决此问题。
示例:
输入:chars = [“a”,“a”,“b”,“b”,“c”,“c”,“c”]
输出:返回 6 ,输入数组的前 6 个字符应该是:[“a”,“2”,“b”,“2”,“c”,“3”]
解释:“aa” 被 “a2” 替代。“bb” 被 “b2” 替代。“ccc” 被 “c3” 替代。

5.2、题目分析

????(1)题目中明确要求了 修改完输入数组后 再返回数组的长度,并且要求只是用常量额外空间的算法解决此问题,因此使用原地操作来实现;
????(2)维护一个读指针 read ,一个写指针 write ,分别指向 chars 中待读取的字符下标和即将写入的位置下标;
????(3)当 read 移动到了最后一个位置或者移动到了某一段连续相同字符子串的最右侧位置时,我们就写入这个字符,计算并写入字符数量;
????(4)字符数量计算并写入这一块就很妙,妙在 reverse(&chars[anchor], &chars[write]) ,头一次见到这种用法,之前都是 reverse(chars.begin(), chars.end()) 这种用法,最多也就是 reverse(chars.begin(), chars.begin() + n) 这样了。但是其实原理都是一样的,第一个位置就是 开始位置的迭代器 嘛,第二个参数就是结束的 尾后迭代器 嘛。

5.3、代码实现

class Solution {
public:
    int compress(vector<char>& chars) {
        int n = chars.size();
        int read, write = 0, left = 0;
        for(read = 0; read < n; ++read){
            if(read == n-1 || chars[read] != chars[read+1]){
                chars[write++] = chars[read];
                int num = read - left + 1;
                if(num > 1){
                    int anchor = write;
                    while(num){
                        chars[write++] = num % 10 + '0';
                        num /= 10;
                    }
                    reverse(&chars[anchor], &chars[write]);
                }
                left = read + 1;
            }
        }

        return write;
    }
};

6、双指针使用小结

????(1)字符串数组 的题目,好像是废话,几乎所有的题目都是依托这两种数据结构来出题的,更进一步来讲,可以说是依托 数组 来出题的,C++ 里面称之为 容器vector。你看,链表的一些问题求解,比如反转链表、合并链表都可以先将链表元素撸到容器中,再对容器进行相应的操作,最后再撸回链表。所以说 容器 是最为常用的数据结构,以及最重要的数据结构一点都不为过;
????(2)既然 容器 很重要,那就肝它们,顺序容器:vectordequestring 等;关联容器:mapset ,以及按照关键字可重复出现的multisetmultimap ,还有各自对应的无序集合 unordered_mapunordered_set等等;
????(3)需要熟练各种容器的 初始化、元素的 、以及一些数据结构特有的操作。还有一些库函数的使用,比如 二分查找 ,找出大于等于 val 元素的最小元素 upper_bound(beg, end, val) 等。 当然能自己写的函数尽量自己去实现
????(4)双指针就是定义两个指向元素的 下标
????(5)双指针题目做的还不够多,暂时还没有什么特别的想法。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-07 11:22:24  更:2022-05-07 11:25:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:32:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码