数据结构第一周笔记(2)——基本概念(慕课浙大版本--XiaoYu)

145 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

什么是数据结构

  1. 数据对象在计算机中的组织方式

    1. 逻辑结构(一对多的逻辑结构有个名字叫做"树")=>树形结构 线性结构(一对一) 图的结构(多对多)

    2. 物理存储结构

    3. 抽象数据类型(Abstract Data Type)

      1. 数据类型

        1. 数据对象集:就是我们说的"是什么东西"
        2. 数据集合相关联的操作集
      2. 抽象:描述数据类型的方法不依赖于具体实现

        1. 与存放数据的机器无关
        2. 与数据存储的物理结构无关
        3. 与实现操作的算法和编程语言都无关只描述
        4. 只描述数据对象集和相关操作集"是什么",并不涉及"如何做到"的问题
  2. 数据对象必定与一系列加在其上的操作相关联

  3. 完成这些操作所用的方法就是算法

例4:"矩阵"的抽象数据类型定义

  1. 类型名称:矩阵(Matrix)

image-20220626155802588

Multiply:乘的意思

a是矩阵元素的值:那要用二维数组去存他还是一维数组又或者是十字链表呢?答案是不用关心,我们需要的只是一个矩阵

Matrix Add(...):先按行加?先按列加?使用什么语言? 答案是统统不管,这就是"抽象"

什么是算法

定义

  1. 算法(Algorithm)

    1. 一个有限指令集

    2. 接受一些输入(有些情况不需要输入)

    3. 一定至少会产生一个输出(否则就没有意义了)

    4. 一定在有限步骤之后终止的,他不像是操作系统只要不关机就可以一直跑在上面

    5. 描述算法的时候不能有无限循环的概念的

    6. 每一条指令必须

      1. 有充分明确的目标,不可以有歧义
      2. 计算机能处理的范围之内(目标不可以太远大)
      3. 描述手段应该"抽象",不依赖于任何一种计算机语言以及具体的实现手段
    例1:选择排序算法的伪码描述
void SelectionSort ( int List[], int N)
{
    //将N个整数List[0]...List[N-1]进行非递减排序
    for(i = 0; i < N; i++){
        MinPosition = ScanForMin(List, i, N-1);
        //ist[i]到List[N-1]中找最小元,并将其位置赋给MinPosition;
        Swap(List[i],List[MinPosition]);
        //排序部分的最小元换到有序部分的最后位置;
    }
}
//这不是C语言,虽然他带有C语言的一些特征,但他for循环里面的内容是用自然语言来描述的.上面伪码描述特点:抽象
​
抽象----
    List到底是数组还是链表(虽然看上去很像数组)? 其实不管是数组还是链表都不会报错
    Swap用函数还是用宏去实现(虽然他看上去很像一个函数)? 但其实用宏写也可以,在我们使用算法的时候是不关心的

什么是好的算法?

  1. 空间复杂度S(n)——根据算法写成的程序在执行时占用存储单元的长度。这个长度往往与输入数据的规模有关。空间复杂度过高的算法可能导致使用的内存超限,造成程序非正常中断。
  2. 时间复杂度T(n)——根据算法写成的程序在执行时耗费时间的长度。这个长度往往也跟输入数据的规模有关。时间复杂度过高的低效算法可能导致我们在有生之年都等不到运行结果。

这个n是我们要处理的内容,这个程序所用的时间与空间都跟这个n是有直接关系的

//递归实现  弊端:递归的程序对空间的占用有的时候是很恐怖的
void PrintN ( int N )
{
    if( N ){
        //假设N=10w,第一步就是10w-1,调用这个函数之前,你的系统需要把当前的这个函数所有的现有的状态都存到系统内存的某一个地方
        //原本是存一下使用后就可以删掉了,使用递归之后在你执行10w-99999之前要把前面所有的运算先执行一遍而不是直接10w-99999,一次性存这么多内容,内存会爆掉的
        //S(N)=C(常数)*N  =>线性增长
        printN( N - 1 );
        printf("%d\n",N);
    }
    return;
}

借用上面例3的案例

//计算机算加减比算乘除快很多
//直接翻译的结构
double f( int n, double a[], double x)
{
    int i;
    double p = a[0];
    for ( i = 0 ; i <= n ; i++ ){
        p += (a[i] * pow(x,i));
    }
    return p;
}//这里一共运行了(1+2+...+n)=(n²+n)/2次乘法        时间复杂度:T(n) = C1n² +C2n
//秦久邵的方法
double f( int n, double a[], double x)
{
    int i;
    double p = a[n]
        for( i = n ; i > 0 ; i-- ){
            p = a[i-1] + x * p;
        }
    return p;
}//这里一共就运行了n次乘法        时间复杂度:T(n) = C *n

image-20220626165535815

复杂度的渐进表示法

“上界(upper bound)是一个与偏序集有关的特殊元素,指的是偏序集中大于或等于它的子集中一切元素的元素。若数集S为实数集R的子集有上界,则显然它有无穷多个上界,而其中最小的一个上界常常具有重要的作用,称它为数集S的上确界。”

太大的上界跟太小的下界对我们分析算法的时候是没有太大的帮助的,尽可能跟它的真实情况贴得越近越好

image-20220626170030324

当我们在取大O的时候,我们一般取的是最小的那个上界。当我们在取Ω的时候通常是写的我们能力范围内找到的最大的那个下界

image-20220626170944925

image-20220626171628558

复杂度分析窍门

image-20220626172743421

比如说,如果我们有两段算法,我们知道它们复杂度的上界是什么。如果把两段算法拼在一起的时候,总时间就是两段的和。

那么它们的上界,就是两个上界中间,比较大的那个

当我们把两段算法嵌套起来的时候,两个复杂度要相乘的时候,它的上界就是它们上界的乘积

通过这个我们可以知道如果T(n)是关于n的一个k阶多项式的话,真正起作用的只有最大的那一项 ,其他项都是可以忽略不计的

一个for循环的时间复杂度等于循环次数乘以循环体代码的复杂度,这是一个相乘的关系

if-else :结构的复杂度取决于if的条件判断复杂度和两个分支部分的复杂度,总体复杂度取决三者中最大