实用小算法

开头提醒:

打开自己本地任意一个SpringBoot项目,复制代码到test包下跟着敲。

后面几篇文章不再提醒,希望大家养成习惯。看10篇文章,不如自己动手做一次。

我们不执着于一天看多少篇,但求把每一篇都搞懂,慢就是快。

给大家分享一个非常、非常、非常实用的小算法。严格意义上,它不是一个算法,而是一种编码技巧。但其中涉及的思想层面的东西是共通的,如果能熟练掌握它,在某些场景下将大幅提升我们程序的执行效率。

这个算法能解决什么问题呢?它主要处理两个数据集合的匹配问题。

比如,现在有两个数据集合:

public class Demo {

    public static void main(String[] args) {

        // 老公组
        List<Couple> husbands = new ArrayList<>();
        husbands.add(new Couple(1, "梁山伯"));
        husbands.add(new Couple(2, "牛郎"));
        husbands.add(new Couple(3, "干将"));
        husbands.add(new Couple(4, "工藤新一"));
        husbands.add(new Couple(5, "罗密欧"));

        // 老婆组
        List<Couple> wives = new ArrayList<>();
        wives.add(new Couple(1, "祝英台"));
        wives.add(new Couple(2, "织女"));
        wives.add(new Couple(3, "莫邪"));
        wives.add(new Couple(4, "毛利兰"));
        wives.add(new Couple(5, "朱丽叶"));
    }
}

@Data
@AllArgsConstructor
class Couple{
    private Integer familyId;
    private String userName;
}

要求对数据进行处理,最终输出:

梁山伯爱祝英台

牛郎爱织女

干将爱莫邪

工藤新一爱毛利兰

罗密欧爱朱丽叶

第一版算法

优秀的代码都不是一蹴而就的,需要不断地优化和重构。所以一开始我们不要想太多,先把需求完成了再说:

public static void main(String[] args) {

    // 用于计算循环次数
    int count = 0;

    // 老公组
    List<Couple> husbands = new ArrayList<>();
    husbands.add(new Couple(1, "梁山伯"));
    husbands.add(new Couple(2, "牛郎"));
    husbands.add(new Couple(3, "干将"));
    husbands.add(new Couple(4, "工藤新一"));
    husbands.add(new Couple(5, "罗密欧"));

    // 老婆组
    List<Couple> wives = new ArrayList<>();
    wives.add(new Couple(1, "祝英台"));
    wives.add(new Couple(2, "织女"));
    wives.add(new Couple(3, "莫邪"));
    wives.add(new Couple(4, "毛利兰"));
    wives.add(new Couple(5, "朱丽叶"));

    for (Couple husband : husbands) {
        for (Couple wife : wives) {
            // 记录循环的次数
            count++;
            if (husband.getFamilyId().equals(wife.getFamilyId())) {
                System.out.println(husband.getUserName() + "爱" + wife.getUserName());
            }
        }
    }
    
    System.out.println("----------------------");
    System.out.println("循环了:" + count + "次");
}

输出结果

梁山伯爱祝英台

牛郎爱织女

干将爱莫邪

工藤新一爱毛利兰

罗密欧爱朱丽叶

----------------------

循环了:25次

总结一下第一版算法的优缺点。

  • 优点:代码逻辑非常直观,外层for遍历husband,内层for根据husband的familyId匹配到wife
  • 缺点:循环次数过多

当前数据量较小,可能看不出明显差距。实际上这是非常糟糕的一种算法。

想象一下,如果现在男女cp各1000人,那么全部匹配需要1000*1000 = 100w次循环。

如何改进?

我们要明确,在当前这个需求中,每位男嘉宾只能选一位女嘉宾。比如当外层for刚好轮到牛郎时,内层for需要遍历wives找出织女。一旦牛郎和织女牵手成功,其实就没必要继续往下遍历wives了,遍历完了又如何呢,反正只能带走织女。所以明智的做法是,牛郎匹配到织女后,就赶紧下去,换干将上场。

后面的三次其实是没必要的

第二版算法

public static void main(String[] args) {

    // 用于计算循环次数
    int count = 0;

    // 老公组
    List<Couple> husbands = new ArrayList<>();
    husbands.add(new Couple(1, "梁山伯"));
    husbands.add(new Couple(2, "牛郎"));
    husbands.add(new Couple(3, "干将"));
    husbands.add(new Couple(4, "工藤新一"));
    husbands.add(new Couple(5, "罗密欧"));

    // 老婆组
    List<Couple> wives = new ArrayList<>();
    wives.add(new Couple(1, "祝英台"));
    wives.add(new Couple(2, "织女"));
    wives.add(new Couple(3, "莫邪"));
    wives.add(new Couple(4, "毛利兰"));
    wives.add(new Couple(5, "朱丽叶"));

    for (Couple husband : husbands) {
        for (Couple wife : wives) {
            // 记录循环的次数
            count++;
            if (husband.getFamilyId().equals(wife.getFamilyId())) {
                System.out.println(husband.getUserName() + "爱" + wife.getUserName());
                // 牵手成功,换下一位男嘉宾
                break;
            }
        }
    }

    System.out.println("----------------------");
    System.out.println("循环了:" + count + "次");
}

输出结果:

梁山伯爱祝英台

牛郎爱织女

干将爱莫邪

工藤新一爱毛利兰

罗密欧爱朱丽叶

----------------------

循环了:15次

我们发现,循环次数从第一版的25次减少到了15次,区别仅仅是增加了一个break:一旦牵手成功,就换下一位男嘉宾。

break:跳出当前循环(女嘉宾for循环),但不会跳出男嘉宾的for循环。

总结一下第二版算法的优缺点。

  • 优点:执行效率比第一版高
  • 缺点:理解难度稍微提升了一些

这是最终版了吗?不,远远不够。

哈?还能优化吗?

问大家一个问题:

看过《非诚勿扰》吗?一位男嘉宾和一位女嘉宾牵手成功后,这位女嘉宾就要离开舞台了,对吧?

对呀?怎么了?

请你重新看看我们的第二版代码,你会发现即使牛郎和织女牵手成功了,下一位男嘉宾(干将)入场时还是会在循环中碰到织女。织女在上一轮循环中,已经确定和牛郎在一起了,本次干将再去遍历织女是没有意义的。

在前两轮中,梁山伯、牛郎已经确定牵手祝英台、织女,应该把她们两个从舞台请下去

第三版算法

public static void main(String[] args) {

    // 用于计算循环次数
    int count = 0;

    // 老公组
    List<Couple> husbands = new ArrayList<>();
    husbands.add(new Couple(1, "梁山伯"));
    husbands.add(new Couple(2, "牛郎"));
    husbands.add(new Couple(3, "干将"));
    husbands.add(new Couple(4, "工藤新一"));
    husbands.add(new Couple(5, "罗密欧"));

    // 老婆组
    List<Couple> wives = new ArrayList<>();
    wives.add(new Couple(1, "祝英台"));
    wives.add(new Couple(2, "织女"));
    wives.add(new Couple(3, "莫邪"));
    wives.add(new Couple(4, "毛利兰"));
    wives.add(new Couple(5, "朱丽叶"));

    for (Couple husband : husbands) {
        for (Couple wife : wives) {
            // 记录循环的次数
            count++;
            if (husband.getFamilyId().equals(wife.getFamilyId())) {
                System.out.println(husband.getUserName() + "爱" + wife.getUserName());
                // 牵手成功,把女嘉宾从舞台请下来,同时换下一位男嘉宾上场
                wives.remove(wife);
                break;
            }
        }
    }

    System.out.println("----------------------");
    System.out.println("循环了:" + count + "次");
}

输出结果:

梁山伯爱祝英台

牛郎爱织女

干将爱莫邪

工藤新一爱毛利兰

罗密欧爱朱丽叶

----------------------

循环了:5次

我们发现,循环次数从第二版的15次减少到了5次,因为牵手成功的女嘉宾都被请下舞台了:wives.remove(wife)。

如果说,第二版算法是打断wives的循环,那么第三版算法则是直接把wives请出场外。

总结一下第三版算法的优缺点。

  • 优点:执行效率比第二版高了不少
  • 缺点:理解难度稍微提升了一些,平均性能不高

我靠,这还有缺点啊?太牛逼了好吗,我都想不到。什么叫“平均性能不高”?

比如我现在把男嘉宾的出场顺序倒过来:

public static void main(String[] args) {

    // 用于计算循环次数
    int count = 0;

    // 老公组,原先梁山伯第一个出场,现在换罗密欧第一个
    List<Couple> husbands = new ArrayList<>();
    husbands.add(new Couple(5, "罗密欧"));
    husbands.add(new Couple(4, "工藤新一"));
    husbands.add(new Couple(3, "干将"));
    husbands.add(new Couple(2, "牛郎"));
    husbands.add(new Couple(1, "梁山伯"));

    // 老婆组
    List<Couple> wives = new ArrayList<>();
    wives.add(new Couple(1, "祝英台"));
    wives.add(new Couple(2, "织女"));
    wives.add(new Couple(3, "莫邪"));
    wives.add(new Couple(4, "毛利兰"));
    wives.add(new Couple(5, "朱丽叶"));

    for (Couple husband : husbands) {
        for (Couple wife : wives) {
            // 记录循环的次数
            count++;
            if (husband.getFamilyId().equals(wife.getFamilyId())) {
                System.out.println(husband.getUserName() + "爱" + wife.getUserName());
                // 牵手成功,把女嘉宾从舞台请下来,同时换下一位男嘉宾上场
                wives.remove(wife);
                break;
            }
        }
    }

    System.out.println("----------------------");
    System.out.println("循环了:" + count + "次");
}

输出结果

罗密欧爱朱丽叶

工藤新一爱毛利兰

干将爱莫邪

牛郎爱织女

梁山伯爱祝英台

----------------------

循环了:15次

循环次数从5次变成15次,和第二版算法是一样的。

这是怎么回事呢?

第一次是顺序遍历的:

第一位男嘉宾梁山伯上场:遇到第一位女嘉宾祝英台,直接牵手成功。

第二位男嘉宾牛郎上来了,此时祝英台不在了,他遇到的第一位女嘉宾是织女,也直接牵手成功。

第三位男嘉宾干将上场后一看,这不是莫邪吗,也牵手成功走了。

...

但是颠倒顺序后:

之前顺着来的时候,梁山伯带走了祝英台,牛郎出场就直接跳过祝英台了,这就是上一次循环对下一次循环的影响。

而这次,罗密欧错了4次以后终于带走了朱丽叶,但是工藤新一上场后,还是要试错3次才能找到毛利兰。提前离场的朱丽叶在毛利兰后面,所以罗密欧试错积累的优势无法传递给下一次循环。

对于某些算法而言,元素的排列顺序会改变算法的复杂度。在数据结构与算法中,对一个算法往往有三个衡量维度:

  • 最好复杂度
  • 平均复杂度
  • 最坏复杂度

现实生活中,我们往往需要结合实际业务场景与算法复杂度挑选出合适的算法。

在本案例中,第三版算法在男嘉宾顺序时可以得到最好的结果(5次),如果倒序则得到最差的结果(15次)。

第四版算法

终于要向大家介绍第四种算法了。

第四种算法是一种复杂度一致的算法,无论男嘉宾的出场顺序如何改变,效率始终如一。

这是一种怎么样的算法呢?

不急,我们先思考一个问题:

我们为什么要用for遍历?

咋一听,好像有点莫名其妙。不用for循环,我怎么遍历啊?

其实无论何时,使用for都意味着我们潜意识里已经把数据泛化,牺牲数据的特性转而谋求统一的操作方式。想象一下,假设一个数组存了国家男子田径队的队员们,比如110米栏的刘翔、100米项目的苏炳添和谢震业。你如果写一个for循环:

for(sportsMan : sportsMen){
    sportsMan.kualan();
}

在循环中,你只能调用运动员身上的一项技能执行。

  • 你选跨栏吧,苏炳添和谢震业不会啊...
  • 你选100米短跑吧,刘翔肯定比不过专业短跑运动员啊...

所以,绝大多数情况下,for循环意味着抽取共同特性,忽略个体差异。好处是代码通用,坏处是无法发挥个体优势,最终影响效率。

回到案例中来。

每次男嘉宾上场后,他都要循环遍历女嘉宾,挨个问过去:你爱我吗?

哦,不爱。我问问下一位女嘉宾。

他为什么要挨个问?因为“女人心海底针”,他根本不知道哪位女嘉宾是爱他的,所以场上女嘉宾对他来说就是无差异的“黑盒”。

如果我们给场上的女嘉宾每人发一个牌子,让他们在上面写上自己喜欢的男嘉宾号码,那么男嘉宾上场后就不用挨个问了,直接找到写有自己号码的女嘉宾即可牵手成功。

这个算法的思想其实就是让数据产生差异化,外部通过差异快速定位目标数据。

public static void main(String[] args) {

    // 用于计算循环次数
    int count = 0;

    // 老公组
    List<Couple> husbands = new ArrayList<>();
    husbands.add(new Couple(1, "梁山伯"));
    husbands.add(new Couple(2, "牛郎"));
    husbands.add(new Couple(3, "干将"));
    husbands.add(new Couple(4, "工藤新一"));
    husbands.add(new Couple(5, "罗密欧"));

    // 老婆组
    List<Couple> wives = new ArrayList<>();
    wives.add(new Couple(1, "祝英台"));
    wives.add(new Couple(2, "织女"));
    wives.add(new Couple(3, "莫邪"));
    wives.add(new Couple(4, "毛利兰"));
    wives.add(new Couple(5, "朱丽叶"));

    // 给女嘉宾发牌子
    Map<Integer, Couple> wivesMap = new HashMap<>();
    for (Couple wife : wives) {
        // 女嘉宾现在不在List里了,而是去了wivesMap中,前面放了一块牌子:男嘉宾的号码
        wivesMap.put(wife.getFamilyId(), wife);
        count++;
    }

    // 男嘉宾上场
    for (Couple husband : husbands) {
        // 找到举着自己号码牌的女嘉宾
        Couple wife = wivesMap.get(husband.getFamilyId());
        System.out.println(husband.getUserName() + "爱" + wife.getUserName());
        count++;
    }

    System.out.println("----------------------");
    System.out.println("循环了:" + count + "次");
}

输出结果

梁山伯爱祝英台

牛郎爱织女

干将爱莫邪

工藤新一爱毛利兰

罗密欧爱朱丽叶

----------------------

循环了:10次

此时无论你如何调换男嘉宾出场顺序,都只会循环10次。

男嘉宾出场后,无需遍历女嘉宾,直接“按图索骥”找到配偶

小结

第一版和第二版就不讨论了,我们只谈谈第三版和第四版代码。

假设两组数据长度分别是n和m:

第三版的循环次数是n ~

,是波动的,最好效率是n,这是非常惊人的(最差效率同样惊人...)。

第四版始终是 n + m。

在数据量较小的情况下,其实两者差距不大,CPU执行时间差可以忽略不计。我们设想n, m=1000的情况。

此时第三版的循环次数是:1000 ~

最好的结果是1000,固然可喜。但是最差的结果是1000+999+...+1=500500。

而此时第四版的循环次数是 1000+1000=2000,与第三版最好的结果相比也只差了1000次而已,对于CPU而言可以忽略不计。

考虑到实际编程中,数据库的数据往往是非常杂乱的,使用第三版算法几乎不可能得到最大效率。

所以推荐使用第四版算法。

它的精髓就是利用HashMap给其中一列数据加了“索引”,每个数据的“索引”(Map的key)是不同的,让数据差异化。

了解原理后,如何掌握这简单有效的小算法呢?

记住两步:

  • 先把其中一列数据由线性结构的List转为Hash散列的Map,为数据创建“索引”
  • 遍历另一列数据,依据索引从Map中匹配数据

相比第三版在原有的两个List基础上操作数据,第四版需要额外引入一个Map,内存开销稍微多了一点点。算法中,有一句特别经典的话:空间换时间。第四版勉强算吧。但要清楚,实际上Couple对象并没有增多,Map只是持有原有的Couple对象的引用而已。新增的内存开销主要是Map的索引(Key)。

请大家务必掌握这个小算法,后面很多地方会用到它。

拓展思考

我们都知道,实际开发中我们从数据库查询得到的数据都是由Mapper封装到单个List中,也就是说不具备“两个数据集合匹配”这种前提呀。

此时转换一下思维即可,比如前端要全量获取城市,而且是二级联动:

|-浙江省

    |-杭州市

    |-宁波市

    |-温州市

    |-...

|-安徽省

    |-合肥市

    |-黄山市

    |-芜湖市

    |-...

而数据库查出来的是:

id    name     pid

1     浙江省    0

2    杭州市     1

3    宁波市     1

4    温州市     1

5    安徽省     0

6    合肥市     5

7    黄山市     5

8    芜湖市     5

此时,List需要“自匹配”。

我们可以把“自匹配”转为“两个数据集合匹配”(List转Map,然后List和Map匹配):

是不是觉得似曾相识呀。

上面这种情况属于自关联匹配,强行把同一张表的数据当成两个数据通过id和pid匹配。而实际开发中,更为常见的是两张表的数据匹配:

因为有些公司不允许过多的JOIN查询,此时就只能根据主表先把分页的10条数据查出来,再根据主表数据的ids把从表的10条数据查出来,最后在内存中匹配。(其实对于10条数据,用for循环也没问题)

尝试封装工具类

很多时候,我们只是想做一下List转Map,写一大串Stream确实挺烦的,此时可以考虑专门封装一个转换类:

public class ConvertUtil {

    private ConvertUtil() {
    }

    /**
     * 将List转为Map
     *
     * @param list         原数据
     * @param keyExtractor Key的抽取规则
     * @param <K>          Key
     * @param <V>          Value
     * @return
     */
    public static <K, V> Map<K, V> listToMap(List<V> list, Function<V, K> keyExtractor) {
        if (list == null || list.isEmpty()) {
            return new HashMap<>();
        }
        Map<K, V> map = new HashMap<>(list.size());
        for (V element : list) {
            K key = keyExtractor.apply(element);
            if (key == null) {
                continue;
            }
            map.put(key, element);
        }
        return map;
    }

    /**
     * 将List转为Map,可以指定过滤规则
     *
     * @param list         原数据
     * @param keyExtractor Key的抽取规则
     * @param predicate    过滤规则
     * @param <K>          Key
     * @param <V>          Value
     * @return
     */
    public static <K, V> Map<K, V> listToMap(List<V> list, Function<V, K> keyExtractor, Predicate<V> predicate) {
        if (list == null || list.isEmpty()) {
            return new HashMap<>();
        }
        Map<K, V> map = new HashMap<>(list.size());
        for (V element : list) {
            K key = keyExtractor.apply(element);
            if (key == null || !predicate.test(element)) {
                continue;
            }
            map.put(key, element);
        }
        return map;
    }

    /**
     * 将List映射为List,比如List<Person> personList转为List<String> nameList
     *
     * @param originList 原数据
     * @param mapper     映射规则
     * @param <T>        原数据的元素类型
     * @param <R>        新数据的元素类型
     * @return
     */
    public static <T, R> List<R> resultToList(List<T> originList, Function<T, R> mapper) {
        if (originList == null || originList.isEmpty()) {
            return new ArrayList<>();
        }
        List<R> newList = new ArrayList<>(originList.size());
        for (T originElement : originList) {
            R newElement = mapper.apply(originElement);
            if (newElement == null) {
                continue;
            }
            newList.add(newElement);
        }
        return newList;
    }

    /**
     * 将List映射为List,比如List<Person> personList转为List<String> nameList
     * 可以指定过滤规则
     *
     * @param originList 原数据
     * @param mapper     映射规则
     * @param predicate  过滤规则
     * @param <T>        原数据的元素类型
     * @param <R>        新数据的元素类型
     * @return
     */
    public static <T, R> List<R> resultToList(List<T> originList, Function<T, R> mapper, Predicate<T> predicate) {
        if (originList == null || originList.isEmpty()) {
            return new ArrayList<>();
        }
        List<R> newList = new ArrayList<>(originList.size());
        for (T originElement : originList) {
            R newElement = mapper.apply(originElement);
            if (newElement == null || !predicate.test(originElement)) {
                continue;
            }
            newList.add(newElement);
        }
        return newList;
    }

    // ---------- 以下是测试案例 ----------

    private static List<Person> list;

    static {
        list = new ArrayList<>();
        list.add(new Person("i", 18, "杭州", 999.9));
        list.add(new Person("am", 19, "温州", 777.7));
        list.add(new Person("iron", 21, "杭州", 888.8));
        list.add(new Person("man", 17, "宁波", 888.8));
    }

    public static void main(String[] args) {
        Map<String, Person> nameToPersonMap = listToMap(list, Person::getName);
        System.out.println(nameToPersonMap);

        Map<String, Person> personGt18 = listToMap(list, Person::getName, person -> person.getAge() >= 18);
        System.out.println(personGt18);
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Person {
        private String name;
        private Integer age;
        private String address;
        private Double salary;
    }
}

大家还可以继续自行扩展哟~

比如,现在的listToMap()方法只支持 key:object,如果我希望是key:field,该怎么封装呢?(答案见评论区)

部分同学可能会觉得有点难,甚至会有一系列疑问:这参数什么意思啊?哪来的接口?写法好诡异…等等,这是因为缺少泛型和Java8的相关知识。不要慌,暂时有个印象即可,我们会在学习Java8新特性后再复习上面这段代码。

留个坑

我在第三版算法中留了一个坑,但它是隐性的,刚好这个场景下不会暴露。大家可以试着把第三版的break去掉,看看会不会出问题。

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

进群,大家一起学习,一起进步,一起对抗互联网寒冬

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python 计算最大回撤

1. 什么是最大回撤 最大回撤是评估金融产品收益的一个非常重要的风险指标&#xff0c;它指的是在选定历史周期内任一历史时点往后推&#xff0c;产品净值走到最低点时的收益率回撤幅度的最大值。 以上图为例&#xff0c; 最大回撤 ( V a l u e A − V a l u e B ) V a l u e …

《2020年最新面经》—字节跳动Java社招面试题

文章目录 前言&#xff1a;一面&#xff1a;01、Java基础知识答疑&#xff0c;简单概述一下&#xff1f;02、倒排索引了解吗&#xff1f;使用Java语言怎么实现倒排&#xff1f;03、详细讲解一下redis里面的哈希表&#xff0c;常用的Redis哈希表命名有哪些&#xff0c;举例说明其…

MongoDB相关基础操作(库、集合、文档)

文章目录 一、库的相关操作1、查看数据库2、查看当前库3、创建数据库4、删除数据库 二、集合的相关操作1、查看库中所有集合2、创建集合2.1、显示创建2.2、隐式创建 3、删除集合 三、文档的相关操作1、插入文档1.1、插入单条文档1.2、插入多条文档1.3、脚本方式 2、查询文档3、…

<Linux>权限管理|权限分类|权限设置|权限掩码|粘滞位

文章目录 Linux权限的概念Linux权限管理a. 文件访问者的分类b. 文件类型和访问权限c. 文件权限表示方法d. 文件权限的设置权限掩码file指令粘滞位 权限总结权限作业 Linux权限的概念 Linux下有两种用户&#xff1a;超级用户(root)和普通用户。 超级用户&#xff1a;可以在Lin…

【算法挨揍日记】day29——139. 单词拆分、467. 环绕字符串中唯一的子字符串

139. 单词拆分 139. 单词拆分 题目描述&#xff1a; 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 解题思路&am…

反弹Shell

概述 反弹shell&#xff08;reverse shell&#xff09;就是控制端监听在某TCP/UDP端口&#xff0c;被控端发起请求到该端口&#xff0c;并将其命令行的输入输出转到控制端。reverse shell与telnet&#xff0c;ssh等标准shell对应&#xff0c;本质上是网络概念的客户端与服务端…

新版mmdetection3d将3D bbox绘制到图像

环境信息 使用 python mmdet3d/utils/collect_env.py收集环境信息 sys.platform: linux Python: 3.7.12 | packaged by conda-forge | (default, Oct 26 2021, 06:08:21) [GCC 9.4.0] CUDA available: True numpy_random_seed: 2147483648 GPU 0,1: NVIDIA GeForce RTX 3090 …

记录-2023/11/18

1. 序列器中可以定义update方法 2. 分页之后前端获取数据的方式要进行改变 JavaScript的switch语法2 situation "a"switch (situation) {case "a":console.log("a")breakcase "b":console.log("b")breakcase "c&quo…

小美的树上染色

美团2024届秋招笔试第一场编程真题 先提一个小知识&#xff1a;题目中凡是提到树结构都要使用图的存储方式&#xff0c;只有二叉树例外。 分析&#xff1a;在树结构中&#xff0c;孩子和父节点是相邻节点&#xff0c;而父节点可能有多个孩子节点。在染色的过程中&#xff0c;…

【linux】补充:高效处理文本的命令学习(tr、uniq、sort、cut)

目录 一、tr——转换、压缩、删除 1、tr -s “分隔符” &#xff08;指定压缩连续的内容&#xff09; 2、tr -d 想要删除的东西 ​编辑 3、tr -t 内容1 内容2 将内容1全部转换为内容2&#xff08;字符数需要一一对应&#xff09; 二、cut——快速剪裁命令 三、uniq——去…

MongoDB之索引和聚合

文章目录 一、索引1、说明2、原理3、相关操作3.1、创建索引3.2、查看集合索引3.3、查看集合索引大小3.4、删除集合所有索引&#xff08;不包含_id索引&#xff09;3.5、删除集合指定索引 4、复合索引 二、聚合1、说明2、使用 总结 一、索引 1、说明 索引通常能够极大的提高查…

Python3.7+PyQt5 pyuic5将.ui文件转换为.py文件、Python读取配置文件、生成日志

1.实际开发项目时&#xff0c;是使用Qt Designer来设计UI界面&#xff0c;得到一个.ui的文件&#xff0c;然后利用PyQt5安装时自带的工具pyuic5将.ui文件转换为.py文件&#xff1a; pyuic5 -o mywindow.py mywindow.ui #先是py文件名&#xff0c;再是ui文件名样式图 QT5 UI&am…

端口号大揭秘:网络世界的“门牌号”有多牛?

大家好&#xff0c;今天我们来聊一聊网络中的端口号。如果你以为端口号只是冷冰冰的数字&#xff0c;那你就大错特错了。端口号&#xff0c;这些看似枯燥的数字背后&#xff0c;隐藏着一个个生动的故事。 1. 80/tcp - HTTP&#xff1a;数字版的美食街 首先&#xff0c;让我们迈…

6.9平衡二叉树(LC110-E)

绝对值函数&#xff1a;abs() 算法&#xff1a; 高度和深度的区别&#xff1a; 节点的高度&#xff1a;节点到叶子节点的距离&#xff08;从下往上&#xff09; 节点的深度&#xff1a;节点到根节点的距离&#xff08;从上往下&#xff09; 逻辑&#xff1a;一个平衡二叉树…

对象与this

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 最近想再聊聊Java的对象…

JVM 调优指南

文章目录 为什么要学 JVM一、JVM 整体布局二、Class 文件规范三、类加载模块四、执行引擎五、GC 垃圾回收1 、JVM内存布局2 、 JVM 有哪些主要的垃圾回收器&#xff1f;3 、分代垃圾回收工作机制 六、对 JVM 进行调优的基础思路七、 GC 情况分析实例 JVM调优指南 -- 楼兰 ​ JV…

系列七、GC垃圾回收【四大垃圾算法-标记压缩算法】

一、原理 在整理压缩阶段&#xff0c;不再对标记的对象回收&#xff0c;而是通过所有存活对象都向一端移动。可以看到&#xff0c;标记的存活对象将会被整理&#xff0c;按照内存地址依次排列。如此一来&#xff0c;当我们需要给新对象分配内存时&#xff0c;JVM只需要持有一个…

永久关机windows系统自动更新

1、打开cmd执行 reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" /v FlightSettingsMaxPauseDays /t reg_dword /d 3000 /f2、设置&#xff0c;打开windows更新高级选项 修改暂停日期&#xff0c;可长达十年。 3、你小子

解锁数据安全之门:探秘迅软DSE的文件权限控制功能

企业管理者在进行数据安全管控时通常只关注到文件的加密方式&#xff0c;却忽略了以下问题&#xff1a;对于企业内部文档&#xff0c;根据其所承载的涉密程度不同&#xff0c;重要程度也不相同&#xff0c;需要由不同涉密等级的的人员进行处理&#xff0c;这就需要对涉密文档和…

战神传奇【我本沉默精修版】win服务端+双端+充值后台+架设教程

搭建资源下载:战神传奇【我本沉默精修版】win服务端双端充值后台架设教程-海盗空间
最新文章