数据结构【基础知识点总结】
一、数据
数据(Data)是信息的载体,它能够被计算机识别、存储和加工处理。它是计算机程序加工的原料,应用程序处理各种各样的数据。 计算机科学中,所谓数据就是计算机加工处理的对象,它可以是数值数据,也可以是非数值数据。数值数据是一些整数、实数或复数,主要用于工程计算、科学计算和商务处理等; 非数值数据包括字符、文字、图形、图像、语音等。
二、数据元素
数据元素(Data Element)是数据的基本单位。在不同的条件下,数据元素又可称为元素、结点、顶点、记录等。例如,学生信息检索系统中学生信息表中的一个记录、八皇后问题中状态树的一个状态、教学计划编排问题中的一个顶点等,都被称为一个数据元素。 有时,一个数据元素可由若干个数据项(Data Item)组成,例如,学籍管理系统中学生信息表的每一个数据元素就是一个学生记录。它包括学生的学号、姓名、性别、籍贯、出生年月、成绩等数据项。 这些数据项可以分为两种:一种叫做初等项,如学生的性别、籍贯等,这些数据项是在数据处理时不能再分割的最小单位;另一种叫做组合项,如学生的成绩,它可以再划分为数学、物理、化学等更小的项。通常,在解决实际应用问题时是把每个学生记录当作一个基本单位进行访问和处理的。
三、数据对象
数据对象(Data Object)或数据元素类(Data Element Class)是具有相同性质的数据元素的集合。在某个具体问题中,数据元素都具有相同的性质(元素值不一定相等),属于同一数据对象(数据元素类),数据元素是数据元素类的一个实例。例如,在交通咨询系统的交通网中,所有的顶点是一个数据元素类,顶点A 和顶点B 各自代表一个城市,是该数据元素类中的两个实例,其数据元素的值分别为A 和B。
四、数据结构
数据结构研究的三个方面: (1)数据集合中各数据元素之间所固有的逻辑关系,即数据的逻辑结构; (2)在对数据进行处理时,各数据元素在计算机中的存储关系,即数据的存储结构; (3)对各种数据结构进行的运算。 数据结构是指相互有关联的数据元素的集合。 数据的逻辑结构包含: (1)表示数据元素的信息; (2)表示各数据元素之间的前后件关系。 数据的存储结构有顺序、链接、索引等。 线性结构条件: (1)有且只有一个根结点; (2)每一个结点最多有一个前件,也最多有一个后件。 非线性结构:不满足线性结构条件的数据结构。
五、数据的逻辑结构
数据的逻辑结构有以下两大类: 线性结构:有且仅有一个开始结点和一个终端结点,且所有结点都最多只有一个直接前驱和一个直接后继。 线性表是一个典型的线性结构。栈、队列、串、数组等都是线性结构。 非线性结构:在该类结构中至少存在一个数据元素,它具有两个或者两个以上的前驱或后继。 如树和二叉树集合结构和多维数组、广义表、图、堆等数据结构都是非线性结构。
六、基本的数据结构
集合结构:数据元素的有限集合。数据元素之间除了“属于同一个集合”的关系之外没有其他关系。 线性结构:数据元素的有序集合。数据元素之间形成一对一的关系。 树型结构:树是层次数据结构,树中数据元素之间存在一对多的关系。 图状结构:图中数据元素之间的关系是多对多的。
七、数据的存储结构
数据的存储结构可采用顺序存储或链式存储的方法。 顺序存储方法是把逻辑上相邻的元素存储在物理位置相邻的存储单元中,由此得到的存储表示称为顺序存储结构。顺序存储结构是一种最基本的存储表示方法,通常借助于程序设计语言中的数组来实现。 链式存储方法对逻辑上相邻的元素不要求其物理位置相邻,元素间的逻辑关系通过附设的指针字段来表示,由此得到的存储表示称为链式存储结构,链式存储结构通常借助于程序设计语言中的指针类型来实现。 除了通常采用的顺序存储方法和链式存储方法外,有时为了查找的方便还采用索引存储方法和散列存储方法。
八、算法
算法:是指解题方案的准确而完整的描述。 算法不等于程序,也不等计算机方法,程序的编制不可能优于算法的设计。 算法的基本特征:是一组严谨地定义运算顺序的规则,每一个规则都是有效的,是明确的,此顺序将在有限的次数下终止。特征包括: (1)可行性; (2)确定性,算法中每一步骤都必须有明确定义,不充许有模棱两可的解释,不允许有多义性; (3)有穷性,算法必须能在有限的时间内做完,即能在执行有限个步骤后终止,包括合理的执行时间的含义; (4)拥有足够的情报。 算法的基本要素:一是对数据对象的运算和操作;二是算法的控制结构。 指令系统:一个计算机系统能执行的所有指令的集合。 基本运算和操作包括:算术运算、逻辑运算、关系运算、数据传输。 算法的控制结构:顺序结构、选择结构、循环结构。 算法基本设计方法:列举法、归纳法、递推、递归、减斗递推技术、回溯法。 算法复杂度:算法时间复杂度和算法空间复杂度。 算法时间复杂度是指执行算法所需要的计算工作量。 算法空间复杂度是指执行这个算法所需要的内存空间。
时间复杂度
定义:设问题的规模为n,把一个算法的时间耗费T(n)称为该算法的时间复杂度,它是问题规模为n的函数。
常用的算法的时间复杂度的顺序:(比较时只看最高次幂)
for ( i = 1 , i < = 10 , i++ ) x=x+c; =>O(1) for ( i = 1 , i < = n , i++ ) x=x+n; =>O(n) 多嵌套一个for,则为 =>O(n^2) 以此类推 真题难点:i = 1,while(i < = n) i = i * 3;=>O(log3^n) i = i * 2;=>O(log2^n) 以此类推
九、线性表
线性表由一组数据元素构成,数据元素的位置只取决于自己的序号,元素之间的相对位置是线性的。 在复杂线性表中,由若干项数据元素组成的数据元素称为记录,而由多个记录构成的线性表又称为文件。 非空线性表的结构特征: (1)且只有一个根结点a1,它无前件; (2)有且只有一个终端结点an,它无后件; (3)除根结点与终端结点外,其他所有结点有且只有一个前件,也有且只有一个后件。结点个数n称为线性表的长度,当n=0时,称为空表。 线性表的顺序存储结构具有以下两个基本特点: (1)线性表中所有元素的所占的存储空间是连续的; (2)线性表中各数据元素在存储空间中是按逻辑顺序依次存放的。 ai的存储地址为:adr(ai)=adr(a1)+(i-1)k,,adr(a1)为第一个元素的地址,k代表每个元素占的字节数。 顺序表的运算:插入、删除。
十、线性链表
数据结构中的每一个结点对应于一个存储单元,这种存储单元称为存储结点,简称结点。 结点由两部分组成:(1)用于存储数据元素值,称为数据域;(2)用于存放指针,称为指针域,用于指向前一个或后一个结点。 在链式存储结构中,存储数据结构的存储空间可以不连续,各数据结点的存储顺序与数据元素之间的逻辑关系可以不一致,而数据元素之间的逻辑关系是由指针域来确定的。 链式存储方式即可用于表示线性结构,也可用于表示非线性结构。 线性链表,head称为头指针,head=null(或0)称为空表,如果是两指针:左指针(llink)指向前件结点,右指针(rlink)指向后件结点。 线性链表的基本运算:查找、插入、删除。
单链表
指针域中存储的信息称做指针或链。N个结点链结成一个链表,由于此链表的每一个结点中包含一个指针域,故又称线性链表或单链表。
循环链表
循环链表是单链表的变形
循环链表最后一个结点的next指针不为空,而是指向了表的前端。为简化操作,在循环链表中往往加入表头结点。
循环链表的特点是:只要知道表中某一结点的地址,就可搜寻到所有其他结点的地址。
双向链表
双向链表是指在前驱和后继方向都能游历(遍历)的线性链表。 在双向链表结构中,每一个结点除了数据域外,还包括两个指针域,一个指针指向该结点的后继结点,另一个指针指向它的前趋结点。通常采用带表头结点的循环链表形式。
用指针实现表
用数组实现表时,利用了数组单元在物理位置上的邻接关系表示表元素之间的逻辑关系。 优点是: 无须为表示表元素之间的逻辑关系增加额外的存储空间。 可以方便地随机存取表中任一位置的元素。 缺点是: 插入和删除运算不方便,除表尾位置外,在表的其他位置上进行插入或删除操作都须移动大量元素,效率较低。 由于数组要求占用连续的存储空间,因此在分配数组空间时,只能预先估计表的大小再进行存储分配。当表长变化较大时,难以确定数组的合适的大小
顺序表与链表的比较
顺序表的存储空间可以是静态分配的,也可以是动态分配的。链表的存储空间是动态分配的。顺序表可以随机或顺序存取。链表只能顺序存取。顺序表进行插入/删除操作平均需要移动近一半元素。链表则修改指针不需要移动元素。若插入/删除仅发生在表的两端,宜采用带尾指针的循环链表。存储密度=结点数据本身所占的存储量/结点结构所占的存储总量。顺序表的存储密度= 1,链表的存储密度< 1。总结:顺序表是用数组实现的,链表是用指针实现的。用指针来实现的链表,结点空间是动态分配的,链表又按链接形式的不同,区分为单链表、双链表和循环链表。
十一、栈和队列
栈是限定在一端进行插入与删除的线性表,允许插入与删除的一端称为栈顶,不允许插入与删除的另一端称为栈底。 栈按照“先进后出”(filo)或“后进先出”(lifo)组织数据,栈具有记忆作用。用top表示栈顶位置,用bottom表示栈底。 栈的基本运算:(1)插入元素称为入栈运算;(2)删除元素称为退栈运算;(3)读栈顶元素是将栈顶元素赋给一个指定的变量,此时指针无变化。 队列是指允许在一端(队尾)进入插入,而在另一端(队头)进行删除的线性表。rear指针指向队尾,front指针指向队头。 队列是“先进行出”(fifo)或“后进后出”(lilo)的线性表。 队列运算包括(1)入队运算:从队尾插入一个元素;(2)退队运算:从队头删除一个元素。 循环队列:s=0表示队列空,s=1且front=rear表示队列满
十二、栈
是限定仅在表尾进行插入或删除操作的线性表。栈是一种后进先出(Last In First Out)/先进后出的线性表,简称为LIFO
用指针实现栈—链(式)栈链式栈
无栈满问题,空间可扩充 插入与删除仅在栈顶处执行 链式栈的栈顶在链头 适合于多栈操作
链栈的基本操作
1)进栈运算
进栈算法思想:
1)为待进栈元素x申请一个新结点,并把x赋给 该结点的值域。
2)将x结点的指针域指向栈顶结点。
3)栈顶指针指向x结点,即使x结点成为新的栈顶结点。
具体算法如下:
SNode *Push_L(SNode * top,ElemType x)
{
SNode *p;
p=(SNode*)malloc(sizeof(SNode));
p->data=x;
p->next=top;
top=p;
return top;
}
2)出栈运算
出栈算法思想如下:
1)检查栈是否为空,若为空,进行错误处理。
2)取栈顶指针的值,并将栈顶指针暂存。
3)删除栈顶结点。
SNode *POP_L(SNode * top,ElemType *y)
{SNode *p;
if(top==NULL) return 0;/*链栈已空*/
else{
p=top;
*y=p->data;
top=p->next; free(p);
return top;
}
3)取栈顶元素
具体算法如下:
void gettop(SNode *top)
{
if(top!=NULL)
return(top->data); /*若栈非空,则返回栈顶元素*/
else
return(NULL); /*否则,则返回NULL*/
}
十三、队列(Queue)
是只允许在表的一端进行插入,而在另一端进行删除的运算受限的线性表。其所有的插入均限定在表的一端进行,该端称为队尾(Rear);所有的删除则限定在表的另一端进行,该端则称为队头(Front)。如果元素按照a1,a2,a3....an的顺序进入队列,则出队列的顺序不变,也是a1,a2,a3....an。所以队列具有先进先出(First In First Out,简称FIFO)/后进后出特性。如车站排队买票,排在队头的处理完走掉,后来的则必须排在队尾等待。在程序设计中,比较典型的例子就是操作系统的作业排队。队列的顺序存储结构称为顺序队列,顺序队列实际上是运算受限的顺序表,和顺序表一样,顺序队列也是必须用一个数组来存放当前队列中的元素。由于队列的队头和队尾的位置是变化的,因而要设两个指针分别指示队头和队尾元素在队列中的位置。循环队列是为了克服顺序队列中“假溢出”,通常将一维数组Sq.elem[0]到Sq.elem.[MaxSize-1]看成是一个首尾相接的圆环,即Sq.elem[0]与Sq.elem .[maxsize-1]相接在一起。这种形式的顺序队列称为循环队列。用线性链表表示的队列称为链队列。链表的第一个节点存放队列的队首结点,链表的最后一个节点存放队列的队尾首结点,队尾结点的链接指针为空。另外还需要两个指针(头指针和尾指针)才能唯一确定,头指针指向队首结点,尾指针指向队尾结点
十四、 树与二叉树
树是一种简单的非线性结构,所有元素之间具有明显的层次特性。 在树结构中,每一个结点只有一个前件,称为父结点,没有前件的结点只有一个,称为树的根结点,简称树的根。每一个结点可以有多个后件,称为该结点的子结点。没有后件的结点称为叶子结点。 在树结构中,一个结点所拥有的后件的个数称为该结点的度,所有结点中最大的度称为树的度。树的最大层次称为树的深度。 二叉树的特点:(1)非空二叉树只有一个根结点;(2)每一个结点最多有两棵子树,且分别称为该结点的左子树与右子树。 二叉树的基本性质: (1)在二叉树的第k层上,最多有2k-1(k≥1)个结点; (2)深度为m的二叉树最多有2m-1个结点; (3)度为0的结点(即叶子结点)总是比度为2的结点多一个; (4)具有n个结点的二叉树,其深度至少为[log2n]+1,其中[log2n]表示取log2n的整数部分; (5)具有n个结点的完全二叉树的深度为[log2n]+1; (6)设完全二叉树共有n个结点。如果从根结点开始,按层序(每一层从左到右)用自然数1,2,….n给结点进行编号(k=1,2….n),有以下结论: ①若k=1,则该结点为根结点,它没有父结点;若k>1,则该结点的父结点编号为int(k/2); ②若2k≤n,则编号为k的结点的左子结点编号为2k;否则该结点无左子结点(也无右子结点); ③若2k+1≤n,则编号为k的结点的右子结点编号为2k+1;否则该结点无右子结点。 满二叉树是指除最后一层外,每一层上的所有结点有两个子结点,则k层上有2k-1个结点深度为m的满二叉树有2m-1个结点。 完全二叉树是指除最后一层外,每一层上的结点数均达到最大值,在最后一层上只缺少右边的若干结点。 二叉树存储结构采用链式存储结构,对于满二叉树与完全二叉树可以按层序进行顺序存储。 二叉树的遍历: (1)前序遍历(dlr),首先访问根结点,然后遍历左子树,最后遍历右子树; (2)中序遍历(ldr),首先遍历左子树,然后访问根结点,最后遍历右子树; (3)后序遍历(lrd)首先遍历左子树,然后访问遍历右子树,最后访问根结点。
十五、树
①结点的度:结点拥有子节点的个数 ②树的度:该树中最大的度数 ③叶子结点:度为零的结点 ④分支结点:度不为零的结点 ⑤内部结点:除根结点之外的分支结点 ⑥开始结点:根结点又称为开始结点 结点的高度:该结点到各结点的最长路径的长度 森林:m(m≥0)棵互不相交的树的集合。将一棵非空树的根结点删去,树就变成一个森林; 反之,给m棵独立的树增加一个根结点,并把这m棵树作为该结点的子树,森林就变成一棵树。 2.结点的层数和树的深度 ①结点的层数:根结点的层数为1,其余结点的层数等于其双亲结点的层数加1。 ②堂兄弟:双亲在同一层的结点互为堂兄弟。 ③树的深度:树中结点的最大层数称为树的深度。 注意:要弄清结点的度、树的度和树的深度的区别。 树中结点之间的逻辑关系是“一对多”的关系,树是一种非线性的结构 树的遍历 先序遍历:访问根结点——先序遍历根的左子树——先序遍历根的右子数 中序遍历:中序遍历左子树——访问根结点——中序遍历右子树 后序遍历:后序遍历左子树——后序遍历右子树——访问根结点 最优二叉树(哈夫曼树):最小两结点数相加的值再与次小结点数合并。 已知一棵二叉树的前根序序列和中根序序列,构造该二叉树的过程如下: 1. 根据前根序序列的第一个元素建立根结点; 2. 在中根序序列中找到该元素,确定根结点的左右子树的中根序序列; 3. 在前根序序列中确定左右子树的前根序序列; 4. 由左子树的前根序序列和中根序序列建立左子树; 5. 由右子树的前根序序列和中根序序列建立右子树。 -已知一棵二叉树的后根序序列和中根序序列,构造该二叉树的过程如下: 1. 根据后根序序列的最后一个元素建立根结点; 2. 在中根序序列中找到该元素,确定根结点的左右子树的中根序序列; 3. 在后根序序列中确定左右子树的后根序序列; 4. 由左子树的后根序序列和中根序序列建立左子树; 5. 由右子树的后根序序列和中根序序列建立右子树。
十六、图
G= ( V , E ) = ( 顶点,边)
无向完全图有n(n - 1)/ 2 个边 ,有向完全图有n(n - 1)个边 。n表结点。
边无向(),弧有向<>
迪杰斯特拉(Dijkstra)算法
是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。
弗洛伊德(Floyd)算法<邻接矩阵求>
是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
普里姆(Prim)算法
普里姆算法的基本思想:
从连通网络N= {V,E}中的某一顶点u0出发,选择与它关联的具有最小权值的边(u0,v),将其顶点加入到生成树顶点集合S中。以后每一步从一个顶点在S中而另一个顶点不在S中的各条边中选择权值最小的边(u,v),把它的顶点加入到集合S中。如此继续下去,直到网络中的所有顶点都加入到生成树顶点集合S中为止。
克鲁斯卡尔(Kruskal)算法
克鲁斯卡尔算法的基本思想:
设有一个有n个顶点的连通网络N= {V,E},最初先构造一个只有n个顶点,没有边的非连通图T= {V,∅},图中每个顶点自成一个连通分支。当在E中选到一条具有最小权值的边时,若该边的两个顶点落在不同的连通分支上,则将此边加入到T中;否则将此边舍去,重新选择一条权值最小的边。如此重复下去,直到所有顶点在同一个连通分支上为止。
十七、查找计数
顺序查找的使用情况: (1)线性表为无序表; (2)表采用链式存储结构。 二分法查找只适用于顺序存储的有序表,对于长度为n的有序线性表,最坏情况只需比较log2n次。
十八、排序计数
排序是指将一个无序序列整理成按值非递减顺序排列的有序序列。
交换类排序法:(1)冒泡排序法,需要比较的次数为n(n-1)/2; (2)快速排序法。
插入类排序法:(1)简单插入排序法,最坏情况需要n(n-1)/2次比较;(2)希尔排序法,最坏情况需要o(n1.5)次比较。
选择类排序法:(1)简单选择排序法,
最坏情况需要n(n-1)/2次比较;(2)堆排序法,最坏情况需要o(nlog2n)次比较。
排序小结
1、就平均时间性能而言,快速排序最佳。但在最坏情况下不如堆排序和归并排序。(归并排序对n较大时适用) 2、当序列中的记录“基本有序”或n值较小时,直接插入排序是最佳的方法,因此常将它与其他排序方法结合使用,如快速排序、归并排序等。 3、基数排序的时间复杂度也可写成O(d*n),因此它最适用于n值很大而关键字较小的序列。 4、稳定的排序方法:简单排序。不稳定的排序方法:快速排序、堆排序。 一般来说,排序过程中的“比较”是在相邻的两个记录的关键字之间进行的排序方法是稳定的。
参考:
http://c.biancheng.net/cpp/u/shuju/
http://www.cnhonkerarmy.com/thread-94743-1-7.html
https://www.jianshu.com/p/730be599b6b7
原文地址:https://www.cnblogs.com/-wenli/p/11072445.html