三 . 栈的顺序存储结构

一 .  栈

1 .  栈的定义

栈是限定仅在表尾进行插入和删除操作的线性表,表尾是指栈顶,而不是栈底

  • 我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)
  • 不含任何数据元素的栈称为空栈
  • 栈又称为后进先出的线性表简称LIFO结构
  • 栈本身是一个线性表,其数据元素具有线性关系,只不过它是一种特殊的线性表而已

栈的插入:进栈,也称压栈,入栈。类似子弹入弹夹

栈的删除:出栈,也称弹栈。如图弹夹中子弹处夹

2 .  栈接口Stack的定义

package com.day2.栈;

public interface Stack<E> {
    /**
     * 进栈一个元素
     * @param e 进栈的元素
     */
    void push(E e);

    /**
     *出栈一个元素
     * @return 当前出栈的栈顶元素
     */
    E pop();

    /**
     * 获取当前栈顶
     * @return 当前栈顶元素
     */
    E peek();

    /**
     * 判断当前栈是否为空
     * @return  是否为空的布尔值
     */
    boolean isEmpty();

    /**
     * 获取栈中元素个数
     * @return 元素个数
     */
    int getSize();

    /**
     * 清空栈
     */
    void clear();
}

3 .  栈的顺序存储结构ArrayStack的定义

代码基于:《二 .  动态数组的概念及实现》中ArrayList实现        https://blog.csdn.net/qq_36549593/article/details/100664767

package com.day2.栈;

import com.day1线性表.ArrayList;

/**
 * 栈的实现完全可以借助ArrayList,ArrayList的尾部即为栈顶
 * @param <E>
 */
public class ArrayStack<E> implements Stack<E> {
    private ArrayList<E> list;  //将ArrayList作为实现ArrayStack的工具

    /**
     * 创建一个默认容量大小的栈
     */
    public  ArrayStack(){
        list = new ArrayList<>();
    }

    /**
     * 创建一个容量为指定capacity大小的栈(顺序表)
     * @param capacity
     */
    public ArrayStack(int capacity){
        list = new ArrayList<>(capacity);
    }

    @Override
    public void push(E e) {
        list.addLast(e);
    }   //入栈,将元素添加到ArrayList的尾部

    @Override
    public E pop() {
        return list.removeLast();
    }   //弹栈,删除ArrayList的尾部

    @Override
    public E peek() {
        return list.getLast();  //获取ArrayList的尾部
    }

    @Override
    public boolean isEmpty() {  //ArrayList为空即栈为空
        return list.isEmpty();
    }

    @Override
    public int getSize() {  //ArrayList的元素个数即为栈的元素个数
        return list.getSize();
    }

    @Override
    public void clear() {
       list.clear();
    }

    @Override
    public boolean equals(Object obj) {     //两个栈相比,相当于两个线性表相比
        if (obj==null){
            return false;
        }
        if (obj==this){
            return true;
        }
        if (obj instanceof ArrayStack){ // 不确定obj是不是ArrayStack
            ArrayStack stack = (ArrayStack) obj;    //将obj强转ArrayStack
            return list.equals(stack.list);    //线性表和线性表比较
        }
        return false;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("ArrayStack: size="+getSize()+",capacity="+list.getCapacity()+"\n");
        if (isEmpty()){
            sb.append("[]");
        }else {
            sb.append('[');
            for (int i = 0; i < getSize(); i++) {
                sb.append(list.get(i));
                if (i==getSize()-1){
                    sb.append(']');
                }else {
                    sb.append(',');
                }
            }
        }

        return sb.toString();
    }
}

二 .  双端栈

1.双端栈的定义

是指将一个线性表的两端当作栈底分别进行入栈和出栈操作

思路:它们是在数组的两端,向中间靠拢。top1和top2是栈1和栈2的栈顶指针,可以想象,只要它们俩不见面,两个栈就可以一直使用。

2.双端栈的顺序存储结构ArraayStackDoubleEnd的定义

2.代码实现

package com.day2.栈;

import com.day1线性表.ArrayList;

public class ArrayStackDoubleEnd<E> implements Stack<E>{
    private E[] data;   //用于储存数据的容器
    private int leftTop;    //左端栈的栈顶 开始在-1
    private int rightTop;   //右端栈的栈顶 开始在 data.length
    private static int DEFAULT_SIZE = 10;   //默认容量大小为10
    enum Direction{ //定义一个枚举方向,以便使用时传入左端或右端
        LEFT,RIGHT;
    }

    public ArrayStackDoubleEnd(){
        this(DEFAULT_SIZE);
    }
    public ArrayStackDoubleEnd(int capacity){
        data = (E[]) new Object[capacity];
        leftTop = -1;
        rightTop = data.length;
    }

    /**
     * 判断是否已满
     * @return
     */
    private boolean isFull(){
        return leftTop+1==rightTop;
    }
    /**
     * 从指定端口加入元素e
     * @param dir
     * @param e
     */
    public void push(Direction dir,E e){
        if (isFull()){  //如果满了需要扩容
            //扩容
            resize(data.length*2);
        }
        if (dir==Direction.LEFT){   //如果传入的值为左端,往左端添加元素
            data[++leftTop]=e;  //左端点添加元素左指针向右移
        }else { 
            data[--rightTop]=e; //右端点添加元素右指针向左移
        }
    }

    private void resize(int newLen) {
        E[] newData = (E[]) new Object[newLen];
        //左端复制 , 遍历原数组按角标给新数组就好了
        for (int i = 0; i <= leftTop; i++) {
            newData[i] = data[i];
        }
        //右端复制 , 需要遍历的是新数组的角标
        int index = data.length-1;  //需要从后往前遍历,需要获取最后一位的角标
        int i;
        for ( i= newData.length-1 ;i >= newData.length-data.length+rightTop ; i--) {    //i从右往左遍历,newData.length-data.length+rightTop可以得到右端点的第一个元素在新数组的角标
            newData[i]=data[index--];
        }
        data = newData;
        rightTop = i+1; //此时i以及超出了右端元素的范围,所以+1
    }


    /**
     * 弹出指定端口的栈顶
     * @param dir
     * @return
     */
    public E pop(Direction dir){
        if (dir==Direction.LEFT){   
            if (leftTop==-1){
                throw  new IllegalArgumentException("左端栈为空");
            }
            E e = data[leftTop--];  //趋于栈顶(左端点)操作,将左端点的指针向左移一位,实际上弹栈时,我们并没有将栈顶的元素删除
            if (getSize() <= data.length / 4 && data.length > DEFAULT_SIZE) {   //弹栈次数过多会导致空间浪费,剩余空间过大便需要缩容
                resize(data.length/2);
            }
            return e;
        }else {
            if (rightTop==data.length){
                throw new IllegalArgumentException("右端栈为空");
            }
            E e = data[rightTop++];
            if (getSize() <= data.length / 4 && data.length > DEFAULT_SIZE) {
                resize(data.length/2);
            }
            return e;    //右端点的指针向右移
        }
    }

    /***
     * 获取指定端口的栈顶元素
     * @param dir
     * @return
     */
    public E peek(Direction dir){
        if (dir==Direction.LEFT){
            if (leftTop==-1){
                throw  new IllegalArgumentException("左端栈为空");
            }
            return data[leftTop];   //判空后直接便可以将左端点所指的元素返回
        }else {
            if (rightTop==data.length){
                throw new IllegalArgumentException("右端栈为空");
            }
            return data[rightTop];
        }
    }

    /**
     * 获取指定端口的元素个数
     * @param dir
     * @return
     */
    public int getSize(Direction dir){
        if (dir==Direction.LEFT){
            return leftTop+1; //leftTop相当于角标,角标+1即为左端元素的个数
        }else {
            return data.length-rightTop;    
        }
    }

    /**
     * 判断指定端口是否为空
     * @param dir
     * @return
     */
    public boolean isEmpty(Direction dir){
        if (dir==Direction.LEFT){
            return leftTop==-1; //栈顶是初始值,栈为空
        }else {
            return rightTop==data.length;
        }
    }

    /**
     * 清空指定端口的栈
     * @param dir
     */
    public void clear(Direction dir){
        if (dir==Direction.LEFT){
            leftTop = -1;   //栈顶基于leftTop和rightTop操作,将它们设为没有值的状态,也就意味着栈是空的
        }else {
            rightTop = data.length;
        }
    }
    /**
     * 哪端少进哪端
     * @param e 进栈的元素
     */
    public void push(E e) {
        if (getSize(Direction.LEFT)<=getSize(Direction.RIGHT)){ //如果左边的元素小于等于右边的元素,新元素添加到左端
            push(Direction.LEFT,e);
        }else {
            push(Direction.RIGHT,e);    //左端多则进右端
        }
    }
    /**
     * 哪端多弹哪端
     * @return
     */
    @Override
    public E pop() {
        if (isEmpty()){
            throw new IllegalArgumentException("双端栈为空");
        }
        if (getSize(Direction.LEFT)>getSize(Direction.RIGHT)){  
            return pop(Direction.LEFT);
        }else {
            return pop(Direction.RIGHT);
        }
    }

    /**
     * 哪端多获取哪端,一样多默认左端
     * @return
     */
    @Override
    public E peek() {
        if (isEmpty()){
            throw new IllegalArgumentException("双端栈为空");
        }
        if (getSize(Direction.LEFT)>getSize(Direction.RIGHT)){  //左端和右端进行元素数的比较,哪边多获取哪边的栈顶
            return peek(Direction.LEFT);
        }else {
            return peek(Direction.RIGHT);
        }
    }

    /**
     * 判断左端栈和右端栈是否都为空
     * @return
     */
    @Override
    public boolean isEmpty() {
        return isEmpty(Direction.LEFT)&&isEmpty(Direction.RIGHT);
    }   //左端和右端都为空即为全空

    /**
     * 获取左端栈和右端栈元素的总和
     * @return
     */
    @Override
    public int getSize() {
        return getSize(Direction.LEFT)+getSize(Direction.RIGHT);
    }

    /**
     * 左右两端都清空
     */
    @Override
    public void clear() {
        clear(Direction.RIGHT);
        clear(Direction.RIGHT);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj==null){
            return false;
        }
        if (obj == this){
            return true;
        }
        if (obj instanceof ArrayStackDoubleEnd){
            ArrayStackDoubleEnd<E> stack = (ArrayStackDoubleEnd) obj;   //将ob转换为ArrayStackDoubleEnd
            if (getSize()==stack.getSize()){    //比较长度后比较内容
                ArrayList<E> list1 = new ArrayList<>(getSize());    //比较内容直接用ArrayList,将栈里的内容给ArrayList
                ArrayList<E> list2 = new ArrayList<>(getSize());
                //拼接当前栈的左部分
                for (int i = 0; i <= leftTop; i++) {
                    list1.addLast(data[i]);
                }
                //拼接当前栈的右部分
                for(int i = rightTop; i < data.length; i++){
                    list1.addLast(data[i]);
                }
                //拼接传入栈的左部分
                for (int i = 0; i <= stack.leftTop; i++) {
                    list2.addLast(stack.data[i]);
                }
                //拼接传入栈的右部分
                for(int i = stack.rightTop; i < stack.data.length; i++){
                    list2.addLast(stack.data[i]);
                }
                return list1.equals(list2); //用ArrayList的equals()直接比较内容
            }
        }
        return false;
    }

    @Override
    public String toString() {  
        StringBuilder sb = new StringBuilder();
        sb.append("ArrayStackDoubleEnd: size="+getSize()+",capacity="+data.length+"\n");
        if (isEmpty()){
            sb.append("[]");
        }else {
            sb.append('[');
            int count = 0;
            //拼接当前栈的左部分
            for (int i = 0; i <= leftTop; i++) {
                sb.append(data[i]);
                count++;
                if (count==getSize()){  //左端点添加完后不确定右端点有无元素,所以定义count,左端点每拼接一位count+1,如果count=栈内所有元素的数量,就意味着元素拼接完成
                    sb.append(']');
            }else {
                    sb.append(',');
                }
        }
            //拼接当前栈的右部分
            for(int i = rightTop; i < data.length; i++){
                sb.append(data[i]);
                count++;
                if (count==getSize()){
                    sb.append(']');
                }else {
                    sb.append(',');
                }
            }
        }
        return sb.toString();
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值