队列
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。