数据结构<1>时间复杂度详解和leetcode例题

2023-11-16

什么是时间复杂度和空间复杂度

前言(算法效率)

算法效率分析分为两种:第一种是时间效率,第二种是空间效率。
时间效率被称为时间复杂度,而空间效率被称作空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间

时间复杂度的计算

时间复杂度是一个衡量算法时间的相对标准。他不是一个固定的时间例如多少分多少秒,而是一个大概的估计数值。比如同一个算法在不同的机器上运行的时间肯定不同。这时候就需要我们的时间复杂度来衡量算法的时间效率。

时间复杂度通常使用大O的渐进表示法(空间复杂度也是)

那么什么是大O的渐进表示法呢?

列举几个实例给搭建看看 O(N),O(N^2),O(1);

如上所示就是大O的渐进表示法。括号内就是算法执行的次数,要记住时间复杂度计算的是执行次数
但是他不是一个准确的数字而是一个估计值,比如(N^2+2N+10)在N很大的时候除了最高次项其他的都可以忽略不计,这类似与数学中的极限思想,只保留对结果影响最大的哪一项。

推导法则如下:
1.若括号内是常数都写成O(1)
2.若是括号内是多项式例如O(N^2+3n+9)则只保留最高次项
3.若是最高次项的系数不为1则统一写成1

下面我们来看几个例子学习一下:

// 请计算一下Func1基本操作执行了多少次?
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);
}

这个算法的具体执行次数是(N*N+2N+10)
而他的时间复杂度是O(N^2)

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

要注意有的算法有最坏情况,最好情况和平均情况。比如上面这个算法是在一个字符串中寻找一个字符,最好情况就是一上来就找到了,时间复杂度是O(1),最坏情况是遍历完了字符串才找到或者没找到时间复杂度为O(N),平均情况为O(N/2);

我们计算时间复杂度一般是以最坏情况

// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{
assert(a);
int begin = 0;
int end = n;
while (begin < end)
{
int mid = begin + ((end-begin)>>1);
if (a[mid] < x)
begin = mid+1;
else if (a[mid] > x)
end = mid;
else
return mid;
}
return -1;
}

上面这段代码是二分查找,每次查找都可以排除一半的元素,比如数组有N个元素我们查找了x次2^x = N所以**x = log2(N);

但是在算法中我们一般写成O(logN);

// 计算阶乘递归Factorial的时间复杂度?
long long Factorial(size_t N)
{
return N < 2 ? N : Factorial(N-1)*N;
}

这是递归的时间复杂度计算。

每次递归的时间复杂度都是O(1),递归进行了N次,所以这个算法的时间复杂度是O(N);

插入一点:递归的效率在时间复杂度相同的情况下比循环低,因为递归需要创建函数栈帧,而循环不需要

//循环求斐波那契数列

#include<stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	
	int* fib = (int*)malloc(n * (sizeof(int)));

	fib[0] = 1;
	fib[1] = 1;
	for (int i = 2; i < n; i++)
	{
		fib[i] = fib[i - 1] + fib[i - 2];
	}
	for (int j = 0; j < n; j++)
	{
		printf("%d ", fib[j]);
	}
	return 0;
}

该算法的时间复杂度是O(n);

int show(int *num,int m,int n)
{
    int x = 0;
    for(int i=0;i<m;i++)
    {
        x^=num[i];
    }
    for(int j = 0;j<n;j++)
    {
        x^=num[j];
    }
    return x;
}

上面的算法时间复杂度是O(m+n)要注意我们不明确m和n的关系所以不能乱写,比如若m>>n那时间复杂度就是O(m)反之就是O(n),如果m和n差不多那就是O(n^2) 或 O(m^2)

为了了解各个时间复杂度的差异我们来看一下图
在这里插入图片描述

图上我们可以看到logn与1非常接近,所以这两个都是近乎最好的算法复杂度。

空间复杂度的计算

上文提到,时间复杂度实际上就是计算代码执行次数
而空间复杂度实际上就是计算变量个数

这里需要我们先记住一句话时间是累积的空间是不累积的。后面会解释的
空间复杂度也是用大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;
}
}

形参也会计算在内,所以这段代码的变量共有5个根据大O的渐进表示法为O(1),这里要注意循环了n次并不是o(n)因为上面说到空间是不累积的,每次循环完之后变量都会销毁下一次循环在创建,可以理解为使用同一片空间。

// 计算阶乘递归Factorial的空间复杂度?
long long Factorial(size_t N)
{
return N < 2 ? N : Factorial(N-1)*N;
}

递归的空间复杂度是O(n),因为每次递归都会创建一次栈帧,递归在没有递归到底的时候是不会销毁前面的栈帧的(这也就是递归太深会导致栈溢出的原因)。

//循环求斐波那契数列

#include<stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	
	int* fib = (int*)malloc(n * (sizeof(int)));

	fib[0] = 1;
	fib[1] = 1;
	for (int i = 2; i < n; i++)
	{
		fib[i] = fib[i - 1] + fib[i - 2];
	}
	for (int j = 0; j < n; j++)
	{
		printf("%d ", fib[j]);
	}
    free(fib);
	return 0;
}

该代码的时间复杂度是O(n)因为malloc开辟的空间也要计算进去。虽然 最后释放了,但是这就类似于时间复杂度的最坏情况了,我们计算的是最多的时候创建了几个变量,占用多少内存。

oj练习

消失的数字

这里的思路是,将给定数组的所有元素异或起来,然后再与标准的数组进行异或;因为相同数字异或(按位比,相同为0,不同为1)后为0,所以最后剩下的那个数字就是数组消失的数组。
注:相同数字异或为0
任何数字异或上0之后都不变
这里也可以用求和,给定数组求和,减去标准数组之和即是消失的数字。(有溢出的可能)

int missingNumber(int* nums, int numsSize){
    int n = 0;
    for(int i = 0;i<numsSize;++i)
    {
        n^=nums[i];
    }
    for(int j = 0;j<numsSize+1;++j)
    {
        n^=j;
    }
    return n;
}

旋转数组

相信大家一定能想到第一种思路就是

1.把最后一个数组元素保存起来,然后将数组元素右移一个单位,再把最后一个元素放到数组开头位置。但是这样是双层循环,时间复杂度为O(N)所以效率很低。
2.用空间换时间的思想,再创建一个数组,保存nums的后k个元素,然后再保存数组的前numSize-k个元素。这样是需要递归一次数组就可以了。时间复杂度是O(N);
3.最好的方法是,先逆置后k个数组元素,再逆置前numSize-k个数组元素,最后再整体逆置(大神想出来的方法)时间复杂度为O(1)!!!
下面我们来实现一下第三种方法。


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){
    if(k>=numsSize)
    {
        k%=numsSize;
    }
    reverse(nums,numsSize-k,numsSize-1);
    reverse(nums,0,numsSize-k-1);
    reverse(nums,0,numsSize-1);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构<1>时间复杂度详解和leetcode例题 的相关文章

随机推荐

  • Vue项目中的富文本插件表格、字号相关问题

    vue quill editor字号自定义 在近日开发项目过程中需要用到富文本 不过样式和工具栏需要按照需求来自定义 我首先想到的是用vue quill editor 不过vue quill editor的字号是以large small这类
  • FileInputFormat详解

    转载 http blog csdn net hellozpc article details 45771933 https my oschina net leejun2005 blog 133424 1 概述 我们在设置MapReduce输
  • ThinkPhp5.1快速创建模块

    快速生成模块 生成一个test模块的指令如下 gt php think build module test 表示自动生成test模块 自动生成的模块目录包含了config controller model和view目录以及common ph
  • 【内存泄漏】- 4. 使用python的gc+pyrasite模块检测python内存泄漏

    Python内存泄漏测试 1 Python内存泄漏处理机制 为了解决内存泄漏的问题 Python2 0的版本开始引入 引用计数 并基于引用计数实现了自动垃圾收集 后来为了解决循环引用导致内存泄漏的问题 又引入 标记 清除 分代回收 机制 P
  • Java实现图片上传

    使用 IDEA 软件 实现文件上传 结合JSP form表单及Servlet 实现文件上传 一 JSP界面 HTML界面 配置 实现文件上传需要配置form表单 基本设置 其中固定两个值是 提交方式只能为post enctype是固定的
  • 瑞萨电子TPS-1教学-第一讲TPS-1 PROFINET Demo Board概述 视频

    瑞萨电子TPS 1教学 第一讲TPS 1 PROFINET Demo Board概述 Renesas
  • 【深度好文】企业数字化转型的核心要素及能力架构分析

    数字化转型究竟是什么 首先我们还是摘录下百度词条上对数字化转型的一个简单说明如下 数字化转型是建立在数字化转换和数字化升级基础上 进一步触及公司核心业务 以新建一种商业模式为目标的高层次转型 数字化转型是开发数字化技术及支持能力以新建一个富
  • 72. Edit Distance

    Given two words word1 and word2 find the minimum number of steps required to convert word1 to word2 each operation is co
  • Self-attention计算方法

    三个矩阵 首先 Inputs为x1 x4 是一个sequence 每一个Input先通过一个Embedding 乘上一个Matrix得到 a1 a4 然后放入self attention 在self attention当中 每一个Input
  • Qt中的C++指针

    Qt中的C 指针 q ptr 在私有类中定义一个名字为q ptr 类型为公有类的数据指针 通过这个指针来访问公有类的数据 d ptr 在公有类中定义一个名字为d ptr 类型为私有类的数据指针 通过这个指针来访问私有类的数据 具体定义在qg
  • C语言

    要求设计的管理系统能够实现以下功能 1 每一条记录包括一个学生的学号 姓名 3个成绩 平时成绩 作业成绩 考试成绩 2 成绩录入功能 成绩信息用文件保存 可以一次完成若干条记录 3 成绩信息显示浏览功能 完成全部学生记录的显示 4 查询功能
  • elasticsearch 集群no known master node

    为什么80 的码农都做不了架构师 gt gt gt It is usually handled automatically If autodiscovery doesn t work Edit the elastic search conf
  • 装饰器在js中的实现原理

    在项目中总是能看到 connect Debounce 巴拉巴拉 通过百度才知道它叫做装饰器 装饰器有什么好 比如说 当我们写好了一个组件它叫做纯洁 当时在创造它的时候我们只想让它做一件事 但是突然有人告诉你我要加一个功能 可是我又不想让我的
  • 医学图像配准软件 ANTs(Advanced Normalization Tools)的安装和使用说明

    本文是关于医学图像配准软件 ANTs Advanced Normalization Tools 的安装和使用说明 ANTs ANTs 是 Advanced Normalization Tools 的缩写 是基于 C 语言的一个医学图像处理的
  • 利用openslide-python处理病理

    参考 博客总结https www jianshu com p bd5b572b5269 官方文档 https openslide org api python module openslide 获取元信息 如每个像素有多少微米 import
  • 杭电ACM 第2036题

    include
  • Linux 定时备份mysql数据并同步到其他mysql服务器中

    备份还原操作 导出数据库 usr bin mysqldump u root pwd database gt database sql 导入数据库 mysql u root p database lt database sql 备份到压缩文件
  • Android 实现WebView

    activity main xml
  • Git首次提交代码到仓库步骤(资料)

    第一步 登陆码云 第二步 创建一个 新的项目 第三步 创建成功后到这个页面 Git 全局设置 git config global user name 意米 git config global user email 142453222851
  • 数据结构<1>时间复杂度详解和leetcode例题

    文章目录 什么是时间复杂度和空间复杂度 前言 算法效率 时间复杂度的计算 空间复杂度的计算 oj练习 什么是时间复杂度和空间复杂度 前言 算法效率 算法效率分析分为两种 第一种是时间效率 第二种是空间效率 时间效率被称为时间复杂度 而空间效