数据结构之栈和队列
其实栈和队列是特殊的线性表,特殊之处在于插入和删除的位置受到限制,如果插入和删除只在一端进行,那么这就是栈,如果插入在尾部进行,删除在头部进行就是队列,下面我们具体来看看栈和队列的实现。
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; }