离谱!老板迷恋修dao,强制让员工去~

文摘   2024-11-17 14:30   陕西  

前段时间,某个小公司的一名程序员朋友跟我吐槽,说他入职快两年了,老板迷恋修道,还强制员工去,声称是“培训”。

你没听错,就是“修道”!问题是这还是收费的,我就问问,这世界上还有什么能比这种事更离谱的?

他说入职前,完全没听老板提过这个,直到有一天,他被叫去参加了个所谓的“修道”。结果呢,老板一脸严肃:“修道能让你更有内心的平静,技术提高也是顺带的。”我听了都想问问这老板,是不是脑袋被门夹了。
话说回来,咱做程序员的,哪有时间去修道啊?我愿意把时间用来跟代码“修行”💻,而不是闭眼冥想、摆弄香火。
不过,朋友也很矛盾。你知道的,小公司裁员随时可能,技术一般找新工作也不容易,压力山大。总之,要是你面对这种“无礼”要求,是硬钢着不去,还是硬着头皮去呢?🧐

算法题:排列序列

聊个经典的算法问题:滑动窗口最大值,听名字就觉得有点高深,对吧?其实它是一个很有意思的算法问题,我自己做过几次,觉得这个题目不仅锻炼了我们对数据结构的理解,还能让我们对一些常见的技巧,比如双指针、队列等有更深的认识。
首先,给大家一个简洁的描述:假设你有一个长度为n的数组,你需要找出数组中每个大小为k的滑动窗口里的最大值。

你知道的,在面试中这种题目基本是不会少的,不管是做系统架构的还是做前端的,都会涉及到一些滑动窗口的操作。简单的说,窗口每次滑动一个元素,求出该窗口中的最大值。

例子

假设你有一个数组:[1, 3, -1, -3, 5, 3, 6, 7],窗口大小k=3。那这道题的输出应该是:[3, 3, 5, 5, 6, 7],也就是说,滑动窗口内的最大值分别是3、3、5、5、6、7。
要注意,直观的暴力解法会是啥?简单地说,就是每次滑动窗口,都去遍历当前窗口,找出最大值。那样时间复杂度是O(n*k),效率显然不高,尤其当n和k很大的时候,简直就是浪费时间。那我们怎么优化呢?来看看我们怎么用更巧妙的方法解决这个问题。

双端队列(Deque)

一种高效的做法是使用双端队列(Deque),这个数据结构特别适合做滑动窗口问题。基本思路就是通过队列来存储窗口中的元素,但需要做一些优化,以确保队列中始终存储的是可能成为最大值的元素的索引。换句话说,队列的头部永远保存着当前窗口内的最大值的索引。
具体做法是:
  1. 遍历数组中的元素,对于每个新元素,先把不可能成为最大值的元素(即那些比当前元素小的元素)从队列中删除。
  2. 如果队列的头部已经超出了当前窗口的范围,就把它移除。
  3. 每次遍历完一个元素后,队列头部的元素就是当前窗口的最大值。

Java代码实现

import java.util.Deque;
import java.util.LinkedList;

public class SlidingWindowMax {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return new int[0];
        }

        // 结果数组
        int[] result = new int[nums.length - k + 1];
        // 双端队列,存储数组元素的索引
        Deque<Integer> deque = new LinkedList<>();

        for (int i = 0; i < nums.length; i++) {
            // 移除队列中不在窗口内的元素
            if (!deque.isEmpty() && deque.peek() < i - k + 1) {
                deque.poll();
            }

            // 移除队列中所有比当前元素小的元素
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }

            // 将当前元素的索引加入队列
            deque.offer(i);

            // 当窗口已经滑动完整,开始记录结果
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque.peek()];
            }
        }
        return result;
    }

    public static void main(String[] args) {
        SlidingWindowMax swm = new SlidingWindowMax();
        int[] nums = {13, -1, -35367};
        int k = 3;
        int[] result = swm.maxSlidingWindow(nums, k);
        for (int val : result) {
            System.out.print(val + " ");
        }
    }
}

代码解释

  • Deque<Integer> deque = new LinkedList<>(); 这里我们用双端队列来存储元素的索引,为什么要存索引?因为这样可以方便我们控制滑动窗口的范围。
  • if (!deque.isEmpty() && deque.peek() < i - k + 1) { deque.poll(); } 这个条件判断是用来检查队列中的元素是否还在当前窗口内。如果当前队列头部的元素已经不在窗口范围内了,就把它弹出。
  • while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) { deque.pollLast(); } 这个while循环负责去掉队列中那些比当前元素小的元素,因为它们不可能再成为最大值。
  • result[i - k + 1] = nums[deque.peek()]; 每次滑动完窗口,我们就把队列头部的元素添加到结果数组中。

时间复杂度

这段代码的时间复杂度是O(n),因为每个元素最多进入队列一次,也最多被移除一次。所以整体上,我们只需遍历一次数组,时间复杂度为O(n)。

空间复杂度

空间复杂度是O(k),因为在最坏的情况下,队列中最多会存储k个元素的索引。

小结

通过使用双端队列,我们就能够在每次滑动窗口时保持O(1)的时间复杂度来更新最大值,而不需要每次都去遍历整个窗口。虽然实现起来看似有点复杂,但是一旦理解了滑动窗口的原理,结合队列优化,效率提升是非常明显的。

-END-


ok,今天先说到这,老规矩,给大家分享一份不错的副业资料,感兴趣的同学找我领取。

以上,就是今天的分享了,看完文章记得右下角给何老师点赞,也欢迎在评论区写下你的留言

程序员老鬼
10年+老程序员,专注于AI知识普及,已打造多门AI课程,本号主要分享国内AI工具、AI绘画提示词、Chat教程、AI换脸、Chat中文指令、Sora教程等,帮助读者解决AI工具使用疑难问题。
 最新文章