栈(Stack)是一种特殊的线性表,特点可以描述为后入先出。然后栈根据存储结构的不同可以分为顺序栈(顺序存储结构)和链式栈(链式存储结构),可以把栈看作是向上开口的容器,最先放入到容器当中的元素在最底部,最后放入容器的元素在最顶部,能够操作的元素只能是在容器最顶部的元素。
Clear:空栈
isEmpty:判断栈是否为空
isFull:判断栈是否为满,如果存储结构为链式存储结构则无栈满
push:压入元素
pop:取出元素
peek:取栈顶元素不对栈顶指针作修改
使用顺序存储结构作为栈的容器结构,使用类变量top存储栈顶的索引位置作为栈顶指针。
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();
}
}
使用链式存储结构作为栈的容器结构,使用Node类实例top作为栈顶指针指向最顶部元素。
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指针就已经指到到了容器空间以外了。
为了避免上面所说的假溢出情况出现可以改为使用循环队列
这里给出两种方法实现:
这种方法实现循环队列少用一个存储单元方便用来判断队列满和队列空。
判断队列满:(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();
}
}
}