动态数组----双端栈的实现

本文介绍了一种特殊的线性数据结构——双端栈的定义、特点及其实现方法。双端栈允许两端作为栈底进行入栈和出栈操作,通过动态数组实现,并支持扩容和缩容。

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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值