栈和队列的进阶

本文详细介绍了栈和队列这两种数据结构的概念、生活中的例子、Java中的实现以及实际应用。通过实例展示了如何在Java中使用栈和队列,并通过代码实现模拟栈和队列的功能。同时,探讨了栈和递归的关系,以及如何用栈和队列互相转换。此外,还提供了用栈和队列实现括号匹配和逆波兰表达式求值的算法。
摘要由CSDN通过智能技术生成

目录

一.栈

1.什么是栈

2.栈的示意图

 3.栈在生活中的例子

4.栈在Java中的定义和使用

(1)常用方法

 (2)栈的常用操作练习

5.栈的模拟实现

(1)问题分析

(2)代码实现

6.栈的简单应用(习题)

(1)简单的括号匹配

(2)逆波兰表达式求值

7.递归与栈的关系

(1)关系说明

(2)栈,虚拟机栈,栈帧的区别

二.队列

1.队列概念

 2.Java中队列的实现

3.常用方法

 4.队列常用操作练习

5.队列的模拟实现

(1)问题分析

(2)代码实现

6.循环队列

(1)引言

(2)循环队列实现图解

三.栈和队列的转换习题

1.用队列实现一个栈

2.用栈实现一个队列

(1)实现目标

(2)图解

 (3)代码实现

3.实现最小栈

(1)目的


一.栈

1.什么是栈

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

2.栈的示意图

 3.栈在生活中的例子

给枪上膛,或者后吃的东西先吐出来。

4.栈在Java中的定义和使用

栈继承Vector类,底层还是连续的存储空间,和ArrayList底层都是数组。

(1)常用方法

 (2)栈的常用操作练习

import java.util.Stack;

public class StackDemo {
    public static void main(String[] args) {
        Stack<Integer> st = new Stack<>();
        st.push(1);
        st.push(2);
        st.push(3);
        st.push(4);
        System.out.println(st.pop());//4弹出
        System.out.println(st.peek());//查看栈顶元素
        System.out.println(st.size());

    }
}

5.栈的模拟实现

(1)问题分析

首先需要一个数组,然后再需要一个size来判断栈空间是否以满,通过上面分析,可以知道栈理解为对数组进行尾插和尾删的操作。这里还实现了一个动态扩容的方法,每次以原数组2倍的方式进行扩容。

(2)代码实现

import java.util.Arrays;
import java.util.EmptyStackException;

public class MyStack {
    public static void main(String[] args) {
        Stack<String> st = new Stack<>();
        System.out.println(st.isEmpty());
        st.push("111");
        st.push("222");
        st.push("333");
        st.push("444");
        System.out.println(st.getSize());
        System.out.println(st.isEmpty());
        System.out.println(st.peek());
        System.out.println(st.pop());
        System.out.println(st.peek());

        st.pop();
        st.pop();
        st.pop();
        st.pop();
        System.out.println(st.getSize());


    }
}
class Stack<E>{
    E[] array;
   private int size;//统计栈中元素
    public Stack(){
        array= (E[])new Object[2];
    }
    //获取栈中元素的个数
    public int getSize(){
        return size;
    }
    public boolean isEmpty(){
        if(size==0){
            return true;
        }
        return false;
    }
    public E pop(){
    if(size>0){
        size--;
        return array[size];
    }
    throw new EmptyStackException();
    }
    public E push(E ele){
        //确保是否可以添加
        ensureCapacity(size);
        array[size]= ele;
        size++;
        return array[size-1];
    }
    //查看栈顶元素
    public E peek(){
        if(size>0){
            return array[size-1];
        }
        throw new EmptyStackException();//栈为空异常
    }
    //扩容
    private void ensureCapacity(int size){
        if(size==array.length){
            array = Arrays.copyOf(array,size*2);
        }
    }

}

6.栈的简单应用(习题)

(1)简单的括号匹配

问题:给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

匹配的时候 

不符合的情况: 

 代码实现:

    public boolean isValid(String s) {
                //遇到左括号进栈,遇到右括号出栈匹配
                Stack<Character> st = new Stack<>();
                for(int i=0;i<s.length();i++){
                    char ch = s.charAt(i);
                    //判断是否为左括号
                    if(ch=='(' || ch=='[' || ch=='{'){
                        st.push(ch);
                    }
                    //右括号没匹配完栈为空或者左右括号不匹配
                    else if(st.empty()|| !((ch==')' && st.peek()=='(')||
                     (ch==']' && st.peek()=='[')||
                     (ch=='}' && st.peek()=='{'))){
                         return false;
                    }else{
                        st.pop();//匹配,栈顶出元素
                    }
                }
                if(st.empty())//栈空
                return true;
               return false;//左括号多则返回否
    }

(2)逆波兰表达式求值

逆波兰表达式就是后缀表达式,我们平时的在数学中的加减乘除就是一种中缀表达式,为了使计算机可以计算,所有就有了逆波兰表达式,目的就是让计算机好识别。

题目:有效的算符包括 +-*/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

举例:((2 + 1) * 3) = 9    ------》逆波兰表达式: ["2","1","+","3","*"]

我们用代码实现就是将字符串中的逆波兰求出结果

示意图:

 代码实现:

    public int evalRPN(String[] tokens) {
        Stack<Integer> st = new Stack<>();
        for(int i=0;i<tokens.length;i++){
            String tmp=tokens[i];
            //判断字符产是否为算术符
            if(!(tmp.equals("+")||
               tmp.equals("-")||
               tmp.equals("*")||
               tmp.equals("/"))){
                   int in = Integer.parseInt(tmp);
                 st.push(in);//不是就入栈
               }
          
               else{
                   int right = st.pop();//弹出栈顶2个元素
                   int left = st.pop();
                   
                   switch(tmp){//弹出的元素进行计算将结果进行入栈
                       case "+": st.push(left+right);
                            break;
                        case "-":
                         st.push(left-right);
                            break;
                         case "*":
                          st.push(left*right);
                            break;
                          case "/":
                           st.push(left/right);
                            break;

                   }
               }
        }
        return st.peek();//走后栈顶元素为求解结果
    }

7.递归与栈的关系

(1)关系说明

由于Java中存在虚拟机栈,而递归和栈的后进后出关系相同,我们可以将递归转化用栈来进行实现。

(2)栈,虚拟机栈,栈帧的区别

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作

虚拟机栈:是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型

栈帧:每个栈帧都对应一个被调用的方法,可以理解为方法的运行空间

二.队列

1.队列概念

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

图解:

 2.Java中队列的实现

底层是接口,需要借助链表LinkedList来进行实现,而链表又是根据双向链表进行实现的,下面我们会自己实现一个简单的队列

3.常用方法

方法功能
boolean offer(E e)入队列
E poll()出队列
peek()获取队头元素
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空

 4.队列常用操作练习

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

public class QueueDemo {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        System.out.println(queue.size());
        System.out.println(queue.poll());//出队列
        System.out.println(queue.peek());//查看队头元素

    }
}

5.队列的模拟实现

(1)问题分析

队列一个队头标记,一个队尾标记,其中队尾添加相当于一个尾插,队头出队相当于为头删,其中还有一个size来标记队列中有多少个元素,从而进行判断队列是否为空,并且通过双向链表进行实现。

(2)代码实现

public class MyQueue {
    public static void main(String[] args) {
    Queue<String> st=  new Queue<>();
//        System.out.println(st.poll());
        System.out.println(st.getSize());
        st.offer("111");
        st.offer("222");
        st.offer("333");
        System.out.println(st.getSize());
        System.out.println(st.peek());
        System.out.println(st.poll());
        System.out.println(st.peek());
        st.poll();
//        st.poll();
        System.out.println(st.peek());
//        System.out.println(st.poll());

    }
}
//原队列是接口,借助链表来进行实现
class Queue<E>{
    Node<E> first;//队头
    Node<E> last;//队尾
    private int size;
    public int getSize(){
        return size;
    }
    //队尾中插入元素
    public E offer(E ele){
        //链表尾插
        Node<E> newNode= new Node<>(ele);
        if(first==null){
            first=newNode;
            last=newNode;
        }else{
            Node<E> cur=first;
            while(cur.next!=null){
                cur=cur.next;
            }
            cur.next=newNode;
            newNode.prev=cur;
            last=newNode;
        }
        size++;
        return  last.value;
    }
    //队头移除元素
    public E poll(){
        if(first==null){
            return null;
        }
        first=first.next;
        E value= first.value;
        first.prev.next=null;
        first.prev=null;
        size--;
        return value;
    }
    //获取队头元素,不移除
    public E peek(){
        if(first==null){
            throw new RuntimeException("队列为空,无法获取");
        }
        return first.value;
    }
    public boolean isEmpty(){
        return size==0;
    }
}
class Node<E>{
    E value;
    Node<E> prev;
    Node<E> next;
    public Node(E value){
        this.value=value;
    }
}

6.循环队列

(1)引言

虽然Java中是通过链表来进行实现队列,但是还有另一种方法实现队列,那就是数组,由于直接使用数组会导致在删除后导致空间的浪费,所以就有了循环队列,这种方式实现的队列空间利用率是很好的。

(2)循环队列实现图解

 (3)代码实现

class MyCircularQueue {
        int[] arr;//数组
        int count;//有效元素个数
        int front;
        int rear;
        int size;//数组空间大小
    public MyCircularQueue(int k) {
        arr=new int[k];
        size=arr.length;
    }
    //向队尾添加元素
    public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }
        arr[rear%size]=value;
        rear++;
        count++;
        return true;
    }
    //出队列
    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        front++;
        count--;
        return true;

    }

    //获取队头元素
    public int Front() {
        if(isEmpty()){
            return -1;
        }
        return arr[front%size];
    }
    //获取队尾元素
    public int Rear() {
        if(isEmpty()){
            return -1;
        }
        return arr[(rear-1)%size];
    }
    //判空
    public boolean isEmpty() {
        return 0==count;
    }
    //判满
    public boolean isFull() {
        return size==count;
    }
}

三.栈和队列的转换习题

1.用队列实现一个栈

这里使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

 

 代码实现:

class MyStack {
    Queue<Integer> s1 = new LinkedList<>();
    Queue<Integer> s2 = new LinkedList<>();
    
    public MyStack() {

    }
    
    public void push(int x) {
        if(s1.size()!=0){
            s1.offer(x);
        }else if(s2.size()!=0){
            s2.offer(x);
        }else{
            s1.offer(x);
        }
    }
    
    public int pop() {

        int val=0;//保存需要返回的值
        //s1队列为空
        if(!s1.isEmpty()){
            while(s1.size()>1){
                s2.offer(s1.poll());
        }
        val=s1.poll();

        }else {
        //s2队列不为空
            while(s2.size()>1){
                s1.offer(s2.poll());
            }
            val=s2.poll();

        }
            return val;

    }
    
    public int top() {
      int val=0;//保存需要返回的值
        //s2队列为空
        if(!s1.isEmpty()){
            while(s1.size()>1){
                s2.offer(s1.poll());
        }
         val=s1.poll();
        s2.offer(val);

        }else {
        //s2队列不为空
            while(s2.size()>1){
                s1.offer(s2.poll());
            }
            val=s2.poll();
            s1.offer(val);

        }
            return val;
    }
    
    public boolean empty() {
        return s1.size()==0 && s2.size()==0;
    }
}

2.用栈实现一个队列

(1)实现目标

我们使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty

(2)图解

 (3)代码实现

class MyQueue {
    Stack<Integer> st1;//入队列操作
    Stack<Integer> st2;//出队列操作
    public MyQueue() {
        st1=new Stack<>();
        st2=new Stack<>();
        
    }
    //入队列
    public void push(int x) {
        st1.push(x);
    }
    //出队列
    public int pop() {
        if(st2.empty()){
            while(!st1.empty())
            st2.push(st1.pop());
        }
        return st2.pop();
    }
    //插卡队头元素
    public int peek() {
        if(st2.empty()){
            while(!st1.empty())
            st2.push(st1.pop());
        }
        return st2.peek();
    }
    //判断队列是否为空
   
    public boolean empty() {
        return st1.empty() && st2.empty();
    }
}

3.实现最小栈

(1)目的

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

(2)图解

(3 代码实现

class MinStack {
    Stack<Integer> st;
    public MinStack() {
        st = new Stack<>();
    }
    //入栈
    public void push(int val) {
        if(st.empty()){
            st.push(val);
            st.push(val);
        }else{
            int min = getMin();
            if(min>val){
                st.push(val);
                st.push(val);
            }else{
                st.push(min);
                st.push(val);
            }
        }

    }
    //弹出栈顶元素
    public void pop() {
        st.pop();
        st.pop();
    }
    //查看栈顶元素
    public int top() {
        return st.peek();
    }
    //得到最小值
    public int getMin() {
        int t = st.pop();
        int min = st.peek();
        st.push(t);
        return min;
    }
}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值