3、栈
栈是一种特殊的线性表
栈只能在一端(栈顶)进行操作,往栈里添加元素叫入栈,删除栈里的元素叫出栈,后进的元素先出
官方栈:java.util.Stack
栈的应用:浏览器的前进与后退
如果输入三个网址(假设123三个)
出栈过程(后退操作):假设现在正在看3,想要返回2,需要将3移出栈,同理再想看1,将2移出栈,这两个元素组成的新栈从栈顶到栈底依次为:2、3
入栈过程(前进操作):后退到1后,想要回到2页面,就得把2移回来,重新变成栈顶
上述操作体现“先进后出”思想
4、队列
队列也是一种特殊的线性表
队列可以两端操作,但两端操作分别受限,队尾只能添加元素(入队),队头只能删除元素(出队)
类比排队买票,如果要买票就得从队尾排,买完票就从队头出来,中间叫插队,是不允许的
因为队列在头尾进行操作,所以最好用“双向链表”
官方队列:Queue
利用栈实现队列:准备两个栈(inStack,outStack)
入队:将元素push进inStack中
出队:如果outStack为空,由于出队要从队头出,那么把inStack中的元素push进outStack,再pop栈顶元素
如果不为空,比如已弹出一个元素其他元素还在里面,还是pop栈顶元素
class MyQueue {
private Stack<Integer> inStack;
private Stack<Integer> outStack;
public MyQueue() {
inStack = new Stack<>();
outStack = new Stack<>();
}
//入队
public void push(int x) {
inStack.push(x);
}
//出队
public int pop() {
if(outStack.isEmpty()){
while(!inStack.isEmpty()){ //如果inStack不为空,把inStack内元素放入outStack中
outStack.push(inStack.pop());
}
}
return outStack.pop(); //弹出栈顶元素
}
//获取队头
public int peek() {
if(outStack.isEmpty()){
while(!inStack.isEmpty()){ //如果inStack不为空,把inStack内元素放入outStack中
outStack.push(inStack.pop());
}
}
return outStack.peek();
}
//判断队列是否为空
public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty(); //两个栈都为空
}
}
双端队列:在头尾分别都可以添加或删除元素
循环队列:动态数组实现
指针front指向队头,从队头删除元素时,front指针指向下一位;当数组最后一位已被占用且front前尚有空位,添加元素会在前面的空位进行添加;数组满时就动态扩容
front使用int,实际存储的是数组下标
假设front目前位于数组的最后一位,再次添加元素时front需要到数组0位置而不是length+1,所以要写 front = (front + 1) % elements.length
动态扩容:由于是循环的,扩容后希望头指针就指在首元素位置,所以进行front重置;也因此,让newElements找到对应的元素要使用 (i + front) % elements.length;