LeetCode队列和栈

本文介绍了队列和栈这两种基本数据结构及其在LeetCode算法中的应用。通过广度优先搜索(BFS)和深度优先搜索(DFS)的例子,如岛屿数量、打开转盘锁等,展示了队列和栈如何解决实际问题。同时,文章还探讨了如何使用栈实现队列,以及队列实现栈的策略,进一步解释了这些数据结构的灵活性和实用性。
摘要由CSDN通过智能技术生成


数据结构与算法是今后学习、求职一直会用到的重要内容,鉴于我大一学的一团糟的惨状,我决定根据LeetCode专题的节奏来梳理一些重要的数据结构与算法,巩固一下自己的基础知识,也希望可以给没有好好学习数据结构与算法的小伙伴提个醒,快去刷题!

队列

队列是一种先进先出(FIFO)的数据结构,插入操作称为入队(enqueue),删除操作称为出队(dequeue)。
在这里插入图片描述
为了实现队列,可以使用动态数组和指向队列头部的指针。但是这样的实现会导致队列出现假饱和现象,即经过不断的插入和删除元素操作后,虽然队列是空的,但已经无法再插入新的元素。因此,在实际使用中,我们会设计循环队列,通过固定大小的数组和两个指针来指示起始位置和结束位置。

接下来我们实现自己的循环队列:

class MyCircularQueue {
   
private:
    vector<int> data;
    int head, tail, length;
public:
    /** Initialize your data structure here. Set the size of the queue to be k. */
    MyCircularQueue(int k) {
   
        head = tail = -1;
        length = k;
        data.resize(k);
    }
    
    /** Insert an element into the circular queue. Return true if the operation is successful. */
    bool enQueue(int value) {
   
        if (isFull())
            return false;
        tail = (tail + 1) % length;
        data[tail] = value;
        if (tail == 0 && head == -1) // first element
            head = 0;
        return true;
    }
    
    /** Delete an element from the circular queue. Return true if the operation is successful. */
    bool deQueue() {
   
        if (isEmpty())
            return false;
        head = (head + 1) % length;
        if (head == (tail + 1) % length) // last element
            head = tail = -1;
        return true;
    }
    
    /** Get the front item from the queue. */
    int Front() {
   
        if (isEmpty())
            return -1;
        return data[head];
    }
    
    /** Get the last item from the queue. */
    int Rear() {
   
        if (isEmpty())
            return -1;
        return data[tail];
    }
    
    /** Checks whether the circular queue is empty or not. */
    bool isEmpty() {
   
        if (tail == -1)
            return true;
        return false;
    }
    
    /** Checks whether the circular queue is full or not. */
    bool isFull() {
   
        if ((tail + 1) % length == head)
            return true;
        return false;
    }
};

/**
 * 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();
 */
函数 作用
q.push(x) 将元素x添加到队列的末尾
q.pop() 删除队列的第一个元素,并不返回删除的元素
q.front() 返回队列的第一个元素
q.back() 返回队列的最后一个元素
q.empty() 当队列为空时返回true
q.size() 返回队列中元素的个数

广度优先搜索(BFS)

广度优先搜索最广泛的应用是找出从根节点到目标节点的最短路径,我们将根据节点到根节点的距离依次遍历,第一次找到目标节点时,就是根节点到目标节点的最短距离。节点的处理顺序与他们添加进处理队列的顺序相同,因此我们可以使用队列这样的数据结构来处理BFS问题。

BFS的Java模板代码如下:

/**
 * Return the length of the shortest path between root and target node.
 */
int BFS(Node root, Node target) {
   
    Queue<Node> queue;  // store all nodes which are waiting to be processed
    Set<Node> used;     // store all the used nodes
    int step = 0;       // number of steps neeeded from root to current node
    // initialize
    add root to queue;
    add root to used;
    // BFS
    while (queue is not empty) {
   
        step = step + 1;
        // iterate the nodes which are already in the queue
        int size = queue.size();
        for (int i = 0; i < size; ++i) {
   
            Node cur = the first node in queue;
            return step if cur is target;
            for (Node next : the neighbors of cur) {
   
                if (next is not in used) {
   
                    add next to queue;
                    add next to used;
                }
            }
            remove the first node from queue;
        }
    }
    return -1;          // there is no path from root to target
}

其中used为一个哈希集,用来记录已经访问过的节点。当确定没有循环(如树的遍历)或者希望多次将同一节点加入队列中时,可以不使用哈希集。

为了更好地应用BFS算法,我们将看以下3个例题。

岛屿数量

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。

这道题目既可以使用深度优先搜索,也可以使用广度优先搜索来做,为了熟悉BFS,我们展示BFS的解法如下:

class Solution {
   
public:
    int numIslands(vector<vector<char>>& grid) {
   
        int row = grid.size();
        if (row == 0)
            return 0;
        int col = grid[0].size();
        
        queue<pair<int, int> > q;
        int ans = 0;
        for (int i = 0; i < row; ++i)
            for(int j = 0; j < col; ++j){
   
                if (grid[i][j] == '1'){
    // a new island
                    ans += 1;
                    q.push(make_pair(i, j));
                    grid[i][j] = '0';
                    while (!q.empty()) {
    // BFS searching for the neighbors
                        pair<int, int> pr = q.front();
                        q.pop();
                        int x = pr.first, y = pr.second;
                        if (x - 1 >= 0 && grid[x - 1][y] == '1') {
   
                            q.push(make_pair(x - 1, y));
                            grid[x - 1][y] = '0';
                        }
                        if (x + 1 < row && grid[x + 1][y] == '1') {
   
                            q.push(make_pair(x + 1, y));
                            grid[x + 1][y] = '0';
                        }
                        if (y - 1 >= 0 && grid[x][y - 1] == '1') {
   
                            q.push(make_pair(x, y - 1));
                            grid[x][y - 1] = '0';
                        }
                        if (y + 1 < col && grid[x][y + 1] == '1') {
   
                            q.push(make_pair(x, y + 1));
                            grid[x][y + 1] = '0';
                        }
                    }
                }
            }
        return ans;
    }
};

打开转盘锁

你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。

这道题相比于一般的BFS问题,增加了一个deadends集合,其中的状态不允许到达,那么我们可以简单地将deadends集合并入visited集合。由于这里会反复到达同一个状态,因此我们使用set来定义visited集合,以避免状态的重复添加。

class Solution {
   
public:
    // check if it is a deadend number
    bool is_deadend(vector<string>& deadends, string s){
   
        int length = deadends.size();
        for (int i = 0; i < length; ++i)
            if (s == deadends[i])
                return true;
        return false;
    }
    int openLock(vector<string>& deadends, string target) {
   
        if (is_deadend(deadends, "0000"))
            return -1;
        if (target == "0000")
            return 0;
        queue<string> q;
        q.push("0000");
        set<string> se; // record visited node, including deadends
        se.insert("0000");
        for (int i = 0; i < deadends.size(); ++i)
            se.insert(deadends[i]);
        int ans = 0;
        while (!q.empty()){
   
            ans += 1;
            int num = q.size();
            for (int i = 0; i < num; i++) {
   
                string state = q.front
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值