顺序栈(ArrayStack)
顺序栈是限定仅在表尾进行插入和删除操作的线性表
顺序栈要进行的某些功能,其顺序表ArrayList中也有同样的操作,基于面向对象,可用对象直接调用。
- 写一个接口Stack,在里面定义顺序栈即将要实现的某些功能。同样,跟List一样需要继承Iterable类,在测试类中遍历数组时,可使用foreach循环,支持不使用下标来遍历数组。
package DS01.动态数组;
public interface Stack<E> extends Iterable<E> {
//获取当前栈的有效元素个数
int getSize();
//栈是否为空
boolean isEmpty();
//弹栈
E pop();
//进栈
void push(E e);
//返回当前栈顶的元素
E peek();
//清空当前栈
void clear();
}
- 顺序栈ArrayStack继承Stack接口,实现多种方法,在实现某些方法时,发现于顺序表ArrayList的某些操作一样,基于面向对象思想,可直接通过对象直接引用。
package DS01.动态数组;
/*
* 顺序栈是基于顺序表而实现的
* 可以在ArrayStack内部直接创建一个ArrayList的对象
* 就可以通过对象直接调用ArrayList的方法了
* */
import java.util.Iterator;
public class ArrayStack<E> implements Stack<E> {
//定义一个ArrayList类型的变量
private ArrayList<E> list;
//创建一个默认长度的栈
public ArrayStack(){//在创建ArrayStack的对象时其实就是创建了一个ArrayList的对象
list=new ArrayList<>();
}
//创建一个一个指定长度capacity长度的栈
public ArrayStack(int capacity){
list=new ArrayList<>(capacity);
}
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
//弹栈操作其实就是顺序表的删除表尾元素
@Override
public E pop() {
return list.removeLast();
}
//压栈操作就是顺序表中的在表尾添加元素
@Override
public void push(E e) {
list.addLast(e);
}
//返回当前栈顶元素其实就是在顺序表中返回最后一个位置的元素
@Override
public E peek() {
return list.get(getSize()-1);
}
@Override
public void clear() {
list.clear();
}
//接口Stack继承Iterable类
//就继承了iterator()这个函数
//然后实现此接口Stack的类ArrayStack就必须重写此函数
@Override
public Iterator<E> iterator() {
return list.iterator();
}
public String toString(){
StringBuilder sb=new StringBuilder();//运用这个是因为在打印字符串时每次都要重新打印一个字符串
sb.append(String.format("ArrayList: %d/%d\n",getSize(),list.getCapacity()));
sb.append('[');
if(isEmpty()){
sb.append(']');
}else {
for(int i=0;i<getSize();i++){
sb.append(list.get(i));
if(i==getSize()-1){
sb.append(']');
}else {
sb.append(',');
}
}
}
return sb.toString();
}
}
- 编写测试类,进行调试
package DS01.动态数组;
import java.util.Iterator;
public class TestArrayStack {
public static void main(String[] args) {
ArrayStack<Integer> stack=new ArrayStack<>();
System.out.println(stack);
for(int i=1;i<=5;i++) {
stack.push(i);
}
//打印对象就是打印该对象的toString();
System.out.println(stack);
}
}
4.运行结果
注意:顺序栈的一系列操作都是基于顺序表来实现的,所以在进行ArrayStack一系列操作时,ArrayList的准备工作一定要做好。
双端栈(ArrayStackDoubleEnd)
所谓双端链栈,其实就是将一个线性表的两端当作栈底分别进行入栈和出栈操作。
- 双端栈有些方法也是实现接口Stack的,然后也是实现自己的,跟线性表就没有关系了。
package DS01.动态数组;
import java.util.Iterator;
/*
* 双端栈有些方法也是实现栈接口的,然后实现自己的方法,跟顺序表就没有关系了
*
* */
public class ArrayStackDoubleEnd<E> implements Stack<E> {
private static final int DEFAULT_SIZE=10;
private E[] data;
//左端栈顶
private int leftTop;
//右端栈顶
private int rightTop;
public ArrayStackDoubleEnd(){
this(DEFAULT_SIZE);
}
public ArrayStackDoubleEnd(int capacity){
if(capacity<=0){
throw new IllegalArgumentException("容量不能小于0");
}
//创建一个新数组
data=(E[])(new Object[capacity]);
//左栈顶初始化下标为-1
leftTop=-1;
//右栈顶初始化下标为数组长度
rightTop=data.length;
}
//获取左栈中有效元素个数
public int getLeftSize(){
return rightTop+1;
}
//获取右栈中有效元素个数
public int getRightSize(){
return data.length-rightTop;
}
//获取栈中所有元素的个数
@Override
public int getSize() {
return getLeftSize()+getRightSize();
}
//判断左栈是否为空
public boolean LeftisEmpty(){
return leftTop==-1;
}
//判断右栈是否为空
public boolean RightisEmpty(){
return rightTop==data.length;
}
//判断栈是否为空
@Override
public boolean isEmpty() {
return LeftisEmpty()&&RightisEmpty();
}
public E Leftpop(){
if(LeftisEmpty()){
throw new IllegalArgumentException("左栈为空");
}
E ret=data[leftTop--];
//如果当前容量小于总容量的1/4切当前容量除2之后必须大于默认容量时缩容
if(getSize()<=data.length/4&&data.length/2>10){
resize(data.length/2);
}
return ret;
}
public E Rightpop(){
if(RightisEmpty()){
throw new IllegalArgumentException("右栈为空");
}
E ret=data[rightTop++];
if(getSize()<=data.length/4&&data.length/2>=10){
resize(data.length/2);
}
return ret;
}
@Override
public E pop() {
if(getLeftSize()>=getRightSize()){
return Leftpop();
}else{
return Rightpop();
}
}
private void resize(int newLen){
E[] newData=(E[])(new Object[newLen]);
//扩容和缩容时左边的复制
for(int i=0;i<=leftTop;i++){
newData[i]=data[i];
}
if(newData.length>data.length){//扩容时右边
for(int i=data.length-1;i>=rightTop;i--){
newData[i+data.length]=data[i];
}
rightTop=rightTop+data.length;//更新右栈顶
}else{//缩容时右边
for(int i=data.length-1;i>rightTop;i--){
newData[i-newData.length]=data[i];
}
rightTop=rightTop-newData.length;
}
data=newData;
}
public void Leftpush(E e){
//判满
if(leftTop+1==rightTop){
resize(data.length*2);
}
leftTop++;
data[leftTop]=e;
}
public void Rightpush(E e){
if(rightTop-1==leftTop){
resize(data.length*2);
}
rightTop--;
data[rightTop]=e;
}
@Override
public void push(E e) {
if(getLeftSize()<=getRightSize()){
Leftpush(e);
}else{
Rightpush(e);
}
}
public E Leftpeek(){
if(LeftisEmpty()){
throw new IllegalArgumentException("左栈为空");
}
return data[leftTop];
}
public E Rightpeek(){
if(RightisEmpty()){
throw new IllegalArgumentException("右栈为空");
}
return data[rightTop];
}
@Override
//返回当前栈顶的元素
public E peek() {
if(getLeftSize()>=getRightSize()){
return Leftpeek();
}else{
return Rightpeek();
}
}
//清空当前的栈
@Override
public void clear() {
data=(E[])(new Object[DEFAULT_SIZE]);
leftTop=-1;
rightTop=data.length;
}
public String toString(){
StringBuilder sb=new StringBuilder();
sb.append(String.format("ArrayStackDoubleEnd: %d/%d\n",getSize(),data.length));
if(LeftisEmpty()){
sb.append("left:bottom[] top");
}else{
sb.append("left:bottom[");
for(int i=0;i<=leftTop;i++){
sb.append(data[i]);
if(i==leftTop){
sb.append("]top");
}else{
sb.append(",");
}
}
}
sb.append("\n");
if(RightisEmpty()){
sb.append("right:top [] bootom\n");
}else{
sb.append("right:top [");
for(int i=rightTop;i<data.length;i++){
sb.append(data[i]);
if(i==data.length-1){
sb.append("] bottom");
}else{
sb.append(',');
}
}
}
return sb.toString();
}
@Override
public Iterator<E> iterator() {
return null;
}
}
- 编写测试类
package DS01.动态数组;
public class TestArrayStackDoubleEnd {
public static void main(String[] args) {
ArrayStackDoubleEnd<Integer> stack=new ArrayStackDoubleEnd<>();
System.out.println(stack);
for(int i=1;i<=6;i++){
stack.Leftpush(i);
}
for(int i=1;i<=6;i++){
stack.Rightpush(i);
}
System.out.println(stack);
}
}
- 运行结果
注意哦,代码中有大部分代码解释,相信以大家的智慧肯定能读懂的。