首先我们应该知道栈:
遵循后进先出/先进后出的原则。最先放入栈中的元素 — 栈底元素,最后放入栈中的元素 — 栈顶元素。将元素放入栈中 ---- 入栈/压栈,将元素从栈中取出 ---- 出栈/弹栈。
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; } }
上面就简单实现了链表形式的栈结构。
这里附图一张链表的结构示意图。如果读者能看懂,那么恭喜你对链表的结构原理基本掌握啦。不懂请读者继续多学习啦。 欢迎评论交流。