zoukankan      html  css  js  c++  java
  • 栈和队列题目汇总

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

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

    class CQueue {
        stack<int> stack1,stack2;
    public:
        CQueue() {
            while (!stack1.empty()) {
                stack1.pop();
            }
            while (!stack2.empty()) {
                stack2.pop();
            }
        }
        
        void appendTail(int value) {
            stack1.push(value);
        }
        
        int deleteHead() {
            // 如果第二个栈为空
            if (stack2.empty()) {
                while (!stack1.empty()) {// 数据从栈1存入栈2,这样在栈2栈顶元素为最先加入元素
                    stack2.push(stack1.top());
                    stack1.pop();
                }
            } 
            if (stack2.empty()) {
                return -1;
            } else {// 删除第一个加入的节点
                int deleteItem = stack2.top();
                stack2.pop();
                return deleteItem;
            }
        }
    };
    
    /**
     * Your CQueue object will be instantiated and called as such:
     * CQueue* obj = new CQueue();
     * obj->appendTail(value);
     * int param_2 = obj->deleteHead();
     */
     
     

      

    使用队列实现栈的下列操作:

    • push(x) -- 元素 x 入栈
    • pop() -- 移除栈顶元素
    • top() -- 获取栈顶元素
    • empty() -- 返回栈是否为空

    思路:

    队列模拟栈,其实一个队列就够了,那么我们先说一说两个队列来实现栈的思路。

    队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并没有变成先进后出的顺序。

    所以用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质。

    但是依然还是要用两个队列来模拟栈,只不过没有输入和输出的关系,而是另一个队列完全是用来备份的!

    如下面动画所示,用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。

    class MyStack {
    public:
        queue<int> que1;
        queue<int> que2; // 辅助队列
        /** Initialize your data structure here. */
        MyStack() {
    
        }
    
        /** Push element x onto stack. */
        void push(int x) {
            que1.push(x);
        }
    
        /** Removes the element on top of the stack and returns that element. */
        int pop() {
            int size = que1.size();
            size--;
            while (size--) { // 将que1 导入que2,但要留下最后一个元素
                que2.push(que1.front());
                que1.pop();
            }
    
            int result = que1.front(); // 留下的最后一个元素就是我们要返回的值
            que1.pop();
            que1 = que2; // 再将que2赋值给que1
            while(!que2.empty()) { // 清空que2
                que2.pop();
            }
            return result;
        }
    
        /** Get the top element. */
        int top() {
            return que1.back();
        }
    
        /** Returns whether the stack is empty. */
        bool empty() {
            return que1.empty();
        }
    };
    
    /**
     * Your MyStack object will be instantiated and called as such:
     * MyStack* obj = new MyStack();
     * obj->push(x);
     * int param_2 = obj->pop();
     * int param_3 = obj->top();
     * bool param_4 = obj->empty();
     */

     

    3、设计循环队列

    设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

    循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

    你的实现应该支持如下操作:

    MyCircularQueue(k): 构造器,设置队列长度为 k 。
    Front: 从队首获取元素。如果队列为空,返回 -1 。
    Rear: 获取队尾元素。如果队列为空,返回 -1 。
    enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
    deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
    isEmpty(): 检查循环队列是否为空。
    isFull(): 检查循环队列是否已满。
     

    示例:

    MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
    circularQueue.enQueue(1);  // 返回 true
    circularQueue.enQueue(2);  // 返回 true
    circularQueue.enQueue(3);  // 返回 true
    circularQueue.enQueue(4);  // 返回 false,队列已满
    circularQueue.Rear();  // 返回 3
    circularQueue.isFull();  // 返回 true
    circularQueue.deQueue();  // 返回 true
    circularQueue.enQueue(4);  // 返回 true
    circularQueue.Rear();  // 返回 4
     

    提示:

    所有的值都在 0 至 1000 的范围内;
    操作数将在 1 至 1000 的范围内;
    请不要使用内置的队列库。

    class MyCircularQueue {
        int* data;
        int size;
        int head;
        int tail;    
        int capicity;
    public:
        /** Initialize your data structure here. Set the size of the queue to be k. */
        MyCircularQueue(int k) {
            data = new int[k];
            size = k;
            head = 0;
            tail = 0;  
            capicity = 0;
        }
        
        /** Insert an element into the circular queue. Return true if the operation is successful. */
        bool enQueue(int value) {
            if (capicity < size) {
                data[tail++] = value;
                ++capicity;
                if (tail == size) {
                    tail = 0;
                }
                return true;
            }
            return false;
        }
        
        /** Delete an element from the circular queue. Return true if the operation is successful. */
        bool deQueue() {
            if (capicity > 0) {
                ++head;
                --capicity;
                if (head == size) {
                    head = 0;
                }
                return true;
            }
            return false;
        }
        
        /** Get the front item from the queue. */
        int Front() {
            if (capicity == 0) {
                return -1;
            }
            return data[head];
        }
        
        /** Get the last item from the queue. */
        int Rear() {
            if (capicity == 0) {
                return -1;
            }
            return tail == 0 ? data[size - 1] : data[tail - 1];   
        }
        
        /** Checks whether the circular queue is empty or not. */
        bool isEmpty() {
            return capicity == 0;
        }
        
        /** Checks whether the circular queue is full or not. */
        bool isFull() {
            return capicity == size;
        }
    };
    
    /**
     * Your MyCircularQueue object will be instantiated and called as such:
     * MyCircularQueue* obj = new MyCircularQueue(k);
     * bool param_1 = obj->enQueue(value);
     * bool param_2 = obj->deQueue();
     * int param_3 = obj->Front();
     * int param_4 = obj->Rear();
     * bool param_5 = obj->isEmpty();
     * bool param_6 = obj->isFull();
     */
    

      

      

    4、设计实现双端队列。
    你的实现需要支持以下操作:

    MyCircularDeque(k):构造函数,双端队列的大小为k。
    insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
    insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
    deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
    deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
    getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
    getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
    isEmpty():检查双端队列是否为空。
    isFull():检查双端队列是否满了。
    示例:

    MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
    circularDeque.insertLast(1); // 返回 true
    circularDeque.insertLast(2); // 返回 true
    circularDeque.insertFront(3); // 返回 true
    circularDeque.insertFront(4); // 已经满了,返回 false
    circularDeque.getRear(); // 返回 2
    circularDeque.isFull(); // 返回 true
    circularDeque.deleteLast(); // 返回 true
    circularDeque.insertFront(4); // 返回 true
    circularDeque.getFront(); // 返回 4
     
     

    提示:

    所有值的范围为 [1, 1000]
    操作次数的范围为 [1, 1000]
    请不要使用内置的双端队列库。

    解题思路

    注意:
    head是头部待出队位置(获取头元素,即直接访问arr[head])
    从头插入,head是逆时针旋转(需要先减1)
    从头删除,head是顺时针旋转

    tail是尾部待入队位置,(获取尾元素, 需要先将tail逆时针旋转1)
    从尾插入,tail是顺时针旋转
    从尾删除,tail是逆时针旋转

    class MyCircularDeque {
    public:
        /** Initialize your data structure here. Set the size of the deque to be k. */
        MyCircularDeque(int k) {
            m_head = 0;
            m_tail = 0;
            m_size = 0;
            m_capacity = k;
            m_arr = new int[k];
        }
    
    
        /** Adds an item at the front of Deque. Return true if the operation is successful. */
        bool insertFront(int value) {
            if (isFull())
                return false;
            
            //head指向当前元素, 前插时,head逆时针旋转
            --m_head;
            if (m_head < 0)
            {
                m_head = m_capacity - 1;
            }
    
            m_arr[m_head] = value;
    
            ++m_size;
    
            return true;
        }
    
        /** Adds an item at the rear of Deque. Return true if the operation is successful. */
        bool insertLast(int value) {
            if (isFull())
                return false;
    
            m_arr[m_tail++] = value;
            if (m_tail == m_capacity)
            {
                m_tail = 0;
            }
    
            ++m_size;
    
            return true;
        }
    
        /** Deletes an item from the front of Deque. Return true if the operation is successful. */
        bool deleteFront() {
            if (isEmpty())
                return false;
    
            //从头删除 head顺时针旋转
            ++m_head;
            if (m_head == m_capacity)
            {
                m_head = 0;
            }
    
            --m_size;
    
            return true;
        }
    
        /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
        bool deleteLast() {
            if (isEmpty())
                return false;
    
            --m_tail;
            if (m_tail < 0)
            {
                m_tail = m_capacity - 1;
            }
    
            --m_size;
    
            return true;
        }
    
        /** Get the front item from the deque. */
        int getFront() {
            if (isEmpty())
                return -1;
    
            return m_arr[m_head];
        }
    
        /** Get the last item from the deque. */
        int getRear() {
            if (isEmpty())
                return -1;
    
            //注意:最后元素的索引是m_tail的上一个元素
            int index = m_tail - 1;
            if (index < 0)
            {
                index = m_capacity - 1;
            }
            return m_arr[index];
        }
    
        /** Checks whether the circular deque is empty or not. */
        bool isEmpty() {
            return m_size == 0;
        }
    
        /** Checks whether the circular deque is full or not. */
        bool isFull() {
            return m_size == m_capacity;
        }
    
    
    private:
        int* m_arr;
        int m_head;
        int m_tail;
        int m_size;  //元素个数
        int m_capacity; //容量 固定长度
    };
    
    
    
    /**
     * Your MyCircularDeque object will be instantiated and called as such:
     * MyCircularDeque* obj = new MyCircularDeque(k);
     * bool param_1 = obj->insertFront(value);
     * bool param_2 = obj->insertLast(value);
     * bool param_3 = obj->deleteFront();
     * bool param_4 = obj->deleteLast();
     * int param_5 = obj->getFront();
     * int param_6 = obj->getRear();
     * bool param_7 = obj->isEmpty();
     * bool param_8 = obj->isFull();
     */
    

     

    5、406. 根据身高重建队列

    设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。

    注意:
    总人数少于1100人。

    示例

    输入:
    [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

    输出:
    [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

    核心思路非常简单:

    先排身高更高的,这是要防止后排入人员影响先排入人员位置
    每次排入新人员[h,k]时,已处于队列的人身高都>=h,所以新排入位置就是people[k]

    ## 身高降序排列,身高相同,人数升序排列,然后按照新的排序,人数作为排位位置进行插入;

    分别用两种方式实现插入,list效率更高;

    class Solution {
    public:
        vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
            /*
            *  1. 首先考虑身高相同的情况: 例如 [7,0] 和 [7,1], [7,0]一定在[7,1]前面
            *  2. 然后接着考虑身高不同的情况, 身高低的相对于身高高的人来说, 是看不见的, 不影响k值
            *  3. 所以可以按照 [h,k] h大小排序, h相同按照k的大小排序
            *  4. [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]  --- >->->
            *     [[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]
            *  5. 按照 [h,k]中k的位置在容器中插入: 
            *     5.1 -> [7,0], [7,1]
            *     5.2 -> [7,0], [6,1], [7,1]
            *     5.3 -> [5,0], [7,0], [6,1], [7,1]
            *     5.4 -> [5,0], [7,0], [5,2], [6,1], [7,1]
            *     5.5 -> [5,0], [7,0], [5,2], [6,1], [4,4], [7,1]
            */
    
    
            //STL排序 sort 用法:sort(first_pointer,first_pointer+n,cmp),默认为升序,若要使用降序,自行写cmp 函数
            auto cmp = [](const vector<int>& v1, const vector<int>& v2) {
            	//若第一个元素相同,则按照第二个元素升序排列,否则,按照第一个元素降序排列
                return v1[0] == v2[0] ? v1[1] < v2[1] : v1[0] > v2[0];
            };
    
            //标准模版库算法实例化,身高降序排列,人数升序排列
            sort(people.begin(), people.end(), cmp);
    
    
            //按照顺序重新插入,按照人数的位置进行插入
    
            //方法一:list 实现,效率更高:
            // list<vector<int>> sort_list;
            // for (auto item : people) {
            //     auto iter = sort_list.begin();
            //     //advance迭代器就是将迭代器it,移动n位。如果it是随机访问迭代器,那么函数进行1次运算符计算操作,否则函数将对迭代器进行n次迭代计算操作
            //     advance(iter, item[1]);
            //     sort_list.insert(iter, item);
            // }
            // //list转换为vector
            // vector<vector<int>> ret(sort_list.begin(), sort_list.end());
            // return ret;
    
    
            //方法二:vector 实现:
            vector<vector<int>> res;
    
            for(auto item:people){
            	res.insert(res.begin()+item[1],item);
            }
    
            return res;
    
        }
    };
    

      

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

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

    示例:

    MinStack minStack = new MinStack();
    minStack.push(-2);
    minStack.push(0);
    minStack.push(-3);
    minStack.min();   --> 返回 -3.
    minStack.pop();
    minStack.top();      --> 返回 0.
    minStack.min();   --> 返回 -2.
    

      

    思路:用一个辅助栈来专门维护栈的最小值

    class MinStack {
    public:
        /** initialize your data structure here. */
        MinStack() {
        }
        stack<int>s;
        stack<int>Min;
        //push操作,如果最小栈为空,则将元素入栈,否则比较栈顶元素和该元素大小确定是否入栈
        void push(int x) {
            s.push(x);
            if(Min.empty()||x<=Min.top())Min.push(x);
        }
        
        //pop操作,如果原始栈不为空并且栈顶元素和和最小栈顶元素相同,则都出栈,否则只对原始栈出栈
        void pop() {
            if(!s.empty()){
                if(s.top()==Min.top())Min.pop();
                s.pop();
            }
        }
        //top操作不变
        int top() {
            return s.top();
        }
        // min 函数就是返回最小栈栈顶元素
        int min() {
            return Min.top();
        }
    };
    
    /**
     * Your MinStack object will be instantiated and called as such:
     * MinStack* obj = new MinStack();
     * obj->push(x);
     * obj->pop();
     * int param_3 = obj->top();
     * int param_4 = obj->min();
     */
    

      

    7、面试题 03.05. 栈排序

    栈排序。 编写程序,对栈进行排序使最小元素位于栈顶。最多只能使用一个其他的临时栈存放数据,但不得将元素复制到别的数据结构(如数组)中。

    该栈支持如下操作:pushpoppeek 和 isEmpty。当栈为空时,peek 返回 -1。

    示例1:

     输入:
    ["SortedStack", "push", "push", "peek", "pop", "peek"]
    [[], [1], [2], [], [], []]
     输出:
    [null,null,null,1,null,2]
    

    示例2:

     输入: 
    ["SortedStack", "pop", "pop", "push", "pop", "isEmpty"]
    [[], [], [], [1], [], []]
     输出:
    [null,null,null,null,null,true]
    

    解题思路:

    利用辅助栈可以保证每次插入新元素的适合s1都是有序的,
    比如 s1 = {1,5,6,8,9},插入 7
    先把 9和8 插入 s2, s2 = {9,8}
    再把 7 插入 s1, s1 = {1,5,6,7}
    再把 s2 中数字插入 s1, s1 = {1,5,6,7,8,9}
    这样思路最简单但是也比较麻烦

    class SortedStack {
    public:
        stack<int> s1, s2;
        SortedStack() {
            
        }
        
        //比较栈顶元素和将要入栈的元素,如果栈顶元素小于将入栈的元素,则出栈到辅助栈
        void push(int val) {
            while(!s1.empty() && s1.top() < val){
                s2.push(s1.top());
                s1.pop();
            }
            s1.push(val);
            //然后将辅助栈元素再入栈道原始栈,此时栈有序,且栈顶为最小元素
            while(!s2.empty()){
                s1.push(s2.top());
                s2.pop();
            }
        }
        
        void pop() {
            if(!s1.empty())
                s1.pop();
        }
        
        int peek() {
            if(!s1.empty())
                return s1.top();
            return -1;
        }
        
        bool isEmpty() {
            return s1.empty();
        }
    };
    
    /**
     * Your SortedStack object will be instantiated and called as such:
     * SortedStack* obj = new SortedStack();
     * obj->push(val);
     * obj->pop();
     * int param_3 = obj->peek();
     * bool param_4 = obj->isEmpty();
     */
    

      

    另一种解法,维护两个栈,原栈为降序,辅助栈为升序
    比如s1 = {8, 7, 3} s2 = {}
    插入 5,因为比s1.top大,把3插入s2中,然后 5插入 s1 中
    s1 = {8,7,5} s2={3}
    这样既能保证 s1 中的元素一定大于 s2 中元素,也可以使得两个栈都是按顺序排列
    不必要像第一种解法一样需要在push的时候就把 s2 中元素重新加入到 s1 中去

    class SortedStack {
    public:
        stack<int>s1;//原栈为降序
        stack<int>s2;//辅助栈为升序
        SortedStack() {
    
        }
        
        void push(int val) {
            while(!s2.empty() && s2.top() > val){//辅助栈中存在比val大的值
                s1.push(s2.top());
                s2.pop();
            }
            while(!s1.empty() && s1.top() < val){//原栈中有比val小的值
                s2.push(s1.top());
                s1.pop();
            }
            s1.push(val);
        }
        
        void pop() {
            while(!s2.empty()){//清空辅助栈
                s1.push(s2.top());
                s2.pop();
            }
            if(!s1.empty()) s1.pop();
        }
        
        int peek() {
            while(!s2.empty()){//清空辅助栈
                s1.push(s2.top());
                s2.pop();
            }
            if(!s1.empty()) return s1.top();
            else return -1;
        }
        
        bool isEmpty() {
            return s1.empty() && s2.empty();
        }
    };
    
    /**
     * Your SortedStack object will be instantiated and called as such:
     * SortedStack* obj = new SortedStack();
     * obj->push(val);
     * obj->pop();
     * int param_3 = obj->peek();
     * bool param_4 = obj->isEmpty();
     */
    

      

  • 相关阅读:
    openssl对数组加密解密的完整实现代码
    OpenSSl 加密解密 示例(终于有编程实践了)
    QT中QProcess调用命令行的痛苦经历(调用Winrar,设置工作目录,获得输出,注意引号与括号,等等)
    Ubuntu 14.04远程登录服务器--openssh的安装和配置简明步骤
    可复用的批量文件修改工具
    TFS二次开发、C#知识点、SQL知识
    Backbone.js的技巧和模式
    Hibernate:如何映射聚合?
    JavaScript之创建对象
    为什么选择MongoDB?
  • 原文地址:https://www.cnblogs.com/Allen-rg/p/13692750.html
Copyright © 2011-2022 走看看