栈与队列

栈与队列

栈(Stack)是一种特殊的线性表,特点可以描述为后入先出。然后栈根据存储结构的不同可以分为顺序栈(顺序存储结构)和链式栈(链式存储结构),可以把栈看作是向上开口的容器,最先放入到容器当中的元素在最底部,最后放入容器的元素在最顶部,能够操作的元素只能是在容器最顶部的元素。

在这里插入图片描述
在这里插入图片描述

栈的基本操作

Clear:空栈

isEmpty:判断栈是否为空

isFull:判断栈是否为满,如果存储结构为链式存储结构则无栈满

push:压入元素

pop:取出元素

peek:取栈顶元素不对栈顶指针作修改

SeqStack(顺序栈)

使用顺序存储结构作为栈的容器结构,使用类变量top存储栈顶的索引位置作为栈顶指针。

SeqStack具体实现
package stack;

public class SeqStack implements Stack {
	private int top = -1;
	private Object[] stackElements;

	SeqStack() {
		this(64);
	}

	SeqStack(int length) {
		stackElements = new Object[length];
	}

	@Override
	public boolean isEmpty() {
		return top == -1;
	}

	@Override
	public boolean isFull() {
		return top == stackElements.length - 1;
	}

	@Override
	public void push(Object element) throws Exception {
		if (isFull())
			throw new Exception("栈满...");
		else if (stackElements == null)
			throw new Exception("容器未创建...");
		stackElements[++top] = element;
	}

	@Override
	public Object pop() {
		if (isEmpty())
			return null;
		else
			return stackElements[top--];
	}

	@Override
	public Object peek() {
		return stackElements[top];
	}

	@Override
	public void clear() {
		top = -1;
		stackElements = null;
	}

	@Override
	public int getLength() {
		return stackElements.length;
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("{");
		for (int i = 0; i < stackElements.length; i++) {
			if(stackElements[i]!=null) {
				sb.append(stackElements[i]);
				if (i != stackElements.length - 1)
					sb.append(",");
			}
		}
		sb.append("}");
		return sb.toString();
	}
}

LinkedStack(链式栈)

使用链式存储结构作为栈的容器结构,使用Node类实例top作为栈顶指针指向最顶部元素。

LinkedStack具体实现
public class LinkedStack implements Stack {

	private Node top;
	
	@Override
	public void clear() {
		top = null;
	}

	@Override
	public int getLength() {
		Node p = top;
		int count = 0;
		while(p!= null) {
			count++;
			p = p.getNext();
		}
		return count;
	}

	@Override
	public boolean isEmpty() {
		return top == null;
	}

	@Override
	public boolean isFull() {
		return false;
	}

	@Override
	public void push(Object element) throws Exception {
		top = new Node(element,top);
	}

	@Override
	public Object pop() {
		if(!isEmpty()) {
			Object data = top.getData();
			top = top.getNext();
			return data;
		}
		return null;
	}

	@Override
	public Object peek() {
		return top.getData();
	}

    public String toString(){
        ...
    }
}

队列

队列可以认为是一个有前后开口的容器,其中一个口作为入口一个口作为出口。

队列根据存储结构又能分成顺序队列和链式队列

队列的基本操作

clear:清空队列

isEmpty:判断空队列

length:队列长度

peek:取队列首元素

offer:入队

poll:出队

顺序队列

索引0的位置作为队列头,每次取出元素重队列头取出,插入元素从队列尾插入

单队列

单队列是最常见的队列,但是因为单队列每次添加新元素都是从队尾位置添加的原因会出现假溢出的情况,即空取现象。

在这里插入图片描述

如上图,每一个元素入队rear指针向后移动一位,每一个元素出队front指针向后移动一位。

取出两个元素以后,front就来到了3的位置,此时有两个空位,如果要插入两个元素的话rear指针就需要向后移动两位,这样rear指针就已经指到到了容器空间以外了。

循环队列

为了避免上面所说的假溢出情况出现可以改为使用循环队列

这里给出两种方法实现:

  1. 少用一个存储单元
  2. 添加flag标记
第一种

在这里插入图片描述

这种方法实现循环队列少用一个存储单元方便用来判断队列满和队列空。

状态判断

判断队列满:(rear+1)%queue.length == front

入队后rear指针的变动:rear = (rear+1)%queue.length

出队后front指针的变动:front = (front +1)%queue.length

核心实现
	@Override
	public boolean isEmpty() {
		return front == rear;
	}

	@Override
	public boolean isFull() {
		return front == (rear + 1) % elements.length;
	}

	@Override
	public void offer(Object element) throws Exception {
		if (isFull()) {
			throw new Exception("队列满...");
		}
		else {
			elements[rear] = element;
			rear = (rear+1)%elements.length;
		}
	}

	@Override
	public Object poll() throws Exception {
		if(isEmpty()) {
			throw new Exception("队列空...");
		}else {
			Object temp = elements[front];
			front = (front+1)%elements.length;
			return temp;
		}
	}
第二种

第二种方法则不需要额外空出一个存储单元来,但是需要创建一个标记变量来标记上次对队列的操作是入队还是出队。

状态判断

队列满:rear==front&&flag == true

队列空:rear==front&&flag == false

其中flag为true时说明队列的上一步操作是元素入队

flag为false时说明队列的上一步操作是元素出队或没有过操作

核心实现
	@Override
	public boolean isEmpty() {
		return rear==front&&!flag;
	}

	@Override
	public boolean isFull() {
		return rear==front&&flag;
	}
	@Override
	public void offer(Object element) throws Exception {
		if(isFull()) {
			throw new Exception("队列满...");
		}else {
			elements[rear] = element;
			rear = (rear+1)%elements.length;
			this.flag = true;
		}
	}

	@Override
	public Object poll() throws Exception {
		if(isEmpty()) {
			throw new Exception("队列空...");
		}else {
			Object temp = elements[front];
			front = (front+1)%elements.length;
			this.flag = false;
			return temp;
		}
	}

链式队列

链式队列是使用连式存储结构的队列,相对来说没有什么特殊之处

需要注意的是,当front与rear都为null时队列为空,当poll到最后一个元素时(即队列不为空且front与rear指向同一位置时)需要把rear也设置为null,因为此时rear依旧保存原来的元素,此时队列应该为空却不为空(front通过rear.getNext()已经设置为null)。

具体实现如下:

package queue;

public class LinkedQueue implements Queue {
	Node front, rear = null;

	@Override
	public void clear() {
		front = rear = null;
	}

	@Override
	public boolean isEmpty() {
		return front == rear && front == null;
	}

	@Override
	public boolean isFull() {
		return false;
	}

	@Override
	public int getLength() {
		int length = 0;
		for (Node temp = front; temp != rear && temp != null; temp = temp.getNext()) {
			length++;
		}
		return length;
	}

	@Override
	public Object peek() {
		return front.getData();
	}

	@Override
	public void offer(Object element) throws Exception {
		if (isEmpty()) {
			front = rear = new Node(element);
		} else {
			rear.setNext(new Node(element));
			rear = rear.getNext();
		}
	}

	@Override
	public Object poll() throws Exception {
		if (isEmpty()) {
			throw new Exception("队列空...");
		} else {
			Object temp = front.getData();
			if (front == rear)
				rear = null;
			front = front.getNext();
			return temp;
		}
	}

}

优先级队列

优先级队列是将数据按照优先级大小进行存储的队列,为了快速的访问到优先级高的元素和快速的插入操作通常选择链式存储结构来实现。

具体操作除入队前进行遍历操作找到当前优先级适宜的插入位置进行插入外,与链式队列基本相同。

package queue;

public class PriorityQueue implements Queue{
	private Node front,rear;
	
	@Override
	public void clear() {
		front = rear = null;
	}

	@Override
	public boolean isEmpty() {
		return front == rear && front == null;
	}

	@Override
	public boolean isFull() {
		return false;
	}

	@Override
	public int getLength() {
		int length = 0;
		for (Node temp = front; temp != rear && temp != null; temp = temp.getNext()) {
			length++;
		}
		return length;
	}

	@Override
	public Object peek(){
		if(isEmpty()) {
			return null;
		}else {
			return front.getData();
		}
	}

	@Override
	public void offer(PriorityData element) throws Exception {
		if(isEmpty()) {
			front = rear = new Node(element);
		}else{
			Node temp = front,p = front;
			while(p!=null&&element.getPriority()<=p.getData().getPriority()) {
				temp = p;
				p = p.getNext();
			}
			Node n = new Node(element);
			if(p == null) { //判断结果为插入位置为队尾
				temp.setNext(n);
			}else if(temp == null) { //判断结果为插入位置为队首
				n.setNext(front);
				front = n;
			}else { //队列中
				n.setNext(p);
				temp.setNext(n);
			}
		}
	}

	@Override
	public PriorityData poll() throws Exception {
		if(isEmpty()) {
			throw new Exception("队列空...");
		}else {
			Node temp = front;
			if (front == rear)
				rear = null;
			front = front.getNext();
			return temp.getData();
		}
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值