字符串

"本文探讨了C/C++中字符串的处理,包括字符串以''结尾的特点,以及可能导致的越界问题。同时,介绍了双指针在字符串替换、数组操作及寻找数组元素特定组合中的应用,如替换空格、移除元素、两数之和、三数之和和四数之和的问题。这些案例展示了双指针在提高算法效率上的作用。"
摘要由CSDN通过智能技术生成

字符串 & 双指针

字符串是由若干字符组成的序列。

C/C++中每个字符串都以字符’\0’作为结尾,这样就能很方便地找到字符串的尾部。因此,每个字符串都有一个额外字符的开销,稍不留神就会造成字符串的越界

strcpy(str,"01234567899");
cout<<str<<endl;

报错如下:
在这里插入图片描述
为了节省内存,C/C++把常量字符串放到单独的一个内存区域。当几个指针赋值给相同的常量字符串时,它们实际上会指向相同的内存地址。但是用常量内存初始化数组,情况却有所不同。

int main(){
    char str1[]="hello world";
    char str2[]="hello world";

    char* str3 = "hello world";
    char* str4 = "hello world";

    if(str1==str2)
        cout<<"str1 and str2 are the same\n";
    else
    {
        cout<<"str1 and str2 are the same\n";
    }

    if(str3==str4)
        cout<<"str3 and str4 are the same\n";
    else
    {
        cout<<"str3 and str4 are the same\n";
    }

    
    
    return 0;
}

结果:
在这里插入图片描述

题目05 替换空格

在这里插入图片描述
**思路:**双指针法

通过代码

class Solution {
public:
    string replaceSpace(string s) {
        int originalLength=s.size();
        int numOfBlank=0;
        int newLength=0;
        
        if(s.empty())
            return s;
        
        for(int i=0;i<originalLength;i++){
            if(s[i] == ' ')
                numOfBlank++;//空格数加1
        }
        //i指向原字符串的末尾
        int i=originalLength;
        newLength = originalLength + 2*numOfBlank;
        s.resize(newLength);
        int j = newLength;//j指向新字符串的末尾

        while(i<j && i>=0 && j>=0){
            if(s[i] != ' '){
                s[j] = s[i];
                i--;
                j--;
            }
            else{
                i--;
                s[j] = '0';
                j--;
                s[j] = '2';
                j--;
                s[j] = '%';
                j--;
            }
        }

        return s;
    }
};

更优美一点的代码

class Solution {
public:
    string replaceSpace(string s) {
        int originalLength=0;
        int numOfBlank=0;
        int newLength=0;
        int i=0;
        if(s.empty())
            return s;
        
        while(s[i] != '\0'){
            if(s[i] == ' '){
                numOfBlank++;
            }
            i++;
        }
        //i指向原字符串的末尾
        originalLength = i;
        newLength = originalLength + 2*numOfBlank;
        s.resize(newLength);
        int j = newLength;//j指向新字符串的末尾

        while(i<j && i>=0 && j>=0){
            if(s[i] != ' '){
                s[j--] = s[i];
            }
            else{
                s[j--] = '0';
                s[j--] = '2';
                s[j--] = '%';
            }
            i--;
        }

        return s;
    }
};

注意:

  1. s的大小要改变,使用resize()函数。
  2. 对内存覆盖有足够的警惕性。

时间效率是 O ( n ) O(n) O(n)。本题只是拓展了字符串的大小,并未进行复制等操作,因此空间的消耗也是最小的。

举一反三

在合并两个数组(包括字符串)时,如果从前往后复制每个数字(或字符)需要重复移动数字(或字符)多次,我们就可以考虑从后往前复制,这样就能减少移动的次数,从而提高效率。

双指针相关题目

27 移除元素

在这里插入图片描述
在这里插入图片描述
想法:
本题元素的顺序可以改变!!!
最后与val相等的元素可以直接去掉。
两个指针,一个在头,一个在尾,头动。

注意:
一定要注意数组的范围,不要越界!!!

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int len = nums.size();
        //异常情况,数组长度为0 
        if(len == 0 )
            return 0;
        
        int i = 0;
        int j = len-1; 

        while(i<=j && i<len && j>=0){ 
            while(j>=0 && nums[j] == val){//要注意j的范围,不然数组超出范围了
                j--;
                len--;
            }

            while(i<len && nums[i] != val)//同样要注意数组的范围
                i++;

            if( i<j && i<len && j>=0 && nums[i] == val){//在这里还要检查一遍,i和j的位置关系
                len--;
                nums[i] = nums[j];
                i++;
                j--;
            }
        }
        return len;


    }
};

时间复杂度是 O ( n ) O(n) O(n),空间复杂度是 O ( 1 ) O(1) O(1)

1 两数之和

在这里插入图片描述
在这里插入图片描述
思路:
双指针,i从0~len-2,j 从 i+1 ~ len-1,时间复杂度是 O ( n 2 ) O(n^2 ) O(n2)

暴力法

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int len = nums.size();
        vector <int> res;
        //特殊情况
        if(len==0||len==1)
            return res;
        
        for(int i = 0 ;i<len-1;i++){
            for(int j=i+1;j<len;j++){
                if(nums[i] + nums[j] == target){
                    res.push_back(i);
                    res.push_back(j);
                    return res;
                }
            }
        }
        return res;

    }
};

一遍哈希表

边存入哈希表,边检查已存在哈希表的是否符合条件。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int len = nums.size();
        map<int ,int> a;
        vector<int> b(2,-1);

        if(len<2)
            return b;

        for(int i=0;i<len;i++){
            if(a.count(target-nums[i])>0){//1存在,0不存在
                b[1]=a[target-nums[i]];
                b[0]=i;
                break;
            }
            a[nums[i]]=i;
        }

        return b;
        

    }
};

时间复杂度 O ( n ) O(n) O(n)

15 三数之和

在这里插入图片描述
难点:去除重复解,要先排序,根据和的大小移动L,R

双指针法

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int len = nums.size();
        vector<vector<int>> res;
        vector<int> tmp(3);

        int L;
        int R;
        
        if(len<3){
            return res;
        }

        //对数组进行排序
        sort(nums.begin(),nums.end());
        
        for(int i = 0;i<len;i++){
            if(i !=0 && nums[i]==nums[i-1]){//重复跳过
                continue;
            }

            if(nums[i]>0){//升序,三数之和不可能为0
                break;
            }

            L = i+1;
            R = len-1;

            tmp[0]=nums[i];

            while(L<R){
                if(nums[i]+nums[L]+nums[R]==0){
                    tmp[1]=nums[L];
                    tmp[2]=nums[R];
                    res.push_back(tmp);
                    while(L<R  && nums[L]==nums[L+1]){
                        L++;
                    }
                    while(L<R && nums[R]==nums[R-1]){
                        R--;
                    }
                    //双指针同时收缩
                    L++;
                    R--;
                }
                else if(nums [i] + nums [L] + nums [R] >0){
                    R--;
                }
                else{
                    L++;
                }
            }
        }
        return res;
    }
};

时间效率是 O ( n 2 ) O(n^2) O(n2)

使用哈希解法

18 四数之和

在这里插入图片描述
注意:

  1. 要注意指针变动!!
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        int len = nums.size();
        vector<vector<int>> res;

        if(len<4){
            return res;
        }

        sort(nums.begin(),nums.end());

        for(int i=0;i<len-3;i++){
            
            if(i!=0 && nums[i]==nums[i-1]){
                continue;
            }
            for(int j=i+1;j<len-2;j++){
                
                if(j!=i+1 && nums[j]==nums[j-1]){
                    continue;
                }

                int L=j+1;
                int R=len-1;

                while(L<R){
                    if(nums[i]+nums[j]+nums[L]+nums[R]==target){
                        res.push_back({nums[i],nums[j],nums[L],nums[R]});
                       
                        while(L<R && nums[L]==nums[L+1]){
                            L++;
                        }

                        while(L<R && nums[R]==nums[R-1]){
                            R--;
                        }
                        L++;  //指针要改变呀!!!
                        R--;  //指针要改变呀!!
                    }
                    else if(nums[i]+nums[j]+nums[L]+nums[R]<target){
                        L++;
                    }
                    else{
                        R--;
                    }
                }

            }
        }
        return res;
    }
   
};

在这里插入图片描述
时间复杂度是 O ( n 3 ) O(n^3) O(n3)。没有使用新的数组,因此空间复杂度是 O ( 1 ) O(1) O(1)

206 反转链表

在这里插入图片描述
双指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur=NULL;
        ListNode* pre=head;

        while(pre!=NULL){
            ListNode* t = pre->next;

            pre->next=cur;
            cur=pre;
            pre = t;
        }

        head=cur;

        return head;

    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值