Day26 Java中的数据结构概念

什么是数据结构

数据结构 (不是建筑结构、人体结构)

数据结构(data structure )是指相互之间存在一种或多种特定关系的数据元素的集合。
是组织并存储数据以便能够有效使用的一种专门格式,它用来反映一个数据的内部构成,即一个数据由那些成分数据构成,以什么方式构成,呈什么结构。
这里写图片描述
这里写图片描述

由于信息可以存在于逻辑思维领域,也可以存在于计算机世界,因此作为信息载体的数据同样存在于两个世界中。
表示一组数据元素及其相互关系的数据结构同样也有两种不同的表现形式,
一种是数据结构的逻辑层面,即数据的逻辑结构;
一种是存在于计算机世界的物理层面,即数据的存储结构。

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

数据结构类型概述

数据的逻辑结构指数据元素之间的逻辑关系(和实现无关)。

分类1:线性结构和非线性结构

线性结构:有且只有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前驱和一个直接后继。
线性表就是一个典型的线性结构,它有四个基本特征:
1.集合中必存在唯一的一个”第一个元素”;
2.集合中必存在唯一的一个”最后的元素”;
3.除最后元素之外,其它数据元素均有唯一的”直接后继”;
4.除第一元素之外,其它数据元素均有唯一的”直接前驱”。
这里写图片描述
相对应于线性结构,非线性结构的逻辑特征是一个结点元素可能对应多个直接前驱和多个直接后继。
常见的非线性结构有:树(二叉树等),图(网等)。
这里写图片描述
树:
生活案例:单位组织架构、族谱
技术案例:文件系统。
这里写图片描述
这里写图片描述

图:
生活案例:交通线路图,地铁图
这里写图片描述
这里写图片描述
分类2:集合结构 线性结构 树状结构 网络结构
逻辑结构有四种基本类型:线性结构、树状结构和网络结构。
表和树是最常用的两种高效数据结构,许多高效的算法能够用这两种数据结构来设计实现。
集合结构:就是数学中所学习的集合。集合中的元素有三个特征:
1).确定性(集合中的元素必须是确定的)
2).唯一性(集合中的元素互不相同。例如:集合A={1,a},则a不能等于1)
3).无序性(集合中的元素没有先后之分),如集合{3,4,5}和{3,5,4}算作同一个集合
该结构的数据元素间的关系是“属于同一个集合”,别无其它关系。
因为集合中元素关系很弱,数据结构中不对该结构进行研究

线性结构:数据结构中线性结构指的是数据元素之间存在着“一对一”的线性关系的数据结构。

树状结构:除了一个数据元素(元素 01)以外每个数据元素有且仅有一个直接前驱元素,但是可以有多个直接后续元素。
特点是数据元素之间是 1 对 多的联系

网络结构:每个数据元素可以有多个直接前驱元素,也可以有多个直接后续元素。特点是数据元素之间是多对 多 的联系
这里写图片描述
数据的存储结构
数据的存储结构主要包括数据元素本身的存储以及数据元素之间关系表示,是数据的逻辑结构在计算机中的表示。
常见的存储结构有顺序存储,链式存储,索引存储,以及散列存储。

顺序存储结构:把逻辑上相邻的节点存储在物理位置上相邻的存储单元中,结点之间的逻辑关系由存储单元的邻接关系来体现。
由此得到的存储结构为顺序存储结构,通常顺序存储结构是借助于计算机程序设计语言(例如C/C++)的数组来描述的。
(数据元素的存储对应于一块连续的存储空间,数据元素之间的前驱和后续关系通过数据元素,在存储器中的相对位置来反映)
这里写图片描述
链式存储结构:数据元素的存储对应的是不连续的存储空间,每个存储节点对应一个需要存储的数据元素。
每个结点是由数据域和指针域组成。 元素之间的逻辑关系通过存储节点之间的链接关系反映出来。
逻辑上相邻的节点物理上不必相邻。
这里写图片描述
索引存储结构:除建立存储结点信息外,还建立附加的索引表来标识结点的地址。
比如图书、字典的目录
这里写图片描述
散列存储结构:根据结点的关键字直接计算出该结点的存储地址 HashSet HashMap
一种神奇的结构,添加、查询速度快。
这里写图片描述
这里写图片描述
这里写图片描述

线性表

逻辑结构

线性表(linear list) )
线性表是n个类型相同数据元素的有限序列,通常记作(a 0 , a 1 , …a i-1 , a i , a i+1 …,a n-1 )。
1.相同数据类型
在线性表的定义中,我们看到从a 0 到a n-1 的n个数据元素是具有相同属性的元素。
比如说可以都是数字,例如(23, 14, 66, 5, 99);
也可以是字符,例如(A, B, C, … Z);
当然也可以是具有更复杂结构的数据元素,例如学生、商品、装备。
相同数据类型意味着在内存中存储时,每个元素会占用相同的内存空间,便于后续的查询定位。
2.序列(顺序性)
在线性表的相邻数据元素之间存在着序偶关系,
即a i-1 是a i 的直接前驱,则a i 是a i-1 的直接后续,
同时a i 又是a i+1 的直接前驱,a i+1 是a i 的直接后续。
唯一没有直接前驱的元素a 0 一端称为表头,
唯一没有后续的元素a n-1 一端称为表尾。
除了表头和表尾元素外,任何一个元素都有且仅有一个直接前驱和直接后继。
3.有限
线性表中数据元素的个数n定义为线性表的长度,n是一个有限值。
当n=0 时线性表为空表。
在非空的线性表中每个数据元素在线性表中都有唯一确定的 序号, 例如a 0 的序号是 0,a i 的序号是i。
在一个具有n > 0 个数据元素的线性表中,数据元素序号的范围是[0, n-1]。
这里写图片描述
这里写图片描述

存储结构

顺序表—-顺序存储结构
这里写图片描述
特点:在内存中分配连续的空间,只存储数据,不需要存储地址信息。位置就隐含着地址。
优点:
1.节省存储空间,因为分配给数据的存储单元全用存放结点的数据(不考虑c/c++语言中数组需指定大小的情况),结点之间的逻辑关系没有占用额外的存储空间。
2.索引查找效率高,即每一个结点对应一个序号,由该序号可以直接计算出来结点的存储地址。
假设线性表的每个数据元素需占用K个存储单元,并以元素所占的第一个存储单元的地址作为数据元素的存储地址。
则线性表中序号为i的数据元素的存储地址LOC(a i )与序号为i+1 的数据元素的存储地址LOC(a i+1 )之间的关系为
LOC(a i+1 ) = LOC(a i ) + K
通常来说,线性表的i号元素a i 的存储地址为
LOC(a i ) = LOC(a 0 ) + i×K
其中LOC(a 0 )为 0 号元素a 0 的存储地址,通常称为线性表的起始地址。

缺点:
1.插入和删除操作需要移动元素,效率较低。
2.必须提前分配固定数量的空间,如果存储元素少,可能导致空闲浪费。
3.按照内容查询效率低,因为需要逐个比较判断
这里写图片描述

2.链表—-链式存储结构

这里写图片描述
特点:数据元素的存储对应的是不连续的存储空间,每个存储结点对应一个需要存储的数据元素。
每个结点是由数据域和指针域组成。 元素之间的逻辑关系通过存储节点之间的链接关系反映出来。
逻辑上相邻的节点物理上不必相邻。
缺点:
1、比顺序存储结构的存储密度小 (每个节点都由数据域和指针域组成,所以相同空间内假设全存满的话顺序比链式存储更多)。
2、查找结点时链式存储要比顺序存储慢(每个节点地址不连续、无规律,导致按照索引查询效率低下)。
优点:
1、插入、删除灵活 (不必移动节点,只要改变节点中的指针,但是需要先定位到元素上)。
2、有元素才会分配结点空间,不会有闲置的结点。

在使用单链表实现线性表的时候,为了使程序更加简洁,我们通常在单链表的最前面添加一个哑元结点,也称为头结点。
在头结点中不存储任何实质的数据对象,其 next 域指向线性表中 0 号元素所在的结点,
可以对空表、非空表的情况以及对首元结点进行统一处理,编程更方便,常用头结点。
一个带头结点的单链表实现线性表的结构图如图 所示。
这里写图片描述

其他链表
双向链表

单链表的一个优点是结构简单,但是它也有一个缺点,即在单链表中只能通过一个结点的引用访问其后续结点,而无法直接访问其前驱结点,
要在单链表中找到某个结点的前驱结点,必须从链表的首结点出发依次向后寻找,但是需要Ο(n)时间。
为此我们可以扩展单链表的结点结构,使得通过一个结点的引用,不但能够访问其后续结点,也可以方便的访问其前驱结点。
扩展单链表结点结构的方法是,在单链表结点结构中新增加一个域,该域用于指向结点的直接前驱结点。
扩展后的结点结构是构成双向链表的结点结构,如图 所示。
这里写图片描述
双向链表是通过上述定义的结点使用 pre 以及 next 域依次串联在一起而形成的。一个双向链表的结构如图所示。
这里写图片描述
在双向链表中同样需要完成数据元素的查找、插入、删除等操作。在双向链表中进行查找与在单链表中类似,只不过在双向链表中查找操作可以从链表的首结点开始,
也可以从尾结点开始,但是需要的时间和在单链表中一样

Java中的LinkedList底层使用的就是双向链表。

循环链表

在一个循环链表中, 首节点和末节点被连接在一起。这种方式在单向和双向链表中皆可实现。
要转换一个循环链表,你开始于任意一个节点然后沿着列表的任一方向直到返回开始的节点。
循环链表可以被视为”无头无尾”。

循环链表中第一个节点之前就是最后一个节点,反之亦然。
循环链表的无边界使得在这样的链表上设计算法会比普通链表更加容易。
对于新加入的节点应该是在第一个节点之前还是最后一个节点之后可以根据实际要求灵活处理,区别不大。
单向链表的循环带头结点的非空链表
这里写图片描述

栈和队列

栈的定义

栈(stack )又称堆栈,它是运算受限的线性表。
其限制是仅允许在表的一端进行插入和删除操作,不允许在其他任何位置进行插入、查找、删除等操作。
表中进行插入、删除操作的一端称为 栈顶(top) ,栈顶保存的元素称为 栈顶元素。
相对的,表的另一端称为栈底(bottom)

当栈中没有数据元素时称为空栈;
向一个栈插入元素又称为 进栈或 入栈;
从一个栈中删除元素又称为 出栈或 退栈。
由于栈的插入和删除操作仅在栈顶进行,后进栈的元素必定先出栈,
所以又把堆栈称为 后进先出表(Last In First Out,简称 LIFO)

这里写图片描述
这里写图片描述

队列

队列定义

队列(queue )简称队,它同堆栈一样,也是一种运算受限的线性表,
其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
在队列中把插入数据元素的一端称为 队尾(rear) ),删除数据元素的一端称为 队首(front) )。
向队尾插入元素称为 进队或入队,新元素入队后成为新的队尾元素;
从队列中删除元素称为 离队或出队,元素出队后,其后续元素成为新的队首元素。
由于队列的插入和删除操作分别在队尾和队首进行,每个元素必然按照进入的次序离队,
也就是说先进队的元素必然先离队,所以称队列为 先进先出表(First In First Out,简称FIFO)。
这里写图片描述

双端队列deque double ended queue 通常读为“deck”

所谓双端队列是指两端都可以进行进队和出队操作的队列,如下图所示,将队列的两端分别称为前端和后端,两端都可以入队和出队。其元素的逻辑结构仍是线性结构
这里写图片描述
在双端队列进队时:前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列中前端进的元素的后面。在双端队列出队时,无论前端出还是后端出,先出的元素排列在后出的元素的前面。

输出受限的双端队列,即一个端点允许插入和删除,另一个端点只允许插入的双端队列。
这里写图片描述

树和二叉树

树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。
父子关系在树的结点之间建立了一个层次结构。
树的结点包含一个数据元素及若干指向其子树的若干分支。
在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点,或简称为树根。

我们可以形式地给出树的递归定义如下:
树(tree )是 n(n ≥ 0)个结点的有限集。它
1) 或者是一棵空树(n = 0),空树中不包含任何结点。
2) 或者是一棵非空树(n > 0),此时有且仅有一个特定的称为 根(root )的结点;
当n > 1 时,其余结点可分为m(m > 0)个互不相交的有限集T 1 ,T 2 ,…,T m ,
其中每一个本身又是一棵树,并且称为根的 子树(sub tree) )。
这里写图片描述
例如图 (a)是一棵空树、(b)是只有一个根节点的树、(c)是一棵有 10个结点的树,其中A是根,
其余的结点分成 3 个不相交的集合:T1 ={B,E,F}、T2 ={C,G}、T3 ={D,H,I,J},每个集合都构成一棵树,且都是根A的子树。

生活案例:树:单位组织架构、族谱
技术案例:文件系统。
这里写图片描述
这里写图片描述

结点的度与树的度

结点拥有的子树的数目称为结点的 度(Degree) )。
度为 0 的结点称为 叶子(leaf )或终端结点。
度不为 0 的结点称为 非终端结点或 分支结点。除根之外的分支结点也称为内部结点。
树内各结点的度的最大值称为树的度

这里写图片描述

父亲、儿子、兄弟
父亲(parent):一个结点的直接前驱结点
儿子(child):一个结点的直接后继结点
兄弟(sibling) :同一个父亲结点的其他结点
结点 A 是结点 B、C、D 的父亲,结点 B、C、D 是结点 A 的孩子。
由于结点 H、I、J 有同一个父结点 D,因此它们互为兄弟。

祖先、子孙、堂兄弟
将父子关系进行扩展,就可以得到祖先、子孙、堂兄弟等关系。
结点的 祖先是从根到该结点路径上的所有结点。
以某结点为根的树中的任一结点都称为该结点的 子孙。
父亲在同一层次的结点互为 堂兄弟

二叉树

每个结点的度均不超过 2 的有序树,称为 二叉树(binary tree) 。
与树的递归定义类似,二叉树的递归定义如下:
二叉树或者是一棵空树,或者是一棵由一个根结点和两棵互不相交的分别称为根的左子树和右子树的子树所组成的非空树。
这里写图片描述
由以上定义可以看出,
二叉树中每个结点的孩子数只能是 0、1 或 2 个,并且每个孩子都有左右之分。
位于左边的孩子称为左孩子,位于右边的孩子称为右孩子;
以左孩子为根的子树称为左子树,以右孩子为根的子树称为右子树。

这里写图片描述

满二叉树:
高度为k并且有 2k+1 -1 个结点的二叉树。
在满二叉树中,每层结点都达到最大数,即每层结点都是满的,因此称为满二叉树。
完全二叉树:
若在一棵满二叉树中,在最下层从最右侧起去掉相邻的若干叶子结点,得到的二叉树即为完全二叉树。
这里写图片描述

二叉树的存储结构

二叉树的存储结构有两种:顺序存储结构和链式存储结构。
顺序存储结构

对于满二叉树和完全二叉树来说,可以将其数据元素逐层存放到一组连续的存储单元中,如图所示。
用一维数组来实现顺序存储结构时,将二叉树中编号为 i 的结点存放到数组中的第 i 个分量中。
如此根据二叉树性质,可以得到结点 i 的父结点、左右孩子结点分别存放在 、2i 以及 2i+1 分量中
这里写图片描述
这种存储方式对于满二叉树和完全二叉树是非常合适也是高效方便的。
因为满二叉树和完全二叉树采用顺序存储结构既不浪费空间,也可以根据公式很快的确定结点之间的关系。
但是对于一般的二叉树而言,必须用“虚结点”将一棵二叉树补成一棵完全二叉树来存储,
否则无法确定结点之间的前驱后续关系,但是这样一来就会造成空间的浪费
这里写图片描述

链式存储结构

设计不同的结点结构可构成不同的链式存储结构。
在二叉树中每个结点都有两个孩子,则可以设计每个结点至少包括 3 个域:数据域、左孩子域和右孩子域。
数据域存放数据元素,左孩子域存放指向左孩子结点的指针,右孩子域存放指向右孩子结点的指针。如图 (a)所示。
利用此结点结构得到的二叉树存储结构称为二叉链表。

为了方便找到父结点,可以在上述结点结构中增加一个指针域,指向结点的父结点。如图 (b)所示。
采用此结点结构得到的二叉树存储结构称为三叉链表。
这里写图片描述

二叉树遍历

这里写图片描述
遍历(Traverse):
就是按照某种次序访问树中的所有结点,且每个结点恰好访问一次。
也就是说,按照被访问的次序,可以得到由树中所有结点排成的一个序列。
树的遍历也可以看成是人为的将非线性结构线性化。
这里的“访问”是广义的,可以是对结点作各种处理,例如输出结点信息、更新结点信息等。
在我们的实现中,并不真正的“访问”这些结点,而是得到一个结点的线性序列,以线性表的形式输出

将整个二叉树看做三部分:根、左子树、右子树。如果规定先遍历左子树、再遍历右子树。
那么根据根的遍历顺序就有三种遍历方式

先序/根遍历DLR:根 左子树 右子树
中序/根遍历LDR:左子树 根 右子树
后根/序遍历LRD:左子树 右子树 根
按照层次遍历:
注意:由于树的递归定义,其实对三种遍历的概念其实也是一个递归的描述过程

先序遍历DLR: 1 4 5 2 3 6 7
中序遍历LDR: 4 5 1 3 2 6 7
后序遍历LRD: 5 4 3 7 6 2 1

面试题:已知一棵二叉树的后序遍历的序列为 5 4 3 7 6 2 1,中序遍历的序列为 4 5 1 3 2 6 7,
则其先序遍历的序列是什么?

查找树

二叉查找/搜索/排序树 BST (binary search/sort tree)

或者是一棵空树;
或者是具有下列性质的二叉树:
(1)若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值;
(2)若它的右子树上所有结点的值均大于它的根节点的值;
(3)它的左、右子树也分别为二叉排序树。
这里写图片描述
平衡二叉树(Self-balancing binary search tree) 自平衡二叉查找树 又被称为AVL树(有别于AVL算法)
它是一 棵空树
或它的左右两个子树的高度差(平衡因子)的绝对值不超过1,
并且左右两个子树都是一棵平衡二叉树,
同时,平衡二叉树必定是二叉搜索树,反之则不一定

平衡因子(平衡度):结点的平衡因子是结点的左子树的高度减去右子树的高度。(或反之定义)
平衡二叉树:每个结点的平衡因子都为 1、-1、0 的二叉排序树。或者说每个结点的左右子树的高度最多差1的二叉排序树。

平衡二叉树的目的是为了减少二叉查找树层次,提高查找速度

平衡二叉树的常用实现方法有AVL、红黑树、替罪羊树、Treap、伸展树等

这里写图片描述
红黑树
R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种平衡二叉树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

注意:
(01) 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
(02) 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树

这里写图片描述

红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(logN),效率非常之高。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。
例如,Java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。

1.图的基本概念:多对多关系

这里写图片描述
这里写图片描述
这里写图片描述

图(graph)是一种网状数据结构,图是由非空的顶点集合和一个描述顶点之间关系的集合组成。
    其形式化的定义如下:
Graph = ( V , E )
V = {x| x∈某个数据对象}
E = {<u , v>| P(u , v)∧(u,v∈V)}
V 是具有相同特性的数据元素的集合,V 中的数据元素通常称为 顶点(Vertex) ),
E 是两个顶点之间关系的集合。P(u , v)表示 u 和 v 之间有特定的关联属性。

若<u , v>∈E,则<u , v>表示从顶点 u 到顶点 v 的一条弧,并称 u 为弧尾或起始点,称v 为弧头或终止点,
此时图中的顶点之间的连线是有方向的,这样的图称为 有向图(directedgraph) )。

若<u , v>∈E 则必有<v , u>∈E,即关系 E 是对称的,此时可以使用一个无序对(u , v)来代替两个有序对,
它表示顶点 u 和顶点 v 之间的一条边,此时图中顶点之间的连线是没有方向的,这种图称为 无向图(undirected graph)。

在无向图和有向图中 V 中的元素都称为顶点,而顶点之间的关系却有不同的称谓,即弧或边,为避免麻烦,在不影响理解的前提下,我们统一的将它们称为 边(edge) 。
并且我们还约定顶点集与边集都是有限的,并记顶点与边的数量为|V|和|E|。

这里写图片描述
加权图:
在实际应用中,图不但需要表示元素之间是否存在某种关系,
而且图的边往往与具有一定实际意义的数有关,即每条边都有与它相关的实数,称为 权。
这些权值可以表示从一个顶点到另一个顶点的距离或消耗等信息,在本章中假设边的权均为正数。
这种边上具有权值的图称为 带权图(weighted graph)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值