栈 Stack(LIFO线性表):
栈是一种特殊的线性表,满足后进先出,它限定仅可以在表尾进行删除和增加。push(), pop(), peek(),empty()是它的三个重要的方法。push()是往栈里压入一个元素;pop()是弹出栈顶元素,栈顶指针指向下一个元素;peek()是返回栈顶元素,但不删除栈顶元素;empty是判断栈是否为空。
队列 Queue(FIFO线性表):
队列也是一种特殊的线性表,与栈的规则相反,它满足先进先出原则,它限定只能在表尾添加元素,在表头删除元素。它的基本操作有offer(),poll(),peek()。offer()为在表尾添加一个元素,poll()为在表头删除一个元素,peek()为得到表头的元素,但不删除此元素。对于队列对应的还有三个操作 add(),remove(),element(),这三个操作可以完成相同的功能,但是如果后者操作失败会抛出异常,而前者返回具体的值(null,false等,要看具体的操作)。
***
不过请大家记住一点,不是所有的队列都是FIFO,比如优先队列,它是根据各元素的优先级来决定顺序的。
了解了栈和队列的基本操作,下面我们学习它们的具体实现。
1. Stack类的实现
JDK中写道,Stack类是继承了Vector类, Vector类在java中可以实现自动增长的动态数组。在这里我们用一个普通的对象数组来实现Stack类。代码如下:
public class Stack { private int top; private Object[] data; //初始化堆栈 public Stack(int size){ data = new Object[size]; top = -1; } public boolean empty(){ return top == -1; } public void push(Object object) { if(top == data.length-1){ System.out.println("栈满"); } data[++top] = object; } public Object pop() { if(top == -1){ System.out.println("栈空"); return null; } return data[top--]; } public Object peek() { if(data == null || data.length == 0) return null; return data[data.length-1]; } }
这是堆栈最基本的实现,可能面试中可能会遇到其他问题,比如实现一个动态堆栈,当然在JDK中用vector类实现的堆栈本身就是动态堆栈,因为vector类可以实现可变长数组。对于普通对象数组长度是不可变的,上面的代码仅仅实现了一个固定长度的堆栈,如需改成动态堆栈,我们只需要改进push()方法,其他方法保持一致。改进后的push()方法:
public void push(Object object) { if(top == data.length-1){ Object temp[] = new Object[data.length * 2]; // 把数组的容量增加一倍 for(int i=0; i<data.length; i++) temp[i] = data[i]; data = temp; data[++top] = object; } data[++top] = object; }
我们还可以用链表来实现堆栈,代码如下:
//首先定义一个链表类
class ListNode{ Object object; ListNode next; ListNode(Object object){ this.object = object; } } public class Stack { ListNode head; public void push(Object object) { ListNode node = new ListNode(object); node.next = head; head = node; } public Object pop() { if(head != null) { Object element = head.object; head = head.next; return element; } return null; } public Object peek() { return head.object; } public boolean empty() { return head == null; } }
2. 队列的实现
首先我们要搞清楚,队列是接口不是类,它不可以实例化,也就是不可以new queue(),接口的使用必须依赖于实现它的类,对于接口的引用,采用的是实例化实现该接口的类。下面我们用链表来实现一个队列。
class ListNode{ Object object; ListNode next; ListNode(Object object){ this.object = object; } } public class QueueDemo { ListNode head; ListNode tail; public void offer(Object object) { if(head == null){ tail = new ListNode(object); head = tail; } else { tail.next = new ListNode(object); tail=tail.next; } } public Object poll() { if(head != null) { Object element = head.object; head = head.next; if(head == null) tail = null; return element; } return null; } public Object peek() { if(head == null) return null; return head.object; } }
总结:我们用数组实现了固定长度的堆栈和动态堆栈,然后用链表分别实现了堆栈和队列。堆栈和队列之间也可以相互实现,只要记住队列是先进先出,只可以在队尾添加元素,在对头删除元素;堆栈是后进先出,只可以在表尾添加或删除元素。
以上只是个人总结,希望朋友们多多交流,共同进步!