面试之queue和stack

发布于:2024-12-22 ⋅ 阅读:(17) ⋅ 点赞:(0)

1. 题目

有这样一道面试题,要求用queue实现stack,或者用stack实现queue,语言为C++。

2. 什么是queue和stack

在C++中,queue和stack是两种重要的数据结构。queue是先进先出(FIFO)的数据结构,而栈是后进先出(LIFO)的数据结构。下面详细说明这两种数据结构:

2.1 queue

2.1.1 什么是queue

queue是一种容器,它遵循先进先出(FIFO) 的原则。意味着,第一个插入到queue中的元素将会被第一个访问(或第一个被移除)。queue是一种常用的数据结构,有很多的应用场景,例如:用于缓冲处理循序性任务(任务调度器、消息传递等)。
C++中的queue是通过标准模板库(STL)提供的,可以通过包含<queue>头文件来使用。标准库中的queue是基于已有的容器是实现封装的(如deque/list/vector)。

deque:双端队列,是queue默认的底层容器。deque提供了双端插入和删除操作,适合在队列头部和尾部进行高效的操作。
list:链表,可以自定义使用链表作为底层容器,链表在插入和删除元素时非常高效,尤其是在队列头部和尾部进行操作。
vector:不常用,但也是可以作为底层容器的,但vector在头部插入和删除时效率较低,因为这些操作需要移动大量的元素。

注意: 选择不同的底层容器主要是为了适应不同的需求场景。容器适配器均是通过操作底层容器来实现功能的,因此底层容器的性能特性会直接影响queue的整齐性能和适用性。

2.1.2 底层容器的区别

**deque:**非常适合queue的操作模式,因为它允许在两端进行高效的插入和删除,随机访问性能尚可,内存操作灵活。
**list:**适合需要大量插入和删除操作的场景,但布置随机访问。相比deque,其随机访问性能较差,但在频繁的插入/删除场景中效率更高。
**vector:**虽然提供了快速随机访问机制,但由于头部插入和删除操作开销大,不合适作为queue的底层容器。

#include<queue>

std::queue<int> q1;
std::queue<int, std::list<int>> q2;
std::queue<int, std::vector<int>> q3;

2.1.3 queue的成员函数

成员函数 作用
push() 队列尾端插入数据
pop() 删除队列队首数据
front() 获取队列的队首数据
back() 获取队列的队尾数据
empty() 队列是否为空
size() 队列中元素的个数

2.2 stack

2.2.1 什么是stack

stack是一种后进先出(LIFO)的数据结构。stack的数据存取总是从同一端进行,这个端口被称为top(栈顶),而stack的另一端则被称为bottom(栈底)

2.2.2 底层容器

stack的底层容器可以通过不同的数据结构来实现,最常见的实现方式有list和array。这两种方式各有优缺点,具体选择哪种取决于应用场景(如存储效率,空间管理以及操作的复杂度)。

array:支持快速访问,时间复杂度为O(1);array的内存地址是连续的,容易管理和访问;但是也会有一些不足之处,例如数组长度固定(容易导致内存浪费或栈溢出)、动态性差(当元素数量超过数组大小,必须重新分配数组进行扩容)。
list:可实现动态的栈,即栈的大小可以根据需求动态调整,不收固定大小的限制。但也有一些不足之处,例如额外的指针存储(增加了存储开销)、访问速度较慢(由于链表中的节点在内存中不一定是连续的,访问栈顶元素比数组要慢一些)。

综上所述,我们可以根据应用场景的特性,选择合适的底层容器。

2.2.3 成员函数

成员函数 作用
push() 向栈顶插入元素
pop() 移除栈顶元素
top() 获取栈顶的元素
empty() 栈是否为空
size() 返回栈中元素的个数

3. 代码

#include<iostream>
#include<stack>
#include<queue>
#include<mutex>

namespace fy{

    template<typename T>
    class FyStack{
        using Queue = std::queue<T>;
    public:
        FyStack(){}
        ~FyStack(){}

        bool pop(T &obj){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            if (!fy_queue1_.empty()){
                obj = this->fy_queue1_.front();
                this->fy_queue1_.pop();
                return true;
            } else if(!fy_queue2_.empty()){
                obj = this->fy_queue2_.front();
                this->fy_queue2_.pop();
                return true;
            }
            return false;
        }

        bool push(T &obj){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            if (fy_queue1_.empty()){
                fy_queue1_.push(obj);
                __copy(fy_queue2_, fy_queue1_);
                return true;
            } else {
                fy_queue2_.push(obj);
                __copy(fy_queue1_, fy_queue2_);
                return true;
            }
        }

        void clear(){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            if (!this->fy_queue1_.empty()){
                while(!this->fy_queue1_.empty()){
                    this->fy_queue1_.pop();
                }
            } else {
                while(!this->fy_queue2_.empty()){
                    this->fy_queue2_.pop();
                }
            }
        }

        size_t size(){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            if (!this->fy_queue1_.empty()){
                return this->fy_queue1_.size();
            } else if (!this->fy_queue2_.empty()){
                return this->fy_queue2_.size();
            }
            return 0;
        }

        bool empty(){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            return (this->fy_queue1_.empty() && this->fy_queue2_.empty());
        }
    
    private:
        void __copy(Queue &src, Queue &dst){
            while(!src.empty()){
                auto obj = src.front();
                src.pop();
                dst.push(obj);
            }
        }

    private:
        Queue fy_queue1_, fy_queue2_;
        std::mutex fy_mtx_;
    };

    template<typename T>
    class FyQueue{
        using Stack = std::stack<T>;
    public:
        FyQueue(){}
        ~FyQueue(){}

        bool pop(T &obj){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            if (!fy_stack1_.empty()){
                obj = this->fy_stack1_.top();
                this->fy_stack1_.pop();
                return true;
            } else if(!fy_stack2_.empty()){
                obj = this->fy_stack2_.top();
                this->fy_stack2_.pop();
                return true;
            }
            return false;
        }

        bool push(T &obj){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            if (fy_stack1_.empty()){
                fy_stack1_.push(obj);
                __copy(fy_stack2_, fy_stack1_);
            } else {
                fy_stack2_.push(obj);
                __copy(fy_stack1_, fy_stack2_);
            }
            return true;
        }

        void clear(){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            if (!this->fy_stack1_.empty()){
                while(!this->fy_stack1_.empty()){
                    this->fy_stack1_.pop();
                }
            } else {
                while(!this->fy_stack2_.empty()){
                    this->fy_stack2_.pop();
                }
            }
        }

        size_t size(){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            if (!this->fy_stack1_.empty()){
                return this->fy_stack1_.size();
            } else if (!this->fy_stack2_.empty()){
                return this->fy_stack2_.size();
            }
            return 0;
        }

        bool empty(){
            std::unique_lock<std::mutex> lg(fy_mtx_);
            return (this->fy_stack1_.empty() && this->fy_stack2_.empty());
        }
    
    private:
        void __copy(Stack &src, Stack &dst){
            Stack tmp;
            while(!src.empty()){
                tmp.push(src.top());
                src.pop();
            }
            while(!tmp.empty()){
                dst.push(tmp.top());
                tmp.pop();
            }
        }

    private:
        Stack fy_stack1_, fy_stack2_;
        std::mutex fy_mtx_;
    };

} // namespace fy


int main(){

    std::cout << "*************************stack test***************************" << std::endl;
    fy::FyStack<int> stack;
    int a = 12,
        b = 13,
        c = 14,
        d = 15,
        e = 16;
    stack.push(a);
    stack.push(b);
    stack.push(c);
    std::cout << "stack insert: " << a << " " << b << " " << c << std::endl;

    std::cout << "stack data:";
    int result;
    while(!stack.empty()){
        if (stack.pop(result)){
            std::cout << " " << result;
        }
    }
    std::cout << std::endl;
    std::cout << "***********************stack test end*************************" << std::endl;

    std::cout << "\n\n";

    std::cout << "*************************queue test***************************" << std::endl;
    fy::FyQueue<int> queue;
    queue.push(a);
    queue.push(b);
    queue.push(c);
    queue.push(d);
    queue.push(e);
    std::cout << "queue insert: " << a << " " << b << std::endl;

    std::cout << "queue data:";
    int queue_result;
    while(!queue.empty()){
        if (queue.pop(queue_result)){
            std::cout << " " << queue_result;
        }
    }
    std::cout << std::endl;

    std::cout << "***********************queue test end*************************" << std::endl;

    return 0;
}

4. 测试

*************************stack test***************************
stack insert: 12 13 14
stack data: 14 13 12
***********************stack test end*************************


*************************queue test***************************
queue insert: 12 13
queue data: 12 13 14 15 16
***********************queue test end*************************