数据结构----算法复杂度

 

1.数据结构前言

数据是杂乱无章的,我们要借助结构将数据管理起来

1.1 数据结构

数据结构(Data Structure)是计算机存储、组织数据的⽅式,指相互之间存在⼀种或多种特定关系的数

据元素的集合。没有⼀种单⼀的数据结构对所有⽤途都有⽤,所以我们要学各式各样的数据结构,

如:线性表、树、图、哈希等

1.2 算法

算法(Algorithm):就是定义良好的计算过程,他取⼀个或⼀组的值为输⼊,并产⽣出⼀个或⼀组值作为输出。简单来说算法就是⼀系列的计算步骤,⽤来将输⼊数据转化成输出结果。

好的算法能帮助我们更好的管理数据

数据结构是一种载体存储数据

算法是一种方法,管理数据

数据结构与算法不分家

 

 

学好数据结构可以提升面试和笔试

评估算法的好坏我们从复杂度进行评估

2.算法效率

/*给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。示例 1:输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]提示:1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105进阶:尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?*/
void rotate(int* nums, int numsSize, int k)
{while(k--){int end=nums[numsSize-1];//创建一个临时变量将数组中最后一个数的值进行保存for(int i=numsSize-1;i>0;i--)//将数组中的数整体右移一位{nums[i]=nums[i-1];//在右移的时候我们要从后向前移,我们现将倒数第二个数赋值到最后一个数的位置上,然后随着i的变化进行右移操作}nums[0]=end;//最后我们将之前保存的值放到以一个位置上}}//但是这个存在限制,虽然这个代码没有问题,算法虽然没问题,但是超过时间限制,一但遇到大的数据,时间就变长了

第一种方法虽然算法是对的,但是一但遇到大的数据就计算不过来,计算时间很长

那么就衍生出了算法好坏的概念:算法复杂度

我们通过算法复杂度去衡量算法的好坏

复杂度分为时间复杂度和空间复杂度

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

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

3.时间复杂度

时间复杂度铺垫

定义:在计算机科学中,算法的时间复杂度是⼀个函数式T(N),它定量描述了该算法的运⾏时间。时间复杂度是衡量程序的时间效率,

int main()
{//计算程序运行时间//计算时间我们要用到库time.h里的clock()//可以记录在不同时间段的时间//我们记录开始和结束的时间,就得到了程序运行的时间int begin=clock();//定义起始时间int count = 0;for (int i = 0; i < 100000000; i++){count++;}int end = clock();//定义结束时间//那么printf("time:%d", end - begin);//当前算法运行时间//得到的结果是0毫秒啊,如果我们给上一个亿的范围得到的就是59// 这个时间不是一个确切的时间//使用debug模式会加载一些调试信息,会占用一些我们程序的运行时间return 0;
}
//因为程序运行的时间不是一个确切的数,所以我们时间复杂度不能给出一个确切的数字

时间复杂度是衡量程序的时间效率,那么为什么不去计算程序的运⾏时间呢?

  1. 因为程序运⾏时间和编译环境和运⾏机器的配置都有关系,⽐如同⼀个算法程序,⽤⼀个⽼编译器进⾏编译和新编译器编译,在同样机器下运⾏时间不同。

  2. 同⼀个算法程序,⽤⼀个⽼低配置机器和新⾼配置机器,运⾏时间也不同。

  3. 并且时间只能程序写好后测试,不能写程序前通过理论思想计算评估。

因为程序运行的时间不是一个确切的数,所以我们时间复杂度不能给出一个确切的数字

我希望时间复杂度在编写算法之前就知道的,这个运行时间只能在程序编写好之后进行测试,不能在编写前计算评估

所以复杂度不是一个精确的数字,是一个粗估的数字

int main()
{int count = 0;//运行1次for (int i = 0; i < 100000000; i++){count++;//运行了100000000次}int end = clock();return 0;
}
//那么运行次数是100000001次

程序效率:每条语句运行时间 * 次数

这个运行时间和编译环境和运行环境相关的,这个时间是不确定的,但是运行次数我们是能确定的

随着这个运行次数的增加,运行时间是正相关的,所以我们在计算时间复杂度的时候可以直接根据运行次数进行确定

// 请计算⼀下Func1中++count语句总共执⾏了多少次?void Func1(int N)
{int count = 0;//这个for循环执行的次数就是N*Nfor (int i = 0; i < N; ++i){for (int j = 0; j < N; ++j){++count;}} //这个for循环执行的次数就是2*Nfor (int k = 0; k < 2 * N; ++k){++count;} int M = 10;//执行次数是10while (M--){++count;}
}//那么我们判断的这个题的时间复杂度是T(N)=N^2+2N+10//因为int count =0和int M=10的执行次数都是1,我们可以忽略不计的,对总的时间复杂度影响不大的//T(N)=N^2+2N+10
//对于这个算术式,N^2的影响最大,2N+10对当前复杂度影响较小,是可以忽略不计的
// 
//那么我们就能得出T(N)=N^2//可以得出时间复杂度是粗估,但是时间复杂度不是这么写的,我们要用上大O的渐进表示法
//O(n^2)就是这个题的时间复杂度了

O(n^2)就是这个题的时间复杂度了

实际中我们计算时间复杂度时,计算的也不是程序的精确的执⾏次数,精确执⾏次数计算起来还是很⿇烦的(不同的⼀句程序代码,编译出的指令条数都是不⼀样的),计算出精确的执⾏次数意义也不⼤,因为我么计算时间复杂度只是想⽐较算法程序的增⻓量级,也就是当N不断变⼤时T(N)的差别,上⾯我们已经看到了当N不断变⼤时常数和低阶项对结果的影响很⼩,所以我们只需要计算程序能代表增⻓量级的⼤概执⾏次数,复杂度的表⽰通常使⽤⼤O的渐进表⽰法。

⼤O的渐进表⽰法

⼤O符号(Big O notation):是⽤于描述函数渐进⾏为的数学符号

推导大O阶规则

1.时间复杂度函数式T(N)中,只保留最高阶项,去掉那些低阶项,因为当N不断变大时,低阶项对结果影响越来越小,当N无穷大时,就可以忽略不计了。

2.如果最高阶项存在且不是1,则去除这个项目的常数系数,因为当N不断变大,这个系数2对结果影响越来越小,当N无穷大时,就可以忽略不计了。

3.T(N)中如果没有N相关的项目,只有常数项,用常数1取代所有加法常数。

时间复杂度示例

// 计算Func2的时间复杂度?
void Func2(int N)
{int count = 0;//次数是1,忽略不计//次数是2Nfor (int k = 0; k < 2 * N; ++k){++count;} int M = 10;//次数是10while (M--){++count;} printf("%d\n", count);
}
//那么这个题我们觉得的时间复杂度是O(2N)
//但是实际确实O(N),因为这里的倍数对我们的时间复杂度影响不是很大

那么这个题就对应上了规则2

// 计算Func3的时间复杂度?
void Func3(int N, int M)
{int count = 0;//执行了M次for (int k = 0; k < M; ++k){++count;} //执行了N次for (int k = 0; k < N; ++k){++count;} printf("%d\n", count);
}
//那么我们就得到了时间复杂度的函数式:T(N)=M+N
//M和N都是执行次数,都是变量,都会影响到时间复杂度的结果
//那么这个题的时间复杂度就是O(M+N)//如果说M和N中有一个较大的项,那我们就保留高阶项
//如果M>>N的话,那么这里的时间复杂度就是O(M)
//如果M<<N的话,那么这里的时间复杂度就是O(N)
//如果M大约等于N的话,那么在时间复杂度里面就都保留
// 计算Func4的时间复杂度?
void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++k){++count;} printf("%d\n", count);
}
//执行的次数是100,那么得出的函数式是T(N)=100
//那么我们的时间复杂度就是O(1)
//这里的1不是运行一次,而是代表所有的常数,可以说这个是一个表示法
//计算strchr的时间复杂度?  字符串的查找
const char* strchr(const char* str, int character){const char* p_begin = s;while (*p_begin != character){if(*p_begin == '\0')return NULL;p_begin++;}return p_begin;
}
/*
T(N)取决于字符的长度N以及查找的位置
如果我们找了几次就找到了,那么这个时间复杂度就是O(1)
但是我们如果找到最后字符的时候才找到或者没有找到,那么我们这个时间复杂度就是O(N)
如果查找位置在中间的话,那么时间复杂度就是O(N/2)所以这个题的时间复杂度取决于这个字符串的长度和查找的位置但是对于这种有多个选项的时间复杂度呢,我们应该选择哪个作为时间复杂度呢?O(1)是最好的情况
O(N)是最坏的情况
O(N/2)是平均的情况*/

通过上⾯我们会发现,有些算法的时间复杂度存在最好、平均和最坏情况。

最坏情况:任意输⼊规模的最⼤运⾏次数(上界)

平均情况:任意输⼊规模的期望运⾏次数

最好情况:任意输⼊规模的最⼩运⾏次数(下界)

⼤O的渐进表⽰法在实际中⼀般情况关注的是算法的上界,也就是最坏运⾏情况

那么对于这个题来说,我们应该关注最差的情况,那么这个题的时间复杂度就是O(N)

// 计算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;}
}
//当数组有序的情况下,外层循环执行一次,内层循环执行N次,因为数组有序,就只执行N次,那么时间复杂度就是O(N)
// 
//外层循环执行第一次的时候,内层循环执行N次,因为不是有序的,所以我们外层循要执行N次
/*
外1  2      3  ........n
内n-1   n-2            0
那么次数就是n-1+n-2+n-3+...+1
就是等差数列求和(n-1+1)*(n-1)/2
在这个结果中对结果影响最大的是N^2,所以这个代码的时间复杂度就是O(N^2)*/

冒泡排序的时间复杂度为O(N^2)

void func5(int n)
{int cnt = 1;while (cnt < n){cnt *= 2;}
}
/*
* 当N为10的时候,我们循环4次就停止了
* 假设执行的次数为x,那么2^x=n
* 那么x就等于log2 n   ,
* 
* 所以这里的时间复杂度是O(log2 n)
*/

注意课件中和书籍中 log2 n 、 log n 、 lg n 的表⽰

当n接近⽆穷⼤时,底数的⼤⼩对结果影响不⼤。因此,⼀般情况下不管底数是多少都可以省略不写,即可以表⽰为 log n

不同书籍的表⽰⽅式不同,以上写法差别不⼤,我们建议使⽤ log n

对于计算机来说,这个底数的大小可以忽略不计的,因为影响很小

键盘是无法输入底数的

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{if (0 == N)return 1;return Fac(N - 1) * N;
}
/*
递归的时间复杂度是多少呢
每次递归的时间复杂度是O(1)
总共有n个O(1),那么时间复杂度就是O(N)
*/

将每次递归的时间复杂度进行相加

4.空间复杂度

空间复杂度也是⼀个数学表达式,是对⼀个算法在运⾏过程中因为算法的需要额外临时开辟的空间。空间复杂度不是程序占⽤了多少bytes的空间,因为常规情况每个对象⼤⼩差异不会很⼤,所以空间复杂度算的是变量的个数。

空间复杂度计算规则基本跟实践复杂度类似,也使⽤⼤O渐进表⽰法。

注意:函数运⾏时所需要的栈空间(存储参数、局部变量、⼀些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运⾏时候显式申请的额外空间来确定

空间复杂度计算⽰例

// 计算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;}
}
/*
外面的for循环内局部变量end,内部还有变量exchange
内循环的变量i,总共三个变量
所以这里的空间复杂度是O(1)*/

三个变量,那么这个空间复杂度就是O(3)

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{if (N == 0)return 1;return Fac(N - 1) * N;
}
/*
F(N)->F(N-1)->....->F(1)->F(0)
总共的空间复杂度就是O(N)
*/
//通过动态申请内容也会涉及到空间复杂度的计算的int func(int n)
{int arr[n] = malloc(sizeof(int) * n);
}
//这里的空间复杂度也是O(N)

5.常见复杂度对比

 

6.复杂度算法题--旋转数组

. - 力扣(LeetCode)

思路一:

首先将最后一位数进行保存,再将剩下的数字往右移一位,然后再将保存的数放到第一位

/*给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。示例 1:输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]提示:1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105进阶:尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?*/
void rotate(int* nums, int numsSize, int k)
{while(k--){int end=nums[numsSize-1];//创建一个临时变量将数组中最后一个数的值进行保存for(int i=numsSize-1;i>0;i--)//将数组中的数整体右移一位{nums[i]=nums[i-1];//在右移的时候我们要从后向前移,我们现将倒数第二个数赋值到最后一个数的位置上,然后随着i的变化进行右移操作}nums[0]=end;//最后我们将之前保存的值放到以一个位置上}}//但是这个存在限制,虽然这个代码没有问题,算法虽然没问题,但是超过时间限制,一但遇到大的数据,时间就变长了

但是这个题有时间的限制,一但给到了一个很大的数字,那么就要消耗很多的时间,就不满足题目要求了 将numsSize看作是N

这个代码的外层循环是k,内层循环是N-1,那么时间复杂度就是O(K*N),其实可以将K看做是N,都是变量,没啥区别,那么时间复杂度就是O(N^2),这个时间复杂度效率很低,所以在提交的时候我们会遇到超出时间限制的错误

既然这里的时间复杂度是O(N^2),空间复杂度是O(1)

那我们能不能先办法将时间复杂度降到O(N)呢?

思路二:空间换时间

申请一个新数组,数组大小为numsSize

假设K=3, 我们将原数组的后三个数字要放到新数组的前面,然后旧数组剩下的数字我们直接搬到新数组内

申请新数组等大的空间,先将后k个数据放到新数组中,再将剩下的数据挪到新数组中

最后我们还要将新数组的值挪到原数组中,为为原数组中的每个数进行重新赋值

void rotate(int* nums, int numsSize, int k)
{int newArr[numsSize];//创建一个数组大小和原先数组大小一样的数组for (int i = 0; i < numsSize; ++i){newArr[(i + k) % numsSize] = nums[i];} for (int i = 0; i < numsSize; ++i){nums[i] = newArr[i];}
}
/*1 2 3 4 5 6 7
假设k是3,那么就是原数组后3数字放到新数组的前3个位置,原先的前4个数字放到新数组的后4个数字newArr[(i + k) % numsSize] = nums[i];第一次时,i=0 
newArr[(0 + 3) % 7] = nums[0];
newArr[(3) % 7] = nums[0];
newArr[3] = nums[0];
将原先数组的第一个赋值到新数组的第4个元素的位置i=3时,就将原先数组的4放到新数组的最后一个位置当i=4时,那么代码就是newArr[(4 + 3) % numsSize] = nums[4];
newArr[(7) %  7] = nums[4];
newArr[0] = nums[4];
将原先数组的下标为4的数字放到新数组的地址个位置通过这个代码我们就实现了将原数组后k个数放到新数组的前k个位置,
将原数组的剩下的4个数据放到新数组的后4个位置在后面的循环中,我们就将新数组中的值重新拿回到原数组内,因为我们打印的是原数组,在原数组中进行改变*/

那么这个代码的时间复杂度是多少呢?

在第一个循环中,时间复杂度是O(N),在第二个循环中时间复杂度是O(N)

那么总的时间复杂度就是O(2N),根据规则,消掉系数,那么最后的时间复杂度就是O(N)

这种方法的时间复杂度就达到了O(N)

但是这种思路的空间复杂度也是O(N)

我们申请了新的空间,这个空间大小是N个,那么空间复杂度就是O(N)

这个思路虽然时间复杂度降到了O(N),但是我们是拿空间复杂度换的

思路三:

让时间复杂度为O(N),空间复杂度是O(1)

就是说明不需要额外申请空间

/*思路三1 2 3 4 5 6 7n=7,当前数组内数据为7
旋转的k=3第一步将前n-k个数据逆置  
这里的就是1 2 3 4
那么逆置后的结果就是4 3 2 1第二步就是将后k个数据进行逆置
这里的就是5 6 7
逆置后的结果就是 7 6 5 那么我们经历了一二步,得到了4 3 2 1 7 6 5最后一步我们再将整体进行逆置
得到了5 6 7 1 2 3 4
*/
/*
我们在思路三已经想到了通过三步逆置达到效果
那我们就将逆置的函数写出来
我们在需要逆置的数组设置两个下标
最左边的下标是left
最右边的下标是right
那么我们每次将left和right的下标进行交换
交换完成之后我们将left和right进行++操作,逆置下一对数字
直到我们left和right重叠了我们就停止逆置操作那么,理论成立,实践开始*///逆置函数
void reverse(int nums, int left,int right)//逆置的数组   逆置开始的起始位置
{while (left < right)//这里写等于就是多此一举的{//left和right指向的数据进行交换int tmp = nums[left];nums[left] = nums[right];nums[right] = tmp;left++;right--;}
}void rotate(int* nums, int numsSize, int k)
{k = k % numsSize;//不让k超过numsSize//前n-k个数据逆置reverse(nums, 0, numsSize - k - 1);//后k个数据逆置reverse(nums, numsSize-k, numsSize-1);//整体逆置reverse(nums, 0, numsSize - 1);
}/*
如果当前数组里面只有-1,但是我们要进行逆置2次,该怎么实现我们需要对k进行处理,让k余上数组的大小,可以避免多余的逆置操作
一但逆置的次数大于数组的长度,这个步骤就起到了作用,减小了代码的运行时间k = k % numsSize;//不让k超过numsSize*/

第一步将前n-k个数据逆置

第二步就是将后k个数据进行逆置

最后一步我们再将整体进行逆置

我们还要对逆置的次数进行取余,保证次数要小于数组的长度

我们对这个代码进行时间复杂度的分析

reverse函数 只有一个变量tmp,那么空间复杂度就是O(1)

对于逆置函数来说,我们调用了三次

第一次调用要交换的次数是(numsSize - k) / 2

第二次交换的次数是k / 2

第三次交换的次数是numsSize / 2

那么总的交换次数就是(numsSize - k) / 2 + k / 2 + numsSize / 2

所以时间复杂度就是O(N)

对于rotate函数来说,我们调用了三次reverse函数,因为reverse函数的时间复杂度是O(N),那么我们的rotate函数的时间复杂度就是O(N)

对于空间复杂度来说,rotaet本身仅仅只是调用了三次逆置函数,并没有额外开辟空间创建变量

所以空间复杂度是O(1)

如果对于逆置函数的时间复杂度还不理解的话你可以这么理解

时间复杂度的定义通常是最差的情况下

那么就是我们要进行整个数组的交换,这个就是最差的情况

假设数组有N个元素,那么我们就要交换N/2次,那么我们的时间复杂度就是O(N)

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

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

相关文章

查看路由表 netstat -r

“Kernel IP routing table” 是Linux系统中用于展示和配置IP路由的表。它告诉操作系统如何将数据包从一个网络接口发送到另一个网络或主机。下面是对您给出的路由表条目的解释&#xff1a; Destination&#xff1a;目的地地址&#xff0c;可以是具体的IP地址&#xff0c;也可…

ctfshow解题方法

171 172 爆库名->爆表名->爆字段名->爆字段值 -1 union select 1,database() ,3 -- //返回数据库名 -1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema库名 -- //获取数据库里的表名 -1 union select 1,group_concat(…

Python爬虫入门02:Fiddler下载使用教程

文章目录 手机抓包全攻略&#xff1a;Fiddler 工具深度解析引言Fiddler 工具简介为什么选择 Fiddler&#xff1f; 安装与配置 Fiddler步骤一&#xff1a;下载与安装步骤二&#xff1a;配置浏览器代理步骤三&#xff1a;安装 HTTPS 证书 配置手机以使用 Fiddler步骤一&#xff1…

操作系统面试知识点总结5

#来自ウルトラマンメビウス&#xff08;梦比优斯&#xff09; 1 IO管理概述 1.1 I/O 设备 I/O 设备的类型分类。 1.1.1 按使用特性 人机交互类外部设备&#xff0c;例如打印机、显示器等。存储设备&#xff0c;例如磁盘、光盘等。网络通信设备&#xff0c;例如网络接口等。 1…

【计算机网络】IP地址和子网掩码(IP地址篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️计算机网络】 文章目录 前言IP地址网络地址网络地址的定义与组成作用分类网络地址的分配与管理 广播地址&#xff08;Broadcast Address&#xff09;定义构成类型作用注意事项 广播地址功能 组播地址…

HiveSQL题——炸裂+开窗

一、每个学科的成绩第一名是谁&#xff1f; 0 问题描述 基于学生成绩表输出每个科目的第一名是谁呢&#xff1f; 1 数据准备 with t1 as(selectzs as name,[{"Chinese":80},{"Math":70}],{"English"…

CompletableFuture使用详解

简单的任务&#xff0c;用Future获取结果还好&#xff0c;但我们并行提交的多个异步任务&#xff0c;往往并不是独立的&#xff0c;很多 时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。如果要我们手动用 Future 实现&#xff0c;是非常麻烦的。 CompletableFutur…

JMeter基本使用

一、JMeter线程组相关 进程&#xff1a;正在运行的程序。线程&#xff1a;是进程中的执行线索。线程组&#xff1a;进程中有许多线程&#xff0c;为了方便管理&#xff0c;可以对线程按照性质分组&#xff0c;分组的结果就是线程组。PS&#xff1a;三者关系&#xff0c;一个进…

【设计模式】(万字总结)深入理解Java中的创建型设计模式

1. 前言 在软件开发的世界里&#xff0c;设计模式是一种被广泛接受并应用的解决方案。它们不仅仅是代码的设计&#xff0c;更是对问题的思考和解决的方法论。在Java开发中&#xff0c;特别是在面向对象的编程中&#xff0c;设计模式尤为重要。创建型设计模式&#xff0c;作为设…

【初阶数据结构篇】实现顺序结构二叉树(堆的实现方法)

文章目录 实现顺序结构二叉树&#xff08;堆&#xff09;前言代码位置堆的概念与结构概念 堆的实现堆初始化和销毁堆的插入堆的判空堆的删除数据取堆顶数据 实现顺序结构二叉树&#xff08;堆&#xff09; 前言 ​ ⼀般堆使⽤顺序结构的数组来存储数据&#xff0c;堆是⼀种特…

一键生成惊艳图像:使用Stable Diffusion打造惊人视觉效果

一、简介 Stable Diffusion是一种由Stability AI开发的生成式AI模型&#xff0c;能够将文本提示转换为高质量的图像。作为生成式扩散模型的一部分&#xff0c;Stable Diffusion通过解析和理解输入的文本提示&#xff0c;逐步生成与描述相匹配的图像。这一技术不仅在图像生成和…

MybatisPlus核心用法

MybatisPlus核心用法 目录 MybatisPlus核心用法1.使用MybatisPlus的基本步骤&#xff1a;2.MybatisPlus是如何获取实现CRUD的数据库表信息的&#xff1f;3.MybatisPlus的常用注解有哪些&#xff1f;如果表名称和实体类名称不符合约定该怎么做&#xff1f;IdType的常见类型有哪些…

漏洞复现-F6-11泛微-E-Cology-SQL

本文来自无问社区&#xff0c;更多漏洞信息可前往查看http://www.wwlib.cn/index.php/artread/artid/15575.html 0x01 产品简介 泛微协同管理应用平台e-cology是一套企业级大型协同管理平台 0x02 漏洞概述 该漏洞是由于泛微e-cology未对用户的输入进行有效的过滤&#xff0…

Skyeye云智能制造企业版源代码全部开放

智能制造一体化管理系统 [SpringBoot2 - 快速开发平台]&#xff0c;适用于制造业、建筑业、汽车行业、互联网、教育、政府机关等机构的管理。包含文件在线操作、工作日志、多班次考勤、CRM、ERP 进销存、项目管理、EHR、拖拽式生成问卷、日程、笔记、工作计划、行政办公、薪资模…

vue3 快速入门 (六) : vue中调用HTTP请求

1. 本文环境 Vue版本 : 3.4.29Node.js版本 : v20.15.0系统 : Windows11 64位IDE : VsCode 1.91.0 2. 访问HTTP 在Vue中&#xff0c;访问HTTP&#xff0c;可以使用axios第三方库。 axios 是一个基于 promise 的网络请求库&#xff0c;可以用于浏览器和 node.js。 axios使用简…

遗传算法与深度学习实战——进化深度学习

遗传算法与深度学习实战——进化深度学习 0. 前言1. 进化深度学习1.1 进化深度学习简介1.2 进化计算简介 2. 进化深度学习应用场景3. 深度学习优化3.1 优化网络体系结构 4. 通过自动机器学习进行优化4.1 自动机器学习简介4.2 AutoML 工具 5. 进化深度学习应用5.1 模型选择&…

【mybatis的批量操作】

在日常开发中&#xff0c;如果要操作数据库的话&#xff0c;或多或少都会遇到批量数据的处理&#xff0c;我们公司使用的mybaits-plus作为持久层的框架&#xff0c;今天就简单介绍一下mybaits批量操作数据库的几种方式。 1.循环插入 其实这种方式并不是批量插入&#xff0c;只…

功能实现——使用 OpenPDF 将 HTML 转换为 PDF,并将其上传到 FTP 服务器

目录 1.需求分析2.项目环境搭建3.将 HTML 转换为 PDF3.1.代码实现mail.htmlHtmlToPDFController.javaPDFConverterService.javaPDFConverterServiceImpl.java 3.2.测试3.3.注意事项 4.将生成的 PDF 上传到 FTP 服务器4.1.搭建 FTP 服务器4.2.配置文件4.3.代码实现FtpUtil.javaF…

谷粒商城实战笔记-75-商品服务-API-品牌管理-品牌分类关联与级联更新

文章目录 一&#xff0c;引入Mybatis Plus分页插件二&#xff0c;品牌列表的模糊查询三&#xff0c;增加品牌测试数据四&#xff0c;开发后台品牌关联分类接口1&#xff0c;接口product/categorybrandrelation/catelog/list2&#xff0c;接口product/categorybrandrelation/sav…

汽车长翅膀:GPU 是如何加速深度学习模型的训练和推理过程的?

编者按&#xff1a;深度学习的飞速发展离不开硬件技术的突破&#xff0c;而 GPU 的崛起无疑是其中最大的推力之一。但你是否曾好奇过&#xff0c;为何一行简单的“.to(‘cuda’)”代码就能让模型的训练速度突飞猛进&#xff1f;本文正是为解答这个疑问而作。 作者以独特的视角&…