数据结构之栈和队列

时间:2022-02-09 17:39:08

  其实栈和队列是特殊的线性表,特殊之处在于插入和删除的位置受到限制,如果插入和删除只在一端进行,那么这就是栈,如果插入在尾部进行,删除在头部进行就是队列,下面我们具体来看看栈和队列的实现。

1:栈

定义:栈是一种特殊的线性表,其插入和删除只能在一端进行,允许操作的一端叫做栈顶,不允许操作的一端叫做栈底。插入元素我们叫做进栈(push),删除元素我们叫做出栈(pop)。下面我们定义栈的接口

public interface SStack<T> {
/**
* 验证是否是空栈
*
@return
*/
boolean isEmpty();

/**
* 进栈
*
@param x
*/
void push(T x);

/**
* 出栈
*
@return
*/
T pop();

/**
* 取栈顶元素
*
@return
*/
T get();
}

2:顺序表实现栈

要实现栈,首先我们需要一个变量来记录栈顶的位置,这个变量我们设置top。我们定义top初始值为-1,也就是说当栈空的时候top就是-1。栈的特点就是先进后出,那么进栈就很简单了,我们只需要让top++即可,然后取得时候只需要去top对应的元素,后进去的就先出来,先进去的后出来。代码如下

private Object[] elements;
private int top;//记录栈顶元素位置

public SeqStack() {
this(64);
}

public SeqStack(int size) {
this.elements = new Object[size];
this.top = -1;
}

public boolean isEmpty() {
return this.top == -1;
}

public void push(T x) {
if (x == null) {
return;
}
if (this.top == this.elements.length - 1) {
Object[] temp
= this.elements;
this.elements = new Object[2 * this.elements.length * 2];
for (int i = 0; i < this.top; i++) {
this.elements[i] = temp[i];
}
}
this.top++;
this.elements[this.top] = x;
}

public T pop() {
return this.top == -1 ? null : (T) this.elements[this.top--];
}

public T get() {
return this.top == -1 ? null : (T) this.elements[this.top];
}

3:链式表实现栈

链式实现更新的简单了,同样我们只需要用一个头节点来记录栈顶即可,如果头节点为空说明是空栈,出栈的时候只需求去头节点,入栈的时候,把新元素赋值给头元素,头元素的后继节点为原先的头节点即可。代码如下

  public Node<T> top;
public LinkedStack(){
top
=new Node<T>(null,null);
}
public LinkedStack(T[] elements) {
if (elements == null) {
return;
}
for (int i = 0; i < elements.length; i++) {
this.top = new Node<T>(elements[i], this.top);
}
}

public boolean isEmpty() {
return this.top == null;
}

public void push(T x) {
if (x==null){
return;
}
this.top=new Node<T>(x,this.top);
}

public T pop() {
if (this.top != null) {
T old
= this.top.data;
this.top = this.top.next;
return old;
}
return null;
}

public T get() {
return this.top == null ? null : this.top.data;
}

4:栈的应用

我们经常写代码应该知道当我们写方法的时候少一个}编译器会立马报异常,现在我们考虑是如何实现的。

假设这个一个字符串,我们遇到{号的时候把它放入栈中,如果遇到}号的时候就出栈,如果出栈的是{则说明一对{}匹配成功,如果栈空,或者不是{表示缺少与}相匹配的{符合,如果最后栈不为null,说明缺少}号。我们来实现下。

 public static boolean isValid(String str) {
SStack
<Character> characterSStack = new SeqStack<Character>(str.length());
boolean result=true;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
switch (ch) {
case '{':
characterSStack.push(ch);
break;
case '}':
boolean a = characterSStack.isEmpty();
boolean b = characterSStack.get() != '{';
if (a || b) {
result
= false;
}
characterSStack.pop();
break;
}
if (!result) {
break;
}
}
if (!characterSStack.isEmpty()) {
result
=false;
}
return result;
}

 5:队列

队列我们说了只可以在队列和队尾操作,队尾我们加入元素,队列我们删除元素,没有元素我们称为空队列。下面先定义接口

public interface Queue<T> {
/**
* 验证队列是否为空
*
@return
*/
boolean isEmpty();

/**
* 入队
*
@param x
*/
void enQueue(T x);

/**
* 出队
*
@return
*/
T deQueue();
}

6:顺序表实现队列

既然我们分别是在头和尾进行操作,那么我们就需要2个变量值来记录队列和队尾。我们分别用front、rear分别表示对列和队尾,如果我们加入第一个一个元素,rear和front都+1,其他的我们只需要rear+1,如果出对列同样我们让front+1,这样一来就满足我们的条件,但是问题是如果加入5个元素后rear=4,然后在出对列2个元素那么front=1;这样一来我们的队列就无法全部运用上,出现了假溢出。所以我们考虑采用循环的队列模式。具体实现如下

public class SeqQueue<T> implements Queue<T> {
private Object[] elements;
private int rear;//队尾
private int front;//队头

public SeqQueue() {
this(64);
}

public SeqQueue(int length) {
if (length <= 0) {
return;
}
this.elements = new Object[length];
this.front = rear = 0;
}

public boolean isEmpty() {
return this.front == this.rear;
}

public void enQueue(T x) {
if (x == null) {
return;
}
if ((this.rear + 1) % this.elements.length == this.front) {
Object[] temp
= this.elements;
this.elements = new Object[2 * this.elements.length * 2];
int i = this.front, j = 0;
while (i != this.rear) {
this.elements[j] = temp[i];
i
= (i + 1) % temp.length;
j
++;
}
this.front = 0;
this.rear = j;
}
this.elements[this.rear] = x;
this.rear = (this.rear + 1) % this.elements.length;
}

public T deQueue() {
if (this.isEmpty()) {
return null;
}
T old
= (T) this.elements[this.front % this.elements.length];
this.front = this.front + 1 * this.elements.length;
return old;
}

7:链式实现对列

链式的就比较简单了,如果是队尾的话我们直接可以把rear的后继节点赋值为新元素,然后在把rear节点赋值为新元素。但是我们要考虑的是空表插入,这个时候就没有后继节点了,这个时候我们直接把新元素赋值给front即可。如果出队就更简单了,直接获取front即可。我们看下源码

public class LinkedQueue<T> implements Queue<T> {
private Node<T> front;
private Node<T> rear;

public boolean isEmpty() {
return this.front == this.rear && this.front == null;
}

public void enQueue(T x) {
if (x == null) {
return;
}
Node
<T> p = new Node<T>(x, null);
if (this.front == null) {
this.front = p;
}
else {
this.rear.next = p;
}
this.rear = p;
}

public T deQueue() {
if (this.front == null) {
return null;
}
T old
= this.front.data;
this.front = this.front.next;
return old;
}