数据结构入门之循环队列1

237 阅读4分钟

队列

1 写一下自己的总结: 定义定长的数组(队列长度恒定),可以理解为五个坑位,确定两个指针first和end,一个first指向第一个队列顶部,一个指向队列的最后,初始值都为-1,此时数组内部没元素。enQueue方法往队列占据第一个坑,此时first指向0即第一个坑位,每一次队列元素新增不会影响first的指向,只需要让end指向队列的尾部。计算方式 this.end = (this.end + 1) % this.size; 可以理解成首尾相接的圆圈。当第一位的坑位空了时 4=>最后元素的位置,加在堆的最后一位即: (4+1)/5 = 0 => 第一个位置 依次类推

删除队列的方法与其类似,这个时候是往后移动first,注意如果只剩下一个元素的情况删完队列又变空,first和end又回到-1

力扣实现循环队列


    // 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

    /**
     * Initialize your data structure here. Set the size of the queue to be k.
     * @param {number} k
     */
    var MyCircularQueue = function (k) {
        this.queue = new Array(k) // empty * 5
        this.frist = -1;
        this.end = -1;
        this.size = k; // 数组大小
    };

    /**
     * Insert an element into the circular queue. Return true if the operation is successful. 
     * @param {number} value
     * @return {boolean}
     */
    MyCircularQueue.prototype.enQueue = function (value) {

        if (this.isFull() == true) {
            return false
        }

        if (this.isEmpty() == true) {
            this.frist = 0
        }

        this.end = (this.end + 1) % this.size;
        this.queue[this.end] = value;
        return true


    };

    /**
     * Delete an element from the circular queue. Return true if the operation is successful.
     * @return {boolean}
     */
    MyCircularQueue.prototype.deQueue = function () {
        if (this.isEmpty() == true) {
            return false
        }
        if (this.frist == this.end) {
            this.frist = -1
            this.end = -1
            return true
        }
        this.queue[this.frist] = undefined;
        this.frist = (this.frist + 1) % this.size
        return true
    };

    /**
     * Get the front item from the queue.
     * @return {number}
     */
    MyCircularQueue.prototype.Front = function () {
        if (this.isEmpty() == true) {
            return -1
        }
        return this.queue[this.frist]
    };

    /**
     * Get the last item from the queue.
     * @return {number}
     */
    MyCircularQueue.prototype.Rear = function () {
        if (this.isEmpty() == true) {
            return -1
        }
        return this.queue[this.end]
    };

    /**
     * Checks whether the circular queue is empty or not.
     * @return {boolean}
     */
    MyCircularQueue.prototype.isEmpty = function () {
        return this.frist == -1
    };

    /**
     * Checks whether the circular queue is full or not.
     * @return {boolean}
     */
    MyCircularQueue.prototype.isFull = function () {
        return ((this.end + 1) % this.size) == this.frist;
    };
    var obj = new MyCircularQueue(5)
    var param_1 = obj.enQueue(100)
    var param_2 = obj.deQueue()
    var param_3 = obj.Front()
    var param_4 = obj.Rear()
    var param_5 = obj.isEmpty()
    var param_6 = obj.isFull()
    /**
     * Your MyCircularQueue object will be instantiated and called as such:
     * var obj = new MyCircularQueue(k)
     * var param_1 = obj.enQueue(value)
     * var param_2 = obj.deQueue()
     * var param_3 = obj.Front()
     * var param_4 = obj.Rear()
     * var param_5 = obj.isEmpty()
     * var param_6 = obj.isFull()
     */

要说框架的话,我们先举例一下 BFS 出现的常见场景好吧,问题的本质就是让你在一幅「图」中找到从起点 start 到终点 target 的最近距离,这个例子听起来很枯燥,但是 BFS 算法问题其实都是在干这个事儿,把枯燥的本质搞清楚了,再去欣赏各种问题的包装才能胸有成竹嘛。

这个广义的描述可以有各种变体,比如走迷宫,有的格子是围墙不能走,从起点到终点的最短距离是多少?如果这个迷宫带「传送门」可以瞬间传送呢?

再比如说两个单词,要求你通过某些替换,把其中一个变成另一个,每次只能替换一个字符,最少要替换几次?

再比如说连连看游戏,两个方块消除的条件不仅仅是图案相同,还得保证两个方块之间的最短连线不能多于两个拐点。你玩连连看,点击两个坐标,游戏是如何判断它俩的最短连线有几个拐点的?

本质上就是一幅「图」,让你从一个起点,走到终点,问最短路径。这就是 BFS 的本质,框架搞清楚了直接默写就好。

记住下面这个框架就 OK 了:

// 计算从起点 start 到终点 target 的最近距离
int BFS(Node start, Node target) {
    Queue<Node> q; // 核心数据结构
    Set<Node> visited; // 避免走回头路
    
    q.offer(start); // 将起点加入队列
    visited.add(start);
    int step = 0; // 记录扩散的步数

    while (q not empty) {
        int sz = q.size();
        /* 将当前队列中的所有节点向四周扩散 */
        for (int i = 0; i < sz; i++) {
            Node cur = q.poll();
            /* 划重点:这里判断是否到达终点 */
            if (cur is target)
                return step;
            /* 将 cur 的相邻节点加入队列 */
            for (Node x : cur.adj())
                if (x not in visited) {
                    q.offer(x);
                    visited.add(x);
                }
        }
        /* 划重点:更新步数在这里 */
        step++;
    }
}

队列 q 就不说了,BFS 的核心数据结构;cur.adj() 泛指 cur 相邻的节点,比如说二维数组中,cur 上下左右四面的位置就是相邻节点;visited 的主要作用是防止走回头路,大部分时候都是必须的,但是像一般的二叉树结构,没有子节点到父节点的指针,不会走回头路就不需要 visited。