泛型数组实现
利用泛型数组作为底层数据结构实现栈
定义属性和方法
public class MyStack<T> {
//利用泛型数组作为底层数据结构实现栈
private T [] elements;
private int size = 0;
//定义默认容量
private static final int DEFAULT_CAPACITY = 10;
//无参构造函数
public MyStack() {
elements = (T[]) new Object[DEFAULT_CAPACITY];
}
//添加元素到栈顶
public void push(T t){
ensureCapacity();
elements[size++] = t;
System.out.println("push succeed: " + t);
}
//扩容方法
private void ensureCapacity() {
if (size == elements.length){
T[] oldElements = elements;
elements = (T[]) new Object[size + (size >>> 1)];
System.out.println(elements.length);
System.arraycopy(oldElements,0,elements,0,size);
System.out.println("stack is full, capacity is expanded to " + elements.length);
}
}
//弹出栈顶元素并返回
public T pop(){
if (size == 0){
throw new RuntimeException("stack is empty!!!!!!");
}
T element = elements[--size];
elements[size] = null;
System.out.println("pop succeed: " + element);
return element;
}
//返回栈顶元素(不弹出)
public T peek(){
if (size == 0){
throw new RuntimeException("stack is empty!!!!!!");
}
return elements[size - 1];
}
//判断栈是否为空
public boolean isEmpty(){
return size == 0;
}
public int size(){
return size;
}
public String toString(){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
for (int i = 0; i < size; i++) {
stringBuilder.append(elements[i]);
if (i != size - 1){
stringBuilder.append(",");
}
}
stringBuilder.append("]");
return stringBuilder.toString();
}
}
扩容方法
此处模拟java底层数组扩容机制
1、定义默认容量DEFAULT_CAPACITY = 10,并在构造函数初始化时才开辟空间赋值,节约内存空间。
2、在每次压栈压入元素时调用函数ensureCapacity()判断栈是否已满,已满则扩容致原来长度的1.5倍(java数组底层认为1.5倍对空间利用最合适)
private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacity() {
if (size == elements.length){
T[] oldElements = elements;
elements = (T[]) new Object[size + (size >>> 1)];
System.out.println(elements.length);
System.arraycopy(oldElements,0,elements,0,size);
System.out.println("stack is full, capacity is expanded to " + elements.length);
}
}
public void push(T t){
ensureCapacity();
elements[size++] = t;
System.out.println("push succeed: " + t);
}
测试
对所定义的栈进行方法的测试,包括异常的抛出(利用junit5的断言机制)
public class WorkOneTest {
@Test
public void MyStackTest(){
MyStack<Character> ms = new MyStack<>();
Assertions.assertThrows(Exception.class, () -> ms.pop());
Assertions.assertThrows(Exception.class, () -> ms.peek());
ms.push('a');
ms.push('b');
ms.push('@');
ms.push('d');
ms.push('e');
System.out.println(ms.size());
System.out.println(ms.isEmpty());
System.out.println(ms.toString());
System.out.println(ms.peek());
ms.push('a');
ms.push('b');
ms.push('@');
ms.push('d');
ms.push('e');
ms.push('e');
System.out.println(ms.toString());
System.out.println(ms.peek());
System.out.println(ms.pop());
System.out.println(ms.toString());
}
}
测试通过
作业
给定一个字符串str。设计一个算法采用顺序栈判断str是否为形如“序列1@序列2”的合法字符串,其中序列2是序列1的逆序,str中恰好只有一个@字符。
思路
首先把字符串转成数组,然后定义两个个标签flag和flag2,遍历字符串入栈,读到@则使标签flag取反,终止本次循环,停止入栈,continue直接开始下一次循环。
从这开始,遍历的i继续,也就是从@的后面开始,由于flag已被取反,后面的入栈if将跳过,转而执行下面的判断if。即入栈元素为@前面的一半。
利用栈的”先进后出“结构,后面的判断为@后一半遍历的同时,把前一半已经入栈的元素先peek对比是否相同,相同则pop弹出释放,不相同则把标签flag2置为false,并直接break退出循环。
最后打印结果。
@Test
@DisplayName("作业第一题测试")
@ParameterizedTest
@ValueSource(strings = {"asd@dsa","!#$%@%$#!","你好@好你","@"})
public void WorkOneTest(String str) {
MyStack<Character> ms = new MyStack<>();
char[] ch = str.toCharArray();
boolean flag = false;
boolean flag2 = true;
for(int i = 0; i < ch.length; i++){
if(ch[i] =='@'){
flag = true;
continue;
}
if(!flag) {
ms.push(ch[i]);
}
else{
if(ch[i] == ms.peek()){
ms.pop();
}
else{
flag2 = false;
break;
}
}
}
System.out.println("该字符串["+str+"]是否合法: " + flag2 );
}
}
使用了JUnit5的参数化测试,方便传参测试
@ParameterizedTest
@ValueSource(strings = {"asd@dsa","!#$%@%$#!","你好@好你","@"})