数据结构简介&基础时空复杂度计算方法

一、数据结构基础知识

1、数据结构的定义:数据结构由某一数据元素的集合和该集合中数据元素之间的关系组成。

Data_Structure={D(Data),R(relation)} 

补充:字典序比较(假设存在两个任意的字符串需要按照字典序比较,那么我们将从左向右依次对对位的两者字符ASCII码值进行比较,若在某一位对位上ASCII码不相等,则可以比较出结果;若出现在对位对比过程中,某一个字符串已经末位对比完了依然和另一字符串对位相等,那么字符串较长的则较大)。

2、数据结构常见基本数据元关系结构

数据结构简介&基础时空复杂度计算方法_第1张图片

 3、数据结构的核心技术

     数据结构的核心技术是分解与抽象。通过分解可以划分出数据的层次(数据-数据元-数据项),再通过抽象,舍弃数据元素的具体内容,就得到数据的逻辑结构。然后,通过增加对实现细节的考虑进一步得到存储结构和实现运算即可完成设计编程。

    数据的存储结构是逻辑数据的存储映像。数据结构的存储结构有以下4种基本方法构建:

(1)顺序存储方法。该方法把逻辑上相邻的元素存放在物理位置上相邻的存储单元中。常见实现基础为一维数组;

(2)链接存储方法。元素之间的逻辑关系依靠附加的指针域阐明,常见实现基础为指针;

(3)索引存储方法。该方法在存储元素信息的同时,还建立附加的索引表。若每个结点在索引表中都有一个索引项,则该索引表称为稠密索引,若一组相邻的结点在索引表中只有一个索引项,则该索引表称为稀疏索引。注:稀疏索引中索引项中的地址指示一组相邻结点的起始存储位置。

静态稀疏索引表分析为例(数据结构-清华大学297页)

建立索引表的目的

    索引技术是组织大型数据库以及磁盘文件的一种重要技术。其能够快速顺序对比索引表中的元素关键码并匹配数据总表中的所在块从而在块内再次检索关键码所唯一标识的对象。这样能有效缩小查找范围,减小搜索算法空间复杂度。

建立索引表的抽象前提条件

    在每一个对象中有若干属性,其中应当有一个属性,其值可以唯一地标识这个对象,这个属性称为(key)关键码。若使用基于关键码的搜索,搜索结果应该是唯一的。

一般静态索引表与数据总表的联系

    静态分块索引表的关键码应该是有序排列,而数据总表的分块一般是块间有序,块内无序。对于静态分块索引我们应该保证索引表关键码可以通过判断区分块区,从而说明索引表关键码必须是数据总表的块内数据的边界,例如一般为一个块区内的最大数字(最大关键码)。

数据结构简介&基础时空复杂度计算方法_第2张图片 图源:Unique-You

 索引表的实现构成:

     一般为索引表Struct(包含标志性关键码、块长以及块首指针)和数据总表Struct(整个数据元素内容)两个数据元素,然后分别用一维有序数组存储。

(4)散列存储方法。该方法的处理方式是根据结点的关键码通过一个函数计算直接得到该结点的存储地址。

散列表(哈希表)建立方法(来源:执念斩长河):

https://blog.csdn.net/m0_37149062/article/details/105915215?utm_source=app&app_version=4.15.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

二、算法的基础时空复杂度计算方法

1、算法的定义:一个为解决某一特定任务规定了一个运算序列的有穷指令集。

2、算法的特性:(1)有输入(2)有输出(3)确定性—算法的每一步都应确切地、无歧义的定义(4)有穷性(5)能行性—原则上都能通过计算机指令精确地执行,甚至人们仅用笔和纸做有限次运算就能完成。

:算法与程序的本质区别就是程序可以是无穷等待的,其他均相似。

3、算法的性能标准

(1)正确性;

(2)可使用性(用户友好性):要求算法能够很方便地使用。为便于用户使用,要求该算法具有良好地界面和完备的用户文档;

(3)可读性;

(4)效率;

(5)健壮性:要求在算法中加入对输入参数、打开文件、读文件记录、子程序调用状态进行自动检错、报错并通过与用户对话来纠错的功能;

(6)简单性:算法的简单性便于用户编写、分析和调试,它与算法的出错率直接相关。

4、算法的渐进分析

    算法的渐进分析简称算法分析。算法分析直接与它所求解的问题的规模n有关,因此,通常将问题规模作为分析的参数,求算法的时间和空间开销与问题规模n的关系。

(1)算法的渐进时间复杂度

    要确定一个算法的准确的程序步数是非常困难的,而且也不是很必要。所以我们只要得出算法计算规模的数量级就可以大致进行估算分析。算法渐进空间复杂度一般采用大O渐进表示(针对最坏情况),其一般提法为:当且仅当存在正整数C和n0,使得T(n)<=Cf(n)对所有的n>=n0成立,则称该算法的时间增长率在O(f(n))中,记为O(f(n))

    渐进值可直接考虑关键操作的程序步数,找出其与n的函数关系f(n),从而得到渐进时间复杂度。其中,关键操作大多在循环和递归中。

    多个嵌套循环采用大O乘法规则(当两个嵌套循环程序段的时间代价分别为T1(n)=O(f(n))和T2(m)=O(g(m))时,那么将两个程序连在一起后整个程序段的时间代价为T(n,m)=O(f(n)*g(m)))

    多个并列循环采用大O加法规则(当两个并列的程序段的时间代价分别为T1(n)=O(f(n))和T2(m)=O(g(m))时,那么将两个程序连在一起后整个程序段的时间代价为T(n,m)=O(max{f(n),g(m)}))

数据结构简介&基础时空复杂度计算方法_第3张图片

 

 

 

 

数据结构简介&基础时空复杂度计算方法_第4张图片

 

注:计算机计算代码片段时间开销的小tip

数据结构简介&基础时空复杂度计算方法_第5张图片

注:比较迭代求和与递归求和程序的程序步数,虽然可能后者的程序步数会少一些,但这并不说明后者比前者运行时间少。事实上,递归调用会调用到栈时间开销会更大。

       一个语句本身的程序步数可能不等于该语句一次执行所具有的程序步数。所以要明确语句总执行次数和程序步数的区别!!一个程序语句可能包含多个程序步数。

(2)算法的渐进空间复杂度

 程序所需的存储空间包括两部分:

固定部分:这部分空间的大小与输入输出个数多少、数值大小无关。主要包括存放程序指令代码的空间,常数、简单变量、定长成分变量所占据的空间。静态空间可简单统计就可估算,估算按照存储单元来记,不涉及具体的数据存储字节。

举例:

float rsum(float a[],const int n)
{
if(n<=0) return 0;
else return rsum(a,n-1)+a[n-1];
}

    为了实现递归过程用到了递归工作栈,每递归一层就要加一个工作记录到递归工作栈中,工作记录为函数返回值、函数返回地址、数组a的首地址以及算法规模int n,然而易知需要递归n+1次才能结束,所以递归栈占用空间为4(n+1),再者a【n-1】、n、0分别为一个存储单位,只不过当n趋于一定程度大时,后续三者不影响估计此代码段的空间渐进复杂度。

可变部分:这部分空间主要包括其与问题规模有关的变量所占空间、递归工作栈所用空间,以及算法在运行过程中通过new、delete命令动态使用的空间。

附录:时间复杂度步数计算规则

数据结构简介&基础时空复杂度计算方法_第6张图片

数据结构简介&基础时空复杂度计算方法_第7张图片 

  地大作业(值得思考部分题目)

 数据结构简介&基础时空复杂度计算方法_第8张图片                                                                                                                

                                                                                                                                              2021年9月19日 

 

 

你可能感兴趣的:(C++编程及算法练习,数据结构)