关于栈和队列的基础知识这里不再赘述,具体可以参照数据结构教材,但只需要记住两者的特性:栈——先进后出,队列:先进先出。
1 使用数组实现固定大小的栈和队列结构
1.1 使用数组构建栈结构
这里实现进栈出栈和取栈顶元素三个方法
//数组栈
public class ArrayStack{
private Integer[] arr;
private Integer index;
public ArrayStack(int initSize){
if(initSize<0){
throw new IllegalArgumentException("the size is less than 0");
}
arr=new Integer[initSize];
index=0;
}
//获取栈顶元素
public Integer top(){
if(index==0){
return null;
}
return arr[index-1];
}
//进栈
public void push(int ele){
if(index==arr.length){
throw new ArrayIndexOutOfBoundsException("has no space to store data");
}
//先放元素 再++
arr[index++]=ele;
}
//出栈
public Integer pop(){
if(index<=0){
throw new ArrayIndexOutOfBoundsException("no element can be given");
}
//先-- 再放元素
return arr[--index];
}
}
1.2 使用数组构建队列结构
这里实现入队出队和取栈顶元素三个方法,其中借助了Size进行约束限制队列中元素的数目
//数组队列
public static class ArrayQueue{
private int size;
private int start;
private int end;
private Integer arr[];
public ArrayQueue(int initSize){
if(size<0){
throw new IllegalArgumentException("the size is less than 0");
}
arr=new Integer[initSize];
start=end=size=0;
}
//入队
public void inQueue(int ele){
if(size==arr.length){
throw new ArrayIndexOutOfBoundsException("index outBound");
}
size++;
arr[end]=ele;
end=nextIndex(arr.length,end);
}
//出队
public Integer outQueue(){
if(size==0){
throw new ArrayIndexOutOfBoundsException("Queue is empty");
}
size--;
Integer result=arr[start];
start=nextIndex(arr.length,start);
return result;
}
//得到进出队列限一次操作的索引值
public int nextIndex(int size,int index){
return index==size-1 ? 0 : index+1;
}
//得到队首元素
public Integer peek(){
if(size==0){
throw new ArrayIndexOutOfBoundsException("Queue is empty");
}
return arr[start];
}
}
1.3 在Java内部也实现了Stack类,具体可以参考一下Java API,在写一些算法题目的时候,可以直接调用相关的API实现。下面结合一道题目来描述下Java内部Stack的使用。
用栈实现出栈入栈和取最小值的时间复杂度为O(1)
算法分析:这里其实我们的出栈和入栈本身就是O(1),但是在求取最小值的时候,需要考虑使用双栈结构实现,一个是只存储数据的栈,另一个是只存储每次当前最小值的栈结构,假设入栈序列为(10,7,6,3,8,9),其算法原理如下图所示:
上图步骤为,每次将序列中的数字进入数据栈,在最小栈中栈顶存储的是当前数据战中的最小址,这样在取数据栈内元素最小值时,可以直接获取栈顶元素即位最小值,保证了时间复杂度是1,在数据栈中元素进行弹出时,最小值栈也跟着弹出元素。当然这种实现,是两种栈都使用了相同的空间数目。
代码如下:
//栈结构实现进出栈以及获取最小值的时间复杂度为O(1)
//思想是使用两个栈
public class OptimizeStack{
private Stack<Integer> dataStack;
private Stack<Integer> minStack;
public OptimizeStack(){
this.dataStack=new Stack<Integer>();
this.minStack=new Stack<Integer>();
}
//进栈
public void push(int ele){
if(this.minStack.isEmpty()){
this.minStack.push(ele);
}else if(ele>this.minStack.peek()){
this.minStack.push(this.minStack.peek());
this.dataStack.push(ele);
}else{
this.minStack.push(ele);
this.dataStack.push(ele);
}
}
//得到最小值
public Integer getMin(){
if(this.minStack.isEmpty()){
throw new NullPointerException("no data in stack");
}
return this.minStack.peek();
}
//出栈
public Integer pop(){
if(this.dataStack.isEmpty()){
throw new NullPointerException("no data in stack");
}
this.minStack.pop();//从最小栈中弹出
return this.dataStack.pop();//从数据栈中弹出
}
}
当考虑节约空间时,数据栈的大小和序列数是一样的,但是最小栈可以只保存最小值,即碰到比最小栈顶小的数据再入到最小栈中,如下图:
代码实现如下:
public static class OptimizeStack{
private Stack<Integer> dataStack;
private Stack<Integer> minStack;
public OptimizeStack(){
this.dataStack=new Stack<Integer>();
this.minStack=new Stack<Integer>();
}
//进栈
public void push(int ele){
if(this.minStack.isEmpty()){
this.minStack.push(ele);
}else if(ele>this.minStack.peek()){
//this.minStack.push(this.minStack.peek());
this.dataStack.push(ele);
}else{
this.minStack.push(ele);
this.dataStack.push(ele);
}
}
//得到最小值
public Integer getMin(){
if(this.minStack.isEmpty()){
throw new NullPointerException("no data in stack");
}
return this.minStack.peek();
}
//出栈
public Integer pop(){
if(this.dataStack.isEmpty()){
throw new NullPointerException("no data in stack");
}
if(this.dataStack.peek()==this.minStack.peek()){
this.minStack.pop();//从最小栈中弹出
}
return this.dataStack.pop();//从数据栈中弹出
}
}
注意比较以上的不同,其实很简单的操作。
有问题欢迎指正!!!!!
【未完待续。。。。】