算法基础篇之数据结构基础

数据结构基础

前言:数据结构是算法的基础,没有数据结构就没有算法。本文开始阐述常见的数据结构,以及各个的特性。由于代码实现过于简单,jdk中大部分都能找到相应代码,另外加了代码这篇文章就太长了,后续考虑用添加附件的方式,贴上代码。另外就是特殊数据结构,例如二叉树的特殊实例,AVL树,红黑树,B树、SB树等等,不在本文阐述详谈,后续整理相关流程图代码后,以单独文章体现。
-------------------------------------------------------------------------------正文开始----------------------------------------------------------------------------------------

一、什么是数据结构

数据结构是数据对象、存在该对象的实例以及组成实例数据元素之间的各种关系,并且这种关系可以通过定义相关的函数来给出。
数据(Data):数据是信息的载体,能够被计算机识别。存储和加工处理。
数据元素(Data Element):数据元素的数据的基本单位,也称之为元素、节点、顶点、记录等。
数据结构(Data Structure): 数据之间的相互关系,即数据的组织形式。

二、数据结构的组成:

逻辑结构(Logical Structure):数据元素之间的逻辑关系
存储结构(Storage Structure):数据元素及其逻辑关系在计算机中存储的表现形式
数据运算:能够对数据施加的操作,常用的有:CRUD和排序
这三个方面是一个有机的整体,缺一不可。任何一个发生变化都将导致一个全新的数据结构出现。

三、数据结构的分类

线性结构按照逻辑结构进行简单分类的话分为线性结构和非线性结构。
线性结构:数据表中各个节点具有线性关系。具体包含内容如下:
1、数据结构是非空集
2、有且只有一个开始节点和一个终端节点
3、所有节点做多只有一个直接前驱节点和一个直接后续节点。
一维数组、线性表、栈、队列和串都是线性结构。

非线性结构:简单的说就是各个节点之间具有多个对应关系。具体包含内容如下:
1、非线性结构是非空集
2、一个节点可能有多个直接前驱结点和直接后继节点。
二维数组、多维数组、广义表、树、图都是非线性结构的。

四、数据结构的存储方式

顺序存储方式:在一块连续的内存存储区域一个接着一个的存放数据,一般采用数组或者结构数组来描述
连接存储方式:也称之为“链式存储结构”,该方式比较灵活,一般在元数据项中增加引用类型来表示节点间的位置关系
索引存储方式
采用附件索引表的方式来存储节点信息,关键字+地址。关键字理解为唯一标识一个节点的数据项。
细分类为稠密索引和稀疏索引。
稠密索引:就是每个节点都在索引表中有一个索引项,索引的引用地址就是节点的存储位置。
稀疏索引:就是一组节点在索引表中只对应了一个索引项,索引项的地址表示的是一组节点的起始存储位置
散列存储方式:就是根据节点关键字直接计算出该阶段的存储地址。例如hash表

五、常用的数据结构

数组(Array):一种聚合数据类型,是将具有相同数据类型的若干数量的数据有序的组织在一起的有限集合,是最基本的数据结构。
栈(Stack)
一种特殊的线性表,只能在一个表的固定端进行数据节点的插入和删除操作。按照后进先出原则存储数据。栈中没有数据时,称之为空栈。
队列(Queue)
队列和栈类似,也是一种特殊的线性表。和栈不同的是,只允许在表的一端进行插入操作,另一端进行删除操作。先进先出原则存储数据
链表(Linked List):按照链式存储结构进行存储的数据结构,有非连续的特点。每个节点包括数据域和引用域。
树(Tree:典型的非线性结构,包括n个节点的有穷集合。在树结构中有且只有一个根节点,并且其他节点有仅有一个前驱结点,但是可以有n个后续节点的数据结构。
图(Graph):也是非线性结构。数据节点称之为顶点,而边是顶点的有序偶对。
堆(Heap):是一种特殊的树形结构,一般都是二叉堆。特点是根节点的值是所有节点中最小的或者最大的,并且根节点的两个子树也是堆结构。
散列表(Hash):源自于散列函数(Hash Function),思想是如果在结构中存在关键字和T相等的记录,必定在F(T)的存储位置上找到该记录,这样可以不用比较直接取数据。

六、下面具体阐述各个数据结构

(1)线性表

  线性表是由n(n≥0)个数据组成的有限序列。元素的个数称之为表的长度。即“把所有数据用一根线儿串起来,再存储到物理空间中”
逻辑特征:
  1、有且只有一个开始节点,并且没有这个节点没有前驱节点。有且只有一个后续节点。
  2、有且只有一个终结节点,没有直接的后续节点。有且只有一个直接前驱节点。
  3、同一线性表,元素数据类型必须一致。每个元素的数据长度必须相同。
基本运算:
  1、初始化:构建一个空的线性表L
  2、计算表长:计算线性表L中节点的个数
  3、获取节点:取出线性表L中第i个节点的数据
  4、插入节点:在线性表第i的位置插入一个新的节点,后续节点后移,长度加1。
  5、删除节点:删除线性表L中的第i位置的节点。后续节点前移,长度减1。
  线性表的存储方式:集中存放和分散存放。也就是下面要说的顺序表和链表 。结构如图:


(2)顺序表

 按照顺序存储数据方式的线性表,所有的节点按照逻辑次序依次存在计算机的一组连续的存储单元。结构如图:

 由于顺序表是依次存放的,只要知道首地址机每个数据元素的所占用的存储长度,很容易就计算出任何数据元素的存储位置。
 每个节点用c个存储单元,开始节点a1,并设a1的存储地址为LOC(a1),那么ai的存储地址为LOC(ai),公式为:LOC(ai)=LOC(a1)+(i-1)*C

顺序表还可以再细分类
 1、静态顺序表:使用固定长度的数组存储
 2、动态顺序表:使用动态开辟的数组存储

适用场景适用于需要大量访问元素的,而少量增添/删除元素的程序

顺序表的相关操作
 插入:1、先将后面的数据后移 2、再将后续节点后移 ,长度加1
 追加:只是一种特殊的插入,直接在最后插入长度加1即可,不必大量的移动数据
 删除: 1、先删除当前元素节点 2、再将后续节点前移, 长度减1
 查找:顺序表的查找分为按照下标查找和关键字匹配,都是借助循环完成。

顺序表优点
 1、按照索引查询元素速度快
 2、按照索引遍历方便
顺序表缺点
 1、插入和删除节点时,需要移动大量的数据。
 2、表比较大,比较难分配足够的连续存储空间存储数据,往往导致内存分配失败。
 3、为了克服以上缺点可以采用链表结构

(3)链表结构

 1、链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,
 2、每个结点包含两个内容,数据部分,保存的是实际数据。另一个是地址部分,保存的是下一个结点地址。
 3、链表结构是一种动态存储分配的结构形式,可以根据需要动态的申请内存单元。

首先有个头引用head表示,指向第一个节点。尾部节点的引用为null,表示链表到此结束。典型链表的数据结构如图:

 链表最大的好处是节点之间不要求连续存放,因此在保存大龄数据时,不需要分配一块连续的空间。使用new函数动态分配节点的存储空间。删除时候赋值为null,释放内存空间。
 当然这也是链表的缺点,每个节点都要存放一个引用变量存储下个节点的位置,但是在某些场合,利大于弊。 链表不能想顺序表那样随机访问,只能从head依次寻找下一个节点,知道找到为止。

链表还可以细分为如下几个分类
 1、单向链表:同上面的结构一样,每个节点中只包含一个引用。
 2、双向链表:每个节点都包含两个引用,一个指向上一个引用,另一个指向下一个引用。
 3、单向/双向循环链表:在单向链表中,终端节点的引用域null改为指向表头节点或者开始节点。
 4、多重链的循环链表:将表中的节点链在多个环上。
 具体结构如图:

链表的优点
 1.任意位置插入,删除效率高
 2.空间利用率高(用就申请不用不申请
链表的缺点
 1.空间不连续,容易造成内存碎片
 2.不能像顺序表那样,根据下标随机访问
适用场景:适用于需要进行大量增添/删除元素操作 而对访问元素无要求的程序

链表的基本操作
追加节点
 1、分配内存空间保存新的节点。
 2、从head开始逐个检查直到找到最后一个节点。
 3、将表尾节点的地址部分设置为新增节点的地址
 4、新增节点嫡长子设置为空地址null
过程如图:

插入头结点
 1、分配内存空间,保存新的结点。
 2、将新增节点指向头引用head指向的节点。
 3、将头引用head执行新的节点
过程如图:

查找节点:
 只能通过关键字查找,从头结点开始,对之后的结点逐一比较,直到找到为止。返回该结点的引用,方便程序后续处理。
插入节点
 1、分配内存空间,保存新增的结点。
 2、找到插入的逻辑位置,也就是位于哪两个节点之间。
 3、修改插入位置结点的引用,使其指向新增节点,之后将新增节点指向原插入位置结点所指向的结点
 过程如图:

删除节点
 1、查找需要删除的结点,从head头开始查找,找到为止。
 2、将前一结点指向当结点的下一结点。
 3、删除结点
 过程如图:node上一结点的引用,h引用下一结点。删除结点后将被删除的指向设置为null

链表的长度
 链表结构的物理上不是连续存储,因此计算链表长度需要遍历整个链表的对结点数量进行累加操作。
链表的优点
1、链表是很常用的一种数据结构,不需要初始化容量,可以任意加减元素;
2、 添加或者删除元素时只需要改变前后两个元素结点的指针域指向地址即可,所以添加,删除很快;
链表的缺点
1、 因为含有大量的指针域,占用空间较大。
2、查找元素需要遍历链表来查找,非常耗时。

(4)栈结构

 1、栈是一种特殊的数据结构,在中断处理特别是重要数据的现场保护有这重要意义。
 2、栈结构是从数据的运算来划分的,也就是说栈具有特殊的运算规则。
 3、栈结构是一种线性结构。
 4、在栈结构中只能在一端进行操作,该操作端称为栈顶,另一端称之为栈底。
 5、栈结构是按照“先进后出,后进先出”的原则处理结点数据的。
 6、生活中的例子:仓库的货堆,先来的只能放在里面/下面,后来的货物只能放在外面/上面。取货是总是先取外面/上面的,才能取出里面/下面的货物。

数据结构如图:

细分类:
 顺序栈结构和链式栈结构
顺序栈结构:使用一组地址连续的内存单元依次保存栈中的数据。序号0的元素就是栈底,最后一个元素称之为栈顶。
链式站结构:使用链表形式保存栈中个元素的值。head所指向的元素就是栈顶,链表尾部就是栈底。
 动态栈和静态栈
静态栈:使用固定长度的线性表
动态栈:使用动态长度的线性表

栈的优缺点
 优点:提供后进先出(LIFO)的存取方式,添加速度快
 缺点:只能在一头操作,存取其他项很慢
栈的基本操作
 因为只有栈顶元素是可以访问的,所以栈的基本操作只有两个。
入栈(Push)
 1、首先判断栈顶top,如果top大于总长度,则表示栈溢出,进行出错处理;否则执行以下操作。
 2、设置top=top+1,栈顶引用加1,指向入栈地址。
 3、将入栈结点保存到top指向的位置
 流程图:

出栈 (Pop):
 1、判断栈顶top,如果top=0,则表示空栈,进行出错处理。否则执行下面步骤
 2、将栈顶引用top所指向位置的元素返回
 3、设置栈顶引用减1,指向栈的下一个元素,原来栈顶元素被弹出。
 流程图:


(5)队列结构

 1、队列结构和栈结构类似,也是按照运算来分类的,具有特殊的运算规则。
 2、在队列结构中允许在两端进行操作,两端的操作不同。一端只能进行删除操作,称之为“队头”。另一端进行新增操作称之为“队尾”。
 3、队列里没有元素称之为“空队列”。
 4、队列结构是按照“先进先出,后进后出”的原则处理结点数据的。
 5、生活的例子:银行排号系统和医院的挂号系统

细分类
 顺序队列结构:使用一组地址连续的内存单元依次保存队列中的数据
 链式队列结构:使用链表形式保存队列中个元素的值
 静态队列:使用固定长度的线性表
 动态队列:使用动态长度的线性表
队列的优缺点
 优点:提供先进先出的存储方式,添加速度快,允许重复
 缺点:只能在一头添加,另一头获取,存取其他项很慢
队列的基本操作
入队列:将一个元素添加到队尾
 1、首先判断队尾tail,如果等于最大长度,则表示溢出。进行出错处理,否则执行如下操作
 2、设置队列tail+1(队列顶引用加1,指向入队队列地址)
 3、将队列元素保存到tail指向的位置
出队列:将队头结点取出,同时删除该元素,使后一个元素成为队头。
 1、首先判断队头head,如果等于tail,则表示空队列。进行出错处理,否则执行如下操作
 2、取出队列首结点(实际返回的是队头元素的引用)
 3、设置队头head的序号,使其指向后一个元素

(6)树结构

  树结构是一种描述非线性层次关系的数据结构,重要的是树的概念。
  树是n个数据结点的集合,在该集合中包含一个根节点,根节点之下分布着一些互不交叉的子集合,这些子集合是根结点的子树。
  生活中的树结构:家族族谱

结构特征
1、有且只有一个节点没有直接前驱结点,这个结点就是树的根结点。
2、除了根结点以外,其余的每个结点有且只有一个直接前驱结点
3、每个结点可以有任意多个直接后继结点
结构如图:

树的相关概念
 1、父结点和子结点:每个结点子树的根称为该结点的子结点,相应的该结点称为其子结点的父节点
 2、兄弟结点:具有同一父结点的结点称为兄弟结点
 3、结点的度:一个结点所包含子树的数量
 4、树的度:树中所有结点中最大的度
 5、叶节点:树中的度为零的结点称为叶结点或者终端结点
 6、分支结点:树中的度不为零的结点称为分支结点或非终端结点
 7、结点的层数:结点的层数从树的根结点开始计算,根结点为第1层、依次向下为第2层、等3层等等。
 8、树的深度:树中结点的最大层数称为树的深度
 9、有序树和无序树:树中各结点的子树(兄弟结点)按照一定从左到右排列的,称为有序树,反之称为无序树
 10、森林:n(n>0)棵互不相交树的集合

树结构一般采用括号法表示
 1、根结点放入一对圆括号中;
 2、根结点的子树由左到右的顺序放入括号中;
 3、对子树左上述相同的操作
 上图中则可以表示为:(A(B(E,F)),(C(G,H)),(D(I)))

树结构的分类
 树分支的数量限制,可以将树结构分为两类
二叉树:二叉树也就是一个节点最多只有两个子节点的树结构。例如 红黑树
多叉树:多叉树一个节点可以有多于两个的子节点。例如 B树
 树节点的有序性
查找树:也可以叫搜索树,基本特征为任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值
无序树:节点的键值无特定大小关系
常见的树形结构:AVL树、红黑树,B树、B+树、SB树、二叉查找树、二叉搜索树、四叉数、八叉树、KD树、哈夫曼树。
-----------------------------------------------本文只对典型二叉树进行学习。其他树结构后续更新-------------------------------------------------------------

二叉树
 二叉树是树结构中最简单的一种形式。二叉树是树形结构的重点,任意的树都可以转换成对应的二叉树,因此二叉树是所有树结构的基础。
 二叉树是一种特殊的树形结构,它是n个结点的集合,并且每个结点最多只能有两个子结点,即二叉树的子树还是二叉树。
 二叉树的一个结点上对应的两个子树分别为左子树和右子树,有左右之分,因此二叉树是有序树。

二叉树可以分为两种特殊的形式:满二叉树和完全二叉树
满二叉树:在树结构中,除了最下一层的叶结点之外,每个结点都有两个子结点。
完全二叉树:在树结构中,除了最下一层的叶结点之外,其他各层的结点树都达到了最大数量。且最后一层叶结点按照从左到右的顺序连续存在,只缺最后一层右侧若干结点。
具体如图:


二者的关系:满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树。

完全二叉树的性质
树中包含n个结点,对于任意的一个结点m来说,具有如下性质:
 1、如果m != 1,则结点m的父结点的编号为 m/2;
 2、如果2m ≤ n,则结点m的左子树根结点的编号为2m;若2m>n,则没有左结点,也没有右结点
 3、如果2
m+1 ≤ n,则结点m的右子树根结点编号为 2m+1;2m+1 > n,则没有右子树。

二叉树的存储方式
 顺序存储:顺序存储方式是最基本的数据存储方式。与线性表类似,一般采用一维结构数组来表示
 完全二叉树的顺序存储,如下图所示。

左侧是一个典型的完全二叉树,每个结点存储字符类型。按顺序存储的话,则先存储根结点,然后陆续存储下一层的结点数据,直到所有的结点数据完全存储。
关系:根据上面可以推算出各个节点之间的位置关系:
 节点D位于数组的第四个位置,父结点的编号为4/2,结点B
 D的左子结点为24=8,结点H
 D的右子结点为2
4+1=9,结点I

非完全二叉树的顺序存储,如图所示

为了满足完全二叉树的性质,需要将一个非完全二叉树填充成一个完全二叉树。将缺少的部分填上空的数据结点来构成完全二叉树。
但是这种方式有很大的缺点,大量浪费存储空间。所以得出结论,顺序存储一般只适用完全二叉树的情况,对于其他情况,一般采用链式存储方式。

链式存储
 与线性结构的链式存储类似,二叉树的链式存储结构包含结点元素及分别指向左子树和右子树的引用。如图:

图结构

 树结构每一层的元素可以和多个下层元素关联,但是只能和一个上层元素关联。在树形结构基础上的规则进一步拓展,每个元素之间可以任意关联,这就构成了一个图结构。如图:

基本概念
顶点(Vertex):图中的数据元素结点
边(Edge):图中连接这些顶点的线
图结构在数学上一般记作:G=(V,E) 或者 G(V(G), E(G)),
 V(G)表示的图结构中所有顶点的集合,顶点可以用不同的数字或者字母来表示。
 E(G)表示的图结构中所有边的集合,每条边由所连接的两个顶点表示。
特别注意的是:V(G)必须是非空,E(G)可以为空。

无向图:图结构中的所有边都没有方向性,称之为无向图。上图就是典型无向图表示:
 V(G)={VA,VB,VC,VD,VF}。 VA表示A节点
 E(G)={(VA,VB),(VA,VE),(VB,VD),(VC,VE),(VD,VE),(VA,VC)}。 (VA,VB)表示A结点连接B结点的边

有向图:图结构中的所有边都具有方向性,称之为有向图

 表示:采用尖括号表示边的方向性。
 V(G)={VA,VB,VC,VD,VF}。 VA表示A节点
 E(G)={<VA,VB>,<VA,VE>,<VB,VD>,<VC,VE>,<VD,VE>,<VA,VC>}。 <VA,VB>表示A结点连接B结点的边

顶点的度(Degree):连接顶点边的数量称为该顶点的度
 表示:区分有向图和无向图
 无向图:D(V), 例如上无向图中,VA的度为3,表示为D(A)=3
 有向图: 有向图分为入度和出度之分
入度:表是以该顶点的入边数量,记为ID(V)。例如:有向图A顶点表示为ID(A)=1
出度:表是以该顶点的出边数量,记为OD(V)。例如:有向图A顶点表示为OD(A)=1

在有向图中,顶点的总度为入度和出度之和,即D(V)=ID(V)+OD(V)
邻接顶点:图结构中一条边的两个顶点。
无/有向完全图:在图结构中,每两个顶点之间都存在一条边,有向的称之为有向完全图,无向的称之为无向完全图。

 对于无向完全图:顶点N和边数的关系,总边数=N(N-1)/2
 对于无向完全图:顶点N和边数的关系,总边数=N(N-1)

子图
 子图的概念类似于子集合,由于一个完整的图结构包括顶点和边。因此,一个子图的顶点和边都应该是图结构的子集合。具体如图:
 特别注意的是:如果只有顶点集合是子集的,或者只有边集合是子集的,都不是子图

路径
 路径就是图结构中两个顶点之间的连线,路径上边的数量称之为路径长度。两个顶点之间的路径可能途径多个其他顶点,两个顶点之间的路径也可能不止一条,相应的路径长度也会不一样。
 比如顶点A到顶点C之间存在两条路径 (VA,VB)、(VB,VC) 或者 (VA,VE)、(VE,VC)

简单路径:在图结构中,如果一条路径上的顶点不重复出现,则称之为简单路径。
:在图结构中,如果第一个顶点和最后一个顶点相同,则称之为环或者回路
简单回路:在图结构中,如果除第一个顶点和最后一个顶点相同,其余各顶点都不重复的回路简单回路,或者简单环路
连通:图中两个顶点之间存在路径,则称之为两个顶点是连通的。两个顶点之间可以不是邻接顶点,只要有路径连接即可。
连通图:在无向图中,任意两个顶点都是全通的,那么称之为连通图。只要包含任意两个顶点是不连通的,则称之为非连接图。
连通分量:无向图的最大连通子图称之为该图的连通分量。对于一个连通图,连通分量只有一个,那就是连通图自身。
强连通图
 有向图中,任意两个顶点都是连通的,称为强连通图,反之称之为非强连通图。注意的是,有向图中顶点1到顶点2有向连接,但是顶点2到顶点1之间不一定是连通的。
强连通分量:有向图的最大连通子图称之为该图的强连通分量。对于一个强连通图,连通分量也只有一个,那就是连通图自身。
权(weight):在实际应用中,往往需要将边表示成某种数值,这个数值就是该边的权。区别方向,为有向带权图和无向带权图。实际生活中权就相当于人际关系的亲密度。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值