数据结构与算法入门

基本概念

数据:描述客观事物的数值、字符各种符号的集合。

数据项:具有原子性的不可分割的最小数据单位

数据元素:数据的基本单位,数据集合的个体

数据对象:性质相同的数据元素的集合,数据的子集

数据结构:相互之间存在一种或多宗特定关系的数据元素的集合

数据的逻辑结构:数据结构的逻辑层面

数据的存储结构:数据结构的物理层面

数据结构=逻辑结构+存储结构+(在存储结构上的)运算/操作

数据结构类型

数据的逻辑结构

线性结构和非线性结构

线性结构:有且只有一个起点和重点和唯一的直接前驱和后继

案例:冰糖葫芦,排队取餐

非线性结构:多个直接前驱和后继

常见的:树(二叉树等),图(网等)

树:单位的组织架构、文件系统

图:交通线路图,地图

集合结构,线性结构,树状结构,网状结构

表和树是最常用的两种高效数据结构

集合结构:数学中的集合。确定性,唯一性,无序性。

该结构的数据之间的关系是:属于同一个集合,元素关系很弱。

线性结构:元素之间一对一的线性关系的数据结构。

树状结构:除了开头,其他都有唯一的直接前驱和多个后继元素。

特点:一对多

网状结构:每个元素都是多个前驱后继

特点:多对多

数据的存储结构

常见的:顺序存储,链式存储,索引存储,散列存储

顺序存储结构

把逻辑上相邻的节点存储在物理位置上相邻的存储单元中。

通常顺序存储结构是借助数组来描述的,是一块连续的存储空间

优点:节省存储空间,采用这种方法时,可实现对节点的随机存取。

缺点:插入删除需要移动元素,效率低,固定空间,导致空闲浪费。

数据结构与算法入门

链式存储结构

不连续的存储空间,每个存储节点对应一个需要存储的数据元素。

每个节点是由数据域和指针组成。

逻辑上相邻,物理上不必相邻。

优点:插入、删除灵活,不必移动元素,不会闲置空间。

缺点:存储密度小,查找慢。

数据结构与算法入门

索引存储结构

除建立存储节点信息外,还建立附加的索引来表来标识节点的地址。

案例:图书、字典的目录就是索引。

数据结构与算法入门

散列存储结构

根据节点的关键字直接计算出该节点的存储地址HashSet HashMap

一种神器的结构,添加、查询速度无比的快。

案例:线性表 = 顺序表 + 链表

数据结构与算法入门

同一逻辑结构可以对应多种存储结构。

同样的运算,在不用的存储结构中,其实现的过程是不一样的。

 

算法:

指令的集合。为了解决特定问题而规定的一系列操作。

特性:输入、输出、可行性、有穷性、确定性

通俗的说:算法就是阶梯的过程。

案例:求0+1+2+3+...+10000=?

算法1、依次相加 ,使用while,for,do-while实现

算法2、递归实现

算法3、高斯解法:首尾相加*50,梯形或者三角形公式完成。

梯形:(1+10000)*10000/2 或 三角形:100*101/2

算法的优劣依据:复杂度(时间和空间)

时间复杂度:计算机的工作量。

空间复杂度:计算机所耗内存空间。

时间复杂度

时间频度:一个算法中的语句执行的次数,表示为T(n),n表示问题的规模。

一个算法话费的时间与算法中语句执行的次数成正比

时间复杂度:问题的规模,而不是具体的次数。就是时间频度去掉低阶项和首项常数,简称T(n) = O(f(n))

f(n)是T(n)的同数量级函数

O表示最坏情况下的时间复杂度

案例:

某两个算法的时间频度是:

T(n) = 10000n2+10n+6

T(n) = 10n2+10n+6

时间复杂度都是:

T(n) = O(n2)

因为平均时间复杂度和最坏时间复杂度大部分算法的结果都一样,所以讨论最坏时间复杂度,因为平均时间复杂度难算。

Ο(欧米可荣)符号给出了算法时间复杂度的上界(最坏情况  <=

比如:T(n) =O(n2) 

Ω(欧米伽)符号给出了时间复杂度的下界(最好情况 >=

比如:T(n) =Ω(n2) 

而Θ(西塔)给出了算法时间复杂度的精确阶(最好和最坏是同一个阶  =

比如:T(n) =Θ(n2) 

时间复杂度的计算:

找出算法中的基本语句,如:循环体

计算基本语句的执行次数的数量级;最高次幂,忽略系数。

重点分析:增长率

用大O表示算法的时间性能。将数量级放入大O

案例:

简单语句:

int count = 0;

时间复杂度为:T(n) = O(1);

10000个简单语句也是T(n) = O(1);因为是常数,不是无穷大的n

单循环语句:

 

int n=10;

int count=0; 

for (int i=1; i<=n; i++){

  count++; 

}

时间复杂度为:T(n) = O(n);

(2)

int n=10;

int count=0; 

for (int i=1; i<=n; i*=2){

  count++; 

}

结果:1 2 4 8 16 32 64 128 256...

230 = 210*210*210 = 1024*1024*1024 = 1073741824 ≈1000*1000*1000=10亿

时间复杂度为:T(n) = O(log2n);

嵌套循环

(1)

int n=8, count=0; 

for (int i=1; i<=100n; i++)

  for (int j=1; j<=10n; j++) 

      count++; 

}

}

时间复杂度:O(n2)=内循环n次*外循环n次

(2)

int n=8, count=0; 

for (int i=1; i<=100n; i*=2)

  for (int j=1; j<=10n; j++) 

      count++; 

}

}

时间复杂度:O(n*log2n)=内循环n次*外循环log2n次

(3)

int n=8, count=0; 

for (int i=1; i<=100n; i++)

  for (int j=1; j<=i; j++) 

      count++; 

}

}

时间复杂度:O(n2)=1+2+3+4....+n=(1+n)*n/2

常用的时间复杂度:

   常数阶O(1)

       对数阶O(log2n) 

       线性阶O(n) 

       线性对数阶O(n*log2n) 

       平方阶O(n2)

       立方阶O(n3) 

       ... 

       k次方阶O(nk) 

       指数阶O(2n) 

       阶乘阶O(n!) 

       上面各种时间复杂度级别,执行效率越来越低。 

差异:例:对数阶O(log2n)   线性阶O(n) ,当n=10的8次方时,此时是1亿次,一个是8次

空间复杂度

存储量包括:

自身

输入数据

辅助变量

输出数据所占空间只取决于问题本身,和算法无关,只需要分析除输入和程序之外的辅助变量所占的空间。

简称:S(n) = O(g(n))

(1)

int fun(int n){  

int i,j,k,s;

s=0; 

for (i=0;i<=n;i++)          

for (j=0;j<=i;j++)           

for (k=0;k<=j;k++)    

s++;

空间复杂度:S(n) = O(1),就是4个变量占的空间

递归

void fun(int a[],int n,int k) {

int i; 

if (k==n-1){

for (i=0;i<n;i++){}

printf("%d\n",a[i]);  //执行n次 

}

}else{

for (i=k;i<n;i++) 

a[i]=a[i]+i*i;

          fun(a,n,k+1); 

}

}

空间复杂度:S(n) = O(n),调用1次开辟一个空间。

 

相关文章: