请你用数组来实现队列和栈(大厂经典面试题)
提示:之前我们说过用双向队列实现栈和队列,贼简单
栈和队列,双端队列,如何用链表实现双端队列,如何用双端队列实现栈和队列
现在,用数组实现队列和栈,这样遍历速度快
数组实现队列
咋做呢?
关键在模拟队列的先进先出
数组目前定长为N=limit,咱们让数组arr的左边为出,而arr的右边为入,这样的话,来一个数放在数组右边,弹出一个的话,从左边拿。
(0)每一次存入一个数,push(value),则size++,记录真实存入数的量;
(1)你真实存入数组中的数目size,如果到了N,说明不能再存了,返回队列已满!
(2)如果下一次再来新的数,咱们需要在上一次放的数的下一个位置放,代表我后来的;
而且size<N时,当放入的数到了N-1位置,本次的数就要去0位置继续放;
也就是说,咱们要是让pushi做即将放入新数的下标的话,如果本次pushi放在N-1,下一次就得循环倒回去0位置。
(3)只要size>0,你就能poll(),咱们得找到哪个数最先进来的,让它先出去,这就是队列的本质!!
我们这样吧,单独让polli做要弹出的下标,最开始pulli=0,毕竟第一个进来的就是0位置,然后是1位置,递增
故,每次需要弹出时,咱们让size–,代表少了一个元素;
然后,ans=arr[polli],它就是要弹出的位置,
然后立马让polli++,代表下一次要弹出polli位置的数,因为递增的下标就是先进来的那些元素。
【但是要注意,这里是循环的下标推导,也就是当本次polli=N-1时,下一次polli要跟随输入那个数去0位置】
所以总结一下:
——当索引位置到N-1位置时,下一次应该去0位置:
代码:
//环形下标控制函数 public int nextIndex(int i){ return i < len - 1 ? i + 1 : 0;//如果i还没有到最后一个位置,递增,到了len-1则去0 }
用arr替代链表做队列,让pushi单独做输入数据的索引下标,让polli单独做弹出元素的下标,两者都是递增的规律,保证先进先出
size用来表示实际的输入元素,push则size++,poll则size–
反正保证size>0或者size<=N,才有效
手撕数组实现队列的结构:
手撕之前,我想说:java中的构造函数,如果你构造函数中的变量N和属性变量this.N名字一样,是没法赋值成功的
必须用下面两种方式:
(1)this.N = N;
(2)将构造函数中的N改为别的名字,比如Nx
public ReviewQueuebyArray(int Nx){
//构造函数说明清楚队列的大小
N = Nx;
arr = new int[N];
只有这样才能赋值成功,这是代码高手可能都会忽略的问题……
//复习数组实现队列
public static class ReviewQueuebyArray{
public int[] arr;
private int pushi;
private int polli;
private int N;
private int size;
public ReviewQueuebyArray(int Nx){
//构造函数说明清楚队列的大小
N = Nx;
arr = new int[N];
pushi = 0;
polli = 0;//这俩都默认为0
size = 0;//最开始没有数
}
//循环递推下标的函数
private int nextIndex(int N, int i){
return i == N - 1 ? 0 : i + 1;//遇到N-1,下一次去0,否则i++
}
//判空
public boolean isEmpty(){
return size == 0;//没有实际存一个数,就是空
}
//push函数
public void push(int value){
//检查size
if (size == N) throw new RuntimeException("没有空间了哦!");
arr[pushi] = value;
size++;
pushi = nextIndex(N, pushi);//循环递推下标
}
//poll函数
public int poll(){
//检查size
if (size == 0) throw new RuntimeException("根本没有元素啊!");
int ans = arr[polli];
size--;
polli = nextIndex(N, polli);//循环递推下标
return ans;
}
}
public static void test2(){
ReviewQueuebyArray queuebyArray = new ReviewQueuebyArray(4);
queuebyArray.push(1);
queuebyArray.push(2);
queuebyArray.push(3);
//queuebyArray.push(4);
int[] ans = queuebyArray.arr;
for(Integer i: ans) System.out.print(i +" ");
System.out.println();
System.out.println("pushi:"+ queuebyArray.pushi);
System.out.println("polli:"+ queuebyArray.polli);
}
看:只压3个数的话,下一次就应该放pushi了
1 2 3 0
pushi:3
polli:0
咱再弹出一个试试:System.out.println(queuebyArray.poll());
结果如下:
1
1 2 3 0
pushi:3
polli:1
这样就完美滴将数组arr拿来实现队列了,关键就在size控制实际入的数,然后用pushi控制入的下标,polli控制弹出的下标。
数组实现栈
有了上面数组实现队列的经验,实现栈也是类似的道理的
先进后出
那数组arr放入仍然用pushi的话,实际上,弹出的话,就让pushi–,然后弹出arr[pushi]
非常简单,不是吗?
所以的话,咱也不需要统计size了,一个pushi就行,循环下标也不需要,因为你最多就只能放N个
手撕代码:
//复习数组实现栈,很简单,一个pushi搞定一切
public static class ReviewStackbyArray{
public int[] arr;
private int pushi;
private int N;
public ReviewStackbyArray(int Nx){
N = Nx;
arr = new int[N];
pushi = 0;
}
//判空
public boolean isEmpty(){
return pushi == 0;
}
//push
public void push(int value){
if (pushi == N) throw new RuntimeException("满了!");
arr[pushi++] = value;//先赋值再加pushi
}
//pop
public int pop(){
if (pushi == 0) throw new RuntimeException("空的!");
return arr[--pushi];//先减再加,非常简单。
}
}
public static void test3(){
ReviewStackbyArray stackbyArray = new ReviewStackbyArray(4);
stackbyArray.push(1);
stackbyArray.push(2);
stackbyArray.push(3);
System.out.println("此时的pushi:"+ stackbyArray.pushi);
int[] ans = stackbyArray.arr;
for(Integer i: ans) System.out.print(i +" ");
System.out.println();
System.out.println(stackbyArray.pop());
System.out.println("此时的pushi:"+ stackbyArray.pushi);
}
显然,这比数组实现队列简单多了
看结果:
最开始压入3个数,自然pushi=3
此时的pushi:3
1 2 3 0
3
此时的pushi:2
然后弹出一个,自然pushi就要倒回来,此时的3已经无效了!
咱们就是通过pushi来控制,哪些是有效地栈中的数,哪些是无效的
如果有新的数来,则3会被覆盖的。
总结
提示:重要经验:
1)将数组arr拿来实现队列了,关键就在size控制实际入的数,然后用pushi控制入的下标,polli控制弹出的下标。
2)用数组arr实现栈,再简单不过了,一个pushi搞定压入弹出操作。