栈和队列-上
上篇👇
Java比较器之Comparable和Comparator
栈和队列基本概念
栈和队列都属于线性表,因为它们也都用于存储逻辑关系为 “一对一” 的数据。
使用栈结构存储数据,讲究“先进后出”,即最先进栈的数据,最后出栈;使用队列存储数据,讲究 “先进先出”,即最先进队列的数据,也最先出队列。
栈和队列都属于线性表,根据线性表分为顺序表和链表的特点,栈也可分为顺序栈和链表,队列也分为顺序队列和链队列
顺序表实现栈及其基本操作
分析
top始终代表下一个存放的元素的下标,每次存放完之后top++.
出栈是得到栈顶元素elem[top-1],让top–,再次入栈时原来的值就会被覆盖掉
源码
/**
* user:ypc;
* date:2021-05-02;
* time: 15:04;
*/
public class MyStack {
int [] elem ;
int top;
MyStack(){
this.elem = new int[10];
this.top = 0;
}
public void push(int val){
this.elem[this.top] = val;
this.top++;
}
public int pop() throws UnsupportedOperationException{
if(empty())throw new UnsupportedOperationException("栈为空");
int val = this.elem[this.top-1];
this.top--;
return val;
}
public int peek(){
return this.elem[this.top-1];
}
public int size(){
return this.top;
}
public boolean empty(){
return this.top == 0;
}
}
class MyStackTest{
public static void main(String[] args) {
System.out.println("顺序表表实现栈:===================================");
MyStack stack = new MyStack();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
stack.push(5);
System.out.println("栈顶是:"+stack.peek());
System.out.println("栈的长度是:"+stack.size());;
stack.pop();
stack.pop();
System.out.println("出栈后栈顶是:"+stack.peek());
System.out.println("出栈后栈的长度是:"+stack.size());;
System.out.println("栈是否是空:"+stack.empty());
stack.pop();
stack.pop();
stack.pop();
System.out.println("栈是否是空:"+stack.empty());
}
}
运行结果
链表实现栈及其基本操作
分析
原理和顺序表实现栈一样,入栈每次让入的元素的next指向head,要入的元素的节点变成head,出栈就是每次让head的值出,head变为head的next,head==null的时候不能出。为什么不用尾插法呢?尾插要找尾 ,时间复杂度为O(n)。
源码
/**
* user:ypc;
* date:2021-05-02;
* time: 15:04;
*/
class stackNode{
int val;
stackNode next;
stackNode(int val){
this.val = val;
}
}
class MyLinkedStack{
stackNode head;
int top;
public void push(int val){
stackNode node = new stackNode(val);
if(empty()){
this.head = node;
} else{
node.next = this.head;
this.head = node;
}
this.top++;
}
public int pop()throws UnsupportedOperationException{
if(empty()){
throw new UnsupportedOperationException("栈为空");
}
int val = this.head.val;
this.head = this.head.next;
this.top--;
return val;
}
public int peek(){
return this.head.val;
}
public int size(){
return this.top;
}
public boolean empty(){
return this.head == null;
}
}
class MyStackTest{
public static void main(String[] args) {
System.out.println("链表实现栈:");
MyLinkedStack myLinkedStack = new MyLinkedStack();
myLinkedStack.push(1);
myLinkedStack.push(2);
myLinkedStack.push(3);
myLinkedStack.push(4);
myLinkedStack.push(5);
System.out.println("栈顶是:"+myLinkedStack.peek());
System.out.println("栈的长度是:"+myLinkedStack.size());;
myLinkedStack.pop();
myLinkedStack.pop();
System.out.println("出栈后栈顶是:"+myLinkedStack.peek());
System.out.println("出栈后栈的长度是:"+myLinkedStack.size());;
System.out.println("栈是否是空:"+myLinkedStack.empty());
myLinkedStack.pop();
myLinkedStack.pop();
myLinkedStack.pop();
System.out.println("栈是否是空:"+myLinkedStack.empty());
}
}
运行结果
与顺序表实现栈对比
链表实现队列及其基本操作
分析
定义一个头front和一个尾rear,入队总是从尾入,出队从头出
源码
class Node{
int val;
Node next;
Node(int val){
this.val = val;
}
}
public class MyQueue {
public Node front;
public Node rear;
public int size;
public void offer(int val){
Node node = new Node(val);
if(isEmpty()){
this.front = node;
this.rear = node;
}else{
this.rear.next = node;
this.rear = node;
}
this.size++;
}
public int poll() throws UnsupportedOperationException{
if(isEmpty()){
throw new UnsupportedOperationException("队列为空!");
}
int val = this.front.val;
this.front = this.front.next;
this.size--;
return val;
}
public int peek(){
return this.front.val;
}
public int size(){
return this.size;
}
public boolean isEmpty(){
return front == null;
}
}
class MyQueueTest{
public static void main(String[] args) {
System.out.println("=========================================");
MyQueue myQueue = new MyQueue();
myQueue.offer(1);
myQueue.offer(2);
myQueue.offer(3);
myQueue.offer(4);
System.out.println("队列是否为空:");
System.out.println(myQueue.isEmpty());
System.out.println("队列的peek是:");
System.out.println(myQueue.peek());
System.out.println("队列的长度是");
System.out.println(myQueue.size());
myQueue.poll();
System.out.println("队列的peek是:");
System.out.println(myQueue.peek());
myQueue.poll();
myQueue.poll();
myQueue.poll();
System.out.println("队列的长度是");
System.out.println(myQueue.size());
System.out.println("队列是否为空:");
System.out.println(myQueue.isEmpty());
}
}
运行结果
顺序表实现队列及其基本操作
分析
入队从队尾入,出队从头出,队满就是tail = capacity,队空就是head = tail = 0;入队一次,tail++;tail始终代表下一个入队元素的下标;出队就让head后移一位即head++。
入队:
出队:
源码
/**
* user:ypc;
* date:2021-05-02;
* time: 15:05;
*/
class MyOrderQueue{
public int[] values;
public int capacity = 0;
public int head = 0;
public int tail = 0;
public MyOrderQueue(int capacity) {
this.values = new int [capacity];
this.capacity = capacity;
}
public Boolean enQueue(int value) {
if (isFull()) {
return false;
}
this.values[this.tail] = value;
this.tail++;
return true;
}
public int deQueue() throws UnsupportedOperationException{
if (empty()) throw new UnsupportedOperationException("队列为空");
int result = this.values[this.head];
this.head++;
return result;
}
public int getHead() throws UnsupportedOperationException{
if (empty()) throw new UnsupportedOperationException("队列为空");
return this.values[this.head];
}
public int getTail() throws UnsupportedOperationException{
if (empty()) throw new UnsupportedOperationException("队列为空");
return this.values[this.tail];
}
public boolean isFull(){
return this.tail == this.capacity;
}
public boolean empty(){
return this.head == this.tail;
}
}
class MyQueueTest{
public static void main(String[] args) {
System.out.println("顺序队列:");
MyOrderQueue myOrderQueue = new MyOrderQueue(5);
myOrderQueue.enQueue(1);
myOrderQueue.enQueue(2);
myOrderQueue.enQueue(3);
myOrderQueue.enQueue(4);
System.out.println("顺序队列是否满了");
System.out.println(myOrderQueue.isFull());
myOrderQueue.enQueue(5);
System.out.println("顺序队列是否满了");
System.out.println(myOrderQueue.isFull());
System.out.println("顺序队列是否为空:");
System.out.println(myOrderQueue.empty());
System.out.println("顺序队列的peek是:");
System.out.println(myOrderQueue.getHead());
myOrderQueue.deQueue();
System.out.println("顺序队列的peek是:");
System.out.println(myOrderQueue.getHead());
myOrderQueue.deQueue();
myOrderQueue.deQueue();
// myOrderQueue.deQueue();
System.out.println("顺序队列是否为空:");
System.out.println(myOrderQueue.empty());
System.out.println("=====================================");
}
}
运行结果
实现循环队列及其基本操作
分析
为什么要有循环队列?
可以有效的利用资源。用数组实现队列时,如果不移动,随着数据的不断读写,会出现假满队列的情况。即尾数组已满但头数组还是空的;循环队列也是一种数组,只是它在逻辑上把数组的头和尾相连,形成循环队列,当数组尾满的时候,要判断数组头是否为空,不为空继续存放数据。
之前的情况是👇,经过一系列入队和出队操作后变成了这样,如果再要入队的话,rear入完之后,rear++,然后变成了6,也就是队满了,不能入了。
但是我们也看到队的前三个元素出队之后,还有空余的位置,也就是不能让rear++了,让rear变成rear的下一个,当然,front和rear的位置不可能一直入上所示。同理front也是一样的。
循环队列就是入队时,判满,在入队赋值之后,让this.rear = (this.rear+1)%this.elem.length;上图在rear=5,入队一个值,rear= (5+1)% 6 =0,就可以在0号下标继续入队了,在其它位置也是一样的。出队也是同理,判空,然后this.front = (this.front+1)%this.elem.length;
什么时候队为空呢?也就是this.rear == this.front 的时候为空。
那么如何判满呢?不可能继续使用this.rear == this.front 来判满了,循环队列的判满是:if((this.rear+1)%this.elem.length==this.front)return true;
也就是让rear的下一个是不是front来判断是否满了,牺牲掉了一个队列的位置。
判空:
判满:
源码
class MyCircularQueue {
public int []elem;
public int front;
public int rear;
public MyCircularQueue(int k) {
this.elem = new int[k+1];
}
public boolean enQueue(int value) {
if(isFull()) return false;
this.elem[rear] = value;
this.rear = (this.rear+1)%this.elem.length;
return true;
}
public boolean deQueue() {
if(isEmpty())return false;
this.front = (this.front+1)%this.elem.length;
return true;
}
public int Front() {
if(isEmpty())return -1;
return this.elem[this.front];
}
public int Rear() {
if(isEmpty())return -1;
if(this.rear==0)return this.elem[this.elem.length-1];
return this.elem[this.rear-1];
}
public boolean isEmpty() {
if(this.rear == this.front)return true;
return false;
}
public boolean isFull() {
if((this.rear+1)%this.elem.length==this.front)return true;
return false;
}
}
class MyQueueTest{
public static void main(String[] args) {
MyCircularQueue myCircularQueue = new MyCircularQueue(4);
myCircularQueue.enQueue(1);
myCircularQueue.enQueue(2);
myCircularQueue.enQueue(3);
myCircularQueue.enQueue(4);
System.out.println("循环队列是否为空:");
System.out.println(myCircularQueue.isEmpty());
System.out.println("循环队列的peek是:");
System.out.println(myCircularQueue.Front());
System.out.println("循环队列的长度是");
System.out.println(myCircularQueue.rear);
myCircularQueue.deQueue();
System.out.println("循环队列的peek是:");
System.out.println(myCircularQueue.Front());
myCircularQueue.deQueue();
myCircularQueue.deQueue();
myCircularQueue.deQueue();
System.out.println("循环队列是否为空:");
System.out.println(myCircularQueue.isEmpty());
}
}
运行结果
与链表实现队列和顺序表实现队列比较
用队列实现栈
分析
使用两个队列,入栈时向一个队列入,出栈时将有元素的队列出size()-1个元素到另一个队列,最后返回原来队列的poll(),即就是栈的顶。
源码
/**
* user:ypc;
* date:2021-05-02;
* time: 15:04;
*/
class MyStackAchieveByQueue {
Queue<Integer> q1;
Queue<Integer> q2;
/** Initialize your data structure here. */
public MyStackAchieveByQueue() {
q1 = new LinkedList<>();
q2 = new LinkedList<>();
}
/** Push element x onto stack. */
public void push(int x) {
if(!q1.isEmpty())q1.offer(x);
else if(!q2.isEmpty()) q2.offer(x);
else q1.offer(x);
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
int size1 = q1.size();
int size2 = q2.size();
if(empty()) return -1;
if(!q1.isEmpty()){
int ret = -1;
for (int i = 0; i < size1-1; i++) {
ret = q1.poll();
q2.offer(ret);
}
return q1.poll();
}
else{
int ret = -1;
for (int i = 0; i < size2-1; i++) {
ret = q2.poll();
q1.offer(ret);
}
return q2.poll();
}
// return -1;
}
/** Get the top element. */
public int top() {
int size1 = q1.size();
int size2 = q2.size();
if(empty()) return -1;
if(!q1.isEmpty()){
int ret = -1;
for (int i = 0; i < size1; i++) {
ret = q1.poll();
q2.offer(ret);
}
return ret;
}
else{
int ret = -1;
for (int i = 0; i < size2; i++) {
ret = q2.poll();
q1.offer(ret);
}
return ret;
}
//return -1;
}
/** Returns whether the stack is empty. */
public boolean empty() {
return q1.isEmpty()&&q2.isEmpty();
}
}
运行结果
用栈实现队列
分析
使用两个栈s1,s2,放元素时往s1放,将s1的元素放入s2,取的时候取s2的顶。
源码
class MyQueueAchieveByStack {
/** Initialize your data structure here. */
Stack<Integer> s1 ;
Stack<Integer> s2 ;
/** Initialize your data structure here. */
public MyQueueAchieveByStack() {
s1 = new Stack<>();
s2 = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
s1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if(empty())return -1;
if(s2.empty()){
while(!s1.empty())
s2.push(s1.pop());
}
return s2.pop();
}
/** Get the front element. */
public int peek() {
if(empty())return -1;
if(s2.empty()){
while(!s1.empty())
s2.push(s1.pop());
}
return s2.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return s1.empty()&&s2.empty();
}
}
运行结果
集合框架
Java 集合框架 Java Collection Framework ,又被称为容器 container ,是定义在 java.util 包下的一组接口 interfaces 和其实现类 classes 。其主要表现为将多个元素 element 置于一个单元中,用于对这些元素进行快速的增删查改。
Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了三个子接口:List、Set、Queue(Java5新增的队列),因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。
为什么要使用集合框架呢?肯定是方便啊,不用自己每次写。
集合中的栈和队列及其使用
栈:
队列:
import java.util.LinkedList;
import java.util.Stack;
import java.util.Queue;
/**
* user:ypc;
* date:2021-05-03;
* time: 20:44;
*/
public class TestDemo2 {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
Queue<Integer> queue = new LinkedList<>();
stack.push(1);
stack.push(2);
stack.push(3);
queue.offer(1);
queue.offer(2);
queue.offer(3);
System.out.println(queue.poll());
System.out.println(queue.peek());
System.out.println(queue.isEmpty());
System.out.println(stack.pop());
System.out.println(stack.empty());
System.out.println(stack.size());
System.out.println(stack.search(5));
}
}