1. 双端栈的定义:双端栈是指将一个线性表的两端当做栈底分别进行入栈和出栈操作,主要利用了栈“栈底位置不变,而栈顶位置动态变化” 的特性。如图:
2. 双端栈的实现方式:双端栈是线性表的一种,更是栈的一个特殊分类,所以我们可以用动态数组和栈的思想来实现双端栈,但是由于其操作过于特殊,我们并不能借助ArrayList或ArrayStack实现,所以这里从头开始实现双端栈。
3. 双端栈的实现:
1>. 双端栈的接口:其中只定义双端栈的一些操作方法。
public interface DoubleEndStack<E> extends Iterable<E>{
//获取左端栈的有效元素个数
public int sizeLeft();
//获取右端栈的有效元素个数
public int sizeRight();
//判断左端栈是否为空
public boolean isLeftEmpty();
//判断右端栈是否为空
public boolean isRightEmpty();
//向左端栈中添加指定元素
public void pushLeft(E element);
//向右端栈中添加指定元素
public void pushRight(E element);
//弹出左端栈的栈顶元素
public E popLeft();
//弹出右端栈的栈顶元素
public E popRight();
//查看左端栈的栈顶元素
public E peekLeft();
//查看右端栈的栈顶元素
public E peekRight();
}
2>. 双端栈的实现类:我们在实现类中具体来实现接口中的方法。
public class ArrayDoubleEndStack<E> implements DoubleEndStack<E> {
private int ltop; //表示左端栈的栈顶
private int rtop; //表示右端栈的栈顶
private E[] data; //存储元素的容器
private static int DEFAULT_CAPACITY = 10; //数组容器的默认容量
//初始化data ltop rtop
public ArrayDoubleEndStack(){
data = (E[]) new Object[DEFAULT_CAPACITY];
ltop = -1;
rtop = data.length;
}
/*
当ltop=-1时 表示左端栈为空
左端栈的有效元素个数表示为 ltop + 1
当rtop=data.length时 表示右端栈为空
右端栈的有效元素个数表示为 data.length - rtop
当ltop+1=rtop时 表示栈满
*/
//获取左端栈的有效元素个数
@Override
public int sizeLeft() {
return ltop + 1;
}
//获取右端栈的有效元素个数
@Override
public int sizeRight() {
return data.length - rtop;
}
//判断左端栈是否为空
@Override
public boolean isLeftEmpty() {
return ltop == -1;
}
//判断右端栈是否为空
@Override
public boolean isRightEmpty() {
return rtop == data.length;
}
//向左端栈中添加指定元素
@Override
public void pushLeft(E element) {
if(ltop + 1 == rtop){ //如果双端栈满 就扩容
resize(data.length * 2);
}
//先更新左端栈顶的值 向左端栈栈顶添加元素
data[++ltop] = element;
}
//向右端栈中添加指定元素
@Override
public void pushRight(E element) {
if(ltop + 1 == rtop){ //如果双端栈满 就扩容
resize(data.length * 2);
}
//先更新右端栈顶的值 向右端栈栈顶添加元素
data[--rtop] = element;
}
//弹出左端栈的栈顶元素
@Override
public E popLeft() {
if(isLeftEmpty()){ //判断左端栈是否为空
throw new IllegalArgumentException("left stack is null");
}
//获取左端栈栈顶元素 并将左端栈顶指针更新
E ele = data[ltop--];
//判断左端栈和右端栈有效元素个数之和是否小于等于data容量的四分之一 如果是 就将data缩容至原容量的一半
if(sizeLeft() + sizeRight() <= data.length / 4){
resize(data.length / 2);
}
return ele;
}
//弹出右端栈的栈顶元素
@Override
public E popRight() {
if(isRightEmpty()){ //判断右端栈是否为空
throw new IllegalArgumentException("right stack is null");
}
//获取右端栈栈顶元素 并将右端栈顶指针更新
E ele = data[rtop++];
//判断左端栈和右端栈有效元素个数之和是否小于等于data容量的四分之一 如果是 就将data缩容至原容量的一半
if(sizeLeft() + sizeRight() <= data.length / 4){
resize(data.length / 2);
}
return ele;
}
private void resize(int newLength) {
E[] newData = (E[]) new Object[newLength];
/*
右端栈扩容 直接循环依次赋值即可
而左端栈扩容 要从新数组的newLength - sizeRight()开始 直到newLength - 1结束 最后将rtop重新赋值为newLength - sizeRight()
*/
//操作左端栈 ltop不需要更新
for(int i = 0; i <= ltop; i++){
newData[i] = data[i];
}
//操作右端栈 注意更新rtop指针
int index = rtop;
for(int i = newLength - sizeRight(); i < newLength; i++){
newData[i] = data[index++];
}
rtop = newLength - sizeRight(); //更新rtop指针 (重点)
data = newData; //最后将新数组赋值给原数组
}
//查看左端栈的栈顶元素
@Override
public E peekLeft() {
return data[ltop];
}
//查看右端栈的栈顶元素
@Override
public E peekRight() {
return data[rtop];
}
//获取当前这个数据结构/容器 的 迭代器
//通过迭代器对象 更方便挨个取出每一个元素
//同时 实现了Iterable 可以让当前的数据结构/容器 被foreach循环遍历
@Override
public Iterator iterator() {
return new ArrayDoubleEndStackIterator();
}
class ArrayDoubleEndStackIterator implements Iterator<E>{
ArrayList<E> list;
private Iterator<E> iterator;
//先将双端栈中的全部元素 包括左端栈和右端栈 存放在新的容器ArrayList中 只需迭代新容器ArrayList即可
public ArrayDoubleEndStackIterator() {
list = new ArrayList<>();
for(int i = 0; i < ltop; i++){
list.add(data[i]);
}
for(int i = rtop; i < data.length; i++){
list.add(data[i]);
}
//获取list的迭代器
iterator = list.iterator();
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public E next() {
return iterator.next();
}
}
//格式化输出 格式化双端栈doubleEndStack输出的格式
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("doubleEndStack: " + (sizeLeft() + sizeRight()) + "/" + data.length + "[");
if(isLeftEmpty() && isRightEmpty()){
str.append("]");
}
//先格式化右端栈
for(int i = 0; i <= ltop; i++){
str.append(data[i]);
if(i == ltop && isRightEmpty()){
str.append("]");
}else{
str.append(",");
str.append(" ");
}
}
//格式化左端栈
for(int i = rtop; i < data.length; i++){
str.append(data[i]);
if(i == data.length - 1){
str.append("]");
}else{
str.append(",");
str.append(" ");
}
}
return str.toString();
}
}
本文介绍了一种特殊的线性数据结构——双端栈的定义、特点及其实现方法。双端栈允许两端作为栈底进行入栈和出栈操作,通过动态数组实现,并支持扩容和缩容。
935

被折叠的 条评论
为什么被折叠?



