栈和队列
一、栈
1.概念
栈:一种特殊的线性表,只允许在固定的一端进行插入和删除数据操作。进行数据插入和删除操作的一端称为栈 顶,另一端称为栈底。
栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
2.栈的方法:
3.栈的实现
(1) 使用顺序表实现栈,采用尾插法及尾删法
入栈时将数据元素放入顺序表的最后一个位置,
出栈时将顺序表的最后一个元素出栈
class MyStack {
public int[] elem;
public int top;//栈顶指针
public MyStack(){
this.elem = new int[10];
}
//入栈
public void push(int data){
if(isFull()){
return;
}
this.elem[top++] = data;
}
public boolean isFull(){
if(this.top == this.elem.length){
return true;
}
return false;
}
//出栈-int保存出栈的元素
public int pop(){
//int num = this.elem[--this.top];
if(isEmpty()){
return -1;
}
int num = this.elem[this.top - 1];
this.top--;
return num;
}
public boolean isEmpty(){
if(this.top == 0){
return true;
}
return false;
}
//得到栈顶元素
public int peek(){
if(isEmpty()){
return -1;
}
int num = this.elem[this.top-1];
return num;
}
//获取长度
public int size(){
return this.top;
}
//获取最小元素
public int getMin() {
int min = this.elem[top-1];
for(int i = top - 1; i >= 0;i--){
if (min > elem[i]){
min = elem[i];
}
}
return min;
}
}
(2)使用单链表实现栈,采用头插法及头删法
import java.util.LinkedList;
import java.util.Stack;
class Node{
Node next;
int val;
}
class Mystack1{
public Node head;
public LinkedList<Integer> linkedList;
public Mystack1(){
linkedList = new LinkedList<>();
}
public void push(int x){
Node node = new Node();
node.val = x;
if(head == null){
head = node;
}
node.next = head;
head = node;
}
public int pop(){
if(head == null){
return -1;
}
return head.val;
}
}
public class Test {
public static void main(String[] args) {
Mystack1 mystack1 = new Mystack1();
mystack1.push(2);
mystack1.push(3);
System.out.println(mystack1.pop());//3
}
}
(3)用队列实现栈:
需要两个队列,栈顶相当于对列的队尾
入栈:
若为第一次入栈,则指定入到一个队列中
若不是第一次入栈,则入到不为空的队列中
出栈:
1. 将不为空的队列中的数据留一个出栈,放到为空的另一个队列中
2. 将留下的一个数据出栈,这个数据就是栈的栈顶元素
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
//用队列实现栈,需要两个队列。
class Mystack {
public Queue<Integer> queue1;
public Queue<Integer> queue2;
public Mystack() {
this.queue1 = new LinkedList<>();
this.queue2 = new LinkedList<>();
}
/*
入栈:若为第一次入栈,则指定入到一个队列中
若不是第一次入栈,则入到不为空的队列中
*/
public void push(int x){
if(!queue1.isEmpty()){
queue1.offer(x);
}else if(!queue2.isEmpty()){
queue2.offer(x);
}else{
queue1.offer(x);
}
}
/*
出栈:将不为空的队列中数据留一个出栈,放入到为空的另一个队列中
将留下的数据出栈保留,获得栈顶元素
*/
public int pop(){
if(queue1.isEmpty()&&queue2.isEmpty()){
return -1;
}
int oldData = 0;
if(!queue1.isEmpty()){
int size = queue1.size();
for(int i = 0;i< size-1;i++){
queue2.offer(queue1.poll());
}
oldData = queue1.poll();
}else if(!queue2.isEmpty()){
int size = queue2.size();
for(int i = 0;i<size-1;i++){
queue1.offer(queue2.poll());
}
oldData = queue2.poll();
}
return oldData;
}
//获取栈顶元素
public int top() {
if (queue1.isEmpty() && queue2.isEmpty()) {
return -1;
}
int oldData = 0;
if (!queue1.isEmpty()) {
int size = queue1.size();
for (int i = 0; i < size; i++) {
oldData = queue1.poll();
queue2.offer(oldData);
}
} else if (!queue2.isEmpty()) {
int size = queue2.size();
for (int i = 0; i < size; i++) {
oldData = queue2.poll();
queue1.offer(oldData);
}
}
return oldData;
}
}
public class TestDemo1 {
public static void main(String[] args) {
Mystack mystack = new Mystack();
mystack.push(1);
mystack.push(2);
mystack.push(-1);
mystack.push(-3);
mystack.push(5);
System.out.println(mystack.pop());//5
System.out.println(mystack.top());//-3
System.out.println(mystack.pop());//-3
System.out.println(mystack.pop());//-1
System.out.println(mystack.top());//2
}
}
二、 队列
1.概念
队列:只允许在一端进行插入数据操作,在另一端删除数据的特殊线性表。
队列具有先进先出的出FIFO(First In First Out)
入队列:进行插入操作的一端称为队(Tail/Rear)
出队列:进行删除操作的一端称为队(Head/Front)
栈的方法:
2.队列的实现
用顺序表实现队列,因为出队时需要从顺序表的头进行出队,这样的话,顺序表头之后的元素均要向前移,效率太差。
(1) 用单链表实现队列
尾插法:需要记录单链表的尾巴,不然每次插入时要去找尾巴,耗费较大
头出法:将单链表的头节点后即可
class Node {
public int data;
public Node next;
public Node(int data){
this.data = data;
}
}
public class MyQueue {
public Node head;
public Node tail;
public int useSize;
//入队
public void offer(int data){
Node node = new Node(data);
//第一次入队
if(this.head == null){
this.head = node;
this.tail = node;
} else {
this.tail.next = node;
this.tail = this.tail.next;
}
this.useSize++;
}
//出队
public int poll(){
if(this.head == null){
return -1;
}
int oldData = this.head.data;
this.head = this.head.next;
this.useSize--;
return oldData;
}
//得到队头元素不删除
public int peek(){
if(this.head == null){
return -1;
}
int oldData = this.head.data;
return oldData;
}
//长度
public int size(){
return this.useSize;
}
}
(2) 用栈实现队列:
需要两个栈,定义stack栈和stackTmp;
入队:默认入到stack栈中
出队:将stack栈中的所有元素出栈,放入到stackTmp栈中,将stackTmp栈的栈顶元素出栈
class MyQueue {
public Stack<Integer> stack;
public Stack<Integer> stackTmp;
public MyQueue() {
stack = new Stack<>();
stackTmp = new Stack<>();
}
//入队
public void push(int x) {
stack.push(x);
}
//出队
public int pop() {
if(stackTmp.empty()) {
//将stack 栈当中的所有元素全部倒入 stackTmp;
while (!stack.empty()) {
stackTmp.push(stack.pop());
}
}
if(!stackTmp.empty()) {
//将stackTmp 的栈顶元素弹出-》出栈stackTmp.pop()
return stackTmp.pop();
}
return -1;
}
//获取对头元素
public int peek(){
if(stackTmp.empty()) {
//将stack 栈当中的所有元素全部倒入 stackTmp;
while (!stack.empty()) {
stackTmp.push(stack.pop());
}
}
if(!stackTmp.empty()) {
//将stackTmp 的栈顶元素弹出-》出栈stackTmp.pop
return stackTmp.peek();
}
return -1;
}
//判断队列是否为空
public boolean empty() {
if(stack.empty() && stackTmp.empty()) {
return true;
}
return false;
}
}
循环队列
使用数组来实现循环队列
定义循环对列的头为head,尾巴为rear;
判断循环队列的空:当循环对列的head = rear时,为空
判断循环队列为满:保留一个位置不放元素,当 rear+1 = head 时为满
public class MyQueueCircle {
public int[] elem;
public int front;
public int rear;
public MyQueueCircle(){
this.elem = new int[10];
this.front = 0;
this.rear = 0;
}
//判断是否为满
public boolean isFull(){
if((this.rear+1)%this.elem.length == this.front){
return true;
}
return false;
}
//入队
public boolean enQueue(int value) {
if(isFull()){
return false;
}
this.elem[this.rear] = value;
this.rear = (this.rear+1)%this.elem.length;
return true;
}
//判断是否为空
public boolean isEmpty(){
if(this.front == this.rear){
return true;
}
return false;
}
//出队
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 elem[front];
}
//得到队尾元素
public int Rear() {
if(isEmpty()){
return -1;
}
int index = 0;
if(this.rear == 0){
index = this.elem.length - 1;
} else {
index = this.rear - 1;
}
return this.elem[index];
}
}
双端队列
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。