算法通关村|第零关|热身:数据结构、时间复杂度、空间复杂度

一、“数据结构说来说去也就只有那几种”

说到数据结构,我们讨论的只有固定的以下几种:集合、数组、链表(线性表)、树、图,这几大类:

  1. 集合 { , , , }

从数学/数据结构的领域来看,当我们谈论“集合”(Set)时,我们通常指的是一个不包含重复元素的数据结构。因此,集合中的元素是唯一不重复的;数据元素之间的关系是并列的。

  1. 线性表(1 v 1)

image.png

  1. 树(1 v n)

image.png

  1. 图(n v n)

image.png

还有一种比较特殊的Hash结构,本身使用数组存储的,但访问不是线性的,而是<key,value>的特殊访问方式,先放在这里,后面进度到了再说;

二、“当同一个问题有多种解决方式时,如何评判各种方式的好坏呢?时间T(n)/空间复杂度S(n)”

(一)关于时间效率

T ( n ) = O ( n ) T(n) = O(n) T(n)=O(n)// T代表Time

1. 题目

写程序计算给定多项式,在给定点x处的值;
f 1 ( x ) = a 0 + a 1 x + a 2 x 2 + a 3 x 3 + … … + a n x n f1(x) = a_0+a_1x+a_2x^2+a_3x^3+……+a_nx^n f1(x)=a0+a1x+a2x2+a3x3+……+anxn
f 2 ( x ) = a 0 + x ( a 1 + x ( a 2 + x ( … … ( a n − 1 + x ∗ a n ) f2(x)=a_0+x(a_1+x(a_2+x(……(a_{n-1}+x*a_n) f2(x)=a0+x(a1+x(a2+x(……(an1+xan)

2. 两种实现方式
a) 直接模拟(运行时间长很多)
double f1(int n, double a[], double x){
    double p = a[0];
    for (int i=1;i<=n;i++){
        p = p + a[i]*Math.pow(x,i); 
    }
    return p;
}
b) 标准程序
double f2(int n, double a[], double x){
    double p = a[n];
    for (int i=n;i>0;i--){
        p = a[i-1] + x*p;
    }
    return p;
}
3. 计算时间复杂度(*次数)
  1. 直接模拟
    • 基本操作:Math.pow(x,i); (1<=i<=n):x的i次方,每个循环里有进行i次乘法;
    • T 1 ( n ) = 1 + … … + n = ( 1 + n ) ∗ n 2 = O ( n 2 ) T_1(n) = 1+……+n = \frac{(1+n)*n}{2} = O(n^2) T1(n)=1+……+n=2(1+n)n=O(n2)
  2. 标准程序
    • 基本操作:每个循环里只有1次乘法;
    • T 2 ( n ) = 1 ∗ n = n = O ( n ) T_2(n) = 1*n = n = O(n) T2(n)=1n=n=O(n)
  • 所以当n的规模够大的时候,f2()运行一定是比f1()更快的;
4. 测试运行时间

f1()程序运行时间:2.9E-6/毫秒ms
f2()程序运行时间:1.7E-6/毫秒ms

+ Java中测试代码块运行时间:System.currentTimeMillis();
long starTime = System.currentTimeMillis();
// Your Code Block
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:"+(endTime-starTime)+"/毫秒ms");

Q:当函数运行时间太短,测试结果为0时,如何测试准确运行时间?A:重复!

+ 直观感受n与O(n)的联系

image.png

  • logn:按说是以2为底数的,但也只是常数级别的所以不重要,默认写法忽略底数;

image.png

  • 遇到一个 O ( n 2 ) O(n^2) O(n2)的算法,第一反应是能不能想办法降成 O ( n ∗ l o g n ) O(n*logn) O(nlogn),效率提高会是很显著的;

image.png

  • 修正:第二行第二列的f(n)改成:f(n) = n*logn;

(二)关于空间使用

S ( n ) = O ( n ) S(n) = O(n) S(n)=O(n)// S代表Space

1. “空间复杂度其实就是申请辅助空间做现在的事情”
  • 申请几个变量:O(1)
  • 申请一维数组、链表、队列、栈、Hash:O(n)
  • 申请二维数组:O(n^2)

(到平方级别的辅助空间需求就已经很过分了,基本就用到这些)

2. 题目

写程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1到N的全部正整数;

3. 两种实现方式
a) for循环
void printNCirculation(int N){
    for (int i=1; i<=N ; i++){
         System.out.println(i);
    }
}
b) 递归
void printNRecursion(int N){
     if (N>0){
        printNRecursion(N-1);
        System.out.println(N);
     }
}
4. 测试
  • 测试数据:N = 100, 1000, 10000, 100000……
  • 测试结果:
    • Recursion递归:辅助栈 空间复杂度O(n),在10w测试时报错:StackOverflowError 栈溢出;
    • for循环:辅助变量i 空间复杂度O(1),正常运行无报错;
5. 测试结论
  • 递归的代码整洁易理解,但计算机很不愿意跑递归程序;
  • 递归因为要保存每层的运行数据,对空间的占用是很恐怖的,程序很容易崩溃;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值