刷了四百道算法题,我在项目里用过哪几道呢?

news2024/9/22 3:40:31

大家好,我是老三,今天和大家聊一个话题:项目中用到的力扣算法。

不知道从什么时候起,算法已经成为了互联网面试的标配,在十年前,哪怕如日中天的百度,面试也最多考个冒泡排序。后来,互联网越来越热,涌进来的人越来越多,整个行业越来越内卷的,算法也慢慢成了大小互联网公司面试的标配,力扣现在已经超过3000题了,那么这些题目有多少进入了面试的考察呢?

以最爱考算法的字节跳动为例,看看力扣的企业题库,发现考过的题目已经有1850道——按照平均每道题花20分钟来算,刷完字节题库的算法题需要37000分钟,616.66小时,按每天刷满8小时算,需要77.08天,一周刷五天,需要15.41周,按一个月四周,需要3.85个月。也就是说,在脱产,最理想的状态下,刷完力扣的字节题库,需要差不多4个月时间。

字节题库

那么,我在项目里用过,包括在项目中见过哪些力扣上的算法呢?我目前刷了400多道题,翻来覆去盘点了一下,发现,也就这么几道。

刷题数量

1.版本比较:比较客户端版本

场景

在日常的开发中,我们很多时候可能面临这样的情况,兼容客户端的版本,尤其是Android和iPhone,有些功能是低版本不支持的,或者说有些功能到了高版本就废弃掉。

这时候就需要进行客户端的版本比较,客户端版本号通常是这种格式6.3.40,这是一个字符串,那就肯定不能用数字类型的比较方法,需要自己定义一个比较的工具方法。

某app版本

题目

165. 比较版本号

这个场景对应LeetCode: 165. 比较版本号。

  • 题目:165. 比较版本号 (https://leetcode.cn/problems/compare-version-numbers/)

  • 难度:中等

  • 标签:双指针 字符串

  • 描述:

    给你两个版本号 version1version2 ,请你比较它们。

    版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.330.1 都是有效的版本号。

    比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 010 < 1

    返回规则如下:

    • 如果 *version1* > *version2* 返回 1
    • 如果 *version1* < *version2* 返回 -1
    • 除此之外返回 0

    示例 1:

    输入:version1 = "1.01", version2 = "1.001"
    输出:0
    解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1"
    

    示例 2:

    输入:version1 = "1.0", version2 = "1.0.0"
    输出:0
    解释:version1 没有指定下标为 2 的修订号,即视为 "0"
    

    示例 3:

    输入:version1 = "0.1", version2 = "1.1"
    输出:-1
    解释:version1 中下标为 0 的修订号是 "0",version2 中下标为 0 的修订号是 "1" 。0 < 1,所以 version1 < version2
    

    提示:

    • 1 <= version1.length, version2.length <= 500
    • version1version2 仅包含数字和 '.'
    • version1version2 都是 有效版本号
    • version1version2 的所有修订号都可以存储在 32 位整数
解法

那么这道题怎么解呢?这道题其实是一道字符串模拟题,就像标签里给出了了双指针,这道题我们可以用双指针+累加来解决。

在这里插入图片描述

  • 两个指针遍历version1version2
  • . 作为分隔符,通过累加获取每个区间代表的数字
  • 比较数字的大小,这种方式正好可以忽略前导0

来看看代码:

class Solution {
    public int compareVersion(String version1, String version2) {
        int m = version1.length();
        int n = version2.length();

        //两个指针
        int p = 0, q = 0;

        while (p < m || q < n) {
            //累加version1区间的数字
            int x = 0;
            while (p < m && version1.charAt(p) != '.') {
                x += x * 10 + (version1.charAt(p) - '0');
                p++;
            }

            //累加version2区间的数字
            int y = 0;
            while (q < n && version2.charAt(q) != '.') {
                y += y * 10 + (version2.charAt(q) - '0');
                q++;
            }

            //判断
            if (x > y) {
                return 1;
            }
            if (x < y) {
                return -1;
            }

            //跳过.
            p++;
            q++;
        }
        //version1等于version2
        return 0;
    }
}

应用

这段代码,直接CV过来,就可以直接当做一个工具类的工具方法来使用:

public class VersionUtil {

    public static Integer compareVersion(String version1, String version2) {
        int m = version1.length();
        int n = version2.length();

        //两个指针
        int p = 0, q = 0;

        while (p < m || q < n) {
            //累加version1区间的数字
            int x = 0;
            while (p < m && version1.charAt(p) != '.') {
                x += x * 10 + (version1.charAt(p) - '0');
                p++;
            }

            //累加version2区间的数字
            int y = 0;
            while (q < n && version2.charAt(q) != '.') {
                y += y * 10 + (version2.charAt(q) - '0');
                q++;
            }

            //判断
            if (x > y) {
                return 1;
            }
            if (x < y) {
                return -1;
            }

            //跳过.
            p++;
            q++;
        }
        //version1等于version2
        return 0;
    }
}

前面老三分享过一个规则引擎:这款轻量级规则引擎,真香!

比较版本号的方法,还可以结合规则引擎来使用:

  • 自定义函数:利用AviatorScript的自定义函数特性,自定义一个版本比较函数

        /**
         * 自定义版本比较函数
         */
        class VersionFunction extends AbstractFunction {
            @Override
            public String getName() {
                return "compareVersion";
            }
    
            @Override
            public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
                // 获取版本
                String version1 = FunctionUtils.getStringValue(arg1, env);
                String version2 = FunctionUtils.getStringValue(arg2, env);
                return new AviatorBigInt(VersionUtil.compareVersion(version1, version2));
            }
        }
    
  • 注册函数:将自定义的函数注册到AviatorEvaluatorInstance

        /**
         * 注册自定义函数
         */
        @Bean
        public AviatorEvaluatorInstance aviatorEvaluatorInstance() {
            AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();
            // 默认开启缓存
            instance.setCachedExpressionByDefault(true);
            // 使用LRU缓存,最大值为100个。
            instance.useLRUExpressionCache(100);
            // 注册内置函数,版本比较函数。
            instance.addFunction(new VersionFunction());
            return instance;
        }
    
  • 代码传递上下文:在业务代码里传入客户端、客户端版本的上下文

        /**
         * @param device  设备
         * @param version 版本
         * @param rule    规则脚本
         * @return 是否过滤
         */
        public boolean filter(String device, String version, String rule) {
            // 执行参数
            Map<String, Object> env = new HashMap<>();
            env.put("device", device);
            env.put("version", version);
            //编译脚本
            Expression expression = aviatorEvaluatorInstance.compile(DigestUtils.md5DigestAsHex(rule.getBytes()), rule, true);
            //执行脚本
            boolean isMatch = (boolean) expression.execute(env);
            return isMatch;
        }
    
  • 编写脚本:接下来我们就可以编写规则脚本,规则脚本可以放在数据库,也可以放在配置中心,这样就可以灵活改动客户端的版本控制规则

    if(device==bil){
        return false;
    }
    
    ## 控制Android的版本
    if (device=="Android" && compareVersion(version,"1.38.1")<0){
        return false;
    }
    
    return true;
    

2.N叉数层序遍历:翻译商品类型

场景

一个跨境电商网站,现在有这么一个需求:把商品的类型进行国际化翻译。

某电商网站商品类型国际化

商品的类型是什么结构呢?一级类型下面还有子类型,字类型下面还有子类型,我们把结构一画,发现这就是一个N叉树的结构嘛。

商品树

翻译商品类型,要做的事情,就是遍历这棵树,翻译节点上的类型,这不妥妥的BFS或者DFS!

题目

429. N 叉树的层序遍历

这个场景对应LeetCode:429. N 叉树的层序遍历。

  • 题目:429. N 叉树的层序遍历(https://leetcode.cn/problems/n-ary-tree-level-order-traversal/)

  • 难度:中等

  • 标签: 广度优先搜索

  • 描述:

    给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

    树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

    示例 1:

    img

    输入:root = [1,null,3,2,4,null,5,6]
    输出:[[1],[3,2,4],[5,6]]
    

    示例 2:

    img

    输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
    输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]
    

    提示:

    • 树的高度不会超过 1000
    • 树的节点总数在 [0, 10^4] 之间
解法

BFS想必很多同学都很熟悉了,DFS的秘诀是,BFS的秘诀是队列

层序遍历的思路是什么呢?

使用队列,把每一层的节点存储进去,一层存储结束之后,我们把队列中的节点再取出来,孩子节点不为空,就把孩子节点放进去队列里,循环往复。

N叉树层序遍历示意图

代码如下:

class Solution {
   public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }

        //创建队列并存储根节点
        Deque<Node> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            //存储每层结果
            List<Integer> level = new ArrayList<>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                Node current = queue.poll();
                level.add(current.val);
                //添加孩子
                if (current.children != null) {
                    for (Node child : current.children) {
                        queue.offer(child);
                    }
                }
            }
            //每层遍历结束,添加结果
            result.add(level);
        }
        return result;
    }
}

应用

商品类型翻译这个场景下,基本上和这道题目大差不差,不过是两点小区别:

  • 商品类型是一个属性多一些的树节点
  • 翻译过程直接替换类型名称即可,不需要返回值

来看下代码:

  • ProductCategory:商品分类实体

    public class ProductCategory {
        /**
         * 分类id
         */
        private String id;
        /**
         * 分类名称
         */
        private String name;
        /**
         * 分类描述
         */
        private String description;
        /**
         * 子分类
         */
        private List<ProductCategory> children;
      
        //省略getter、setter
      
    }  
    
  • translateProductCategory:翻译商品类型方法

       public void translateProductCategory(ProductCategory root) {
            if (root == null) {
                return;
            }
    
            Deque<ProductCategory> queue = new LinkedList<>();
            queue.offer(root);
    
            //遍历商品类型,翻译
            while (!queue.isEmpty()) {
                int size = queue.size();
                //遍历当前层
                for (int i = 0; i < size; i++) {
                    ProductCategory current = queue.poll();
                    //翻译
                    String translation = translate(current.getName());
                    current.setName(translation);
                    //添加孩子
                    if (current.getChildren() != null && !current.getChildren().isEmpty()) {
                        for (ProductCategory child : current.getChildren()) {
                            queue.offer(child);
                        }
                    }
                }
            }
        }
    

3.前缀和+二分查找:渠道选择

场景

在电商的交易支付中,我们可以选择一些支付方式,来进行支付,当然,这只是交易的表象。

某电商支付界面

在支付的背后,一种支付方式,可能会有很多种支付渠道,比如Stripe、Adyen、Alipay,涉及到多个渠道,那么就涉及到决策,用户的这笔交易,到底交给哪个渠道呢?

这其实是个路由问题,答案是加权随机,每个渠道有一定的权重,随机落到某个渠道,加权随机有很多种实现方式,其中一种就是前缀和+二分查找。简单说,就是先累积所有元素权重,再使用二分查找来快速查找。

题目

先来看看对应的LeetCode的题目,这里用到了两个算法:前缀和二分查找

704. 二分查找

  • 题目:704. 二分查找(https://leetcode.cn/problems/binary-search)

  • 难度:简单

  • 标签:数组 二分查找

  • 描述:

    给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

    示例 1:

    输入: nums = [-1,0,3,5,9,12], target = 9
    输出: 4
    解释: 9 出现在 nums 中并且下标为 4
    

    示例 2:

    输入: nums = [-1,0,3,5,9,12], target = 2
    输出: -1
    解释: 2 不存在 nums 中因此返回 -1
    

    提示:

    1. 你可以假设 nums 中的所有元素是不重复的。
    2. n 将在 [1, 10000]之间。
    3. nums 的每个元素都将在 [-9999, 9999]之间。
解法

二分查找可以说我们都很熟了。

数组是有序的,定义三个指针,leftrightmid,其中midleftright的中间指针,每次中间指针指向的元素nums[mid]比较和target比较:

二分查找示意图

  • 如果nums[mid]等于target,找到目标
  • 如果nums[mid]小于target,目标元素在(mid,right]区间;
  • 如果nums[mid]大于target,目标元素在[left,mid)区间

代码:

class Solution {
    public int search(int[] nums, int target) {
        int left=0;
        int right=nums.length-1;

        while(left<=right){
            int mid=left+((right-left)>>1);
            if(nums[mid]==target){
                return mid;
            }else if(nums[mid]<target){
                //target在(mid,right]区间,右移
                left=mid+1;
            }else{
                //target在[left,mid)区间,左移
                right=mid-1;
            }
        }
        return -1;
    }
}

二分查找,有一个需要注意的细节,计算mid的时候: int mid = left + ((right - left) >> 1);,为什么要这么写呢?

因为这种写法int mid = (left + right) / 2;,可能会因为left和right数值太大导致内存溢出。同时,使用位运算,也是除以2最高效的写法。

——这里有个彩蛋,后面再说。

303. 区域和检索 - 数组不可变

不像二分查找,在LeetCode上,前缀和没有直接的题目,因为本身前缀和更多是一种思路,一种工具,其中303. 区域和检索 - 数组不可变 是一道典型的前缀和题目。

  • 题目:303. 区域和检索 - 数组不可变(https://leetcode.cn/problems/range-sum-query-immutable/)

  • 难度:简单

  • 标签:设计 数组 前缀和

  • 描述:

    给定一个整数数组 nums,处理以下类型的多个查询:

    1. 计算索引 leftright (包含 leftright)之间的 nums 元素的 ,其中 left <= right

    实现 NumArray 类:

    • NumArray(int[] nums) 使用数组 nums 初始化对象
    • int sumRange(int i, int j) 返回数组 nums 中索引 leftright 之间的元素的 总和 ,包含 leftright 两点(也就是 nums[left] + nums[left + 1] + ... + nums[right] )

    示例 1:

    输入:
    ["NumArray", "sumRange", "sumRange", "sumRange"]
    [[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
    输出:
    [null, 1, -1, -3]
    
    解释:
    NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
    numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
    numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1)) 
    numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))
    

    提示:

    • 1 <= nums.length <= 104
    • -105 <= nums[i] <= 105
    • 0 <= i <= j < nums.length
    • 最多调用 104sumRange 方法
解法

这道题,我们如果不用前缀和的话,写起来也很简单:

class NumArray {
    private int[] nums;

    public NumArray(int[] nums) {
        this.nums=nums;
    }
    
    public int sumRange(int left, int right) {
        int res=0;
        for(int i=left;i<=right;i++){
            res+=nums[i];
        }
        return res;
    }
}

当然时间复杂度偏高,O(n),那么怎么使用前缀和呢?

  • 构建一个前缀和数组,用来累积 (0……i-1)的和,这样一来,我们就可以直接计算[left,right]之间的累加和

前缀和数组示意图

代码如下:

class NumArray {
    private int[] preSum;

    public NumArray(int[] nums) {
        int n=nums.length;
        preSum=new int[n+1];
        //计算nums的前缀和
        for(int i=0;i<n;i++){
            preSum[i+1]=preSum[i]+nums[i];
        }
    }
    
    //直接算出区间[left,right]的累加和
    public int sumRange(int left, int right) {
        return preSum[right+1]-preSum[left];
    }
}

可以看到,通过前缀和数组,可以直接算出区间[left,right]的累加和,时间复杂度O(1),可以说非常高效了。

应用

了解了前缀和和二分查找之后,回归我们之前的场景,使用前缀和+二分查找来实现加权随机,从而实现对渠道的分流选择。

渠道分流选择

  • 需要根据渠道和权重的配置,生成一个前缀和数组,来累积权重的值,渠道也通过一个数组进行分配映射
  • 用户的支付请求进来的时候,生成一个随机数,二分查找找到随机数载前缀和数组的位置,映射到渠道数组
  • 最后通过渠道数组的映射,找到选中的渠道

代码如下:

/**
 * 支付渠道分配器
 */
public class PaymentChannelAllocator {
    //渠道数组
    private String[] channels;
    //前缀和数组
    private int[] preSum;
    private ThreadLocalRandom random;

    /**
     * 构造方法
     *
     * @param channelWeights 渠道分流权重
     */
    public PaymentChannelAllocator(HashMap<String, Integer> channelWeights) {
        this.random = ThreadLocalRandom.current();
        // 初始化channels和preSum数组
        channels = new String[channelWeights.size()];
        preSum = new int[channelWeights.size()];

        // 计算前缀和
        int index = 0;
        int sum = 0;
        for (String channel : channelWeights.keySet()) {
            sum += channelWeights.get(channel);
            channels[index] = channel;
            preSum[index++] = sum;
        }
    }

    /**
     * 渠道选择
     */
    public String allocate() {
        // 生成一个随机数
        int rand = random.nextInt(preSum[preSum.length - 1]) + 1;

        // 通过二分查找在前缀和数组查找随机数所在的区间
        int channelIndex = binarySearch(preSum, rand);
        return channels[channelIndex];
    }

    /**
     * 二分查找
     */
    private int binarySearch(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;

        while (left <= right) {
            int mid = left + ((right - left) >> 2);
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        // 当找不到确切匹配时返回大于随机数的最小前缀和的索引
        return left;
    }
}

测试一下:

    @Test
    void allocate() {
        HashMap<String, Integer> channels = new HashMap<>();
        channels.put("Adyen", 50);
        channels.put("Stripe", 30);
        channels.put("Alipay", 20);

        PaymentChannelAllocator allocator = new PaymentChannelAllocator(channels);

        // 模拟100次交易分配
        for (int i = 0; i < 100; i++) {
            String allocatedChannel = allocator.allocate();
            System.out.println("Transaction " + (i + 1) + " allocated to: " + allocatedChannel);
        }
    }

彩蛋

在这个渠道选择的场景里,还有两个小彩蛋。

二分查找翻车

我前面提到了一个二分查找求mid的写法:

int mid=left+((right-left)>>1);

这个写法机能防止内存溢出,用了位移运算也很高效,但是,这个简单的二分查找写出过问题,直接导致线上cpu飙升,差点给老三吓尿了。

吓惨了

int mid = (right - left) >> 2 + left;

就是这行代码,看出什么问题来了吗?

——它会导致循环结束不了!

为什么呢?因为>>运算的优先级是要低于+的,所以这个运算实际上等于:

int mid = (right - left) >> (2 + left);

在只有两个渠道的时候没有问题,三个的时候就寄了。

当然,最主要原因还是没有充分测试,所以大家知道我在上面为什么特意写了单测吧。

加权随机其它写法

这里用了前缀和+二分查找来实现加权随机,其实加权随机还有一些其它的实现方法,包括别名方法、树状数组、线段树**、随机列表扩展、**权重累积等等方法,大家感兴趣可以了解一下。

加权随机的实现

印象比较深刻的是,有场面试被问到了怎么实现加权随机,我回答了权重累积前缀和+二分查找,面试官还是不太满意,最后面试官给出了他的答案——随机列表扩展

什么是随机列表扩展呢?简单说,就是创建一个足够大的列表,根据权重,在相应的区间,放入对应的渠道,生成随机数的时候,就可以直接获取对应位置的渠道。

public class WeightedRandomList {
    private final List<String> expandedList = new ArrayList<>();
    private final Random random = new Random();

    public WeightedRandomList(HashMap<String, Integer> weightMap) {
        // 填充 expandedList,根据权重重复元素
        for (String item : weightMap.keySet()) {
            int weight = weightMap.get(item);
            for (int i = 0; i < weight; i++) {
                expandedList.add(item);
            }
        }
    }

    public String getRandomItem() {
        // 生成随机索引并返回对应元素
        int index = random.nextInt(expandedList.size());
        return expandedList.get(index);
    }

    public static void main(String[] args) {
        HashMap<String, Integer> items = new HashMap<>();
        items.put("Alipay", 60);
        items.put("Adyen", 20);
        items.put("Stripe", 10);

        WeightedRandomList wrl = new WeightedRandomList(items);

        // 演示随机选择
        for (int i = 0; i < 10; i++) {
            System.out.println(wrl.getRandomItem());
        }
    }
}

这种实现方式就是典型的空间换时间,空间复杂度O(n),时间复杂度O(1)。优点是时间复杂度低,缺点是空间复杂度高,如果权重总和特别大的时候,就需要一个特别大的列表来存储元素。

当然这种写法还是很巧妙的,适合元素少、权重总和小的场景。

刷题随想

上面就是我在项目里用到过或者见到过的LeetCode算法应用,416:4,不足1%的使用率,还搞出过严重的线上问题。

……

在力扣社区里关于算法有什么的贴子里,有这样的回复:

“最好的结构是数组,最好的算法是遍历”。

“最好的算法思路是暴力。”

……

坦白说,如果不是为了面试,我是绝对不会去刷算法的,上百个小时,用在其他地方,绝对收益会高很多。

从实际应用去找刷LeetCode算法的意义,本身没有太大意义,算法题的最大意义就是面试。

刷了能过,不刷就挂,仅此而已。

这些年互联网行业红利消失,越来越多的算法题,只是内卷的产物而已。

当然,从另外一个角度来看,考察算法,对于普通的打工人,可能是个更公平的方式——学历、背景都很难卷出来,但是算法可以。

我去年面试的真实感受,“没机会”比“面试难”更令人绝望。

写到这,有点难受,刷几道题缓一下!



参考:

[1].https://leetcode.cn/circle/discuss/29Y1td/

[2].https://36kr.com/p/1212436260343689

[3].https://leetcode.cn/circle/discuss/QXnn1F/

[4].https://leetcode.cn/circle/discuss/SrePlc/


备注:涉及敏感信息,文中的代码都不是真实的投产代码,作者进行了一定的脱敏和演绎。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1365754.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VTK将二维图像向三维空间中无参数化的曲面表面进行纹理映射(含代码)

实现纹理映射主要是建立纹理空间与模型空间、模型空间与屏幕空间之间的映射关系(见图 6-28)&#xff1a; 其中纹理空间可以定义为u-v 空间&#xff0c;每个轴标范围为 (0.1)。其中对于一个纹理图像&#xff0c;其左下角 v 标为 0.0)&#xff0c;右上角标为 1.1)。而对于简单的参…

假设与灵敏度分析

灵敏度分析 关系究竟有多敏感&#xff0c;就要进行灵敏度分析 如果你改变了系统参数后&#xff0c;引起这个模型&#xff08;公式&#xff09;输出的变化的程度不大&#xff0c;则说明你的模型稳定性较强&#xff08;即灵敏性较差&#xff09;&#xff0c;反之则反&#xff01…

【Python期末】动态爬取电影Top250数据可视化处理(有GUI界面/无数据库)

诚接计算机专业编程作业(C语言、C、Python、Java、HTML、JavaScript、Vue等)&#xff0c;10/15R左右&#xff0c;如有需要请私信我&#xff0c;或者加我的企鹅号&#xff1a;1404293476 本文资源&#xff1a;https://download.csdn.net/download/weixin_47040861/88713693 目录…

简析云能耗管理系统在某高校建筑系统平台的设计与应用

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;根据本项目&#xff0c;依托某学院电能计量管理系统、供水计量监督系统、供热计量管理系统等基础平台&#xff0c;制定了高校建筑能耗综合管理系统平台应用的总体框架和方案。该系统可以实时监控、统计能耗和…

https配置证书

HTTPS 基本原理 https 介绍 HTTPS&#xff08;全称&#xff1a;HyperText Transfer Protocol over Secure Socket Layer&#xff09;&#xff0c;其实 HTTPS 并不是一个新鲜协议&#xff0c;Google 很早就开始启用了&#xff0c;初衷是为了保证数据安全。 国内外的大型互联网…

STM32CubeMX教程20 SPI - W25Q128驱动

目录 1、准备材料 2、实验目标 3、实验流程 3.0、前提知识 3.1、CubeMX相关配置 3.1.1、时钟树配置 3.1.2、外设参数配置 3.1.3、外设中断配置 3.2、生成代码 3.2.1、外设初始化调用流程 3.2.2、外设中断调用流程 3.2.3、添加其他必要代码 4、常用函数 5、烧录验…

互联网干洗店洗鞋店搭建一套私域小程序有哪些优势?

在快节奏的现代生活中&#xff0c;我们常常面临衣物堆积如山、时间却捉襟见肘的困境。 干洗店在中国各大城市随处可见&#xff0c;假如每位顾客每月都需要一套干洗服务&#xff0c;那么一个50万人口的城市每月就有50万套干洗需求。若每家店日均处理100套衣物&#xff0c;那么至…

AE (4)_ 直方图调整的理论

#灵感# 在短暂的高通平台调试中&#xff0c;很看重直方图调整的理解。后来其它平台&#xff0c;不怎么调整这个了。但还是记录一下。 我个人还是倾向 招式简单&#xff0c;但应用到极致。 绝大部分内容来自&#xff1a;刘斯宁&#xff0c;Image Enhancement - CLAHE - 知乎 (z…

图论算法(数学建模)算法以后更新

无权值&#xff0c;无向&#xff0c;当成1就行 有向 有向赋权 顶点度的概念 Dijkstra算法 Dijkstra算法能求-一个顶点到另一-顶点最短路径。它是由Di jkstra于1959年提出的。实际它能出始点到其它所有顶点的最短路径Dijkstra算法是一种标号法:给赋权图的每一一个顶点记一个数&a…

特斯拉难挽倒退?比亚迪为中国汽车市场改写历史

对于电动汽车这个新兴产业&#xff0c;特斯拉长期以来一直处于领头羊的位置&#xff0c;近年来也面临诸多测试。去年底欧洲报道特斯拉在瑞典遭遇罢工冲击&#xff0c;运营陷入诸多困扰&#xff0c;实在出人意料。更让人讶异的是&#xff0c;年终宣布新王者比亚迪在全球销量首次…

【前端设计】文字聚光灯

欢迎来到前端设计专栏&#xff0c;本专栏收藏了一些好看且实用的前端作品&#xff0c;使用简单的html、css语法打造创意有趣的作品&#xff0c;为网站加入更多高级创意的元素。 案例 文字聚光灯效果可以用于网站标题 html <!DOCTYPE html> <html lang"en&quo…

Unity组件开发--短连接HTTP

1.网络请求管理器 using LitJson; using Cysharp.Threading.Tasks; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Events;using System.Web; using System.Text; using Sy…

SSM框架学习笔记01 | 注解开发

文章目录 1. 注解形式定义bean2.纯注解开发3.bean管理4. 依赖注入5. 第三方bean管理总结 1. 注解形式定义bean Compoenet ControllerServiceRepository 配合代码块 <context:component-scan /> 使用 2.纯注解开发 Configuration ComponentScan AnnotationConfigApplicati…

【开源】基于JAVA的教学过程管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 教师端2.2 学生端2.3 微信小程序端2.3.1 教师功能如下2.3.2 学生功能如下 三、系统展示 四、核心代码4.1 查询签到4.2 签到4.3 查询任务4.4 查询课程4.5 生成课程成绩 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVu…

国科大计算机体系结构期末考试——更新中

题型一、第二章的画图 给一个逻辑表达式&#xff0c;画出晶体管级别的电路图 cmos电路的基本电路&#xff1a; 与非门的功能是对多个输入信号进行逻辑与操作&#xff0c;然后对结果进行取反。 或非门的功能是对多个输入信号进行逻辑或操作&#xff0c;然后对结果进行取反。 …

链表:两个一组,反转链表

1、针对单链表&#xff0c;当我们进行操作时&#xff0c;如果需要进行反转或者进行其他操作时&#xff0c;有链表断开的情况&#xff0c;不妨考虑下使用辅助指针来记录断开后的链表位置&#xff0c;将需要处理的数据处理好后&#xff0c;可以使用此辅助指针找到链表的位置 #in…

服务器故障与管理口与raid

一&#xff0c;服务器常见故障 1&#xff0c;系统不停重启进入不了系统 排查是否是硬件故障&#xff0c;系统盘是否损坏&#xff08;硬盘灯红色&#xff0c;黄色&#xff0c;绿色&#xff09; 查看系统第一启动项是那种方式(硬盘 网络网卡 光驱 U盘) bios 是否双系统&#x…

使用命令行方式搭建uni-app + Vue3 + Typescript + Pinia + Vite + Tailwind CSS + uv-ui开发脚手架

使用命令行方式搭建uni-app Vue3 Typescript Pinia Vite Tailwind CSS uv-ui开发脚手架 项目代码以上传至码云&#xff0c;项目地址&#xff1a;https://gitee.com/breezefaith/uniapp-vue3-ts-scaffold 文章目录 使用命令行方式搭建uni-app Vue3 Typescript Pinia V…

C# ReaderWriterLock类学习

前言 今天这篇文章我们来学习一下ReaderWriterLock类&#xff0c;ReaderWriterLock类定义了实现单写程序和多读程序语义的锁。这个类主要用于文件操作&#xff0c;即多个线程可以读取文件&#xff0c;但只能用一个线程来更新文件。使用ReaderWriterLock类时&#xff0c;任意数…

并发(9)

目录 50.AQS的核心思想是什么&#xff1f; 51.AQS有哪些核心方法&#xff1f; 52.AQS定义什么样的资源获取方式&#xff1f; 53.AQS底层使用了什么样的设计模式&#xff1f; 54.什么是可重入&#xff0c;什么是可重入锁&#xff1f;他用来解决什么问题&#xff1f; 55.Ree…