栈是数据结构中最重要的结构之一,也是算法中最爱考的,我们平常写代码的时候习惯了直接用自带的类Stack,直接用里面自带的方法:push、pop、peek等。那你是否自己动手写过一个带基本功能的栈?跟我一起写一下吧!!!
首先先来了解一下栈栈结构其实就是一种线性结构。如果从数据的存储结构来进一步划分,栈结构包括两类。
- 顺序栈结构:即使用一组地址连续的内存单元 依次保存栈中的数据。在程序中,可以定义一个指定大小的结构数组来作为栈,序号为0的元素就是栈底,在定义一个变量top保存栈底的序号即可
- 链式栈结构:即使用链表形式保存栈中各元素的值。链表首部(head引用所指向元素)为栈顶,链表尾部(指向地址为Null)为栈底。
其中datan是栈顶元素,data1是栈底元素
也就是说保存和取出数据都只能从栈结构的一端进行。从数据的运算角度来分析,栈结构是按照“后进先出(Last In First Out,LIFO)”的原则处理结点的数据。
生活中也有很多栈结构的生动的例子。例如,当仓库中堆放货物时,先来的货物放在里面,后来的货物放在外面;当要取出货物时,总是先取外面的,最后才取里面的货物,也就是后放入的货物先取出。
首先先准备一下数据
创建一个栈结构数据元素的类DATA类里面就是我们要存放的什么样的数据
public class DATA {
String name;
int age;
}
我就少写两个,我要存的数据是姓名和年龄。
接下来在创建一个栈结构的类StackType。
public class StackType {
static final int maxsize=50;//定义一下栈中所存的最大容量
DATA data[]=new DATA[maxsize];//存放数据元素
int top;//指向栈顶元素,当top=0时栈为空
}
第一步初始化栈
在使用顺序栈之前,首先要创建一个空的顺序栈,即初始化顺序栈。
操作步骤为:
- 按符号常量SIZE指定的大小申请一片内存空间,用来保存栈中的数据。
- 设置栈顶引用的值为0,表示一个空栈
StackType STInit()//申请栈内存
{
StackType p;
if((p=new StackType())!=null)//如果申请内存成功
{
p.top=0;//设置指向栈顶的指针为0
return p;
}
return null;
}
判断空栈
如果是空栈,则表示栈中没有数据,此时可以进行入栈操作,但不可以进行出栈操作。
boolean StackEmpty(StackType s)//判断栈是否为空,注意方法的返回类型
{
boolean f;
f=(s.top==0);//如果指向栈顶的top等于0那就代表是空栈
return f;
}
判断满栈
如果栈为满,则表示该栈结构中不能再入栈,只能出栈。
boolean StackFull(StackType s)//判断栈是否为满
{
boolean f;
f=(s.top==maxsize);//如果top等于我们上面定义的数据存放最大值,那就代表栈满
return f;
}
清空栈
清空栈中所有的数据
void STClear(StackType s)//清空栈
{
s.top=0;
}
在上述代码中,输入参数s是一个指向操作的栈的引用。在程序中,简单的将栈顶引用top设置为0,表示执行清空栈操作。
入栈操作
入栈(Push)是栈结构的基本操作,主要操作是将数据元素保存到栈结构。入栈操作的具体步骤如下:
(1)首先判断栈顶top,如果top>=maxsize,则表示栈满,不能在入栈;否则做以下操作。
(2)设置top=top+1;先将指向栈顶的top加一。
(3)将入栈元素保存到top指向的位置。
代码如下:
//注意返回类型是int如果入栈成功则返回1,反之则返回0
int PushSt(StackType s,DATA data)//入栈操作 data是要入栈的数据
{
if(s.top+1>maxsize)
{
System.out.println("栈以满");
return 0;
}
s.data[++s.top]=data;//为什么不是s.top++下面是解释
//细节:先++再在加过后的top所指的地方按放数据。
//假设top指的是第三个3,第三个3已经有数据了,就先将top上移然后在存放数据.(这里是解释++s.top)
return 1;
}
出栈操作
出栈与入栈相反,其操作是从栈顶弹出一个数据元素,出栈操作的步骤如下:
(1)判断栈顶top,如果top==0,则表示空栈,进行出错处理;否则执行以下操作。
(2)将栈顶引用top所指定的位置元素返回。
(3)设置top=top-1,也就是使栈顶引用减1,指向栈的下一个元素,原来栈顶元素被弹出。
代码如下:
//注意返回类型是DATA出栈的是DATA类型的数据name,age所以返回类型是DATA
DATA PopSt(StackType s)//出栈操作
{
if(s.top==0)
{
System.out.println("栈空");
System.exit(0);//0表示正常退出,其他数字表示非正常退出
}
return (s.data[s.top--]);
//细节:top本来指向第3个,先用3这个数字,先将3这块的数据输出来后再将top--
}
入栈和出栈有两个细节++s.top和s.top–,大家这么聪明肯定都只带++a和a++的区别
读取结点数据
由于栈结构只能在一端进行出入操作,所以这里的读取操作其实就是读取栈顶的数据。
需要注意的是读结点数据和出栈不同。读结点数据的操作仅仅显示栈顶结点数据的内容,读完该数据还存在栈中,依然是栈顶数据。而出栈操作则将栈顶数据弹出,该数据就不再存在了。
代码如下:
DATA PeekST(StackType s)//读栈顶元素
{
if(s.top==0)
{
System.out.println("栈空");
System.exit(0);
}
return (s.data[s.top]);
//不做++或--操作因为只读不取,由于栈特殊操作所以只能读栈顶元素
}
以上就是一个栈基本操作的各方法,现在我们把它放在一个类里面
代码如下:
public class StackType {
static final int maxsize=50;
DATA data[]=new DATA[maxsize];
int top;
StackType STInit()//申请栈内存
{
StackType p;
if((p=new StackType())!=null)//如果申请内存成功
{
p.top=0;
return p;
}
return null;
}
boolean StackEmpty(StackType s)//判断栈是否为空
{
boolean f;
f=(s.top==0);
return f;
}
boolean StackFull(StackType s)//判断栈是否为满
{
boolean f;
f=(s.top==maxsize);
return f;
}
void STClear(StackType s)//清空栈
{
s.top=0;
}
int PushSt(StackType s,DATA data)//入栈操作
{
if(s.top+1>maxsize)
{
System.out.println("栈以满");
return 0;
}
s.data[++s.top]=data;
//细节:先++再在加过后的top所指的地方按放数据。
//本来top指的是3,3已经有数据了,就先将top上移然后在存放数据
return 1;
}
DATA PopSt(StackType s)//出栈操作
{
if(s.top==0)
{
System.out.println("栈空");
System.exit(0);//0表示正常退出,其他数字表示非正常退出
}
return (s.data[s.top--]);
//细节:top本来指向3,先用3这个数字,先将3这块的数据输出来后再将top--
}
DATA PeekST(StackType s)//读栈顶元素
{
if(s.top==0)
{
System.out.println("栈空");
System.exit(0);
}
return (s.data[s.top]);
//不做++或--操作因为只读不取,由于栈特殊操作所以只能读栈顶元素
}
}
接下来我们写一个Test类来测试一下我们写的栈
import java.util.Scanner;
public class Test {
public static void main(String[] args)
{
StackType st=new StackType();
Scanner in=new Scanner(System.in);
DATA data1=new DATA();
StackType stack=st.STInit();//初始化栈
while(true)
{
System.out.println();
System.out.println("输入1入栈");
System.out.println("输入2出栈");
System.out.println("输入3读取栈顶操作");
System.out.println("输入4判断栈是否为空");
System.out.println("输入5判断栈是否满");
System.out.println("输入0退出");
int n=in.nextInt();
if(n==1)
{
DATA data=new DATA();
System.out.println("请输入姓名,年龄进行入栈操作");
data.name = in.next();
data.age=in.nextInt();
st.PushSt(stack,data);
}
if(n==2)
{
System.out.println("按任意非0键进行出栈操作");
String temp=in.next();
while(!temp.equals("0"))
{
data1=st.PopSt(stack);
System.out.println("姓名 年龄");
System.out.printf("%s %d\n",data1.name,data1.age);
temp=in.next();
}
}
if(n==3)//读取栈顶
{
data1=st.PeekST(stack);
System.out.println("姓名 年龄");
System.out.printf("%s %d\n",data1.name,data1.age);
}
if(n==4)//判断栈是否为空
{
boolean f=st.StackEmpty(stack);
if(f)
{
System.out.println("栈为空");
}
else
{
System.out.println("栈非空");
}
}
if(n==5)//判断栈是否满
{
boolean f=st.StackFull(stack);
if(f)
{
System.out.println("栈以满");
}
else
{
System.out.println("栈非满");
}
}
if(n==0)
{
System.out.println("谢谢使用");
break;
}
}
}
}
总结:以上就是一个栈的基本操作,我们用数组来作为承装数据的容器,我们一共建了三个类:
DATA 放数据的类
StackType 栈结构的类
Test 测试的类
欢迎各位来评论!!!