队列——也是一种操作受限的线性表数据结构

队列

  • 先进先出,后进后出
  • 和栈的入栈push、出栈pop类似,队列提供入队enqueue、出队dequeue两种操作,也是一种操作受限的线性表数据结构
  • 常用:循环队列、阻塞队列、并发队列
  • 基于数组:顺序队列
  • 基于链表:链式队列

顺序队列

//java实现一个顺序队列
public class ArrayQueue{
	private String[] items;
	//数组大小
	private int n = 0;
	
	private int head = 0;
	private int tail = 0;
	//构造
	public ArrayQueue(int capacity){
		this.items = new String[capacity];
		this.n = capacity;
	}
	
	//入队enqueue
	public boolean enqueue(String item){
		if(tail==n){
			//队列满
			return false;
		}else{
			items[tail] = item;
			++tail;
			return true;
		}
		
	}
	//出队dequeue
	public String dequeue(){
		if(head==tail){
			//队列空
			return null;
		}else{
			String res = items[head];
			++head;
			return res;
		}
	}
	
}

  • 和栈不同的是,栈只有一个栈顶指针,出入都在栈顶。
  • 而队列需要头指针head和尾指针tail,head用于出队,tail用于入队。
    问题:
    tail已经移到队列最尾部,head经过几次出队在数组中间,此时队列的数组前面还是空的,但却无法做入队操作?
    **答:**参考数组删除元素,数据搬移问题的解决办法,通过标记出队的元素,当队尾没有空间但队头有空间,一次性清楚被标记的元素,并做数据搬移。
    修改入队enqueue()方法:
public boolean enqueue(String item){
	if(tail==n){
		//队尾没有空间
		if(head==0){
			//队头没有空间
			return false;
		}else{
			//队头有空间
			//数据搬移
			for(int i=head;i<tail;++i){
				items[i-head] = items[i];
			}
			tail = tail-head;
			head = 0;
			
		}
		
	}
}

出队: O(1)
入队: 最好O(1),最坏O(n),均摊O(1)

循环队列

  • 当队尾没有空间时,队头还有空间,tail指向下标0。
  • 例如:队列容量n为9,某时tail指向8,当有元素入队将元素放在下标8的位置,tail此时不指向9,而时指向0;
  • 假如此时head=0,tail也即将指向0,那么此时队列在入队操作后就满了;

问题关键在于 怎样确定队空和队满?
非循环队列:队空 head=tail ,队满 tail=n
循环队列: 队空 head=tail, 队满 (tail+1)%n=head

//顺序循环队列
public class CircularQueue {
  // 数组:items,数组大小:n
  private String[] items;
  private int n = 0;
  // head表示队头下标,tail表示队尾下标
  private int head = 0;
  private int tail = 0;

  // 申请一个大小为capacity的数组
  public CircularQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // 入队
  public boolean enqueue(String item) {
    // 队列满了
    if ((tail + 1) % n == head) return false;
    items[tail] = item;
    tail = (tail + 1) % n;
    return true;
  }

  // 出队
  public String dequeue() {
    // 如果head == tail 表示队列为空
    if (head == tail) return null;
    String ret = items[head];
    head = (head + 1) % n;
    return ret;
  }
}

阻塞队列

  • 阻塞队列其实就是在队列基础上增加了阻塞操作。
  • 在队列为空的时候,从队头取数据会被阻塞,直到队列中有了数据才能返回;
  • 如果队列已经满了,则插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据。

并发队列

  • 线程安全的队列我们叫作并发队列。
  • 最简单直接的实现方式是直接在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或者取操作。
  • 实际上,基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列。
  • 这也是循环队列比链式队列应用更加广泛的原因。

熄灯

队列应用场景:

  • 对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队。
  • 例如:线程池,数据库连接池等。

你可能感兴趣的:(算法和数据结构,队列,数据结构,链表,java,queue)