数据结构初阶 算法的时间复杂度与空间复杂度的讲解 个人随堂笔记

本文详细介绍了数据结构与算法的概念,重点阐述了时间复杂度和空间复杂度的定义、计算方法,包括大O符号的应用,以及递归调用函数的复杂度分析。通过实例探讨了常见复杂度如O(N)和O(NlogN)等,最后给出了轮转数组的三种优化方法及其复杂度分析。
摘要由CSDN通过智能技术生成

算法的时间复杂度与空间复杂度的讲解

什么是数据结构与算法

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

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

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

大O符号(Big O notation):是用于描述函数渐进行为的数学符号。
推导大O阶方法:
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

时间复杂度

算法中的基本操作的执行次数,为算法的时间复杂度。

时间复杂度,计算时为该算法的最坏运行情况,是一个稳健保守预期。
抓大头,取决定性结果的一项,是执行次数的量级,去掉那些对结果影响不大的项。忽略系数(N无限大时,系数对结果的影响可以忽略不计),常数次时(无论常数多大)复杂度为O(1)

常见的时间复杂度的计算

void Func3(int N,int M)
{
	int count=0;
	for(int k=0;k<3*N;k++)
	{
		++count; 
	}
	for(int k=0;k<2*M;k++)
	{
		++count;
	}
	printf("%d\n",count);
}

因为N,M的量级并不清楚,时间复杂度为O(N+M),去除系数。
如果N和M一样大,则为O(N)或O(M),忽略系数
如果N远大于M,则为O(N)
如果N远小于M,则为O(M)

int binarysearch(int a,int n,int x)
{
	assert(a);
	int begin=0;
	int end=n-1;
	while(begin<=end)
	{
		int mid=begin+((end-begin)>>1);
		if(a[mid]<x)
			begin=mid+1;
		else fi(a[mid]>x)
			end=mid-1;
		else
			return mid;
	}
	return -1;
}

每次查找时,区间数据个数减半,最坏情况,查找区间缩放到只剩一个数据时(开头,结尾,或找不到)为最坏情况,假设查找了x次,则数据个数为2^x=N,则x=log_2 N,时间复杂度为 log_2 N

递归调用函数复杂度的计算

long long Fib(size_t N)
{
	if(N<3)
		return 1;
	return Fib(N-1)+Fib(N-2);
}

在这里插入图片描述
执行次数=2 ^ 0 + 2 ^ 1……2^ (N-1) = 2^N-1
时间复杂度O(2^N)

消失的数字
思路一:遍历加排序,下一个数字不等于下一个数+1,则下一个数就是消失的数字。
时间复杂度:排序最快为O(N*log N)(qsort)+遍历O(N)
思路二:计算0到N的总和减去遍历数组的总和,结果为消失的数字。
时间复杂度:遍历O(N)
思路三:遍历异或
时间复杂度:遍历异或O(N)

int missnumber(int *nums,int numsize)
{
	int N=numsize;
	int sum=N*(N+1)/2;		//计算0到N的总和
	for(int i=0;i<numsize;i++)
	{
		int ret-=num[i];				//遍历减去数组中数的总和
	}
	return ret;
}
int missnumber(int *nums,int numsize)
{
	int N=numsize;
	int ret=0;
	for(int i=0;i<N;i++)
	{
		ret=ret^num[i]^i;				//遍历异或数组中数的与每个数,剩下的ret就是缺失数
	}
	ret=ret^N;							//补上遍历时少异或了N
	return ret;
}

空间复杂度

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

递归函数的空间复杂度

long long* Fibonacci(size_t n)
{
	if (n==0)
		return 0;
	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]=fibaarray[i-1]+fibarray[i-2];
	}
	return fibarray;
}

malloc函数额外开辟了n+1个字节的空间,空间复杂度为O(N)

long long Fac(size_t n)
{
	if (n==0)
		return 1;
	return Fac(n-1)*n; 
}

递归调用时,Fac(n)-》Fac(n-1)-》……-》Fac(1)-》Fac(0)在栈上共开辟了n个常数的空间,空间复杂度为O(N)

long long Fib(size_t N)
{
	if(N<3)
		return 1;
	return Fib(N-1)+Fib(N-2);
}

递归最大问题:深度太深,容易栈溢出
时间是累计的,空间是可以重复利用的
从Fib(N)-》Fib(N-1)……Fib(3)-》Fib(2),调用到Fib(2)时返回值时释放空间,返回Fib(3),Fib(3)-》Fib(3-1)时,将原Fib(2)的空间给Fib(3-1)复用
额外的空间开辟为Fib最深的栈帧个数,为O(N)

void func1()
{
int a=0;
printf("%p\n",&a);
}
void func2()
{
int a=0;
printf("%p\n",&a);
}
int main()
{
	func1();
	func2();
	return 0;
}

可以看出打印的地址相同,不同函数使用同一块空间

练习题

轮转数组oj
给定一个整数数组nums,将数组中的元素向右轮转k个位置,其中k时非负数
要求时间复杂度O(N),空间复杂度O(1)

方法一:循环右旋1次,合计右旋k%N次(右旋N次数组没有改变)
时间复杂度:最坏情况下右旋N-1次,O(N^2)
空间复杂度O(1)

方法二:创建一个与原数组相同大小的空间temp,将后k个元素放到temp前面,将剩下的k-1个元素放到temp后面
时间复杂度:O(N),遍历复制数组
空间复杂度:O(N),开辟N个大小空间的temp数组

方法三:前N-k个数组元素逆置,后k个元素逆置,整体逆置
时间复杂度:O(N)
空间复杂度:O(1)

#define  _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void revolve(int* arr,int numbersize ,int k)  //方法一
{
	int n=numbersize ;
	k %= n;
	for (int i = 0; i < k; i++)
	{
		int temp = arr[n - 1];
		for (int j = n-1; j >0; j--)
		{
			arr[j] = arr[j-1];
		}
		arr[0] = temp;
	}
}


void rotate(int* arr, int numbersize, int k)  //方法二
{
	k %= numbersize;
	int* temp = (int*)malloc(sizeof(int) * numbersize);
	memcpy( temp+k, arr, sizeof(int)*(numbersize-k));
	memcpy( temp, arr + numbersize - k,sizeof(int)*k);
	memcpy(arr, temp, sizeof(int)*numbersize);
}

void reverse(int* arr, int left, int right)   //方法三
{
	while (left < right)
	{
		int temp = arr[left];
		arr[left] = arr[right];
		arr[right] = temp;
		left++;
		right--;
	}
}
void inverse(int* arr, int numbersize, int k)
{
	k %= numbersize;
	reverse(arr, 0, numbersize - k-1);
	reverse(arr, numbersize-k, numbersize-1);
	reverse(arr, 0, numbersize - 1);
}



void print(int* arr, int numbersize)   //输出
{
	for (int i = 0; i < numbersize; i++)
	{
		printf("%d", arr[i]);
	}
	printf("\n");
}
void test1()     //测试
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	revolve(arr, sizeof(arr) / sizeof(arr[0]), 2);
	print(arr, sizeof(arr) / sizeof(arr[0]));
}
void test2()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	rotate(arr, sizeof(arr) / sizeof(arr[0]), 2);
	print(arr, sizeof(arr) / sizeof(arr[0]));
	
}
void test3()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	inverse(arr, sizeof(arr) / sizeof(arr[0]), 2);
	print(arr, sizeof(arr) / sizeof(arr[0]));

}
int main()
{
	test1();
	test2();
	test3();
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值