软件设计师——数据结构与算法基础

数据结构与算法基础设计到的内容

  • 数组与矩阵
  • 线性表(⭐⭐⭐)
  • 广义表
  • 树与二叉树(⭐⭐⭐)
  • 排序与查找(⭐⭐⭐)
  • 算法基础及常见的算法(⭐⭐⭐)
数组

一维数组 a[n]
a[i]的存储地址为 :a+i*len
二维数组 a[m][n]
a[m][n]的存储地址(按行存储)为:a+(i*n+j)*len
a[m][n]的存储地址(按列存储)为:a+(j*m+i)*len

例:已知五行五列的二维数组a中的各元素占两个字节,求元素a[2][3]按行优先存储的存储地址?
a+(2*5+3)*2

稀疏矩阵

如果一个矩阵中记录的元素大量为0,则被称之为稀疏矩阵。
在这里插入图片描述
上三角矩阵和下三角矩阵在存储时会将该矩阵按行优先或者时列优先压缩连续存储在一个数组中。

数据结构的定义

1.什么是数据结构?
数据结构就是计算机存储和组织数据的一种方式。不同的数据结构在存储和使用上会存在很大的差异。
2.数据逻辑结构分为线性结构(一条线)和非线性结构(树,图)。
分成不同的结构,主要还是为了进行分类探讨和研究。
从广义上来讲,图可以包含树,树可以包含线性结构。

线性表的定义

1.线性表 (a1 ,a2 ,a3 ,a4 …,an
2.线性表常见的两种存储结构:顺序存储结构(顺序表)和链式存储结构(链表)
(区别:链表不一定是连续的,通过指针指向下一个数据的地址)
在这里插入图片描述
链表的基本操作:

  • 单链表删除节点
  • 单链表插入节点
  • 双向链表删除节点
  • 双向链表插入节点
线性表——顺序存储与链式存储对比

在这里插入图片描述
这里涉及到的查找算法时间复杂度指的是顺序查找,不涉及到二分查找等查找算法。

线性表——队列与栈

在这里插入图片描述
在循环队列中为了清楚的区分队空和队满,选择少存一个元素,即当tail指向空间的下一个空间是head指向的空间时,表示队满。

例:元素按照a,b,c的次序进入栈,请尝试写出其所有可能的出栈序列。(注意:入栈指的并不是一次性全部入栈)

广义表

广义表是n个表元素组成的有序序列,是线性表的推广。
通常用递归的形式进行定义,记作:LS=(a0 ,a1 ,…,an

注:其中LS是表名,ai 是表元素,它可以是表(称作子表),也可以是数据元素(称为原子)。其中n是广义表的长度(也就是最外层包含的元素个数),n=0的广义表为空表;而递归定义的重数就是广义表的深度,直观地说,就是定义中所含括号的重数(原子的深度为0,空表的深度为1)。

基本运算:取表头head(Ls)和取表尾tail(Ls)
若有:LS1=(a,(b,c),(d,e))
表头head表示最外层的第一个元素,表尾tail是除了表头之外的所有元素
head(LS1)=a
tail(LS1)=((b,c),(d,e))

对应的LS1的长度为3 (最外层包含的元素的个数),深度为2 (表示括号的重数)

例如:有广义表LS1=(a,(b,c),(d,e)),要将其中的b字母去除,请写出具体的操作。
有操作:tail(LS1)=((b,c),(d,e))
能得出具体操作为:head(head(tail(LS1)))

树与二叉树

涉及到的内容:结点的度,树的度,叶子结点,分支结点,内部结点,父结点,子结点,兄弟结点,层次

结点的度:表示一个结点拥有的孩子结点数。
树的度:表示一个树中结点的度的最大值。

叶子结点:没有孩子结点的结点。
分支结点:有相应的分支
内部结点:除了根结点和叶子结点以外的都算内部结点。

父结点和子结点是一对相对关系。
兄弟结点:分为亲兄弟结点和堂兄弟结点,位于同一层次。

树与二叉树——完全二叉树

二叉树分为三大类
1.满二叉树:整个树是完整的,没有缺失的部分。
2.完全二叉树:除了最下一层外,是一个满二叉树,且最下一层满足从左向右补满的规则。
3.非完全二叉树:与完全二叉树的区别,就是不满足最下一层从左向右补满的规则。

二叉树的重要特性:
1.在二叉树的第i层上最多有2i-1 个结点(i>=1)
2.深度为k的二叉树最多有2k -1 个结点(i>=1)
3.对任何一棵二叉树,如果其叶子结点数为n0 ,度为2的结点数位n2 ,则n0 =n2 +1
4.如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到 [log2n] +1(向下取整)层,每层从左到右 ),则对任一结点i(1<=i<=n),有以下三种情况:
(1)如果i=1,则结点i无父结点,是二叉树的根,如果i>1,则父结点位[i/2](向下取整)——父结点
(2)如果2i>n,则结点i为叶子结点,无左结点,否则,其左子结点为结点2i ——左结点
(3)如果2i+1>n,则结点i无右子叶结点,否则,其右子结点是结点2i+1 ——右结点

树与二叉树——二叉树遍历

四种遍历方式

  • 前序遍历(根,左,右)
  • 中序遍历(左,根,右)
  • 后序遍历(左,右,根)
  • 层次遍历
    在这里插入图片描述
    层次遍历的结果为:1 2 3 4 5 6 7 8 (直接根据层次从上向下遍历)
    前序遍历的结果为:1 2 4 5 7 8 3 6
    中序遍历的结果为:4 2 7 8 5 1 3 6
    后续遍历的结果为:4 8 7 5 2 6 3 1
树与二叉树——反向构造二叉树

由二叉树的序列推出二叉树的情况。
例如:由前序序列ABHFDECG 和中序序列 HBEDFAGC 构造二叉树。
(由两种序列可以推导出二叉树,但必须要包括中序序列)

树与二叉树——树转二叉树

孩子结点——左子树结点
兄弟结点——右孩子结点
例如
在这里插入图片描述

树与二叉树——查找二叉树

查找二叉树(又被称为二叉排序树)的特点:
1.左孩子小于根
2.右孩子大于根

例如:
在这里插入图片描述

插入结点:
1.若该键值结点已存在,则不再插入
2.若查找二叉树为空树,则以新结点为查找二叉树
3.将要插入结点键值与插入后父结点键值比较,就能确定新结点是父结点的左子节点,还是右子结点

删除结点:
1.若待删除结点是叶子结点,则直接删除
2.若待删除结点只有一个子结点,则将这个子结点与待删除结点的父结点直接连接
3.若待删除的结点p有两个子结点,则在其左子树上,用中序遍历寻找关键值最大的结点s,用结点s的值代替结点p的值,然后删除结点s,结点s必属于以上1,2情况之一

注意:一般在插入和删除结点,在没有特殊说明的情况下,只需要满足在调整前后树的结构不要发生太大的变化且满足调整前后都为二叉排序树即可。

树与二叉树——最优二叉树(哈夫曼树)

哈夫曼树是一种工具,用于哈夫曼编码。
哈夫曼编码是一种编码方式,原理是将原始信息编码长度变得更短,从而节省存储空间和传输带宽,在多媒体领域用得较为广泛,且属于无损压缩的方式。

涉及到的内容:

  • 树的路径长度
  • 带权路径长度
  • 树的带权路径长度(树的代价)

哈夫曼树的目的:让整个树的带权路径长度最短。

例:假设有一组权值5,29,7,8,14,23,3,11,请尝试构造哈夫曼树。
(思路:依次递归选择权值最小的两个数进行组合)
带权路径长度最小为 271

树与二叉树——线索二叉树

为什么会有线索二叉树?
因为二叉树中很多结点的指针为空,为了利用这些空闲的指针,引出了线索二叉树。
线索二叉树的三种分类:

  • 前序线索二叉树
  • 中序线索二叉树
  • 后序线索二叉树

依次按照前中后序遍历的顺序,决定指针的指向。
在这里插入图片描述

树与二叉树——平衡二叉树

为什么提出平衡二叉树?
针对排序二叉树引出,因为排序二叉树中,同一种情况可能存在很多种二叉树的形式,造成查询的效率也就依次不同,这时引出了平衡二叉树。
二叉树的特点:

  • 任何结点的左右子树深度相差不能超过1
  • 每结点的平衡度只能为-1,0或1(平衡度=左子树深度-右子树深度)
图——基本概念

完全图

  • 在无向图中,若每对顶点之间都有一条边相连,则称该图为完全图。(complete graph)
  • 在有向图中,若每对顶点之间都有两条有向边相互连接,则称该图为完全图。
图的存储——邻接矩阵

用一个n阶方阵R来存放图中各结点的关联信息,其 矩阵元素Rij 定义为
Rij =1 表示 定点i 到顶点j有邻接边
Rij =0 表示 定点i 到顶点j无邻接边

图的存储——邻接表

首先把每个顶点的邻接顶点用链表示出来,然后用一个一位数组来顺序存储上面每个链表的头指针。(由数组和链表构成)
在这里插入图片描述

图——图的遍历

遍历方法分两种:
1.深度优先遍历
原理:(1)首先访问出发顶点V
(2)依次从V出发搜索V的任意一个邻接点W
(3)若W未访问过,则从该点出发继续深度优先遍历
类似于数的前序遍历

2.广度优先遍历
原理:(1)首先访问出发顶点V
(2)然后访问与顶点V邻接的全部未访问顶点W,X,Y…
(3)然后再依次访问W,X,Y…邻接的未访问的顶点

例:
在这里插入图片描述
该图的深度优先遍历顺序为 : 1 2 4 8 5 3 6 7
广度优先遍历顺序为:1 2 3 4 5 6 7 8 (相当于之前的层次遍历)

图——拓扑排序

我们把用有向边表示活动之间开始的前后关系。这种有向图称为用顶点表示活动网络,简称AOV网络。
例:
在这里插入图片描述
上述的拓扑序列可以为:02143567,01243657,02143657,01243567 等…
注意:这里的4需要2和1 都完成才能执行…

图的最小生成树——普里姆算法

什么叫图的最小生成树?
即为将图中一些线和边给去掉,留下的若干条边,能连接上所有的结点,保证留下的边是权值比较小的边,使得留下的边的权值总和是最小的情况,这样就成为最小生成树。(即能连上所有结点的边的条数最小为结点数-1)
在这里插入图片描述
普里姆的基本思想为:
1.分为两个集合A,B,集合A为已选中的结点,集合B中为未选中的结点
2.从任意结点出发,将该节点放入集合A中
3.找出集合A中的点与集合B中的点连接的边的最小的权值,然后把这个结点从集合B放入到集合A中
4.重复3,直至集合B中没有结点位置

图的最小生成树——克鲁斯卡尔算法

克鲁斯卡尔算法的基本思想:在保证不形成环路的情况下,依次选择权值最小的边。

算法基础——算法的特性

1.有穷性:执行有穷步后结束。
2.确定性:算法中每一条指令都必须有确切的含义,不能含糊不清。
3.有输入(0个或0个以上的输入)
4.有输出(至少满足一个或一个以上的输出)
5.有效性:算法的每个步骤都能有效执行并能得到确定的结果。例如:a=0,b/a就无效。

算法基础——算法的复杂度

时间复杂度:是指程序运行从开始到结束所需要的时间。
通常分析时间复杂度的方法是从算法中选取一种对于所研究的问题来说是基本运算的操作,以该操作重复执行的次数作为算法的时间度量。一般来说,算法种原操作重复执行的次数是规模n的某个函数T(n)。由于许多情况下要精确计算T(n)是困难的,因此引入了渐进时间复杂度再数量上估计一个算法的执行时间。其定义如下:
如果存在两个常数c和m,对于所有的n,当n>=m时有f(n)<=cg(n),则有f(n)=O(g(n))。也就是说,随着n的增大,f(n)渐进地不大于g(n)。例如,一个程序的实际执行时间为T(n)=3n3 +2n2 +n,则T(n) =O(n3)
常见的对算法执行所需时间的度量
O(1) < O(log2 n) < O(n) < o(nlog2 n) < O(n2) < O(n3) < O(2n)
注意:
1.时间复杂度以程序中最高的决定。()
2.O(1) ,O(2). …都默认记为O(1) ,常数确定个数的情况
3.对树的处理中很多都存在时间复杂度为O(log2 n)

空间复杂度:是指一个算法在运行过程中临时占用存储空间大小的度量。一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小。

查找——顺序查找

顺序查找的思想:将待查找的关键子key的元素从头到尾与表中元素进行比较,如果中间存在关键字key的元素,则返回成功,否则,则查找失败。
平均查找长度 为 ASL=(1+n)/ 2
时间负责度O(n/2) 属于 O(n)

查找——二分查找

二分查找的基本思想:将待查值k依次与查找区的中间元素比较(当不等,每次比较查找区缩小一半,直至查找区只剩一个元素,仍然不相等时,则返回查找失败)
二分查找的必要前提:有序序列
注意:
1.取中间元素:(low + high) /2 是向下取整
2.比较时不再进行比较中间元素 即 (low + high) /2 -1 或者 (low + high) /2 +1

折半查找在查找成功时关键字的比较次数最多为[log2n]+1 次
折半查找的时间复杂度为O(log2 n )
(相比顺序查找在数据越多的时间,优势越明显)

查找——散列表

散列表查找的基本思想是:已知关键字集合U,最大的关键字为m,涉及一个函数Hash,它以关键字为自变量,关键字的存储地址为因变量,将关键字映射到一个有限的,地址连续的区间T[0...n-1](n<=m)中,这个区间就称为散列表,散列查找中使用的转换函数称为散列函数。

例:记录关键码(3,8,12,17,9),取m=10(存储空间为10),p=5,散列函数h=key%p。
3%5=3 8% 5= 3
3和8存储地址冲突,存储到下一个单元(线性探测法)

有两种解决冲突的方法:
1.线性探测法
2.伪随机数法

排序

排序的概念
可以分为稳定和不稳定排序(区分:在排序过程中某些数的前后顺序不断发生改变)
也可以分为内排序和外排序

排序方法分类
1.插入类排序

  • 直接插入排序
  • 希尔排序
    2.交换类排序
  • 冒泡排序
  • 快速排序
    3.选择类排序
  • 简单选择排序
  • 堆排序(效率高,但过程复杂)
    4.归并排序
    5.基数排序
排序——直接插入排序

直接插入排序原理:即当插入第i个记录时,R1 ,R2 ,…,Ri-1 均已排好序,因此,将第i个记录Ri 依次与Ri-1,…,R2 ,R1 进行比较,找到核实的位置插入。算法很简单明了,但速度很慢。

排序——希尔排序

希尔排序(Shell)排序的原理:先取一个小于n的整数d1 作为第一个增量,把文件的全部记录分成d1 个组。所有距离为d1 的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后取第二个增量d2 < d1 重复上述的分组和排序,直至所取的增量dt =1(dt <dt-1 <…<d2 <d1 ),即所有记录放在同一组中进行直接插入排序为止。该方法实际上是一种分组插入方法。

希尔排序效率比直接插入排序要高。(数据越多,效率越明显)

排序——直接选择排序

直接选择排序的原理:首先在所有记录中选出排序码最小的记录,把它与第1个记录交换,然后在其余的记录内选出排序码最小的记录,与第2个记录交换…依次类推,直到所有记录排完为止。

排序——堆的概念

设有n个元素的序列{K1 ,K2 ,…,Kn },当且仅当满足下述关系之一时,称之为堆。
(1)ki <= k2i 且 ki <= k2i+1 小顶堆(所有的孩子结点都比父结点大)
(2)ki >= k2i 且 ki >= k2i+1 大顶堆

建堆的思想:首先按完全二叉树的方式,把所有结点排进堆中如图1.1,然后对所有非叶子结点,从最后一个非叶子结点开始进行调整。(依次和最大的孩子结点作比较——大顶堆的情况)
例1:
在这里插入图片描述
例2:(输出堆顶元素,将最后一个元素放到堆顶位置)
在这里插入图片描述
针对情况:依次选出最大或者最小值。

排序——冒泡排序

冒泡排序的基本思想:通过相邻元素之间的比较和交换,将排序码较小的元素逐渐从底部移向顶部。由于整个排序的过程就像水底下的气泡一样逐渐向上冒,因此称之为冒泡算法。

排序——快速排序

快速排序采用的时分治法,其基本思想时将原问题分解成若干个规模更小但结构与原问题相似的子问题。通过递归地解决这些子问题,然后再将这些子问题的解组合成原问题的解。

原理:
1.选择起始第一个数作为基准
2.开始和结束位置分别给定两个指针
3.依次与基准值作比较(直到两指针到同一位置结束比较,此时完成一轮基准点的位置排序)
快速排序的两个重要步骤:
第一步,再待排序的n个记录中任取一个记录,以该记录的排序码为准,将所有记录都分为两组,第1组都小于该数,第2组都大于该数。
第二步,采用相同的方法对左右两组分别进行排序,直到所有记录都排到相应的位置为止。

排序——归并排序

归并也叫合并,是将两个或两个以上的有序子表合并成一个新的有序表。若将两个有序表合并成一个有序表,则称为二路合并。
合并的过程是:比较A[i]和A[j]的排序码大小,若A[i]的排序码小于等于A[j]的排序码,则将第一个有序表中的元素A[i]复制到R[k]中,并令i和k分别加1;如此循环下去,直到其中一个有序表比较和复制完,然后再将另一个有序表的剩余元素复制到R中。

基本思想:先将一个无序表分成若干个子表(一般元素个数为2,4,8…),然后对子表单独进行排序,然后再将相邻子表合并,将字表A中最小的值和子表B中最大的进行比较…排序形成一个新的子表…直至整个表为一张有序表

排序——基数排序

基数排序是一种借助多关键字排序思想对单逻辑关键字进行排序的方法。基数排序不是基于关键字比较的排序方法,它适合于元素 很多而关键字较少的序列。基数的选择和关键字的分解是根据关键字的类型来决定的,例如关键字是十进制数,则按个位,十位来分解。

例:
在这里插入图片描述

排序——排序算法的复杂度比较

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Q672405097/article/details/89785385