java中栈和队列

一、栈(stack)

1、栈的创建和使用

栈是Vector的一个子类,标准:后进先出
创建:Stack st = new Stack();//Integer 类型的栈
方法
1 boolean empty()
测试堆栈是否为空。
2 Object peek( )
查看堆栈顶部的对象,但不从堆栈中移除它。
3 Object pop( )
移除堆栈顶部的对象,并作为此函数的值返回该对象。
4 Object push(Object element)
把项压入堆栈顶部。
5 int search(Object element)
返回对象在堆栈中的位置,以 1 为基数。

2、应用

子程序的调用:在跳往子程序前,会将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
处理递归调用:和子程序的调用类似,只是出了存储下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
逆序输出。
表达式的转换[中缀表达式转后缀表达式]与求值
二叉树的遍历。
图的深度优先(depth-first)搜索法。
数制转换:通过求余法,每次将余数进栈,最后将所有余数出栈即可。
括号匹配校验
迷宫求解
实现递归-汉诺塔

3、扩展(单调栈)

单调栈: 顾名思义就是在入栈时遵循单调原则,可以求出一个元素向左(或向右)所能扩展到的最大长度,并不是说在这一段区间内是单调的,而是保证在该区间内该元素一定是最大或最小;
实战例子
下一个更大元素 II
描述:给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
题解

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        int[] ret = new int[n];
        Arrays.fill(ret, -1);
        Deque<Integer> stack = new LinkedList<Integer>();
        for (int i = 0; i < n * 2 - 1; i++) {
            while (!stack.isEmpty() && nums[stack.peek()] < nums[i % n]) {
                ret[stack.pop()] = nums[i % n];
            }
            stack.push(i % n);
        }
        return ret;
    }
}

二、队列

队列是一种特殊的线性表,遵循的原则就是**“先入先出**”

1.队列的基本类型

普通队列(一端进另一端出):
Queue queue = new LinkedList()或Deque deque = new LinkedList()
双端队列(两端都可进出)
Deque deque = new LinkedList()
堆栈
Deque deque = new LinkedList()
注意:Java堆栈Stack类已经过时,Java官方推荐使用Deque替代Stack使用。Deque堆栈操作方法:push()、pop()、peek()

2.普通队列的操作

压入元素(添加):add()、offer()
相同:未超出容量,从队尾压入元素,返回压入的那个元素。
区别:在超出容量时,add()方法会对抛出异常,offer()返回false

弹出元素(删除):remove()、poll()
相同:容量大于0的时候,删除并返回队头被删除的那个元素。
区别:在容量为0的时候,remove()会抛出异常,poll()返回false

获取队头元素(不删除):element()、peek()
相同:容量大于0的时候,都返回队头元素。但是不删除。
区别:容量为0的时候,element()会抛出异常,peek()返回null。

3、双端队列

Deque接口扩展(继承)了 Queue 接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从 Queue 接口继承的方法完全等效于 Deque 方法,如下表所示:
在这里插入图片描述
双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法,如下表所示:
在这里插入图片描述

4、扩展(优先队列)

优先队列PriorityQueue是Queue接口的实现,可以对其中元素进行排序,可以放基本数据类型的包装类(如:Integer,Long等)或自定义的类对于基本数据类型的包装器类,优先队列中元素默认排列顺序是升序排列。
常用方法:

peek()//返回队首元素
poll()//返回队首元素,队首元素出队列
add()//添加元素
size()//返回队列元素个数
isEmpty()//判断队列是否为空,为空返回true,不空返回false

使用:
1.队列保存的是基本数据类型的包装类

//自定义比较器,降序排列
static Comparator<Integer> cmp = new Comparator<Integer>() {
      public int compare(Integer e1, Integer e2) {
        return e2 - e1;
      }
    };
public static void main(String[] args) {
        //不用比较器,默认升序排列
        Queue<Integer> q = new PriorityQueue<>();
        q.add(3);
        q.add(2);
        q.add(4);
        while(!q.isEmpty())
        {
            System.out.print(q.poll()+" ");
        }
        /**
         * 输出结果
         * 2 3 4 
         */
        //使用自定义比较器,降序排列
        Queue<Integer> qq = new PriorityQueue<>(cmp);
        qq.add(3);
        qq.add(2);
        qq.add(4);
        while(!qq.isEmpty())
        {
            System.out.print(qq.poll()+" ");
        }
        /**
         * 输出结果
         * 4 3 2 
         */
}

2.队列保存的是自定义类

//矩形类
class Node{
    public Node(int chang,int kuan)
    {
        this.chang=chang;
        this.kuan=kuan;
    }
    int chang;
    int kuan;
}

public class Test {
    //自定义比较类,先比较长,长升序排列,若长相等再比较宽,宽降序
    static Comparator<Node> cNode=new Comparator<Node>() {
        public int compare(Node o1, Node o2) {
            if(o1.chang!=o2.chang)
                return o1.chang-o2.chang;
            else
                return o2.kuan-o1.kuan;
        }
        
    };
    public static void main(String[] args) {
        Queue<Node> q=new PriorityQueue<>(cNode);
        Node n1=new Node(1, 2);
        Node n2=new Node(2, 5);
        Node n3=new Node(2, 3);
        Node n4=new Node(1, 2);
        q.add(n1);
        q.add(n2);
        q.add(n3);
        Node n;
        while(!q.isEmpty())
        {
            n=q.poll();
            System.out.println("长: "+n.chang+" 宽:" +n.kuan);
        }
     /**
      * 输出结果
      * 长: 1 宽:2
      * 长: 2 宽:5
      * 长: 2 宽:3
      */
    }
}

3.优先队列遍历
PriorityQueue的iterator()不保证以任何特定顺序遍历队列元素。若想按特定顺序遍历,先将队列转成数组,然后排序遍历

Queue<Integer> q = new PriorityQueue<>(cmp);
        int[] nums= {2,5,3,4,1,6};
        for(int i:nums)
        {
            q.add(i);
        }
        Object[] nn=q.toArray();
        Arrays.sort(nn);//数组排序
        for(int i=nn.length-1;i>=0;i--)
            System.out.print((int)nn[i]+" ");
        /**
         * 输出结果
         * 6 5 4 3 2 1 
         */

4.比较器升降序说明

Comparator<Object> cmp = new Comparator<Object>() {
        public int compare(Object o1, Object o2) {
            //升序
            return o1-o2;
            //降序
            return o2-o1;
        }
    };

5、补充(Deque)

Deque成为双端队列,是Queue接口的子接口,具体实现类为LinkedList。
Deque可以实现两种数据结构:
若Deque规定从一端进出队列,可以看做栈;若从一端进队,从另一端出队,则可以看做队列。
Deque中的主要方法有:
1.Queue Method:
boolean: add(E e)
作用:将指定的元素插入此队列(如果立即可行且不会违反容量限制),在成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException异常。

boolean: offer(E e)
作用: 将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。
E : remove()
作用: 获取并移除此队列的头。此方法与 poll 唯一的不同在于:此队列为空时将抛出一个异常。

E: poll()
作用: 获取并移除此队列的头,如果此队列为空,则返回 null。

E: element()
作用: 获取,但是不移除此队列的头。此方法与 peek 唯一的不同在于:此队列为空时将抛出一个异常。

E: peek()
作用: 获取但不移除此队列的头;如果此队列为空,则返回 null。

  1. Stack Method:

void: push(E e);
作用: 添加元素到栈顶

E :pop();
作用:返回栈顶元素并且删除栈顶元素。

E: peek();
作用:获取但是不删除栈顶元素。

6.单调队列

例子:剑指 Offer 59 - I. 滑动窗口的最大值
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
算法思路
窗口对应的数据结构为 双端队列 ,本题使用 单调队列 即可解决以上问题。遍历数组时,每轮保证单调队列 dequedeque :
dequedeque 内 仅包含窗口内的元素 \Rightarrow⇒ 每轮窗口滑动移除了元素 nums[i - 1]nums[i−1] ,需将 dequedeque 内的对应元素一起删除。
dequedeque 内的元素 非严格递减 \Rightarrow⇒ 每轮窗口滑动添加了元素 nums[j + 1]nums[j+1] ,需将 dequedeque 内所有 < nums[j + 1]<nums[j+1] 的元素删除。
算法流程:
(1)初始化: 双端队列 dequedeque ,结果列表 resres ,数组长度 nn ;
(2)滑动窗口: 左边界范围 i \in [1 - k, n - k]i∈[1−k,n−k] ,右边界范围 j \in [0, n - 1]j∈[0,n−1] ;
若 i > 0i>0 且 队首元素 deque[0]deque[0] == 被删除元素 nums[i - 1]nums[i−1] :则队首元素出队;
删除 dequedeque 内所有 < nums[j]<nums[j] 的元素,以保持 dequedeque 递减;
将 nums[j]nums[j] 添加至 dequedeque 尾部;
(3)若已形成窗口(即 i \geq 0i≥0 ):将窗口最大值(即队首元素 deque[0]deque[0] )添加至列表 resres ;
(4)返回值: 返回结果列表 resres ;
代码

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        for(int j = 0, i = 1 - k; j < nums.length; i++, j++) {
            // 删除 deque 中对应的 nums[i-1]
            if(i > 0 && deque.peekFirst() == nums[i - 1])
                deque.removeFirst();
            // 保持 deque 递减
            while(!deque.isEmpty() && deque.peekLast() < nums[j])
                deque.removeLast();
            deque.addLast(nums[j]);
            // 记录窗口最大值
            if(i >= 0)
                res[i] = deque.peekFirst();
        }
        return res;
    }
}





评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值