什么是队列
通常情况下,有一片连续的存储区,并且里面存储着不同类型的元素,还有两个指针,一个头指针,一个尾指针。并且尾指针指向的位置是最后一个元素的下一位(通常情况下)。支持出队入队操作,这就是队列。
普通队列
普通队列只支持从队首出队,队尾入队。出队操作头指针往后移动一位,入队操作尾指针向后移动一位,把元素放进队列。
队列溢出
在普通队列中,插入的元素数量,大于队列长度时,此时叫做队列溢出。但是在队列中元素并没有满,而只是尾指针移动到了队列末尾,叫做队列假溢出。
循环队列
为了对应解决队列假溢出的问题,出现了循环队列,如果尾指针指向队列末尾,此时队首是空,那么就可以将尾指针移动到队首位置,如还有添加元素入队操作,那么依次判断并往后移动,这样就类似与链表有环,所以叫做循环队列。
队列的典型应用场景
- CPU的超线程技术,每个CPU都有N个核心如双核四核,每个核心对应一个指令队列,如果一个核心对应多个队列,就叫虚拟核心。比如双核对应4个任务队列就叫虚拟四核(标准实现一般都是一个核心对应两个队列,以达到最大利用率和性能)。
- 线程池的任务队列,(扩展:进程和线程,进程代表资源的总和,线程代表进程中的某个事物,一个进程可以包含多个线程)
- 总结:队列的使用场景是用做缓冲区。
使用JS创建一个普通队列
实现普通队列的基本属性和操作(不是标准答案,只是实现队列大概的结构)
class Queue {
constructor(length){
this.queue = new Array(length); // 初始化队列长度
this.head = 0; // 初始化头指针
this.tail = 0; // 初始化尾指针
}
push(val) { // 入队
if (this.full()) {
console.error("队列已满");
return;
}
this.queue[this.tail] = val;
this.tail += 1; // 尾指针向后移动一位
this.getQueueElement();
}
pop() { // 出队
if (this.empty()) return;
this.head += 1; //头指针往后走一位
this.getQueueElement();
}
empty() { // 判空
return this.head === this.tail;
}
full() { // 判满
return this.tail === this.queue.length;
}
front() { // 查看队首元素
return this.queue[this.head];
}
getQueueElement() {
let ret = ""
for (let i = this.head; i < this.tail; i++){
ret += ` ${this.queue[i]} `;
}
console.log(ret);
}
}
const ordinaryQueue = new Queue(5);
ordinaryQueue.push(1);
ordinaryQueue.push(2);
ordinaryQueue.push(3);
ordinaryQueue.pop();
ordinaryQueue.pop();
ordinaryQueue.push(4);
ordinaryQueue.push(5);
ordinaryQueue.push(6); // 队列假溢出。
console.log(ordinaryQueue.front()) // 队首元素
使用JS创建一个循环队列
实现循环队列的基本属性和操作(不是标准答案,只是实现队列大概的结构)
class Queue {
constructor(length){
this.queue = new Array(length); // 初始化队列长度
this.head = 0; // 初始化头指针
this.tail = 0; // 初始化尾指针
this.length = length; // 队列总大小
this.cnt = 0; // 队列元素中的数量
}
push(val) { // 入队
if (this.full()) {
console.error("队列已满");
return;
}
this.queue[this.tail] = val;
this.tail += 1; // 尾指针向后移动一位
this.cnt += 1;
if (this.tail === this.length){ // 判断是否指向数组最后一位
this.tail = 0; // 继续从队列头开始走,达到一圈一圈循环的效果
}
this.getQueueElement();
}
pop() { // 出队
if (this.empty()) return;
this.head += 1; //头指针往后走一位
this.cnt -= 1;
if (this.head === this.length){ // 判断是否指向数组最后一位
this.head = 0; // 继续从队列头开始走,达到一圈一圈循环的效果
}
this.getQueueElement();
}
empty() { // 判空
return this.cnt === 0;
}
full() { // 判满
return this.cnt === this.length;
}
front() { // 查看队首元素
return this.queue[this.head];
}
size() { // 返回队列中元素的数量
return this.cnt;
}
getQueueElement() {
let ret = ""
for (let i = 0, j = this.head; i < this.cnt; i++){
ret += ` ${this.queue[j]} `;
j +=1;
if(j === this.length) j = 0; // 因为head有可能已经走了n圈了。所以cnt个输出,需要重置j
}
console.log(ret);
}
}
const loopQueue = new Queue(5);
loopQueue.push(1);
loopQueue.push(2);
loopQueue.push(3);
loopQueue.pop();
loopQueue.pop();
loopQueue.push(4);
loopQueue.push(5);
loopQueue.push(6); // 队列依然可插入。
loopQueue.push(7);
loopQueue.push(8); // 队列真溢出。
console.log(loopQueue.front()) // 队首元素