代码随想录算法训练营第十天| leetcode 232、225、20、1047

栈与队列理论基础(C++)

1.队列现进先出(队尾进队头出),栈是先进后出(栈顶进出)。

2.栈提供push和pop等接口,所有元素必须符合现进后出规则,所以栈不提供走访功能,也不提供迭代器,不像是set或者map提供迭代器iterator来遍历元素。

队列先进先出的数据结构,同样不允许有遍历行为,不提供迭代器。

3.栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。

4.STL中栈和队列不被归类为容器,而被归类为container adapter(容器适配器)。

5.栈的底层实现可以是vector,deque,list。

6.SGI STL中,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。(deque是一个双向队列,封住一端即可实现栈的逻辑)

SGI STL中队列底层实现缺省情况下一样使用deque实现。

//指定vector为栈的底层实现
std::stack<int,std::vector<int>> third;
//指定list为队列的底层实现
std::queue<int,std::list<int>> third;

7.栈有如下接口:push()、pop()、返回栈顶元素top()、empty()、size()。

队列有如下接口:push()、pop()、返回队头元素front()、返回队尾元素back()、empty()、size()。

用栈实现队列 leetcode 232

class MyQueue {
public:
    stack<int> stackIn;
    stack<int> stackOut;
    MyQueue() {
    }
    
    void push(int x) {
        stackIn.push(x);
    }
    
    int pop() {
        if(stackOut.empty()){
            while(!stackIn.empty()){
                stackOut.push(stackIn.top());
                stackIn.pop();
            }
        }
        int result=stackOut.top();
        stackOut.pop();
        return result;
    }
    
    int peek() {
        int result=this->pop();
        stackOut.push(result);
        return result;
    }
    
    bool empty() {
        return stackIn.empty()&&stackOut.empty();
    }
};

总结

1.使用两个栈,一个stackIn压入元素模仿队列入队(stackIn的栈顶就是队列的队尾),另一个栈stackOut接受stackIn弹出元素并压入栈内(stackOut的栈顶就是队列的队头)。当stackOut为空,stcakIn不为空时将stackIn中所有元素弹出并压入stackOut。

2.peek函数中,使用this指针可以避免代码重用。

用队列实现栈 leetcode 225

解法一:两个队列实现栈

class MyStack {
public:
    queue<int> que1;
    queue<int> que2;
    MyStack() {
    }
    
    void push(int x) {
        que1.push(x);
    }
    
    int pop() {
        int size=que1.size();
        size--;
        while(size--){
            que2.push(que1.front());
            que1.pop();
        }
        int result=que1.front();
        que1.pop();
        que1=que2;
        while(!que2.empty()){
            que2.pop();
        }
        return result;
    }
    
    int top() {
        return que1.back();
    }
    
    bool empty() {
        return que1.empty();
    }
};

总结

1.队列模仿入栈,就是que1队尾入即可。模仿出栈,就相当于是先让que1队尾的元素出来,所以que2的作用就是保存que1除队尾元素之外的所有元素,然后让que1除队尾元素外都出队,再让队尾元素出队模仿出栈,再将que2的元素赋给que1,再删除que2中所有元素。

解法二:一个队列实现栈

class MyStack {
public:
    queue<int> que;
    MyStack() {
    }
    
    void push(int x) {
        que.push(x);
    }
    
    int pop() {
        int size=que.size();
        size--;
        while(size--){
            que.push(que.front());
            que.pop();
        }
        int result=que.front();
        que.pop();
        return result;
    }
    
    int top() {
        return que.back();
    }
    
    bool empty() {
        return que.empty();
    }
};

总结

1.一个队列实现栈,模仿入栈就是队列的入队,模仿出栈要先把队列的队头元素从队尾入队再从队头弹出,一开始队尾元素成为队头,此时再将队头元素弹出即可。

出现的错误

实现pop函数的时候,直接返回了que.front(),队头元素,没有将队头元素弹出。

有效的括号 leetcode 20

class Solution {
public:
    bool isValid(string s) {
        if(s.size()%2!=0) return false;
        stack<char> sta;
        for(int i=0;i<s.size();i++){
            if(s[i]=='(') sta.push(')');
            else if(s[i]=='{') sta.push('}');
            else if(s[i]=='[') sta.push(']');
            else if(sta.empty()||s[i]!=sta.top()) return false; //第二三种不匹配
            else sta.pop();
        }
        return sta.empty(); //匹配或第一种不匹配
    }
};

总结

1.一共有三种不匹配的情况,第一种就是出现了多余的左括号,第二种是左括号与右括号不匹配,第三种是出现了多余的右括号。

2.分别用栈来模拟三种不匹配的情况,遍历字符串遇到左括号那么就从栈顶压入一个相应类型的右括号,遍历到右括号就从栈中弹出一个与之对比。

第一种不匹配的情况:字符串遍历完成之后栈中还有元素。

第二种不匹配的情况:遍历到右括号时从栈中弹出一个与之对比不相同。

第三种不匹配的情况:字符串还没有遍历完但栈空了。
3.首先可以先做一个剪枝,如果字符串长度是奇数那肯定不符合返回false。

删除字符串中的所有相邻重复项 leetcode 1047

解法一:使用栈

class Solution {
public:
    string removeDuplicates(string s) {
        string res="";
        stack<char> sta;
        for(char t:s){
            if(sta.empty()||t!=sta.top()) sta.push(t);
            else sta.pop();
        }
        while(!sta.empty()){
            res+=sta.top();
            sta.pop();
        }
        reverse(res.begin(),res.end());
        return res;
    }
};

总结

1.遍历字符串,如果栈为空且栈顶元素不等于当前遍历到的元素,就把遍历到的元素压入栈内,如果遍历到的元素等于栈顶元素,就弹出栈顶元素,以此实现删除相邻相同字符的操作。

2.string s,t;  s+=t;  可以直接使用“+”将两个字符串拼接在一起。

解法二:使用字符串代替栈

class Solution {
public:
    string removeDuplicates(string s) {
        string res;
        for(char t:s){
            if(res.empty()||t!=res.back()) res.push_back(t);
            else res.pop_back();
        }
        return res;
    }
};

总结

1.遍历字符串s,如果字符串res为空且res尾部元素(相当于栈顶)不等于当前遍历到的元素,就把遍历到的元素放到字符串res的尾部,如果遍历到的元素等于res尾部元素,就删除res的尾部元素,以此实现删除相邻相同字符的操作。

2.最后直接返回字符串res即可(res尾部相当于栈顶,res首部相当于栈底)。

栈结构的特殊性,非常适合做对称匹配类的题目。

第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值