数据结构整理

更多内容可以访问我的个人博客


1.线性表

线性结构是最简单,也是最常用的数据结构之一。特点是:在数据元素的有限集中,除第一个元素无直接前驱,最后一个元素无直接后续以外,每个数据元素有且仅有一个直接前驱元素和一个直接后续元素

线性表的抽象数据类型基本操作:

getSize()isEmpty()insert(i,e)contains(e)get(i)remove(e)
获取大小是否为空插入是否包含元素e获取序号为i的元素删除e
1.1 线性表的顺序存储与实现

线性表的顺序存储是用一组地址连续的存储单元依次存储线性表的数据元素。以数据元素在机内存储地址相邻来表示线性表中数据元素之间的逻辑关系。

优点是只要确定了线性表的起始地址,线性表中任意元素都可以做到随机存取。缺点是插入或删除元素时,需要移动后续元素,增加了开销

在抽象数据类型的实现中都是使用数组来描述数据结构的顺序存储结构。

1.2 线性表的链式存储与实现

实现线性表的另一方法是链式存储,即用指针将存储线性表中数据元素的那些单元依次串联在一起。

这种方法弥补了数组中连续存储的缺点,在插入或删除元素时,不再需要移动元素。但需要在每个单元设置指针,增加了额外的存储开销,并且在查找元素时需要从头指针开始查询,无法做到随机存取

链表又可以分为单链表与双向链表

①单链表:有一个存储数据元素的数据域,一个指向下一个元素地址的指针域(Java中没有显式的指针类型,是使用对象引用来实现的)。

public class SLNode{
    /*数据域*/
    private Object e;
    /*下一个元素的引用*/
    private SLNode next;
}

优点:结构简单。

缺点:只能通过引用访问后续节点,而无法直接访问其前驱节点。

②双向链表:扩展单链表,增加一个指针域存放其前驱节点的地址。

public class DLNode {
    private Object e;
    private DLNode pre;
    private DLNode next;
}

优点:可以直接找点节点的前驱。

缺点:增加了一个指针域,增加了存储开销。并且插入、删除元素时更加繁琐。

1.3 两种实现方式的比较
从时间上进行比较

查找操作上,若基于序号,顺序存储有随机存取的特性,而链式存储需要从头结点遍历;若基于内容,则两者都需要从头遍历

而在插入和删除操作上,顺序存储需要移动大量数据元素;而链表只需修改几个指针。

从空间上进行比较

顺序存储的存储空间是预先静态分配的,虽然在实现过程中可以动态扩展,但若长度变化较大,会存在较大的空闲空间空间利用率低;而链式存储空间是动态分配的。

当线性表的数据元素简单,并且线性表的长度变化不大时,链式存储需要额外的指针域空间,所以可以选择顺序存储。


2.栈与队列
2.1 栈

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

和线性表类似,堆栈也有两种基本的存储结构:顺序存储结构和链式存储结构。

**递归(recursion)**是指在定义自身的同时又出现了对自己的引用。如果一个算法直接或间接地调用自己,则称这个算法是一个递归算法,由两部分组成:递归终止条件和递归调用。

函数调用期间的相关信息的保存需要使用一个堆栈来实现。系统将整个程序运行时需要的数据空间安排在一个堆栈中,每当调用一个函数时就为它在栈顶分配一个存储区,**每当一个函数返回时就释放它的存储区。**一个递归算法的实现实际上就是多个相同函数的嵌套调用。

2.2 队列

队列(queue)简称队,他同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。插入数据的一端称为队尾(rear),删除数据元素的一端称为队首(front)。队列又称为先进先出表(FIFO)

和线性表类似,队列也有两种基本的存储结构:顺序存储结构和链式存储结构。

单元围成一个圆环的队列即为循环队列


3. 树

树是一种非线性结构,数据元素之间的逻辑关系是前驱唯一而后续不唯一的,即数据元素之间是一对多的关系。

3.0 树的性质
◆ 结点的层次和树的深度

树的结点包含一个数据元素及若干指向其子树的若干分支。

结点的层次从根结点开始定义,层次数为0的结点是根结点。根节点的子树的根的层次数为1。

结点的最大层次数称为树的深度高度。树中结点也有高度,其高度是以该结点为根的树的高度。

◆ 结点的度与树的度

结点拥有的子树的数目称为称为结点的度(Degree),度为0的结点称为叶子(Leaf)

◆ 树的边数

树中的结点数等于树的边数加1,也等于所有结点的度数之和加1。因为除了根结点以外每个结点都与指向它的一条边对应。在对涉及树结构的算法复杂性进行分析时,可以用结点的数目作为规模的度量。

◆ 有序树、m叉树、森林

有序树:将树中结点的各子树看成是从左至右有次序的,则称该树为有序树;若不考虑子树的顺序则成为无序树。对于有序树,我们可以明确地定义每个结点的第一个孩子、第二个孩子等。

m叉树:树中所有结点最大度数为m的有序树。

森林(forest):是m(m>=0)棵互不相交的树的集合。

3.1 二叉树
◆ 性质

① 在二叉树的第i层上最多有2^i个结点。

② 高度为h的二叉树至多有2^{h+1}-1个结点。

③ 对任何一棵二叉树T,如果其叶子结点数为n0,度为2的结点数为n2,则n0 =n2 + 1。

◆ 满二叉树

高度为K并且有2^{k+1}-1个结点的二叉树。在满二叉树中,每层结点都达到最大数,即每层结点都是满的,因此称为满二叉树。

◆ 完全二叉树

若在一棵满二叉树中,在最下层从最右侧起去掉相邻的若干叶子结点,得到的二叉树即为完全二叉树。

可见,满二叉树必为完全二叉树,但完全二叉树不一定为满二叉树。

由完全二叉树的定义可以得到:

① 有n个结点的完全二叉树的高度为(log n的向下取整)

② 含有n >= 1个结点的二叉树的高度至多为n - 1;高度至少为(log n的向下取整)

③ 如果对一棵有n个结点的完全二叉树的结点进行编号,则对任一结点i( 1 <= i <= n),有

(1) 如果 i = 1,则结点i是二叉树的根,无双亲;如果i > 1,则其双亲结点Pi是( i / 2 向下取整) 。

(2) 如果2i > n,则结点i无左孩子;否则其左孩子是结点2i。

(3) 如果2i + 1 > n,则结点i无右孩子;否则其右孩子是结点2i + 1。

3.2 二叉树的存储结构
◆ 顺序存储结构

对于满二叉树和完全二叉树来说,可以将其数据元素逐层存放到一组连续的存储单元中。

在这里插入图片描述

这种方式对于满二叉树和完全二叉树是非常合适也是高效方便的。因为满二叉树和完全二叉树采用顺序存储结构既不浪费空间,也可以根据公式很快地确定节点之间的关系。但对于一般的二叉树而言,必须用“虚节点”将一棵二叉树补成完全二叉树来存储,会造成空间浪费

◆ 链式存储结构

设计不同的结点结构可构成不同的链式存储结构。至少应包含三个域:数据域、左孩子域、右孩子域,为了方便找到父节点,可以增加一个指向父节点的指针域

3.3 树的存储结构
◆ 双亲表示法

每个结点中有一个指向其父亲结点的数组下标的域

找一个结点的父亲结点只需O(1),但找儿子结点或者兄弟结点可能要遍历整个数组。

在这里插入图片描述

◆ 孩子链表表示法

用一个线性表来存储树的所有结点信息,称为结点表。对每个结点建立一个孩子表。孩子表中只存储孩子结点的地址信息,可以是指针,数组下标甚至内存地址。由于每个结点的孩子数目不定,因此孩子表常用单链表来实现。

在孩子链表表示法中,通过某个结点找到其孩子较为容易,遍历其孩子链表即可,但找其父节点麻烦。因此可以结合孩子链表表示法和双亲表示法,在每个结点再设置一个指示双亲结点的域。

在这里插入图片描述

◆ 孩子兄弟表示法

树的孩子兄弟表示法又称为二叉树表示法。每个结点除了data域外,还含有两个指针域,分别指向该结点的第一个孩子和右邻兄弟。

在这里插入图片描述

3.4 Huffman树

在计算机系统中,符号需要进行二进制编码。而为了缩短数据编码长度,可以采用不定长编码。其基本思想是:给使用频度较高的字符编较短的编码,这是数据压缩的最基本思想。

前缀码:在一个编码系统中,任何一个编码都不是其他编码的前缀,则称该编码系统的编码是前缀码。当一个编码系统中采用定长编码时,可以不用分隔符;如果采用不定长编码,必须使用前缀码或分隔符,否则会在解码时产生歧义。而使用分隔符就又增加了编码长度,所以一般采用前缀编码。

Huffman树:它是由n个带权叶子结点构成的所有二叉树中带权路径长度最小的二叉树,Huffman树又称最优二叉树

构造Huffman树的算法步骤如下:

① 根据给定的n个权值,构造n棵只有一个根节点的二叉树,n个权值分别是这些二叉树根节点的权,F是由这n棵二叉树构成的集合。

② 在F中选取两棵根节点树值最小的树作为左、右子树,构造一棵新的二叉树,置新二叉树根的权值 = 左右子树根节点权值之和。

③ 从F中删除这两棵树,并将新树加入F。

④ 重复②、③,直到F中只含一棵树为止。

在这里插入图片描述


4. 图

图(graph)是一种网状数据结构,是由非空的顶点集合一个描述顶点之间关系的集合组成。

4.1 图及基本术语
简单图:

对简单图而言,图中中所有的边自然构成一个集合,并且每条边的两个顶点均不相同.

顶点的度、入度、出度:

顶点的度(degree)是指依附于某顶点 v 的边数,通常记为 TD (v).顶点 v 的**入度(in degree)**是指以顶点 为终点的边的数目,记为 ID (v);顶点 v **出度(out degree)**是指以顶点 v 为起始点的边的 数目,记为 OD (v).

完全图、稠密图、稀疏图

假设在图 G = ( V , E )中有 n 个顶点和 m 条边.

  1. 若 G 是无向图,则有 0 ≤ m ≤ n(n-1)/2.

  2. 若 G 是有向图,则有 0 ≤ m ≤ n(n-1).

由此,在具有n个顶点的图中,边的数目为Ο(n2).有n(n-1)/2 条边的无向图称为无向完全图;有 n(n-1)条边的有向图称为有向完全图.有 很少边(如 m < n log n)的图称为稀疏图,反之边较多的图称为稠密图.

路径、环路及可达分量

所谓图中的一条通路或路径(path),就是由m+1 个顶点与m条边交替构成的一个序列ρ = { v0, e1 , v1 , e2 , v2 , … , em , vm},m ≥ 0,且ei = (vi-1 , vi),1 ≤ i ≤ m.路径上边的数目称为路径长度,计作|ρ|.

长度|ρ| ≥ 1 的路径,若路径的第一个顶点与最后一个顶点相同,则称之为环路或环 (cycle).

在有向图 G 中,若从顶点 s 到顶点 v 有一条通路,则称 v 是从 s 可达的.对于顶点 s, 从 s 可达的所有顶点所组成的集合,称作 s 在 G 中对应的可达分量.

连通性与连通分量

在无向图中,如果从一个顶点vi到另一个顶点vj(i≠j)有路径,则称顶点vi和vj是连通的. 如果图中任意两顶点vi,vj∈V,vi和vj都是连通的,则称该图是连通图(connected graph).

所谓连通分量(connected component),是指无向图的极大连通子图.显然任何连通图的连通分量只有一个,即本身.而非连通图有多个连通分量,各个连通分量之间是分离的, 没有任何边相连.

有向图中,若图中任意一对顶点vi和vj (i≠j)均有一条从顶点vi到另一个顶点vj的路径, 也有从vj到vi的路径,则称该有向图是强连通图.有向图的极大强连通子图称为强连通分量.

权与网

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

4.2 图的存储方法
4.2.1 邻接矩阵

图的邻接矩阵(adjacent matrix)表示法是使用数组来存储图结构的方法,也被称为数组表示法.它采用两个数组来表示图:一个是用于存储所有顶点信息的一维数组,另一个是 用于存储图中顶点之间关联关系的二维数组,这个关联关系数组也被称为邻接矩阵.

假设图G=(V , E)有n个顶点,即V={v0,v1,…,vn-1},则表示G中各顶点关联关系的为一个n×n的矩阵A,若< u,v >之间存在直接连线,那么对应矩阵元素为1,否则为∞.

在这里插入图片描述

邻接矩阵存储存在以下特点:

① 首先,无向图的邻接矩阵一定是一个对称矩阵.因此,在具体存放邻接矩阵时只需存放上(或下)三角矩阵的元素即可.

② 其次,对于无向图, 邻接矩阵的第i行(或第i列)非∞元素的个数正好是第i个顶点的度TD(vi).

③ 再次,对于有向 图,邻接矩阵的第i行(第i列)非∞元素的个数正好是第i个顶点的出度OD(vi)(入度ID(vi)).

④ 最后,通过邻接矩阵很容易确定图中任意两个顶点之间是否有边相连;但是,要确定图中有多少条边,则必须按行,按列对每个元素进行检测,所花费的时间代价很大.

邻接矩阵存储的缺点:

① 尽管由n个顶点构成的图中最多可以有n的2次方条边,但是在大多数情况下,边的数目远远达不到这个量级,因此,在邻接矩阵中大多数单元 都是闲置的.空间利用率较低。

矩阵结构是静态的,其大小N需要预先估计,然后创建N×N的矩阵.然而,图的规模往往是动态变化的,N的估计过大会造成更多的空间浪费,如果估计过小则经 常会出现空间不够用的情况.

4.2.2 邻接表

邻接表(adjacency list)是图的一种链式存储方法,邻接表表示法类似于树的孩子链表表示法.在邻接表中对于图G中的每个顶点vi建立一个单链表,将所有邻接于vi的顶点vj链成 一个单链表,并在表头附设一个表头结点,这个单链表就称为顶点vi的邻接表.

在邻接表中共有两种结点结构,分别是边表结点和表头结点.每个边表结点由 3 个域组成,其中邻接点域(adjvex)指示与顶点vi邻接的顶点在图中的位置, 链域(nextedge)指向下一条边所在的结点,数据域(info)存储和边有关的信息,如权值等信息.在头结点中,除了设有链域(firstedge)指向链表中的第 一个结点之外,还有用于存储顶点vi相关信息的数据域(data).

在这里插入图片描述

就存储空间而言,对于 n 个顶点,m 条边的无向图,若采用邻接表作为存储结构,则需 要 n 个表头结点和 2m 个边表结点.显然在边稀疏(m<<n(n-1)/2)的情况下,用邻接表存储 要比使用邻接矩阵节省空间.

4.3 最小生成树

我们看到对于连通图而言从图中不同顶点出发或从同一顶点 出发按照不同的优先搜索过程可以得到不同的生成树.如此,对于一个连通网(连通带权图)来说,生成树不同,每棵树的代价(树中每条边 上权值之和)也可能不同,我们把代价最小的生成树称为图的最小生成树(minimum spanning tree).

4.3.1 Prim算法

从某个点开始,找到不在树集合但与树集合相连的最短权值的点,将其加入树集合,重复这一步,直到整个图所有点都加入树集合。

在这里插入图片描述

4.3.2 Kruskal算法(克鲁斯卡尔算法)

找到权值最小,且两边端点至少有一个不在树集合内,不断找最小权值的边,直到所有点都加入树集合。

在这里插入图片描述

4.4 单源最短路径
4.4.1 Dijkstra算法

参考博客:深入理解 Dijkstra 算法实现原理

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。
它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止

这样一个有权图,Dijkstra算法可以计算任意节点其他节点的最短路径.

在这里插入图片描述

算法思路:

① 指定一个节点,例如我们要计算 ‘A’ 到其他节点的最短路径

② 引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度),U集合包含未求出最短路径的点(以及A到该点的路径,注意 如上图所示,A->C由于没有直接相连 初始时为∞

③ 初始化两个集合,S集合初始时 只有当前要计算的节点,A->A = 0, U集合初始时为A->B = 4, A->C = ∞, A->D = 2, A->E = ∞.

④ 从U集合中找出路径最短的点,加入S集合,例如A->D = 2

更新U集合路径若( 'D 到 B,C,E 的距离' + 'AD 距离' < 'A 到 B,C,E 的距离' )则更新U

循环执行 4、5 两步骤,直至遍历结束,得到A 到其他节点的最短路径

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值