【数据结构】——队列

一、队列的定义及特点

队列是一种只允许在表的一端删除(称作表头)和表的另一端加入(称作表尾)的线性表。

从定义可以看出队列的特点是先进先出。

二、顺序表示的循环队列

概括来说循环队列是通过取模操作解决队列空间假溢出的问题,能更充分的利用所分配空间。因为在一般队列的顺序表示中,无论增加还是删除,头指针或尾指针都会递加,这样就会导致数组的低索引位置不可再被使用。

这种弊端在链式表示中不存在,因为链式表示中并不是一次开辟所有空间,而是动态开辟,并且删除的结点空间可以被释放。

这里只实现顺序表示的循环队列,个人认为链表的循环没有太大意义,如果只是循环而不增加队列长度,那么顺序表示更方便,如果需要增加队列长度的话,附带头尾指针的链表同样可以替代。

详细过程见代码

public class CircularQueue {
    private int[] arr;
    private int mod;
    private int head; 									// 头指针,指向队列第一个元素
    private int tail; 									// 尾指针,指向队列最后一个元素的下一个位置
    private int size; 									// 队列中已有的元素数目

    public CircularQueue(int size) {
        mod = size + 1; 								// 这里的模取mod=size+1是因为下面选用的队列判满条件会导致少用一个元素空间
        arr = new int[mod];
        head = tail = this.size = 0; 					// 初始化头尾指针 都指向0
    }

    private boolean isEmpty() { 						// 判断队空
        return head == tail;
    }

    private boolean isFull() {
        // 判断队满,这种取模操作必定会导致减少一个可使用空间,并且注意这个不可用空间并不固定。
        return (tail + 1) % mod == head;
    }

    public void inQueue(int num) {
        if (isFull()) {									// 入队前判断队满
            System.out.println("队满!");
        } else {
            arr[tail] = num;
            tail = (tail + 1) % mod;
            size++;
        }
    }

    public int outQueue() {
        if (isEmpty()) {								// 出队前判断队空
            System.out.println("队空!");
            return -1;
        } else {
            int num = arr[head];
            head = (head + 1) % mod;
            size--;
            return num;
        }
    }

    public int getSize() {
        // 直接tail-head可能会出现负数,这是因为循环过程中两指针不断移动,会出现tail<head的情况。
        return (tail - head + mod) % mod;
    }

}

重点要理解为什么mod=size+1。因为如果以实际的队列长度size为模,会出现队列判空和判满条件一致的情况,即:

  • 队列为空时,有tail=head
  • 队列为满时,同样有tail=head

此时头尾指针相遇对应两种情况,为了避免这种情况,在实际队列长度的基础上+1,多空出一个元素空间,此时:

  • 当队列为空,仅有tail=head,头尾指针相遇的这种情况
  • 队列满时,有(tail+1)%mod=head

三、链式表示的一般队列

public class LinkedQueue {
	private class Node { 								// 使用单链表存储
		int data;
		Node next;

		public Node() {
		
		}

		public Node(int num) {
			data = num;
		}
	}

	Node head; 											// 头结点,指向队列中的第一个结点
	Node tail;
	int size;

	public LinkedQueue() {
		head = new Node(); 								// 初始化,新建头结点,尾指针指向头结点
		tail = head;
		size = 0;
	}

	public void inQueue(int num) { 						// 入队不需要判断队满,入队元素连接在队尾,尾指针后移
		Node node = new Node(num);
		tail.next = node;
		tail = node;
		size++;
	}

	public int outQueue() { 							// 出队需要判断队空
		if (head == tail) {
			System.out.println("队空!");
			return -1;
		}

		if (head.next == tail) { 						// 队列中只有一个结点时断开头结点的引用
			Node node = tail;
			head.next = null;
			tail = head;
			size--;
			return node.data;
		} else { 										// 否则头结点指针指向这个结点的下一个结点
			Node node = head.next;
			head.next = head.next.next;
			size--;
			return node.data;
		}
	}

	public int getSize() {
		return size;
	}
}

队列判空条件同样可以使用成员变量size。链队的删除还应该注意要释放结点空间,但因为java中的GC在断开结点所有引用之后会自动回收,所以省略了这一步。

补充:用两个栈实现队列

很简单,具体代码就不写了,思路是一个栈用作入队,一个栈用作出队,当出队栈空时将入队栈中的所有元素依次弹出压入到出队栈中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值