力扣-栈和队列

一、栈类-Stack类

栈的声明+初始化:Stack s=new Stack<>();
在这里插入图片描述

二、队列-Queue接口

Deque q1= new ArrayDeque<>();
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

重点:
在这里插入图片描述
上面两个方法是ArrayDeque类中的两个方法
方法1:将ArrayDeque类型转化为Object[]类型
方法2:将ArrayDeque类型转化为T[]类型

注意事项:
1、不能将Object[] 转化为String[],转化的话只能是取出每一个元素再转化。java中的强制类型转换只是针对单个对象的,想要偷懒将整个数组转换成另外一种类型的数组是不行的,这和数组初始化时需要一个个来也是类似的。
2、Character[]不能直接强转成char[],需要一个个转化。

总之,想使用ArrayDeque类中的toArray方法,转换成基本类型数组的话,还是比较麻烦的,必须使用循环一个个转化

**三、滑动窗口最大值 -单调队列 (递增或递减) **
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例 1:

输入: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

//自定义数组
class MyQueue {
    Deque<Integer> deque = new LinkedList<>();
    //弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
    //同时判断队列当前是否为空
    void poll(int val) {
        if (!deque.isEmpty() && val == deque.peek()) {
            deque.poll();
        }
    }
    //添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
    //保证队列元素单调递减
    //比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
    void add(int val) {
        while (!deque.isEmpty() && val > deque.getLast()) {
            deque.removeLast();
        }
        deque.add(val);
    }
    //队列队顶元素始终为最大值
    int peek() {
        return deque.peek();
    }
}

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length == 1) {
            return nums;
        }
        int len = nums.length - k + 1;
        //存放结果元素的数组
        int[] res = new int[len];
        int num = 0;
        //自定义队列
        MyQueue myQueue = new MyQueue();
        //先将前k的元素放入队列
        for (int i = 0; i < k; i++) {
            myQueue.add(nums[i]);
        }
        res[num++] = myQueue.peek();
        for (int i = k; i < nums.length; i++) {
            //滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
            myQueue.poll(nums[i - k]);
            //滑动窗口加入最后面的元素
            myQueue.add(nums[i]);
            //记录对应的最大值
            res[num++] = myQueue.peek();
        }
        return res;
    }
}

其实,大家可以自己观察一下单调队列的实现,nums 中的每个元素最多也就被 push_back 和 pop_back 各一次,没有任何多余操作,所以整体的复杂度还是 O(n)。

空间复杂度因为我们定义一个辅助队列,所以是O(k)。

四、优先队列- PriorityQueue(大小顶堆)

PriorityQueue(Comparator<? super E> comparator)
创建具有默认初始容量的 PriorityQueue ,并根据指定的比较器对其元素进行排序。

大顶堆

  PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
       public int compare(int[] pair1, int[] pair2) {
       return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
            }
        });

根据map的value值正序排,相当于一个小顶堆

 PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());

1、
(o1, o2) -> o1.getValue() - o2.getValue()
相当于
new Comparator<int[]>() {
public int compare(Map.Entry<Integer, Integer> pair1, Map.Entry<Integer, Integer> pair2) {
return pair1.getValue() - pair2.getValue() ;
}
}
2、(o1, o2) -> o1.getValue() - o2.getValue()小顶堆 队列头放的是最小元素
(o1, o2) -> o2.getValue() - o1.getValue()大顶堆 队列头放的是最大元素

3、大家对这个比较运算在建堆时是如何应用的,为什么左大于右就会建立小顶堆,反而建立大顶堆,比较困惑。确实 例如我们在写快排的cmp函数的时候,return left>right 就是从大到小,return left<right 就是从小到大。优先级队列的定义正好反过来了,可能和优先级队列的源码实现有关(我没有仔细研究),我估计是底层实现上优先队列队首指向后面,队尾指向最前面的缘故!
个人: 从左到右为从大到小,而队列头的右边,所以是小顶堆

PriorityQueue类:

方法说明
boolean add(E e)将指定的元素插入到此优先级队列中。
void clear()从此优先级队列中删除所有元素。
Comparator<? super E> comparator()返回用于为了在这个队列中的元素,或比较null如果此队列根据所述排序natural ordering的元素。
boolean contains(Object o)如果此队列包含指定的元素,则返回 true 。
Iterator iterator()返回此队列中的元素的迭代器。
boolean offer(E e)将指定的元素插入到此优先级队列中。
E peek()检索但不删除此队列的头,如果此队列为空,则返回 null 。
E poll()检索并删除此队列的头,如果此队列为空,则返回 null 。
boolean remove(Object o)从该队列中删除指定元素的单个实例(如果存在)。
int size()返回此集合中的元素数。
Spliterator spliterator()在此队列中的元素上创建late-binding和失败快速 Spliterator 。
Object[] toArray()返回一个包含此队列中所有元素的数组。
T[] toArray(T[] a)返回一个包含此队列中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。

五、java中栈和队列的类

1、 Stack

问题一、C++中stack 是容器么?

Java中Stack类继承了Vector类,在其基础上实现了了栈的功能。由于是直接继承而非通过接口进行隐藏(如Queue虽然由LinkedList实现,但对其非队列接口进行了隐藏),Java的Stack拥有Vector的所有方法并且继承了其线程安全特性(所以也和Vector一样在性能上有所损耗)。

在List的基础上,Stack添加了以下方法:

push:向栈中压入一个元素并返回该元素。
peek:获取栈顶元素,栈为空抛出异常。
pop:获取并弹出栈顶元素,栈为空抛出异常。
empty:同isEmpty()。
search:基于lastIndexOf()实现,返回搜索元素离栈顶的最近距离。

可见,Stack是一个古老的,并为了模拟栈的操作不惜重复实现同一函数的方法。当比较注重效率时,显然基于LinkedList或者ArrayList对栈重新实现,会有更好的效果。

2、Queue
从继承层次上看,Queue和List,Map,Set一样,是直接继承了Collections类的容器类,但是从其实现上看,Queue又可以视为一种特殊的List。原因是Queue的直接实现方法是LinkedList,并对其功能加以限制,仅暴露了Queue支持的功能:add(),remove(),element(),offer(),poll(),peek(),put(),take()。当然LinkedList这些功能对于List接口,也并非全部暴露。

Queue对象能够使用以下方法操作:
add:队列末尾添加一个元素,若队列已满抛出异常。

offer:队列末尾添加一个元素,若队列已满返回false,成功返回true。另外,可以附加时间,时间单位参数设置超时。

remove:移除并返回队列头部元素,若队列已空抛出异常。

poll:移除并返回队列头部元素,若队列已空返回Null。另外,可以附加时间,时间单位参数设置超时。

element:返回队列头部元素,若队列已空抛出异常。

peek:返回队列头部元素,若队列已空返回Null。

需要注意的是,由于队列中poll和peek操作以Null为标志,所以队列中添加Null元素是不合法的。

3、 PriorityQueue
在介绍完Stack和Queue后,自然要说另一个最基本的数据结构——Heap,也就是这里的PriorityQueue优先队列。有关优先队列,我在很久很久以前介绍过C++的优先队列,Java也一样,重点部分是对Comparator的操作。
由于知道PriorityQueue是基于Heap的,当新的元素存储时,会调用siftUpUsingComparator方法,其定义如下:

private void siftUpUsingComparator(int k, E x) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (comparator.compare(x, (E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = x;
}

我们从代码可以看出,当Comparator的返回值为负时,会进行siftUp操作。例如,如果我们想让数值大的数字(下例子中为a)先出列,则让compare(a,b),则返回-1即可。可以看出来,和C++类似,方法取得的是“不小于”的概念。我们定义的方法可以视为是一个“小于”方法。

另外我们要注意到Comparator这个接口尽管有很多方法,但是有@FunctionalInterface标志,说明这是一个函数接口,换言之,在Java8中,我们可以使用lambda表达式来写这个方法。

用匿名类方法写的Comparator:

   PriorityQueue priorityQueue=new PriorityQueue(new Comparator<Student>() {
            @Override
            public int compare(Student student1, Student student2) {
                return student1.mark.compareTo(student2.mark);
            }
        });

用lambda表达式写Comparator

   PriorityQueue priorityQueue=new PriorityQueue((Comparator<Student>)(student1, student2)->student1.mark.compareTo(student2.mark));

这里需要注意的是,如果不加上类型转换,Java无法正确推断lambda表达式的类型。

问题二、stack 提供迭代器来遍历stack空间么?
栈提供push 和 pop 等等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。不像是set 或者map 提供迭代器iterator来遍历所有元素。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值