• 栈与队列c++算法练习


    用栈实现队列

    请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
    
    实现 MyQueue 类:
    
    void push(int x) 将元素 x 推到队列的末尾
    int pop() 从队列的开头移除并返回元素
    int peek() 返回队列开头的元素
    boolean empty() 如果队列为空,返回 true ;否则,返回 false
    说明:
    
    你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
    你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    class MyQueue {
    public:
    //模拟两个栈进行操作
        stack stIn;
        stack stOut;
        MyQueue() {
    
        }
        
        void push(int x) {
            //往插入栈push元素
            stIn.push(x);
        }
        
        int pop() { 
            //先判断输出栈是否为空,
            if(stOut.empty()) {
               //如果为空,就得把插入栈全部元素插入到输出栈中
                while(!stIn.empty()) {
                    stOut.push(stIn.top());
                    stIn.pop();
                }
            }
            //然后再pop元素
            int result = stOut.top();
            stOut.pop();
            return result;
        }
        
        int peek() {
            if(stOut.empty()) {
                 while(!stIn.empty()) {
                    stOut.push(stIn.top());
                    stIn.pop();
                }
            }
            int res = this->pop();
            stOut.push(res);
            return res;
        }
        //判断两个栈都为空的情况,队列才为空
        bool empty() {
            return stIn.empty() && stOut.empty();
        }
    };
    
    /**
     * Your MyQueue object will be instantiated and called as such:
     * MyQueue* obj = new MyQueue();
     * obj->push(x);
     * int param_2 = obj->pop();
     * int param_3 = obj->peek();
     * bool param_4 = obj->empty();
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    用队列实现栈

    请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
    
    实现 MyStack 类:
    
    void push(int x) 将元素 x 压入栈顶。
    int pop() 移除并返回栈顶元素。
    int top() 返回栈顶元素。
    boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
    
    
    注意:
    
    你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
    你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
    
    
    示例:
    
    输入:
    ["MyStack", "push", "push", "top", "pop", "empty"]
    [[], [1], [2], [], [], []]
    输出:
    [null, null, null, 2, 2, false]
    
    解释:
    MyStack myStack = new MyStack();
    myStack.push(1);
    myStack.push(2);
    myStack.top(); // 返回 2
    myStack.pop(); // 返回 2
    myStack.empty(); // 返回 False
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    class MyStack {
    public:
        queue 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    有效的括号

    给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
    
    有效字符串需满足:
    
    左括号必须用相同类型的右括号闭合。
    左括号必须以正确的顺序闭合。
    每个右括号都有一个对应的相同类型的左括号。
    
    
    示例 1:
    
    输入:s = "()"
    输出:true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    class Solution {
    public:
        bool isValid(string s) {
            int n = s.size();
            if (n % 2 == 1) {
                return false;
            }
            unordered_map pairs = {
                {')','('},
                {']','['},
                {'}','{'}
            };
            stack stk ;
            for (char ch: s) {
                //count用来判断map的key中右括号是否与字符串相同,相同返回1 不相同 0
                if(pairs.count(ch)) {
                    //在栈顶拿左括号是否与之匹配,不匹配返回false
                    if(stk.empty()||stk.top()!=pairs[ch]) {
                        return false;
                    }
                    //匹配成功就把栈顶给pop掉,继续匹配
                    stk.pop();
                }
                //是左括号就push到栈中
                else {
                    stk.push(ch);
                }
                //如果栈中的左括号都匹配完就返回true.
            }
            return stk.empty();
                }
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    有效括号

    给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
    
    有效字符串需满足:
    
    左括号必须用相同类型的右括号闭合。
    左括号必须以正确的顺序闭合。
    注意空字符串可被认为是有效字符串。
    示例 1:
    
    输入: "()"
    输出: true
    示例 2:
    
    输入: "()[]{}"
    输出: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    class Solution {
    public:
    //O(n)
        // bool isValid(string s) {
        //     int n = s.size();
        //     if (n % 2 == 1) {
        //         return false;
        //     }
        //     unordered_map pairs = {
        //         {')','('},
        //         {']','['},
        //         {'}','{'}
        //     };
        //     stack stk ;
        //     for (char ch: s) {
        //         //count用来判断map的key中右括号是否与字符串相同,相同返回1 不相同 0
        //         if(pairs.count(ch)) {
        //             //在栈顶拿左括号是否与之匹配,不匹配返回false
        //             if(stk.empty()||stk.top()!=pairs[ch]) {
        //                 return false;
        //             }
        //             //匹配成功就把栈顶给pop掉,继续匹配
        //             stk.pop();
        //         }
        //         //是左括号就push到栈中
        //         else {
        //             stk.push(ch);
        //         }
        //         //如果栈中的左括号都匹配完就返回true.
        //     }
        //     return stk.empty();
        //         }
        bool isValid(string s) {
            if(s.size()%2!=0)
            return false;
            stack st;
            for(int i=0;i
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

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

    给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
    
    在 S 上反复执行重复项删除操作,直到无法继续删除。
    
    在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
    
    示例:
    
    输入:"abbaca"
    输出:"ca"
    解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
    提示:
    
    1 <= S.length <= 20000
    S 仅由小写英文字母组成
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    class Solution {
    public:
        string removeDuplicates(string S) {
        //     string stk;
        //     for(char ch : s) {
        //         if(!stk.empty()&&stk.back()==ch){
        //             stk.pop_back();
        //         } 
        //         else{
        //         stk.push_back(ch);
        //         }
        //     }
        //     return stk;
        // }
        stack st;
        for (char s : S) {
            if (st.empty()||s!=st.top()) {
                st.push(s);
            } else {
                st.pop();
            }
        }
        string result = "";
        while(!st.empty()) {
            result+=st.top();
            st.pop();
        }
        reverse(result.begin(),result.end());
        return result;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    逆波兰表达式求值

    根据 逆波兰表示法,求表达式的值。
    
    有效的运算符包括 + ,  - ,  * ,  / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
    
    说明:
    
    整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
    
    示例 1:
    
    输入: ["2", "1", "+", "3", " * "]
    输出: 9
    解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
    示例 2:
    
    输入: ["4", "13", "5", "/", "+"]
    输出: 6
    解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
    示例 3:
    
    输入: ["10", "6", "9", "3", "+", "-11", " * ", "/", " * ", "17", "+", "5", "+"]
    
    输出: 22
    
    解释:该算式转化为常见的中缀算术表达式为:
    
    ((10 * (6 / ((9 + 3) * -11))) + 17) + 5       
    = ((10 * (6 / (12 * -11))) + 17) + 5       
    = ((10 * (6 / -132)) + 17) + 5     
    = ((10 * 0) + 17) + 5     
    = (0 + 17) + 5    
    = 17 + 5    
    = 22    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    class Solution {
    public:
        int evalRPN(vector& tokens) {
            stack st;
            for ( int i = 0; i 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    滑动窗口最大值

    给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
    
    返回滑动窗口中的最大值。
    
    进阶:
    
    你能在线性时间复杂度内解决此题吗?
    
    
    
    提示:
    
    1 <= nums.length <= 10^5
    -10^4 <= nums[i] <= 10^4
    1 <= k <= nums.length
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    class Solution {
    // private:
    //     class MyQueue { //单调队列
    //     public:
    //         deque que; //使用deque来实现单调队列
    //         void pop(int value) {
    //             //先判断队列是否为空,再判断pop的值是否为队头的值 ,是就删除
    //             if(!que.empty() && value==que.front()) {
    //                 que.pop_front();
    //             }
    //         }
    //             //如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,知道push的数值小于等于队列入口元素的数值为止
    //             // 这样就保持了队列里的数值是单调从大到小的了。
    //         void push(int value) {
    //             while(!que.empty() && value > que.back()) {
    //                 que.pop_back();
    //             }
    //             que.push_back(value);
    //         }
    //         //查询当前队列里的最大值,直接返回队列前端也就是front就可以了
    //         int front() {
    //             return que.front();
    //         }
            
    //     };
    // public:
    //     vector maxSlidingWindow(vector& nums, int k) {
    //         MyQueue que;
    //         vector result;
    //         for (int i = 0;i < k; i++) {    //先将前k个元素放入队列
    //             que.push(nums[i]);
    //         }
    //         result.push_back(que.front());
    //         for ( int i=k;i maxSlidingWindow(vector& nums, int k) {
        deque q;
        int n = nums.size();
        for (int i=0;i=nums[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);
        }
        vector ans = {nums[q.front()]};
        for (int i=k;i=nums[q.back()])
            {
                q.pop_back();
            }
            q.push_back(i);
            while(q.front()<=i-k) {
                q.pop_front();
            }
            ans.push_back(nums[q.front()]);
        }
        return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    前k个高频元素

    方法:优先队列,需要用到堆

    优先队列入门

    定义

    priority_queue;

    Type是要存放的数据类型

    Container是实现底层堆的容器,必须是数组实现的容器,如vector、deque

    Functional是比较方式/比较函数/优先级

    priority_queue;

    此时默认的容器是vector,默认的比较方式是大顶堆less

    举例

    //小顶堆
    priority_queue q;
    //大顶堆
    priority_queue q;
    //默认大顶堆
    priority_queue a;
    //pair
    priority_queue > a;
    pair b(1, 2);
    pair c(1, 3);
    pair d(2, 5);
    a.push(d);
    a.push©;
    a.push(b);
    while (!a.empty())
    {
    cout << a.top().first << ’ ’ << a.top().second << ‘\n’;
    a.pop();
    }
    //输出结果为:
    2 5
    1 3
    1 2

    常用函数

    top()

    pop()

    push()

    emplace()

    empty()

    size()

    自定义比较方式

    当数据类型并不是基本数据类型,而是自定义的数据类型时,就不能用greater或less的比较方式了,而是需要自定义比较方式

    在此假设数据类型是自定义的水果:

    struct fruit
    {
    string name;
    int price;
    };
    有两种自定义比较方式的方法,如下

    1.重载运算符

    重载”<”

    若希望水果价格高为优先级高,则

    //大顶堆
    struct fruit
    {
    string name;
    int price;
    friend bool operator < (fruit f1,fruit f2)
    {
    return f1.peice < f2.price;
    }
    };
    若希望水果价格低为优先级高

    //小顶堆
    struct fruit
    {
    string name;
    int price;
    friend bool operator < (fruit f1,fruit f2)
    {
    return f1.peice > f2.price; //此处是>
    }
    };

    2.仿函数

    若希望水果价格高为优先级高,则若希望水果价格高为优先级高,则

    //大顶堆
    struct myComparison
    {
    bool operator () (fruit f1,fruit f2)
    {
    return f1.price < f2.price;
    }
    };

    //此时优先队列的定义应该如下
    priority_queue q;

    给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
    
    
    
    示例 1:
    
    输入: nums = [1,1,1,2,2,3], k = 2
    输出: [1,2]
    示例 2:
    
    输入: nums = [1], k = 1
    输出: [1]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    class Solution {
        public: 
            //小顶堆
            class mycomparison{
                public:
                    bool operator()(const pair& lns, const pair& rhs) {
                    //小顶堆是大括号
                        return lns.second > rhs.second;
                    }
            };
            vector topKFrequent(vector& nums,int k) {
                // 要统计元素出现频率
                unordered_map map;// map
                for(int i=0;i,vector>,mycomparison> pri_que;
                for(unordered_map::iterator it = map.begin();it!=map.end();it++) {
                    pri_que.push(*it);
                    if(pri_que.size()>k) {  // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
                        pri_que.pop();
                    }
                }
                  // 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
                vector result(k);
                for(int i=k-1;i>=0;i--) {
                    result[i]=pri_que.top().first;
                    pri_que.pop();
                }
                return result;
            }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
  • 相关阅读:
    基于JavaWeb的大学社团管理系统的设计与实现
    【C++】C/C++内存管理
    LeetCode 91 双周赛
    udp协议下sendto与recvfrom函数对应的errno
    智慧校园数字实验室云边协同服务器开发研究
    nacos-springboot搭建
    【排序算法】希尔排序
    【SpringBoot】SpringBoot 读取配置文件中的自定义属性的 5 种方法
    Node端异常捕获
    数字信号处理(MATLAB入门例子)
  • 原文地址:https://blog.csdn.net/wenjinjie1/article/details/127900317