算法和数据结构基础知识介绍(包括时间复杂度和空间复杂度)

本文为阅读《漫画算法》一书的笔记记录。因为在读书的过程中,发现一些知识点生疏,而这些知识点又是十分重要又基础的,因此有必要做个记录,加深对这些知识点的理解。

文章目录

一、什么是算法?

二、什么是数据结构?

三、时间复杂度

时间复杂度的定义

四、空间复杂度 

空间复杂度的表示

空间复杂度的计算

五、时间和空间的取舍 


一、什么是算法?

 

1.算法的定义

在数学领域中,算法是用于解决某一类问题的公式和思想。例如求1+2+3+4...一直加到10000的结果,我们采用(1+10000)x 10000 / 2 = 50 005 000,这种采用等差数列求和的方法,被称为高斯算法。

计算机科学领域的算法,本质是一系列程序指令用于解决特定的运算和逻辑问题

2.算法的特性

输入:有零个或多个输入

输出:有一个或多个输出

有穷性:算法必须能在执行有限个步骤之后终止

确定性:算法的每一步骤都必须有确切的定义

可行性:算法中的每个步骤都能被分解为基本的可执行的步骤,每个计算步骤都能可以在有限时间内完成。

3.算法的应用领域

运算:数学运算

查找:搜索引擎中搜索某个关键词

排序:电商网站的商品价格从低到高排序

最优决策:AI角色找到迷宫的最佳路径

面试:面试官喜欢问算法方面的问题

二、什么是数据结构?

数据结构是算法的基石。如果把算法比喻成美丽灵动的舞者,那么数据结构就是舞者脚下广阔而坚实的舞台。数据结构的英文是data structure,是数据的组织、管理和存储的格式,其使用目的是为了高效地访问和修改数据。

数据结构包含数组、链表这样的线性数据结构,也包含树、图这样的复杂的数据结构。

常见的数据结构有:

线性结构:数组,链表,栈,队列,哈希表;

树:二叉树,二叉堆等;

图:无向图,有向图等。

三、时间复杂度

由于受运行环境和输入规模的影响,代码的绝对执行时间是无法预估的。但我们却可以预估代码的基本操作执行次数。为了更清楚地了解时间复杂度,下面先给出几种常见的计算代码执行次数的例子。

1.执行次数是线性

场景:每3min吃1cm面包,那么吃掉n cm长的面包需要多久?

计算:T(n) = 3n

void eat1(int n) {
	for (int i = 0; i < 0; i++) {
		System.out.println("等待1min");
		System.out.println("等待1min");
		System.out.println("吃1cm面包");
	}
}

2.执行次数是对数计算的

场景:每5min吃掉面包剩余长度的一半,那么把n cm长的面包吃的只剩1cm 需要多久?

计算:T(n) = 5log(n)

void eat2(int n) {
	for (int i = n; i > 1; i/=2) {
		System.out.println("等待1min");
		System.out.println("等待1min");
		System.out.println("等待1min");
		System.out.println("等待1min");
		System.out.println("吃一半面包");
	}
}

3.执行次数是常数

场景:每3min吃掉1cm面包,每2min吃掉一个鸡腿,那么吃掉整个鸡腿需要多久? (此时就和面包无关了)

计算:T(n) = 2

void eat3(int n) {
	System.out.println("等待1min");
	System.out.println("吃1个鸡腿");
}

4.执行次数是用多项式计算的

场景:吃掉第一个1cm面包需要1min,第二个1cm需要2min,后面每吃掉1cm比上一个1cm多1min,问吃掉n cm的面包需要多久?(相当于计算1+2+3+...+n)

计算:T(n)=0.5n^2+0.5n

void eat4(int n) {
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < i; j++) {
			System.out.println("等待1min");
		}
		System.out.println("吃掉1cm面包");
	}
}

时间复杂度的定义

我们有了基本操作的执行次数T(n),如算法A的执行次数为T(n) = 100n, 算法B的执行次数为T(n) = 5n^2,那么如何分析和比较代码的运行时间呢?渐进时间复杂度的官方定义如下。

若存在函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于0的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称为O(f(n)),O为算法的渐进时间复杂度,简称为时间复杂度。(也称大O表示法)

简单点说:时间复杂度是对一个算法运行时间长短的度量,用大O表示,记作T(n)=O(f(n))。

推导时间复杂度的原则:

  • 如果运行时间是常数量级,则用常数1表示;
  • 只保留时间函数中的最高阶项;
  • 如果最高阶项存在,则省去最高阶项前面的系数。

因此,上述四个场景的时间复杂度如下所示。

执行次数:T(n)=3n,T(n)=5logn,T(n)=2,T(n)=0.5n^2+0.5n

时间复杂度:T(n) =  O(n),T(n)=O(logn),T(n)=O(1),T(n)=O(n^2)

记住一定要加上大O,来表示时间复杂度。

常见的时间复杂度按照从低到高的顺序,包括O(1),O(logn),O(n),O(nlogn),O(n^2)

四、空间复杂度 

在运行一段程序时,我们不仅要执行各种运算指令,同时也会根据需要,存储一些临时的中间数据,以便后续指令可以更方面地继续执行。何时需要中间数据呢,我们以一个例子来说明:

给出n个整数,如{3,1,2,5,4,9,7,2},其中有两个整数是重复的,找出这两个重复的整数。

最简单的粗暴方法的思路如下:遍历整个数列,每遍历到一个新的整数就和之前的所有整数进行对比,检查是否有重复,那么检查的次数为1+2+3+...+(n-1),可见执行的总次数为0.5n^2+0.5n,时间复杂度为O(n^2)。

若此时,我们每遍历一个整数,就把该整数存放到“字典中”,当遍历下一个整数时,就不必再和前面的数进行比较,而直接去“字典”中查找。如下图所示,“字典”左侧的key代表整数值,右侧的value代表该整数出现的次数。由于查找“字典”本身的时间复杂度为O(1),所以整个算法的时间复杂度为O(n)。而这个所谓的“字典”,是一种特殊的数据结构,叫做“散列表”。这个数据结构需要开辟一定的内存空间来存储有用的数据信息。

空间复杂度的表示

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的度量,它同样使用了大O表示法。

程序占用空间大小的计算公式记作S(n)=O(f(n)),其中n为问题的规模,f(n)为算法所占存储空间的函数。

空间复杂度的计算

1.常量空间

算法的存储空间大小固定,和输入规模没有直接的关系时,空间复杂度记作O(1)。

void fun1(int n) {
	int var = 3;
	...
}

2.线性空间

算法分配空间是一个线性的集合(如数组),并且集合大小和输入规模n成正比时,空间复杂度记作O(n)。

void fun2(int n) {
	int[] array = new int[n];
	...
}

3.二维空间

算法分配的空间是一个二维数组集合,并且集合的长度和宽度都与输入规模n成正比时,空间负责度记作O(n^2)。

void fun3(int n) {
	int[][] matrix = new int[n][n];
	...
}

4.递归空间

在递归的代码中,虽然没有显示地声明变量或集合,但是在计算机执行程序时,会专门分配一块内存,用来存储“方法调用栈”。

“方法调用栈”包括进栈和出栈两个行为。当进入一个新方法时,执行入栈操作,把调用的方法和参数信息压入栈中。当方法返回时,执行出栈操作,把调用的方法和参数信息从栈中弹出。

执行递归操作所需要的内存空间和递归的深度成正比。纯粹的递归操作的空间复杂度也是线性的,如果递归的深度是n,那么空间复杂度就是O(n)。

void fun4(int n) {
    if(n<=1){
        return;
    }
    fun4(n-1);
    ...
}

五、时间和空间的取舍 

鱼和熊掌不可兼得,很多时候,我们不得不在时间复杂度和空间复杂度之间进行取舍。

在上述寻找重复整数的例子中,双重循环的时间复杂度为O(n^2),空间复杂度为O(1),这属于牺牲时间来换取空间的情况。

而字典法的空间复杂度是O(n),时间复杂度是O(n),属于牺牲空间来换取时间的情况。

记住:在绝大多数时候,时间复杂度更为重要一些,我们宁可多分配一些内存空间,也要提升程序的执行速度。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值