〇、前言
1.上期回顾
在上期的内容中,我们讲到了双指针的思想,同时基于这个思想利用数组解决了一些经典的问题,删除数组中的的固定值、数组中奇偶移动等。当然我们要明白双指针思想不止存在于数组中中,在所有的线性结构中都可以使用。
2.本章内容
在今天,我们将学习另一种比较特殊的线性表,又称访问受限的线性表。栈!
一、栈的基础知识
1.栈的定义与特征
定义:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。
特征:
- 进行数据插入和删除的一端成为栈顶。
- 遵循先进先出LIFO的原则。
- 数据的插入的操作也叫进栈/压栈/入栈。
- 数据删除的操作也叫出栈。
栈的结构图:
2.栈的操作
栈的常用操作有:
- push(E):增加一个元素
- pop():弹出栈顶元素(删除)
- peek():显示栈顶元素(不删除)
- empty():判断栈内元素是否为空
注:我们在设计自己的栈时,不管是用数组还是链表,都必须实现上面的方法!
例题: 入栈顺序为1234,所有可能的出栈序列是什么?
答:1234√ 1243√ 1324√ 1342√ 1423× 1432√2134√ 2143√ 2314√ 2341√ 2413× 2431√3124× 3142× 3214√ 3241√ 3412× 3421√4123× 4132× 4213× 4231× 4312× 4321√
3.Java中的栈
Java中的util中提供了栈Stack类:
public static void main(String[] args) {
Stack<Integer> stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println("栈顶元素为:" + stack.peek());
while (!stack.empty()) {
//只显示没出栈
System.out.println(stack.peek());
//出栈并且显示
System.out.println(stack.pop());
}
}
二、基于数组实现栈
步骤:
- 采用顺序表实现栈,内部使用数组为基础,实现对元素的存取操作。
- 初始化栈,分配存储大小。
- 实现push、pop、peek、empty等操作。
- 插入元素时,判断是否溢出,若溢出则扩容并拷贝。
class Mystack<T> {
//实现栈的数组
private Object[] stack;
//栈顶元素
private int top;
Mystack() {
//初始容量为10
stack = new Object[10];
}
//判断是否为空
public boolean isEmpty() {
return top == 0;
}
//返回栈顶元素
public T peek() {
T t = null;
if (top > 0) {
t = (T) stack[top - 1];
}
return t;
}
public void push(T t) {
expandCapacity(top + 1);
stack[top] = t;
top++;
}
//出栈
public T pop() {
T t = peek();
if (top > 0) {
stack[top - 1] = null;
top--;
}
return t;
}
//扩大容量
public void expandCapacity(int size) {
int len = stack.length;
System.out.println(len);
if (size > len) {
size = size * 3 / 2 + 1;//每次扩大50%
stack = Arrays.copyOf(stack, size);
}
}
public static void main(String[] args) {
Mystack<String> stack = new Mystack<>();
System.out.println(stack.peek());
System.out.println(stack.isEmpty());
stack.push("java");
stack.push("is");
stack.push("beautiful");
stack.push("language");
System.out.println(stack.pop());
System.out.println(stack.isEmpty());
System.out.println(stack.peek());
}
}
注:图中的top指向的并不是栈顶元素而是栈顶的上一位元素。
入栈:出栈:
三、基于链表实现栈
使用链表实现栈也比较容易,我们只需要在头结点插入和删除元素就行了。
代码:
class ListStack<T> {
//定义链表
class Node<T> {
public T t;
public Node next;
}
public Node<T> head;
//构造函数初始化头指针
ListStack() {
head = null;
}
//入栈
public void push(T t) {
if (t == null) {
throw new NullPointerException("参数不能为空");
}
if (head == null) {
head = new Node<T>();
head.t = t;
head.next = null;
} else {
Node<T> temp = head;
head = new Node<>();
head.t = t;
head.next = temp;
}
}
//出栈
public T pop() {
if (head == null) {
return null;
}
T t = head.t;
head = head.next;
return t;
}
//取栈顶元素
public T peek() {
if (head == null) {
return null;
}
T t = head.t;
return t;
}
//栈空
public boolean isEmpty() {
if (head == null) {
return true;
} else {
return false;
}
}
public static void main(String[] args) {
ListStack stack = new ListStack();
System.out.println(stack.isEmpty());
stack.push("Java");
stack.push("is");
stack.push("beautiful");
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
// System.out.println(stack.peek());
System.out.println(stack.isEmpty());
}
}