栈(Stack)是一种基本的数据结构,它遵循“后进先出”(Last In First Out,LIFO)的原则。在栈中,最后插入的元素首先被弹出。
栈的常见操作
- 压栈(Push):将一个元素添加到栈的顶部。
- 出栈(Pop):从栈的顶部移除一个元素,并返回该元素。
- 获取栈顶元素(Peek):返回栈顶部的元素,但不对栈进行修改。
- 判断栈是否为空(isEmpty):检查栈是否为空,若为空则返回 true,否则返回 false。
栈可以使用数组或链表来实现,也可以使用其他数据结构。不同的实现方式会导致不同的性能特征,如时间复杂度和空间复杂度。。在底层,栈通过数组或链表来存储元素。每当进行压栈操作时,元素被添加到数组或链表的顶部。而出栈操作则是从顶部移除元素。
使用数组模拟栈
使用数组实现栈时,可以使用一个指针(通常称为 top)指向栈顶。需要注意的是,数组的大小需要提前确定。
- 压栈操作:将元素添加到 top + 1 的位置,并将 top 增加 1。
- 出栈操作:返回 top 位置的元素,并将 top 减少 1。
- 获取栈顶元素:返回 top 位置的元素。
- 判断栈是否为空:如果 top 等于 -1,则栈为空。
public class SqStack<E> {
//默认的初始化数组大小 为10
private static final int DEFAULT_CAPACITY = 10;
private E[] stack;
private int size;
public SqStack() {
this.stack = (E[]) new Object[DEFAULT_CAPACITY];
this.size = 0;
}
public SqStack(int length) {
this.stack = (E[]) new Object[length];
this.size = 0;
}
public void display(){
if(isEmpty()){
System.out.println("栈空");
return;
}
for (E e : stack) {
System.out.println(e+" ");
}
System.out.println();
}
public boolean isEmpty() {
return size==0;
}
public boolean isFull(){
return size==stack.length;
}
public int getSize() {
return size;
}
public void reSize(){
E[] newArrSqList = (E[]) new Object[stack.length+(stack.length)>>1];
System.arraycopy(stack,0,newArrSqList,0,size);
stack = newArrSqList;
}
public void push(E e) {
if(this.isFull()){
this.reSize();
}
size++;
stack[size-1] = e;
}
public E pop() {
E popElement = stack[size-1];
stack[size-1] = null;
size--;
return popElement;
}
public E peek() {
E peekElement = stack[size-1];
return peekElement;
}
}
使用链表模拟栈
使用链表实现栈时,可以将链表的头部作为栈顶。链表节点除了包含存储元素的值,还需要包含指向下一个节点的指针。
- 压栈操作:将元素添加到链表的头部,更新栈顶指针。
- 出栈操作:删除链表的头部节点,并返回其值,更新栈顶指针。
- 获取栈顶元素:返回链表的头部节点的值。
- 判断栈是否为空:如果栈顶指针为空,则栈为空。
// 单链表节点类
public class Node<E> {
public E data;
public Node next;
public Node(E data) {
this.data = data;
this.next = null;
}
public Node() {
this.data = null;
this.next = null;
}
}
public class LinkListStack<E> implements Stack<E> {
public Node head;
private int size;
public LinkListStack() {
this.head = new Node<E>();
this.size = 0;
}
public LinkListStack(Node head) {
this.head = head;
this.size = 0;
}
public void push(E e) {
Node temp = head;
Node p = new Node(e);
if(isEmpty()){
head.next = p;
size++;
return;
}
while (temp.next!=null){
temp = temp.next;
}
temp.next = p;
size++;
}
public E pop() {
if(isEmpty()){
System.out.println("空栈");
return null;
}
Node temp = head;
while (temp.next.next!=null){
temp = temp.next;
}
Node p = temp.next;
temp.next = null;
size--;
return (E) p.data;
}
public E peek() {
if(isEmpty()){
System.out.println("空栈");
return null;
}
Node temp = head;
while (temp.next!=null){
temp = temp.next;
}
return (E)temp.data;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size==0;
}
public void display(){
if(isEmpty()){
System.out.println("空栈");
return;
}
Node temp = head.next;
System.out.print("{");
while (temp.next!=null){
System.out.print(temp.data+" ");
temp = temp.next;
}
System.out.println(temp.data+"}");
}
}
栈的测试代码
public class test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// SqStack<String> stack = new SqStack<String>(10);
LinkListStack<String> stack = new LinkListStack<String>();
boolean flag = true;
String c = null;
String e = null;
while (flag){
System.out.print("请输入要进行的操作:");
c = sc.next();
switch (c){
case "push":
System.out.println("请输入要插入的元素");
e = sc.next();
stack.push(e);
break;
case "pop":
e = stack.pop();
if(e!=null){
System.out.println("出栈元素为:"+e);
}
break;
case "peek":
e = stack.peek();
if(e!=null){
System.out.println("栈顶元素为:"+e);
}
break;
case "display":
stack.display();
break;
default:
flag = false;
break;
}
}
}
}
Java封装好的栈
Java中提供了一个封装好的栈类,即 java.util.Stack 类。使用这个类,可以更方便地实现栈的操作。
以下是 java.util.Stack 类中一些常用的方法:
- push(E item):将元素压入栈顶。
- pop():移除并返回栈顶元素。
- peek():返回栈顶元素,但不对栈进行修改。
- empty():判断栈是否为空,为空返回 true,否则返回 false。
- search(Object o):查找元素在栈中的位置,如果存在则返回相对于栈顶的位置(最顶部元素为 1),如果不存在则返回 -1。
```java
import java.util.Stack;
public class StackExample {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
// 压栈
stack.push(1);
stack.push(2);
stack.push(3);
// 出栈
int top = stack.pop();
System.out.println("出栈元素:" + top);
// 获取栈顶元素
int peek = stack.peek();
System.out.println("栈顶元素:" + peek);
// 判断栈是否为空
boolean empty = stack.empty();
System.out.println("栈是否为空:" + empty);
// 查找元素位置
int position = stack.search(2);
System.out.println("元素 2 在栈中的位置:" + position);
}
}
## 栈的实际应用
1. **函数调用栈**:在计算机的程序执行过程中,函数调用会形成一个由栈组成的调用栈。
2. **递归算法**:递归算法使用栈来保存每个递归步骤的状态。
3. **表达式求值**:在编译器中,栈可以用于解析和计算表达式(后缀表达式)。
4. **撤销操作**:在文本编辑器或图形编辑器中,栈可以用于实现撤销操作。
5. **浏览器历史记录**:浏览器使用栈来记录用户浏览网页的历史记录。
通过栈的特性和常见操作,我们可以在不同的领域中应用栈来解决问题,并提高程序的效率和性能。