数据:是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合
数据元素:是组成数据的、有一定意义的基本单位,在计算机中通常作为整体处理,也被称为记录
数据项:一个数据元素可以由若干个数据项组成,数据项是数据不可分割的最小单位
数据对象:是性质相同的数据元素的集合,是数据的子集
不同数据元素之间不是独立的,而是存在特定的关系,我们将这些关系称为结构
数据结构:是相互之间存在一种或多种特定关系的数据元素的集合
逻辑结构:是指数据对象中数据元素之间的相互关系
集合结构:集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系。各个数据元素是”平等”的,类似于数学中的集合
线性结构:线性结构中的数据元素之间是一对一的关系
树形结构:树形结构中的数据元素之间存在一种一对多的层次关系
图形结构:图形结构的数据元素是多对多的关系
物理结构(存储结构):是指数据的逻辑结构在计算机中的存储形式
顺序存储结构:是把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的
链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的,数据元素的存储关系并不能反映其逻辑关系,因此需要用一个指针存放数据元素的地址,这样通过地址就可以找到相关数据元素的位置
数据类型:是指一组性质相同的值的集合及定义在此集合上的一些操作的总称
在C语言中,按照取值的不同,数据类型可以分为两类
是不可以再分解的基本类型,包括整型、字符型等
由若干个类型组合而成,是可以再分解的,例如整型数组
抽象数据类型(Abstract Data Type, ADT):是指一个数学模型及定义在该模型上的一组操作,如”整数”类型->整型
抽象数据类型体现了程序设计中问题分解、抽象和信息隐藏的特性
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作
算法具有五个基本特性:输入、输出、有穷性、确定性和可行性
正确性:算法的正确性是指算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案
正确性大体分为以下四个层次:
可读性:算法设计的另一个目的是为了便于阅读、理解和交流
健壮性:当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结果
设计算法应该尽量满足时间效率高和存储量低的需求
事后统计方法
这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低
缺陷较大,不予采纳
事前分析估算方法
在计算机程序编制前,依据统计方法对算法进行估算
一个程序的运行时间,依赖于算法的好坏和问题的输入规模
在分析程序的运行时间时,最重要的是把程序看成是独立于程序设计语言的算法或一系列步骤
函数的渐近增长:给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n > N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐近快于g(n)
判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注主项(最高阶项)的阶数
在进行算法分析时,语句总是执行次数T(n)是关于问题模型n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级,算法的时间复杂度,也就是算法的时间量度,记作:T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度,其中f(n)是问题规模n的某个函数
一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法
常数阶:O(1)
不管n为多少,执行的次数都是恒定的,不会随着n的变大而发生变化,其时间复杂度为O(1)
线性阶:O(n)
循环结构中的代码需要执行n次,其时间复杂度为O(n)
for(i = 0; i < n; i++){
}
对数阶:O(logn)
有多少个2相乘后大于n,则会退出循环
2^x=n
int count = 1;
while(count < n){
count = count * 2;
}
平方阶
两层循环嵌套
int i,j;
for(i = 0; i < n; j++){
for(j = i; j < n; j++){
}
}
算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记作:S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数
当不用限定词地使用”复杂度”时,通常都是指时间复杂度
线性表(List):零个或多个数据元素的有限序列
除第一个元素外,每一个元素有且只有一个直接前驱元素,除了最后一个元素外,每一个元素有且只有一个直接后继元素,数据元素之间的关系是一对一的关系
在较复杂的线性表中,一个数据元素可以由若干个数据项组成
线性表的顺序存储结构,指的是用一段地址连续的存储单元一次存储线性表的数据元素
在内存中占据一定的内存空间,然后把相同数据类型的数据元素依次存放在这块空地中
属性:
存储器中的每个存储单元都有自己的编号,这个编号称为地址
对于每个线性表位置的存入或者取出数据,对于计算机来说都是相等的时间( LOC(ai)=LOC(a1)+(i−1)∗c ),也就是一个常数,因此它的时间复杂度为O(1)
插入:
删除:
插入或删除时,平均移动次数和最中间的那个元素移动次数相等,为(n-1)/2
插入和删除时,时间复杂度为O(n)
优点:
缺点:
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的,这就意味着,这些数据元素可以存在内存未被占用的任意位置
链式结构中,除了要存储数据元素信息外,还要存储它的后继元素的存储地址
链表中第一个结点的存储位置叫做头指针,之后的每一个结点,其实就是上一个的后继指针指向的位置,最后一个结点的指针为null
为了更加方便地对链表进行操作,有时会在单链表的第一个结点前附设一个结点,称为头结点,头结点的数据域可以不存储任何信息,也可以存储如线性表的长度等附加信息,头结点的指针域存储指向第一个结点的指针
头指针与头结点的异同:
单链表中查找某一个元素,必须要从头开始找
获得链表第i个数据的算法思路:
时间复杂度为O(n)
在ai和ai+1之间插入一个数据,只需要
s->next=p->next;
p->next=s;
单链表第i个数据插入结点的算法思路:
在ai-1和ai+1之间删除ai结点,只需要
q=p->next;
p->next=q->next;
单链表第i个数据删除结点的算法思路:
单链表在查询、插入和删除操作上的时间复杂度都是O(n),但是如果需要从第i个位置,插入10个元素,对于顺序存储结构意味着,每一次插入都需要移动n-i个元素,每次都是O(n),而单链表,只需要在第一次时,找到第i个位置的指针,此时为O(n),接下来的时间复杂度都是O(1),所以对于插入或者删除数据越频繁的操作,单链表的效率优势就越是明显
单链表整表创建的算法思路:
循环:
单链表整表删除的算法思路如下:
循环
存储分配方式
时间性能
查找
插入和删除
空间性能
用数组描述的链表叫做静态链表(数组中的元素由两个数据域组成,data和cur)
数组中的第一个元素(下标为0)的cur存放备用链表的第一个结点的下标(即下一个元素插入存放的位置),数组的最后一个元素的cur则存放第一个有数值的元素的下标(即存放链头的位置)
将元素”丙”插入到”乙”和”丁”之间
将元素”甲”删除
优点
缺点
将单链表中终端结点的指针端由空指针改为指向头指针,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表
双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域
非空的循环的带头结点的双向链表如下图所示
双向链表在插入和删除时,需要更改两个指针变量
s->prior = p;
s->next = p->next;
p->next->prior = s;
p->next = s;
删除结点p,只需要下面两步骤
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);