数据结构期末复习
与数据结构相关的基本概念
数据:数据是所有能被输入到计算机中,且能被计算机处理的符号(数字、字符等)的集合,它是计算机操作对象的总称。
数据元素:
- 原子型数据元素:原子型数据元素不可分割
- 由多个款项构成的数据元素:其中每个款项被称为一个数据项,数据项是数据结构中讨论的“最小单位”。
如下:描述一个学生信息的数据元素由以下四个数据项构成。
学号 | 姓名 | 性别 | 年龄 |
201814402213 | 箪苒 | 女 | 15 |
数据结构包括数据的逻辑结构和物理结构两个方面。
- 数据的逻辑结构:指数据元素之间的逻辑关系。与数据的存储无关,是独立于计算机的。(可细分为:集合结构、一对一的线性结构、一对多的非线性结构:树结构和图结构)
- 数据的物理结构(存储结构):是数据的逻辑结构在存储器的表示。它依赖于计算机。
数据的物理结构(存储结构)分为两大类:顺序存储结构(是把相邻的数据元素存储在一块连续地址空间的内存中。程序设计方法是使用数组。)、链式存储结构。
- 顺序存储结构的特点是:逻辑上相邻的数据元素在物理上也相邻,数据间的逻辑关系表现在数据元素的存储位置关系上。
- 链式存储结构的特点是:逻辑上相邻的数据元素在物理上不一定相邻,数据间的逻辑关系表现在结点的链接关系上。
算法
算法是描述求解问题的操作步骤的集合。
算法的性质:
- 输入性:有零个或若干个输入。
- 输出性:产生一个或多个输出。
- 有穷性:在执行有限个步骤后结束。
- 确定性:每一条语义的含义明确,无二义性。
- 可行性:每步计算都可以在有限的时间内完成。
在设计算法时需要考虑以下原则:算法的正确性、可读性(是否易于阅读)、健壮性(一个算法对不合理数据输入的反应能力和处理能力,也称为容错性)、高效率(算法的执行时间)与低存储量(执行过程中所需的存储空间)。
算法的时间复杂度
算法时间复杂度的度量:算法中基本操作的执行次数。多数情况下,取最深循环内的语句所描述的操作作为基本操作。
时间复杂度T(n) = O(f(n))中增长最快的项/此项的系数
int i = 1,k = 0;
do{
k = k+10*i;
i = i+1;
}
while(i != n);
如上述代码所示:
k = k+10*i; 和 i = i+1; 都是基本语句。
时间复杂度为O(n).
算法的空间复杂度分析主要是分析算法在运行时所需要的内存空间的数量级。
线性表
线性表是一种可以在任何位置进行插入和删除数据元素的操作。由n个相同类型的数据元素组成的线性结构。
顺序表:用一组地址连续的存储单元依次存储线性表的各个数据元素。即采用顺序存储结构的线性表。它通常采用数组实现数组元素的存储。
线性表顺序存储的特点:
(1)逻辑上相邻的数据元素,其物理上也相邻。
(2)若已知表中首元素在存储器中的位置,则其他元素的存储位置也可利用数组下标求出。
代码要求:插入和删除
插入代码:
public void insert(Object obj,int i)
{
if(size==maxSize)//maxSize是数组中最多存放元素个数
{
throw new Exception("顺序表已满,无法插入");
}
if(i<0||i>Size)
{
throw new Exception("参数错误");
}
for(int j = size;j>i;j--)//size 是数组中的元素个数
{
listArray[j]=listArray[j-1];
}
listArray[i] = obj;
size++;
}
删除代码:
public object delete(int i)
{
if(size==0)
{
throw new Expection("顺序表已空,无法删除!");
}
if(i<0||i>size-1)
{
throw new Expection("参数错误");
}
Object it = listArray[i];
for(int j = i ;j<size-1;j++)
{
listArray[j]=listArray[j+1];
}
size--;
return it;
}
顺序表的效率分析:
顺序表插入、删除一数据元素的平均时间复杂度为O(n);
顺序表支持随机读取,所以顺序表取元素的时间复杂度为O(1)
顺序表的优缺点:
优点:支持随机读取,以及内存空间利用效率高。
缺点:需要预先给出数组的最大数据元素个数,另外插入和删除操作时需要移动较多的元素。
链式存储结构:是用指针把相互直接关联的结点(即直接前驱结点和直接后继结点)链接起来。
指针表示一个数据元素逻辑意义上的存储位置
一个数据元素和一个指针成为一个结点
链式存储结构是基于指针实现的
链式存储结构的线性表称为链表,根据结点构造链的方法不同,链表主要有单链表、循环单链表、和双向链表三种
头节点:在链表的首元结点之前附设的一个结点,不计入表的长度
头指针:指向链表中的第一个节点
空表:
不带头结点时,当头指针的值为空时表示空表;
带头结点时,当头指针的指针域为空时表示空表。
单链表(不带头结点)
代码要求:插入、删除、定位
定位代码:
//定位
public void index (int i)
{
if(i<0||i>size-1)
{
thorw new Exception("参数错误");
}
current = head;
int j = 0;
while((current!=null)&&j<i)
{
current = current.next;
j++;
}
}
插入代码:
//插入:
public void insert(Object obj,int i)
{
if(i<0||i>size)
{
throw new Exception("参数错误");
}
if( i == 0)
{
head = new Node(obj,head);
size++;
}
else
{
index(i-1);
current.next = new Node(obj,current.next);
size++;
}
}
单链表的效率分析:
定位的时间复杂度:O(n)
仅就插入和删除的时间复杂度为O(1)
但是,如果要在单链表中进行在某结点前插入或删除的操作,因为要从头查找前驱结点,所以一般情况下,单链表插入和删除操作的时间复杂度为O(n)
顺序表和单链表的比较 :
顺序表支持随机读取,顺序表读取数据元素操作的时间复杂度为O(1)
单链表不支持随机读取,单链表读取数据元素操作的时间复杂度为O(n)
存储空间的利用
顺序表存在使用空间的浪费与溢出问题,单链表不存在这个问题。
插入和删除
顺序表的插入和删除操作有时需移动大量元素
单链表的插入和删除操作不需要移动数据元素
顺序表适合查找,链表适合插入和删除。
循环链表:从任意结点出发均可找到表中其他结点
双向链表:可方便找到任一节点的前驱
仿真链表:不用指针也能实现链式存储和运算
堆栈和队列
堆栈:是一种 特殊的线性表,只允许在固定一端进行插入和删除操作。
后进先出
队列:是一种特殊的线性表,只允许在一段进行插入操作,在其另一端进行删除操作。(队尾插入,队头删除)
先进先出
顺序循环队列的队空和队满
队列满的状态:front = rear ;
队列空的状态:front = rear ;
区分队列空队列满:少用一个存储空间
以队尾rear+1等于队头front为队列满的判断条件。
即判断条件为:(rear+1)%maxsize=front
队列为空的判断条件仍为rear = front;
数组:数组是n个相同类型的数据元素,构成的,占用一块地址连续的内存单元的有限集合(随机读取)
特殊矩阵、稀疏矩阵的特点
特殊矩阵(压缩存储):
1.只存储相同矩阵元素的一个副本
n阶对称矩阵:aij = aji
n阶对称矩阵的元素关于主对角线对称,故只要存储矩阵中上三角或下三角中的元素,让每两个对称的元素共享一个存储空间,能节省近一半的存储空间
即将n的平方个元素压缩在n(n+1)/2个存储单元中。
2.采用不等长的一维数组
n阶三角矩阵
以对角线划分,n阶矩阵有n阶上三角矩阵和n阶下三角矩阵两种。
n阶上三角矩阵:它的下三角不包括主对角线中的元素均为0或常数
n阶下三角矩阵:它的上三角不包括主对角线中的元素均为0或常数
大多数情况下,n阶三角矩阵的常数为0
稀疏矩阵:矩阵中非零元素的个数远远小于矩阵元素的个数的矩阵。(压缩存储)
压缩存储结构:三元组顺序表、三元组链表
三元组线性表表示的稀疏矩阵{(第几行,第几列,值)}
递归算法:若一个算法直接的或间接的调用自己本身,则称这个算法是递归算法。
树是由n个结组成的有限集合。
二叉树
基本特征:
1.每个结点最多有两棵子树
2.左子树和右子树次序不能颠倒
二叉树与度不超过2的树不同,与度不超过二的有序树也不同
尽管二叉树与树有很多相似之处,但是二叉树不是树的特殊情况
满二叉树 完全二叉树的区别
完全二叉树允许在最底层的的右边缺少连续若干个结点。
二叉树的性质:(根结点所在的层数为0,设层数为 i,深度为k)
1.在一棵非空二叉树上,第i层最多有2的i次方个结点
2.深度为k的二叉树最多有2的k+1次方-1个结点
3.叶子数 = 度为2的结点数+1
4.具有n个结点的完全二叉树的深度为【log2(n+1)】-1【向上取整】
5.对一棵有n个结点的完全二叉树的结点按照从上至下和从左至右的顺序对所有结点从0开始顺序编号,则对于序号为i的结点,有:
(1)如果i=0,则结点i无双亲,是二叉树的根;
如果i>0,则结点的双亲结点是(i-1)/2
(2)i结点的左孩子结点:2i+1
i结点的右孩子结点:2i+2
二叉树的存储结构:顺序存储、链式存储、仿真指针存储
二叉树的遍历:使每个结点被访问且仅被访问一次
前序遍历:根、左、右
中序遍历:左、根、右
后序遍历:左、右、根
层次遍历:先被访问的父结点的孩子结点 先于 后被访问的父结点的孩子结点进行访问
用二叉树表示算术表达式:
前序遍历:前缀表示法
中序遍历:中缀表示法
后序遍历:后缀表示法
前序和中序或前序和后序能唯一决定一棵二叉树
哈夫曼树(最优二叉树):树中所有叶子结点的带权路径长度之和最小的树
哈夫曼树的特点:肯定没有度为1的结点,一棵n个叶子结点的哈夫曼树有2n-1个结点
构造哈夫曼树,哈夫曼编码左0右1
树与二叉树的转换
树转化为二叉树:将树中兄弟结点连成一条线(特点是根结点没有右孩子)
二叉树转化为树:把所有的右孩子变为兄弟
森林转化为二叉树:最右边的右子树变森林,其他右子树变兄弟
树的先根遍历与二叉树的前序遍历相同
书的中序遍历与二叉树的中序遍历相同
树中没有中序遍历,因为没有左右子树之分
1.各森林先转换成二叉树,再连接成一棵二叉树。
2.森林直接变兄弟,再转化为二叉树
图:由结点集合及结点间关系集合组成的一种数据结构
有向图
无向图
- 完全图:图中任意两个顶点都有一条边相连
若n个顶点的无向图有n(n-1)/2条边相连,称为无向完全图
若n个顶点的有向图有n(n-1)条边相连,称为有向完全图
子图
带权图
路径长度:
不带权图指路径上边的条数
带权图指路径上各边的权值之和
连通图:在无向图中,如果图中任意一对顶点都是连通的,则称此图是连通图
强连通图:在有向图中,每一对顶点i和j都有一条从i到j,和从j到i的路径
生成树:一个连通图中的最小连通子图,它含有图中全部n个顶点,但只有n-1条边
结点的度:与它相关联的边的条数,在有向图中,结点的度等于结点的入度和出度。
图的存储结构:邻接矩阵和邻接表
邻接矩阵A:带权图有n个结点,则用n行n列的矩阵A表示该图
无向图:顶点i的度= 第i行中1的个数
无向图的邻接矩阵是对称的
有向图:顶点i的出度 = 第i行中1的个数
顶点i的入度 = 第i列中的1的个数
顶点的度 =入度+出度
有向图的邻接矩阵可能是不对称的
邻接矩阵的优点:容易实现图的操作,如顶点的度、判断顶点之间是否有边、找顶点的邻接点
邻接矩阵的缺点: n个顶点需要n*n个单元存储边,空间效率为O(n*n),对稀疏图而言尤其浪费空间
图的邻接表:是一种顺序存储与链式存储相结合的存储方法,顺序存储部分用来保存图中顶点的信息,链式存储部分用来保存图中边的信息
无向图邻接表的空间效率:O(n+2e)
有向图邻接表的空间效率:O(n+e)
邻接表的优点:空间效率高,容易寻找顶点的邻接点
邻接表的缺点:有向图中计算顶点的度不方便
对于任意确定的图邻接矩阵是唯一的,但是,邻接表不是唯一的
空间复杂度不一样
邻接矩阵用于稠密图的存储
邻接表用于稀疏图的存储
图的遍历
从已给的连通图中某一顶点出发,沿着一边访问图中所有的顶点,且使每个顶点只被访问一次,就叫做图的遍历。
遍历的实质:找每个顶点邻接点的过程
图的深度优先遍历:
图的广度优先遍历:
生成树:是一个极小的连通子图,它含有图中的全部顶点,但只有n-1条边
如果无向连通图是一个带权图,那么它的所有生成树中必定有一棵边的权值总和为最小的生成树。称这棵树为最小生成树。
求最小生成树的方法:
(1)克鲁斯卡尔算法
将边归并,适用于求稀疏网的最小生成树
(2)普里姆算法
将顶点归并,与边数无关,适用于稠密网
排序和查找代码
三种基本排序(冒泡、简单插入、简单选择)
查找(顺序查找)