队列与BFS

一、队列

1.队列的定义
在这里插入图片描述
队列是FIFO(先入先出) 数据结构。插入(insert)操作也称作入队(enqueue),新元素始终被添加在队列的末尾。 删除(delete)操作也被称为出队(dequeue),你只能移除第一个元素。

2.队列的实现

用动态数组实现的队列

**class MyQueue {
    private:
        vector<int> data;       
        int p_start;    //指向队首        
    public:
        MyQueue() {p_start = 0;}
        
        bool enQueue(int x) {
            data.push_back(x);
            return true;
        }
        
        bool deQueue() {
            if (isEmpty()) {
                return false;
            }
            p_start++;
            return true;
        };
       
        int Front() {
            return data[p_start];
        };
       
        bool isEmpty()  {
            return p_start >= data.size();
        }
};
**

这种实现很简单,但在某些情况下效率很低。 随着起始指针的移动,浪费了越来越多的空间。更有效的方法是使用循环队列。 可以使用固定大小的数组和两个指针来指示起始位置和结束位置。 目的是重用我们之前提到的被浪费的存储。

class MyCircularQueue {
private:
    vector<int> data;
    int head;	//队首
    int tail;	//队尾
    int size;	//栈容量
    //注意:队首和队尾都指向有效位置,初始化和空队列时,head=tail=-1,使它们不指向无效位置;
public:
    MyCircularQueue(int k) {//初始化,栈空时head=tail=-1,不指向任何内容
        data.resize(k);		//data[-1]是不存在的
        head = -1;	
        tail = -1;
        size = k;
    }    
    bool enQueue(int value) {
        if (isFull()) {
            return false;
        }
        if (isEmpty()) {	//队列为空时插入元素,head=0,tail=0;
            head = 0;
        }
        tail = (tail + 1) % size;
        data[tail] = value;
        return true;
    }   
    /** Delete an element from the circular queue. Return true if the operation is successful. */
    bool deQueue() {
        if (isEmpty()) {
            return false;
        }
        if (head == tail) {	//队列中只有一个元素,删除后队列为空,所以head=tail=-1
            head = -1;
            tail = -1;
            return true;
        }
        head = (head + 1) % size;
        return true;
    }
    int Front() {
        if (isEmpty()) {
            return -1;
        }
        return data[head];
    }    
    int Rear() {
        if (isEmpty()) {
            return -1;
        }
        return data[tail];
    }   
    bool isEmpty() {
        return head == -1;
    }
    bool isFull() {
        return ((tail + 1) % size) == head;
    }
};

循环队列的实现

3.内置队列的使用

大多数流行语言都提供内置的队列库,,可以直接使用。
queue q;  //初始化
q.push(1);  //入队
q.empty();  //队空
q.pop();   //出队
q.front();   //队首
q.back();   //队尾
q.size();   //队大小

5.解决问题

leetcode 346.数据流中的移动平均值
leetcode 621.任务调度器

二、BFS

广度优先搜索(BFS)的一个常见应用是找出从根结点到目标结点的最短路径。与树的层序遍历类似,越是接近根结点的结点将越早地遍历。
首先将根结点排入队列。然后在每一轮中,我们逐个处理已经在队列中的结点,并将所有邻居添加到队列中。值得注意的是,新添加的节点不会立即遍历,而是在下一轮中处理。结点的处理顺序与它们添加到队列的顺序是完全相同的顺序,即先进先出(FIFO)。这就是我们在 BFS 中使用队列的原因。
使用 BFS 的两个主要方案:遍历或找出最短路径。通常,这发生在树或图中,也可以用于更抽象的场景中。

//返回到目标的最短路径长度
int BFS(Node root, Node target) {
    Queue<Node> queue;  // 保存所有等待访问的结点
    int step = 0;       //距离开始的距离
    
    add root to queue;
    // BFS
    while (queue is not empty) {
        step = step + 1;
        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) {
                add next to queue;
            }
            remove the first node from queue;
        }
    }
    return -1;   //没有查找到
}

1.如代码所示,在每一轮中,队列中的结点是等待处理的结点。
2.在每个更外一层的 while 循环之后,我们距离根结点更远一步。变量 step 指示从根结点到我们正在访问的当前结点的距离。

有时,确保我们永远不会访问一个结点两次很重要。否则,我们可能陷入无限循环。如果是这样,我们可以在上面的代码中添加一个哈希集来解决这个问题。这是修改后的伪代码:

//返回到目标的最短路径长度
int BFS(Node root, Node target) {
    Queue<Node> queue;  //保存所有等待访问的结点
    Set<Node> used;     //保存访问过的结点
    int step = 0;       //距离开始的距离
    
    add root to queue;
    add root to used;
    // BFS
    while (queue is not empty) {
        step = step + 1;
        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;          //没有结果
}

有两种情况不需要使用哈希集:
1.完全确定没有循环,例如,在树遍历中;
2.确实希望多次将结点添加到队列中。

图 二维数组
多源bfs:首先需要把多个源点都入队.
为了防止某个节点多次入队必须标记是否访问过,否则会超时。可以单独定义个访问标记数组在其入队之前就将其设置成已访问,或者直接在原数组中修改。

BFS求的是最短路径,但是求最远距离时不矛盾:
1.求最短路径时,只要找到目标和,返回。
2.求最长路径时,所有目标值都访问完,返回。

leetcode 130.被围绕的区域
leetcode 200.岛屿数量
leetcode 542.01 矩阵
leetcode 1162.地图分析
leetcode 79. 单词搜索
leetcode 994.腐烂的橘子

树 二叉树
单源bfs:首先把 root 节点入队
Tree 是有向的因此不需要标识是否访问过.
leetcode 102.二叉树的层次遍历
leetcode 101.对称二叉树

其它
leetcode 207.课程表
leetcode 279.完全平方数
leetcode 301.删除无效的括号
leetcode 752.打开转盘锁

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值