【算法笔记】时间复杂度与空间复杂度

前言:我们写程序来解决具体问题时,有时候会想到不同的解决方法,也就是算法,但该以什么来衡量这个算法的好坏呢?我们主要以时间复杂度(主要)和空间复杂度来衡量这个算法的好坏


1.时间复杂度

1.1定义:

在近代中,电脑硬件发展的速度非常快,比如我们耳熟能详的摩尔定律就是在这个背景下诞生的CUP的运算速度的越来越快,像是一些比较垃圾的CUP都能有每秒几亿次的计算效率, 而内存的发展更是,所以我们在衡量一个算法的好坏的时候我们一般主要关注时间复杂度。

时间复杂度并不是指每个时间段这个程序的运行速率,因为这个东西很受硬件的影响,比如我的电脑比你的电脑好很多的话,我更快的出了结果难道我写的程序就一定比你好吗?这样比较显然是不公平的,所以我们说的是时间 复杂度,但是我们关注的其实是这个程序的运行次数

比如在下面的函数中:

cpp 复制代码
void fun1()
{
	int sum = 0;
	for (int i = 1; i <= 2 * n; i++)
	{
		sum += i;
        sum++;
	}
	int k = 7;
	while(k--)
	{
		sum += k;
	}
	cout << sum << endl;
}

这个函数的运行次数就是2 * n + 7次,至于为什么是这个呢?for循环里不是执行了两个语句,难道不是4 * n吗?前面也说过由于现在的CUP运算速度足够快,说以我们一般只关注这个程序的循环执行次数而不关心里面具体的语句数量,当然不能粗暴的只看外层的循环,我们还得视具体的逻辑来判断。因此我们也可以不关心后面的常数项,比如后面那个7次减减。

我们一般使用大O来表示一个算法的复杂度,这个程序的复杂度就为(n)的级别,n前面的那个系数我们也是不关心的,不管是2n还是3n我们都认为复杂度为n,原理和不管常数项的处理是一样的

而且我们一般只关注影响最大的那一项,比如当一个程序的运行次数式子同时存在n 和 n ^ 2 时我们认为他的时间复杂度为O(n ^ 2)


1.2一些例子和常见的时间复杂度

cpp 复制代码
void fun2()
{
	int sum = 0;
	for (int i = 1; i <= m * n; i++)
	{
		//
	}
	
	while(n--)
	{
		//
	}
	cout << sum << endl;
}

这个程序的执行次数就是n * m + n, 用大O表示法就是O(n * m)级别的,但当这个m和n相近时我们也可以认为O(n ^ 2)

cpp 复制代码
int fun3(int n)
{
	if (n < 3) return 1;
	
	return fun3(n - 2) + fun3(n - 1);
}

这个是计算斐波那契数列的递归函数,这个函数在每次调用自己时都会展开两项,所以它的时间复杂度就是O(2^n)级别的,一般当时间为O(n^3)我们就认为这个时间复杂度已经很高了,更何况是指数级别的,这种时间复杂度一般放OJ都会超时的,它的展开图可以大致认为是下面:

黑色的因为返回比较早就没展开到一个完成的三角形,但我们还是可以认为它的时间复杂度为指数级别的,我们也可以通过一些方法来优化,比如记忆化搜索,这个后面我应该会有更新,可以优化为一个n的时间复杂度

cpp 复制代码
int fun4(int n)
{
	if (n == 1) return 1;
	
	return n * fun4(n - 1);
}

这个函数会展开n - 1次,所以我们认为它的时间复杂度为O(n)

上面就是常见的时间复杂度,下面的表是一些补充:

表达式 大O表示法 阶数
999999 O(1) 常数阶
3n + 7 O(n) 线性阶
n^2 + 5n + 10 O(n^2) 平方阶
2\log_2 n + 5 O(\log n) 对数阶
n + 3n\log_2 n O(n\log n) n\log n 阶
2n^3 + 3n^2 + 4n O(n^3) 立方阶
2^n O(2^n) 指数阶

当2为对数的底数时可以忽略,关于其他的一些对数时间复杂度我认为还是讲到一些算法,比如分治、二分时顺便讲解它的时间复杂度会更好


2.空间复杂度

1.1定义:

程序在运行时会有压栈和出栈的操作,一个函数在内存中也需要开辟空间来运行,这会对内存造成一定的消耗。空间复杂度也同样可以使用大O表示法来表示它的空间复杂度,方法也和计算时间复杂度非常的相似,同时我们也不太关注空间复杂度,下面我就举一些简单的例子来讲解

1.2一些空间复杂度例子

cpp 复制代码
void sort(std::vector<int> arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

这是一个非常简单的冒泡排序,这里我并没用对这个原本的顺序表进行引用操作,所以它会在内存中重新开辟一个大小为n的动态顺序表,至于是几n的我们也不关心,所以它的空间复杂度为O(n)

cpp 复制代码
void rev(std::vector<int>& arr) {
    int left = 0;               
    int right = arr.size() - 1; 
    
    while (left < right) {
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
        left++;
        right--;
    }
}

这是一个反转数组的函数,这里我对原数组进行了引用并没有额外的空间消耗,所以它的空间复杂度为O(1)级别的

cpp 复制代码
int fun4(int n)
{
	if (n == 1) return 1;
	
	return n * fun4(n - 1);
}

这个函数会调用自身n-1次,每一次调用都会占用内存,所以它的空间复杂度为O(1)级别的


完结

相关推荐
浅念-1 天前
从LeetCode入门位运算:常见技巧与实战题目全解析
数据结构·数据库·c++·笔记·算法·leetcode·牛客
talen_hx2961 天前
《零基础入门Spark》学习笔记 Day 16
笔记·学习·spark
kobesdu1 天前
【ROS2实战笔记-3】RViz2图形底层与调试暗坑
笔记·机器人·ros·rviz
深蓝海拓1 天前
基于QtPy (PySide6) 的PLC-HMI工程项目(八)在上位机中解析上行报文
网络·笔记·python·学习·plc
Engineer邓祥浩1 天前
JVM学习笔记(9) 第三部分 虚拟机执行子系统 第8章 虚拟机字节码执行引擎
jvm·笔记·学习
xuhaoyu_cpp_java1 天前
MySql学习(三)
经验分享·笔记·学习·mysql
鱼鳞_1 天前
Java学习笔记_Day31(IO流)
java·笔记·学习
全栈游侠1 天前
09-FreeRTOS通信机制(一)
笔记
智者知已应修善业1 天前
【51单片机独立按键控制往复流水灯启停】2023-6-13
c++·经验分享·笔记·算法·51单片机
民乐团扒谱机1 天前
【读论文】基于非线性光学的全光子人工神经网络处理器
论文阅读·笔记·论文