目录
一、栈的基本知识
1.栈的概念
2.栈的功能
3.栈的实现
二、栈的代码实现
1.栈的基本属性与方法
2.栈的遍历
3.入栈实现
4.出栈实现
5.数据测试
6.完整的程序代码
总结
一、栈的基本知识
1.栈的概念
根据百度百科,我们知道“栈”是存储货物或供旅客住宿的地方,可引申为仓库、中转站,所以引入到计算机领域里,栈就是数据暂时存储的地方,当栈中没有数据元素时叫做空栈。
栈(stack),是一种运算受限的线性表,限定只在表尾进行插入和删除操作,插入删除这一端就叫栈顶,另一端就叫栈底,总结一下就是“栈底固定,栈顶浮动”。
而正因为栈运算受限,只能在栈顶进行插入删除操作,所以栈是一种先进后出(后进先出)的数据结构。
2.栈的功能
栈的主要功能有push(入栈)、pop(出栈)、peek(获取栈顶元素,但不删除)、empty(判断栈是否为空)等,这里我们重点讨论入栈和出栈。
入栈:即向一个栈插入新元素,把新元素放到原栈顶元素的上面,使之成为新的栈顶元素。
出栈:即从一个栈中删除元素,把原栈顶元素删除,使其相邻的元素成为新的栈顶元素。
为了更好地理解栈的基本概念与入栈出栈,下面用图进行说明:
3.栈的实现
因为栈存储的是相同类型的数据,所以栈的实现有两种,一种是顺序栈,底层为数组;另一种是链式栈,利用链表实现。今天我们主要通过数组(顺序表)来完成栈的实现。
二、栈的代码实现
1.栈的基本属性与方法
有了前几天顺序表和链表的相关基础,创建栈的基本属性和方法就可以很快完成,代码如下:
public class CharStack {
/**
* The depth.
*/
public static final int MAX_DEPTH = 10;
/**
* The actual depth.
*/
int depth;
/**
* The data.
*/
char[] data;
/**
*********************
* Construct an empty char stack.
*********************
*/
public CharStack() {
depth = 0;
data = new char[MAX_DEPTH];
} // of the first constructor
这里我们同样利用final关键字来定义栈的最大长度MAX_DEPTH = 10,然后定义成员变量(depth、data),再利用new关键字为data分配内存空间。
2.栈的遍历
栈的遍历同样可以通过重写toString()方法来实现,大体结构上与顺序表、链表差不多,只是循环的时候,要注意由于栈是在栈顶这一端进行插入删除操作,所以为了便于后续入栈和出栈,我们将栈顶端对应数组的最右端(下标最大处),代码如下:
/**
*********************
* Overrides the method claimed in Object, the superclass of any class.
*********************
*/
public String toString() {
String resultString = "";
for (int i = 0; i < depth; i++) {
resultString += data[i];
} // of for i
return resultString;
} // of toString
3.入栈实现
和顺序表、链表一样,在入栈之前,我们显然需要先考虑此栈是否已满,对于这个问题,一样的套路,直接利用if语句进行判断:
- 如果此时栈的长度depth = MAX_DEPTH,那么就输出Stack full.,提示此栈已满。
- 如果此时栈的长度未达到MAX_DEPTH,说明栈还未满,直接将插入元素令为新的栈顶元素即可完成入栈。我们知道在插入前,原栈顶元素的下标为depth - 1(因为栈底从0开始索引),那么显然新栈顶元素的下标应在此基础上加1,所以data[depth] = paraChar;,最后不要忘了把栈的实际长度depth增加1(因为插入了一个新元素)。
/**
*********************
* Push an element.
*
* @param paraChar The given char.
* @return Success or not.
*********************
*/
public boolean push(char paraChar) {
if (depth == MAX_DEPTH) {
System.out.println("Stack full.");
return false;
} // of if
data[depth] = paraChar;
depth++;
return true;
} // of push
4.出栈实现
顺序表、链表执行删除操作之前需要先判断是否为空表,类似的,在执行出栈操作之前,也需要先判断栈是否为空,这里仍然用的是if语句进行判断,并将'\0'作为返回值。
出栈其实就是删除栈顶元素,也就是删除栈最上面的元素(数组最右边的元素),所以直接将栈的长度depth减少1即可,不过,在此之前还是需要将要删除的栈顶元素赋给resultChar并返回。
/**
*********************
* Pop an element.
*
* @return The popped char.
*********************
*/
public char pop() {
if(depth == 0) {
System.out.println("Nothing to pop.");
return '\0';
} // of if
char resultChar = data[depth - 1];
depth--;
return resultChar;
} // of pop
5.数据测试
接下来我们照例进行数据测试:
/**
*********************
*The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
CharStack tempStack = new CharStack();
for(char ch = 'a'; ch < 'm'; ch++) {
tempStack.push(ch);
System.out.println("The current stack is: " + tempStack);
} // of for ch
char tempChar;
for(int i = 0; i < 12; i++) {
tempChar = tempStack.pop();
System.out.println("Popped: " + tempChar);
System.out.println("The current stack is: " + tempStack);
} // of for i
} // of main
6.完整的程序代码
package datastructure;
/**
*Char stack. I do not use Stack because it is already defined in Java.
*
*@auther Xin Lin 3101540094@qq.com.
*/
public class CharStack {
/**
* The depth.
*/
public static final int MAX_DEPTH = 10;
/**
* The actual depth.
*/
int depth;
/**
* The data.
*/
char[] data;
/**
*********************
* Construct an empty char stack.
*********************
*/
public CharStack() {
depth = 0;
data = new char[MAX_DEPTH];
} // of the first constructor
/**
*********************
* Overrides the method claimed in Object, the superclass of any class.
*********************
*/
public String toString() {
String resultString = "";
for (int i = 0; i < depth; i++) {
resultString += data[i];
} // of for i
return resultString;
} // of toString
/**
*********************
* Push an element.
*
* @param paraChar The given char.
* @return Success or not.
*********************
*/
public boolean push(char paraChar) {
if (depth == MAX_DEPTH) {
System.out.println("Stack full.");
return false;
} // of if
data[depth] = paraChar;
depth++;
return true;
} // of push
/**
*********************
* Pop an element.
*
* @return The popped char.
*********************
*/
public char pop() {
if(depth == 0) {
System.out.println("Nothing to pop.");
return '\0';
} // of if
char resultChar = data[depth - 1];
depth--;
return resultChar;
} // of pop
/**
*********************
*The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
CharStack tempStack = new CharStack();
for(char ch = 'a'; ch < 'm'; ch++) {
tempStack.push(ch);
System.out.println("The current stack is: " + tempStack);
} // of for ch
char tempChar;
for(int i = 0; i < 12; i++) {
tempChar = tempStack.pop();
System.out.println("Popped: " + tempChar);
System.out.println("The current stack is: " + tempStack);
} // of for i
} // of main
} // of class CharStack
运行结果:
入栈时:测试的数据是从'a'到'm'之前(即从'a'到'l'这12个数据元素) ,但是在程序最开始我们就已经规定了栈的最大长度为10,所以不用说,当执行入栈操作时,最后'k'、'l'这两个数据元素必然没办法完成入栈。
出栈时:我们设置了for循环的次数为12次,即需要执行12次出栈操作,但是此时栈中只有'a'~'j'这10个数据元素,所以最后两次出栈时Nothing to pop.
总结
总的来说,今天的代码还是比较容易的,一方面是因为有了前两天顺序表和链表的基础,另一方面是因为栈的操作本身就不复杂,而且入栈出栈的时间复杂度均为O(1),所以利用栈来存取数据是比较迅速的。不过,栈虽然操作不复杂,但是它在计算机领域却有着举足轻重的作用,栈不仅是一种高效的内存结构,还贡献于计算机底层技术(例如函数调用、中断处理、程序调试等)。