leetcode链表&数组、字符串笔记(双指针、字符串处理STL函数及正则表达式去空格、提取字母和非数字)

持续更新中....

目录

一、链表(双指针,链表成环,取链表倒数第k个节点)

二、数组与字符串

1.反转字符串 344

2.最长公共前缀 14

3.两数之和 II - 输入有序数组 167(双指针,二分查找)

4.长度最小的子数组 209(双指针,二分查找)

5.翻转字符串里的单词 151(STL函数/正则表达式去空格)

验证回文字符串(双指针,正则表达式提取字母和数字)

6.删除排序数组中的重复项 26(双指针)

 移动零 283(数组,双指针)

7.无重复字符的最长子串 3(滑动窗口、散列)


一、链表(双指针,链表成环,取链表倒数第k个节点)

由于链表网上博客比较详细,所以偷个懒就不整理了。

  1. 主要参考:链表与二叉树。链表问题主要是用双指针方法解决的,非常简练。(包含链表成环问题、查找删除倒数第k个节点、合并链表、删除表中元素)
  2. 补充:leetcode链表面试题目集锦
  3. 补充:轻松搞定面试中的链表题目

二、数组与字符串

1.反转字符串 344

  1. 双指针,移动首尾指针交换字符直至中间
  2. 使用C++STL库,reverse(s.begin(),s.end())

2.最长公共前缀 14

知识点:取子字符串str.substr(pos,len)

  1. str为空串,则直接返回空串;
  2. 依次将第一个字符串的每个字符与其他字符串比对,直至出现不匹配,记录最多相同字符的个数cnt
  3. 返回第一个字符串的子串,strs[0].substr(0,cnt);

3.两数之和 II - 输入有序数组 167(双指针,二分查找)

题目描述:

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

1.双指针思想:使用两个指针,初始分别位于第一个元素和最后一个元素位置,比较这两个元素之和与目标值的大小。如果和等于目标值,我们发现了这个唯一解。如果比目标值小,我们将较小元素指针增加一。如果比目标值大,我们将较大指针减小一。

2.二分查找思想:第一重循环,移动左端点i(从0到n-1);第二重循环,j从i+1到n-1使用二分查找numbers[j],使得numbers[j]+numbers[i]=target。

4.长度最小的子数组 209(双指针,二分查找)

知识点:c++ 关于如何获取int型的最大值(包含在头文件<climits>中,符号常量INT_MAX)

题目描述:

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。
如果不存在符合条件的连续子数组,返回 0。

示例: 

输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。

双指针思想:我们都保持子数组的左端点不动去找右端点。其实一旦知道这个位置开始的子数组不会是最优答案了,我们就可以移动左端点。我们用 2 个指针,一个指向数组开始的位置,一个指向数组最后的位置,并维护区间内的和 sum大于等于 s 同时数组长度最小。

(双指针)
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int cnt = INT_MAX;
        int n = nums.size();
        for(int i=0, left=0, sum=0;i<n;i++){
            sum += nums[i]; //找到和可以大于等于s的位置
            while(sum>=s){  //移动左端点,直至最优解
                cnt = min(cnt,i-left+1);
                sum -= nums[left++];
            }
        }
        return cnt==INT_MAX?0:cnt;
    }
};

二分查找思想:新建数组prevsum(前n项和),第一重循环固定左端点i,第二重循环二分查找从i开始最小的连续子数组末尾(即prevsum[mid]-prevsum[i]>=s&&prevsum[mid-1]-prevsum[i]<s)。

(二分查找)
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int cnt= 0;
        int n = nums.size();
        if(n==0) return 0;
        vector<int> prevsum;
        prevsum.push_back(0);
        for(int i=0, sum=0;i<n;i++){
            sum += nums[i];
            prevsum.push_back(sum);
        }
        for(int i=0;i<n;i++){
            for(int left=i+1, right=n;left<=right;){
                int mid = (left + right)/2;
                if(prevsum[mid]-prevsum[i]>=s&&prevsum[mid-1]-prevsum[i]<s){
                    if(i>0){
                        int t = mid-i;
                        if(t<cnt) cnt = t;
                    }
                    else cnt = mid - i;
                    break;
                }
                else if(prevsum[mid]-prevsum[i]>=s){
                    right = mid -1;
                }
                else{
                    left = mid + 1;
                }
            }
        }
        return cnt;
    }
};

5.翻转字符串里的单词 151(STL函数/正则表达式去空格)

给定一个字符串,逐个翻转字符串中的每个单词。

 

示例 1:

输入: "the sky is blue"
输出: "blue is sky the"
示例 2:

输入: "  hello world!  "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:

输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-words-in-a-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:1)处理首尾空格、中间连续空格;2)翻转字符串;3)翻转单词

知识点:如下。参考链接:1)CCF第三题字符串处理stl函数及正则表达式 2)C++ 解析正则表达式

(字符串处理STL函数)
//查找
1)int string.find(const char *s, int pos=0,int n);//从pos开始查找字符串s中前n个字符在当前串中的位置(n和pos都可以不写)
2)int find(const string &s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
3)find_first_of('c');//查找字符c第一次在字符串中出现的位置,如果不存在则返回-1
4)find_first_not_of('c');//查找字符串中第一个不是字符c的字符位置,一般可以配合erase来使用,用来处理字符串前缀
5)find_last_of('c');
6)find_last_not_of('c');
//删除erase
1)erase(it);
2)erase(first,last);//迭代器
3)erase(pos,len);
//替代
1)str.replace(pos,len,str2);
2)str.replace(it1,it2,str2);
(正则表达式)
//处理空格
regex pattern("规则");
str = regex_replace(str,pattern,str2);
1)\s 匹配任何空白字符,包括空格、制表符、换页符等等
2)^ 匹配输入字符串的开始位置
3)$ 匹配输入字符串的结尾位置
4)+ 匹配前面的子表达式一次或多次
5){n,} 	n 是一个非负整数。至少匹配n 次。
//按空格分割字符串,存入数组
regex split("\\s");
sregex_token_iterator
        p(str.begin(),str.end(),split,-1);
sregex_token_iterator end;
vector<string> words;
while(p!=end){
    words.push_back(*p++);
}
//STL函数
lass Solution {
public:
    string reverseWords(string s) {
        s.erase(0,s.find_first_not_of(' '));
        s.erase(s.find_last_not_of(' ')+1,s.size());
        int pos = s.find("  ");
        while(pos!=-1){
            s.replace(pos,2," ");
            pos = s.find("  ");
        }
        reverse(s.begin(),s.end());
        pos = s.find(' ');
        string::iterator left = s.begin();
        string::iterator right = left+pos;
        while(pos!=-1){
            reverse(left,right);
            left = right + 1;
            pos = s.find(' ',pos+1);
            right = s.begin() + pos;
        }
        reverse(left,s.end());
        return s;
    }
};
//正则表达式
class Solution {
public:
    string reverseWords(string s) {
    string str = s;
    //处理空格
    regex htblank("^\\s+|\\s+$"); 
    regex midblank("\\s{2,}");
    str = regex_replace(str,htblank,"");
    str = regex_replace(str,midblank," ");
    //翻转整个字符串
    reverse(str.begin(),str.end());
    //根据空格分割字符串
    regex split("\\s");
    sregex_token_iterator
        p(str.begin(),str.end(),split,-1);
    sregex_token_iterator end;
    vector<string> words;
    while(p!=end){
        words.push_back(*p++);
    }
    //反转单词,重新拼接
    str.clear();
    for(vector<string>::iterator it=words.begin();it!=words.end();it++){
       reverse((*it).begin(),(*it).end());
       str += *it + " ";
    }
    str.erase(str.end()-1);
    return str;
    }
};

验证回文字符串(双指针,正则表达式提取字母和数字)

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama"
输出: true

双指针:先大小写转换,再用首尾指针逐个比对,跳过非字母非数字(推荐)。

正则表达式思路:提取字母和数字,大小写转换,逐个字母比对(或者直接reverse看两个字符串是否相等)。

\w 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]'
\W 匹配非字母、数字、下划线。等价于 '[^A-Za-z0-9_]'。

//大小写转换STL函数
include <algorithm>
transform(str1.begin(),str1.end(),str1.begin(),::tolower);
transform(str2.begin(),str2.end(),str2.begin(),::toupper);
//双指针
class Solution {
public:
    bool isPalindrome(string s) {
        transform(s.begin(),s.end(),s.begin(),::tolower);
        int left = 0, right = s.size()-1;
        while(left<right){
            if((s[left]<='9'&&s[left]>='0')||(s[left]>='a'&&s[left]<='z')){
                if((s[right]<='9'&&s[right]>='0')||(s[right]>='a'&&s[right]<='z')){
                    if(s[left]!=s[right]) return false;
                    else{
                        left++;
                        right--;
                    }
                }
                else{
                    right--;
                }
            }
            else{
                left++;
            }
        }
        return true;     
    }
};

 

//正则表达式
class Solution {
public:
    bool isPalindrome(string s) {
        regex notword("\\W");
        sregex_token_iterator
            p(s.begin(),s.end(),notword,-1);
        sregex_token_iterator end;
        //一定要用新的字符串保存*p
        /*原因:程序员负责确保传递给迭代器构造函数的 std::basic_regex 对象活得长于迭代器。因为迭代器存储指向 regex 的指针,故在销毁 regex 后自增迭代器会访问悬垂指针。*/
        /*作者把s.clear()重用,结果就是第一个字符会变成空格,蜜汁调了很久*/
        string result;
        while(p!=end){
            result += *p++;
        } 
        transform(result.begin(),result.end(),result.begin(),::tolower);//全部转小写
        for(int i=0,len=result.size();i<len/2;i++){
            if(result[i]!=result[len-i-1]) return false;
        } 
        return true;
    }
};

6.删除排序数组中的重复项 26(双指针)

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。
示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

思路:官方题解。简单的理解,就是要把不同的元素搬到数组的最前端。设置两个指针slow和fast,不断移动fast直至遇到与slow不同的元素,将它搬到slow下面的位置,直至fast遍历完整个数组。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size()==0||nums.size()==1) return nums.size();
        int slow = 0, fast = 1;
        while(fast<nums.size()){
            if(nums[slow]!=nums[fast]){
                nums[++slow] = nums[fast];
            }
            fast++;
        }
        return slow+1;
    }
};

 移动零 283(数组,双指针)

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/move-zeroes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:和上一题26删除数组中重复元素一样。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slow, fast;
        int len = nums.size();
        //查找第一个0的位置
        for(slow=0;slow<len;slow++){
            if(!nums[slow]) break;
        }
        for(fast = slow+1;fast<len;fast++){
            if(nums[fast]!=0){
                nums[slow++] = nums[fast];
                nums[fast]=0;
            }
        }
    }
};

7.无重复字符的最长子串 3(滑动窗口、散列)

题解:【leetcode】3. 无重复字符的最长子串题解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值