思路分析:
1.首先需要定义一个结点类,用来作为栈存储数据的空间。
由结点id(用于标识结点,根据容量从0递增),下个结点,数据三个部分构成。
2.初始化栈时,定义一个size变量,来指定栈的容量。定义一个辅助的结点变量flag来指向链表最后一个结点的下一个结点,即初始化栈顶,当然flag也始终指向栈顶。(初始化的flag并不算在栈的空间之中,只是作为一个约定而已)
3.由于选择的是尾插法进行入栈操作,每次入栈和出栈时需要遍历整个栈空间即链表去按照id寻找对应的结点来移动栈顶,所以定义一个辅助结点变量top始终指向链表的头结点。又因为入栈时,新栈顶其实是当前结点的前一个结点,即id是递减的,那么定义一个辅助变量resize来标识栈中实际存在的数据的个数(从0开始),初始化为size的值。
4.入栈,需要判断栈是否已满。判断条件为flag.no=0;
具体操作:
- 首先判断栈是否满,如果满了,则数据不能入栈。反之,继续第2步。
- 遍历链表,寻找id为resize-1的结点即为新的栈顶,然后赋值。
- resize–;
- 将flag指向新的栈顶。
5.出栈,需要判断栈是否已空。判断条件flag.no=size;
具体操作:
- 首先判断栈是否空,如果为空,则不能出栈。反之,继续第2步。
- 取出栈顶的数据
- 移动栈顶,新栈顶即为当前栈顶的下一个结点。
- resize++;
代码实现
class MyListStack {
//创建结点
private class Node {
int data;
Node nextNode;
int no;
public Node(int no) {
this.no = no;
}
}
//定义一个辅助结点,始终指向链表的头结点
private Node top;
//定义栈的容量
private int size;
private int no = 0;
//定义一个辅助变量用于寻找入栈时的新栈顶
private int resize;
//定义一个辅助结点,指向当前的栈顶
private Node flag;
public MyListStack(int size) {
this.size = size;
this.resize = size;
if(this.size > 0){
Node item = new Node(this.no);
this.top = item;
for(int i = 0; i < this.size-1; i++){
this.no++;
item.nextNode = new Node(this.no);
item = item.nextNode;
}
this.no++;
//flag指向最后一个结点的下一个节点,即栈顶
item.nextNode = new Node(this.no);
flag = item.nextNode;
}
}
//判断栈满
public boolean isFull(){
return flag.no == 0;
}
//判断是否栈空
public boolean isEmpty(){
return flag.no == size;
}
//入栈
public void push(int num){
if(isFull()){
System.out.println("栈满,无法添加数据");
return;
}
Node node = top;
while(true){
if(node.no == resize-1){
break;
}
node = node.nextNode;
}
resize--;
//赋值
node.data = num;
//移动栈顶
flag = node;
}
//出栈
public int pop(){
if(isEmpty()){
throw new RuntimeException("栈空,无法出栈");
}
//取出当前栈顶的数据
int value = flag.data;
Node node = top;
flag = flag.nextNode;
resize++;
return value;
}
//显示栈中数据
public void showStack(){
int sum = 0;
Node item = top;
while (true){
if(item.no == size){
break;
}
if(item.data!=0) {
System.out.println("stack[" + sum++ + "] = " + item.data);
}
item = item.nextNode;
}
}
}