leetcode算法专题训练:四.栈与队列专题

四.栈和队列专题


剑指 Offer 09. 用两个栈实现队列

题目描述:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof

解题思路:设置两个队列,左边的队列用来入队,右边的队列用来出队。

时间复杂度:O(1)

空间复杂度:O(1)

class CQueue {
    std::stack<int> stk1;
    std::stack<int> stk2;
public:
    CQueue() {
    }
    
    void appendTail(int value) {
        stk1.push(value);
    }
    
    int deleteHead() {
        if (stk2.empty())
        {
            if (stk1.empty())
                return -1;
            else
            {
                while (!stk1.empty())
                {
                    stk2.push(stk1.top());
                    stk1.pop();
                }
            }
        }
        int val = stk2.top();
        stk2.pop();
        return val;
    }
};


剑指 Offer 30. 包含min函数的栈

题目描述:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof

解题思路:用两个栈,一个栈存放正常的元素,另一个栈保存此时元素中的最小值。

时间复杂度:min, push, pop, top 均为O(1)

空间复杂度:O(N)

class MinStack {
    std::stack<int> stk1;
    std::stack<int> stk2;
public:
    /** initialize your data structure here. */
    MinStack() {
    }
    
    void push(int x) {
        stk1.push(x);
        if (stk2.empty())
            stk2.push(x);
        else
        {
            int val = stk2.top()<=x ? stk2.top() : x;
            stk2.push(val); 
        }
    }
    
    void pop() {
        if (!stk1.empty())
        {
            stk1.pop();
            stk2.pop();
        }
    }
    
    int top() {
        return stk1.empty() ? -1 :  stk1.top();
    }
    
    int min() {
        return stk2.empty() ? INT32_MAX : stk2.top();
    }
};


剑指 Offer 59 - II. 队列的最大值

题目描述:请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof

解题思路:用两个辅助queue,一个存放元素的值,一个作为单调递减队列

时间复杂度:O(N)

空间复杂度:O(N)

class MaxQueue {
    std::deque<int> que1;
    std::deque<int> que2;
public:
    MaxQueue() {
    }
    
    int max_value() {
        return que2.empty() ? -1 : que2.front();
    }
    
    void push_back(int value) {
        que1.push_back(value);
        while (!que2.empty() && value>que2.back())
            que2.pop_back();
        que2.push_back(value);
    }
    
    int pop_front() {
        if (que1.empty())
            return -1;
        int val = que1.front();
        que1.pop_front();
        if (que2.front() == val)
            que2.pop_front();
        return val;
    }
};



剑指 Offer 31. 栈的压入、弹出序列

题目描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof

解题思路:遍历弹栈序列,并设置辅助栈,保证每一次循环都能够进行弹栈即可,若无法弹栈,那么就将压栈序列挨个入栈。

时间复杂度:O(N)

空间复杂度:O(N)

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        std::stack<int> stk;
        int idx = 0;
        for (int i = 0; i < popped.size(); i++)
        {
            while (stk.empty() || stk.top()!=popped[i])
            {
                if (idx == pushed.size())
                    return false;
                stk.push(pushed[idx++]);
            }
            stk.pop();
        }
        return true;
    }
};


20.有效的括号

题目描述:给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-parentheses

解题思路:建立辅助栈,for循环遍历字符串,当 栈为空 或者 元素与栈顶不匹配 的时候继续入栈,反之出栈;最后根据栈是否为空判断该字符串的有效性。另外判断括号匹配需要建立哈希表。

时间复杂度:O(N)

空间复杂度:O(N)

class Solution {
public:
    bool isValid(string s) {
        unordered_map<char,char> hashmap{{'(',')'}, {'{','}'}, {'[', ']'}};
        std::stack<char> stk;
        for (auto& e: s)
        {
            if (stk.empty() ||  e!=hashmap[stk.top()])
                stk.push(e);
            else
                stk.pop();
        }
        return stk.empty();
    }
};



32.最长的有效括号

题目描述:给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: “((()))”
输出: 3
解释: 最长有效括号子串为 “((()))”
示例 2:
输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”
示例 3:
输入: “()(())”
输出: 6
解释: 最长有效括号子串为 “()(())”

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-valid-parentheses

解题思路:先通过模拟栈的方法,记录不能够进行匹配消除的所有 ‘(’ 的下标置位为1,然后该问题就进行了转换,也就是在只有0 和 1 的数组中求 连续0 的最大长度,可以以 1 作为界限,计算中间区域的长度。

时间复杂度:O(N)

空间复杂度:O(N)

class Solution {
public:
    int longestValidParentheses(string s) {
        
        stack<int> stk;
        int n = s.size();
        for (int i = 0; i < n; ++i)
        {
            if (!stk.empty() && '('==s[stk.top()] && ')'==s[i])
                stk.pop();
            else
                stk.push(i);
        }

        int r = n;
        int ans = 0;
        while (!stk.empty())
        {
            int l = stk.top();
            stk.pop();
            ans = std::max(r-l-1, ans);
            r = l;
        }
        ans = std::max(ans, r-0);
        return ans;
    }
};



22.括号生成

题目描述:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses

解题思路:DFS法,记录左右括号的剩余值,当剩余值都为0时说明匹配完成;注意左括号的剩余值是必须小于右括号的剩余值。

时间复杂度:

空间复杂度:

class Solution {
    vector<string> ans;
public:
    void DFS(int l, int r, string& path)
    {
        if (0 == l && r == 0)
        {
            ans.push_back(path);
            return;
        }
        if (l <= r)
        {
            if (l >= 0)
            {
                path.push_back('(');
                DFS(l - 1, r, path);
                path.pop_back();
            }
            if (r >= 0)
            {
                path.push_back(')');
                DFS(l, r - 1, path);
                path.pop_back();
            }
        }
    }

    vector<string> generateParenthesis(int n) {
        string path;
        DFS(n, n, path);
        return ans;
    }
};



150.逆波兰表达式求值

题目描述:
根据 逆波兰表示法,求表达式的值。
有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/evaluate-reverse-polish-notation

解题思路:逆波兰表达式的核心在于遇到数字时进行入栈,遇到运算符号时出栈两个元素,并将其计算的结果入栈,最后弹出栈中剩下的最后一个元素即可。

时间复杂度:O(N)

空间复杂度:O(N)

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        if (tokens.empty())
            return 0;
        std::stack<int> stk;
        for(auto& e: tokens)
        {
            if (e.size()==1 && !isdigit(e[0]))
            {
                int b = stk.top();
                stk.pop();
                int a = stk.top();
                stk.pop();
                switch(e[0])
                {
                    case '+': stk.push(a+b); break;
                    case '-': stk.push(a-b); break;
                    case '*': stk.push(a*b); break;
                    case '/': stk.push(a/b); break;
                    default: break;
                }
            }
            else 
                stk.push(stoi(e));
        }
        int sum = stk.top();
        return sum;
    }
};



239. 滑动窗口最大值

题目描述:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-maximum

解题思路:用一个单调deque作为辅助,存放的是单调递减的元素下标,当新的元素更大不满足单调减的特性时需要从队尾出队,当deque长度超长时需要从队头出队。

时间复杂度:O(N)

空间复杂度:O(N)

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if (nums.empty() || nums.size()<k )
            return {};
        std::deque<int> deque;//存储下标
        for (int i = 0; i < k; ++i)
        {
            while (!deque.empty() && nums[i]>=nums[deque.back()])
                deque.pop_back();
            deque.push_back(i);
        }
        vector<int> ans{nums[deque.front()]};
        for (int i = k; i < nums.size(); i++)
        {
            if (i - deque.front() >= k)
                deque.pop_front();
            while (!deque.empty() && nums[i]>=nums[deque.back()])
                deque.pop_back();
            deque.push_back(i);
            ans.push_back(nums[deque.front()]);
        }
        return ans;
    }
};



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值