栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

栈 和 队列

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

栈 和 队列

目录
  • 一、栈
    • 1.1栈的概念
    • 1.2栈的使用
    • 1.3栈的模拟实现
    • 1.4栈的经典题目
    • 1.5栈、虚拟机栈、栈帧
  • 二、队列
    • 2.1队列的概念
    • 2.2队列的使用
    • 2.3队列的模拟实现
      • 2.3.1使用单链表实现模拟实现队列
      • 2.3.2使用数组实现循环队列
    • 2.4双端队列
    • 2.5队列经典面试题

一、栈 1.1栈的概念

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

1.2栈的使用

public static void main(String[] args) {
        Stack s = new Stack();
        s.push(1);
        s.push(2);
        s.push(3);
        s.push(4);
        System.out.println(s.size());
        System.out.println(s.peek());
        s.pop();
        System.out.println(s.pop());
        if(s.empty()){
            System.out.println("栈空");
        }else{
            System.out.println(s.size());
        }
    }

执行结果:

1.3栈的模拟实现
import java.util.Arrays;

public class MyStack {
    int[] array;
    int size;
    public MyStack(){
        this.array = new int[3];
    }
    public int push(int e){
        ensureCapacity();
        this.array[this.size++] = e;
        return e;
    }
    public int pop(){
        if(empty()){
            throw new RuntimeException("栈为空,无法弹出栈顶元素");
        }
        int ret=this.array[this.size-1];
        //this.arry[this.size-1]=null; 当栈中存储的数据类型是引用类型时,要将其置为null
        this.size--;
        return ret;
    }
    public int peek(){
        if(empty()){
            throw new RuntimeException("栈为空,无法获取栈顶元素");
        }
        return array[size-1];
    }
    public boolean empty(){
        return 0 == this.size;
    }
    public int size(){
        return this.size;
    }
    private void ensureCapacity(){
        if(this.size == this.array.length){
            this.array = Arrays.copyOf(array, size*2);
        }
    }
}

使用:

public class Test4 {

    public static void main(String[] args) {
        MyStack myStack=new MyStack();
        myStack.push(1);
        myStack.push(2);
        myStack.push(3);
        myStack.push(4);
       int x= myStack.pop();
        System.out.println(x);
        x= myStack.pop();
        System.out.println(x);
        x= myStack.pop();
        System.out.println(x);
        x= myStack.peek();
        System.out.println(x);
        x= myStack.pop();
        System.out.println(x);
    }
}

执行结果:

1.4栈的经典题目

(1)
答案:C

(2)使用栈逆序打印链表

源码:

 Stack s = new Stack<>();
      // 将链表中的结点保存在栈中
        Node cur = head;
        while(null != cur){
            s.push(cur);
            cur = cur.next;
        }
      // 将栈中的元素出栈
        while(!s.empty()){
            System.out.print(s.pop().val + " ");
        }

(3) 中缀表达式转后缀表达式

将中缀表达式转成后缀表达式的目的是什么呢?

便于使用栈来计算表达式的值

(4) 逆波兰表达式

OJ链接
思路:就是上面的使用栈求后缀表达式的值

源码:

 public int evalRPN(String[] tokens) {

                Stack stack=new Stack<>();
        for (String str:tokens) {
         
            if(str.equals("+")||str.equals("-")||str.equals("*")||str.equals("/")){
               int s1=stack.pop();
              int s2=stack.pop();
                switch (str){
                    case "+":
                        stack.push(s2+s1);
                        break;
                    case "-":
                        stack.push(s2-s1);
                        break;
                    case  "*":
                        stack.push(s2*s1);
                        break;
                    case "/":
                        stack.push(s2/s1);
                        break;
                    default:
                        break;
                }
            }else {
                stack.push(Integer.parseInt(str));
            }
        }

  return stack.pop();
    }

(5) 有效的括号
OJ链接

源码:

 public boolean isValid(String s) {
    Stack stack=new Stack<>();
    char[] str=s.toCharArray();
    for(int i=0;i
         
        if(str[i]=='('||str[i]=='{'||str[i]=='['){
            stack.push(str[i]);
        }else {
            if(stack.empty()){
                return false;
            }
            char tmp=stack.peek();
            if(str[i]==')'&&tmp=='('||str[i]=='}'&&tmp=='{'||str[i]==']'&&tmp=='['){
                stack.pop();
            }else {
                return false;
            }
            
        }
        
    }
        if(!stack.empty()){
            return false;
        }
       return true;

}

(6)栈的压入、弹出序列
OJ链接

源码:

  public boolean IsPopOrder(int [] pushA,int [] popA) {
        if(pushA.length==0||popA.length==0){
            return  false;
        }
        Stack stack=new Stack<>();
        int j=0;
        for (int i = 0; i < pushA.length; i++) {
              stack.push(pushA[i]);
                while(!stack.empty()&&stack.peek()==popA[j]&&j< popA.length){
                    stack.pop();
                    j++;
                }
        }
        return stack.empty();
    }

【注意】:

1.5栈、虚拟机栈、栈帧

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

虚拟机栈: 主管Java程序的运行,它保存方法的局部变量(8 种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回。

栈帧:函数调用时,系统为其分配的一块空间

二、队列 2.1队列的概念

队列:

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

2.2队列的使用

在Java中,Queue只是一个接口,实现这个接口的类是LinkList,所以其底层是一个双向链表

Queue 接口中的方法

add :入队列
offer:入队列
remove:出队列
poll:出队列
peek:获取队头元素

【注意】:

  1. Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

  2. LinkedList不仅可以当作链表来使用,也可以当作队列,又因为它实现了Deque(双端队列),所以还可以当作栈来使用。

2.3队列的模拟实现 2.3.1使用单链表实现模拟实现队列

使用单向链表实现队列:

增加一个指向链表结尾的引用last
从链表的结尾入队,链表的头出队 (入队和出队的时间复杂度都是O(1))

为什么不是从头入队,从尾巴出队呢??
这样的话,出队的时间复杂度是O(N)

public class MyLinkedList {
    class Node{
        int val;
        public  Node next;
        public Node(int val) {
            this.val = val;
        }
    }
    public Node head;
    public Node last;
    public int usedSize;//队列中元素个数
   
    public void offer(int val){
        Node node=new Node(val);
        if(this.head==null){
            this.head=this.last=node;
        }else{
            this.last.next=node;
            this.last=node;
        }
        this.usedSize++;
    }
    
    public int poll(){
        if(empty()){
            throw new RuntimeException("队列为空,不能出队");
        }
        int val= this.head.val;
        this.head=this.head.next;
        //如果队列中只有一个节点,将last也要置为null;
        if(this.head==null){
            this.last=null;
        }
        this.usedSize--;
       return val;
    }
    
    public int peek(){
        if(empty()){
            throw new RuntimeException("队列为空,无法获取队头元素");
        }
        int val= this.head.val;
        return val;
    }
    
    public int size(){
          return this.usedSize;
    }
    
    public boolean empty(){
        return this.usedSize==0;
    }
}

2.3.2使用数组实现循环队列

数组下标循环的技巧:

  1. 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length
  2. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length

对于数组实现队列时,还需要解决下面这个问题:


(1)通过增加usedSize属性记录数据的个数
每增加一个数据usedSize++
每删除一个数据usedSize–
(2)空一个位置

该方法相对于第一种方法,浪费了一个空间
(3) 使用标记

下面使用空一格的方式实现循环对垒
循环队列的属性:

    private int[] elem;
    public  int front;//标加队列的头
    public int rear;//标记队列的尾

构造方法:用来初始化数组

public MyCircularQueue(int k) {

    elem=new int[k];
}

普通方法:

(1)判断循环队列是否满

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

(2)入队
在入队之前需要判断队列是否满
将数据放在rear下标对应的位置
rear=(rear+1)%elem.length

【注意】:
不能是rear++,否则会发生数组越界

 public boolean enQueue(int value) {
        if(isFull()){
            return false;
        }
           this.elem[this.rear]=value;
           this.rear=(this.rear+1)% elem.length;
             return true;
        }

(3)判断队列是否为空

        public boolean isEmpty() {
        if(this.front==this.rear){
            return true;
        }
            return false;
        }

(4)出队
在出队前需要判断队列是否为空
front=(front+1)%elem.length

        public boolean deQueue() {

                if(isEmpty()){
                    return false;
                }
                    this.front=(this.front+1)%this.elem.length;
                    return true;
                }

(5)获取队头元素

  public int Front() {
            if(isEmpty()){
                return -1 ;
            }
            return this.elem[front];
        }

(6)获取队尾元素
当rear==0时需做特殊处理,返回elem.length-1位置的值

  public int Rear() {
            if(isEmpty()){
                return -1 ;
            }
            int index=(rear==0)?this.elem.length-1:rear-1;
            return this.elem[index];
        }
2.4双端队列

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。
那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
Deque是一个接口,使用时必须创建LinkedList的对象

2.5队列经典面试题

(1) 用队列实现栈
OJ链接

思路:

源码:

class MyStack {

private  Queue q1;
private  Queue q2;


    public MyStack() {
q1=new LinkedList<>();
q2=new LinkedList<>();


    }
    
    public void push(int x) {
        if(!q1.isEmpty()){
            q1.offer(x);
        }else if(!q2.isEmpty()){
            q2.offer(x);
        }else{
            q1.offer(x);
        }
    }
    
    public int pop() {
if(empty()){
    return -1;
}
if(!q1.isEmpty()){
    int size=q1.size();
    for(int i=0;i
     q2.offer(q1.poll());
    }
    return q1.poll();
}else {
    int size=q2.size();
    for(int i=0;i
     q1.offer(q2.poll());
    }
    return q2.poll();
}
}

    
    public int top() {
if(empty()){
    return -1;
}

int tmp=-1;
if(!q1.isEmpty()){
    int size=q1.size();
    for(int i=0;i
    tmp=q1.poll();
     q2.offer(tmp);
    }
    return tmp;
}else {
    int size=q2.size();
    for(int i=0;i
    tmp=q2.poll();
     q1.offer(tmp);
    }
    return tmp;
}
    }
    
    public boolean empty() {
if(q1.isEmpty()&&q2.isEmpty()){
    return true;
}
return false;
    }
}

(2)用栈实现队列
OJ链接

思路:


源码:

import java.util.Stack;

class MyQueue {

    private Stack s1;
    private Stack s2;
    public MyQueue() {
        s1=new Stack<>();
        s2=new Stack<>();
    }
    
    public void push(int x) {
        s1.add(x);
        
    }
    
    public int pop() {
      if(empty()){
            return -1;
        }
       if(s2.isEmpty()){
           while (!s1.isEmpty()){
               s2.add(s1.pop());
           }
       }
      return s2.pop();
    }
    
    public int peek() {
      if(empty()){
            return -1;
        }
        if(s2.isEmpty()){
            while (!s1.isEmpty()){
                s2.add(s1.pop());
            }
        }
        return s2.peek();
    }
    
    public boolean empty() {
if(s1.isEmpty()&&s2.empty()){
    return true;
}
return false;
    }
}

(3)实现一个最小栈
OJ链接


源码:

import java.util.Stack;

class MinStack {

    
    private Stack s1;
    private Stack minStack;
    public MinStack() {
        s1=new Stack<>();
        minStack=new Stack<>();
    }
    
    public void push(int val) {

        s1.push(val);
        if(minStack.isEmpty()){
            minStack.push(val);
        }else{
            int cur=minStack.peek();
            if(val<=cur){
                minStack.push(val);
            }
        }

    }
    
    public void pop() {

        if(s1.isEmpty()){
            return ;
        }
        int cur=s1.peek();
        s1.pop();
        if(cur==minStack.peek()){
            minStack.pop();
        }

    }
    
    public int top() {
        if(s1.isEmpty()){
            return -1 ;
        }
        return s1.peek();
    }
    
    public int getMin() {
return minStack.peek();
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/888361.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号