目录
一、简述算法与数据结构
数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的
数据元素的集合。它是以增删改查的方式在内存中管理数据。
算法(Algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果。
想进中小厂:《剑指offer》、数据结构(严蔚敏)、数据结构(殷人昆)、大话数据结构
想进大厂:《剑指offer》、力扣刷题、数据结构(严蔚敏)、数据结构(殷人昆)、大话数据结构
刷题推荐力扣、剑指offer。
时间复杂度主要衡量一个算法的运行快慢;
空间复杂度主要衡量一个算法运行所需要的额外空间。
算法的时间复杂度是一个函数(数学表达式),定量地描述了该算法的运行时间。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。
即找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。
- // 请计算一下Func1中++count语句总共执行了多少次?
- void Func1(int N)
- {
- int count = 0;
- 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;
- while (M--)
- {
- ++count;
- }
- printf("%d\n", count);
- }
Func1执行的基本操作次数:
F(N)=N^2+2*N+10
取以下值时,
N = 10 F(N) = 130
N = 100 F(N) = 10210
N = 1000 F(N) = 1002010
实际计算时,我们只需要大概执行次数。
大O符号:描述函数渐进行为的数学符号。
推导大O阶方法:(量级的估算)
a.用常数1取代运行时间中的所有加法常数。
b.在修改后的运行次数函数中,只保留最高阶项。(决定性影响)
c.如果最高阶项系数存在且不是1,则去除与这个项目相乘的常数,得到的结果就是大O阶。
所以,Func1的时间复杂度为O(N^2)
通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
在实际中一般情况关注的是算法的最坏运行情况(悲观保守的预估),所以数组中搜索数据时间复杂度为O(N)。
例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);
- }
例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);
- }
例3:
- // 计算Func4的时间复杂度?
- void Func4(int N)
- {
- int count = 0;
- for (int k = 0; k < 100; ++ k)
- {
- ++count;
- }
- printf("%d\n", count);
- }
例4:
- // 计算strchr的时间复杂度?
- const char * strchr ( const char * str, int character );
这是一个字符串查找函数,基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为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;
- }
- }
例6:
- // 计算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;
- else
- return mid;
- }
- return -1;
- }
例7:
- // 计算阶乘递归Fac的时间复杂度?
- long long Fac(size_t N)
- {
- if(0 == N)
- return 1;
- return Fac(N-1)*N;
- }
例8:
- // 计算斐波那契递归Fib的时间复杂度?
- long long Fib(size_t N)
- {
- if(N < 3)
- return 1;
- return Fib(N-1) + Fib(N-2);
- }
空间复杂度是对一个算法在运行过程中额外占用存储空间大小的量度。
空间复杂度计算的是变量的个数,而不是程序占用了多少字节的空间,计算规则用推导大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;
- }
- }
end,exchange,i是额外定义的三个变量,所以空间复杂度的数学表达式就是3,由推导大O阶方法知道空间复杂度为O(1)。
例2:
- // 计算阶乘递归Fac的空间复杂度?
- long long Fac(size_t N)
- {
- if (N == 0)
- return 1;
- return Fac(N - 1) * N;
- }
例3:
- // 计算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;
- }
例4:
- void f1(int n)
- {
- int a = 0;
- int* p = (int*)malloc(4 * n);
- printf("%p,%p\n", &a, &p);
- if (p == NULL)
- {
- perror("malloc fail\n");
- return;
- }
- free(p);
- }
- int main()
- {
- f1(10);
- f1(10);
- return 0;
- }
5201314 | O(1) | 常数阶 |
3logn+4 | O(logn) | 对数阶 |
3n+4 | O(n) | 线性阶 |
2n+3nlogn+14 | O(nlogn) | nlogn阶 |
3n^2+4n+5 | O(n^2) | 平方阶 |
2^n | O(2^n) | 指数阶 |
O(n!) |
以上复杂度效率由高到低。
(1)消失的数字
数组nums
包含从0
到n
的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
https://leetcode.cn/problems/missing-number-lcci/
(2)轮转数组
给你一个数组,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
https://leetcode.cn/problems/rotate-array/
本次分享到此结束,后续会有不断更新,不要忘记一键三连噢~