当前位置: 首页 > news >正文

究竟是什么样的讲解数组算法的博客让我写了三小时???

版本说明

当前版本号[20231004]。

版本修改说明
20231004初版

目录

文章目录

  • 版本说明
  • 目录
  • 二. 基础数据结构
    • 2.1 数组
      • 1) 概述
      • 2) 动态数组
        • 1)插入
          • addlast 方法
            • 测试: addlast 方法
          • add 方法
            • 测试:add方法
          • addlast 方法与 add 方法合并版
          • get 方法
            • 测试:get 方法
        • 2) 遍历
          • 遍历方法1:consumer 遍历
            • 测试:consumer 遍历
          • 遍历方法2:迭代器遍历
            • 测试:迭代器遍历
          • 遍历方法3:stream 遍历
            • 测试:stream 遍历
        • 插入与遍历方法汇总
        • 插入与遍历测试类汇总
        • 3)删除
          • 测试:删除方法
        • 4)扩容
        • 5)插入或删除性能
      • 3) 二维数组
        • **测试**
      • 4) 局部性原理
        • **对效率的影响**

二. 基础数据结构

2.1 数组

1) 概述

定义

在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识

因为数组内的元素是连续存储的,所以数组中元素的地址,可以通过其索引计算出来,例如:

int[] array = {1,2,3,4,5}

知道了数组的数据起始地址 B a s e A d d r e s s BaseAddress BaseAddress,就可以由公式 B a s e A d d r e s s + i ∗ s i z e BaseAddress + i * size BaseAddress+isize 计算出索引 i i i 元素的地址

  • i i i 即索引,在 Java、C 等语言都是从 0 开始
  • s i z e size size 是每个元素占用字节,例如 i n t int int 4 4 4 d o u b l e double double 8 8 8

小测试

byte[] array = {1,2,3,4,5}

已知 array 的数据的起始地址是 0x7138f94c8,那么元素 3 的地址是什么?

答:0x7138f94c8 + 2 * 1 = 0x7138f94ca

空间占用

Java 中数组结构为

  • 8 字节 markword
  • 4 字节 class 指针(压缩 class 指针的情况)
  • 4 字节 数组大小(决定了数组最大容量是 2 32 2^{32} 232
  • 数组元素 + 对齐字节(java 中所有对象大小都是 8 字节的整数倍[^12],不足的要用对齐字节补足)

例如

int[] array = {1, 2, 3, 4, 5};

的大小为 40 个字节,组成如下

8 + 4 + 4 + 5*4 + 4(alignment)

随机访问性能

即根据索引查找元素,时间复杂度是 O ( 1 ) O(1) O(1)

2) 动态数组

1)插入
addlast 方法

意在把新增的元素加入到最后一个到动态数组里。

public class 动态数组
{private int size = 0;//逻辑大小private int capacity = 8;//所创建数组的数组容量private int[] array = new int[capacity];/*** 向最后位置 [size] 添加元素** @param element 待添加元素*/public void addlast(int element){array[size] = element;size++;}
}

这个类具有以下特点:

  • size变量表示当前数组中已存储的元素数量。
  • capacity变量表示数组的容量,即可以容纳的最大元素数量。
  • array是一个整型数组,用于存储元素。
  • addlast(int element)方法用于在数组的末尾添加一个元素,并将size增加1。
测试: addlast 方法

​ 这段代码作为一个JUnit测试类,在测试方法test1中,首先创建了一个动态数组对象,然后向数组中添加了5个元素,最后遍历数组并打印出每个元素的值。

package SuanFa.test;import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import SuanFa.初始算法.第一章数组.动态数组;
import java.util.Arrays;public class 动态数组Test {@Testpublic void test1(){动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);dynamicarray.addlast(5);for(int i = 0; i < 5; i++){System.out.println(dynamicarray.get(i));}}
}

测试结果如下:

image-20231004104127182

注:接下来的所有方法中将只保留最主要的代码。

add 方法

意在向某一个索引里加入新值。

     /*** 向 [0 .. size] 位置添加元素** @param index   索引位置* @param element 待添加元素*/public void add(int index, int element){if(index >=0 && index < size){System.arraycopy(array, index, array, index+1, size - index);array[index] = element;size++;}}

这个类具有以下特点:

  • size变量表示当前数组中已存储的元素数量。
  • capacity变量表示数组的容量,即可以容纳的最大元素数量。
  • array是一个整型数组,用于存储元素。
  • add(int index, int element)方法用于在指定索引位置插入到指定索引位置,并确保索引在合法范围内(大于等于0且小于当前数组大小)。
  • 如果索引合法,使用System.arraycopy()方法将原数组中从指定索引位置开始的元素向后移动一个位置,腾出空间来插入新元素。(复制 array 数组中从下标为 index 的元素复制到 array 数组的下标为 index+1 的元素上,并且只复制 size - index 的元素)
  • 将新元素赋值给指定索引位置。
  • 增加size变量的值,表示数组中已存储的元素数量增加了1。
  • 如果索引不合法,该方法不会执行任何操作。
测试:add方法

​ 这段代码作为一个JUnit测试类,在测试方法test1中,首先创建了一个动态数组对象,然后向数组中添加了4个元素,接着使用add方法在索引为2的位置插入了一个值为9的元素。最后遍历数组并打印出每个元素的值。

    @Testpublic void test2(){动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);//dynamicarray.addlast(5);dynamicarray.add(2, 9);for(int i = 0; i < 5; i++){System.out.println(dynamicarray.get(i));}}
}

测试结果如下:

image-20231004104452737

addlast 方法与 add 方法合并版

由于addlast与add方法中,都有 array[下标] = element; size++; 这两段代码,于是也可以对他们进行优化,放在一起

     /*** 向 [0 .. size] 位置添加元素** @param index   索引位置* @param element 待添加元素*/public void add(int index, int element){// 添加逻辑if(index >=0 && index < size){// 向后挪动, 空出待插入位置System.arraycopy(array, index,array, index+1, size - index);}array[index] = element;size++;}/*** 向最后位置 [size] 添加元素** @param element 待添加元素*/public void addlast(int element){add(size, element);}

这个类具有以下特点:

  • size变量表示当前数组中已存储的元素数量。
  • capacity变量表示数组的容量,即可以容纳的最大元素数量。
  • array是一个整型数组,用于存储元素。
  • add(int index, int element)方法用于在指定索引位置插入一个元素。
  • addlast(int element)方法用于在数组的末尾添加一个元素。
  • add(int index, int element)方法将传入的元素插入到指定索引位置,并确保索引在合法范围内(大于等于0且小于当前数组大小)。
  • 如果索引合法,使用System.arraycopy()方法将原数组中从指定索引位置开始的元素向后移动一个位置,腾出空间来插入新元素。
  • 将新元素赋值给指定索引位置。
  • 增加size变量的值,表示数组中已存储的元素数量增加了1。
  • 如果索引不合法,该方法不会执行任何操作。
  • addlast(int element)方法调用add(int index, int element)方法,将元素添加到数组的末尾
  • 数组会自动调整大小以适应新元素的添加。
get 方法

意在接收一个整数参数 index ,返回数组中下标为 index 的元素

        /*** 查询元素** @param index 索引位置, 在 [0..size) 区间内* @return 该索引位置的元素*/public int get(int index){return array[index];}

这个类具有以下特点:

  • size变量表示当前数组中已存储的元素数量。
  • capacity变量表示数组的容量,即可以容纳的最大元素数量。
  • array是一个整型数组,用于存储元素。
  • 查询方法get接收一个整数参数 index ,返回数组中下标为 index 的元素
测试:get 方法

与测试:addlast 方法相同

    @Testpublic void test1(){动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);dynamicarray.addlast(5);for(int i = 0; i < 5; i++){System.out.println(dynamicarray.get(i));}}
2) 遍历
遍历方法1:consumer 遍历

​ 这段代码定义了一个名为 foreach 的方法,该方法接收一个 Consumer 类型的参数consumer。在方法内部,使用for循环遍历数组array,**对于每个元素,调用consumer的accept方法执行传入的操作。**最后,forEach方法没有返回值(void)。

    /*** 遍历方法1** @param consumer 遍历要执行的操作, 入参: 每个元素*/public void foreach(Consumer<Integer> consumer){for(int i = 0; i<size ; i++)// 提供 array[i]// 返回 void{consumer.accept(array[i]);}}
测试:consumer 遍历

​ 在该测试方法中,首先创建了一个动态数组对象dynamicarray,并向其中添加了5个元素。然后调用forEach方法遍历数组,对于每个元素,使用Lambda表达式打印出该元素的值。

public class 动态数组Test {@Testpublic void test3(){动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);dynamicarray.forEach((element->{System.out.println(element);}));}
}

测试结果如下:

image-20231004163648866

遍历方法2:迭代器遍历

​ 该动态数组类实现了Iterable接口,并重写了iterator方法

在类中重写了Iterable接口的iterator方法,返回一个Iterator对象,该对象实现了Iterator接口的方法hasNext和next。其中,hasNext方法用于判断是否还有下一个元素,next方法用于**返回当前元素并将指针移动到下一个元素。**由于没有给出数组元素的添加和删除操作,因此该类的迭代器只能从头到尾遍历一遍数组。

public class 动态数组 implements Iterable<Integer>
{private int size = 0;//逻辑大小private int capacity = 8;//容量private int[] array = new int[capacity];/*** 遍历方法2 - 迭代器遍历*/@Overridepublic Iterator<Integer> iterator(){return new Iterator<Integer>() {int i = 0;@Overridepublic boolean hasNext() { // 有没有下一个元素return i < size;}@Overridepublic Integer next() { // 返回当前元素,并移动到下一个元素return array[i++];}};
}
测试:迭代器遍历

​ 使用增强for循环遍历数组,对于每个元素,打印出该元素的值。

@Testpublic void test4(){动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);for(Integer element: dynamicarray){System.out.println(element);}}

测试结果如下:

image-20231004112314866

遍历方法3:stream 遍历

​ 这段代码定义了一个名为stream的公共方法,该方法返回一个IntStream类型的对象。在方法内部,使用IntStream.of()方法创建一个包含数组array中前size个元素的IntStream流并返回该流。

    /*** 遍历方法3 - stream 遍历** @return stream 流*/
public IntStream stream(){return IntStream.of(Arrays.copyOfRange(array,0,size));}
测试:stream 遍历
 @Testpublic void test5(){动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(7);dynamicarray.addlast(2);dynamicarray.addlast(4);dynamicarray.addlast(1);dynamicarray.stream().forEach(element->{System.out.println(element);});}

测试结果如下:

image-20231004164731217

插入与遍历方法汇总

​ 以下这段代码定义了一个名为动态数组的类,汇总了所有方法,实现了Iterable接口。该类方法分别有:

  1. add(int index, int element):向指定索引位置添加元素的方法。如果索引有效(即大于等于0且小于当前大小),则将该位置及之后的元素向后移动一位,然后将新元素放置在指定索引位置上,并将大小加1。
  2. addlast(int element):向数组末尾添加元素的方法。调用add(size, element)实现。
  3. get(int index):获取指定索引位置的元素的方法。
  4. foreach(Consumer<Integer> consumer):遍历数组并对每个元素执行给定的消费者操作。
  5. stream():返回一个IntStream流,其中包含数组中的所有元素。使用Arrays.copyOfRange(array, 0, size)创建一个包含数组元素的新数组,并将其转换为IntStream流。
package SuanFa.初始算法.第一章数组;import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.IntStream;public class 动态数组 implements Iterable<Integer> {private int size = 0;//逻辑大小private int capacity = 8;//容量private int[] array = new int[capacity];@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {int i = 0;@Overridepublic boolean hasNext() { // 有没有下一个元素return i < size;}@Overridepublic Integer next() { // 返回当前元素,并移动到下一个元素return array[i++];}};}public void add(int index, int element) {if (index >= 0 && index < size) {System.arraycopy(array, index,array, index + 1, size - index);}array[index] = element;size++;}public void addlast(int element) {add(size, element);}public int get(int index) {return array[index];}public void foreach(Consumer<Integer> consumer) {for (int i = 0; i < size; i++) {consumer.accept(array[i]);}}public IntStream stream() {return IntStream.of(Arrays.copyOfRange(array, 0, size));}
}
插入与遍历测试类汇总

该类包含了5个测试方法:

  1. test1():测试了向动态数组末尾添加元素的功能。首先创建了一个空的动态数组对象dynamicarray,然后连续调用addlast()方法向数组末尾添加了5个元素(1到5)。最后使用循环遍历数组并打印每个元素的值。
  2. test2():测试了在指定位置插入元素的功能。首先创建了一个空的动态数组对象dynamicarray,然后连续调用addlast()方法向数组末尾添加了5个元素(1到5)。接着调用add()方法在索引为2的位置插入了一个新元素9。最后使用循环遍历数组并打印每个元素的值。
  3. test3():测试了使用IntStream流遍历动态数组的功能。首先创建了一个空的动态数组对象dynamicarray,然后连续调用addlast()方法向数组末尾添加了5个元素(1到5)。接着调用stream()方法获取一个包含数组元素的IntStream流,并使用forEach()方法对每个元素执行打印操作。
  4. test4():测试了使用for-each循环遍历动态数组的功能。首先创建了一个空的动态数组对象dynamicarray,然后连续调用addlast()方法向数组末尾添加了5个元素(1到5)。接着使用for-each循环遍历数组,并在每次迭代中打印当前元素的值。
  5. test5():测试了在动态数组中间插入元素的功能。首先创建了一个空的动态数组对象dynamicarray,然后连续调用addlast()方法向数组末尾添加了4个元素(7、2、4、1)。最后使用流
package SuanFa.test;import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;import SuanFa.初始算法.第一章数组.动态数组;import java.lang.annotation.ElementType;
import java.util.Arrays;public class 动态数组Test {@Testpublic void test1() {动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);dynamicarray.addlast(5);for (int i = 0; i < 5; i++) {System.out.println(dynamicarray.get(i));}}@Testpublic void test2() {动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);dynamicarray.add(2, 9);for (int i = 0; i < 5; i++) {System.out.println(dynamicarray.get(i));}}@Testpublic void test3() {动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);dynamicarray.stream().forEach(element -> {System.out.println(element);});}@Testpublic void test4() {动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);for (Integer element : dynamicarray) {System.out.println(element);}}@Testpublic void test5() {动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(7);dynamicarray.addlast(2);dynamicarray.addlast(4);dynamicarray.addlast(1);dynamicarray.stream().forEach(element -> {System.out.println(element);});}
}
3)删除

​ 该方法接受一个整数参数 index,表示要删除元素的索引位置。

​ 首先,它将要删除的元素保存在变量 removed 中。然后,使用 System.arraycopy() 方法将索引位置后的元素向前移动一位,覆盖掉要删除的元素。

​ 接下来,通过将数组的大小减一来更新数组的大小。

​ 最后,返回被删除的元素。

/*** 从 [0 .. size) 范围删除元素** @param index 索引位置* @return 被删除元素*/
public int remove(int index){// 保存要删除的元素int removed = array[index];// 将索引位置后的元素向前移动一位System.arraycopy(array, index + 1, array, index, size - index - 1);// 更新数组的大小size--;// 返回被删除的元素return removed;
}
测试:删除方法

​ 这是一个J用于测试动态数组的测试类。首先创建一个dynamicarray的动态数组对象,然后使用addlast方法向数组中添加元素1、2、3、4和5。接着**调用remove方法移除索引为2的元素,并将其值赋给变量removed。**最后,使用stream方法和forEach遍历打印数组中的所有元素。

@Testpublic void test6() {动态数组 dynamicarray = new 动态数组();dynamicarray.addlast(1);dynamicarray.addlast(2);dynamicarray.addlast(3);dynamicarray.addlast(4);dynamicarray.addlast(5);int removed = dynamicarray.remove(2);System.out.println(removed);System.out.println("-----");dynamicarray.stream().forEach(element -> {System.out.println(element);});}

测试结果如下:

image-20231004201626597

4)扩容
  • ​ 该方法首先进行容量检查。
  • ​ 如果当前数组的大小为0,即数组为空,那么将创建一个新的数组,其容量为capacity
  • ​ 如果当前数组的大小等于容量,即数组已满,那么将通过将容量增加至原来的两倍来扩展数组。
  • ​ 然后,创建一个新的数组newArray,其容量为新的容量值。
  • ​ 接下来,使用System.arraycopy()方法将原数组中的元素复制到新数组中。
  • ​ 最后,将新数组赋值给原数组,完成数组的扩展操作。
  • ​ 这个方法通常用于动态数组的管理,以确保数组在插入元素时能够自动调整其容量大小,以适应不断增长的数据量。
  private void checkAndGrow() {// 容量检查if (size == 0) {array = new int[capacity];} else if (size == capacity) {capacity += capacity >> 1;int[] newArray = new int[capacity];System.arraycopy(array, 0,newArray, 0, size);array = newArray;}}
5)插入或删除性能

部位置,时间复杂度是 O ( n ) O(n) O(n)

间位置,时间复杂度是 O ( n ) O(n) O(n)

部位置,时间复杂度是 O ( 1 ) O(1) O(1)(均摊来说)

3) 二维数组

int[][] array = {{11, 12, 13, 14, 15},{21, 22, 23, 24, 25},{31, 32, 33, 34, 35},
};
  • 二维数组占 32 个字节,其中 array[0],array[1],array[2] 三个元素分别保存了指向三个一维数组的引用

  • 三个一维数组各占 40 个字节

  • 它们在内层布局上是连续

更一般的,对一个二维数组 A r r a y [ m ] [ n ] Array[m][n] Array[m][n]

  • m m m 是外层数组的长度,可以看作 row 行
  • n n n 是内层数组的长度,可以看作 column 列
  • 当访问 A r r a y [ i ] [ j ] Array[i][j] Array[i][j] 0 ≤ i < m , 0 ≤ j < n 0\leq i \lt m, 0\leq j \lt n 0i<m,0j<n时,就相当于
    • 先找到第 i i i 个内层数组(行)
    • 再找到此内层数组中第 j j j 个元素(列)
测试

Java 环境下(不考虑类指针和引用压缩,此为默认情况),有下面的二维数组

byte[][] array = {{11, 12, 13, 14, 15},{21, 22, 23, 24, 25},{31, 32, 33, 34, 35},
};

已知 array 对象起始地址是 0x1000,那么 23 这个元素的地址是什么?

答:

  • 起始地址 0x1000
  • 外层数组大小:16字节对象头 + 3元素 * 每个引用4字节 + 4 对齐字节 = 32 = 0x20
  • 第一个内层数组大小:16字节对象头 + 5元素 * 每个byte1字节 + 3 对齐字节 = 24 = 0x18
  • 第二个内层数组,16字节对象头 = 0x10,待查找元素索引为 2
  • 最后结果 = 0x1000 + 0x20 + 0x18 + 0x10 + 2*1 = 0x104a

4) 局部性原理

这里只讨论空间局部性

  • cpu 读取内存(速度慢)数据后,会将其放入高速缓存(速度快)当中,如果后来的计算再用到此数据,在缓存中能读到的话,就不必读内存了
  • 缓存的最小存储单位是缓存行(cache line),一般是 64 bytes,一次读的数据少了不划算啊,因此最少读 64 bytes 填满一个缓存行,因此读入某个数据时也会读取其临近的数据,这就是所谓空间局部性
对效率的影响

比较下面 ij 和 ji 两个方法的执行效率

int rows = 1000000;
int columns = 14;
int[][] a = new int[rows][columns];StopWatch sw = new StopWatch();
sw.start("ij");
ij(a, rows, columns);
sw.stop();
sw.start("ji");
ji(a, rows, columns);
sw.stop();
System.out.println(sw.prettyPrint());

ij 方法

public static void ij(int[][] a, int rows, int columns) {long sum = 0L;for (int i = 0; i < rows; i++) {for (int j = 0; j < columns; j++) {sum += a[i][j];}}System.out.println(sum);
}

ji 方法

public static void ji(int[][] a, int rows, int columns) {long sum = 0L;for (int j = 0; j < columns; j++) {for (int i = 0; i < rows; i++) {sum += a[i][j];}}System.out.println(sum);
}

执行结果

0
0
StopWatch '': running time = 96283300 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
016196200  017%  ij
080087100  083%  ji

可以看到 ij 的效率比 ji 快很多,为什么呢?

  • 缓存是有限的,当新数据来了后,一些旧的缓存行数据就会被覆盖
  • 如果不能充分利用缓存的数据,就会造成效率低下

以 ji 执行为例,第一次内循环要读入 [ 0 , 0 ] [0,0] [0,0] 这条数据,由于局部性原理,读入 [ 0 , 0 ] [0,0] [0,0] 的同时也读入了 [ 0 , 1 ] . . . [ 0 , 13 ] [0,1] ... [0,13] [0,1]...[0,13],如图所示

image-20221104164329026

但很遗憾,第二次内循环要的是 [ 1 , 0 ] [1,0] [1,0] 这条数据,缓存中没有,于是再读入了下图的数据

image-20221104164716282

这显然是一种浪费,因为 [ 0 , 1 ] . . . [ 0 , 13 ] [0,1] ... [0,13] [0,1]...[0,13] 包括 [ 1 , 1 ] . . . [ 1 , 13 ] [1,1] ... [1,13] [1,1]...[1,13] 这些数据虽然读入了缓存,却没有及时用上,而缓存的大小是有限的,等执行到第九次内循环时

image-20221104164947154

缓存的第一行数据已经被新的数据 [ 8 , 0 ] . . . [ 8 , 13 ] [8,0] ... [8,13] [8,0]...[8,13] 覆盖掉了,以后如果再想读,比如 [ 0 , 1 ] [0,1] [0,1],又得到内存去读了

相关文章:

究竟是什么样的讲解数组算法的博客让我写了三小时???

版本说明 当前版本号[20231004]。 版本修改说明20231004初版 目录 文章目录 版本说明目录二. 基础数据结构2.1 数组1) 概述2) 动态数组1&#xff09;插入addlast 方法测试: addlast 方法 add 方法测试&#xff1a;add方法 addlast 方法与 add 方法合并版get 方法测试&#x…...

Day-05 CentOS7.5 安装docker

参考 &#xff1a; Install Docker Engine on CentOS | Docker DocsLearn how to install Docker Engine on CentOS. These instructions cover the different installation methods, how to uninstall, and next steps.https://docs.docker.com/engine/install/centos/ Doc…...

Makefile

目录 Makefile Makefile格式 Makefile函数&#xff1a;foreach和wildcard $(foreach var,list,text) $(wildcard pattern) 完善Makefile Makefile 在Linux中使用make工具来编译程序&#xff08;特别是大程序&#xff09;&#xff0c;而make工具所执行的动作依赖于Makefil…...

c语言练习77:公因⼦的数⽬

公因⼦的数⽬ 题⽬描述&#xff1a; 给你两个正整数 a 和 b &#xff0c;返回 a 和 b 的公因⼦的数⽬。 如果 x 可以同时整除 a 和 b &#xff0c;则认为 x 是 a 和 b 的⼀个公因⼦ 。 • ⽰例 1&#xff1a; 输⼊&#xff1a;a 12, b 6 输出&#xff1a;4 解释&#…...

【C++】C++11——右值引用和移动语义、左值引用和右值引用、右值引用使用场景和意义、完美转发、新的类功能

文章目录 C115.右值引用和移动语义5.1左值引用和右值引用5.2左值引用与右值引用比较5.3右值引用使用场景和意义5.4右值引用引用左值及其一些更深入的使用场景分析5.5完美转发 6.新的类功能 C11 5.右值引用和移动语义 右值引用是C11引入的一个新特性&#xff0c;用于支持移动语义…...

Spring Boot的创建和使用(JavaEE进阶系列2)

目录 前言&#xff1a; 1.什么是Spring Boot&#xff1f;为什么要学习Spring Boot&#xff1f; 2.Spring Boot优点 3.创建Spring Boot项目 3.1准备工作 3.2Spring Boot创建 3.2.1通过idea的方式创建 3.2.2通过网页创建 4.Spring Boot中的配置文件 4.1Spring Boot配置…...

【OLSR路由协议】链路状态路由(OLSR)协议中选择多点中继节点算法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

双重差分模型(DID)论文写作指南与操作手册

手册链接&#xff1a;双重差分模型&#xff08;DID&#xff09;论文写作指南与操作手册https://www.cctalk.com/m/group/90983583?xh_fshareuid60953990 简介&#xff1a; 当前&#xff0c;对于准应届生们来说&#xff0c;毕设季叠加就业季&#xff0c;写作时间显得十分宝贵…...

ping 的工作原理

ping 是一个常用于网络诊断的命令行工具&#xff0c;用于测试两台计算机之间的网络连通性。它的工作原理如下&#xff1a; 发出 ICMP Echo 请求&#xff1a; 当你在终端中运行 ping 命令并指定目标主机的IP地址或域名时&#xff0c;计算机会创建一个特殊的ICMP&#xff08;In…...

93. 复原 IP 地址

有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址&#xff0c;但是 "0.011.255.24…...

竞赛选题 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…...

第15届蓝桥STEMA测评真题剖析-2023年8月20日Scratch编程中级组

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第155讲。 第15届蓝桥第1次STEMA测评&#xff0c;这是2023年8月20日举办的STEMA&#xff0c;比赛仍然采取线上形式。这…...

html5 checkbox

1 选中状态的设置与获取 &#xff08;1&#xff09;获取checkbox是否选中&#xff1a; $("#checkbox").is(":checked");$("#checkbox-id")get(0).checked$(#checkbox-id).attr(checked)&#xff08;2&#xff09;设置checkbox是否选中 $("…...

安装matplotlib_

安装pip 安装matplotlib 安装完毕 导入出现bug.........

STM32复习笔记(六):STM32远程升级BootLoader相关

目录 Preface&#xff1a; &#xff08;一&#xff09;STM32上电启动流程 &#xff08;二&#xff09;BootLoader相关 &#xff08;三&#xff09;Clion配置 Preface&#xff1a; 有关STM32的BootLoader主要还是参考了许多大佬的文章&#xff0c;这里只是简单地列举一下&am…...

MASA MAUI iOS 文件下载与断点续传

文章目录 背景介绍方案及代码1、新建MAUI项目2、建立NSUrlSession会话连接3、使用NSUrlSessionDownloadTask 创建下载任务4、DidWriteData 监听下载5、DidFinishDownloading 完成下载6、CancelDownload (取消/暂停)下载7、ResumeDownload 恢复下载8、杀死进程-恢复下载 效果图总…...

NPDP产品经理知识(产品创新流程)

1.复习组合管理&#xff1a; 组合管理的目标 > 价值最大化&#xff0c;项目平衡&#xff0c;战略一致&#xff0c;管道平衡(资源需求和供给)&#xff0c;盈利充分 (实现财务目标) 产品创新流程就是管理风险的过程。 模糊前端&#xff1a; 产品创新章程&#xff1a;PIC 包…...

Android日常——记一次Android事件分发需求的实现

背景&#xff1a;在一个事件分发复杂的view中&#xff0c;插入一个可点击的控件&#xff0c;且不能影响到本身的事件分发。 尝试: 1.对view本身设置点击事件&#xff1b;由于view整体是交由root view去处理分发&#xff0c;存在滑动、边界处理、调出其他界面等复杂操作。设置点…...

【Python】函数(function)和方法(method)的区别

这里先说结论&#xff0c;为了满足心急的小伙伴&#xff1a;method与function的最大区别就是参数有无进行绑定。 自定义类Test&#xff1a; 首先先来一个自定义类&#xff1a; class Test:def Func_normal(arg):print(Func_normal:,arg)staticmethoddef Func_static(arg):pri…...

linux入门---信号的理解

目录标题 如何理解计算机中的信号如何查看计算机中的信号初步了解信号的保存和发送如何向目标进程发送信号情景一&#xff1a;使用键盘发送信号情景二&#xff1a;系统调用发送信号情景三&#xff1a;硬件异常产生信号情景四&#xff1a;软件条件产生信号 核心转储信号的两个问…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...