数据结构知识点 面试问答
文章目录
- 数据结构知识点 面试问答
0.简述
数据结构知识点总结,无代码。
本文为博主纯手打,如有文字错误或理解错误,请批评指正,将在第一时间修改。
1.请简述数据结构
1.1 概念
数据结构是指数据元素的集合及元素间的相互关系和构造方法。
1.2 作用
数据结构是程序设计的重要基础,可以为应用所涉及的数据选择适当的逻辑结构、存储结构及其相应的操作方法,为提高利用计算机解决问题的效率服务。
1.3 逻辑结构
逻辑结构是指数据元素之间的逻辑关系,即从逻辑关系上描述数据。
逻辑结构主要分为线性结构和非线性结构,有集合结构、线性结构(1对1)、树形结构(1对多)和图形结构(多对多)。
1.4 物理结构/存储结构/映像
存储结构是指数据结构在计算机中的表示。数据的存储结构是逻辑结构用计算机语言的实现,依赖于计算机语言。
存储结构主要有顺序存储、链式存储、索引存储、散列存储(通过关键字得出元素物理地址)。
2.简述算法
2.1 概念
是指解题方案的准确而完整的描述,是一系列解决问题的步骤。
2.2 基本特性
- 有穷性。有限步骤。
- 确定性。每一步都有明确定义。
- 可行性。每一步都要在有限时间内完成。
- 输入。
- 输出。
2.3 算法分析的基本概念
是对一个算法需要多少是计算时间和存储空间作定量的分析。
-
空间复杂度。
衡量算法随问题规模增大,算法所需空间的大小。
-
时间复杂度
衡量算法随问题规模增大,算法执行时间增长的快慢。
时间复杂度总是考虑最坏情况下的时间复杂度,以保证算法的运行时间不会比它更长。
时间复杂度补充概念:
- n:称为问题的规模
- f(n):基本操作执行的次数。
- T(n):重复执行的次数,即所有语句的频度之和。
- O(n):T(n)的数量级。
2.4 算法分析的基本方法
-
递推法。
把问题分成多步,找出相邻几步的关系,从而达到目的。
-
贪心法。
一般可以快速得到解。每一步都以当前情况作为基础作当前最优选择,不考虑整体的情况。
-
分治法。
设计思想是将一个难以直接解决的大问题分解成一些规模较小的相同问题,分而治之。典型实例为归并排序。
分支算法在每一层递归上的步骤:分解、求解、合并。
分治与递归经常同时应用于算法设计中。附:
递归:指子程序/函数直接调用自己或通过一系列调用语句简介调用自己的方法。递归的两个基本要素:
1. 边界条件/递归窗口:确定递归到何时终止。
2. 递归模式/递归体 :大问题如何分解为小问题的。 -
动态规划。
通常用于求解具有某种最优性质的算法。
与分治法类似,将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。不同的是,动态规划中的子问题往往不是独立的。
动态规划步骤:(了解)
1. 对最优解的性质结构特征进行刻画。
2. 递归定义最优解的值。
3. 自底向上的方式计算最优值。
4. 根据最优值的信息构造一个最优解。(可省略)
4.线性表
4.1 概念
线性表是n个元素的有限序列,是最简单、最基本也是最常用的一种线性结构。
4.2 链表
-
概念
链表是一种物理存储单元上非连续的存储结构,数据元素间的逻辑顺序通过链表中的指针链接次序实现。
-
分类
- 单向链表(带不带头结点)
- 双向链表
- 循环链表
- 静态链表(借助数组实现)
-
好处/优点
相比数组,链表可以动态申请空间,无需预先知道数据大小,可以充分利用计算机内存空间。
-
缺点
失去数组随机存取的优点,而且指针域也需要占用空间。
-
相关算法
-
如何合并两个有序链表为一个新的有序链表
方法1:新建链表A,分别遍历两个链表,每次对比,将小的结点接在A后面。
方法2:直接合并两个链表,再采用冒泡排序或其他排序方式对合并后的链表进行排序。
-
如何反转链表
方法1:利用3个临时指针,将原链表中的结点两两交换位置,遍历到结尾即可。
方法2:遍历原链表,将每个结点利用头插法的方式插到新链表中,实现链表的反转。
-
如何从尾到头打印链表
方法:顺序遍历原链表,将每个结点的值存入栈中。遍历完后,依次出栈即可。
-
如何知道一个链表是否有环
方法:使用快慢指针思路,快指针一次走两步,慢指针一次走一步,如果两个指针在遇到空指针前先遇到(即相等),则说明有环。
-
如何找到一个链表的倒数第K位结点
方法:使用快慢指针思路,快指针先走K步,然后快慢指针同时走(一次一步),当快指针到达结尾时,此时慢指针即为所求。
-
如何找到链表的中间结点
方法:使用快慢指针思路,快指针一次走两步,慢指针一次走一步,当快指针到达结尾时,此时慢指针即为所求。
-
4.3 顺序存储和链式存储的区别
(可从数组和链表的概念、逻辑结构、物理结构和相关操作上回答)
顺序存储是指用一组地址连续的存储单元依次存储线性表中的数据元素,元素关系无需占用额外的空间存储,一般使用数组来实现。优点是可以随机存取表中的元素,缺点是插入和删除操作需要移动元素时需要先挪出空位置再插入或删除元素。
而链式存储是通过用指针连接起来的结点来存储数据元素,存储各元素的结点地址不要求连续,所以需要同时存储元素之间的逻辑关系。优点是在插入或删除数据元素时可以直接插入,缺点是访问元素时需要遍历链表。
5.请简述堆栈
5.1 概念
栈是只能通过它的一端来实现数据存储和检索的一种线性数据结构。
5.2 存储结构
-
顺序栈
使用一组连续的存储单元(数组)依次存储自栈顶到栈底的数据元素,同时附设指针top指示栈顶元素的位置。
顺序栈需要预先定义/申请栈的存储空间,即栈空间有限。
共享栈:利用栈底相对位置不变的特性,让两个顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
-
链栈
使用链式存储的栈。
链表的头指针是栈顶指针。
5.3 应用
-
表达式求值。
利用后缀表达式/逆波兰式,利用两个栈,一个操作数栈,一个运算符栈。遍历逆波兰式,遇到操作数入栈,遇到运算符将两个操作数出栈运算后再入回操作数栈,直至遍历完逆波兰式。
-
括号匹配。
括号序列。遇到左括号入栈,遇到右括号出栈并检查是否与左括号类型匹配。当遍历完序列后栈中没有左括号说明匹配成功。
-
将一些递归算法转换为非递归算法。
6.请简述队列
6.1 概念
队列是一种先进先出的线性表,尾进头出。
6.2 存储结构
- 顺序队列:利用一组地址连续的存储单元(数组)存放队列中的元素,常用循环队列。
- 链队列:队列的链式存储,队列的头指针指向队头(前的头结点)处。
6.3 应用(需要排队的场合)
- 操作系统中处理打印任务的打印队列
- 舞会排队问题
7.请简述串
7.1 概念
串是一种特殊的线性表,其数据元素为字符。
7.2 存储结构
- 顺序存储结构。用一组地址连续的存储单元来存储串值的字符序列。
- 链式存储结构。用链表存储串中的字符时,每个结点中可以存储一个字符,也可以存储多个字符,此时要考虑存储密度问题。结点大小的选择会直接影响对串的处理效率。
7.3 相关算法模式匹配算法:
(模式串,即子串)
-
暴力破解算法(Brute Force,BF算法)
- 思想:从主串的第一个字符起与模式串的第一个字符比较,若相等,则继续逐一对字符进行后续的比较,否则从主串第二个字符起与模式串的第一个字符重新比较,直到模式串中每个字符依次和主串中一个连续的字符序列相等时为止。
-
KMP算法(Knuth-Morria-Pratt算法)
-
简介
对暴力破解算法的改进,消除了主串指针的回溯,即利用已经得到的“部分匹配”结果将模式串向右“滑动”尽可能远的距离,再继续进行比较。
-
思想
当模式串中的字符(第x个)与主串中相应的字符(第y个)不相等时,因为其前x个字符已经获得了成功的匹配,所以若模式串中的“第0个到第k-1个”与“第k个到第x-1个”相同(即模式中一部分前缀和一部分后缀相同),此时可令模式中第k个字符与主串中第y个字符比较。(而不需要再从模式串的第1位与主串开始比较的下一位比较)
-
实现
在KMP算法中,依据模式串的next函数值实现子串的滑动。
-
求next数组
第一位-1,第二位0,第三位开始看前面后方有没有和前方往后重复的,重复x位就填x。例如
模式串 a b a b a b b next[] -1 0 0(前面后缀重复前缀0位) 1(重复1位A) 2(重复2位AB) 3(重复3位ABA) 4(重复4位ABAB) -
遍历主串(同时依据next数组)
依次匹配,如果遇到模式串y位与主串x位不相等,则将主串x位与模式串 next[y] 位比较,如果遇到j==0,即next[0] == -1,则x++,y++,继续比较,重复直至结束。
-
-
8.请简述数组
8.1 概念
数组是定长线性表在尾数上的拓展,即线性表中的元素又是一个线性表,每个数据元素类型相同、结构一致。
8.2 存储结构
适合采用顺序存储结构。
其中二维数组的存储结构可分为以行为主序和以列为主序的两种方法(C语言为以行为主序)。
9.请简述矩阵
9.1 简介
一般指二维数组。在数据结构中,主要讨论如何在节省存储空间的情况下使矩阵的各种运算能高效运行。
9.2 特殊矩阵
元素(或非0元素)的分布有一定的规律的矩阵。
可以考虑压缩在一维数组中,并建立起每个非0元素在矩阵中的位置与其在一维数组中的位置之间的对应关系。
常见的特殊矩阵有:
-
对称矩阵
-
概念
n阶方阵A[1…n][1…n]中任意一个元素 a[i][j] 都有 a[i][j] = a[j][i],其中可分为上三角区、主对角线和下三角区。
-
压缩
将A[1…n][1…n]存放在一维数组B[n(n + 1) / 2]中。
-
-
三角矩阵
-
概念
上三角矩阵中,下三角区所有元素均为同一常量。下三角矩阵反之。
-
压缩
上三角矩阵中,只需存储主对角线、上三角区上的元素和下三角区的常量一次,可将其压缩存储在B[n(n+1)/2+1]中。下三角矩阵类似。
-
-
对角矩阵/带状矩阵
-
概念
为三对角矩阵:对于n阶方阵A中的任一元素a[i][j],当|i - j| > 1时,有a[i][j]=0。
-
压缩
将3条对角线上的元素按行优先方式存放在一维数组B中,且a[1][1]存放于B[0]中。
-
9.3 稀疏矩阵
-
概念
非0元素个数远远少于0元素的个数,且非0元素的分布没有规律的矩阵。
-
压缩
使用存储非0元素值及其位置的三元组(位置x,位置y,值)来压缩稀疏矩阵
-
三元组存储结构
1.顺序存储结构。三元组顺序表。
2.链式存储结构。常用十字链表。
10.请简述广义表
10.1 概念
广义表是线性表的推广,由0个或多个单元素或子表组成的有限序列。
10.2 结构特点
广义表与线性表的区别是:线性表的元素都是结构上不可分的单元素,而广义表的元素可以是单元素,也可以是再有结构的表(可表中有表)。
广义表可以被其他广义表所共享。
广义表可以是一个递归的表(表中元素可以是自己)。
10.3 存储结构
广义表难以用顺序存储结构表示,通常采用链式存储结构。
11.请简述树
11.1 概念
树的定义是递归的,也就是一棵树由若干棵子树构成,而子树又由更小的子树构成。树结构中的一个数据元素可以有两个或以上的直接后继元素。树可以用来描述客观世界中广泛存在的层次结构关系。
11.2 存储结构
-
双亲表示法
-
概念
使用用一组地址连续的单元(数组)存储树的结点,并在每个结点中指出其双亲结点在该存储结构(数组)的位置(即下标)。
-
特点
求指定结点的双亲和祖先都十分方便,但求指定结点的孩子及后代则需要遍历整个数组。
-
-
孩子表示法
-
概念
在存储结构中用指针指示出结点的每个孩子,为树中每个结点孩子建立其所有孩子结点构成的单链表,即n个结点的树有n个单链表。
(可以考虑将双亲表示法和孩子表示法结合起来,形成树的双亲孩子表示结构。)
-
特点
便于查找每个结点的子孙,但要查找双亲则可能需要遍历所有的链表。
-
-
孩子兄弟表示法/二叉链表表示法
-
概念
在链表的结点中设置两个指针域分别指向该结点的第一个孩子和下一个兄弟。
可用于树、森林与二叉树之间的转换。
-
特点
左子树为孩子,右子树为自己的兄弟。
-
11.3 遍历
- 树的先根遍历。按“根-左到右”的顺序进行遍历。
- 树的后根遍历。按“左到右-根”的顺序进行遍历。
12.请简述森林
12.1 概念
森林是很多棵树组成的集合。
12.2 遍历
- 先序遍历森林。按顺序依次遍历森林中的树,其中的树使用先根遍历方式。
- 中序遍历森林。按顺序依次遍历森林中的树,其中的树使用后根遍历方式。
12.3 转换
- 树、森林 → 二叉树。利用孩子兄弟表示法。
- 二叉树 → 树、森林。反向孩子兄弟表示法。
13.请简述二叉树
13.1 概念
二叉树是n个结点有限集合,它可以是空树,也可以由一个根结点及两颗不相交的且分别成为左、右子树的二叉树所组成。二叉树与树都具有递归性质。
13.2 树与二叉树的区别
两者虽然有很多联系,但它们是不同的概念。
- 二叉树中结点的子树要区分左子树和右子树。即使在结点只有一颗子树的情况下,也要明确指出该子树是左子树还是右子树。
- 二叉树结点最大度为2,而树中不限制结点的度数。
13.3 结构的逻辑性质/特征
- 二叉树第 k 层上有最多2^(k-1)个结点。
- 高度为 k 的二叉树最多有(2^k) - 1个结点。
- 度为0的结点数 = 度为2的结点数 + 1
13.4 相关分类
-
满二叉树
深度为k的二叉树有(2^k) - 1 个结点的二叉树
-
完全二叉树
除了最后一层,其余各层都满的,且最后一层是叶子结点必须从左到右放置的。
-
线索二叉树
-
前言
二叉树的遍历实质上是对一个非线性结构进行线性化的过程,遍历使得每个结点在这些线性序列中有且仅有一个直接前驱和直接后驱。但二叉链表存储结构中,只能找到一个结点的左、右孩子,不能直接得到结点在任一遍历序列中的前驱和后,这些信息只能在遍历的动态过程中才能得到,线索二叉树来保存这些动态过程得到的信息。
-
建立过程(线索化)
-
使用线索链表建立,其中结点为:
左tag Lchild data Rchild 右tag 左tag:0表示为Lchild为左孩子,1表示为直接前驱(线索)。
右tag:0表示为Rchild为右孩子,1表示为直接后继(线索)。
-
建立过程略微复杂(略)
-
-
13.5 存储结构
-
顺序存储结构
用一组地址连续的存储单元存储二叉树的结点,把结点排成一个适当的线性序列,并且结点在这个序列中的相互位置能反映出结点之间的逻辑关系。
完全二叉树使用顺序存储结构既简单又节省空间(例如堆排序中堆的表示),一般二叉树不宜采用。
-
链式存储结构
用二叉链表或三叉链表来存储二叉树,链表的头指针指向二叉树的根节点。
13.6 遍历操作
- 先序遍历。按“根-左-右”的顺序进行遍历。
- 中序遍历。按“左-根-右”的顺序进行遍历。
- 后序遍历。按“左-右-根”的顺序进行遍历。
- 层序遍历。一层一层遍历。
13.7 实际应用
-
最优二叉树/哈弗曼树
-
定义
一类带权路径长度最短的树。
-
相关概念
路径长度 = 从树根到每个叶子之间的路径长度之和。
结点A的带权路径长度 = A到树根之间的路径长度与该结点权值的乘积。
树的带权路径长度WPL = 树中所有叶子结点的带权路径长度之和。
-
建立
- 根据给定的n个权值构成二叉树的集合F。(最后最优二叉树结点数目=2×n-1)
- 在F中选两棵权值最小的树作为左右子树构造一棵新的二叉树,新二叉树权值为左右子树权值之和。(哪个是左子树右子树没规定,随意)
- F中删除这两个节点,同时将新二叉树加入到F中。
- 重复2、3步。
-
应用(哈弗曼编码)
在数据传送时,信息表现为0和1的二进制形式。对每个字符编制相同长度的二进制码成为等长编码,而为了提高传输的速度,可以采用变长编码方式。同时,必须要保证编码不存在二义性(任意字符编码都不是其它字符编码的前缀)。哈夫曼编码就是符合上述要求的编码方式,通过采用自底向上的形式构造哈夫曼树。按照字符的概率分配码长,实现平均码长最短的编码。
-
-
二叉查找树/二叉排序树/二叉搜索树(第15部分查找有详解)
14.请简述图
14.1 概念
图G是由图中顶点的非空有限集合V和图中边的有限集合E构成的二元组,记作G=(V,E)。
网:边带权值的图。
14.2 分类
- 有向图/无向图。
- 完全图:图中每个顶点与除自身外的其他顶点都有关系的图。
- 稀疏图/稠密图:相对而言。
- 连通图/连通分量:所有结点连通的无向图叫作连通图。无向图G的极大连通子图称为G的连通分量。
- 强连通图/强连通分量:所有结点双向连通的有向图叫作强连通图。有向图G的极大连通子图称为G的强连通分量。
14.3 逻辑结构特点
图中的顶点之间不存在全序关系(即无法将图中的顶点排列成一个线性序列),任何一个顶点都可以被看作第一个顶点。此外,任一顶点的邻接点之间也不存在次序关系。
14.4 存储方法
-
邻接矩阵
用一个矩阵表示图中顶点之间的关系,有关系则值为1,反之为0。
无向图的邻接矩阵是对称的。
-
邻接表
为图中每个顶点建立一个单链表,第 i 个单链表中的结点表示衣服于顶点Vi的边。
邻接链表中的结点有表头结点和表结点/边结点两种类型。
-
表头结点。组成顶点数组。
结点名 邻接节点 -
表结点/边结点
自身在 顶点数组 中的位置 表头结点邻接的下一个结点
-
14.5 图的遍历
-
深度优先遍历(DFS)
类似于树的先根遍历,在第一次经过一个顶点时就进行访问操作。
-
广度优先遍历(BFS)
从某顶点v出发,在访问了v之后再依次访问各个未被访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直到图中所有已被访问的顶点的领结点都被访问到。
14.6 最小生成树
-
概念
权值最小的生成树。
生成树:即图的最小联通子图。在图的生成树中任意加一条边都必然形成回路。可以利用深度优先遍历和广度优先遍历的二到深度优先生成树和广度优先生成树。
-
建立
-
普利姆(Prim)算法
时间复杂度为O(n²),与图的边数无关,适合求稠密网的最小生成树。
- 连通网N=(V,E),N上最小生成树边的集合TE={ },顶点集合U={u0}。
- 重复:在所有 u∈U,v∈V-U 的边 (u,v)∈E 中找一条权值最小的边 (u0,v0),把这条边并入集合TE,同时将v0并入集合U,直到U=V时(即顶点全部取得)为止。
- 此时TE中必然有n-1条边,**T=(V,{TE})**为N的最小生成树。
-
克鲁斯卡尔(Kruskal)算法
时间复杂度为O(eloge),与图中的顶点数无关,适合求稀疏网的最小生成树。
- 连通图N=(V,E),令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{ }),图中每个顶点自成一格连通分量。
- 重复:从E中选择权值最小的边,若该边两端的顶点落在T中不同的连通分量上,则将此边加入T中,否则舍去此边,继续重复。直到T中所有顶点都在同一连通分量上为止。
-
14.7 拓扑排序
-
概念
将AOV网中的所有顶点排成一个线性序列的过程,并且该序列满足:网中从顶点A到B有一条路径,则在该线性序列中,顶点A必然在顶点B之前。
相关概念:
AOV网:不带权有向无环图,用顶点表示活动,边表示活动(顶点)发生的先后关系。
-
特点
-
拓扑排序的时间复杂度O(n + e)。
-
有向无环图中可以利用DFS进行逆拓扑排序。
-
-
求解过程
- 在AOV网中选择一个入度为0(没有前驱)的顶点且输出它。
- 从网中删除该顶点及与该顶点有关的所有弧。
- 重复1、2步,直到网中不存在入度为0的顶点为止。
14.8 最短路径
-
概念
给定带权有向图G和源点v0,求从v0到G中其余各顶点路径长度最短的那条路径叫做最短路径。
-
求解过程
-
迪杰斯特拉(Dijkstra)算法-O(n²)
设图中已找到最短路径的顶点集合S和图中剩余顶点集合T。初试时,S只有源点V0,然后不断从T中选取到顶点V0路径长度最短的顶点V并入到S中。S每并入一个新顶点V,都要修改V到T中顶点的最短路径长度值。不断重复此过程,直到T的顶点全部并入到S中为止。
-
弗洛伊德(Floyd)算法-O(n³)
如果求图中任意一对顶点间的最短路径,通常使用该算法。
具体过程略微复杂(略)
-
14.9 关键路径
-
概念
从源点到汇点的路径中,长度最长的路径(即完成工程时间最短的路径)称为关键路径。
-
相关概念:
- AOE网:带权的有向无环图。
- 关键活动:关键路径上所有活动。如果任何一项关键活动没有按期完成,就会影响整个工程的进度,而缩短关键活动的工期通常可以缩短整个工程的工期。
-
建立
-
在n个顶点的AOE网中,顶点v0表示源点、顶点v(n-1)表示汇点。则
-
事件的最早发生时间ve(j)。
从源头v0到vj的最长路径长度(时间)。这个时间决定了所有从vj发出的弧所表示的活动能够开工的最早时间。ve(j) = max{ve(i) + dut(<i,j>)}(ve(0) = 0)
-
顶点时间的最晚发生时间vl(i)。
不推迟整个工期的前提下,时间vi的最晚发生时间。
-
活动a(k)的最早开始时间e(k) 。
活动a(k)最早可开工时间。e(k) = l(k)。
-
活动a(k)的最晚发生时间l(k)。
在不推迟整个工期的前提下,该活动的最晚开始时间。l(k) = vl(j) - dut(<i,j>)。
-
-
对于活动a(k)来说,若e(k)=l(k),则表示活动a(k)是关键活动。
说明该活动最早可开工时间与整个工程计划允许该活动最晚的开工时间一致,施工期一点也不能拖延,若a(k)不能按期完成,则可能使整个工程提前完工。
-
15.请简述查找
15.1 概念
查找是一种常用的基本运算。
查找表是指同一类数据元素构成的集合。
15.2 静态查找表
查询查询某个特定数据是否在表中或检索数据的各种属性的表。
-
顺序查找
从表的一端开始,逐个比较查找。
-
折半查找/二分查找
-
过程:
在表中元素已经按关键字递增方式排序的情况下。首先将待查元素的关键字与表中间位置的关键字进行比较,若相等,则查找成功;如果关键字大于中间位置的值,则说明待查记录只可能在后半个子表中,反之则在前半个子表。下一步再在子表中查找,直到成功/失败。
-
特点:
折半查找比顺序查找的效率要搞,但它要求表必须是有序的。
折半查找适用于表不易变动,且又经常进行查找的情况。
-
-
分块查找/索引顺序查找
-
简介
对顺序查找方法的改进,效率介于顺序查找与折半查找之间。
-
使用过程
建立过程:首先将表分成若干块,每一块的关键字不一定有序,但块之间是有序的,即后一块中所有记录的关键字均大于前一块中最大的关键字。此外,还需建立一个“索引表”(存放每块的最大关键字的起始位置(下标)),索引表按关键字有序。
查找过程:先在索引表确定待查记录所在的块(折半查找),再在块内顺序查找具体值(顺序查找)。
-
特点
因为分块查找实际上是两次查找的过程,因此其平均查找长度应该是两次查找的平均查找长度(索引查找与块内查找)之和。
-
15.3 动态查找表
若需要在查找表中插入不存在的数据元素,或者从查找表中删除已存在的某个数据元素,则称此类查找表为动态查找表。
-
二叉排序树/二叉查找树/二叉搜索树
-
概念
它或者是一棵空树,或者是具有以下性质的二叉树:
- 如果它的左子树非空,则左子树上所有结点的值均小于根节点的值。
- 如果它的右子树非空,则右子树上所有结点的值均大于根节点的值。
- 左、右子树本身是二叉排序树。
-
相关算法
-
查找
二叉排序树非空时,将给定值与根节点的关键字值比较,若相等,则查找成功;若不相等,则当根节点的关键字值大于给定值时,下一步到根的左子树中进行查找,否则到根的右子树进行查找。
-
插入
每读入一个元素,建立一个新结点。
若二叉树非空,则将新结点的值与根节点值比较,如果小于根节点的值,则插入到左子树中,否则插入到右子树中;
若二叉树为空,则新结点作为二叉树的根节点。
-
删除
- 当删除的结点为叶子结点。直接删除。
- 当删除的结点的子树只有一侧。将子树成为其双亲(接替自己的位置)。
- 当删除的结点的左右子树都在。将左/右子树的最右/左下角的结点接替自己的位置
-
-
特点
分析搜索二叉树的查找过程可知,只有在树的形态比较均匀的情况(AVL树)下,查找效率才能达到最佳。
-
-
平衡二叉树(AVL树)
-
概念
它或者是一棵空树,或者是具有下列性质的二叉树:
- 其左右子树的高度之差绝对值不超过1,即平衡二叉树上所有结点的平衡因子只可能是-1、0 和 1。
- 其左右子树都是平衡二叉树。
-
保持平衡的基本思路
每当在二叉排序树中插入一个结点时,首先检查是否因插入破坏了平衡。若是,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子树中结点之间的关系,以达到新的平衡。
(最小不平衡子树:离插入结点最近且以平衡因子的绝对值大于1的结点作为根的子树)
-
相关操作
-
插入
- LL型单向右旋平衡处理。
-
RR型单项左旋平衡处理。
- LR型先左后右双向旋转平衡处理。
- RL型先右后左双向旋转平衡处理。
-
删除
平衡二叉树上删除操作比插入操作更复杂。
当待删结点的两子树都不空,就用该结点左子树上的中序遍历的最后一个结点(或其右子树上的第一个结点)替换该结点,将情况转化为待删除的结点只有一个子树后再进行处理。当一个结点被删除后,从被删结点到树根的路径上所有结点的平衡因子都需要更新。(对于每一个位于该路径上的平衡因子为±2的结点来说,都要进行平衡处理)
-
-
-
B树
-
概念
一棵m阶的B树,或为空树,或为满足下列特性的m叉树:
- 树中的每个结点最多有m棵子树
- 若根结点不是叶子结点,则最少有两棵子树。
- 除根结点之外的所有非终端结点至少有「m/2」棵子树
- 所有的非终端结点中包含下列数据信息:关键字、指向子树根节点的指针。
- 所有的叶子结点都出现在同一层次上,并且不带信息(可以看做是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)
-
操作
-
查找过程
首先在根节点所包含的关键字中查找给定的关键字,若找到则成功返回;否则确定待查找的关键字所在的子树并继续进行查找,知道查找成功或查找失败(指针为空)时为止。
-
插入/删除
运算复杂,涉及结点“分裂”及“合并”问题。此处省略
-
-
-
哈希表
-
概念
通过计算一个以记录的关键字为自变量的函数(哈希函数)来得到该记录的地址的表。
-
哈希函数(哈希表的建立)
对于哈希函数的构造需要解决两个问题:函数应该是一个压缩映像函数(压缩性)、函数应该较好的散列性(尽量减少冲突)。
- 直接定址法。根据关键字直接定位下标。
- 数字分析法。根据所有关键字的每一位的数字分布情况具体分析确定函数。
- 平方取中法。取关键字平方后中间几位或其组合作为散列地址。
- 折叠法。将关键字断开,相加,得地址。
- 随机数法。使用随机数。
- 除留余数法。假设列长A,选择不大于A的B,用B去 除关键字,除去的余数为地址。即H(key) = key % B。
-
处理散列冲突的方法
一般情况下,冲突只能尽可能减少而不能完全避免。
-
开放地址法。H( i ) = ( H( key ) + d( i ) ) % m。
-
线性探测法
- d( i ) = 1,2,3,…。
- 容易产生聚集现象,但一定有位置。
-
二次/平方探测法。
- d( i ) = 1²,(-1)²,2²,(-2)²,3²,…。
- 不一定会有位置。
-
伪随机数法。
- 不一定会有位置。
-
链地址法/拉链法。
在查找表的每一个记录中增加一个链域,链域中存放下一个具有相同哈希函数值的记录的存储地址。
利用链域,就把若干个发生冲突的记录链接在一个链表内。
当链域的值为NULL时,表示已没有后续记录。
-
再哈希法(了解)
H( i ) = RH(i)(key) (i = 1,2,…,k)
RH(i)是不同于H(i)的哈希函数,即在同义词发生地址冲突时计算另一个哈希函数地址,直到冲突不再发生。
这种方法不易发生聚集现象,但是会增加计算时间。
-
建立公共缓冲区
无论由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入公共溢出区。
-
-
查找元素
查找时,用与存入元素时相同的哈希函数和冲突处理方法计算得到待查记录的存储地址,然后到相应的存储单元获得有关信息再判定查找是否成功。
查找过程中需要和给定值进行比较的关键字的个数取决于下列3个因素:哈希函数、处理冲突的方法和哈希表的填装因子。
装填因子:α = 表中装入的记录数/哈希表的长度。标志着哈希表的装满程度。α越小,表中填入的记录相对少,装填时发生冲突的可能性越小,查找时比较的关键字也少。
-
15.4 查找方法的性能比较
查找方法 | 时间复杂度 |
---|---|
顺序查找 | O(n) |
折半查找/二分查找 | O(log₂n) |
分块查找/索引顺序查找 | O(n+log₂n) |
二叉排序树 | O(n) |
哈希表/散列表 | 性能与装填因子有关 |
16.请简述内部排序
16.1 直接插入排序
在插入第i个记录时,R1、R2、…、Ri-1已经排好序,这时将Ri的关键字Ki依次与关键字Ki-1、Ki-2等进行比较,从而找到应该插入的位置并将Ri插入,插入位置及其后的记录依次向后移动。
16.2 折半插入排序
对插入排序算法的改进,即在直接插入排序算法的查找有序子表部分,使用折半查找实现。
16.3 希尔排序
-
简介
又称“缩小增量排序”,是对直接插入排序的改进。
-
基本思路
将整个按照增量序列(如4,2,1)待排记录序列分割成若干子序列,对各个子序列分别进行直接插入排序,直至增量序列为1时,即对全体记录进行一次直接插入排序,完成排序。
16.4 冒泡排序
n个记录排序:首先将第1、2记录的关键字比较,若为逆序,则交换,再比较2、3关键字,以此类推至结尾。这是一躺冒泡排序,可以得到最后一位为最大值。再进行第二趟,对n-1个关键字进行同样的方式排序,以此类推。
16.5 快速排序
-
基本思路
通过一次快排划分为前半区和后半区,其中前半区的关键字均不大于后半区的关键字,然后再对这两个分区快排,递归式进行。
-
具体做法
- 设两个指针i,j分别位于序列的首位与末位。设序列的一个关键字(通常为第一位)为key。
- 从j向前搜索,找到关键字小于key 的记录时,将该记录向前移到i的位置。
- 接着从i往后搜索,找到关键字大于key的记录时,将该记录向后移到j的位置。
- 重复该过程直至i与j相等为止。
-
相关特点
- 当每次基准点能把表等分为左右两个长度相近的子表时,速度最快。
- 当表本身已是有序/逆序时,速度最慢。
- 移动次数=表其他元素的移动次数+1(自己最后移动)
- 递归次数与各元素的初始排列有关,分区后两边平衡,递归次数少,不平衡则递归次数多。即对n个关键字进行快排,最大递归深度为n,最小递归深度为log₂n。
- 递归次数与处理顺序无关。
- 快速排序的平均空间复杂度为O(log₂n),最坏情况为O(n)。
16.6 简单选择排序
n个记录排序:通过n - i在次关键字之间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录进行交换,当i等于n时排序完成。
16.7 堆排序
- 具体做法:
- 按堆的定义排成一个序列(建立初始堆(完全二叉树))
- 从(完全二叉树)最后,从下向上调整保证每棵小二叉树的根节点都大于左右子树结点,输出对顶的最大关键字(对于大根堆而言)
- 将(完全二叉树)最后的叶子结点代替刚移走的堆顶,再从上向下调整保证每棵小二叉树的根节点都大于左右子树结点,调整后堆顶便为次大的关键字。
- 如此反复第三步,直到全部关键字排成有序序列为止。
- 理解
- 建堆:插入新元素时放入(完全二叉树最后面),从下向上,从后往前调整堆。
- 调整堆:取走堆顶时,将(完全二叉树最后)元素替代取走的堆顶,从上向下调整堆。
- 性质特点
- 向具有n个结点的堆中插入/删除一个新元素的时间复杂度:O(log₂n)
- 构建n个记录的初始堆时间复杂度:O(n)
- 对n个记录进行堆排序,最坏情况下其时间复杂度为O(nlog₂n)
16.8 归并排序
两路归并排序:把一个有n个记录的无序文件看成是由n个长度为1的有序子文件组成的文件,然后进行两两归并,得到[ n/2 ]个长度为2或1的有序文件,再两两归并,如此反复,直至形成那个记录的有序文件为止。(即22归并,44归并,88归并…,同理,三路归并为33归并,99归并,27 27归并)
二路归并排序中,归并趟数的数量级是O(log₂n)。
归并排序算法在平均/最坏情况下的空间复杂度都是O(n)。外部排序通常采用归并排序法。
16.9 基数排序(了解)
- 基本思路:
- 设立r个队列,队列的编号分别为0、1、2、…、r-1。
- 按最低有效为的值把n个关键字分配到这r个队列中。
- 按照队列编号从小到大将各队列中的关键字依次手机起来。
- 再按照次低有效为的值把刚收集起来的关键字分配到r个队列中。
- 重复上述分配和收集过程,直到按照最高有效位分配和收集。
16.10 常用内部排序方法比较
排序方法 | 特点 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|
直接插入 | n小且记录本身信息量小,基本有序时效率最高 | O(n²) | O(1) | √ |
折半插入 | 对直接插入算法的改进 | O(n²) | O(1) | √ |
希尔排序 | 时间复杂度与增量的选取有关 | O(n^1.3) | O(1) | |
冒泡排序 | 基本有序时效率高 | O(n²) | O(1) | √ |
快速排序 | 平均性能最好,适用n大,且完全乱序的序列 | O(nlog₂n) | O(log₂n) | |
简单选择 | n小时效率高 | O(n²) | O(1) | |
堆排序 | n大时有优越性 | O(nlog₂n) | O(1) | |
归并排序 | n大,且需要稳定时用,但在所有排序中是最占空间的 | O(nlog₂n) | O(n) | √ |
基数排序 | n很大且关键字位数少 | O(d(n+rd)) | O(rd) | √ |
PS:红字为插入类排序、绿字为交换类排序、蓝字为选择类排序。
Note:
- 希尔排序和堆排序都利用了顺序存储的特性,用链式存储的序列时时间复杂度会增加。
- 简单选择排序与序列初始状态无关。