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

第一讲 | 算法复杂度

算法复杂度

  • 一、数据结构前言
    • 1、数据结构(DS)
    • 2、算法(Algorithm)
  • 二、算法效率
    • 1、复杂度的概念
  • 三、时间复杂度
    • (1)、案例
    • (2)、大O的渐进表示法
    • (3)、时间复杂度计算示例
      • 示例1
      • 示例2
      • 示例3
      • 示例4:在字符串里面查找一个字符
      • 示例5
      • 示例6
      • 示例7
  • 四、空间复杂度
    • 空间复杂度计算示例
      • 示例1:冒泡排序的空间复杂度
      • 示例2:阶乘递归的空间复杂度
  • 五、常见复杂度对比
  • 六、复杂度算法题
    • 轮转数组
      • 思路1:循环k次将数组的每一个元素都向后挪动一位
      • 思路2:创建一个与原数组nums的空间一样大的数组tmp,将数据挪动到新数组数据轮转后对应的位置,再一一覆盖回原数组
      • 思路3:最优解,三次逆置。时间复杂度是O(n),空间复杂度是O(1)

算法复杂度:把数据存储在数据结构中必须应用一部分算法的思想,所以数据结构与算法不分家,讲数据结构就是在讲算法,讲算法离不开数据结构,算法是借助复杂度去评估算法的好与坏。

初阶数据结构阶段学习顺序表、链表、栈、队列、二叉树5种数据结构以及常见排序算法。初阶数据结构中我们将继续使用C语言来实现基础的数据结构,在掌握数据结构的同时巩固了刚结束的C语法知识。图、哈希表、红黑树等数据结构将在C++中学习。

一、数据结构前言

1、数据结构(DS)

数据结构(Data Structure) 是计算机存储、组织数据方式,指相互之间存在一种或多种特定关系的数据元素的集合。没有一种单一的数据结构对所有用途都有用,所以我们要学各式各样的数据结构,如:线性表、树、图、哈希等。

数据结构类似一个盒子可以存储数据,但是也不能只存储数据,也可以往外拿数据。比如,你有一个存钱罐,你可以往里面放钱也可以往外拿钱。所以一个盒子即能存数据也能取数据就是所谓的数据结构。

存储数据、取数据统一叫作组织数据。常见的组织数据的方式是增删查改,比如数组可以完成增删查改,那么数组就是一种最基本的数据结构。既然数组就可以组织数据,那么为什么要学习那么多数据结构?这是因为一个数组只能存储同类型的数据,若想存储不同类型的数据,首先想到的就是结构体,但是结构体只是表示有这一个结构,实际不是一个有效的容器来存储数据。所以我们需要针对不同的操作场景选择不同的高效的数据结构——没有一种单一的数据结构对所有用途都有用

当考虑数组组织数据时它的效率如何?一道题有不同的解决方法,哪种算法更好就要考虑各自算法的效率如何。数据结构能存储数据并且组织数据,对数据进行存储和组织就离不开算法。

2、算法(Algorithm)

算法(Algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成符合条件的输出结果

例如:冒泡排序,输入就是乱序的数组,输出就是有序的数组。将乱序的数组变为有序的数组需要编写代码,不同的人有不同的思路,可以将算法理解为思路/解决办法。哪一种算法更好?——通过算法效率分析。

二、算法效率

如何衡量一个算法的好坏呢?

案例:轮转数组

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

思路:循环k次将数组所有元素向后移动一位。

向右轮转1次:i从下标n - 1开始,先用tmp存储最后一个值,怕被覆盖,i - 1的值赋值给i,当i == 0跳出循环,tmp赋值给下标0对应的数。

再把向右轮转1次的过程重复k次即可。

在这里插入图片描述

代码自测运行是没有问题的,但是提交之后就有问题了,这就涉及到算法的好与坏了:

在这里插入图片描述

怎么衡量算法的好与坏呢?有没有一种方式在提出算法思想之后就知道算法的好与坏呢?——那就要探讨复杂度了。这个算法题暂且告一段落。

1、复杂度的概念

算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。

时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。

摩尔定律:内存是由很多的晶体管组成的,每隔大约两年,晶体管的数量大约会增1倍,也就是内存会不断地增加,同时导致其价格越来越低。所以这就导致我们对空间的关注度不是很高,但不是说空间很多且又很便宜我们就可以随意浪费。

三、时间复杂度

定义:在计算机科学中,算法的时间复杂度是一个函数式T(N),而不是具体的运行时间数字,它定量描述了该算法的运行时间。

函数式。例如,f(x) = ax + b,函数的最终结果由x决定,a、b是常数不会影响f(x)的实际结果。说明算法的时间复杂度函数式T(N)(T:Time)中有一个会影响算法的好与坏的算法输入变量N。比如刚刚的算法题,有3个输入,所以这3个输入都可以看作是“影响算法的好与坏的算法输入变量”,变量k越大循环次数越多;数组及其数组个数变量,数组个数越多,循环次数也越多。说明“输入”都会影响程序的运行时间。

时间复杂度是衡量程序的时间效率,那么为什么不去计算程序的具体运行时间而是要通过函数式呢?

  1. 因为程序的运行时间与编译环境和运行机器的配置都有关系,比如同一个算法程序,用一个老编译器进行编译和新编译器编译,在同样机器下运行时间不同;同一个算法程序,用一个老低配置机器和新高配置机器,运行时间也不同。
  2. 并且时间只能在程序写好后测试,不能在写程序前通过理论思想计算评估。

比如计算程序的具体运行时间:

#include <stdio.h>
#include <time.h>// clock()的头文件
int main()
{int nums[] = { 1, 2, 3, 4, 5, 6, 7 };int numsSize = 7;int k = 100000;// 记录起始时间int start = clock();// 单位mswhile (k--){int tmp = nums[numsSize - 1];for (int i = numsSize - 1; i > 0; --i){nums[i] = nums[i - 1];}nums[0] = tmp;}// 记录结束时间int end = clock();printf("time:%d\n", end - start);return 0;
}

发现多次的运行结果不一样,具体运行时间到底是0ms还是1ms?所以这不是具体的运行时间数字,也就是说没有办法得到准确的数字。所以算法的时间复杂度是一个函数式。

在这里插入图片描述

那么算法的时间复杂度是一个函数式T(N)到底是什么呢?这个T(N)函数式计算了程序的执行次数(一个程序,只要知道了有多少语句以及每个语句的执行次数就可以知道大概的时间复杂度了,就是根据执行次数评估运行时间)。

通过c语言编译链接章节学习,我们知道算法程序被编译后生成二进制指令,程序运行,就是cpu执行这些编译好的指令。


一个程序消耗的时间资源应该与输入的变量和程序本身两者都有关系,如果时间复杂度只研究变量对程序消耗时间资源的影响,那么用什么来表示程序本身对时间的影响?

程序本身里有条件判断语句、有break语句、有变量的定义、有循环语句等。变量的定义相较于循环语句,很明显循环语句的执行次数较多。程序会被编译成二进制指令,二进制指令的数量可以忽略不计,比如变量的定义是2条二进制指令,for循环语句是10条二进制指令,2条、10条指令在计算机里可以忽略不计,因为CPU在1s中可以执行上亿条指令。既然可以不去关注当前语句产生的二进制指令的数量,那就认为每条语句就是一条二进制指令,所以循环语句是一条指令,变量的定义也是一条指令:

int count = 0;// 一条指令
for (int i = 0; i < N; ++i)// 一条指令{}

当前定义变量的时间是计算不出来的,for循环定义变量i的时间也是计算不出来的。每条语句的运行时间 * 执行次数 = 总的执行时间,但是运行时间是不能确定的,因为跟编译环境和运行机器的配置都有关系,所以就认为每条语句的运行时间大差不差。那么就重点看执行次数,变量的定义只执行1次,所以循环才是拉开差距的地方。所以计算时间复杂度绝大多数情况下看的是循环,但也不是只看循环,递归算法的时间复杂度那就看的是递归,递归其实也是一种循环。那用什么来表示程序本身?程序本身就是由一条一条语句组成的。


不同语句执行时被编译成个数不同的二进制指令,每一条语句被编译成几条二进制指令是不知道的。假如,下面的int count = 0;语句被编译成两条二进制指令、for循环嵌套语句被编译成10条二进制指令、最下面for循环语句被编译成20条二进制指令。2条、10条、20条…,各语句指令的多少对于CPU执行这些指令时是没有区别的,因为CPU在1s中内可以执行上亿条指令,被编译成多少条指令对结果是没有影响的,所以不需要针对二进制指令计算,仅需针对一行一行的代码执行次数计算,比如,可以把int count = 0;看成一条指令计算(只执行一次);for循环,循环N次,代码执行N次,可以看成N条指令计算。再就是CPU的执行速度很快,对变量的单次定义执行1次可以忽略不计,所以看的都是循环语句。

那么我们通过程序代码或者理论思想计算出程序的执行次数的函数式T(N),假设每句指令执行时间基本一样(实际中有差别,但是微乎其微),那么执行次数和运行时间就是等比正相关,这样也脱离了具体的编译运行环境。执行次数就可以代表程序时间效率的优劣。比如解决一个问题的算法a程序T(N) = N,算法b程序T(N) = N^2,那么算法a效率一定优于算法b。


(1)、案例

算法的时间复杂度是一个函数式T(N),试着写出下面示例的时间复杂度:

// 请计算⼀下Func1中++count语句总共执行了多少次?
void Func1(int N)
{int count = 0;// 执行1次忽略不计for (int i = 0; i < N; ++i){for (int j = 0; j < N; ++j){++count;}}for (int k = 0; k < 2 * N; ++k){++count;}int M = 10;// 执行1次忽略不计while (M--){++count;}
}

函数式受输入变量N决定。计算当前的时间复杂度函数式实际上看的是循环语句,变量的单次定义可以忽略不计。函数式:T(n) = n^2 + 2n + 10(n影响最终的T(n))。时间复杂度衡量的是变量对算法效率结果的影响趋势(也可以说变量对最终时间复杂度的影响趋势:随着n的增加,时间复杂度会发生什么变化),也就是n越大,执行次数越多,时间复杂度越差;n越小,执行次数越少,时间复杂度越好。

给定一个N,n^2对结果的影响最大,因为CPU在1s中之内可以执行上亿次,所以2n + 10对结果的影响不大:

在这里插入图片描述

所以,T(n)可以写成T(n) = n^2。那么可不可以说当前的时间复杂度就是T(n) = n^2呢?——不是的。实际的时间复杂度是用大O的渐进表示法表示的:本题时间复杂度为O(n^2),这里的O就是大O的渐进表示法。实际中我们计算时间复杂度时,计算的也不是程序的精确的执行次数,精确执行次数计算起来还是很麻烦的(不同的一句程序代码,编译出的指令条数都是不一样的),计算出精确的执行次数意义也不大,因为我们计算时间复杂度只是想比较算法程序的增长量级,也就是当N不断变大时T(N)的差别,上面我们已经看到了当N不断变大时常数和低阶项对结果的影响很小,所以我们只需要计算程序能代表增长量级的大概执行次数(函数式),复杂度的表示通常使用大O的渐进表示法

理解一下“时间复杂度衡量的是变量对算法效率结果的影响趋势”中的趋势:就拿时间复杂度为O(n^2)为例,趋势用坐标轴表示。x轴表示变量n,y轴表示最终的时间复杂度。时间复杂度的趋势:随着n的增加,n^2的趋势会陡一些,时间复杂度也随之增加。

在这里插入图片描述

(2)、大O的渐进表示法

大O符号(Big O notation):是用于描述函数渐进行为的数学符号。

在这里插入图片描述

(3)、时间复杂度计算示例

示例1

// 计算Func2的时间复杂度?
void Func2(int N)
{int count = 0;for (int k = 0; k < 2 * N; ++k){++count;}int M = 10;while (M--){++count;}printf("%d\n", count);
}

函数式:T(n) = 2n + 10,根据推导大O阶规则1,只保留最高阶项,T(n) = 2n;根据推导大O阶规则2,T(n) = n。

所以Func2的时间复杂度是O(n)

时间复杂度增长趋势:随着n的增加,n相较于n^2的趋势会较平缓一些,时间复杂度也随之增加。

在这里插入图片描述

示例2

// 计算Func3的时间复杂度?
void Func3(int N, int M)
{int count = 0;for (int k = 0; k < M; ++k){++count;}for (int k = 0; k < N; ++k){++count;}printf("%d\n", count);
}

函数式:T(n) = m + n。T(n)中的n是指代变量,并不意味着等号右边只有n是变量,这里有两个变量:m和n。比如说指代同学1为数学课代表,也指代同学2为数学课代表,但是同学1和同学2不是同一个人,只是用数学课代表指代这两位同学。这里的高阶项即是m也是n,没有低阶项,高阶项的系数也是1,所以时间复杂度为O(m + n)。但是这两变量中怎么能确定就没有低阶项呢?往下分析,针对m + n,有3种情况去讨论:

  • m == n,时间复杂度:O(n)或者O(m)
  • m >> n,时间复杂度:O(m)。(注意:这里不是m > n,若是m > n,时间复杂度就是O(m)或者O(n),同理<的时间复杂度也是O(m)或者O(n),>或<就没有比较的意义)
  • m << n,时间复杂度:O(n)

其实默认情况下时间复杂度就是O(m + n),因为不知道哪个变量大。

示例3

// 计算Func4的时间复杂度?
void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++k){++count;}printf("%d\n", count);
}

函数式:T(n) = 100,根据推导大O阶规则第3条,T(n) = 1。所以时间复杂度是O(1)

时间复杂度增长趋势:n怎么变化都始终不会影响到最终的时间效率。不管T(n)多大,始终是一条平滑的直线,没有任何趋势,所以可以用常数1取代所有加法常数。时间复杂度研究的不是具体的时间,而是研究变量对最终时间复杂度的影响趋势。所以这里时间复杂度为O(1)。

在这里插入图片描述

示例4:在字符串里面查找一个字符

// 计算strchr的时间复杂度?
const char* strchr(const char* str, char character)
{const char* p_begin = str;while (*p_begin != character){if (*p_begin == '\0')return NULL;p_begin++;}return p_begin;
}

时间复杂度跟当前字符串的长度有关系。假设给了一个字符串"abcd……uithjl",长度为n,时间复杂度取决于字符串的长度n。当查找 ‘a’ 字符时,发现就查找一次就行;当查找b字符时,发现就查找两次;假设整个字符串中就最后一个有效字符是l,那么要查找n次。本题的时间复杂度取决于输入变量:字符串的长度以及查找的字符。若查找的字符在字符串的前面的位置,那么时间复杂度基本是常数,也就是O(1);若查找的字符在字符串的后面的位置,不管是n、n - 1、n - 2,时间复杂度都是O(n);若查找的字符在字符串的中间以及附近的位置,只需要遍历n/2次左右,根据推导大O阶规则,时间复杂度为O(n)。

因此,这个时间复杂度分为:
最好情况(类比在字符串前面查找到指定字符):O(1)
最坏情况(类比在字符串后面查找到指定字符):O(n)
平均情况(类比在字符串中间查找到指定字符):O(n)

发现这个时间复杂度没办法立马给出来,它是要根据情况去定的,所以像这样,时间复杂度要分为3种情况:最好情况、最坏情况、平均情况。

但是一般算法的好与坏我们看的一定是最坏的情况。 所以本题时间复杂度是O(n)

在这里插入图片描述

示例5

// 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}

冒泡排序有循环的嵌套,循环嵌套的时间复杂度一定是O(n^2)吗?——不一定。是需要具体分析的。

外层循环定义变量end,有n个数据进行冒泡排序。
第一次循环,end = n,内层循环i < n,数据要比较n - 1次。
第二次循环,end = n - 1,内层循环i < n - 1,数据要比较n - 2次。

第n次循环,数据要比较1次。

所以本题的时间复杂度计算的就是n次循环中总的执行次数,总的执行次数T(n) = 1+2+3+…+(n-2)+(n-1)。等差数列求和。

在这里插入图片描述
根据推导大O阶规则,T(n) = n^2。 时间复杂度是O(n^2)

示例6

void func5(int n)
{int cnt = 1;while (cnt < n){cnt *= 2;}
}

在这里插入图片描述

有时会把底数省略了,写成logn,这种写法在数学中一定是错误的,但是在计算机中是可以这样写的。原因1:没办法用键盘敲出底数。原因2:

在这里插入图片描述
所以本题时间复杂度是O(logn)

示例7

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{if (0 == N)return 1;return Fac(N - 1) * N;
}

这里没有循环欸,不要忘了,递归也可以通过循环的方式去写,只是算法写法不一样。所以递归也是一种循环

计算递归的时间复杂度:

当前函数栈帧中有Fac(N),调用Fac(N),传递变量N。若N不是0,则进行递归调用,调用Fac(N - 1);若N - 1不是0,进行递归,调用Fac(N - 2)…。一直递归到参数为0为止,再向上回溯。

递归算法的时间复杂度,先说结论:递归算法的时间复杂度 = 单次递归的时间复杂度 * 递归次数

不断递归的过程就是循环的过程,每一次递归调用就是一次循环。每递归一次就调用一次函数,创建函数栈帧。单次递归函数时,函数体内没有循环,所以不管是Fac(N-1)、Fac(N-2)、… 、Fac(1)、Fac(0),单次递归的时间复杂度都是O(1)。

那么递归次数怎么计算呢?注意是递归次数,除了Fac(N)不是递归,是运行后直接调用的,剩下的都是递归调用。Fac(N-1)、Fac(N-2)、… 、Fac(1)、Fac(0)一共是N次递归。所以时间复杂度是O(n)

在这里插入图片描述

四、空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中因为算法的需要额外临时开辟的空间

空间复杂度不是程序占用了多少bytes的空间,因为常规情况每个对象大小差异不会很大,所以空间复杂度算的是变量的个数

空间复杂度计算规则基本跟时间复杂度类似,也使用大O渐进表示法

注意:函数运行时所需要的栈空间(栈空间:函数栈帧空间,函数在调用运行时会去申请的空间。存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时,看函数体里是否有显式申请的额外空间来确定

空间复杂度计算示例

示例1:冒泡排序的空间复杂度

// 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}

结论:函数栈帧在编译期间已经确定好了,只需要关注函数在运行时额外申请的空间。BubbleSort额外申请的空间有exchange等有限个局部变量,使用了常数个额外空间,空间复杂度对应函数表达式F(1),根据推导大O阶规则。因此空间复杂度为 O(1)

这里有无符号整型变量end、整型变量exchange。计算占用空间大小是通过字节个数,但是函数体内所有变量所占的字节总数并不是空间复杂度。因为对于内存来说,字节单位太小了,所以这里仍然粗略地去估计。就让变量end、exchange是一个一个单位,具体一个单位是多少字节是不需要去关注的。既然变量是一个一个单位,同样的在空间复杂度这里,变量所占空间也可以忽略。

示例2:阶乘递归的空间复杂度

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{if (N == 0)return 1;return Fac(N - 1) * N;
}

结论:Fac递归调用了N次,额外开辟了N个函数栈帧,每个栈帧使用了常数个空间,因此空间复杂度为 O(N)

同理,计算递归的空间复杂度 = 单次递归的空间复杂度 * 递归次数。单次递归就是调用当前Fac这个函数,里面没有显示申请额外的空间,所以使用了常数个空间,所以单次递归的空间复杂度是O(1)。

研究复杂度研究的是增长趋势。N越大,递归次数越多。例如,N为10,递归10次;N为100,递归100次;N为1000,递归1000次。曲线增长趋势:

在这里插入图片描述

五、常见复杂度对比

从左到右是复杂度的变化,除了logn,随着输入变量的增加,复杂度的增长趋势逐渐变陡:

在这里插入图片描述

增长趋势越陡,复杂度越差,算法越差:

在这里插入图片描述

六、复杂度算法题

轮转数组

轮转数组

思路1:循环k次将数组的每一个元素都向后挪动一位

时间复杂度是O(n^2),代码不通过。“超出时间限制”:时间复杂度不够好。

空间复杂度是O(1)。

void rotate(int* nums, int numsSize, int k) {while (k--)// 注意是后置++{int tmp = nums[numsSize - 1];for (int i = numsSize - 1; i > 0; --i){nums[i] = nums[i - 1];}nums[0] = tmp;}
}

时间复杂度是O(n^2),用n指代输入条件:k是多少是不知道的,i是根据numsSize确定的,而numsSize也是一个输入变量,具体是多少也是不知道的。

若k = 7,轮转后的结果还是原数组,所以解决k太大的问题:k %= numsSize。7%7 = 0,这样k就回到下标为0的位置了。

在这里插入图片描述
"提交"后还是会有"超出时间限制"的问题。若numsSize是一个亿,k是一个亿减去1,那这里的循环次数还是很多,也就是k接近numsSize时,时间复杂度还是O(n^2)。

在这里插入图片描述

思路2:创建一个与原数组nums的空间一样大的数组tmp,将数据挪动到新数组数据轮转后对应的位置,再一一覆盖回原数组

优化:将时间复杂度降到O(n)。

创建一个新数组回额外开一块空间,所以空间复杂度是O(n)。

void rotate(int* nums, int numsSize, int k) {// 创建一个新数组int tmp[numsSize];// 遍历原数组,将数据挪动到新数组对应的位置for (int i = 0; i < numsSize; ++i){tmp[(k + i) % numsSize] = nums[i];}// 新数组数据一一覆盖回原数组for (int i = 0; i < numsSize; ++i){nums[i] = tmp[i];}
}

思路2与思路1对比,时间复杂度降下来了。为了能突破时间限制会额外开辟空间使代码通过,这本质是一种空间换时间

思路3:最优解,三次逆置。时间复杂度是O(n),空间复杂度是O(1)

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

看"最后执行的输入"这里,k比数据个数大,当数组中只有一个数据,k = 2。第一次逆置,right为-2、left为0,进入不了逆置循环代码;第二次逆置,left为-1、right为0,进入逆置循环nums[left]此时是-1位置的数据,取不到的所以报错。

在这里插入图片描述
特殊处理:若k > numsSize,k %= numsSize。

完整代码:

void reverse(int* nums, int left, int right)
{while (left < right){int tmp = nums[left];nums[left] = nums[right];nums[right] = tmp;++left;--right;}
}
void rotate(int* nums, int numsSize, int k) {k %= numsSize;// 第一次逆置,前numsSize - k个数据逆置reverse(nums, 0, numsSize - k - 1);// 注意传递的是下标// 第二次逆置,后k个数据逆置reverse(nums, numsSize - k, numsSize - 1);// 第三次逆置,整体逆置reverse(nums, 0, numsSize - 1);
}

相关文章:

第一讲 | 算法复杂度

算法复杂度 一、数据结构前言1、数据结构&#xff08;DS&#xff09;2、算法&#xff08;Algorithm&#xff09; 二、算法效率1、复杂度的概念 三、时间复杂度&#xff08;1&#xff09;、案例&#xff08;2&#xff09;、大O的渐进表示法&#xff08;3&#xff09;、时间复杂度…...

【运维】还原 Docker 启动命令的利器:runlike 与 docker-autocompose

&#x1f50d; 还原 Docker 启动命令的利器&#xff1a;runlike 与 docker-autocompose 实用教程 在日常使用 Docker 时&#xff0c;我们常常通过 docker run 启动容器&#xff0c;但有时候过了一段时间就忘记了当初使用的具体参数&#xff08;端口、挂载、环境变量等&#xf…...

IP属地是实时位置还是自己设置

刷微博、抖音时&#xff0c;评论区总能看到“IP属地”&#xff1f;这个突然冒出来的小标签&#xff0c;让不少网友摸不着头脑&#xff1a;‌IP属地是实时位置&#xff0c;还是可以自己设置&#xff1f;‌别急&#xff0c;今天咱们就来聊聊这个话题&#xff01; 1、什么是IP属地…...

Android WIFI体系

先说说WifiLock、MulticastLock 、IWificond WifiLock 允许应用强制保持 WiFi 活跃&#xff0c;即便设备处于休眠状态。如WIFI_MODE_FULL_HIGH_PERF&#xff1a;保持高性能 WiFi 活跃状态&#xff0c;适用于高带宽需求&#xff0c;如视频通话、流媒体。经测试有的场景能减少10…...

什么是静态住宅ip,跨境电商为什么要用静态住宅ip

在数字时代&#xff0c;IP地址不仅是设备联网的“ID”&#xff0c;更是跨境电商运营中的关键工具。尤其对于需要长期稳定、安全操作的场景&#xff0c;静态住宅IP逐渐成为行业首选。 一、什么是静态住宅IP&#xff1f; 静态住宅IP&#xff08;Static Residential IP&#xff0…...

常见位运算总结

目录 常见位运算总结 191:位1的个数 338&#xff1a;比特位计数 461:汉明距离 136&#xff1a;只出现一次的数字 260&#xff1a;只出现一次的数字III 常见位运算总结 191:位1的个数 链接&#xff1a;191. 位1的个数 - 力扣&#xff08;LeetCode&#xff09; class Sol…...

[密码学实战]SDF之对称运算类函数(四)

[密码学实战]SDF之对称运算类函数(四) 一、标准解读:GM/T 0018-2023核心要求 1.1 SDF接口定位 安全边界:硬件密码设备与应用系统间的标准交互层 功能范畴: #mermaid-svg-1jptduZFNFiRZ2lS {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16…...

【C++编程入门】:基本语法

上一篇提到了C关键字和缺省参数以及命名空间域&#xff0c;这篇继续分享C入门语法&#xff0c;把基本语法掌握扎实后面学习更才能更轻松一些。 目录 引用 引用的特性 常引用 内联函数 auto关键字 引用 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&am…...

区块链最佳框架:Truffle vs Hardhat vs Brownie

区块链技术的快速发展使得智能合约开发成为主流&#xff0c;而选择合适的开发框架是提升效率的关键。目前&#xff0c;Truffle、Hardhat和Brownie是三大主流框架&#xff0c;它们各有特点&#xff0c;适用于不同的开发场景和开发者偏好。本文将从功能、生态系统、适用人群等角度…...

Apache Flink的架构设计与运行流程说明

在大数据领域&#xff0c;实时计算的重要性随着业务需求的爆发式增长愈发凸显。从电商的实时销量监控到金融的高频交易风控&#xff0c;从物联网设备的实时告警到社交平台的热点追踪&#xff0c;企业对“秒级甚至毫秒级”数据处理能力的需求已成为刚需。在众多实时计算框架中&a…...

AI+零售:智能推荐、无人店与供应链管理的未来

AI零售&#xff1a;智能推荐、无人店与供应链管理的未来 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 AI零售&#xff1a;智能推荐、无人店与供应链管理的未来摘要引言一、智能推荐系统&#xff1a;从流量收割到用…...

华为云IoT平台与MicroPython实战:从MQTT协议到物联网设备开发

目录 前言 1. 华为云 1.1. 创建实例 1.2. 创建产品 1.3. 编辑服务模型 1.4. 注册设备 1.4.1. 复制设备连接参数 1.5. 连接参考代码 2. micropython版-物联网 2.1. 环境搭建 2.2. 实现步骤 2.3. 示例代码 结语 前言 物联网&#xff08;IoT&#xff09;技术的快速发…...

【Linux】Linux内核模块开发

Linux内核模块开发 零、关于 1、概述 最近在学习Linux相关的东西&#xff0c;学习了U-Boot的编译&#xff0c;Linux的编译&#xff0c;能够在开发板上运行自己编译的U-Boot和Linux了&#xff0c;那么接下来就是在自己编译的Linux上做应用级或者系统级的开发了。本文以字符设…...

linux 下查看指定进程的内存CPU占用情况(用于程序崩溃类的排查)

在程序开发过程中&#xff0c;如果程序较为庞大&#xff0c;逻辑较为复杂时&#xff0c;容易出现运行时崩溃的问题。导致的原因有很多&#xff0c;我这里只对较为通用的内容占用情况作记录&#xff0c;如程序中对文件描述符打开未关闭&#xff08;导致fd积攒过多超过了系统的标…...

ASP.NET MVC​ 入门指南五

26. 响应式设计与移动开发 26.1 响应式视图设计 为了使 MVC 应用程序在不同设备上都能提供良好的用户体验&#xff0c;需要采用响应式设计。可以使用 CSS 框架如 Bootstrap 来实现响应式布局。 引入 Bootstrap&#xff1a;在项目中引入 Bootstrap 的 CSS 和 JavaScript 文件。…...

字节跳动社招面经 —— BSP驱动工程师(4)

接前一篇文章&#xff1a;字节跳动社招面经 —— BSP驱动工程师&#xff08;3&#xff09; 本文内容参考&#xff1a; 嵌入式硬件平台修改启动地址-CSDN博客 特此致谢&#xff01; 上一回开始针对于“嵌入式充电站”发的一篇文章字节跳动社招面经——BSP驱动工程师中的面试题…...

Spring MVC中自定义日期类型格式转换器

在Spring MVC中&#xff0c;自定义日期类型格式转换器可以通过实现Converter接口或使用DateTimeFormat注解。以下是两种方法的详细说明&#xff1a; 方法一&#xff1a;全局自定义转换器&#xff08;推荐&#xff09; 1. 创建日期转换器类 实现 org.springframework.core.con…...

【3D 地图】无人机测绘制作 3D 地图流程 ( 无人机采集数据 | 地图原始数据处理原理 | 数据处理软件 | 无人机测绘完整解决方案 )

文章目录 一、无人机采集数据1、多角度影像数据2、定位与姿态数据 二、无人机采集数据处理原理1、空三解算2、密集点云生成与三维重建3、地形与正射影像生成4、三维模型优化与瓦片化 三、无人机影像处理软件介绍 一、无人机采集数据 无人机原始数据采集 : 多角度影像数据 : 多…...

arduino Nano介绍

【仅供学习&#xff0c;具体参数参考官网或销售商】 Arduino Nano 是一款基于 ATmega328P 微控制器&#xff08;或 ATmega168 旧版&#xff09;的紧凑型开发板&#xff0c;专为嵌入式项目和原型设计而设计。 以下是Arduino Nano V3.0 328P详细介绍&#xff1a; 主要特性 微…...

解决 Flutter 在 iOS 真机上构建失败的问题

在开发 Flutter 应用时&#xff0c;有时会在尝试将应用部署到 iOS 真机时遇到构建失败的问题。错误信息通常类似于以下内容&#xff1a; Could not build the precompiled application for the device. Uncategorized (Xcode): Timed out waiting for all destinations matchi…...

【办公类-89-03】20250429AI写的研讨记录,清除格式,统一格式,名字替换。部分加粗,添加页眉

背景需求: 检查自即,需要AI一下院内的五次科研培训记录。 本次用了豆包 豆包写的不错,也是“水字数”的高手 把每次培训内容贴到WORD里 把AI资料贴到WORD里,发现问题: 1、字体、段落什么都是不统一的,需要统一改成宋体小四,1.5倍行距 2、十个研讨人也要改成真人。就找…...

react-native 安卓APK打包流程

一、使用keytool命令生成一个签名密钥 $ keytool -genkeypair -v -storetype PKCS12 -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000 在 Windows 上keytool命令放在 JDK 的 bin 目录中&#xff08;比如C:\Program Files\…...

Android Studio中OpenCV应用详解:图像处理、颜色对比与OCR识别

文章目录 一、OpenCV在Android中的集成与配置1.1 OpenCV简介1.2 在Android Studio中集成OpenCV1.2.1 通过Gradle依赖集成1.2.2 通过模块方式集成1.2.3 初始化OpenCV 1.3 OpenCV基础类介绍 二、指定区域图像抓取与对比2.1 图像抓取基础2.2 指定区域图像抓取实现2.2.1 从Bitmap中…...

企业办公协同平台安全一体化生态入住技术架构与接口标准分析报告

全球组织数字化与智能化背景下 企业办公协同平台安全一体化生态入住技术架构与接口标准分析报告 一、背景与市场需求 市场规模与增量 根据Statista数据&#xff0c;全球协同办公平台市场规模预计从2023年的$480亿增长至2027年的$900亿&#xff0c;年复合增长率&#xff08;CAG…...

从零搭建体育比分网站:技术选型与API调用实战(附完整源码)

一、前言&#xff1a;为什么选择体育比分项目&#xff1f; 体育数据网站是练手全栈开发的绝佳项目&#xff0c;涉及&#xff1a; ✅ 前端&#xff08;实时数据渲染、可视化图表&#xff09; ✅ 后端&#xff08;API对接、数据缓存、高并发优化&#xff09; ✅ 数据库&#xff…...

非凸科技受邀出席AI SPARK活动,共探生成式AI驱动金融新生态

4月19日&#xff0c;由AI SPARK社区主办的“生成式AI创新与应用构建”主题沙龙在北京举行。活动聚焦生成式AI的技术突破与产业融合&#xff0c;围绕大模型优化、多模态应用、存内计算等前沿议题展开深度探讨。非凸科技受邀出席并发表主题演讲&#xff0c;深入解析金融垂直大模型…...

深入蜂窝物联网 第五章 EC-GSM-IoT 及其他技术:混合组网与前瞻

1. 前言与应用场景 在一些地区,GSM 网络仍然大面积覆盖且运营成本低廉,运营商可通过 EC-GSM-IoT(Extended Coverage GSM for IoT)在现有GSM基站上升级,实现物联网互联。同时,为了满足不同场景的需求,常常需要与 NB-IoT、LTE-M、5G RedCap 等技术混合组网,形成多层次、…...

2025年深圳软件开发公司推荐

随着移动互联网的深度发展&#xff0c;软件开发已成为企业实现数字化转型的重要途径。作为中国科技创新中心的深圳&#xff0c;汇聚了众多技术实力雄厚的软件开发企业。本文将为您精选推荐6家在深圳表现突出的软件开发服务商&#xff0c;帮助企业找到合适的数字化转型合作伙伴。…...

仿腾讯会议——注册登录UI

1、加载素材 2、新添加资源类 3、加载图片 4、添加左侧图片 在左侧添加一个标签 选择图片 选择图片 勾选保证图片不变形 5、修改组件名称 6、设置密码输入框 5、切换 6、编辑提示框 7、定义提交和清空的槽函数 8、设置页面标题和最先显示页面 9、清空登录信息函数实现 10、清空…...

香港科技大学广州|可持续能源与环境学域博士招生宣讲会—四川大学专场

香港科技大学广州&#xff5c;可持续能源与环境学域博士招生宣讲会—四川大学专场 时间&#xff1a;2025年5月8日&#xff08;星期四&#xff09;16:30开始 地点&#xff1a;四川大学基础教学楼A座504 宣讲嘉宾&#xff1a;肖殿勋 助理教授 一经录取&#xff0c;享全额奖学金…...

设计模式(工厂模式)

工厂设计模式&#xff1a;打造你的代码生产线 引言 想象一下&#xff0c;你正站在一家现代化的玩具工厂门前。工厂内部&#xff0c;各种机器有条不紊地运转&#xff0c;原材料在传送带上流动&#xff0c;最终变成精美的玩具。你不需要了解每个玩具的具体制作工艺&#xff0c;…...

本地大模型编程实战(29)查询图数据库NEO4J(2)

上一篇文章 用大语言模型LLM查询图数据库NEO4J(1) 介绍了使用GraphQACypherChain查询NEO4J。用它实现简单快捷&#xff0c;但是不容易定制&#xff0c;在生产环境中可能会面临挑战。 本文将基于langgraph 框架&#xff0c;用LLM(大语言模型)查询图数据库NEO4J。它可以定义清晰复…...

Python爬虫(10)Python数据存储实战:基于pymongo的MongoDB开发深度指南

目录 一、为什么需要文档型数据库&#xff1f;1.1 数据存储的范式变革1.2 pymongo的核心优势 二、pymongo核心操作全解析2.1 环境准备2.2 数据库连接与CRUD操作2.3 聚合管道实战2.4 分批次插入百万级数据&#xff08;进阶&#xff09;2.5 分批次插入百万级数据&#xff08;进阶…...

从遍历序列构造二叉树:前序+中序与中序+后序的递归解法详解

文章目录 1. 问题背景2. 核心思路3. 从前序与中序遍历序列构造二叉树3.1 递归分治思路3.2 代码实现与注释 4. 从中序与后序遍历序列构造二叉树4.1 递归分治思路4.2 代码实现与注释 5. 复杂度分析6. 总结 1. 问题背景 二叉树的遍历方式包括前序&#xff08;根-左-右&#xff09…...

数据挖掘专栏介绍:用 Python + 大语言模型 (LLM) 重塑电商数据价值

写在前面 —— 不止于挖掘,更要智能涌现:用 Python + 大语言模型 (LLM) 重塑电商数据价值 或许你已经跟随我们之前的 “零基础上手Python数据分析” 专栏,掌握了 Pandas 的数据操纵、Matplotlib/Seaborn 的可视化呈现,甚至对传统的数据挖掘技术如聚类、分类、回归有了初步…...

CSS Transition入门指南

CSS Transition 完全指南 目录 Transition 的作用核心属性代码示例使用场景性能优化常见问题思维导图 1. Transition 的作用 CSS Transition 用于在 属性值变化时 创建平滑的过渡效果。例如&#xff1a; 鼠标悬停时按钮放大元素颜色渐变切换位置移动的缓动效果 2. 核心属性…...

Nginx 核心功能

目录 一、基于授权的访问控制 &#xff08;1&#xff09;使用htpasswd 生成用户认证文件 &#xff08;2&#xff09;修改密码文件权限为400&#xff0c;将所有者改为nginx &#xff0c;设置 Nginx 的运行用户能够读取 &#xff08;3&#xff09;修改主配置文件 nginx.conf&…...

排序版研究方向

姓 名研究方向电子邮箱办公电话办公地点曹培根代数cao2024ustc.edu.cn 新楼412陈洪佳代数hjchenustc.edu.cn0551-636076931529陈小伍代数xwchenmail.ustc.edu.cn0551-636062351321梁永祺代数yqliangustc.edu.cn0551-636006171613欧阳毅代数yiouyangustc.edu.cn0551-63600337…...

AI国学智慧语录视频,条条视频10W+播放量

家人们&#xff01;图书类带货玩法真的非常多&#xff0c;之前也分享过蛮多&#xff0c;例如情感语录、育儿教育、爆款图书金句类、AI历史人物解说类等等。 本期继续来分享一个对于普通人来说&#xff0c;上手相当简单&#xff0c;容易起号&#xff0c;可作为长线深耕的AI带货…...

RN 获取视频封面,获取视频第一帧

严格的说&#xff0c;没有解决这个问题&#xff0c;实际上是绕过了这个问题&#xff0c;严格的说获取的也不是第一帧。 RN的video 视频应用&#xff0c;大多是这样的 1、安装 yarn add react-native-video 2、导入 import Video from react-native-video; 3、使用 …...

缓存分片哈希 vs 一致性哈希:优缺点、区别对比及适用场景(图示版)

&#x1f4dc; 引言 在分布式缓存系统中&#xff0c;数据分布策略是设计的关键之一。缓存分片哈希和一致性哈希是两种常见的数据分布算法&#xff0c;它们各有优缺点和适用场景。本文将通过图示对比表格&#xff0c;深入解析这两种算法的核心原理、优缺点及适用场景。 &#x…...

iOS - 音频: Core Audio - 播放

环境 iOS 18 Xcode 16.3 swift-driver version: 1.120.5 Apple Swift version 6.1 (swiftlang-6.1.0.110.21 clang-1700.0.13.3) Target: x86_64-apple-macosx15.0 Core Audio 的架构 声音数据的描述 sample: 一个声道采样的值,采样率定义了每秒从连续信号中提取并组成离散信号…...

Nerfstudio 环境配置与自有数据集(图片和视频)测试全方位全流程实战【2025最新版!!】

一、引言 神经辐射场(Neural Radiance Fields&#xff0c;简称NeRF)是近年来计算机视觉和图形学领域的一项革命性技术&#xff0c;它能够从2D图像中学习复杂的3D场景表示。然而&#xff0c;NeRF技术的实现和应用门槛较高&#xff0c;需要较为专业的计算机视觉和深度学习知识。…...

【Java学习】动态代理有哪些形式?

Java动态代理的两种主要形式 动态代理在Java中有两种主要的实现方式&#xff0c;它们各有特点和使用场景&#xff1a; 1. JDK动态代理 (基于接口) 特点&#xff1a; Java标准库自带的功能&#xff08;java.lang.reflect.Proxy&#xff09;只能代理接口&#xff0c;不能代理…...

Android Studio 中实现方法和参数显示一行

Android Studio 中实现方法和参数显示一行&#xff0c;可通过以下步骤配置&#xff1a; 一、基础格式化设置 ‌快捷键格式化‌ 选中代码后使用 Ctrl Alt L&#xff08;Windows/Linux&#xff09;或 Cmd Option L&#xff08;Mac&#xff09;进行快速格式化27。 ‌菜单操作…...

SQLyog中DELIMITER执行存储过程时出现的前置缩进问题

在SQLyog中执行存储过程时出现的前置缩进问题&#xff0c;实际上反映了SQLyog对SQL语句解析的一个特殊行为。以下是详细解释和解决方案&#xff1a; 问题根源 SQLyog的语句分隔逻辑&#xff1a; SQLyog默认会根据分号(;)和换行自动分隔SQL语句 当代码有缩进时&#xff0c;SQLy…...

基于Spring Boot 3.0、ShardingSphere、PostgreSQL或达梦数据库的分库分表

要实现基于Spring Boot 3.0、ShardingSphere、PostgreSQL或达梦数据库的分库分表&#xff0c;首先需要对ShardingSphere进行一些基本配置。你提到的溯源码、批次号等数据需要考虑到跨年数据的存储&#xff0c;因此要设计一个能够动态扩展的分表策略 添加ShardingSphere依赖 在…...

vscode chrome调试怎么在所有浏览器都好使

chrome调试时只能在打开的浏览器里进行调试&#xff0c;其它打开的chrome浏览器就不能调试了&#xff0c;怎么解决。 右键点击 Chrome 的快捷方式图标&#xff0c;选择属性 在目标一栏&#xff0c;最后加上--remote-debugging-port9222 注意要用空格隔开 lanch.json 文件配置 …...

20250429在Ubuntu 20.04.6下安装VMware Workstation16

20250429在Ubuntu 20.04.6下安装VMware Workstation16 2025/4/29 20:16 缘起&#xff1a;1、在ubuntu14.04下git clone异常该如何处理呢&#xff1f; 2、请问 现在 编译NanoPi NEO的FriendlyCore系统使用ubuntu哪一个版本比较好&#xff1f; ubuntu14.04 编译异常/下载不了&am…...

Java高频面试之并发编程-10

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;ThreadLocalMap 怎么解决 Hash 冲突的&#xff1f; ThreadLocalMap 是 ThreadLocal 的核心实现&#xff0c;它采用 开放…...