数据结构——栈和队列

本文介绍了栈和队列这两种重要的数据结构,详细阐述了它们的概念——栈遵循后进先出(LIFO)原则,队列遵循先进先出(FIFO)原则。文章提供了Java中栈和队列的使用示例,包括数组和链表的实现方式,并讨论了如何使用栈模拟队列以及队列模拟栈的面试题解法。
摘要由CSDN通过智能技术生成

栈和队列

1.栈

1.1基本介绍

1.1.1 概念

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据在栈顶。

1.1.2 栈的使用与方法

在这里插入图片描述
使用:

public static void main(String[] args) {
Stack<Integer> s = new Stack();
s.push(1);
s.push(2);
s.push(3);
s.push(4);
System.out.println(s.size()); // 获取栈中有效元素个数---> 4
System.out.println(s.peek()); // 获取栈顶元素---> 4
s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
if(s.empty()){
System.out.println("栈空");
}else{
System.out.println(s.size());
}
}

1.2 栈的模拟实现

注意:以下代码采用数组实现顺序栈。
栈还可采用单链表实现链式栈此时注意时间复杂度问题,或者双向链表实现(例如LinkedList,可以当作链式栈使用)

LinkedList < Integer > stack = new LinkeedList<>();
还拥有LinkedList的方法。

package demo5;

import java.util.Arrays;
/**
 * @author zq
 * 用数组实现栈
 */
public class MyStack {
    public int[] elem;
    public  int usedSize;

    public MyStack() {
        this.elem = new int[10];
    }
    //压栈方法
    public void push(int val){
        if (isFull()) {
            //扩容
            Arrays.copyOf(elem,2*elem.length);
        }
            elem[usedSize++] = val;
    }
    //判断是否满
    public boolean isFull(){

        return usedSize == elem.length;
    }
    //获取栈顶元素
    public int peek(){
        //判断栈是否为空
        if(usedSize==0){
            throw new RuntimeException("栈为空,无法获取栈顶元素");
        }
        return elem[usedSize-1];
    }
    //出栈
    public int pop(){
        int e = peek();
        usedSize--;
        return e;
    }
}

2.队列

2.1基本介绍

2.1.1 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 。
入队列:进行插入操作的一端称为队尾(Tail/Rear)。
出队列:进行删除操作的一端称为队头
(Head/Front)。

2.1.2 队列的使用与方法

在Java中,Queue是接口,底层通过链表实现。
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
使用方法如下:

public static void main(String[] args) {
Queue<Integer> q = new LinkedList<>();
q.offer(1);
q.offer(2);
q.offer(3);
q.offer(4);
q.offer(5); // 从队尾入队列
System.out.println(q.size());
System.out.println(q.peek()); // 获取队头元素
q.poll();
System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
if(q.isEmpty()){
System.out.println("队列空");
}else{
System.out.println(q.size());
}
}

相关方法:
在这里插入图片描述

2.2 队列的实现

2.2.1 链式队列

注意:以下代码使用单链表(采用头插、头删法保证了时间复杂度为O(1))实现,定义了一个 last 作为尾节点的引用,用于删除尾节点。还可采用双向链表。

代码如下

package demo5;
import java.util.EmptyStackException;
/**
 * @author zq
 * 单链表实现队列
 * 保证时间复杂度为O(1)
 * 只能头插和头删
 */
public class MyQueue {
    //单链表节点
    static class Node{
        public int val;
        public Node next;
        public Node(int val){

            this.val = val;
        }
    }
    Node head = null;//头节点的应用
    Node last = null;//尾节点的引用(便于尾部删除)
    public int usedSize = 0;
    //入队
    public void offer(int data){
        Node node = new Node(data);
        if (head == null){
            head = node;
            last = node;
        }else {
            head.next = node;
            last = node;
            usedSize++;
        }
    }
    public int  poll(){
        if (empty()){
            return -1;
        }
        int ret = head.val;
        head = head.next;
        usedSize--;
        return ret;

    }
    //判断队列是否为空
    public boolean empty(){
        return usedSize == 0;
    }
    public int peek(){
        if (empty()){
            return -1;
        }
        return head.val;
    }
}

2.2.2 顺序队列(循环队列)

采用数组实现
注意:如图数组实现出现的问题,数组没满但是加不进去。
在这里插入图片描述
引出循环队列
在这里插入图片描述
rear和front相遇时出现情况:可能满也可能空。
在这里插入图片描述
解决办法:
1.加usedSize判断是否满
2.牺牲空间,如图此时表示满
在这里插入图片描述
3.rear = (rear + 1)%len 实现从0下标到7下标或者7下标到0下标。

代码实现
题目:设计循环队列

package demo5;

/**
 * @author zq
 * 采用数组实现,利用牺牲空间判断是否为满
 */
class MyCircularQueue {
    private int[] elem;
    private int front;
    private int rear;

    public MyCircularQueue(int k) {
        //浪费空间此时应该k+1,才能解决力扣的题
        this.elem = new int[k+1];

    }
/*
入队列
 */
    public boolean enQueue(int value) {
        //1、队列满
        if (isFull()){
            return false;
        }
        //2、队列不满
        elem[rear] = value;
        //防止越界不能一直++
        rear = (rear+1)%elem.length;
        return true;

    }

    public boolean deQueue() {
        if (isEmpty()){
            return false;
        }
        //头出队列,front后移就行,再添加时原位置值会被覆盖
        front = (front+1) % elem.length;
        return true;

    }
//得到队尾元素
    public int Front() {
        if (isEmpty()){
            return -1;
        }
        return elem[front];

    }
//得到队尾元素
    public int Rear() {
        if (isEmpty()){
            return -1;
        }
        //此时不能直接返回rear-1,
        // 如果遇到rear在0位置,返回值应该是7下标的值,则不行
        int index = (rear == 0)? elem.length -1:rear-1;
        return elem[index];

    }

    public boolean isEmpty() {
        return front==rear;

    }

    public boolean isFull() {
        if ((rear+1) % elem.length==front){
            return true;
        }
        return false;

    }
}
2.2.3双端队列(Deque)
2.2.3.1 概念

1.双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。

2.说明元素可以从队头出队和入队,也可以从队尾出队和入队。

注意:Deque是一个接口,使用时必须创建LinkedList的对象。
如:
栈和队列均可使用该接口

Deque < Integer > stack = new ArrayDeque<>();//双端队列的线性(数组)实现
Deque < Integer > queue = new LinkedList<>();//双端队列的链式实现

3 面试题

3.1用栈实现队列

题目:栈实现队列
思路分析:
用两个栈实现
代码实现:

package demo5;

import java.util.Stack;

/**
 * @author zq
 */
public class Myqueue2 {
    private Stack<Integer> stack1;
    private Stack<Integer> stack2;

    public Myqueue2() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();

    }

    public void push(int x) {
        stack1.push(x);
    }

    public int pop() {
        if(empty()){
            return -1;
        }
        if (stack2.empty()){
            while (!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }

    public int peek() {
        if(empty()){
            return -1;
        }
        if (stack2.empty()){
            while (!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }

    public boolean empty() {
        return stack2.empty()&& stack1.empty();
    }
}


3.2用队列实现栈

题目:队列实现栈
思路分析:
一个队列(先进先出)无法实现栈(先进后出)
“入栈”:入不为空的队列,存储完所有栈的元素
“出栈”:不为空的队列,每出一个元素(不是栈顶元素)放入另一个队列(存储不出栈的队列,并保证顺序不变),直到栈顶元素,取出

在这里插入图片描述
代码实现:

package demo5;

import java.util.LinkedList;
import java.util.Queue;

public class MyStack2 {

    private Queue<Integer> qu1;
    private Queue<Integer> qu2;

    public MyStack2() {
        qu1 = new LinkedList<>();
        qu2 = new LinkedList<>();
    }

    public void push(int x) {
        if (!qu1.isEmpty()){
            qu1.offer(x);
        }else if(!qu2.isEmpty()){
            qu2.offer(x);
        }

    }

    public int pop(){
        //栈为空
        if (empty()){
            return -1;
        }
        if (!qu1.isEmpty()){
            int size = qu1.size();
            for (int i = 0; i < size-1; i++) {
               int  val = qu1.poll();
                qu2.offer(val);
            }
            return qu1.poll();
        }else {
            int size = qu2.size();
            for (int i = 0; i < size-1; i++) {
               int val = qu2.poll();
                qu1.offer(val);
            }
            return qu2.poll();
        }

    }

    public int top() {
        //栈为空
        if (empty()){
            return -1;
        }

        if (!qu1.isEmpty()){
            int val = -1;
            int size = qu1.size();
            for (int i = 0; i < size; i++) {
                val = qu1.poll();
                qu2.offer(val);
            }
            return val;
        }else {
            int size = qu2.size();
            int val = -1;
            for (int i = 0; i < size; i++) {
                val = qu2.poll();
                qu1.offer(val);
            }
            return val;
        }

    }

    public boolean empty() {
       return qu2.isEmpty()&&qu1.isEmpty();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值