数据结构与算法-基础

350 阅读6分钟

1.数据结构

数据->数据对象->数据元素->数据项

数据

“是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合。数据不仅仅包括整型、实型等数值类型,还包括字符及声音、图像、视频等非数值类型。”

特点:①可以输入到计算机 ②可以被计算机处理

数据对象

是性质相同的数据元素的集合,是数据的子集.

数据元素

是组成数据的,且有一定意义的基本单位,在计算机中通常作为整体处理. 也被称为“记录”。

数据项

一个数据元素可以由若干数据项组成.数据项是数据不可分割的最小单位.

1.1 逻辑结构

指的是数据对象中的数据元素之间的相互关系. 逻辑结构分为四种: 集合结构,线性结构,树形结构,图形结构. 具体采用什么的数据结构,是需要根据实际业务需求来进行合理的设计.

集合结构:集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系. 各个数据元素是"平等"的. 它们的共同属性是:"同属于一个集合".

线性结构: 线性结构中的数据元素之间是一对一的关系.常用的线性结构有:线性表,栈,队列,双队列,数组,串。

树型结构: 重要的非线性数据结构。树形数据结构可以表示数据表素之间一对多的关系.树型结构中的数据元素之间存在一种一对多的层次关系. 常见的树形结构: 二叉树,B树,哈夫曼树,红黑树等.

图形结构: 图形结构的数据元素是多对多的关系. 常见的图形结构: 邻近矩阵,邻接表.

1.2 存储结构

物理结构,别称"存储结构". 顾名思义,指的是数据的逻辑结构在计算机的存储形式. 设计好逻辑数据结构之后,数据的存储也是非常重要的. 数据存储结构应该正确反映数据元素之间的逻辑关系.这才是关键! 如何存储数据元素之间的逻辑关系,是实现物理结构的重点和难点.

数据元素的存储结构形式有2种: 顺序存储和链式存储;

1.2.1顺序存储

把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的.

1.2.2链式存储

是把数据元素放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的. 数据元素的存储关系并不能反映逻辑关系,因此需要用一个指针存放数据元素的地址,这样通过地址就可以找到相关关联数据元素的位置.

2.算法

是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作.

问题有千万种,算法就千变万化,算法是描述解决问题的方法。对于同一个问题,有时也会有很多种算法,这时,我们需要一个度量算法的方法。

2.1 算法效率的度量方法

习惯上将算法语句重复执行的次数作为算法的时间量度

2.1.1 算法的时间复杂度

在进行算法分析时,语句的总执行次数T(n)是关于问题规模n的函数. 进而分析T(n)随着n变化情况并确定T(n)的数量级. 算法的时间复杂度,也就是算法的时间量度. T(n) = O(f(n)).

“它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。”

大写O( )来体现算法时间复杂度的记法,我们称之为大O记法。

推导大O阶方法:

①用常数1取代运行时间中所有加法常数;

②在修改后的运行次数函数中,只保留最高阶项;

③如果在最高阶项存在且不是1,则去除与这个项相乘的常数;

2.1.2 常数阶

//1+1+1+1+1+1+1 = 7
void testSum2(int n){
    int sum = 0;                //执行1次
    sum = (1+n)*n/2;            //执行1次
    sum = (1+n)*n/2;            //执行1次
    sum = (1+n)*n/2;            //执行1次
    sum = (1+n)*n/2;            //执行1次
    sum = (1+n)*n/2;            //执行1次
    
    printf("testSum2:%d\n",sum);//执行1次
    
}

无论常数n是多少,执行时间恒定.我们的都称之为具有O(1)的时间复杂度.又称为常数阶;

2.1.3 线性阶

线性阶的循环结构会复杂很多. 要确定某个算法的阶次,我们常常需要先确定某个特定语句或某个语句集的运行次数. 因此,我们要分析算法的复杂度,关键就是要分析循环结构的运行情况.

void add2(int x,int n){
    for (int i = 0; i < n; i++) {
        x = x+1;
    }
}

这段代码的时间复杂度为O(n).

2.1.4 对数阶

int count = 1;
while(count < n){
	count = count * 2;
}

这段代码的时间复杂度为O(㏒n).

2.1.5 平方阶

/x=x+1; 执行n*n次
void add3(int x,int n){
    for (int i = 0; i< n; i++) {
        for (int j = 0; j < n ; j++) {
            x=x+1;
        }
    }
}

这段代码的时间复杂度为O(n^2).

2.2 算法空间复杂度

算法设计有一个重要原则,即空间/时间权衡原则(space/time tradeoff)

算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记做: S(n) = n(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数.

一般情况下, 一个程序在机器上执行时,除了需要寄存本身所用的指令,常数,变量和输入数据外,还需要一些对数据进行操作的辅助存储空间. 其中,对于输入数据所占的具体存储量取决于问题本身,与算法无关. 这样 只需要分析该算法在实现时所需要的辅助空间就可以了.

对一个算法,其时间复杂度和空间复杂度往往会互相影响. 当追求一个较好的时间空间复杂度时,可能会导致占用较多的存储空间. 即可能会使用空间复杂度的性能变差.反之亦然. 不过,通常情况下,鉴于运算空间较为充足,人们都以算法时间空间复杂度作为算法优先的衡量指标.