一、什么是栈?
说的术语一点就是一种只能在一端进行插入和删除操作的线性表,可以用数组实现也可以用链表实现。说的白话一点,栈就像一个玻璃杯,只有一个杯口可以进水,倒水的时候,水也是从唯一的杯口倒出。而且后来倒入的水在上面会先倒出去。
二、实现的基本操作
// 将数据入栈
// 弹出栈顶数据
// 访问栈顶数据
// 判断栈是否为空
// 扩大栈容量
// 显示栈内元素
三、数组实现栈
// 初始化栈
public void init() {
size = 0;
maxSize = 10;
Capacity(maxSize);
}
//入栈
public void push(T value) {
if(size == maxSize) {
Capacity(maxSize*2);
}
dataArr[size] = value;
size++;
}
//出栈
public T pop() {
if(size==0) {
return null;
}
T value = dataArr[size-1];
dataArr[size-1] = null;
size--;
return value;
}
//获取栈顶元素
public T peek() {
if(length()==0) {
return null;
}
return dataArr[size-1];
}
//判断是否为空
public boolean isEmpty() {
return length()==0;
}
//返回栈的长度
public int length() {
return size;
}
//打印栈内的元素,从高地址到低地址
public void printStack() {
Mystack<T> stack = new Mystack<>();
System.out.println("栈内元素:");
for(int i=0;i<length();i++) {
System.out.println(dataArr[i]);
}
测试结果:
public static void main(String[] args) {
Mystack<Integer> stack = new Mystack<>();
stack.init();
System.out.println("是空栈吗? "+stack.isEmpty());
stack.isEmpty();
stack.push(10);
stack.push(9);
stack.push(8);
stack.push(7);
stack.push(6);
stack.push(5);
System.out.println("是空栈吗? "+stack.isEmpty());
stack.printStack();
System.out.println("栈顶元素是 "+stack.peek());
System.out.println("出栈成功了,元素"+stack.pop());
stack.printStack();
}
效果图:
四、一个数组实现两个栈
问题描述:
通过一个定长的数组实现两个栈(后进先出),如果栈满了,可以扩容。
问题分析:
将一个数组分成两部分有多种方法:
①按索引奇偶划分
②从数组中间往两边发展,中间的地方表示两个栈的栈底
③从数组两边往中间发展,数组两端分别为两个栈的栈底,当两个栈的栈顶指针相遇则满了。
比较这三种方法我们可以发现①②方法都是将两个栈的长度均匀分配了,但实际应用中,两个栈的大小是不定的,如果栈1的元素很少,栈2的元素很多,那么就会出现栈1分配的空间浪费,栈2空间不足的情况,所以我们可以采用第三种方案进行优化。
我们可以使用四个变量,增加了top
size1,size2,//表示栈内元素个数
top1,top2//表示栈顶的数组索引
// 扩大数组容量
public void Capacity() {
T[] oldArr = dataArr;
int t = maxSize;
maxSize *= 2;
dataArr =(T[])new Object[maxSize];
System.out.println("容量大小为:"+maxSize);
//不能写成dataArr =new T[maxSize];因为T是一种泛型不是具体的类型不能直接创建数组
for(int i=0;i<=top1;i++) {
dataArr[i] = oldArr[i];
}
for(int i=maxSize-1,j=t-1;i>=0 && j>=top2;i--,j--) {
dataArr[i] = oldArr[j];
}
top2=maxSize-size2;//注意更新top2的值
System.out.println("扩大容量了top1="+top1);
System.out.println("扩大容量了top2="+top2);
}
// 判断数组是否满了,满了就扩大容量
public void isFull() {
if(top2==top1) {
System.out.println("满栈了top1= "+top1 );
System.out.println("满栈了top1= "+top2 );
Capacity();
}
}
我觉得最值得注意的就是在扩大容量的时候要更新top2的值,我就是刚开始没有更新然后在满栈的时候添加元素就出现了错误的结果,这个时候建议画图这个方便计算下标
全部代码:
package com.lm.Stack1120;
public class TwoStack<T>{
private T[] dataArr;//实现的数组
private int maxSize;//数组长度
private int size1;//栈1的元素个数
private int size2;//栈2的元素个数
private int top1;//栈1的栈顶索引
private int top2;//栈2的栈顶索引
public TwoStack() {
init();
}
public void init() {
maxSize = 10;
size1 = 0;
size2 = 0;
top1 = 0;
top2 = maxSize;
dataArr = (T[])new Object[maxSize];//注意初始化长度,不然会产生空指针的错误
}
// 判断数组是否满了,满了就扩大容量
public void isFull() {
if(top2==top1) {
System.out.println("满栈了top1= "+top1 );
System.out.println("满栈了top1= "+top2 );
Capacity();
}
}
//栈1入栈
public void push_1(T value) {
isFull();
dataArr[top1] = value;
System.out.println("入栈成功! "+dataArr[top1] );
top1++;
size1++;
System.out.println("top1= "+top1 );
}
//栈2入栈
public void push_2(T value) {
isFull();
top2--;
size2++;
dataArr[top2] = value;
System.out.println("top2= "+top2 );
}
//栈1出栈
public T pop_1() {
if(size1==0) {
System.out.println("栈1为空!");
return null;
}else {
T value = dataArr[top1];
top1--;
size1--;
return value;
}
}
//栈2出栈
public T pop_2() {
if(size2==0) {
System.out.println("栈2为空!");
return null;
}else {
T value = dataArr[top2];
top2++;
size2--;
return value;
}
}
//获取栈1栈顶元素
public T peek_1() {
if(size1==0) {
System.out.println("栈2为空!");
return null;
}else {
return dataArr[top1];
}
}
//获取栈2栈顶元素(基本数据类型不能返回null)
public T peek_2() {
if(size2==0) {
System.out.println("栈2为空!");
return null;
}else {
return dataArr[top2];
}
}
//栈1元素个数
public int length_1() {
return size1;
}
//栈2元素个数
public int length_2() {
return size2;
}
// 扩大数组容量
public void Capacity() {
T[] oldArr = dataArr;
int t = maxSize;
maxSize *= 2;
dataArr =(T[])new Object[maxSize];
System.out.println("容量大小为:"+maxSize);
//不能写成dataArr =new T[maxSize];因为T是一种泛型不是具体的类型不能直接创建数组
for(int i=0;i<=top1;i++) {
dataArr[i] = oldArr[i];
}
for(int i=maxSize-1,j=t-1;i>=0 && j>=top2;i--,j--) {
dataArr[i] = oldArr[j];
}
top2=maxSize-size2;//注意更新top2的值
System.out.println("扩大容量了top1="+top1);
System.out.println("扩大容量了top2="+top2);
}
//打印栈1元素
public void print_1() {
System.out.println("栈1的元素:");
if(size1==0) {
System.out.println("无");
}
for(int i=0;i<top1;i++) {
if(dataArr[i] != null)
System.out.println(dataArr[i]);
}
}
//打印栈2元素
public void print_2() {
System.out.println("栈2的元素:");
if(size2==0) {
System.out.println("无");
}
for(int i=maxSize-1;i>=top2;i--) {
if(dataArr[i] != null)
System.out.println(dataArr[i]);
}
}
public static void main(String[] args) {
TwoStack<Object> ts = new TwoStack<>();
ts.init();
ts.push_1(0);
ts.push_1(1);
ts.push_1(2);
ts.push_1(3);
ts.print_1();
ts.push_2(4);
ts.push_2(5);
ts.push_2(6);
ts.push_2(7);
ts.push_2(8);
ts.push_2(9);
ts.print_2();
ts.push_1(100);
ts.push_2(200);
ts.print_1();
ts.print_2();
}
}
五、链表实现栈
因为栈的后进先出的特点,如果将链表的头部表示成入栈出栈的一端,那么入栈的时候就是在头部插入插入结点,出栈的时候也是删除头部结点,这个操作是不是很熟悉,就是之前说过的单向链表(当然也可以使用其他链表实现),在这里我使用简单的单向链表。
public class StackLink {
private Node top;//表示栈的第一个空位
private int size;
public class Node{//创建结点类
private Object data;
Node next;
public Node(Object data) {
this.data = data;
}
}
public void init() {
top = null;
size = 0;
}
//入栈(链表头部插入结点)
public void push(Object value) {
Node node = new Node(value);
node.next = top;
top = node;
}
//出栈(头部删除结点)
public Object pop() {
if(top==null) {
return null;
}
Node node = top;
top = top.next;
return node.data;
}
//判断是否为空
public boolean isEmpty() {
return top==null;
}