1.栈(Stack)
1.1 概念
栈 :一种特殊的线性表,其 只允许在固定的一端进行插入和删除元素操作 。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO ( Last In First Out )的原则。
压栈:栈的插入操作叫做进栈 / 压栈 / 入栈, 入数据在栈顶 。
出栈:栈的删除操作叫做出栈。 出数据在栈顶 。
如图所示:
进栈:进栈顺序是ABCD,A先压入栈底,B压在A上,如此依次压栈,最后的D就是栈顶数据
出栈:只能从一端出,所以栈顶数据先出,出栈顺序就为DCBA,D最先出,A最后出
现实生活也有很多像栈一样的结构,如枪里的子弹最先打出去的是最后装的子弹,羽毛球桶最先拿出来的羽毛球是最后放进去的,都符合栈的后进先出LIFO(Last In First Out)的原则。
1.2栈的常用方法
2.队列(Queue)
2.1 概念
队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为 队尾( Tail/Rear ) 出队列:进行删除操作的一端称为 队头 ( Head/Front )
2.2 队列的实现
如图我们可以知道队列是一个接口,接口不能实例化,但可以通过实现了该接口的类来实例化,所以 Queue可以通过ArrayList,LinkedList,PriorityQueue等来实例化该接口;
public static void main ( String [] args ) {
Queue < Integer > q = new LinkedList <> ();
Queue < Integer > q1 = new ArrayList <> ();
Queue < Integer > q2 = new PriorityQueue <> ();
}
3****循环队列
如何区分空与满
-
通过添加 size 属性记录
-
保留一个位置
-
使用标记
通过1方法来模拟实现k长度的循环队列:class MyCircularQueue {
int[] elem;
int front;
int rear;
int size;
public MyCircularQueue(int k) {
elem=new int[k];
front=rear=0;
}public boolean enQueue(int value) { if(isFull()){ return false; }else{ elem[rear]=value; rear=(rear+1)% elem.length; size++; return true; } } public boolean deQueue() { if(isEmpty()){ return false; }else{ front=(front+1)%elem.length; size--; return true; } } public int Front() { if(isEmpty()){ return -1; } return elem[front]; } public int Rear() { if(isEmpty()){ return -1; } int rear1=(rear==0)?elem.length-1:rear-1;//rear==0的时候比较特殊 return elem[rear1]; } public boolean isEmpty() { return size==0; } public boolean isFull() { return size==elem.length; }
}
通过2方法来模拟实现k长度的循环队列:
class MyCircularQueue {
int[] elem;
int front;
int rear;
public MyCircularQueue(int k) {
elem=new int[k+1];//浪费一个空间来判断循环队列的队满;
front=rear=0;
}
public boolean enQueue(int value) {
if(isFull()){
return false;
}else{
elem[rear]=value;
rear=(rear+1)% elem.length;
return true;
}
}
public boolean deQueue() {
if(isEmpty()){
return false;
}else{
front=(front+1)%elem.length;
return true;
}
}
public int Front() {
if(isEmpty()){
return -1;
}
return elem[front];
}
public int Rear() {
if(isEmpty()){
return -1;
}
int rear1=(rear==0)?elem.length-1:rear-1;//rear==0的时候比较特殊
return elem[rear1];
}
public boolean isEmpty() {
return front==rear;
}
public boolean isFull() {
return (rear+1)% elem.length==front;
}
}
4.栈和队列的相互转化
4.1用队列实现栈
栈:先进后出
队列:先进先出
进栈的实现:两队列都空就放数据到队列1里,否则就哪个队列为空就放哪里
出栈的实现:将有不为空的队列里的size-1个元素方到另一个队列里,然后就将只剩下一个元素的队列出队列就实现的出栈;
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1=new LinkedList<>();
queue2=new LinkedList<>();
}
public void push(int x) {
if(!queue1.isEmpty()){
queue1.add(x);
}else if(!queue2.isEmpty()){
queue2.add(x );
}else{
queue1.add(x);
}
}
public int pop() {
if(empty()){
return -1;
}
if (queue1.isEmpty()){
int size= queue2.size();
for (int i = 0; i < size-1; i++) {
int val=queue2.poll();
queue1.add(val);
}
return queue2.poll();
}
else {
int size= queue1.size();
for (int i = 0; i < size-1; i++) {
int val = queue1.poll();
queue2.add(val);
}
return queue1.poll();
}
}
public int top() {
if(empty()){
return -1;
}
if (queue1.isEmpty()){
int size= queue2.size();
for (int i = 0; i < size-1; i++) {
int val=queue2.poll();
queue1.add(val);
}
int val2= queue2.peek();
queue1.add(queue2.poll());
return val2;
}
else {
int size= queue1.size();
for (int i = 0; i < size-1; i++) {
int val = queue1.poll();
queue2.add(val);
}
int val2= queue1.peek();
queue2.add(queue1.poll());
return val2;
}
}
public boolean empty() {
return (queue2.isEmpty()&&queue1.isEmpty());
}
}
注意:
**问题:**观察上图发现只是实例化queue接口的类不一样,其余代码一样,那为什么结果会不一样呢?
解答:
优先级队列 是会自动按照升序排序的 也就是每次push进来一个元素 都会自动排成升序 所以stack中从栈底到栈顶分别是 1 2 2 3 4,因此结果是4 4 3
而双向链表是没有排序这个性质 从栈底到栈顶分别是 1 2 3 4 2 ,因此结果是 2 2 4
4.1用栈实现栈队列
栈:先进后出
队列:先进先出
进队的模拟实现:
stack1专门用来放元素,都为空的时候就将元素加入stack1中,stack1不为空就直接push,为空就将stack2中的元素全部放入stack1中
出队的模拟实现:
stack2专门用来出元素,进行出对操作时将元素放入stack2中出元素即可
class MyQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
public MyQueue() {
stack1=new Stack<>();
stack2=new Stack<>();
}
public void push(int x) {
if(!stack1.isEmpty()) {
stack1.push(x);
}else if(!stack2.isEmpty()){
int size=stack2.size();
for (int i = 0; i < size; i++) {
int val=stack2.pop();
stack1.push(val);
}
stack1.push(x);
}else{
stack1.push(x);
}
}
public int pop() {
if(empty()){
return -1;
}
if(stack2.isEmpty()){
int size=stack1.size();
for (int i = 0; i < size; i++) {
stack2.push(stack1.pop());
}
return stack2.pop();
}else{
return stack2.pop();
}
}
public int peek() {
if(empty()){
return -1;
}
if(stack2.isEmpty()){
int size=stack1.size();
for (int i = 0; i < size; i++) {
stack2.push(stack1.pop());
}
return stack2.peek();
}else{
return stack2.peek();
}
}
public boolean empty() {
return stack1.isEmpty()&&stack2.isEmpty();
}
}