练题100天——DAY19:含退格的字符串+有序数组的平方

今天写了两道力扣练习题,难度:★★~★★★,每道题都写了不止一种写法,也有官方题解,本来以为今天只能写一道题的,超额完成了✌。

一.比较含退格的字符串 ★★★☆☆

题目

力扣844.给定 st 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true# 代表退格字符。注意:如果对空文本输入退格字符,文本继续为空。

我的思路

1.利用双指针实现元素的判断和赋值:fast指针负责遍历字符串s和t;slow指针负责作为动态指针sArr和tArr的索引。针对不同的情况,对fast和slow进行更新

2.更新元素:当s[fast] != '#' 时,直接将s[fast]赋值给sArr[slow],并且将fast和slow都向后移动;当s[fast] == '#' 时,slow向前移动,以便fast碰到下一个不是#的元素,进行赋值操作,相当于覆盖。

3.对s和t分开操作,然后用strcmp(sArr和tArr)和0比较,将比较结果赋值给bool变量res,最后返回res即可。

遇到过的报错+分析说明:

1.heap-buffer-overflow,由写操作越界引起 (向动态分配的数组进行写入操作时,访问了数组范围外的地址):如果用 char* tArr=(char*)malloc((tLen)*sizeof(char)); 语句动态分配,当字符串为空字符串时,不会分配空间,而最后还有tArr[slow]='\0';语句进行赋值操作,就会报错。

2.stack-buffer-overflow(栈缓冲区溢出),由字符串索引越界引起:因为最的思路是用 i 控制次数,而没有直接用fast,所以没有对fast进行范围的限制,导致fast大于了数组的范围。

cpp 复制代码
int fast=0;
int slow=0;
for(int i=0;i<sLen;i++){
    while(s[fast]=='#'){
         fast++;
         if(slow!=0){
              sArr[--slow]=0;
         }
    }
    sArr[slow++]=s[fast++];
}

下面有两个代码,核心区别只有一处,但是运行结果是有差异的

代码1
cpp 复制代码
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        int sLen=s.size();
        int tLen=t.size();
        //预留一个空间存'\0'
        char* sArr=(char*)malloc((sLen+1)*sizeof(char));
        char* tArr=(char*)malloc((tLen+1)*sizeof(char));
        if(sArr==NULL || tArr==NULL){
            free(sArr);
            free(tArr);
            return false;
        }
        //---------------------------
        int slow=0;
        int fast=0;
        for(;fast<sLen;fast++){
            if(s[fast]=='#'){
                if(slow>0){
                   slow--;
                }
            }
            else{
                    sArr[slow++]=s[fast];
            }
        }
        sArr[slow]='\0';
        slow=0;
        for(fast=0;fast<tLen;fast++){
            if(t[fast]=='#'){
                if(slow>0){
                   slow--;
                }
            }
            else{
                    tArr[slow++]=t[fast];
            }
        }
        tArr[slow]='\0';

        bool res=(strcmp(sArr,tArr)==0);
        free(sArr);
        free(tArr);
        return res;
    }
};
代码2
cpp 复制代码
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        int sLen=s.size();
        int tLen=t.size();
        //预留一个空间存'\0'
        char* sArr=(char*)malloc((sLen+1)*sizeof(char));
        char* tArr=(char*)malloc((tLen+1)*sizeof(char));    
        //---------------------------
        int slow=0;
        int fast=0;
        for(;fast<sLen;fast++){
            if(s[fast]=='#'){
                if(slow>0){
                   slow--;
                }
            }
            else{
                    sArr[slow++]=s[fast];
            }
        }
        sArr[slow]='\0';
        slow=0;
        for(fast=0;fast<tLen;fast++){
            if(t[fast]=='#'){
                if(slow>0){
                   slow--;
                }
            }
            else{
                    tArr[slow++]=t[fast];
            }
        }
        tArr[slow]='\0';

        bool res=(strcmp(sArr,tArr)==0);
        free(sArr);
        free(tArr);
        return res;
    }
};

核心区别:是否在分配空间后进行NULL的判断和处理

消耗内存分布的差异主要是一些极端场景的例子造成的。第一个代码有对NULL的处理,面对极端情况就不会往后继续执行,减少了后续各种内存的消耗。

力扣官方题解

题解1

模拟"栈"的操作:遍历字符串的每一个字符,遇到非#,将其"入栈",加入res中;遇到#,将res"栈顶"的元素弹出(在res非空时才能弹出)

说明:for(char ch : s){ } 相当于 for(int i=0;i<s.size();i++) { char ch=s[i]; }

cpp 复制代码
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        return build(s)==build(t);
    }
    string build(string s){
        string res;
        for(char ch:s){
            if(ch=='#'){
                if(!res.empty()){
                    res.pop_back;
                }
                
            }else {
                res.push_back(ch);
            }
        }
        return res;
    }
};
题解2

双指针(有一点点难理解,我下次理解了再来写下我的理解)

cpp 复制代码
class Solution {
public:
    bool backspaceCompare(string S, string T) {
        int i = S.length() - 1, j = T.length() - 1;
        int skipS = 0, skipT = 0;

        while (i >= 0 || j >= 0) {
            while (i >= 0) {
                if (S[i] == '#') {
                    skipS++, i--;
                } else if (skipS > 0) {
                    skipS--, i--;
                } else {
                    break;
                }
            }
            while (j >= 0) {
                if (T[j] == '#') {
                    skipT++, j--;
                } else if (skipT > 0) {
                    skipT--, j--;
                } else {
                    break;
                }
            }
            if (i >= 0 && j >= 0) {
                if (S[i] != T[j]) {
                    return false;
                }
            } else {
                if (i >= 0 || j >= 0) {
                    return false;
                }
            }
            i--, j--;
        }
        return true;
    }
};
。

二.有序数组的平方 ★★☆☆☆

题目

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

思路1------暴力法

直接计算出nums中每个数的平方,并保存在新数组中,然后利用sort函数对新数组进行升序排序

代码
cpp 复制代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len=nums.size();
        vector<int> res(len);
        for(int i=0;i<len;i++){
            res[i]=nums[i]*nums[i];
        }
        sort(res.begin(),res.end());
        return res;
    }
};

思路2------"双指针"法

这里的双指针我打了双引号,因为感觉跟题解的双指针相比还是low low的

我的思路是找出nums中的第一个正数的位置 i,将left和right两个指针分别设为 i - 1 和 i,然后通过比较两个指针的值将较小的放入新数组中(index作为新数组的索引,从0开始往后移动)

如果只是找第一个正数的位置,就无法处理全为负数的情况,所以我添加了标志变量flag,作为nums数组中是否有正数/0的标志,如果有就置flag=1。遍历完nums后,如果flag为0,直接从数组长度的一半处规定两个指针,但是left=len-1; right=len。可以通过全是正数的情况理解:全是正数,nums数组本身就是从小到大排序,所以置left=-1; right=0; 而left<0,不会进入while循环,直接进行后续的操作(对right进行移动);nums数组全为负数的情况类似,令left=len-1; right=len; 将left一直向前移动即可

我的思路有一点麻烦,可以选择看后面的官方题解思路

代码1
cpp 复制代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len=nums.size();
        vector<int> res(len);
        //以第一个正数作为分界点,两个指针分别向两边移动
        //比较两个指针所指的数的平方,小的放入res然后移动
        int index=0;//res的索引
        int left=0;
        int right=0;
        int flag=0;
        //遍历nums找到第一个正数
        for(int i=0;i<len;i++){
            if(nums[i]>=0){
                left=i-1;
                right=i;
                flag=1;
                break;
            }
        }
        if(flag==0){
            left=len-1;
            right=len;
        }
        while(right<len && left>=0){
            if(nums[left]*nums[left]<=nums[right]*nums[right]){
                res[index++]=nums[left]*nums[left];
                left--;
            }else{
                res[index++]=nums[right]*nums[right];
                right++;
            }
        }
        //处理剩余数字
        //右边没走完
        if(right<len){
            while(right<len&&index<len){
                res[index++]=nums[right]*nums[right];
                right++;
            }
        }else{
            while(left>=0){
                res[index++]=nums[left]*nums[left];
                left--;
            }
        }
        return res;
    }
};
代码2

将只有正负数的情况单独提取出来了,直接利用for循环对新数组的元素进行赋值

cpp 复制代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len=nums.size();
        vector<int> res(len);
        //只有正数
        if(nums[0]>=0){
            for(int i=0;i<len;i++){
                res[i]=nums[i]*nums[i];
            }
            return res;
        }
        //只有负数
        else if(nums[len-1]<=0){
            for(int i=0,j=len-1;i<len;i++,j--){
                res[j]=nums[i]*nums[i];
            }
            return res;
        }
        //正负数都有
        else{
        //以第一个正数作为分界点,两个指针分别向两边移动
        //比较两个指针所指的数的平方,小的放入res然后移动
        int index=0;//res的索引
        int left=0;
        int right=0;
        int flag=0;
        //遍历nums找到第一个正数----------------无法处理全负数的情况
        for(int i=0;i<len;i++){
            if(nums[i]>=0){
                left=i-1;
                right=i;
                break;
            }
        }
        while(right<len && left>=0){
            if(nums[left]*nums[left]<=nums[right]*nums[right]){
                res[index++]=nums[left]*nums[left];
                left--;
            }else{
                res[index++]=nums[right]*nums[right];
                right++;
            }
        }
        //处理剩余数字
        //右边没走完
        if(right<len){
            while(right<len&&index<len){
                res[index++]=nums[right]*nums[right];
                right++;
            }
        }else{
            while(left>=0){
                res[index++]=nums[left]*nums[left];
                left--;
            }
        }
        return res;
        }  
    }
};

力扣官方题解

代码1

利用双指针,i 从前往后,j 从后往前,比较两个指针指向的元素的值,比较大小,对新数组元素赋值。将指向较大的元素的指针在赋值后进行移动,比较下一个元素。

无论是只有正/负数,还是一般的正负数都有的情况,都可以通过先找出最大值来对新数组元素赋值,所以新数组索引初始化为len-1,然后往前移动。

循环结束条件是 i<=j ,而不是 i<j,因为存在 i 和 j 相等的情况

cpp 复制代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len=nums.size();
        vector<int> res(len);
        for(int i=0,j=len-1,index=len-1;i<=j;){//循环结束条件:i<=j
            if(nums[i]*nums[i]>nums[j]*nums[j]){
                res[index--]=nums[i]*nums[i];
                i++;
            }else{
                res[index--]=nums[j]*nums[j];
                j--;
            }
        }
        return res;
    }
};
代码2

理解困难+1

cpp 复制代码
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        int negative = -1;
        for (int i = 0; i < n; ++i) {
            if (nums[i] < 0) {
                negative = i;
            } else {
                break;
            }
        }

        vector<int> ans;
        int i = negative, j = negative + 1;
        while (i >= 0 || j < n) {
            if (i < 0) {
                ans.push_back(nums[j] * nums[j]);
                ++j;
            }
            else if (j == n) {
                ans.push_back(nums[i] * nums[i]);
                --i;
            }
            else if (nums[i] * nums[i] < nums[j] * nums[j]) {
                ans.push_back(nums[i] * nums[i]);
                --i;
            }
            else {
                ans.push_back(nums[j] * nums[j]);
                ++j;
            }
        }

        return ans;
    }
};
相关推荐
Ayanami_Reii1 小时前
进阶数据结构应用-线段树扫描线
数据结构·算法·线段树·树状数组·离散化·fenwick tree·线段树扫描线
leoufung1 小时前
LeetCode 98 Validate Binary Search Tree 深度解析
算法·leetcode·职场和发展
水木姚姚1 小时前
C++ begin
开发语言·c++·算法
浅川.251 小时前
xtuoj 素数个数
数据结构·算法
jyyyx的算法博客1 小时前
LeetCode 面试题 16.18. 模式匹配
算法·leetcode
uuuuuuu1 小时前
数组中的排序问题
算法
Stream1 小时前
加密与签名技术之密钥派生与密码学随机数
后端·算法
Stream1 小时前
加密与签名技术之哈希算法
后端·算法
少许极端2 小时前
算法奇妙屋(十五)-BFS解决边权为1的最短路径问题
数据结构·算法·bfs·宽度优先·队列·图解算法·边权为1的最短路径问题