栈、队列面试题总结

一、有效的括号 – leetcode

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

分析:
        这道题是一个括号匹配问题, 之前有做过类似的题目, 不过那道题的括号类型只有一种,使用一个计数器就可以搞定。 这道题不同, 括号类型的匹配有多种。 因此我们需要使用到栈,我们遍历给我们的字符串, 所有关于左括号(’[’, ‘{’, ‘(’)放到栈中, 当遇到右括号时(’]’,’}’,’)’)时, 我们会取出栈顶的一个元素和它进行匹配, 如果匹配成功, 则从栈中pop掉这个左括号。 如果没有匹配成功, 则直接返回false。直到读取完给我们的字符串。

这里需要考虑几个问题。
1、第一个元素是右括号,则这个右括号无法匹配, 直接返回false.
2、当遍历完字符串, 栈里面的左括号要和右括号匹配消耗完, 如果最终栈不为空, 则返回false, 如果为空,返回true。

源码:

	//判断是否匹配函数
	bool IsMatch(const char& ch, const char& st) {
        
        if(ch == '(' && st == ')') {
            return true;
        } 
        if(ch == '{' && st == '}') {
            return true;
        }
        if(ch == '[' && st == ']') {
            return true;
        }
        return false;
    }

    bool isValid(string s) {

        stack<char> st;					//创建读取左括号栈
        for(const auto& it : s) {
            if(it == '(' || it == '{' || it == '[') {	//为左括号类型时放到栈里面
                st.push(it);
            }
            else {										//为右括号类型则进行匹配判断
                if(st.empty()) {						//第一个元素为右括号类型
                    return false;
                }

                char ch = st.top();						//栈顶左括号元素
           
                if(IsMatch(ch, it)) {					//匹配函数		
                    st.pop();   						//匹配成功, 出队
                }
                else {									//匹配失败
                    return false;
                }
            }
        }
        if(st.empty()) {								//最终判断栈是否为空
            return true;
        }
        else 											//栈不为空
            return false;
    }

测试结果:
在这里插入图片描述

二、用队列实现栈 – leetcode

题目链接

描述:
使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空

分析:
这里我会使用c++去写, 使用STL标准库。 在这里我先提前说一下STL库中queue适配器。STL提供的接口有push,pop, front, back, swap这几个接口等。 这几个接口我会用到。

采用队列实现栈我会使用两个队列去实现, 一个主队列负责存储数据, 另一个辅助队列倒数据(就像两个杯子倒水一样), 另一个队列是为了实现栈的pop操作。(因为队列和栈的性质不行, 需要借助一些思想去做)
        1、push; 就相当于主队列的push操作。
        2、pop; 将主队列中的数据倒入辅助队列当中, 主队列只留下最后一个数据不倒入(最后一个数据不倒入,就相当于pop掉),其他数据都倒入辅助队列。然后调用swap交换两个队列
        3、top; 等价于主队列back接口
        4、empty; 等价于判断主队列empty。

源码:

class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
		//没什么可写的
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        hq.push(x);
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        if(hq.empty()) {
            return -1;
        }

        int front;

        while(!hq.empty()) {
            front = hq.front();
            hq.pop();
            if(hq.empty()) {
                break;
            }
            tq.push(front);
        }
        hq.swap(tq);            //使主队列hq一直不为空
        return front;
    }
    
    /** Get the top element. */
    int top() {
        return hq.back();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return hq.empty();
    }

private:
    queue<int> hq;
    queue<int> tq;
};

测试结果:
在这里插入图片描述

三、用栈实现队列 – leetcode

题目链接

描述
使用栈实现队列的下列操作:
         push(x) – 将一个元素放入队列的尾部。
         pop() – 从队列首部移除元素。
         peek() – 返回队列首部的元素。
         empty() – 返回队列是否为空。
示例1:
MyQueue queue = new MyQueue();
        queue.push(1);
        queue.push(2);
        queue.peek(); // 返回 1
        queue.pop(); // 返回 1
        queue.empty(); // 返回 false

分析:
STL中栈的接口有push、pop、top、empty等接口, 我会使用这些接口去实现队列。同样我们需要两个栈去实现一个队列。 一个主栈负责接收数据, 另一个辅助栈负责倒数据。对于队列的pop、feek操作来说是比较困难的, 其他操作是很简单的。
1、push; 等价于主栈push操作
2、pop; 我们会将主栈的数据倒入辅助栈中, 此时辅助栈的栈顶元素就是我们需要pop的元素, pop辅助栈。 最后将辅助栈的数据倒入主栈。
3、peek; 和pop操作相同, 不过它不会pop辅助栈的栈顶元素, 而是返回辅助栈的栈顶元素。
4、empty; 等价于判断主栈的empty。

源码:

class MyQueue {
public:
    /** Initialize your data structure here. */
    MyQueue() {

    }
    
    /** Push element x to the back of queue. */
    void push(int x) {
        is.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        if(is.empty()) {
            return -1;
        }

        while(!is.empty()) {
            int top = is.top();
            is.pop();
            os.push(top);
        }

        int top = os.top();
        os.pop();

        while(!os.empty()) {
            int top = os.top();
            os.pop();
            is.push(top);
        }

        return top;
    }
    
    /** Get the front element. */
    int peek() {
        if(is.empty()) {
            return -1;
        }

        while(!is.empty()) {
            int top = is.top();
            is.pop();
            os.push(top);
        }

        int front = os.top();

        while(!os.empty()) {
            int top = os.top();
            os.pop();
            is.push(top);
        }

        return front;
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
        return is.empty();
    }

private:
    stack<int> is;
    stack<int> os;
};

测试结果:
在这里插入图片描述

四、最小栈 – leetcode

题目链接

描述:
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
         push(x) —— 将元素 x 推入栈中。
         pop() —— 删除栈顶的元素。
         top() —— 获取栈顶元素。
         getMin() —— 检索栈中的最小元素。
示例1:
输入:
        [“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
        [[],[-2],[0],[-3],[],[],[],[]]
输出:
        [null,null,null,null,-3,null,0,-2]
解释:
        MinStack minStack = new MinStack();
        minStack.push(-2);
        minStack.push(0);
        minStack.push(-3);
        minStack.getMin(); --> 返回 -3.
        minStack.pop();
        minStack.top(); --> 返回 0.
        minStack.getMin(); --> 返回 -2.

分析:
做这道题我们会定义两个栈, 一个main栈是正常存放数据, 另一个min栈是存放最小值数据。

main栈存储原则我就不用多说, push操作。
min栈存储原则:
1、当min栈为空时, 会一直存放数据。
2、每当存放一个新的数据val, 先判断val是否<=min栈的栈顶元素,如果是则将该元素存放倒min栈中, 如果不是则不存放。
3、当主栈pop的时候, 需要判断主栈pop的元素(也就是未pop之前的栈顶元素)是否等于min栈的栈顶元素。 如果相等, min栈pop, 不相等,则不需要pop。

下面讲解一下这些接口的对应实现。
        1、push; main栈直接push, min栈根据上述的原则push。
        2、pop; main栈直接pop, min栈根据上述原则pop。
        3、top; 返回main栈的栈顶元素。
        4、getMin; 返回min栈的栈顶元素。

源码:

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        
        mainstack.push(x);
        if(minstack.empty()) {
            //直接插进来
            minstack.push(x);
        }
        else if(x <= minstack.top()) {
            minstack.push(x);
        }
    }
    
    void pop() {
        if(mainstack.empty()) {
            return;
        }

        int top = mainstack.top();
        mainstack.pop();

        if(top == minstack.top()) {
            minstack.pop();
        }    
    }
    
    int top() {
        return mainstack.top();
    }
    
    int getMin() {
        return minstack.top();
    }

private:
    stack<int> mainstack;
    stack<int> minstack;
};

测试结果:
在这里插入图片描述

五、设计循环队列 – leetcode

添加链接描述

描述:
设计一个循环队列, 满足下面的接口需要。
        1、 MyCircularQueue(k): 构造器,设置队列长度为 k 。
         2、Front: 从队首获取元素。如果队列为空,返回 -1 。
         3、Rear: 获取队尾元素。如果队列为空,返回 -1 。
         4、enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
         5、deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
         6、sEmpty(): 检查循环队列是否为空。
         7、isFull(): 检查循环队列是否已满。

分析:
        这道题就是考察队循环队列的设计, 如果你懂这个设计就很简单, 如果不懂就比较难了。 这道题和我上一篇博客中设计循环队列思想一样。

我在这里就只讲一些重点,不再多叙述, 感兴趣可以看上一篇的博客。
1、开辟空间问题。 题目中给我的长度为k, 实际上是让我们使用定长的容器去做, 我建议使用定长的数组去做。 在开辟空间的时候, 我们会多开辟一个空间也就是k+1大小的空间。 这样做方便设计。
2、队满问题; 既然是定长, 就会存在队满的问题, 判断队满的条件就是:
(rear + 1) % (k + 1) == front。
3、队空问题: 队空条件: rear == front;
4、循环队列; 我们的下标rear、front在++操作后, 需要注意是否越界的情况。

基于这些问题我们就可以很简单写出一个循环队列。

源码:

class MyCircularQueue {
public:
    /** Initialize your data structure here. Set the size of the queue to be k. */
    MyCircularQueue(int k) {
        date = new int[k + 1];
        front = rear = 0;
        n = k;
    }
    
    /** Insert an element into the circular queue. Return true if the operation is successful. */
    bool enQueue(int value) {
        if(isFull()) {
            return false;
        }

        date[rear] = value;   //插入, rear++
        rear++;

        if(rear == n + 1) {     //rearu越界, 重置
            rear = 0;       
        }
        return true;
    }
    
    /** Delete an element from the circular queue. Return true if the operation is successful. */
    bool deQueue() {
        if(isEmpty()) {
            return false;
        }

        front++;                //删除, 右移
        if(front == n + 1) {    //front越界, 重置
            front = 0;
        }
        return true;
    }
    
    /** Get the front item from the queue. */
    int Front() {
        if(isEmpty()) {
            return -1;
        }
        return date[front];
    }
    
    /** Get the last item from the queue. */
    int Rear() {
        if(isEmpty()) {
            return -1;
        }
        if(rear == 0) {
            return date[n];
        }
        else
        return date[rear - 1];
    }
    
    /** Checks whether the circular queue is empty or not. */
    bool isEmpty() {
        return rear == front;
    }
    
    /** Checks whether the circular queue is full or not. */
    bool isFull() {
        return ((rear + 1) % (n + 1)) == front;
    }
private:
    int* date;
    int front;
    int rear;
    int n;
};

测试结果:
在这里插入图片描述

好啦, 本章介绍完毕, 如果您正在准备数据结构方面的面试或者考试, 希望本章的例题会让你更加了解栈、队列。 本章的例题如果你用心做一遍, 相信一定会有很大的收获的。 当然如果您都会, 此篇博客也可以不错复习, 正所谓谦卑学习, 温故而知新嘛。 谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: CSND的数据结构面试往往涵盖了各种经典的问,需要在有限的时间内展示出自己的理论知识和解决问的能力。回答这类问时,通常需要从以下几个方面来展开回答。 首先,对于常见的数据结构,如数组、链表、队列、树、图等,需要了解它们的特性、操作以及常见的应用场景。这样一来,在面试中能够很好地判断使用何种数据结构,并理解其优缺点及适用范围。 其次,应对针对具体数据结构的问,我们应该熟悉并能够灵活运用各种操作和算法,如遍历、插入、删除、查找等。例如,对于链表,我们需要清楚如何反转链表、检测环、寻找中间节点等;对于树,我们需要熟悉二叉树的遍历方式,以及常见的二叉搜索树的操作和平衡二叉树的实现。 此外,还需了解常用的排序和查找算法,如快速排序、归并排序、二分查找等,并熟悉它们的时间复杂度和空间复杂度,以及在不同情况下的使用场景。 最后,面试官可能会要求我们在具体场景中应用数据结构来解决问,并分析算法的效率。在这种情况下,我们需要将问抽象成相应的数据结构,然后给出解决方案,并且合理解释算法的复杂度,并认真考虑算法的边缘情况。 总之,在面试过程中,要全面展示自己对于数据结构的理解和应用能力,同时要注重理论知识的掌握和实际问解决能力的展示,这样才能在数据结构面试中脱颖而出。 ### 回答2: 数据结构是计算机科学中的重要基础知识,面试中经常会涉及到相关问。以下是关于“数据结构面试”的回答。 数据结构面试通常包括以下内容:数组、链表、队列、树、图、哈希表等。对于数组,常见的问包括数组的查找、插入、删除操作,以及数组的有序性,如二分查找等。链表问包括链表的初始化、插入、删除操作,以及链表的环检测等。队列主要考察队列的基本操作,如压、出、入队、出队等。 树问是数据结构面试中的重点,包括二叉树、平衡树、二叉搜索树等。常见问包括二叉树的遍历(前序、中序、后序)、树的深度和高度、树的层次遍历等。对于图问,主要考察图的遍历(深度优先搜索、广度优先搜索)、最短路径问(Dijkstra算法、Floyd-Warshall算法)以及最小生成树问(Prim算法、Kruskal算法)等。哈希表问主要考察哈希函数的设计、哈希冲突的解决方法,以及哈希表的性能分析等。 在准备数据结构面试时,需要牢固掌握各种数据结构的基本操作,理解其原理和实现方式,并能够熟练应用到具体问中。另外,面试过程中还需要注重对算法的分析和优化思路的展示,展示自己对问的思考能力和解决能力。在回答问时要清晰表达自己的思路,思路清晰、逻辑严谨的回答更容易给面试官留下好印象。 最后,通过刷、模拟面试等多种方式来增强自己的实际操作能力和解决问的能力可以更好地应对数据结构面试。多练习、多总结可以提高自己的数据结构和算法水平,为面试打下坚实基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值