84、【栈与队列】leetcode ——1047. 删除字符串中的所有相邻重复项:栈+双指针解法(C++/Python版本)

文章介绍了三种不同的C++实现方式来解决删除字符串中所有相邻重复项的问题,包括使用栈和两种双指针方法。第一种方法利用栈来存储不重复的字符,第二种和第三种双指针方法分别在填充前判断和填充后回退来确保相邻字符不相同。每种方法的时间复杂度均为O(n),空间复杂度分别为O(n)和O(1)。
摘要由CSDN通过智能技术生成

题目描述

在这里插入图片描述
在这里插入图片描述
原题链接:1047. 删除字符串中的所有相邻重复项

解题思路

一、栈顶匹配重复元素

本题需要删除重复且相邻元素,存入不重复元素。根据相邻特点,可采用栈进行实现。

当栈顶元素和遍历的字符串中的字符相同时,则将其弹栈。然后遍历下一个元素,直至全部遍历完,让各个相邻元素都不重复的存入栈中。再将其弹栈存入到子串中,因为栈为后进先出,因此需要让字符串进行逆置,恢复原始顺序。

class Solution {
public:
    string removeDuplicates(string s) {
        stack<char> st;
        string res = "";
        for(char ch : s) {
            // 当栈为空或者栈顶元素与ch不相同,则将ch压入栈中
            if(st.empty() || st.top() != ch) {
                st.push(ch);
            } else {
                // 如果栈中不为空而且栈顶元素与ch相等,则弹出已存入的栈顶元素
                st.pop();
            }
        }
        // 存入res中
        while(!st.empty()) {
            res += st.top();
            st.pop();
        }
        // 因为从栈中弹出的元素为逆序,因此需要将字符串再逆序,让顺序正过来
        reverse(res.begin(), res.end());
        return res;
    }
};

Python

class Solution:
    def removeDuplicates(self, s: str) -> str:
        n = len(s)
        if n == 1:
            return s
        res = list()
        for word in s:
            if res and res[-1] == word:
                res.pop()
            else:
                res.append(word)
        return ''.join(res)

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

二、双指针方法1:先判定,再填充

初步想法是类似于 移除链表元素 的方式,采用快慢双指针存储符合条件的元素,不存储不符合条件的元素。

此题和移除链表元素题的区别,在于本题需要对比的不仅仅是原始链表中的相邻元素,还需要再与已经存入的元素进行对比。

image.png
整体思路是在填充前,先对比当前待填充元素与未来填充元素的情况,再对比当前待填充元素与上一个已填充元素的情况,都不重复时,则填充该元素。

设置j指针用来存储符合条件的元素,始终指向待存储位置的下标
设置i指针遍历原字符串,每次首先对比s[i]s[i+1]字符是否相同,如果相同,则不填充,需要跳过这两个字符。如果不相同,再对比s[i]s[j - 1]中字符是否相同,如果相同,则让j--,让下一次存储覆盖该存入元素。
当原始字符串中相邻元素不相同,而且待存入元素与新字符串的末尾元素不相同时,存入该元素。

其实,就相当于是i为pre指针,i+1为cur指针,j为temp指针(用于存入新元素,指向新子串末尾的下一个位置)。每次对比pre指针和cur指针指向元素,然后再对比pre指针和(temp-1)指针对比。

class Solution {
public:
    string removeDuplicates(string s) {          
        int n = s.size();
        // 用j指向新的待存储元素的位置
        int j = 0;
        for(int i = 0; i < n; i++) {
            // 当s[i]与s[i+1]相同时,跳过这两个元素
            if(i + 1 < n && s[i] == s[i + 1])
                i++;    // 先加1,之后循环结尾还会再加1,从而实现跳过这两个重复元素
            // 当s[i]与s[j - 1]中元素相同时,让此位置元素可被替换,执行j--,并再执行i++跳过该元素
            else if(j > 0 && s[j - 1] == s[i])
                j--;
            // 当相邻元素不重复而且已存入元素和新存入元素不重复时,加入该元素
            else
	            s[j++] = s[i];                
        }        
        s.resize(j);
        return s;
    }
};

Python

class Solution:
    def removeDuplicates(self, s: str) -> str:
        n = len(s)
        if n == 1:
            return s
        res = list(s)
        i = j = 0
        while i < n:
            if i + 1 < n and s[i] == s[i + 1]:
                i += 1
            elif j > 0 and res[j - 1] == s[i]:
                j -= 1
            else:
                res[j] = s[i]
                j += 1
            i += 1
        return ''.join(res[:j])

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

三、双指针方法2:先填充,再判定回退

方法1中,思路是填充前先查看未来情况和原先已填充情况。方法2是先填,再判定,不满足条件,则回退到未填充的时候。

因为本题需要与之前已填充过的元素进行对比,如果先对比后填充,不仅需要对比未来情况,还需要对比已有情况。该方式可以提前跳过不满足条件的情况。

方法2采用先填充后对比的策略,从而让对比方式变成只需对比当前与之前的填充情况即可,但会需要将不满足的条件进行退回,相比于方法1会多填充一次。

采取策略是设置一个i指针用于遍历整个字符串,另设置一个j指针进行填充。每次先进行填充,填充完后,与已填充过的上一个元素进行对比,如果相同,则放弃此次填充,回退到之前的位置。如果不相同,则填充此次位置。

class Solution {
public:
    string removeDuplicates(string s) {
        int n = s.size();
        int j = 0;
        for(int i = 0; i < n; i++) {                    
            s[j] = s[i];                        // 先填充    
            if(j > 0 && s[j] == s[j - 1])      // 填充后与上一个填充元素对比
                j--;                           // 当发现与之前元素相同,则回退
            else
                j++;                           // 与之前元素不同,则填充并指向下一个待填充位置
        }
        s.resize(j);
        return s;
    }
};

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

Python

class Solution:
    def removeDuplicates(self, s: str) -> str:
        n = len(s)
        if n == 1:
            return s
        res = list(s)
        i = j = 0
        while i < n:
            res[j] = s[i]
            if j > 0 and res[j] == res[j - 1]:
                j -= 1
            else:
                j += 1
            i += 1
        return ''.join(res[:j])

参考文章:1047. 删除字符串中的所有相邻重复项

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰阳星宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值