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