Java中使用顺序表ArrayList和链表LinkedList方式实现栈Stack
首先我们应该知道栈:
遵循后进先出/先进后出的原则。最先放入栈中的元素 — 栈底元素,最后放入栈中的元素 — 栈顶元素。将元素放入栈中 ---- 入栈/压栈,将元素从栈中取出 ---- 出栈/弹栈。
import java.util.Stack;
public class StackDemo {
public static void main(String[] args) {
Stack<String> s = new Stack<String>();
// 添加元素
s.push("a");
s.push("b");
s.push("c");
s.push("d");
s.push("e");
s.push("f");
// 查看栈顶元素
// 如果栈为空,则抛出空栈异常,因此最好判断栈是否为空
if(s.isEmpty())
System.out.println(s.peek());
// 移除栈顶元素
System.out.println(s.pop());
// 查找元素
// 从栈顶网栈底查找,且以1为基数
System.out.println(s.search("b"));
System.out.println(s);
}
}
上面通过java中Stack类创建的栈实例,实现对栈元素的插入、查找、移除和添加等基本操作。下面将介绍使用顺序表ArrayList和链表LinkedList方式实现栈Stack。
#1,通过顺序表方式实现
import java.util.Arrays;
import java.util.EmptyStackException;
public class StackDemo2 {
public static void main(String[] args) {
// ....这里操作省略,读者可以粘贴上面的代码测试
}
}
// 这里自定义一个ArrStack类,用于实现顺序表结构原理
class ArrStack{
// 定义两个属性,字符数组生成栈的空间并存储数据和size用于记录栈的长度;
private String[] data = new String[10];
private int size = 0;
// 实现判断栈是否为空
public boolean empty() {
return size == 0;
}
// 获取栈中最后插入的元素,即数组中最后一个元素
public String peek() {
// 判断栈中是否有元素
if(size == 0)
throw new EmptyStackException(); // 如果栈中没有元素,则抛出异常
return data[size - 1]; // 栈中有元素,则返回数组中最后一个元素
}
public String pop() {
String str = this.peek(); // 先获取栈(数组)中最后一个元素
size--;
return str;
}
public void push(String str) {
// 判断是否需要扩容
// size从0开始
if(size >= data.length)
data = Arrays.copyOf(data, data.length * 2);
// 栈的默认初始长度为10,扩容为增加到原长度的两倍
data[size++] = str;
/*
这里可以分解为:
data[size] = str; // 向数组最后插入元素,相当于在栈顶插入元素
size++; // 栈长度加一
*/
}
public int search(String str) {
int k = -1; /
for(int i = size - 1; i >= 0; i--) {
if(data[i] == str || str != null && str.equals(data[i]))
k = (size - i); // 由于查找的元素,栈返回的是从栈顶到栈底的第一个元素
// 所以不应该是i+1,而是size - (i+1)+1;这里用k记录,为查找到则返回-1
}
return k;
}
}
如上所示,在代码中自定义了一个ArrStack用于实现顺序表栈。看起来还是比较简单吧,哈哈。下面则介绍通过链表方式来实现。
# 2, 链表结构方式的栈
import java.util.Arrays;
import java.util.EmptyStackException;
import java.util.LinkedList;
public class StackDemo3 {
public static void main(String[] args) {
// 这里操作代码省略,读者可以自行写入并测试
}
}
class LinkStack{
private int size = 0; // 记录链表长度
private Node first; // 定义链表中节点的头节点,这里指向的是栈的栈顶节点
// 定义一个内部类,表示节点
private class Node{
// 这里只需要实现简单的逻辑和功能,因此只定义两个属性
public Node next; // next属性表示指向链表栈中前一个节点,这里当然是前面插入的节点啦
// 另外的,这里的逻辑和和链表有一点,就是next在链表中指向的是下一个节点,
// 而这里指向的是前面插入的节点, 这里的逻辑读者也要搞清楚
public String data; // 这个属性则记录节点上的数据啦
public Node(Node next, String data) {
super();
this.next = next;
this.data = data;
} // 定义的节点构造函数
}
public boolean empty() {
return size == 0;
}
public String peek() {
// 判断栈中是否有元素
if(size == 0)
throw new EmptyStackException();
return this.first.data; // 前面所说,first指向的是栈链表的栈顶节点,
// 因此这里直接返回使用this.first.data表示栈顶节点的元素
}
public String pop() {
String str = this.peek(); // 先获得栈顶元素
this.first = this.first.next; // 将栈顶节点指针指向原栈顶节点的前一个节点
size--; // 栈长度减一
return str;
}
public void push(String str) {
Node node = new Node(null, str);
// 这里向栈顶插入新节点(元素),首先创建新节点
if(size == 0) {
this.first = node; // 如果栈为空,
}else { // 栈不为空
node.next = this.first; // 将新节点的前一个节点定义为栈链表中的栈顶节点
this.first = node; // 栈顶节点指针指向新节点,将新节点定义为新的栈顶节点
}
size++;
}
public int search(String str) {
int k = -1;
Node node = this.first;
for(int i =0; i <= size - 1; i++) {
if(node .data == str || str != null && str.equals(node .data)) // 判断元素是否相等
k = i + 1; // 因为这里是直接从栈顶开始向栈底开始查找,因此,i + 1就是该节点的为值
node =node.next; // 指向前面的节点
}
return k;
}
}
上面就简单实现了链表形式的栈结构。
这里附图一张链表的结构示意图。如果读者能看懂,那么恭喜你对链表的结构原理基本掌握啦。不懂请读者继续多学习啦。 欢迎评论交流。