一.什么是栈,什么是队列?
栈是后进先出(LIFO)即last in first out
我们若是想实现后进先出,由图可见,只能选择一个口操作数据
队列是先进先出(FIFO)即first in first out
我们若是想实现先进先出,由图可见,取出只与first相关,进入只与last相关
二.数组实现栈
数组与链表的区别见本人用Java实现简易链表的文章中,不清楚的小伙伴可以去看一下喔
1.通过构造方法初始化队列容量
由于数组在实例化确定了长度之后,长度是不可变的,所以需要有容量这个参数且要初始化容量
public MyStack(int capacity) {
datas=new Object[capacity];
}
//创建栈时,给定的定容量
2.成员变量
Object datas[];//底层存储用的数组
int size;//存储的数据个数
3.基本方法实现(put放入,take取出并删除)
public void put(E e){
datas[size] =e;
size++;
}
由于在删除数组中某个数据时,需要将后面的所有元素往前移动一个位置,所以我们在选择栈的操作口时,可以选择尾端,这样,就避免了移动大量数据
public Object take(){
Object last;
if(size==0){
throw new IndexOutOfBoundsException("栈中暂无数据,请先添加数据!!!");
}
else{
last= datas[size-1];
datas[size-1]=null;
}
size--;
return last;
}
三.链表实现栈
为什么要保证head这个节点的data一直为null?
head的data若不为null,用图示表示(图中head的data记为head)
下面图示两个栈的第一个节点都为右边的节点,只是第一个不可以之后,想利用存储上一个节点的方式破局,结果也是不行的
这两种方式均无法实现,其原因是一样的,它们的口分别是head和last,而像上面那样做,只会让新节点离口越来越远,也就是说无法让新节点与head或last联系起来,以至于无法取出刚存入的新节点
而解决方法就是让新节点一直都是在head之后的
取出新节点时,就可以直接与head联系起来,做到后进先出
head自己如果存有data值,无疑应该是第一个加入的,那就会变成先进先出了,所以设置一个这样特殊的head节点就可以很容易的实现先进先出啦
1.链表需要先创建一个节点类
public class MyNode<E> {
Object data;
MyNode<E> next;
public MyNode(E data, MyNode<E> next) {
this.data = data;
this.next = next;
}
}
2.成员变量
MyNode<E> head;
int size;
3.基本方法实现
public void put(E e){
//头节点不存储数据
if(size==0){
head= new MyNode<>(null,new MyNode<>(e,null));
}
else{
//先获取之前的第一个节点
MyNode<E> oldfirst=head.next;
//创建一个新的节点
// 新节点要指向之前的第一个节点
MyNode<E> newnode=new MyNode<>(e,oldfirst);
//头节点指向新节点
head.next=newnode;
}
size++;
}
public Object take() {
//如果栈为空,报错
Object data;
if (size == 0) {
throw new IndexOutOfBoundsException("栈目前为空,请先加入数据!!");
} else {
//先记录一下要被取走的节点数据
MyNode<E> oldfirst = head.next;
data = oldfirst.data;
head.next=oldfirst.next;
size--;
}
return data;
}
四.数组实现队列
构造方法定容量
MyArrayQueue(int capacity){
this.capacity=capacity;
objects=new Object [this.capacity];
}
成员变量
int first=0;
int last=0;
//放入数据时与last有关
//取出数据时与first有关
int capacity;
Object objects[];
方法实现
public void put(E e ){
//防止数据添加超出容量
if(last==capacity){
throw new IndexOutOfBoundsException("超出容量,无法添加!!!");
}
last++;
objects[last-1]=e;
System.out.println("放进去了一个数据");
}
public Object take(){
//取之前判断队列是否为空
if(first==last){
throw new ArrayIndexOutOfBoundsException("当前数列为空,请先添加数据!!!");
}
Object f=objects[first];
//得到这个数据之后,把这个数据删除掉
//用数组的弊端?删除一个数据,需要将所有的数据往前移动?
for(int i=1;i<last;i++){
objects[i-1]=objects[i];
}
System.out.println("拿走了"+f);
//同贪吃蛇类似,需要注意遍历的方向
return f;
}
五.链表实现队列
比起数组实现的队列,链表实现队列会更加容易
1.链表需要先创建一个节点类
public class MyNode<E> {
Object data;
MyNode<E> next;
public MyNode(E data, MyNode<E> next) {
this.data = data;
this.next = next;
}
}
2.成员变量
MyNode<E> first;
MyNode<E> last;
int size;
3.方法实现
public void put(E e){
//实现addlast
//头节点为空时,初始化头节点
if(first==null) {
first = new MyNode<>(e, null);
last=first;
size++;
}
else{
last.next=new MyNode<>(e,null);
last=last.next;
size++;
}
}
public Object take(){
//当队列为空时
if(size==0){
throw new IndexOutOfBoundsException("没有数据可以被取出了!!");
}
//实现pollfirst
Object data=first.data;
MyNode<E> next =first.next;
first.data=null;
first.next=null;
first=next;
size--;
return data;
}
六.一些其他简单方法的实现
以下的方法实现以数组实现栈为例
栈中目前所存数据的个数
public int sizeOf(){
return size;
}
栈是否为空
public boolean isEmpty(){
return size==0;
}
遍历栈中的数据
public void travel(){
System.out.print("{");
for(int i=0;i<size-1;i++){
Object data= datas[i];
System.out.print(data+",");
}
Object data2=datas[size-1];
System.out.print(data2);
System.out.println("}");
}
七.方法测试
测试方法的代码呈现
public static void main(String[] args) {
MyStack<String> stack=new MyStack<>(10);
stack.put("李佳*");
stack.put("赵怡*");
stack.put("陈姝*");
stack.travel();
System.out.println("拿走了"+stack.take());
System.out.println("拿走了"+stack.take());
System.out.println("拿走了"+stack.take());
System.out.println("栈中的数据个数为"+stack.sizeOf());
System.out.println(stack.isEmpty());
}
测试结果呈现
{李佳*,赵怡*,陈姝*}
拿走了陈姝*
拿走了赵怡*
拿走了李佳*
栈中的数据个数为0
true