个人专题目录
1.1. 数据结构概述
1.2. 数据结构概念
现实的数据元素之间有着纷繁复杂的逻辑关系,需要采用合适的物理结构来存储这些数据,并以此为基础对这些数据进行相应的操作。
同时,还要分析这些数据结构在时间、空间上的开销的优劣。这种专门研究应用程序中数据之间逻辑关系、存储方式及其操作的学问就是数据结构。
1.3. 数据结构的研究对象
1.3.1. 数据间的逻辑结构
数据的逻辑结构指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关。
逻辑结构包括:集合结构、线性结构、树形结构、图形结构。
1.3.1.1. 集合
数据元素之间只有“同属于一个集合”的关系
1.3.1.2. 线性关系
数据元素之间存在一个对一个的关系
1.3.1.3. 树形结构
数据元素之间存在一个对多个的关系
1.3.1.4. 网状结构(或图状结构)
数据元素之间存在多个对多个的关系
1.3.2. 数据的存储结构(或物理结构)
数据的存储结构(或物理结构)指数据的逻辑结构在计算机存储空间的存放形式。
数据的存储结构是数据结构在计算机中的表示(又称映像),它包括数据元素的机内表示和关系的机内表示。由于具体实现的方法有顺序、链接、索引、散列等多种,所以,一种数据结构可表示成一种或多种存储结构。
数据元素的机内表示(映像方法): 用二进制位(bit)的位串表示数据元素。通常称这种位串为节点(node)。当数据元素有若干个数据项组成时,位串中与个数据项对应的子位串称为数据域(data field)。因此,节点是数据元素的机内表示(或机内映像)。
关系的机内表示(映像方法):数据元素之间的关系的机内表示可以分为顺序映像和非顺序映像,常用两种存储结构:顺序存储结构和链式存储结构。顺序映像借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。非顺序映像借助指示元素存储位置的指针(pointer)来表示数据元素之间的逻辑关系。
2. 真实结构
2.1. 数组(Array)
2.1.1. 申请内存
一次申请一大段连续的空间,一旦申请到,内存就固定了
2.1.2. 存储思路
所有数据存储在这个连续的空间中
数组中的每一个元素都是一个具体的数据
所有数据都紧密排布,不能有间隔
2.1.3. 操作
读内存
查:每个元素都有一个数值下标, 可以通过下标瞬间定位到某个元素
写内存
增
从头部插入
在中间插入
从尾部插入
注意:插入元素,引起部分元素后移,代价非常高
删
注意:去掉元素,引起部分元素前移,代价非常高
改
注意:只需要修改对应位置的元素的内容,不需要申请或者删除空间
2.1.4. 特点
查询效率非常高
数组查找快, 但插入/删除效率低
2.1.5. 适用范围
查询操作远多于插入/删除操作的场景
简单抽象数据结构的基石
2.2.链表(Linked List)
2.2.1. 申请内存
一次申请一小块内存,按需申请
2.2.2. 存储思路
每一个小数据单独存在一小块内存中,这个单元被称作结点(Node)
每一小块内存知道下一小块的地址
多个不连续的小内存空间组成, 通过内存地址引用形成一个链的结构
表头:程序员获得表头就可以找到链表中所有的元素
表尾:有的链表有,有的没有
2.2.3. 读
查:节点是没有下标的, 只能从链表的表头开始查找某个节点
2.2.4. 写
增
申请一小块内存--结点
插入到链中
从头部插入
从尾部插入
从中间插入
删
从链中剥离结点
释放这个结点的内存
改
改变这个结点中所存储的值
2.2.5. 特点
查找速度慢
链表添加/删除较数组快
2.2.6.适用范围
插入/删除操作远多于查询操作的场景
3. 抽象结构
3.1. 栈(Stack)
栈(Stack)是限制仅在表的一端进行插入和删除运算的线性表。
栈的修改原则:
栈的修改是按后进先出的原则进行。每次删除(退栈)的总是当前栈中"最新"的元素,即最后插入(进栈)的元素,而最先插入的是被放在栈的底部,要到最后才能删除。
3.1.2. 名词
栈顶:插入、删除的这一端为栈顶
栈底:相对于栈顶的另一端
空栈:当表中没有元素时称为空栈
3.1.3. 特点
继承于java.util.Vector
后进先出
3.1.4. 操作
进栈(压栈、入栈): push(ele)
出栈: pop()
查看栈顶元素: peek()
清栈: clear()
大小: size()
判断是否是空:empty()
查找元素出现位置:search(Object obj)
使用场景如: 运行栈
3.2. 队列(Queue)
队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表。
队列的修改原则:
队列的修改是依先进先出的原则进行的。新来的成员总是加入队尾(即不允许"加塞"),每次离开的成员总是队列头上的(不允许中途离队),即当前"最老的"成员离队。
3.2.1. 名词
队头(front):允许删除的一端称为队头(Front)
队尾(rear):允许添加的一端称为队尾(Rear)
空队列:当队列中没有元素时称为空队列
3.2.2. 特点
先进先出
典型实现:PriorityQueue/LinkedList
3.2.3. 操作
加入元素到队列尾部: add(Object obj)/offer(Object obj)
获取队列头部元素,不删除该元素:element()/peek()
获取队列头部元素,并删除该元素:remove()/poll()
清理队列: clear()
得到队列的大小: size()
判断队列是否是空的: isEmpty()
使用场景如: 事件队列, 消息队列
3.3. 树(Tree)
树:
对大量的输入数据,链表的线性访问时间太慢,不宜使用。这里是另外一种重要的数据结构----树,其大部分时间可以保证操作的运行平均时间复杂度为O(logN)。
3.3.1. 名词
根:最上面的结点称之为根,一颗树只有一个根且由根发展而来,从另外一个角度来说,每个结点都可以认为是其子树的根
结点:树中的数据元素都称之为结点
父亲:结点的上层结点,如图中,结点K的父亲是E、结点L的父亲是G
兄弟:具有相同父亲的结点称为兄弟,图中F、G、H互为兄弟
结点的度:结点所拥有的子树的个数称之为结点的度,如结点B的度为3
树叶:度为0的结点,也叫作终端结点,图中D、K、F、L、H、I、J都是树叶
分支结点:度不为0的结点,也叫作非终端结点或内部结点,图中根、A、B、C、E、G都是分支结点
结点的层次:从根节点到树中某结点所经路径上的分支树称为该结点的层次,根节点的层次规定为1,其余结点的层次等于其父亲结点的层次+1
树的深度:树中结点的最大层次数,图中树的深度为4
3.3.2. 二叉树(Binary Tree)
二叉树:
在普通树的基础上,让一棵树中每个节点最多只能包含两个子节点,且严格区分左子节点和右子节点(位置不能换)。
增删改查
搜索
广度优先搜索
深度优先搜索
先序遍历
中序遍历
后序遍历
3.3.2.1 特例
排序二叉树
满足一些条件的二叉树,才被称为排序二叉树。比如:若它的左子树不为空,左子树上的所有节点
值均小于根节点值;若右子树不为空,右子树上的所有节点值均大于根节点值。
特点
可以非常方便地对树中的所有节点进行排序和检索。
红黑树
红黑树顾名思义就是结点是红色或者黑色的平衡二叉树,它通过颜色的约束来维持着二叉树的平衡。下图为一颗典型的二叉树:
对于一棵有效的红黑树而言我们必须增加如下规则:
1、每个结点都只能是红色或者黑色
2、根节点是黑色
3、每片叶子都是黑色的
4、如果一个结点是红色的,则它的两个子节点都是黑色的,也就是说在一条路径上不能出现相邻的两个红色结点
5、从任意一个结点到其每个叶子的所有路径都包含着相同数目的黑色结点
这些约束强制了红黑树的关键性质:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果就是这棵树大致上是平衡的,因为插入、删除和查找某个值得最坏情况时间都要求与树的高度成比例,这个高度理论上限允许红黑树只在最坏情况下都是高效的。
再具体就不说了,可以参看http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html,对红黑树的讲解写得非常好。
TreeMap和TreeSet就是红黑树的典型实现。
3.3.2.2. 使用场景
哈夫曼树
假设需要对一个字符串如“abcdabcaba”进行编码,将它转换为唯一的二进制码,同时要求转换出来的二进制码的长度最小。我们采用哈夫曼树来解决报文编码问题。