【解密算法:时间与空间的博弈】

本章重点

  1. ​​什么是数据结构?

  2. 什么是算法?

  3. 算法效率

  4. 时间复杂度

  5. 空间复杂度

  6. 常见复杂度对比

  7. 复杂度oj练习

1. 什么是数据结构?

        数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。

2.什么是算法?

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

3.算法效率

3.1 如何衡量一个算法的好坏

        我们能不能用一个程序运行的开始的时间到程序结束的时间去描述复杂度呢?

答案是不能,一个程序的运行还会受到很多因素的影响,比如电脑的配置、性能。就比如下面两台电脑,让两台电脑同时执行下面递归计算斐波那契数列40是多少的程序。很明显高性能的计算机器多核CPU,进程并发执行,速度高于左边的单核机器。

#include<stdio.h>
long long Fib(int N)
{if (N < 3)return 1;return Fib(N - 1) + Fib(N - 2);
}
int main()
{//使用斐波那契数列递归方法计算40long long result = Fib(40);printf("%lld", result);return 0;
}

斐波那契数列的递归实现方式非常简洁,但简洁一定好吗?那该如何衡量其好与坏呢?

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

        时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。

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

4.时间复杂度

        时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。

        一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。

        一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。

        即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。

 Func1 执行的基本操作次数 :

        我们发现当N的值越来越多的时候,2*N + 10这个表达式对结果造成的影响非常小,所有实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。

大O的渐进表示法

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

推导大O阶方法:

  1. 用常数1取代运行时间中的所有加法常数。
  2. 在修改后的运行次数函数中,只保留最高阶项。
  3. 如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

使用大O的渐进表示法以后,Func1的时间复杂度为:O(N^2)

通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。另外有些算法的时间复杂度存在最好、平均和最坏情况:

  • 最坏情况:任意输入规模的最大运行次数(上界)
  • 平均情况:任意输入规模的期望运行次数
  • 最好情况:任意输入规模的最小运行次数(下界)

例如:在一个长度为N数组中搜索一个数据x

  • 最好情况:1次找到
  • 平均情况:N/2次找到
  • 最坏情况:N次找到

        在实际中一般情况关注的是算法的最坏运行情况,原因是为了保证算法在任何输入情况下都能保持一定的性能水平。最坏情况时间复杂度是对算法性能的一个保证,确保在任何可能的输入下,算法都不会执行得更差。所以数组中搜索数据时间复杂度为O(N).

常见时间复杂度计算举例

实例一:

// 计算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);
}
  1. 第一个循环:for (int k = 0; k < 2 * N; ++k)

    • 这个循环执行了 2 * N 次,其中 N 是输入的参数。
    • 循环内部只有一个基本操作 ++count
  2. 第二个循环:while (M--)

    • 这个循环执行了固定的 10 次。
    • 循环内部只有一个基本操作 ++count

因此,总的基本操作数可以表示为:2 * N + 10

        在大O符号表示法中,我们通常关注最高阶的项,而忽略低阶项和常数系数。在这种情况下,最高阶项是 2 * N

        因此,Func2 函数的时间复杂度可以表示为 O(N)

实例二:

//计算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);
}
  1. 第一个循环:for (int k = 0; k < M; ++k)

    • 这个循环执行了 M 次,其中 M 是第二个输入参数。
    • 循环内部只有一个基本操作 ++count
  2. 第二个循环:for (int k = 0; k < N; ++k)

    • 这个循环执行了 N 次,其中 N 是第一个输入参数。
    • 循环内部只有一个基本操作 ++count

因此,总的基本操作数可以表示为:M + N

        在大O符号表示法中,我们通常关注最高阶的项,而忽略低阶项和常数系数。在这种情况下,最高阶项是 M 和 N,同时取两者。

        因此,Func3 函数的时间复杂度可以表示为 O(M + N)

补充:

  • 若 M 和 N 的值相同,时间复杂度可以表示为 O(M) 或者 O(N)
  • 若 M >> N 的值,时间复杂度可以表示为 O(M)
  • 若 M << N 的值,时间复杂度可以表示为 O(N)

在这种情况下,最高阶项是 MN,取两者中较大的一个因此,Func3 函数的时间复杂度可以表示为 O(max(M, N))

实例三:

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

    • 这个循环总共执行了 100 次。
    • 循环内部只有一个基本操作 ++count

因此,总的基本操作数可以表示为:100

        在大O符号表示法中,我们通常关注最高阶的项,而忽略低阶项和常数系数。在这种情况下,最高阶项是 100

        因此,Func4 函数的时间复杂度可以表示为 O(1)

实例四:

// 计算strchr的时间复杂度?
const char* strchr(const char* str, int character);

  strchr 函数用于在给定字符串中查找第一个出现的指定字符,并返回该字符后的部分字符串。

  • 最好情况:1次找到
  • 平均情况:N/2次找到
  • 最坏情况:N次找到

        在一般情况下,strchr 函数的时间复杂度可以被认为是 O(N),其中 N 是输入字符串的长度。这是因为 strchr 需要遍历字符串中的每个字符来查找指定字符。在最坏情况下,它可能需要遍历整个字符串才能找到目标字符。

实例五:

// 计算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 是一种基本的排序算法,其时间复杂度取决于待排序数组的初始状态。让我们分析一下 BubbleSort 的时间复杂度。

        BubbleSort 的主要思想是在每一轮遍历中,比较相邻的两个元素,如果顺序不对就交换它们,直到最大(或最小)元素“冒泡”到正确的位置。在每一轮遍历中,至少会将一个元素放置在其最终的位置上。

        最坏情况下,数组完全逆序,需要执行 N-1 轮比较和交换操作。第一轮中,需要比较和可能的交换 N-1 次;第二轮中,需要比较和可能的交换 n-2次...

        所以,总的操作次数为:(N-1) * N / 2 = N * N / 2 - N / 2

在大O符号表示法中,我们通常关注最高阶的项,而忽略低阶项和常数系数。在这种情况下,最高阶项是 n^2。因此,BubbleSort 的时间复杂度可以表示为 O(n^2)

        需要注意的是,BubbleSort 的平均和最坏情况下的时间复杂度都是 O(N^2),即使在最好情况下,数组已经是有序的,BubbleSort 也需要进行 n-1 轮比较,时间复杂度都是 O(N)。

实例六:

// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{assert(a);int begin = 0;int end = n - 1;// [begin, end]:begin和end是左闭右闭区间,因此有=号while (begin <= end){int mid = begin + ((end - begin) >> 1);if (a[mid] < x)begin = mid + 1;else if (a[mid] > x)end = mid - 1;elsereturn mid;}return -1;
}

        BinarySearch(二分查找)是一种高效的查找算法,它的时间复杂度为 O(log N),其中 N 是待查找数组的长度。

        在每一轮循环中,BinarySearch 将当前搜索范围缩小为原来的一半。假设当前搜索范围的长度为 len,在下一轮循环中,搜索范围的长度将减半为 len/2。因此,每一轮循环都可以排除一半的元素。

        最坏情况下,当搜索范围缩小到只有一个元素时,算法结束。而要将 n 个元素缩小到 1 个元素,需要进行 log₂(n) 次操作。

        因此,BinarySearch 的时间复杂度是 O(log n)。

        BinarySearch 在已排序的数组中查找元素,通过不断缩小搜索范围,每次排除掉一半的元素,因此它的时间复杂度非常低。

图解:

 实例七:

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

阶乘递归函数 Fac 的时间复杂度可以通过递归关系来分析。在这种情况下,递归的深度与输入规模 N 相关,每一层递归都会进行一次乘法操作。让我们分析一下:

  1. 第一递归层(N = N):执行 1 次 Fac(N - 1) * N乘法。
  2. 第二递归层(N = N-1):执行 1 次 Fac(N - 2) * N乘法。
  3. 第三递归层(N = N-2):执行 1 次 Fac(N - 1) * N乘法。
  4. ...
  5. 最后一层递归(N = 0):执行 1 次 Fac(0) * N乘法。

        总的乘法操作次数为 N 次。

因此,阶乘递归函数 Fac 的时间复杂度可以表示为 O(N),其中 N 是输入的规模。

        尽管递归函数的每一层都只执行了一个乘法操作,但由于递归的深度与输入规模相关,最终的时间复杂度是线性的。

        总结:递归算法时间复杂度是多次调用的累加。

扩展:

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

阶乘递归函数 Fac 的时间复杂度可以通过递归关系来分析。在这种情况下,递归的深度与输入规模 N 相关,每一层递归都会进行一次乘法操作。让我们分析一下:

  1. 第一递归层(N = N):执行 1 次 Fac(N - 1) * N 乘法 + 执行 N 次 ++count
  2. 第二递归层(N = N-1):执行 1 次 Fac(N - 2) * N 乘法 + 执行 N-1 次 ++count
  3. 第三递归层(N = N-2):执行 1 次 Fac(N - 1) * N 乘法 + 执行 N-2 次 ++count
  4. ...
  5. 最后一层递归(N = 0):执行 1 次 Fac(0) * N 乘法 + 执行0次 ++count

由于上面每次递归执行的乘法只有一次,对于总的操作次数影响较小,可以忽略不记,所以总的操作次数为 N * N /2 次。

因此,阶乘递归函数 Fac 的时间复杂度可以表示为 O(N^2),其中 N 是输入的规模。

        尽管递归函数的每一层都只执行了一个乘法操作,但由于递归的深度与输入规模相关,最终的时间复杂度是线性的。

        总结:递归算法时间复杂度是多次调用的累加。

实例八:

// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N)
{if (N < 3)return 1;return Fib(N - 1) + Fib(N - 2);
}

斐波那契递归函数 Fib 的时间复杂度可以通过递归关系来分析。在这种情况下,递归的深度与输入规模 N 相关,每一层递归都会进行两次递归调用。让我们分析一下:

  1. 第一层递归(N = N):执行 2^0 次递归调用。
  2. 第二层递归(N = N-1):执行 2^2 次递归调用。
  3. 第三层递归(N = N-2):执行 2^3 次递归调用。
  4. ...

        每一层递归调用的次数是指数级别增长的, 总的乘法操作次数为 2^N-1 次。 

        因此,总的递归调用次数可以近似表示为 2^N 次。

        由于每次递归调用都需要一些操作(在这里是两次递归调用),在最坏情况下,每次递归调用的时间开销相对较小。因此,总的时间复杂度仍然可以表示为 O(2^N)

但是实际上右边会缺少,总的时间复杂度低于等于 O(2^N)。

5.空间复杂度 

        空间复杂度也是一个数学表达式,是对一个算法在运行过程中额外临时占用存储空间大小的量度

        空间复杂度不是程序占用了多少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;}
}

        在 BubbleSort 中,输入数组不是额外开辟的空间,不算入到空间复杂度上,其余只使用了很少的额外空间,主要是用来存储一些临时变量,如循环索引、交换标志等。这些额外空间的使用量不会随着输入规模 n 的增加而显著变化。无论输入数组的大小如何,BubbleSort 需要的额外空间是固定的。

        因此,BubbleSort 的空间复杂度为 O(1)

实例二:

// 计算Fibonacci的空间复杂度?
// 返回斐波那契数列的前n项
long long* Fibonacci(size_t n)
{if (n == 0)return NULL;long long* fibArray = (long long*)malloc((n + 1) * sizeof(long long));fibArray[0] = 0;fibArray[1] = 1;for (int i = 2; i <= n; ++i){fibArray[i] = fibArray[i - 1] + fibArray[i - 2];}return fibArray;
}
  1. 额外数组的空间:Fibonacci 函数内部,通过动态内存分配 malloc 来创建一个大小为 (n + 1) 的长整型数组 fibArray,用于存储斐波那契数列的前 n 项。这个数组的空间占用是与 n 相关的。

        所以,总的空间复杂度由额外数组的空间复杂度决定。

        在这种情况下,空间复杂度是 O(N)

实例三:

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

        阶乘递归函数 Fac 的空间复杂度是 O(N),因为每次递归调用都会在函数调用栈上创建一个新的递归帧,每个递归帧需要一些内存空间来存储局部变量、返回地址等。

        在最坏情况下,当递归深度达到 N 时,需要在栈上保留 N 层递归帧。因此,空间复杂度与递归的深度成正比,为 O(N)

实例四:

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

        这个斐波那契递归函数的空间复杂度是 O(N)。虽然函数本身没有显式地使用额外的数组或数据结构,但是在递归调用的过程中,每次调用都会在函数调用栈中创建一个新的函数调用帧。由于递归函数会多次调用自身,每次调用都需要分配一些内存来存储函数参数、局部变量以及返回地址等信息,这些内存会随着递归深度的增加而累积。递归深度直接影响了调用栈中需要分配的内存空间数量。

        我们拿 Fib(6) 举例,在这个递归实现中,当你调用 Fib(6) 时,它会依次调用 Fib(5)Fib(4),然而并不是右边那个Fib(4) ,是 Fib(5) 下面的 Fib(4) ,然后这些调用会进一步调用更低层次的函数,以此类推。当调完 Fib(2)后该函数栈帧就销毁了,然后生成 Fib(1)的栈帧, Fib(1)调完后也就释放了,以此类推。由于栈空间是可以复用的,一个栈帧释放空间就还给操作系统了,可以给后面的函数调用留下空闲的空间,整个过程中,递归最深的层数也就是Fib(6)到 Fib(2)了,其余调用的都是使用之前栈帧释放的空间,且深度也低。

        因此,递归的空间复杂度通常与递归的深度成正比,即 O(N)。这意味着在最坏情况下,调用栈的深度将达到 N 层,每一层都需要一些内存空间。这也是为什么递归在解决某些问题时可能会导致栈溢出或效率较低的原因之一。

下面这个例子就证明了栈空间是可复用滴。

#include<stdio.h>
void Func1()
{int a = 0;printf("%p\n", &a);
}
void Func2()
{int b = 0;printf("%p\n", &b);
}
int main()
{Func1();Func2();return 0;
}

运行结果:

        因为在许多编译器和操作系统中,函数调用时会使用相同的堆栈帧空间。这意味着在一个函数结束后,其分配给局部变量的堆栈空间可能会被重用给下一个函数的局部变量。

        因此,虽然在逻辑上 Func1Func2 是在不同的函数调用中,它们的局部变量 ab 分别被分配到了相同的堆栈空间(因为这两个函数的栈帧在调用时被重用),从而导致它们的局部变量的地址也相同。

6.常见复杂度对比

7.复杂度oj练习

1.消失的数字OJ链接:https://leetcode.cn/problems/missing-number-lcci/

思路一:排序+遍历(后一个数不等于前一个数+1,那么这个数就是消失的数)

复杂度是:O(N*log(N)) - 不符合题意

#include<stdio.h>
#include <stdlib.h>
#include <assert.h>
int compare(const void* a, const void* b)
{return (*(int*)a - *(int*)b);
}
int missingNumber(int* nums, int numsSize) 
{assert(nums);qsort(nums, numsSize, sizeof(int), compare);if (nums[0] != 0)return 0;for (int i = 0; i < numsSize - 1; i++){//后一个数不等于前一个数+1,那么这个数就是消失的数if ((nums[i] + 1) != nums[i + 1])return nums[i] + 1;}return -1;//不存在缺少的数字
}
int main()
{int arr[] = { 9,6,4,2,3,5,7,0,1 };printf("%d\n", missingNumber(arr, sizeof(arr) / sizeof(arr[0])));return 0;
}

思路二:0+N等差公式计算结果 - 数组中的值

复杂度是:O(N) - 符合题意

#include<stdio.h>
#include <assert.h>
int missingNumber(int* nums, int numsSize)
{assert(nums);int result = numsSize * (0 + numsSize + 1) / 2;//计算从0到n的和,一共是n+1个数for (int i = 0; i < numsSize; i++){result -= nums[i];}return result;
}
int main()
{int arr[] = { 9,6,4,2,3,5,7,0,1 };printf("%d\n", missingNumber(arr, sizeof(arr) / sizeof(arr[0])));return 0;
}

思路三:单身狗思路 - 异或运算 - 相同为0,相异为1

复杂度是:O(N) - 符合题意

#include<stdio.h>
#include <assert.h>
int missingNumber(int* nums, int numsSize)
{assert(nums);//0 1 .... N//数组中的值//其他数字成对出现,缺少的数字为单身狗int x = 0;//x为缺失的数字for (int i = 0; i <= numsSize; i++){x ^= i;}for (int i = 0; i < numsSize; i++){x ^= nums[i];}return x;
}
int main()
{int arr[] = { 9,6,4,2,3,5,7,0,1 };printf("%d\n", missingNumber(arr, sizeof(arr) / sizeof(arr[0])));return 0;
}

图解:

2.旋转数组OJ链接:https://leetcode.cn/problems/rotate-array/

思路一:暴力求解,直接右旋

时间复杂度是:O(N^2) ,空间复杂度是:O(1)

#include<stdio.h>
#include<assert.h>
void rotate(int* nums, int numsSize, int k)
{assert(nums);int count = k % numsSize;while (count--){int temp = nums[numsSize - 1];for (int i = numsSize - 1; i > 0; i--){nums[i] = nums[i - 1];}nums[0] = temp;}
}
int main()
{int nums[] = { 1, 2, 3, 4, 5, 6, 7 }; //[7,1,2,3,4,5,6]int k = 0;scanf("%d", &k);rotate(nums, sizeof(nums) / sizeof(nums[0]), k);for (int i = 0; i < sizeof(nums) / sizeof(nums[0]); i++){printf("%d ", nums[i]);}return 0;
}

思路二:新建数组,把数组后k个数先拷贝过来,再把numsSize-k个数拷贝,再拷贝回原数组nums

时间复杂度是:O(N) ,空间复杂度是:O(N)

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
void rotate(int* nums, int numsSize, int k)
{assert(nums);int* temp = (int*)malloc(sizeof(int) * numsSize);k %= numsSize;if (!temp){perror("malloc");return;}memcpy(temp, nums + numsSize - k, sizeof(int) * k);memcpy(temp + k, nums, sizeof(int) * (numsSize - k));//拷贝回去memcpy(nums, temp, sizeof(int) * numsSize);free(temp);temp = NULL;
}
int main()
{int nums[] = { 1, 2, 3, 4, 5, 6, 7 }; //[7,1,2,3,4,5,6]int k = 0;scanf("%d", &k);rotate(nums, sizeof(nums) / sizeof(nums[0]), k);for (int i = 0; i < sizeof(nums) / sizeof(nums[0]); i++){printf("%d ", nums[i]);}return 0;
}

思路三:将前numsSize-k逆置,再将后k个逆置,最后整体逆置数组

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

#include<stdio.h>
#include<assert.h>
void reverse(int* nums, int left, int right)
{assert(nums);{while (left < right){int temp = nums[left];nums[left] = nums[right];nums[right] = temp;left++;right--;}}
}
void rotate(int* nums, int numsSize, int k)
{assert(nums);k %= numsSize;//防止k大于numsSizereverse(nums, 0, numsSize - k - 1);reverse(nums + numsSize - k, numsSize - k, numsSize - 1);//整体逆置reverse(nums, 0, numsSize - 1);
}
int main()
{int nums[] = { 1, 2, 3, 4, 5, 6, 7 }; //[7,1,2,3,4,5,6]int k = 0;scanf("%d", &k);rotate(nums, sizeof(nums) / sizeof(nums[0]), k);for (int i = 0; i < sizeof(nums) / sizeof(nums[0]); i++){printf("%d ", nums[i]);}return 0;
}

本章结束啦!!!

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

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

相关文章

uniapp项目如何运行在微信小程序模拟器上

在HbuilderX中的小程序写完后自己一定要保存&#xff0c;否则会出不来效果 那么怎么让uniapp项目运行在微信小程序开发工具中呢 1 在hbuilderx中点击运行到小程序模拟器 2 然后在项目目录中会生成一个文件夹 在微信小程序开发软件中的工具>安全设置>打开端口 或者在微…

分类预测 | Matlab实现基于TSOA-CNN-GRU-Attention的数据分类预测

分类预测 | Matlab实现基于TSOA-CNN-GRU-Attention的数据分类预测 目录 分类预测 | Matlab实现基于TSOA-CNN-GRU-Attention的数据分类预测效果一览基本介绍研究内容程序设计参考资料 效果一览 基本介绍 Matlab实现分类预测 | Matlab实现基于TSOA-CNN-GRU-Attention的数据分类预…

【LeetCode75】第二十七题(933)最近的请求次数

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码运行结果&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 首先这是LeetCode75里第一道设计类的题目&#xff0c;这种类型的题目会比较新颖&#xff0c;就是按照题目要求来设计一个类。然后…

四、web应用程序技术——HTTP

文章目录 1 HTTP请求2 HTTP响应3 HTTP方法4 URL5 HTTP消息头5.1 常用消息头5.2 请求消息头5.3 响应消息头 6 cookie7 状态码8 HTTP代理9 HTTP身份验证 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是访问万维网使用的核心通信协议&…

基于TF-IDF+TensorFlow+词云+LDA 新闻自动文摘推荐系统—深度学习算法应用(含ipynb源码)+训练数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境TensorFlow环境方法一方法二 模块实现1. 数据预处理1&#xff09;导入数据2&#xff09;数据清洗3&#xff09;统计词频 2. 词云构建3. 关键词提取4. 语音播报5. LDA主题模型6. 模型构建 系统测试工程源代码下载…

android ndk clang交叉编译ffmpeg动态库踩坑

1.ffmpeg默认使用gcc编译&#xff0c;在android上无法使用&#xff0c;否则各种报错&#xff0c;所以要用ndk的clang编译 2.下载ffmpeg源码 修改configure文件&#xff0c;增加命令 cross_prefix_clang 修改以下命令 cc_default"${cross_prefix}${cc_default}" cxx…

MySQL多表关联查询

目录 1. inner join&#xff1a; 2. left join&#xff1a; 3. right join&#xff1a; 4.自连接 5.交叉连接&#xff1a; 6、联合查询 7、子查询 1. inner join&#xff1a; 代表选择的是两个表的交差部分。 内连接就是表间的主键与外键相连&#xff0c;只取得键值一致…

Django配置(部署环境较乱,暂时启用)

django配置 web服务器中部署项目及WSGI简介 web服务器 WSGI 在IIS中部署django项目 安装 wfastcgi &#xff1a;pip install wfastcgi安装IIS&#xff1a; 以上选择项勾选后确定 将CGI文件复制到项目中&#xff0c; 将项目复制到IIS默认目录中 部署IIS 添加变量信息如下…

C++笔记之字节数组的处理

C笔记之字节数组的处理 code review! 文章目录 C笔记之字节数组的处理1.字节数组打印2.将字节数组转换为十六进制字符串并打印3.将字符串转为字节数组4.将字节数组转为字符串5.字节数组和字符数组的区别6.字节数组用于二进制数据存储7.字节数组用于网络通信数据传输8.使用 un…

uniapp+vue3+vite+pinia2.0.33项目初始化

目录 准备工作 注意事项 使用vue-cli创建项目 运行 准备工作 下载hbuild开发工具 HBuilderX-高效极客技巧 下载微信小程序开发工具 概览 | 微信开放文档 uniapp uni-app官网 注意事项 1.node.js版本>16#windows查看node版本 C:\Users\22862>node -v v18.16.0 …

【python】爬取豆瓣电影Top250(附源码)

前言 在网络爬虫的开发过程中&#xff0c;经常会遇到需要处理一些反爬机制的情况。其中之一就是网站对于频繁访问的限制&#xff0c;即IP封禁。为了绕过这种限制&#xff0c;我们可以使用代理IP来动态改变请求的来源IP地址。在本篇博客中&#xff0c;将介绍如何使用代理IP的技术…

WebRTC | 实现数据流的一对一通信

目录 一、浏览器对WebRTC的支持 二、MediaStream与MediaStreamTrack 三、RTCPeerConnection 1. RTCPeerConnection与本地音视频数据绑定 2. 媒体协商SDP 3. ICE &#xff08;1&#xff09;Candidate信息 &#xff08;2&#xff09;WebRTC收集Candidate &#xff08;3&…

搭建Docker环境

目录 一、docker环境搭建 1、卸载旧版本docker 2、安装依赖和设置仓库 3、安装docker 4、启动并加入开机启动 5、验证是否安装成功 二、利用docker搭建nginx 1、拉取镜像 2、启动容器&#xff0c;部署nginx 一、docker环境搭建 1、卸载旧版本docker yum remove docke…

【linux】2 软件管理器yum和编辑器vim

目录 1. linux软件包管理器yum 1.1 什么是软件包 1.2 关于rzsz 1.3 注意事项 1.4 查看软件包 1.5 如何安装、卸载软件 1.6 centos 7设置成国内yum源 2. linux开发工具-Linux编辑器-vim使用 2.1 vim的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行…

[Blender]Geometry nodes altermesh to UE

首先要先下载插件 AlterMesh – Use geometry nodes inside Unreal 下载对应版本的插件后 打开UE&#xff0c;在对应的设置里面挂上blender.exe的路径 去官方下载一个Blender Geometry nodes 的示例 Demo Files — blender.org​​​​​​

Java项目-苍穹外卖-Day03

文章目录 员工分页查询功能实现需求分析和设计代码开发pageHelper底层代码完善 启用禁用员工账号功能开发需求分析 员工分页查询功能实现 需求分析和设计 代码开发 先设计类 将对应分页查询的传参类以及结果类进行封装 对应真正返回的为Result<PageResult>Controller…

电商3D产品渲染简明教程

3D 渲染让动作电影看起来更酷&#xff0c;让建筑设计变得栩栩如生&#xff0c;现在还可以帮助营销人员推广他们的产品。 从最新的《阿凡达》电影到 Spotify 的上一次营销活动&#xff0c;3D 的应用让一切变得更加美好。 在营销领域&#xff0c;3D 产品渲染可帮助品牌创建产品的…

Scratch 之 枪战的枪械画法

大家可以参考百度图片寻找到的AK-47图片&#xff1a;AK47图片 此处我以MK18作为参照&#xff0c;MK18的造型可以在资源中获取 资源链接&#xff1a;https://download.csdn.net/download/leyang0910/88136393 对于不必要的&#xff08;繁琐的&#xff09;线条&#xff0c;我们可…

分享kubernetes部署:基于Ansible自动安装kubernetes

基于Ansible自动安装kubernetes 环境准备 我们以如下机器环境为例&#xff1a; 开放端口&#xff1a; 控制平面节点 工作节点 请按如上中规定的开放端口&#xff0c;或关闭防火墙&#xff1a; systemctlstopfirewalld&&\ systemctldisablefirewalld 安装常用工具 sudo…

点亮你的第一颗Led灯

1、&#x1f4d5;前言 该系列文章用于记录个人学习stm32单片机的过程&#xff0c;全文搭配图文解说&#xff0c;零基础的萌新也能读懂&#xff0c;欢迎指导讨论~ 2、&#x1f4e6;准备材料 2.1、&#x1f4dd;硬件材料清单 面包板1块 PWLINK PowerWriter仿真器1个 杜…